信号通信是在软件层面上对中断机制的一种模拟。信号是进程间通信机制中唯一的异步通信机制。
信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。它可以在任何时候发给某一进程,而无需知道该进程的状态。如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它为止。
信号事件的产生有硬件来源(如按下键盘或者其他硬件故障)和软件来源,软件来源除了软件调用,还包括一些非法运算等操作。
进程可以通过3种方式来响应一个信号:
1、忽略信号:即对信号不做任何处理,其中,有两个信号不能忽略,SIGKILL & SIGSTOP。
2、捕捉信号:定义信号处理函数,当信号发生时,执行相应的操作。
3、执行默认操作:Linux对每种信号都规定了默认操作,下表包含了部分的定义:
信号的相关函数包括信号的发送和设置,具体如下:
发送信号的函数:kill() raise() sigqueue()
设置信号的函数:signal() sigaction() setitimer()
其他函数:alarm() pause()
信号发送:kill() raise()
kill() 函数同kill 系统命令一样,可以发送信号给进程或是进程组(实际上,kill 系统命令就是调用 kill() 函数。需要注意的是,它不仅可以终止进程(发送 SIGTERM信号),也可以向进程发送其他信号。
注意:raise() 函数只能向进程自身发送信号
函数原型:
int kill(pid_t pid,int sig);
int raise(int sig);
说明:pid 为进程号;sig 为信号类型号
raise() 例程:进程在执行while(1)循环输出字符串的时候调用raise(SIGSTOP)使进程停止 \
/* raise.c */#include#include #include #include int main(){ printf("PID: %d\n",getpid()); while(1){ printf("run\n"); raise(SIGSTOP); }}
程序输出:
PID: 55859run[1]+ Stopped ./a.out
可以看到,进程在输出了第句run之后调用raise(SIGSTOP)使进程停止了,使用命令 "ps -ef | grep a.out" 可以看到进程并没有消失,确认进程只是暂停。使用 "kill -9 55859" 杀死进程。
kill() 例程:子进程间隔10us打印一串字符,父进程100us后 kill 子进程。
/* kill.c */#include#include #include #include #include int main(){ pid_t pid; pid = fork(); if(pid == -1){ perror("fork failed:"); exit(-1); } if(pid == 0){ while(1){ printf("child\n"); sleep(1); } }else{ sleep(3); kill(pid,SIGINT); sleep(3); exit(0); }}
alarm() 可以在进程中设置一个定时器,当定时器指定的时间到时,它就向进程发送SIGALARM信号。一个进程只能有一个闹钟,重复设置,前面的闹钟会被覆盖。
示例:
#include#include #include int main(){ alarm(3); while(1){ printf("run\n"); sleep(1); pause(); } printf("waken up\n");}
程序输出:
runAlarm clock
注释掉 pause(); 的程序输出:
runrunrunAlarm clock
信号的设置:signal() sigaction()
signal() 用于设置信号的处理函数。主要用于前32种非实时信号的处理,不支持信号传递信息。
注:signal() 是UNIX系统的遗留版本,不同的系统表现不一且在有的系统上会出现信号丢失的问题。且在man中亦提到,’应避免使用signal(),使用sigaction()代替‘。所以这里我们就不讨论signal(),直接使用sigaction()。
函数原型:
int sigaction(int signum,const struct sigaction *act,struct sigacton *oldact);
说明:signum - 信号类型,除SIGKILL & SIGSTOP 外的任何一个信号;act - 指向sigaction结构体的指针,包含对特殊信号的处理;oldact - 保留信号原先的处理方式。
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void);};
说明:
sa_handler 和 sa_sigaction 两者只能定义一个,且sa_sigaction 是 sa_handler的升级版本,由于sa_sigaction又太复杂,在这里我们就不讨论sa_sigaction。
sa_handler 是一个函数指针,指向信号处理函数。它既可以是用户自定义的处理函数,也可以为SIG_DFL(采用默认的处理方式)或SIG_IGN(忽略信号)。信号处理函数只有一个参数,即信号类型。
sa_mask是一个信号集合,用来指定在信号处理函数执行过程中哪些信号被屏蔽。
sa_flags 中包含了许多标志位,都是和信号处理相关的选项。常见可选值包括(SA_NODEFER/SA_NOMASK/SA_NOCLDSTOP/SA_RESTART/SA_ONESHOT/SA_RESETHAND)
sigaction() 示例:
/* signal.c */#include#include #include #include void my_func(int sign_no){ if(sign_no == SIGINT){ printf("I have got SIGINT\n"); }else if(sign_no == SIGQUIT){ printf("I have got SIGQUIT\n"); }}int main(){ struct sigaction action; //初始化结构体 sigaction(SIGINT,0,&action); action.sa_handler = my_func; sigaction(SIGINT,&action,0); sigaction(SIGQUIT,0,&action); action.sa_handler = my_func; sigaction(SIGQUIT,&action,0); printf("Waiting for signal SIGINT & SIGQUIT\n:"); pause(); exit(0);}