linux 信号signal和sigaction理解

来源:互联网 发布:淘宝卖家基本设置出错 编辑:程序博客网 时间:2024/04/30 14:58

这两天重看APUE看到信号,又记起来了kill并不是直接终止进程的命令,而是发信号的命令,通过发信号来控制进程状态。

[pikaqiu@centos6 ~]$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

这里罗列了全部的信号,其中我们平时ctrl+c是发送SIGINT信号,这个信号的默认动作为终止。我们平时说的kill掉一个进程,是发送SIGTERM信号,默认动作也为终止。

/********************************************************************************* *      Copyright:  (C) 2015 songyong<handy_skyoutlook.com> *                  All rights reserved. * *       Filename:  signal.c *    Description:  This file  *                  *        Version:  1.0.0(2015年08月08日) *         Author:  sky <handy_sky@outlook.com> *      ChangeLog:  1, Release initial version on "2015年08月08日 20时49分25秒" ********************************************************************************/#include <stdio.h>#include <signal.h>void sigtem(int sigo){    printf("Who's Your Daddy!\n");}int main (int argc, char **argv){    signal(SIGTERM, sigtem);    while(1);    return(0);} /* ----- End of main() ----- */

运行后,在另外一个终端killall该进程,会不断打印信息。以此了解signal的用法。很简单对吧,但因为signal的信号对于进程来说是不可靠的。也就是说信号可能无法被捕捉到或者是直接被进程忽略。还有一些问题,总之就是说现在通常已经不使用signal函数装信号,而转为使用 sigaction函数。于是我转载找到了两个浅显易懂点的例程。

=====================================================================================================

转载一:

signal,此函数相对简单一些,给定一个信号,给出信号处理函数则可,当然,函数简单,其功能也相对简单许多,简单给出个函数例子如下:  

#include <signal.h>#include <stdio.h>#include <unistd.h>    void ouch(int sig){     printf("I got signal %d\n", sig);     // (void) signal(SIGINT, SIG_DFL);     //(void) signal(SIGINT, ouch); }    int main() {     (void) signal(SIGINT, ouch);       while(1)     {         printf("hello world...\n");         sleep(1);     }}

当然,实际运用中,需要对不同到signal设定不同的到信号处理函数,SIG_IGN忽略/SIG_DFL默认,这俩宏也可以作为信号处理函数。同时SIGSTOP/SIGKILL这俩信号无法捕获和忽略。注意,经过实验发现,signal函数也会堵塞当前正在处理的signal,但是没有办法阻塞其它signal,比如正在处理SIG_INT,再来一个SIG_INT则会堵塞,但是来SIG_QUIT则会被其中断,如果SIG_QUIT有处理,则需要等待SIG_QUIT处理完了,SIG_INT才会接着刚才处理。

sigaction,这个相对麻烦一些,函数原型如下:

int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);

函数到关键就在于struct sigaction

stuct sigaction{      void  (*sa_handle)(int);      sigset_t sa_mask;      int sa_flags;  void (*sa_sigaction)(int, siginfo_t *,void *);};

1 #include <signal.h>  2 #include <stdio.h>  3 #include <unistd.h>  4   5   6 void ouch(int sig)  7 {  8     printf("oh, got a signal %d\n", sig);  9  10     int i = 0; 11     for (i = 0; i < 5; i++) 12     { 13         printf("signal func %d\n", i); 14         sleep(1); 15     } 16 } 17  18  19 int main() 20 { 21     struct sigaction act; 22     act.sa_handler = ouch; 23     sigemptyset(&act.sa_mask); 24     sigaddset(&act.sa_mask, SIGQUIT); 25     // act.sa_flags = SA_RESETHAND; 26     // act.sa_flags = SA_NODEFER; 27     act.sa_flags = 0; 28  29     sigaction(SIGINT, &act, 0); 30  31  32     struct sigaction act_2; 33     act_2.sa_handler = ouch; 34     sigemptyset(&act_2.sa_mask); 35     act.sa_flags = 0; 36     sigaction(SIGQUIT, &act_2, 0); 37         while(1)        {             sleep(1);        } 38     return;    }

1. 阻塞,sigaction函数有阻塞的功能,比如SIGINT信号来了,进入信号处理函数,默认情况下,在信号处理函数未完成之前,如果又来了一个SIGINT信号,其将被阻塞,只有信号处理函数处理完毕,才会对后来的SIGINT再进行处理,同时后续无论来多少个SIGINT,仅处理一个SIGINT,sigaction会对后续SIGINT进行排队合并处理。

2. sa_mask,信号屏蔽集,可以通过函数sigemptyset/sigaddset等来清空和增加需要屏蔽的信号,上面代码中,对信号SIGINT处理时,如果来信号SIGQUIT,其将被屏蔽,但是如果在处理SIGQUIT,来了SIGINT,则首先处理SIGINT,然后接着处理SIGQUIT。

3. sa_flags如果取值为0,则表示默认行为。还可以取如下俩值,但是我没觉得这俩值有啥用。

SA_NODEFER,如果设置来该标志,则不进行当前处理信号到阻塞

SA_RESETHAND,如果设置来该标志,则处理完当前信号后,将信号处理函数设置为SIG_DFL行为


下面单独来讨论一下信号屏蔽,记住是屏蔽,不是消除,就是来了信号,如果当前是block,则先不传递给当前进程,但是一旦unblock,则信号会重新到达。

#include <signal.h>#include <stdio.h>#include <unistd.h>static void sig_quit(int);int main (void) {    sigset_t new, old, pend;        signal(SIGQUIT, sig_quit);    sigemptyset(&new);    sigaddset(&new, SIGQUIT);    sigprocmask(SIG_BLOCK, &new, &old);    sleep(5);    printf("SIGQUIT unblocked\n");    sigprocmask(SIG_SETMASK, &old, NULL);    sleep(50);    return 1;}static void sig_quit(int signo) {    printf("catch SIGQUIT\n");    signal(SIGQUIT, SIG_DFL);}

gcc -g -o mask mask.c

./mask

========这个地方按多次ctrl+\

SIGQUIT unblocked

catch SIGQUIT
Quit (core dumped)

======================

注意观察运行结果,在sleep的时候,按多次ctrl+\,由于sleep之前block了SIG_QUIT,所以无法获得SIG_QUIT,但是一旦运行sigprocmask(SIG_SETMASK, &old, NULL);则unblock了SIG_QUIT,则之前发送的SIG_QUIT随之而来。

由于信号处理函数中设置了DFL,所以再发送SIG_QUIT,则直接coredump。


==================================================================================================

转载二:

背景:捕捉ctrl+c发送的SIGINT与ctrl+\发送的SIGQUIT信号

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <signal.h>void my_func(int sign_no){if(sign_no == SIGINT){printf("get the signal: SIGINT\n");}else if(sign_no == SIGQUIT){printf("get the signal: SIGQUIT\n");}}int main(void){struct sigaction action;printf("Waiting for signal ...\n");action.sa_handler = my_func;sigemptyset(&action.sa_mask);action.sa_flags = 0;sigaction(SIGINT, &action, 0);sigaction(SIGQUIT, &action, 0);pause();return 0;}

eg:自定义退出函数

 sigact.sa_handler = mysighandler;    sigemptyset(&sigact.sa_mask);    sigact.sa_flags = 0;    sigaction(SIGINT, &sigact, NULL);    sigaction(SIGTERM, &sigact, NULL);    sigaction(SIGQUIT, &sigact, NULL);

信号表:

1) SIGHUP (挂起) 当运行进程的用户注销时通知该进程,使进程终止2) SIGINT (中断) 当用户按下时,通知前台进程组终止进程3) SIGQUIT (退出) 用户按下或时通知进程,使进程终止4) SIGILL (非法指令) 执行了非法指令,如可执行文件本身出现错误、试图执行数据段、堆栈溢出5) SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用6) SIGABRT (异常中止) 调用abort函数生成的信号7) SIGBUS 非法地址, 包括内存地址对齐(alignment)出错. eg: 访问一个四个字长的整数, 但其地址不是4的倍数.8) SIGFPE (算术异常) 发生致命算术运算错误,包括浮点运算错误、溢出及除数为0.9) SIGKILL (确认杀死) 当用户通过kill -9命令向进程发送信号时,可靠的终止进程10) SIGUSR1 用户使用11) SIGSEGV (段越界) 当进程尝试访问不属于自己的内存空间导致内存错误时,终止进程12) SIGUSR2 用户使用13) SIGPIPE 写至无读进程的管道, 或者Socket通信SOCT_STREAM的读进程已经终止,而再写入。14) SIGALRM (超时) alarm函数使用该信号,时钟定时器超时响应15) SIGTERM (软中断) 使用不带参数的kill命令时终止进程17) SIGCHLD (子进程结束) 当子进程终止时通知父进程18) SIGCONT (暂停进程继续) 让一个停止(stopped)的进程继续执行. 本信号不能被阻塞.19) SIGSTOP (停止) 作业控制信号,暂停停止(stopped)进程的执行. 本信号不能被阻塞, 处理或忽略.20) SIGTSTP (暂停/停止) 交互式停止信号, Ctrl-Z 发出这个信号21) SIGTTIN 当后台作业要从用户终端读数据时, 终端驱动程序产生SIGTTIN信号22) SIGTTOU 当后台作业要往用户终端写数据时, 终端驱动程序产生SIGTTOU信号23) SIGURG 有"紧急"数据或网络上带外数据到达socket时产生.24) SIGXCPU 超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。25) SIGXFSZ 当进程企图扩大文件以至于超过文件大小资源限制。26) SIGVTALRM 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.27) SIGPROF (梗概时间超时) setitimer(2)函数设置的梗概统计间隔计时器(profiling interval timer)28) SIGWINCH 窗口大小改变时发出.29) SIGIO(异步I/O) 文件描述符准备就绪, 可以开始进行输入/输出操作.30) SIGPWR 电源失效/重启动31) SIGSYS 非法的系统调用。

在以上列出的信号中,
程序不可捕获、阻塞或忽略的信号有:SIGKILL,SIGSTOP
不能恢复至默认动作的信号有:SIGILL,SIGTRAP
默认会导致进程流产的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默认会导致进程退出的信号有:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
默认会导致进程停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH

此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在进程挂起时是继续,否则是忽略,不能被阻塞。


后半部分分别转载自:

http://blog.csdn.net/beginning1126/article/details/8680757

http://blog.csdn.net/g457499940/article/details/14518783


0 0
原创粉丝点击