LinuxC信号及信号处理(2)

来源:互联网 发布:西安java薪资待遇如何 编辑:程序博客网 时间:2024/05/19 13:58

信号安装

(1) signal()

 #include <signal.h> void (*signal(int signum, void (*handler))(int)))(int); 

如果该函数原型不容易理解的话,可以参考下面的分解方式来理解:

typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler));

第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIGIGN);可以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。 如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIGERR。

(2)sigaction()

#include <signal.h>int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));   

sigaction函数用于改变进程接收到特定信号后的行为。该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。

(3)pause()
pause函数使调用进程挂起直至捕捉到一个信号。

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

pause函数会令目前的进程暂停(进入睡眠状态),直到被信号(signal)所中断,该函数只返回-1,并将errno设置为EINTR

信号处理函数的返回

信号处理函数可以正常返回,也可以调用其他函数返回到程序的主函数中,而不是从该处理程序返回。
1、setjmp/longjmp
使用longjmp可以跳转到setjmp设置的位置

#include <setjmp.h>int setjmp(jmp_buf env);void longjmp(jmp_buf env, int val);

参数env时一个特殊类型的jmp_buf的变量。这一数据类型是某种形式的数组,其中存放的时在调用longjmp时能用来恢复栈状态的所有信息。一般来说env是个全局变量,因为需从另一个函数中引用它。可以在希望返回的位置使用setjmp,直接调用setjmp时返回0;当从longjmp返回时,setjmp的返回值时longjmp的第二个参数的值,可以利用这一点使多个longjmp返回到一个setjmp处。

2、sigsetjmp/siglongjmp

由于信号处理期间自动屏蔽另外正在被处理的信号,而使用setjmp/longjmp跳出信号处理程序时又不会自动将信号屏蔽码修改回原来的屏蔽码,从而引起该信号被永久屏蔽。可以使用sigsetjmp/siglongjmp来解决这一问题。

#include <setjmp.h>int sigsetjmp(sigjmp_buf env, int savesigs);void siglongjmp(sigjmp_buf env, int val);

这两个函数与setjmp/longjmp的唯一区别就是sigsetjmp多了一个参数savesigs,如果savesigs非0;则sigsetjmp在env中保存进程的当前信号屏蔽字,在调用siglongjmp时会从其中恢复保存的信号屏蔽字。

信号的发送

1、kill函数
kill函数用来发送信号给指定的进程

#include <sys/types.h>#include <signal.h>int kill (pid_t pid, int sig);

该函数的行为与第一个参数pid的取值有关,第二个参数sig表示信号编号

如果pid是正数,则发送信号sig给进程号为pid的进程
如果pid为0,则发送信号sig给当前进程所属进程组里的所有进程
如果pid为-1,则把信号sig广播至系统内除1号进程和自身以外的所有进程
如果pid是比-1还小的负数,则发送信号sig给属于进程组-pid的所有进程
如果参数sig是0,则kill()仍执行正常的错误检查,但不发送信号
函数执行成功返回0 ,当有错误发生时返回-1

2、raise函数
raise函数是ANSI C而非POSIX标准定义的,用来给调用他的进程发送信号

#include <signal.h>int raise(int sig);

参数sig表示要发送的信号,成功返回0,失败返回非0值。

3、sigqueue函数
sigqueue函数是一个比较新的发送信号的函数,它支持信号带有参数,从而可以与函数sigaction配合使用。

#include <signal.h>int sigqueue(pid_t pid, int sig, const union sigval value);

sigqueue用来发送信号sig给进程pid .与kill系统调用不同的是,sigqueue在发送信号的同时还支持信号携带参数,另一个不同点时sigqueue不能给一组进程发送信号。参数value是一个共用体,其定义如下:

union sigval {    int sival_int;    void *sival_ptr;};

信号携带的参数要么是一个整型值,要么是一个void型指针。当接收进程的信号处理函数是由sigaction函数设置的并设置了SA_SIGINFO标志时,接收进程可以从siginfo_t结构的si_value域取得信号发送时携带的数据。
函数执行成功时返回0,表明信号被成功发送到目标进程,当有错误发送时则返回-1,错误代码存入errno中。
4、alarm函数
alarm函数可以用来设置定时器,定时器超时将产生SIGALRM信号给调用进程。

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

参数seconds表示设定的秒数,经过seconds后,内核将给调用该函数的进程发送SIGALRM信号。如果seconds为0,则不再发送SIGALRM信号。最新一次调用alarm函数将取消之前一次的设定。
如果之前已经调用过alarm,则返回之前设置的定时器剩余时间;否则如果之前没有设置过定时器,则返回0。
5、getitimer/setitimer
与alarm函数一样,setitimer函数也是用来设置定时器的,且alarm和setitimer使用同一个定时器,因此会相互影响。setitimer具有更多的功能。

#include <sys/time.h>int getitimer(int which, struct itimerval *value);int setitimer(int which, const struct itimerva *value, struct itimerval *ovalue);

第一个参数which用来指定使用哪一个定时器,根据参数which可单独设定每个定时器,定时器的种类如下:

ITIMER_REAL:按实际时间计时,计时到达时讲给进程发送SIGALRM信号,相当于高精度的alarm函数。
ITIMER_VIRTUAL:仅当进程执行时才进行计时。计时到达时将给进程发送SIGTALRM信号。
ITMER_PROF:进程执行的时间以及内核因本进程而消耗的时间都计时。
参数value用来指定定时器的时间,结构struct itimerval的定义如下:

struct itimerval {    struct timeval it_interval;    struct timeval it_value;};

其中结构struct timeval的定义如下:

struct timeval {    long tv_sec;          //秒数    long tv_usec;         //微秒};

对于函数getitimer,如果存在由which指定的定时器,则将剩余时间保存在it_value中,该定时器的初始值保存在it_interval中;如果不存在指定类型的定时器,则将value设置为0返回。执行成功返回0,当有错误发生返回-1.
对于函数setitimer,参数ovalue如果不是空指针,则将在其中保存上次设定的定时器的值。定时器从value递减为0时,产生一个信号,并将it_value的值设为it_interval,然后重新开始计时,如此周而复始。仅当it_value的值为0或者计时到达而it_interval的值为0时,停止计时。执行成功返回0,当有错误发生时返回-1.

6、abort函数

abort函数用来向进程发送SIGABRT信号

#include <stdlib.h>void abort(void);

如果进程设置信号处理函数以扑捉SIGABRT信号,且信号处理函数不返回(如使用longjmp),则abort()不能终止进程。Abort()终止进程时,所有打开的流均会被刷新和关闭。如果进程设置了SIGABRT被阻塞或忽略,abort()将覆盖这种设置。

信号的屏蔽

信号集及信号集操作

sigfillset(sigset_t *set); 设置所有的信号到set信号集中;sigemptyset(sigset_t *set);set信号集中清空所有信号;sigaddset(sigset_t *set,int sig);set信号集中加入sig信号;sigdelset(sigset_t *set,int sig);set信号集中删除sig信号;

阻塞信号相关函数

int sigprocmask(int how,const sigset_t *set,sigset_t *set);  根据how值,设置阻塞信号集,或释放阻塞的信号集int sigpending(sigset_t *set); 获取在阻塞中的所有信号;int sigsuspend(const sigset_t *set);    类似于 pause()函数!