linux 信号下 信号的传递,捕捉

linux编程下signal()函数
当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。根据信号的默认处理规则SIGPIPE信号的默认执行动作是
terminate(终止、退出), 所以client会退出。
若不想客户端退出可以把 SIGPIPE设为SIG_IGN
如:& & signal(SIGPIPE,SIG_IGN);
这时SIGPIPE交给了系统处理。
服务器采用了fork的话,要收集垃圾进程,防止僵死进程的产生,可以这样处理:
signal(SIGCHLD,SIG_IGN); 交给系统init去回收。
这里子进程就不会产生僵死进程了。
signal(SIGHUP, SIG_IGN);
signal信号函数,第一个参数表示需要处理的信号值(SIGHUP),第二个参数为处理函数或者是一个表示,这里,SIG_IGN表示忽略SIGHUP那个注册的信号。
SIGHUP和控制台操作有关,当控制台被关闭时系统会向拥有控制台sessionID的所有进程发送HUP信号,默认HUP信号的action是
exit,如果远程登陆启动某个服务进程并在程序运行时关闭连接的话会导致服务进程退出,所以一般服务进程都会用nohup工具启动或写成一个
unix中进程组织结构为 session 包含一个前台进程组及一个或多个后台进程组,一个进程组包含多个进程。
一个session可能会有一个session首进程,而一个session首进程可能会有一个控制终端。
一个进程组可能会有一个进程组首进程。进程组首进程的进程ID与该进程组ID相等。
这儿是可能会有,在一定情况之下是没有的。
与终端交互的进程是前台进程,否则便是后台进程
SIGHUP会在以下3种情况下被发送给相应的进程:
1、终端关闭时,该信号被发送到session首进程以及作为job提交的进程(即用 &
符号提交的进程)
2、session首进程退出时,该信号被发送到该session中的前台进程组中的每一个进程
3、若父进程退出导致进程组成为孤儿进程组,且该进程组中有进程处于停止状态(收到SIGSTOP或SIGTSTP信号),该信号会被发送到该进程组中的每一个进程。
系统对SIGHUP信号的默认处理是终止收到该信号的进程。所以若程序中没有捕捉该信号,当收到该信号时,进程就会退出。
#include&signal.h&   
设置某一信号的对应动作   
函数原型 :
void (*signal(int signum,void(* handler)(int)))(int);   
或者:typedef void(*sig_t) ( int );   sig_t signal(int signum,sig_t
handler);   
参数说明:  
第一个参数signum指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。  
第二个参数handler描述了与信号关联的动作,它可以取以下三种值:   
(1)一个返回值为正数的函数地址  此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为sig的信号时,就执行handler
所指定的函数。这个函数应有如下形式的定义:   intfunc(int sig);
  sig是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制权返回进程被中断的那一点继续执行。  
(2)SIGIGN   这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。   
(3)SIGDFL   这个符号表示恢复系统对信号的默认处理。   
函数说明 :   signal()会依参数signum
指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。当一个信号的信号处理函数执行时,  如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断。  
返回值: 返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。   
:在信号发生跳转到自定的handler处理函数执行后,系统会自动将此处理函数换回原来系统预设的处理方式,如果要改变此操作请改用sigaction()。  
下面的情况可以产生Signal:  
1. 按下CTRL+C产生SIGINT   
2. 硬件中断,如除0,非法内存访问(SIGSEV)等等   
3. Kill函数可以对进程发送Signal   
4. Kill命令。实际上是对Kill函数的一个包装   
5. 中断。如当Alarm
Clock超时(SIGURG),当Reader中止之后又向管道写数据(SIGPIPE),等等   
2 Signals:
Description
由调用abort函数产生,进程非正常退出
用alarm函数设置的timer超时或setitimer函数设置的interval
某种特定的硬件异常,通常由内存访问引起
由Solaris Thread Library内部使用,通常不会使用
进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略
当被stop的进程恢复运行的时候,自动发送
和实现相关的硬件异常
数学相关的异常,如被0除,浮点溢出,等等
Solaris专用,Hiberate或者Suspended时候发送
发送给具有Terminal的Controlling
Process,当terminal被disconnect时候发送
非法指令异常
BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground
Group的进程
由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround
Group的进程
异步IO事件
实现相关的硬件异常,一般对应SIGABRT
无法处理和忽略。中止某个进程
由Solaris Thread Libray内部使用
在reader中止之后写Pipe的时候发送
当某个事件发送给Pollable Device的时候发送
Setitimer指定的Profiling Interval Timer所产生
和系统相关。和UPS相关。
输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程
非法内存访问
Linux专用,数学协处理器的栈异常
中止进程。无法处理和忽略。
非法系统调用
请求中止进程,kill命令缺省发送
Solaris专用,从Suspend恢复时候发送
实现相关的硬件异常。一般是调试异常
Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程
当Background Group的进程尝试读取Terminal的时候发送
当Background Group的进程尝试写Terminal的时候发送
当out-of-band data接收的时候可能发送
用户自定义signal 1
用户自定义signal 2
setitimer函数设置的Virtual Interval Timer超时的时候
SIGWAITING
Solaris Thread Library内部实现专用
当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程
当CPU时间限制超时的时候
进程超过文件大小限制
Solaris专用,进程超过资源限制的时候发送
  1、不要使用低级的或者STDIO.H的IO函数  2、不要使用对操作  3、不要进行   4、不是浮点信号的时候不要用longjmp
  5、singal函数是由ISO C定义的。因为ISO
C不涉及多进程,进程组以及终端I/O等,所以他对信号的定义非常含糊,以至于对UNIX系统而言几乎毫无用处。  备注:因为singal的语义于现实有关,所以最好使用sigaction函数替代本函数。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。ssj234 的BLOG
用户名:ssj234
文章数:20
访问量:28958
注册日期:
阅读量:1950
阅读量:46793
51CTO推荐博文
1. 信号信号是软中断,用于一个或多个进程之间传递异步信号,如:按下某些终端键,硬件中断等。2常见信号SIGHUP
从终端上发出的结束信号SIGINT
来自键盘的中断信号SIGQUIT 来自键盘的退出信号SIGFPE
浮点异常信号3对信号的处理&1& 忽略该信号&2& 执行系统默认动作&3& 捕捉信号4发送与捕捉信号kill()和raise()---------------kill------------------头文件#include&sys/types.h& #include&signal.h& 函数原型int kill(pid_t pid,int sig); 函数说明kill()可以用来送参数sig指定的信号给参数pid指定的进程。参数pid有几种情况:   pid&0 将信号传给进程识别码为pid 的进程。   pid=0 将信号传给和目前进程相同进程组的所有进程   pid=-1 将信号广播传送给系统内所有的进程   pid&0 将信号传给进程组识别码为pid绝对值的所有进程 sig 信号种类返回值 执行成功则返回0,如果有错误则返回-1。 ---------------raise------------------头文件 #include&signal.h& #include&sys/types.h& 函数原型 int raise(int sig) 函数说明sig 系统信号 向正在执行的程序发送一个信号5代码kill父进程向子进程发送信号,
#include&sys/types.h&&#include&sys/stat.h&&#include&signal.h&&#include&unistd.h&&#include&stdio.h&&#include&stdlib.h&&&int&main()&{&&&&&&&&&pid_t&&&&&&pid=fork();&&&&&if(pid&0)&&&&&&&&&{&&&&&&&&&&&&&perror(&fork()&error\n&);&&&&&&&&&}&&&&&if(pid==0)&&&{&&&&&&printf(&son&process&wait&for&signal!\n&);&&&&&&pause();&&&&}else&&&{&&&&&sleep(3);&&&&&kill(pid,SIGKILL);&&&&&printf(&parent&process&send&signal!\n&);&&&&&waitpid(pid,NULL,0);&&&&printf(&son&process&exit!\n&);&&&&exit(0);&&&}&}&
6输出son process wait for signal! parent process send signal! son process exit!
了这篇文章
类别:未分类┆阅读(0)┆评论(0)进程间通信方式之:信号signal-linux-电脑编程网进程间通信方式之:信号signal作者:佚名 和相关&&
一、信号概述(1)(无名)管道pipe、命名管道FIFO、消息队列queue是用户空间内进程间的通信,而信号则提供了这样一种机制,它可以直接进行用户空间进程和内核空间进程的交互.信号是一种软件中断机制,即当信号发生时,必须用中断的方法告诉内核"请执行下列操作".(2)在终端内输入"#kill -l"可以查看系统所支持的信号.可以看出,每个信号的名字都是以SIG开头.在头文件signal.h(/usr/include/bits/signum.h)中,这些信号都被定义为正整数,即每个信号和一个数字编码相对应.(3)产生信号的条件:&&1.当用户按下某些终端键时可以产生信号.比如按下ctrl+c可以产生中断信号SIGINT..利用kill()函数将信号发送给另一个进程或进程组.&&&&1&进程组:比如父进程产生子进程后,父子进程均属于一个进程组.&& &2&kill命令也是调用kill()函数来发送信号的.& 2.当检测到某种软件条件已发生,并将其通知有关进程时,也产生信号.& 3.硬件异常也可以产生信号.比如数据运算时,除数为0;或者无效的存放访问等.这些条件通常由硬件检测到,并通知内核,然后内核为该条件发生时正在运行的进程产生适当的信号.(4)若内核(空间)向用户空间(进程)发出某个信号时,用户空间(进程)可按照下列3中方式来面对:&&1.忽略此信号&&&大多数信号都可以使用这种方式处理,但信号SIGKILL和SIGSTOP绝不能被忽略.因为它们向超级用户提供了一种使进程终止的可靠方法.&&2.执行信号的默认动作.大多数信号的系统默认动作是终止在进程.&&3.用户可以自己编写函数来处理该信号.&&& 二、信号的API(1)传送信号给指定进程&& int kill(pid_t pid,int sig)&& 函数功能:该函数可以将参数sig指定的信号传给参数pid指定的进程.&& 参数pid: &1&pid&0 :将信号传给进程识别码为pid的进程.&&&&&&&&&&&2&pid=0 :将信号传给和当前进程相同进程族的所有进程.比如父进程产生子进程后,父子进程均属于一个进程组.&&&&&&&&& &3&pid=-1:将信号广播传送给系统中所有的进程.&&&&&&&&&&&4&pid&0 :将信号传给进程组识别码为pid绝对值的所有进程.&& 参数sig: 传递的信号.由于信号在linux中以一个数字编码相对应,因此该参数为int型.&& 例程:主进程向子进程发信号以终止子进程.&&&#include &stdio.h&&& #include &stdlib.h&&& #include &unistd.h&&& #include &string.h&&& #include &sys/stat.h&&& #include &sys/types.h&&& #include &signal.h&
&& int main(void)& {&&& &pid_&&&& pid=fork();&&&& if(pid&0)&&&&&&& perror("fork");&&&& else if(0==pid)&&& {&&&&&&& while(1)&&&&&& {&&&&&&&&&& printf("hello i am child process\n");&&&&&&&&&& sleep(1);&&&&&& }&&&& }&&else&&{&&&&& sleep(3);&&&&& printf("i am a father process,i will send signal now\n");&&&&& kill(pid,SIGINT);&& //也可以写成:kill(pid,2)&&}&&return 0;&}(2)向自己发送一信号&& int raise(int sig)&& 函数功能:该函数可以向自己发送一个sig信号.当然也可以使用kill()函数来实现.
&&&例程:
&& int main(void)&& {&&&&& int i=0;&&&&& while(1)&&&&& {&&&&&&&&&i++;&&&&&&&& if(i==3)&&&&&&&&&&& raise(SIGINT);&&&&&&&& printf("i am father process\n");&&&&&&&& sleep(1);&&&&&&}&&&&&&return 0;&& }&& 执行结果:当i=3时,函数raise()向自己进程发送了终止信号,因此只打印两条信息.&& i am father process&& i am father process&& Attention!!!&& 在linux的64个信号中,大多数在默认情况下都是终止当前信号.包括SIGINT,当到了定时时间后,内核发出SIGINT信号,该信号会终止当前进程.(3)设置信号传送闹铃&& unsigned int alarm(unsigned int seconds)&& 函数功能:用于设置信号SIGALRM经过参数seconds所指定的秒数后传送给当前进程.&& 例程:&& int main(void)&& {&&&&& if(alarm(3)&0)&
//当调用该函数时,系统就启动了定时器,定时到3s后向当前进程发送一个SIGALRM信号.//该信号默认情况下是终止当前进程.&&&&& perror("alarm");&&&&& while(1)&&&&& {&&&&&&&&& sleep(1);&&&&&&&&& printf("i am father process\n");&&&&&&}&&&&& return 0;&& }&& 执行结果:&& i am father process&& i am father process&& Attention!!!&& 在linux的64个信号中,大多数在默认情况下都是终止当前信号.包括SIGALRM,当到了定时时间后,内核发出SIGALRM信号,该信号会终止当前进程.(4)让进程暂停直到信号出现&& int pause(void)&& 函数功能:用于将调用进程挂起直到捕捉到信号为止.该信号可以判断信号是否已到.&& 例程:
&& int main(void)&& {&&&&& pid_&&&&& pid=fork();&&&&& if(pid&0)&&&&&&&& perror("fork");&&&&& else if(0==pid)&&&&& {&&&&&&&& if(pause()&0)&&&&&&&&&&& perror("pause");&&&&&&&& while(1)&&&&&&&& {&&&&&&&&&&&&printf("hello i am child process\n");&&&&&&&&&&& sleep(1);&&&&&&&& }&&&&& }&&&&& else&&&&& {&&&&&&&&&&& sleep(3);&&&&&&&&&&& printf("i am a father process,i will send signal now\n");&&&&&&&&&&& kill(pid,SIGINT);&&&&&&}&&&&& return 0;&& }&& 执行结果:&& 系统在延迟3s后打印输出"i am a father process,i will send signal now",然后结束当前进程.&& 注意,程序并不会打印输出"hello i am child process".&& Attention!!!&& 在linux的64个信号中,大多数在默认情况下都是终止当前信号.包括SIGALRM,当到了定时时间后,内核发出SIGALRM信号,该信号会终止当前进程.
三、信号的处理函数&&& 在linux中,对于信号的处理主要有两种.一种是使用signal()函数,另一种是使用信号集函数组.(1)使用signal()函数&&& 函数原型: void (*signal(int signo,void (*func)(int)))(int)&&& 函数功能: 该函数会根据函数指针func所指向的函数来处理参数signo所指定的信号.&&& 第一个参数:signo是一个整数&&& 第二个整数:是函数指针,它所指向的就是需要处理该信号的函数.&&& 返回值:是一个函数地址,返回上一次的信号处理函数的指针.若出错,则返回SIG_ERR.&&& 例程:&&&& void my_func1(int sign_no)&&&& {&&&&&&&&if(sign_no==SIGINT)&&&&&&&&&& printf("ni hao\n");&&&&&&& else if(sign_no==SIGQUIT)&&&&&&&&&& printf("hello world \n");&&&& }
&&&& void my_func2(int sign_no)&&&& {&&&&&&& if(sign_no==SIGINT)&&&&&&&&&& printf("love china\n");&&&&&&& else if(sign_no==SIGQUIT)&&&&&&&&&& printf("kill japanese\n");&&&&& }
&&&&& int main()&&&&& {& &&&&&&&& void (*p)(int sig_no);&&&&&&&& //定义参数为int型的函数指针p&&&&&&&& printf("Waiting for SIGINT of SIGQUIT\n");&&&&&&&& signal(SIGINT,my_func1);&&&&&& //使用函数my_func1来处理信号SIGINT&&&&&&&& pause();&&&&&&&&&&&&&&&&&&&&&& //等待用户输入SIGINT类的信号,比如ctrl+c&&&&&&&&&&& p=signal(SIGINT,my_func2);&&&&
//使用函数my_func2来处理信号SIGINT,并将返回值赋给p//signal()返回上一次信号处理函数的指针,而上一次处理函数是my_func1,因此p指向函数my_func1.&&&&&&&& pause();&&&&&&&&&&&&&&&&&&&&&&
//等待用户输入SIGINT类的信号,比如ctrl+c
&&&&&&&& signal(SIGQUIT,p);&&&&&
//使用函数my_func1来处理信号SIGQUIT&&&&&&&& pause();&&&&&&&& exit(0);&&&&&& }&& 执行结果:&& Waiting for SIGINT of SIGQUIT&& ni hao&&&&&&&&&& //按下ctrl+c (SIGINT类信号)&& love china&&&&&& //按下ctrl+c (SIGINT类信号)&& hello world&&&&& //按下ctrl+\ (SIGQUIT类信号)&& Attention!!!&& 该函数的第二个参数func是信号处理函数,它有3中写法:&& 1、SIG_IGN :忽略该信号&& 2、SIG_DEL :采用默认方式处理该函数.在linux中多数信号信号的默认是终止当前进程.&& 3、自定义信号处理函数 :如上例所示.(2)信号集函数组&&& 在(1)中的"使用signal()函数来处理信号",每次只能处理一个信号.如果批量处理,则会显得非常麻烦.因此,在linux中就支持信号集函数组来批处理信号.这是我自己的理解.当然,为了支持信号集处理,需要下面几个函数.&&& 1、 int sigemptyset(sigset_t *set)&&&&&&& 初始化信号集合set,并将其设置为空,即信号集合中无信号.&&& 2、 int sigfillset(sigset_t *set)&&&&&&& 初始化信号集合,将信号集合设置为所有信号的集合.比如将linux中的64个信号都放在信号集合set内.&&& 3、 int sigaddset(sigset_t *set,int signo)&&&&&&& 将信号signo加入到信号集合set中.&&& 4、 int sigdelset(sigset_t *set,int signo)&&&&&&& 将信号signo从信号集中删除.&&& 5、 int sigismember(sigset_t *set int signo)&&&&&&& 查询信号signo是否在信号集合set中.&&& 6、 int sigprocmask(int how,const sigset_t *set,sigset_t *oset)&&&&&&& 将指定的信号集合加入到进程的信号阻塞集合中,或从中删除.第一个参数how决定了函数的操作方式:&&&&&&&&SIG_BLOCK:增加一个信号集合到当前进程的阻塞集合中.&&&&&&&&SIG_UNBLOCK:从当前阻塞集合中删除一个信号集合.&&&&&&&&SIG_SETMASK:将当前信号集合设置为信号阻塞集合.&&&&&&&&第二个参数set:阻塞信号的集合.&&&&&&&&第三个参数oset:若提供了oset,则当前进程信号阻塞集合将会保存在oset中.若不想保存,则设置为NULL.&&&例程:将信号SIGINT加入到阻塞信号集合中,再将信号SIGINT从阻塞信号集合中删除.&&&&& int main(void)&&&&&{&&&&&&&& sigset_&&&&&&&&&&&& //设置阻塞集合intmask&&&&&&&&&&&&&&&& sigemptyset(&intmask);&&&&&&& //初始化阻塞集合,并清空&&&&&&&& sigaddset(&intmask,SIGINT);&& //将信号SIGINT加入到阻塞集合中,即ctrl+c(SIGINT信号的一种)不再有效.&&&&&&&& while(1)&&&&&&& {&&&&&&&&&& fprintf(stdout,"SIGINT signal blocked\n");&&&&&&&&&& sigprocmask(SIG_BLOCK,&intmask,NULL); //添加阻塞集合intmask到系统的阻塞集合中&&&&&&&&&& for(i=0;i&10;i++)&
//每秒打印出字符串,在此期间执行ctrl+c(SIGINT信号的一种),程序无法停止//SIGINT信号被加入了系统的阻塞集合中,因此系统将不会对该信号作出反应&&&&&&&&&&{&&&&&&&&&&&&& fprintf(stdout,"Blocked calculation is finished\n");&&&&&&&&&&&&& sleep(1);&&&&&&&&&&}&&&&&&& fprintf(stdout,"SIGINT signal unblocked\n");&&&&&&& sigprocmask(SIG_UNBLOCK,&intmask,NULL); //将阻塞集合intmask从系统的阻塞集合中删除&&&&&&& for(i=0;i&10;i++)&
//每秒打印出字符串,在此期间执行ctrl+c(SIGINT信号的一种),程序可以停止//SIGINT信号从了系统的阻塞集合中删除了,因此系统对该信号将作出反应&&&&&&& {&&&&&&&&&&& fprintf(stdout,"Unblocked calculation is finished\n");&&&&&&&&&&& sleep(1);&&&&&&& }&&&& }&&&& exit(0);&&& }&&
相关资料:|||||||进程间通信方式之:信号signal来源网络,如有侵权请告知,即处理!编程Tags:                &                     下载
 收藏
该文档贡献者很忙,什么也没留下。
 下载此文档
正在努力加载中...
linux信号Linux下Signal信号太详细了,终于找到了
下载积分:2000
内容提示:linux信号Linux下Signal信号太详细了,终于找到了
文档格式:PDF|
浏览次数:177|
上传日期: 02:54:55|
文档星级:
该用户还上传了这些文档
linux信号Linux下Signal信号太详细了,终于找到了.PDF
道客巴巴认证
机构认证专区
加  展示
享受成长特权
官方公共微信当前访客身份:游客 [
爱这个社区,就让它:火起来!
:引用来自“laojiadingxi”的评论我的不行,su没问...
:引用来自“阳光灿烂的_子”的评论释出...哥们是台...
:释出...哥们是台湾的吗 赞rust
:被墙了。看不成
:引用来自“allenkoo”的评论强大的妈妈! 确实...
:强大的妈妈!
:我的不行,su没问题,第四步出现问题,就是mount...
:Good.解决了我的问题,我也想要一个简单的密码。...
:invalid url
:如果是普通用戶呢,怎麼修改自己的密碼?沒有sud...
今日访问:0
昨日访问:32
本周访问:0
本月访问:1334
所有访问:177284
linux信号处理
发表于4年前( 16:38)&&
阅读(1307)&|&评论()
0人收藏此文章,
1. 信号概念信号是进程在运行过程中,由自身产生或由进程外部发过来的消息(事件)。信号是硬件中断的软件模拟(软中断)。每个信号用一个整型常量宏表示,以SIG开头,比如SIGCHLD、SIGINT等,它们在系统头文件&signal.h&中定义,也可以通过在shell下键入kill –l查看信号列表,或者键入man 7 signal查看更详细的说明。信号的生成来自内核,让内核生成信号的请求来自3个地方:l&&&&&&&& 用户:用户能够通过输入CTRL+c、Ctrl+\,或者是终端驱动程序分配给信号控制字符的其他任何键来请求内核产生信号;l&&&&&&&& 内核:当进程执行出错时,内核会给进程发送一个信号,例如非法段存取(内存访问违规)、浮点数溢出等;l&&&&&&&& 进程:一个进程可以通过系统调用kill给另一个进程发送信号,一个进程可以通过信号和另外一个进程进行通信。由进程的某个操作产生的信号称为同步信号(synchronous signals),例如除0;由象用户击键这样的进程外部事件产生的信号叫做异步信号。(asynchronous signals)。进程接收到信号以后,可以有如下3种选择进行处理:l&&&&&&&& 接收默认处理:接收默认处理的进程通常会导致进程本身消亡。例如连接到终端的进程,用户按下CTRL+c,将导致内核向进程发送一个SIGINT的信号,进程如果不对该信号做特殊的处理,系统将采用默认的方式处理该信号,即终止进程的执行;l&&&&&&&& 忽略信号:进程可以通过代码,显示地忽略某个信号的处理,例如:signal(SIGINT,SIGDEF);但是某些信号是不能被忽略的,l&&&&&&&& 捕捉信号并处理:进程可以事先注册信号处理函数,当接收到信号时,由信号处理函数自动捕捉并且处理信号。有两个信号既不能被忽略也不能被捕捉,它们是SIGKILL和SIGSTOP。即进程接收到这两个信号后,只能接受系统的默认处理,即终止线程。2. signal信号处理机制可以用函数signal注册一个信号捕捉函数。原型为:#include &signal.h&typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);signal的第1个参数signum表示要捕捉的信号,第2个参数是个函数指针,表示要对该信号进行捕捉的函数,该参数也可以是SIG_DEF(表示交由系统缺省处理,相当于白注册了)或SIG_IGN(表示忽略掉该信号而不做任何处理)。signal如果调用成功,返回以前该信号的处理函数的地址,否则返回SIG_ERR。sighandler_t是信号捕捉函数,由signal函数注册,注册以后,在整个进程运行过程中均有效,并且对不同的信号可以注册同一个信号捕捉函数。该函数只有一个参数,表示信号值。示例:1、& 捕捉终端CTRL+c产生的SIGINT信号:#include &unistd.h&#include &stdio.h&#include &sys/wait.h&#include &sys/types.h&void SignHandler(int iSignNo){printf(&Capture sign no:%d\n&,iSignNo);}int main(){signal(SIGINT,SignHandler);while(true)sleep(1);return 0;}该程序运行起来以后,通过按 CTRL+c将不再终止程序的运行。应为CTRL+c产生的SIGINT信号已经由进程中注册的SignHandler函数捕捉了。该程序可以通过 Ctrl+\终止,因为组合键Ctrl+\能够产生SIGQUIT信号,而该信号的捕捉函数尚未在程序中注册。2、& 忽略掉终端CTRL+c产生的SIGINT信号:#include &unistd.h&#include &stdio.h&#include &sys/wait.h&#include &sys/types.h&int main(){signal(SIGINT,SIG_IGN);while(true)sleep(1);return 0;}该程序运行起来以后,将CTRL+C产生的SIGINT信号忽略掉了,所以CTRL+C将不再能是该进程终止,要终止该进程,可以向进程发送SIGQUIT信号,即组合键CTRL+\3、& 接受信号的默认处理,接受默认处理就相当于没有写信号处理程序:#include &unistd.h&#include &stdio.h&#include &sys/wait.h&#include &sys/types.h&int main(){signal(SIGINT,DEF);while(true)sleep(1);return 0;}3. sigaction信号处理机制3.1. 信号处理情况分析在signal处理机制下,还有许多特殊情况需要考虑:1、& 册一个信号处理函数,并且处理完毕一个信号之后,是否需要重新注册,才能够捕捉下一个信号;2、& 如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个同类型的信号,这时该怎么处理;3、& 如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个不同类型的信号,这时该怎么处理;4、& 如果程序阻塞在一个系统调用(如read(...))时,发生了一个信号,这时是让系统调用返回错误再接着进入信号处理函数,还是先跳转到信号处理函数,等信号处理完毕后,系统调用再返回。示例:#include &stdio.h&#include &string.h&#include &unistd.h&#include &signal.h&int g_iSeq=0;void SignHandler(int iSignNo){int iSeq=g_iSeq++;printf(&%d Enter SignHandler,signo:%d.\n&,iSeq,iSignNo);sleep(3);printf(&%d Leave SignHandler,signo:%d\n&,iSeq,iSignNo);}int main(){char szBuf[8];int iRsignal(SIGINT,SignHandler);signal(SIGQUIT,SignHandler);do{iRet=read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);if(iRet&0){perror(&read fail.&);}szBuf[iRet]=0;printf(&Get: %s&,szBuf);}while(strcmp(szBuf,&quit\n&)!=0);return 0;}程序运行时,针对于如下几种输入情况(要输入得快),看输出结果:1、& CTRL+c] [CTRL+c] [CTRL+c]2、& [CTRL+c] [CTRL+\]3、& hello [CTRL+\] [Enter]4、& [CTRL+\] hello [Enter]5、& hel [CTRL+\] lo[Enter]针对于上面各种情况,不同版本OS可能有不同的响应结果。3.2. sigaction信号处理注册如果要想用程序控制上述各种情况的响应结果,就必须采用新的信号捕获机制,即使用sigaction信号处理机制。函数原型:#include &signal.h&int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);sigaction也用于注册一个信号处理函数。参数signum为需要捕捉的信号;参数 act是一个结构体,里面包含信号处理函数地址、处理方式等信息。参数oldact是一个传出参数,sigaction函数调用成功后,oldact里面包含以前对signum的处理方式的信息。如果函数调用成功,将返回0,否则返回-1结构体 struct sigaction(注意名称与函数sigaction相同)的原型为:struct sigaction {void (*sa_handler)(int);&&&&&&&& // 老类型的信号处理函数指针void (*sa_sigaction)(int, siginfo_t *, void *);//新类型的信号处理函数指针sigset_t sa_&&&&&&&&&&&&&&&& // 将要被阻塞的信号集合int sa_&&&&&&&&&&&&&&&&&&&&&&&& // 信号处理方式掩码void (*sa_restorer)(void);&&&& // 保留,不要使用。}该结构体的各字段含义及使用方式:1、字段sa_handler是一个函数指针,用于指向原型为void handler(int)的信号处理函数地址,&&&&&& 即老类型&&&&&& 的信号处理函数;2、字段sa_sigaction也是一个函数指针,用于指向原型为:void handler(int iSignNum,siginfo_t *pSignInfo,void *pReserved);的信号处理函数,即新类型的信号处理函数。该函数的三个参数含义为:iSignNum :传入的信号pSignInfo :与该信号相关的一些信息,它是个结构体pReserved :保留,现没用3、字段sa_handler和sa_sigaction只应该有一个生效,如果想采用老的信号处理机制,就应该让sa_handler指向正确的信号处理函数;否则应该让sa_sigaction指向正确的信号处理函数,并且让字段 sa_flags包含SA_SIGINFO选项。4、字段sa_mask是一个包含信号集合的结构体,该结构体内的信号表示在进行信号处理时,将要被阻塞的信号。针对sigset_t结构体,有一组专门的函数对它进行处理,它们是:#include &signal.h&int sigemptyset(sigset_t *set);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& // 清空信号集合setint sigfillset(sigset_t *set);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& // 将所有信号填充进set中int sigaddset(sigset_t *set, int signum);&&&&&&&&&&&&&& // 往set中添加信号signumint sigdelset(sigset_t *set, int signum);&&&&&&&&&&&&&&& // 从set中移除信号signumint sigismember(const sigset_t *set, int signum); // 判断signnum是不是包含在set中例如,如果打算在处理信号SIGINT时,只阻塞对SIGQUIT信号的处理,可以用如下种方法:sigemptyset(&act.sa_mask);sigaddset(&act_sa_mask,SIGQUIT);sigaction(SIGINT,&act,NULL);5、& 字段sa_flags是一组掩码的合成值,指示信号处理时所应该采取的一些行为,各掩码的含义为:掩码描述SA_RESETHAND处理完毕要捕捉的信号后,将自动撤消信号处理函数的注册,即必须再重新注册信号处理函数,才能继续处理接下来产生的信号。该选项不符合一般的信号处理流程,现已经被废弃。SA_NODEFER在处理信号时,如果又发生了其它的信号,则立即进入其它信号的处理,等其它信号处理完毕后,再继续处理当前的信号,即递规地处理。如果 sa_flags包含了该掩码,则结构体sigaction的sa_mask将无效!SA_RESTART如果在发生信号时,程序正阻塞在某个系统调用,例如调用read()函数,则在处理完毕信号后,接着从阻塞的系统返回。该掩码符合普通的程序处理流程,所以一般来说,应该设置该掩码,否则信号处理完后,阻塞的系统调用将会返回失败!SA_SIGINFO指示结构体的信号处理函数指针是哪个有效,如果sa_flags包含该掩码,则sa_sigactiion指针有效,否则是 sa_handler指针有效。练习与验证:针对于先前的5种输入情况,给下面代码再添加一些代码,使之能够进行如下各种形式的响应:1 、[CTRL+c] [CTRL+c]时,第1个信号处理阻塞第2个信号处理;2 、[CTRL+c] [CTRL+c]时,第1个信号处理时,允许递规地第2个信号处理;3 、[CTRL+c] [CTRL+\]时,第1个信号阻塞第2个信号处理;4 、read不要因为信号处理而返回失败结果。#include &stdio.h&#include &string.h&#include &unistd.h&#include &signal.h&int g_iSeq=0;void SignHandlerNew(int iSignNo,siginfo_t *pInfo,void *pReserved){int iSeq=g_iSeq++;printf(&%d Enter SignHandlerNew,signo:%d.\n&,iSeq,iSignNo);sleep(3);printf(&%d Leave SignHandlerNew,signo:%d\n&,iSeq,iSignNo);}int main(){char szBuf[8];int iRact.sa_sigaction=SignHandlerNact.sa_flags=SA_SIGINFO;//sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL);sigaction(SIGQUIT,&act,NULL);do{iRet=read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);if(iRet&0){perror(&read fail.&);}szBuf[iRet]=0;printf(&Get: %s&,szBuf);}while(strcmp(szBuf,&quit\n&)!=0);return 0;}3.3. sigprocmask信号阻塞函数sigaction中设置的被阻塞信号集合只是针对于要处理的信号,例如sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask,SIGQUIT);sigaction(SIGINT,&act,NULL);表示只有在处理信号SIGINT时,才阻塞信号SIGQUIT;函数sigprocmask是全程阻塞,在sigprocmask中设置了阻塞集合后,被阻塞的信号将不能再被信号处理函数捕捉,直到重新设置阻塞信号集合。原型为:#include &signal.h&int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);参数how的值为如下3者之一:a :SIG_BLOCK ,将参数2的信号集合添加到进程原有的阻塞信号集合中b :SIG_UNBLOCK ,从进程原有的阻塞信号集合移除参数2中包含的信号c :SIG_SET,重新设置进程的阻塞信号集为参数2的信号集参数set为阻塞信号集参数oldset是传出参数,存放进程原有的信号集。示例:#include &stdio.h&#include &string.h&#include &unistd.h&#include &signal.h&int g_iSeq=0;void SignHandlerNew(int iSignNo,siginfo_t *pInfo,void *pReserved){int iSeq=g_iSeq++;printf(&%d Enter SignHandlerNew,signo:%d.\n&,iSeq,iSignNo);sleep(3);printf(&%d Leave SignHandlerNew,signo:%d\n&,iSeq,iSignNo);}int main(){char szBuf[8];int iRact.sa_sigaction=SignHandlerNact.sa_flags=SA_SIGINFO;// 屏蔽掉SIGINT 信号,SigHandlerNew 将不能再捕捉SIGINTsigset_t sigSsigemptyset(&sigSet);sigaddset(&sigSet,SIGINT);sigprocmask(SIG_BLOCK,&sigSet,NULL);//sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL);sigaction(SIGQUIT,&act,NULL);do{iRet=read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);if(iRet&0){perror(&read fail.&);}szBuf[iRet]=0;printf(&Get: %s&,szBuf);}while(strcmp(szBuf,&quit\n&)!=0);return 0;}4. 用程序发送信号4.1. kill信号发送函数原型为:#include &sys/types.h&#include &signal.h&int kill(pid_t pid, int sig);参数pid为将要接受信号的进程的pid参数sig为要发送的信号如果成功,返回0,否则为-1。示例,输入结束后,将通过发送信号SIGQUIT把自己杀掉:#include &stdio.h&#include &signal.h&#include &unistd.h&#include &sys/types.h&int main(){while(true){if(getchar()==EOF)kill(getpid(),SIGQUIT);}return 0;}4.2. sigqueue信号发送函数sigqueue也可以发送信号,并且能传递附加的信息。原型为:#include &signal.h&int sigqueue(pid_t pid, int sig, const union sigval value);参数pid为接收信号的进程;参数sig为要发送的信号;参数value为一整型与指针类型的联合体:union sigval {int&& sival_void *sival_};由sigqueue函数发送的信号的第3个参数value的值,可以被进程的信号处理函数的第2个参数info-&si_ptr接收到。示例1,进程给自己发信号,并且带上附加信息:#include &signal.h&#include &sys/types.h&#include &unistd.h&#include &string.h&#include &stdlib.h&#include &stdio.h&void SignHandlerNew(int signum,siginfo_t *info,void *myact){char *pszInfo=(char *)(info-&si_ptr);printf(&Get:%d info:%s\n&,signum,pszInfo);}int main(int argc,char**argv){&&uchar data[]=&other info&;//if(argc&2){printf(&usage: SIGNNUM\n&);return -1;}mysigval.sival_ptr=sig=atoi(argv[1]);sigemptyset(&act.sa_mask);act.sa_sigaction=SignHandlerNact.sa_flags=SA_SIGINFO;sigaction(sig,&act,NULL);while(true){printf(&wait for the signal\n&);sigqueue(getpid(),sig,mysigval);sleep(2);}}示例2:一个进程向另外一个进程发送信号。注意:发送进程不要将自己进程空间的地址发送给接收进程,因为接收进程接收到地址也访问不到发送进程的地址空间的。示例2信号接收程序:#include &signal.h&#include &sys/types.h&#include &unistd.h&#include &string.h&#include &stdlib.h&#include &stdio.h&void SignHandlerNew(int signum,siginfo_t *info,void *myact){printf(&Get:%d info:%d\n&,signum,info-&si_int);}int main(int argc,char**argv){//if(argc&2){printf(&usage: signnum\n&);return -1;}sigemptyset(&act.sa_mask);act.sa_sigaction=SignHandlerNact.sa_flags=SA_SIGINFO;sigaction(atoi(argv[1]),&act,NULL);while(1){printf(&wait for the signal\n&);sleep(2);}return 0;}示例2信号发送程序:#include &signal.h&#include &sys/types.h&#include &unistd.h&#include &string.h&#include &stdlib.h&#include &stdio.h&int main(int argc,char**argv){uint iPid,iSignNo,iD//if(argc&4){printf(&usage: pid signnum data\n&);return -1;}iPid=atoi(argv[1]);iSignNo=atoi(argv[2]);iData=atoi(argv[3]);mysigval.sival_int=iDif(sigqueue(iPid,iSignNo,mysigval)&0)perror(&Send signal fail.&);return 0;}&&&&5. 计时器与信号5.1. 睡眠函数Linux下有两个睡眠函数,原型为:#include &unistd.h&unsigned int sleep(unsigned int seconds);void usleep(unsigned long usec);函数sleep让进程睡眠seconds秒,函数usleep让进程睡眠usec毫秒。sleep 睡眠函数内部是用信号机制进行处理的,用到的函数有:#include &unistd.h&unsigned int alarm(unsigned int seconds);&&&& // 告知自身进程,要进程在seconds秒后自动产生一个//SIGALRM的信号,int pause(void);&&&&&&&&&&&&&&&&&&&&&& // 将自身进程挂起,直到有信号发生时才从pause返回示例:模拟睡眠3秒:#include &signal.h&#include &stdio.h&#include &unistd.h&void SignHandler(int iSignNo){printf(&signal:%d\n&,iSignNo);}int main(){signal(SIGALRM,SignHandler);alarm(3);printf(&Before pause().\n&);pause();printf(&After pause().\n&);return 0;}注意:因为sleep在内部是用alarm实现的,所以在程序中最好不要sleep与 alarm混用,以免造成混乱。5.2. 时钟处理&&Linux为每个进程维护3个计时器,分别是真实计时器、虚拟计时器和实用计时器。l&&&&&&&& 真实计时器计算的是程序运行的实际时间;l&&&&&&&& 虚拟计时器计算的是程序运行在用户态时所消耗的时间(可认为是实际时间减掉(系统调用和程序睡眠所消耗)的时间);l&&&&&&&& 实用计时器计算的是程序处于用户态和处于内核态所消耗的时间之和。例如:有一程序运行,在用户态运行了5秒,在内核态运行了6秒,还睡眠了7秒,则真实计算器计算的结果是18秒,虚拟计时器计算的是5秒,实用计时器计算的是11秒。用指定的初始间隔和重复间隔时间为进程设定好一个计时器后,该计时器就会定时地向进程发送时钟信号。3个计时器发送的时钟信号分别为:SIGALRM,SIGVTALRM和SIGPROF。用到的函数与数据结构:#include &sys/time.h&//获取计时器的设置//which指定哪个计时器,可选项为ITIMER_REAL(真实计时器)、ITIMER_VITUAL(虚拟计时器、ITIMER_PROF(实用计时器))//value为一结构体的传出参数,用于传出该计时器的初始间隔时间和重复间隔时间//如果成功,返回0,否则-1int getitimer(int which, struct itimerval *value);//设置计时器//which指定哪个计时器,可选项为ITIMER_REAL(真实计时器)、ITIMER_VITUAL(虚拟计时器、ITIMER_PROF(实用计时器))//value为一结构体的传入参数,指定该计时器的初始间隔时间和重复间隔时间//ovalue为一结构体传出参数,用于传出以前的计时器时间设置。//如果成功,返回0,否则-1int setitimer(int which, const struct itimerval *value, struct itimer val *ovalue);struct itimerval {struct timeval it_ /* next value */&&&&&&&&&&& // 重复间隔struct timeval it_&&& /* current value */&&&& // 初始间隔&&&&};struct timeval {long tv_&&&&&&&&&&&&&&& /* seconds */&&&&&&&&&&&&&&&&&&& // 时间的秒数部分long tv_&&&&&&&&&&&&&& /* microseconds */&&&&&&& // 时间的微秒部分};示例:启用真实计时器的进行时钟处理#include &stdio.h&#include &unistd.h&#include &signal.h&#include &sys/time.h&void TimeInt2Obj(int imSecond,timeval *ptVal){ptVal-&tv_sec=imSecond/1000;ptVal-&tv_usec=(imSecond%;}void SignHandler(int SignNo){printf(&Clock\n&);}int main(){signal(SIGALRM,SignHandler);TimeInt2Obj(1,&tval.it_value);&&&&&&&&&&& // 设初始间隔为1毫秒,注意不要为0TimeInt2Obj(1500,&tval.it_interval);&&& // 设置以后的重复间隔为1500毫秒setitimer(ITIMER_REAL,&tval,NULL);while(getchar()!=EOF);return 0;}
1)">1)">1" ng-class="{current:{{currentPage==page}}}" ng-repeat="page in pages"><li class='page' ng-if="(endIndex<li class='page next' ng-if="(currentPage
相关文章阅读}

我要回帖

更多关于 linux 信号 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信