信号机制

来源:互联网 发布:帝国cms仿站教程 编辑:程序博客网 时间:2024/05/16 09:42

信号机制


一个完整的信号生命周期可以分为3个重要阶段,这3个阶段由4个重要事件来刻画的:信号产生、信号在进程中注册、信号在进程中注销、执行信号处理函数


一个不可靠信号的处理过程是这样的:如果发现该信号已经在进程中注册,那么忽略该信号。因此,若前一个信号还未注销又产生了相同的信号会产生信号丢失。


而当可靠信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此信号不会丢失。


所有可靠信号都支持排队,而不可靠信号则都不支持排队。

用户进程对信号的响应可以有3种方式:

忽略信号,即对信号不做任何处理,但是有两个信号不能忽略,即SIGKILLSIGSTOP

捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数。

执行缺省操作,Linux对每种信号都规定了默认操作。

执行kill-l可以得到系统的所有信号:



函数介绍:

kill函数不仅仅可以终止进程,(实际上是发出SIGKILL信号)也可以向进程发送其他信号。


man2 kill可以得到详细的介绍:

含义:

sendsignal to a process

头文件:

#include<sys/types.h>

#include<signal.h>

函数原型:

intkill(pid_t pid, int sig);

参数含义:

Ifpid is positive, then signal sig is sent to the process with the ID

specifiedby pid.

如果pid是正数,则pid是信号要发送给进程的进程号

Ifpid equals 0, then sig is sent to every process in the process group

ofthe calling process.

如果pid=0,信号发送给pid所在多进程组的所有进程。

Ifpid 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.

如果 pid=-1.发给所有进程中的进程。除了进程号最大的外

Ifpid is less than -1, then sig is sent to every process in the

processgroup whose ID is -pid.

pid<-1发送给进程ID=-pid的进程

返回值

Onsuccess (at least one signal was sent), zero is returned. On error,

-1is returned, and errno is set appropriately.




raise函数:

含义:

raise- send a signal to the callercaller

kill-- send signal to a process

头文件:

#include<signal.h>

函数原型:

intraise(int sig);

在单进程程序中等于: kill(getpid(),sig);

在多进程程序中等于: pthread_kill(pthread_self(),sig);

Ifthe signal causes a handler to be called, raise() will only return

afterthe signal handler has returned.

返回值:

raise()returns 0 on success, and nonzero for failure.



例子:

下面这个示例首先使用fork创建了一个子进程,接着为了保证子进程不在父进程调用kill之前退出,在子进程中使用raise函数向子进程发送SIGSTOP信号,使子进程暂停。


接下来再在父进程中调用kill向子进程发送信号,在该示例中使用的是SIGKILL

#include<stdio.h>

#include<stdlib.h>

#include<signal.h>

#include<sys/types.h>

#include<sys/wait.h>



intmain()

{

pid_tpid;

intret;


printf("createchild process\n");

if((pid = fork())<0) {

perror("forkerror");

exit(1);

}


if(0 == pid) {

printf("Inchild process do raise() to send 'SIGSTOP' signal.\n\n");

raise(SIGSTOP);

exit(0);

}

else{

printf("Inparent process collect signal which were child process sent,\n");

printf("andcall kill() do options for itself.\n");

printf("pid= %d.\n", pid);

if(0 == (waitpid(pid,NULL,WNOHANG))) {/* return immediately if nochild has exited.

*/

if(0 == (ret=kill(pid,SIGKILL))) {

printf("kill%d\n",pid);

}

else{

perror("killerror\n");

}


}

}

return0;

}




alarm函数

含义:

alarm- set an alarm clock for delivery of a signal

头文件:

#include<unistd.h>

函数原型:

unsignedint alarm(unsigned int seconds);

描述:

alarm() arranges for a SIGALRM signal to be delivered to the calling

processin seconds seconds.

在指定时间到了以后会发送SIGALRM信号。

Ifseconds is zero, no new alarm() is scheduled.

Inany event any previously set alarm() is canceled.

如果之前设置过alarm将会被取消

返回值:

alarm()returns the number of seconds remaining until any previously

scheduledalarm was due to be delivered, or zero if there was no previ‐

ouslyscheduled alarm.


pause函数:

pause- wait for signal

头文件:

#include<unistd.h>

函数原型:

intpause(void);

描述:

pause() causes the calling process (or thread) to sleep until a signal

isdelivered that either terminates the process or causes the invocation of a signal-catching function.

返回值:

pause() only returns when a signal was caught and the signal-catching

functionreturned. In this case pause() returns -1, and errno is set

toEINTR.



信号处理



实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。





一种是使用简单的signal函数,另一种是使用信号集函数组。


signal函数语法要点

所需头文件

#include<signal.h>

函数原型

void(*signal(int signum, void (*handler)(int)))(int))

函数参数

signum:指定信号


handler



函数返回值

成功:以前的信号处理配置。


出错:-1

可见,首先该函数原型整体指向一个无返回值带一个整型参数的函数指针,也是信号的原始配置函数。接着该原型又带有两个参数,其中的第二个参数可以是用户自定义的信号处理函数的函数指针。

typedefvoid sign(int);


sign *signal(int, handler*);



信号集函数组

涉及一系列函数。主要包括一下几个:

sigemptyset:初始化信号集合为空。

sigfillset:初始化信号集合为所有信号的集合。

sigaddset:将指定信号加入到信号集合中去。

sigdelset:将指定信号从信号集中删去。

sigismember:查询指定信号是否在信号集合之中。


创建信号集合函数语法要点

所需头文件

#include<signal.h>

函数原型

intsigemptyset (sigset_t * set)


intsigaddset (sigset_t * set, int signum)


intsigismember (sigset_t * set, int signum)

函数参数

set:信号集。


函数返回值

成功:0sigismember成功返回1,失败返回0


sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态

函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不

包含任何有效信号。函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit

,表示该信号集的有效信号包括系统支持的所有信号。注意,在使用sigset_t类型的变量之

,一定要调用sigemptysetsigfillset做初始化,使信号集处于确定的状态。初始

sigset_t变量之后就可以在调用sigaddsetsigdelset在该信号集中添加或删除某种有效信

号。这四个函数都是成功返回0,出错返回-1sigismember是一个布尔函数,用于判断一个信号

集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1



Sigprocmask函数:

含义:

sigprocmask- examine and change blocked signals

读取和改变进程的屏蔽字

头文件:

#include<signal.h>

函数原型:

intsigprocmask(int how, const sigset_t *set, sigset_t *oldset);

how取值:

SIG_BLOCK

Theset of blocked signals is the union of the current set and

theset argument.

类似:mask=mask|set

SIG_UNBLOCK

The signals in set are removed from the current set of blocked

signals. It is permissible to attempt to unblock a signal which

isnot blocked.

mask=mask&~set


SIG_SETMASK

Theset of blocked signals is set to the argument set.

mask=set

If oldset is non-NULL, the previous value of the signal mask is stored

inoldset.


Ifset is NULL, then the signal mask is unchanged (i.e., how is

ignored), but the current value of the signal mask is nevertheless

returnedin oldset (if it is not NULL).

如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其

中一个信号递达。


Sigpending函数

#include<signal.h>

intsigpending(sigset_t *set);

读取当前进程的未决信号集,通过set传出。调用成功返回0出错返回-1



例子:

#include<signal.h>

#include<stdio.h>


voidprintsigset(const sigset_t *set)

{

inti;

for(i=0;i<32;i++) {

if(sigismember(set,i)==1){

putchar('1');

}else

putchar('0');


}

puts("");



}


intmain()

{

sigset_ts,p;

sigemptyset(&s);

sigaddset(&s,SIGINT);

sigprocmask(SIG_BLOCK,&s,NULL);

while(1) {

sigpending(&p);

printsigset(&p);

sleep(1);

}

return0;

}












程序运行时,每秒钟把各信号的未决状态打印一遍,由于我们阻塞了SIGINT信号,Ctrl-C将会

使SIGINT信号处于未决状态,Ctrl-\仍然可以终止程序,因为SIGQUIT信号没有阻塞。在信号处于阻塞状态时所发出的信号对进程不起作用




sigpaction函数语法要点

所需头文件

#include<signal.h>

函数原型

intsigaction (int signum, const struct sigaction * act, structsigaction * oldact)

函数参数

signum:信号的值,可以为除SIGKILLSIGSTOP外的任何一个特定有效的信号。


oldact:保存原来对相应信号的处理。

函数返回值

成功:0




struct sigaction {

void (*sa_handler)(intsigno);

sigset_t sa_mask;

intsa_flags;

void(*sa_restore)(void);

}


sa_handler是一个函数指针,指定信号关联函数,这里除可以是用户自定义的处理函外,还可以为SIG_DFL(采用缺省的处理方式)或SIG_IGN(忽略信号)。它的处理函数只有一个参数,即信号值。


sa_mask是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被阻塞,在调用信号捕获函数之前,该信号集要加入到信号的信号屏蔽字中。


sa_flags中包含了许多标志位,是对信号进行处理的各个选择。它的常见可选值如下表所示:


常见信号的含义及其默认操作

SA_NODEFER/SA_NOMASK

当捕捉到此信号时,在执行其信号捕捉函数时,系统不会自动阻塞此信号。

SA_NOCLDSTOP

进程忽略子进程产生的任何SIGSTOPSIGTSTPSIGTTINSA_NOCLDSTOPSIGTTOU信号。

SA_RESTART

可让重启的系统调用重新起作用。

SA_ONESHOT/SA_RESETHAND

自定义信号只执行一次,在执行完毕后恢复信号的系统默认动作。



sigaction函数可以读取和修改与指定信号相关联的处理动作。

sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这 样就可以用同一个函数处理多种信号。




例子


#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

//自定义函数

void my_func(int signum)

{


printf("If you wantto quit, please try SIGQUIT.\n");

}



int main()

{

sigset_t set,pendset;

struct sigactionaction1,action2;

//初始化

if (sigemptyset(&set)<0){

perror("sigemptyseterror");

}

//增加

if(sigaddset(&set,SIGQUIT)<0)

perror("sigaddseterror");


if(sigaddset(&set,SIGINT)<0)

perror("sigadddseterror");

//阻塞

if(sigprocmask(SIG_BLOCK,&set,NULL)<0)

perror("sigprocmaskerror");

else

{

printf("blocked \n");

sleep(10);

}

//取消阻塞

if(sigprocmask(SIG_UNBLOCK, &set, NULL) < 0)


perror("sigprocmaskerror");

else

printf("unblock.\n");

while (1) {

//发挥作用

if(sigismember(&set,SIGINT)) {

sigemptyset(&action1.sa_mask);

action1.sa_handler =my_func;

sigaction(SIGINT,&action1,NULL);

}else if(sigismember(&set,SIGQUIT)) {

sigemptyset(&action2.sa_mask);

action2.sa_handler =SIG_DFL;

sigaction(SIGTERM,&action2,NULL);

}


}

return 0;

}

该实例首先把SIGQUITSIGINT两个信号加入信号集,然后将该信号集设为阻塞状态,并在该状态下使程序暂停5秒。


接下来再将信号集设置为阻塞状态,再对这两个信号分别操作,其中SIGQUIT执行默认操作,而SIGINT执行用户自定义函数的操作。


0 0
原创粉丝点击