初学Linux--信号

来源:互联网 发布:fliqlo mac 编辑:程序博客网 时间:2024/06/05 14:20

信号的基本概念

信号的定义
信号又称软中断,用来通知进程发生了异步事件,进程在正常运行时,随时可能被信号所中断,进程可以忽略也可以去执行它。
来源
引起信号的原因有以下几种:
1.程序执行错误代码;
2.同一系统中的其他进程通过kill和系统调用向当前进程发送信号;
3.用户通过控制终端发来信号;
4.子进程结束时发来的SIGCLD;
5.程序中设定的定时器SIGALARM产生的信号;
分类
为方便标识,Linux为每个信号分配了名字,这些名字以SIG开头,可通过shell执行命令kill-l来获得系统中全部信号的列表。在kill-l的输出中每个信号都有一个数字标识,这就是信号的值,程序中可以直接使用值来来代表信号。这些信号的名称和值的定义在头文件

信号安装和处理

信号处理方式
1.忽略:除了SIGKILL和SIGSTOP其他都可以忽略。
2.捕捉:要捕捉需要先安装信号,告诉进程信号收到后执行自定义的函数,已完成特定功能。
3.执行系统默认动作:如果程序不进行任何处理,将按照缺省处理。
用signal安装信号

#include<signal.h>_sighandler_t signal(int _sig,_sighandler_t _handler);

_sig:信号名称
_handler:收到信号后,用于处理的函数。
用sigaction安装信号

#include<signal.h>int sigaction(int_sig,_const struct sigaction*_restrict_act,struct sigaction*_restrict_oact);

_act:指定安装信号的数据结构,是一个struct sigaction指针,定义位于头文件

struct sigaction{#ifdef_USE_POSIX19309union{_sighandler_t sa_handler;void (*sa_sigaction)(int,signfo_t*,void*);}_sigaction_handler;//定义信号的处理函数,实际上为信号处理函数的指针#define sa_handler _sigaction_handler.sa_handler#define sa_sigaction _sigaction_handler.sa._sigaction#else    _sighandler_t sa_handler;#endif    _sigset_t sa_mask;//信号掩码    int sa_flags;//标志位    void (*sa_restorer)(void);};

_oact:输出参数,指向struct sigaction的结构的指针。调用sigaction成功后,将返回信号的原来处理方式,如果不需要获取源信号的处理方式,则该参数传入NULL。
阻塞处理
信号阻塞时,系统内核对相应的信号进行缓存,等到解除阻塞后,再将信号发给进程。
信号会在三种情况下进入阻塞状态:
1.信号的处理函数正在执行,信号会被阻塞直到信号函数执行完成,阻塞将会解除。
2.通过sigaction进行信号安装时,如果设置了sa_mask阻塞信号集,则该集中的信号在处理函数执行期间将会阻塞。
3.系统调用sigprocmask,可通过该系统调用指定阻塞某个信号

#include<signal.h>int sigprocmask(int _how,_cosnt sigset_t*_restrict_set,sigset_t*_restrict_oset);

_how:设置信号阻塞掩码的方式。
_set:阻塞信号集
_oset:原阻塞信号集
信号集的操作
信号集的数据类型为sigset_t,实际是一个结构,结构中唯一的成员是一个long型数组,用于存储多个信号。
Linux提供了多个函数来操控信号集

#include<signal.h>int sigemptyset(sigset_t*_set);//清空信号集int sigfillset(sigset_t*_set);//填充信号集int sigaddset(sigset_t*_set,int_signo);//添加信号int sigdelset(sigset_t*_set,int_signo);//删除信号int sigismember(_const sigset_t*_set,int _signo);//判断是否包含某信号

未决信号的处理
未决是一种信号产生到被接受处理之间的一种过渡状态。如果调用了sigprocmask阻塞了某种信号,则向进程发送的这种信号处于未决状态,linux提供了函数sigpending来获取进程中处于未决状态的信号。

#include<signal.h>int sigpending(sigset_t*_set);

等待信号
某些情况下,程序需要暂停执行,进行休眠状态,等待信号的到来。这时需要pause系统调用,直到进程接受到信号后,pause才会返回

#include<unistd.h>int pause(void);

如果在pause调用之前该信号到达进程,在随后的pause调用中将进入无限期等待。为此Linux提供了更为强大的sigsuspend。sigsuspend设置当前进程的信号掩码,然后进程进入休眠,直到信号到达,工作过程如下:
1.设置信号掩码并阻塞进程
2.收到信号,恢复原来的掩码。
3.调用进程设置的信号处理函数
4.等待信号处理函数返回后,sigsuspend返回。

#include<signal.h>int sigsuspend(_cosnt sigset_t*_set);

信号处理函数的实现
信号处理函数是进程接收到信号后要执行的函数。该函数应该尽量简洁,最好只改变一个外部标志变量的值,而在另外的程序中不断的检测该变量,繁杂的工作都留给这些程序去做在定义信号处理函数时,需注意:
1.如果信号处理程序中需要保存一个全局变量,应该在程序中用到关键字volatile声明此变量。通知编译器,在编译过程中,不要对该变量进行优化,因为优化时,往往会引用该变量的拷贝。信号处理函数中的全局变量,多为异步修改,其值随时可能被信号处理函数改变。通过volatile关键字声明,让编译器在使用该变量时,直接从该变量的内在地址引用。
2.如果在信号处理函数中调用函数,那么该函数必须是可以重入或则保证在信号处理函数执行期间不会有信号到达进程。
在信号处理函数里,有时需要用到长跳转的操作,长跳转是指信号处理函数直接跳转到函数体外的指定代码位置继续运行。Linux提供了两个函数来实现这个功能。
设置跳转点的sigsetjmp和执行跳转的siglongjmp。成功调用sigsetjmp后,该语句所在的位置就是跳转点,该位置指针被保存到sigsetjmp的第一个参数中的。siglongjmp用于跳转到指定的跳转点。

#include<setjmp.h>int sigsetjmp(struct_jmp_buf_tag_env[1],int_savemask);void siglongjmp(sigjmp_buf_env,int_val);

_env:输出参数,实际是一个结构指针。该结构中包涵长跳转指针,是否保存信号掩码以及保存的信号掩码等信息。
_savemask:输入参数,是否保存信号掩码。

信号的发送

使用kill发送信号
kill可以想某个进程或一组进程发送信号

#Include<signal.h>int kill(_pid_t_pid,int _sig);

_pid:进程ID
有些情况需要在发送信号的同时,附加一些数据,为此用到另一个系统调用sigqueue。

#include<signal.h>int sigqueue(_pid_t_pid,int _sig,_const union sigval_val);

_val:输入参数,信号要附加的数据

typedef union sigval{int sigval _int;void*sival_ptr;}sigval_t;

SIGALRM

安装sigalrm信号
要使用定时器,首先需要安装SIGALRM信号,不安转的话,进程收到SIGALRM信号会按缺省终止当前进程。安装过程桐其他信号完全相同。SIGALRM信号处理函数应该要做的操作是设置超时标志,由外部的程序去判断该超时标志,并输出相应的错误信息。
设置定时器
在Linux系统中,每个进程都有一个唯一的定时器,该定时器提供了以秒为单位的定时功能,在超时时间到达后,调用alarm的进程将受到SIGALRM信号。设置定时器的系统调用为alarm,该调用的声明位于

#include<unistd.h>unsigned int alarm(unsigned int_seconds);

SIGCLD信号

子进程的退出过程
在一个子进程退出时,并不立即释放其占用资源,而是通知其父进程,有父进程进行后续的工作,在这一过程中,系统将依次产生下列事件:
1.向父进程发送SIGCLD信号,子进程进入zombie状态
2.父进程收到SIGCLD信号,进行处理。
SIGCLD信号的处理
为了避免出现僵尸进程,基本的处理方法有两种:
1.父进程忽略SIGCLD信号,调用signal(SIGCLD,SIG_IGN)
2.父进程捕获SIGCLD信号,要捕获SIGCLD信号,应该先安装SIGCLD信号,在SIGCLD信号处理函数中的,应该调用wait或者waitpid等函数来获取子进程的退出状态。
参考文献《Linux编程从入门到精通》