linux系统编程之信号(三):信号安装、signal、kill,arise讲解
一,信号安装
如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。
linux主要有两个函数实现信号的安装:signal()、sigaction()。其中signal()只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;而sigaction()是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与 sigqueue() 系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。
二,signal()用法
#include <signal.h>
typedef void (*__sighandler_t) (int);
#define SIG_ERR ((__sighandler_t) -1)
#define SIG_DFL ((__sighandler_t) 0)
#define SIG_IGN ((__sighandler_t) 1)
void (*signal(int signum, void (*handler))(int)))(int);
如果该函数原型不容易理解的话,可以参考下面的分解方式来理解:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIG_IGN);可以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。
如果signal()调用成功,返回最后一次也就是上一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。
传递给信号处理例程的整数参数是信号值,这样可以使得一个信号处理例程处理多个信号。
man帮助说明:
DESCRIPTION
The behavior of signal() varies across Unix versions, and has also var-
ied historically across different versions of Linux. Avoid its use:
use sigaction(2) instead. See Portability below.
signal() sets the disposition of the signal signum to handler, which is
either SIG_IGN, SIG_DFL, or the address of a programmer-defined func-
tion (a "signal handler").
If the signal signum is delivered to the process, then one of the fol-
lowing happens:
* If the disposition is set to SIG_IGN, then the signal is ignored.
* If the disposition is set to SIG_DFL, then the default action asso-
ciated with the signal (see signal(7)) occurs.
* If the disposition is set to a function, then first either the dis-
position is reset to SIG_DFL, or the signal is blocked (see Porta-
bility below), and then handler is called with argument signum. If
invocation of the handler caused the signal to be blocked, then the
signal is unblocked upon return from the handler.
The signals SIGKILL and SIGSTOP cannot be caught or ignored.
RETURN VALUE
signal() returns the previous value of the signal handler, or SIG_ERR
on error.
示例程序:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h> void sig_handler(int signo);
int main(void)
{
printf("mian is waiting for a signal\n");
if(signal(SIGINT,sig_handler) == SIG_ERR){
perror("signal errror");
exit(EXIT_FAILURE);
}
for(; ;);//有时间让我们发送信号 return 0;
} void sig_handler(int signo)
{
printf("catch the signal SIGINT %d\n",signo);
}
结果:
可知我们捕获了SIGINT信号,每当我们按下ctrl+c或利用kill发送SIGINT信号时,执行我们安装的信号处理函数,当我们按下:ctrl+\或kill –SIGQUIT pid发送SIGQUIT信号时,程序退出,那是因为进程对SIGQUIT信号的默认处理动作是退出程序。
现在我们来获得进程的最后一次为安装信号时所指定的处理函数:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h> void sig_handler(int signo);
int main(void)
{
printf("main is waiting for a signal\n");
__sighandler_t prehandler;
prehandler = signal(SIGINT,sig_handler);
if(prehandler == SIG_ERR){
perror("signal errror");
exit(EXIT_FAILURE);
} printf("the previous value of the signal handler is %d\n",(int)prehandler);
//for(; ;);//有时间让我们发送信号 return 0;
} void sig_handler(int signo)
{
printf("catch the signal SIGINT %d\n",signo);
}
结果:
为0,由前面的宏定义:#define SIG_DFL ((__sighandler_t) 0),可知处理动作为SIG_DFL,而SIGINT默认的处理动作就是终止进程
示例:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h> void sig_handler(int signo);
int main(void)
{
printf("main is waiting for a signal\n");
__sighandler_t prehandler;
prehandler = signal(SIGINT,SIG_DFL);
if(prehandler == SIG_ERR){
perror("signal errror");
exit(EXIT_FAILURE);
} for(; ;);//有时间让我们发送信号 return 0;
}
结果:
当按下ctrl+c时发送SIGINT信号给进程,然后进程终止
三,kill()发送信号
发送信号的主要函数有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。
这里我们先将kill函数使用:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid,int signo)
该系统调用可以用来向任何进程或进程组发送任何信号。参数pid的值为信号的接收进程
- pid>0 进程ID为pid的进程
- pid=0 同一个进程组的进程
- pid<0 pid!=-1 进程组ID为 -pid的所有进程
- pid=-1 除发送给每一个调用进程有权限发送的进程除自身及1(init)进程外
Sinno是信号值,当为0时(即空信号),实际不发送任何信号,但照常进行错误检查,因此,可用于检查目标进程是否存在,以及当前进程是否具有向目标发送信号的权限(root权限的进程可以向任何进程发送信号,非root权限的进程只能向属于同一个session或者同一个用户的进程发送信号)。
Kill()最常用于pid>0时的信号发送。该调用执行成功时,返回值为0;错误时,返回-1,并设置相应的错误代码errno。下面是一些可能返回的错误代码:
EINVAL:指定的信号sig无效。
ESRCH:参数pid指定的进程或进程组不存在。注意,在进程表项中存在的进程,可能是一个还没有被wait收回,但已经终止执行的僵死进程。
EPERM: 进程没有权力将这个信号发送到指定接收信号的进程。因为,一个进程被允许将信号发送到进程pid时,必须拥有root权力,或者是发出调用的进程的UID 或EUID与指定接收的进程的UID或保存用户ID(savedset-user-ID)相同。如果参数pid小于-1,即该信号发送给一个组,则该错误表示组中有成员进程不能接收该信号。
man帮助说明:
DESCRIPTION
The kill() system call can be used to send any signal to any process
group or process.
If pid is positive, then signal sig is sent to the process with the ID
specified by pid.
If pid equals 0, then sig is sent to every process in the process group
of the calling process.
If pid equals -1, then sig is sent to every process for which the call-
ing process has permission to send signals, except for process 1
(init), but see below.
If pid is less than -1, then sig is sent to every process in the pro-
cess group whose ID is -pid.
If sig is 0, then no signal is sent, but error checking is still per-
formed; this can be used to check for the existence of a process ID or
process group ID.
For a process to have permission to send a signal it must either be
privileged (under Linux: have the CAP_KILL capability), or the real or
effective user ID of the sending process must equal the real or saved
set-user-ID of the target process. In the case of SIGCONT it suffices
when the sending and receiving processes belong to the same session.
RETURN VALUE
On success (at least one signal was sent), zero is returned. On error,
-1 is returned, and errno is set appropriately.
ERRORS
EINVAL An invalid signal was specified.
EPERM The process does not have permission to send the signal to any
of the target processes.
ESRCH The pid or process group does not exist. Note that an existing
process might be a zombie, a process which already committed
termination, but has not yet been wait(2)ed for.
示例程序:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) void handler(int sig);
int main(int argc, char *argv[])
{
if (signal(SIGUSR1, handler) == SIG_ERR)
ERR_EXIT("signal error");
pid_t pid = fork();
if (pid == -1)
ERR_EXIT("fork error"); if (pid == 0)
{
sleep(1);
kill(getppid(), SIGUSR1);
exit(EXIT_SUCCESS);
} int n = 5;
do
{
printf("the number of seconds left to sleep is %d s\n",n);
n = sleep(n);
} while (n > 0);
return 0;
} void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}
结果:
以上程序里有子进程给父进程发送SIGUSR1信号,父进程收到信号后,睡眠被中断,然后去执行信号处理函数,返回后继续睡眠剩余的时间后退出程序。
现在利用kill给与给定pid同组所有进程发送信号:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) void handler(int sig);
int main(int argc, char *argv[])
{
if (signal(SIGUSR1, handler) == SIG_ERR)
ERR_EXIT("signal error");
pid_t pid = fork();
if (pid == -1)
ERR_EXIT("fork error"); if (pid == 0)
{
pid = getpgrp();
kill(-pid, SIGUSR1);
//kilpg(getpgrp(), SIGUSR1);
exit(EXIT_SUCCESS);
} int n = 5;
do
{
n = sleep(n);
} while (n > 0);
return 0;
} void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}
结果:
可知收到进行了两次信号处理函数的执行:因为当前所属组中只有父子两个进程,从上可知有两种方式给组进程发送信号:kill和killpg
四,arise函数
#include <signal.h>
int raise(int signo)
向进程本身发送信号,参数为即将发送的信号值。调用成功返回 0;否则,返回 -1。
man帮助说明:
DESCRIPTION
The raise() function sends a signal to the calling process or thread.
In a single-threaded program it is equivalent to
kill(getpid(), sig);
In a multithreaded program it is equivalent to
pthread_kill(pthread_self(), sig);
If the signal causes a handler to be called, raise() will only return
after the signal handler has returned.
RETURN VALUE
raise() returns 0 on success, and non-zero for failure.
示例程序:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h> void sig_handler(int signo);
int main(void)
{
printf("mian is waiting for a signal\n");
if(signal(SIGINT,sig_handler) == SIG_ERR){
perror("signal errror");
exit(EXIT_FAILURE);
}
printf("useing raise to send a signal to himself\n");
raise(SIGINT);
sleep(1);
printf("useing kill to send a signal to himself\n");
kill(getpid(),SIGINT); return 0;
} void sig_handler(int signo)
{
printf("catch the signal SIGINT %d\n",signo);
}
结果:
可知两种方式都可以给自身发送信号。
linux系统编程之信号(三):信号安装、signal、kill,arise讲解的更多相关文章
- linux系统编程之管道(三)
今天继续研究管道的内容,这次主要是研究一下命名管道,以及与之前学过的匿名管道的区别,话不多说,进入正题: 所以说,我们要知道命名管道的作用,可以进行毫无关系的两个进程间进行通讯,这是匿名管道所无法实现 ...
- Linux系统编程(24)——信号的生命周期
信号生命周期为从信号发送到信号处理函数的执行完毕. 对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:信号诞生:信号在进程中 ...
- Linux系统编程(20)——信号基本概念
信号及信号来源 信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的.信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知 ...
- Linux系统编程(23)——信号的阻塞
实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending).进程可以选择阻塞(Block)某个信号.被阻塞的信号产生时将保持在未决状态,直到进程 ...
- Linux系统编程(21)——信号的产生
1.通过终端按键产生信号 通过上一篇我们知道了SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,现在我们来验证一下. 首先解释什么是Core Dump ...
- linux系统编程之进程(三)
今天继续学习进程相关的东东,继上节最后简单介绍了用exec函数替换进程映像的用法,今天将来深入学习exec及它关联的函数,话不多说,正式进入正题: exec替换进程映象: 对于fork()函数,它 ...
- linux系统编程之信号(七)
今天继续学习信号,主要是学习关于时间和定时器相关的函数的使用,关于这个实际上有很多内容,这里先简要进行说明,等之后再慢慢进行相关深入,也主要是为接下来要做的一个综合linux系统编程的例子做准备,好了 ...
- Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号
Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号 背景 上一讲我们介绍了Unix IPC中的2种管道. 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道 ...
- linux系统编程(一)概述
glibc库封装了linux系统调用,并提供c语言接口 所以学习linux系统编程,主要参考glibc库系统调用相关api 一.进程控制: fork 创建一个新进程 clone 按指定条件创建子进程 ...
- Linux 系统编程 学习:11-线程:线程同步
Linux 系统编程 学习:11-线程:线程同步 背景 上一讲 我们介绍了线程的属性 有关设置.这一讲我们来看线程之间是如何同步的. 额外安装有关的man手册: sudo apt-get instal ...
随机推荐
- vortex
vortex - Bing dictionary US['vɔr.teks]UK['vɔː(r)teks] n.旋涡:涡旋:低涡:感情(或局势)的旋涡 网络漩涡:涡流:旋风 变形Plural Form ...
- mysql优化概述3
1.前缀索引 建立索引关键字一种方案. 通常会使用字段的整体作为索引关键字. 有时,使用字段前部分数据,也可以去识别某些记录. 语法: index `索引名` (`字段`(N)); 使用字段前N个字符 ...
- Python Socket 编程详细介绍(转)
Python 提供了两个基本的 socket 模块: Socket 它提供了标准的BSD Socket API. SocketServer 它提供了服务器重心,可以简化网络服务器的开发. 下面讲解下 ...
- geoserver 文件系统
我介绍了GeoServer的一些重要的资源以及它们的访问接口,现在来看看它们的保存形式.GeoServer的数据没有保存到数据库,而是文件系统,这让我们的学习轻松不少.默认情况下,GeoServer的 ...
- Mathtype使用技巧
1. 打开/关闭MathType窗口 Alt+Ctrl+q:插入inline公式 Ctrl+S:更新公式到Word相应位置 Alt+F4:保存并关闭MathType窗口,返回Word. 2. 公式 ...
- Jmeter If Controller中设置多个条件用“与”进行连接
"${noteID}"!="NOT FOUND" && "${securitiesId}"!="0P00011FQ ...
- 乞丐版servlet容器第3篇
4 EventListener接口 让我们继续看SocketConnector中的acceptConnect方法: @Override protected void acceptConnect() t ...
- jquery报.live() is not a function的解决方法
jquery报.live() is not a function的解决方法: jquery中的live()方法在jquery1.9及以上的版本中已被废弃了,如果使用,会抛出TypeError: $(. ...
- 2018.09.10 bzoj1499: [NOI2005]瑰丽华尔兹(单调队列优化dp)
传送门 单调队列优化dp好题. 这题其实很简单. 我们很容易想到一个O(T∗n∗m)" role="presentation" style="position: ...
- hdu-1055(贪心)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1055 题意:给一棵树涂色,这棵树的每个节点都有自己的价值Ci,涂色的原则是只由这个节点的父节点涂色之后 ...