Linux编程---信号处理


信号是一种异步的进程间通信的方式.但是这种通知方式能交换的信息很少.只能说给一个事件的标志.类似单片机中的中断,强迫进程停止做当前应当做的事情,而去执行中断执行程序。
 
信号的产生有如下几种:
 
1.用户按下了某个终止键,如ctrl-\或ctrl-c.是由终端程序向当前进程发送一个中断信号.
 
2.程序异常.比如除零错误.
 
3.kill函数向其发送了一个终止信号
 
4.进程向自己发送信号.如进程调用alarm函数.
 
5.企图读写终端的后台进程会得到作业的控制信号SIGTTIN或SIGTOU.
 
6.当进程超越了CPU或文件的大小限制时,内核会生成一个信号.
 
Linux的信号处理和实际使用(结合Redis分析) 

Linux-信号处理

一步一步学Linux C:信号处理潜在危险!!!

一步一步学Linux C:信号处理方法 && 实际应用

Linux信号简介和信号处理相关函数
 
得到信号的进程也有三种方式应对.
 
1.忽略信号.大部分信号都可以忽略,但SIGSTOP和SIGKILL除外
 
2.捕获信号.这就要进程对信号进行专门的设置了.
 
3.执行系统默认动作.这个主要是系统默认对信号规定的默认动作.
 
---流产: 终止进程并且生成内存转储文件.即产生存储上下文环境,寄存器内容等信息的core文件.(abort函数)
 
---终止: 这个不产生core文件
 
---忽略: 忽略信号
 
---挂起: 暂停进程
 
---继续: 恢复进程.
 
 
 
对于进程正处在可中断的睡眠状态并且这个信号是非阻塞的,内核便唤醒此进程使它能够接受信号.
 
对于信号,进程可以立即处理,也可以选择让其悬挂.对于不可靠信号,一个进程对同一种信号只能悬挂一个.也就是信号可以存储,但是只能存一个.对于可靠信号,信号来几个就执行几次信号句柄,信号可以排队,所以可以用来计数.
 
对于小于SIGRTMIN的信号为不可靠信号,SIGRTMIN-SIGRTMAX为可靠信号.实际上实时对应可靠,非实时对应不可靠.
 
 
 
这里是我抄网上的,测试版本为Ubuntu 12.04LTS.我之前也不太明白实时和非实时,可靠和不可靠信号的差别.如果理解有误还请指出.
 
 
 
这里介绍一个NSIG宏.这个宏记录了系统中允许的信号总数+1.
 
 
 
void psignal(int signo,const char *msg);
 
这个函数我第一次见...网上貌似也没资料.....
 
若msg是控制正,函数只打印signo对应的描述,并随后带上一个换行符.
 
若msg是非空指针,平函数用这个字符串作为其输出消息的前缀,并且在此前缀和signo对应的消息之间放置一个冒号和空格将他们分开.
 
 
 
简单来说就是查询信号用途的函数...
 
 
 
不同信号类型中的信号也蛮多的...具体的还是以后编程遇到后慢慢记吧.这里就不写了.
 
 
 
还是写一些函数的说明吧...
 
 
 
一.信号生成
 
int raise(int sig);
 
这个函数功能就是发送sig到调用进程...
 
如果sig信号设置了句柄,那么raise只有在句柄函数返回时才会返回.
 
 
 
int kill(pid_t pid,int sig);
 
这个函数用的比较多吧.虽说叫kill,但实际上是发送信号的函数.并不一定只能发送kill.
 
如果sig为0,那么kill只进行正常的错误检查而不实际发送信号,这常常用来检查pid的合法性.如果发送信号的进程可能不在的话,那么就可以这么用.
 
pid实际上参数用法还蛮多的.
 
>0  发送信号给pid进程
 
=0  发送给进程所在的进程组的所有进程.
 
=-1 广播信号,即发送给能发送的所有进程.
 
<-1 发送信号给进程组ID等于pid绝对值.并且发送者有权向它发送信号的所有进程.
 
 
 
这个pid的取值和之前的wait很像.
 
 
 
这里还提到了权限的问题.一般进程的实际用户ID或有效用户ID必须与接受信号进程的实际用户ID或有效用户ID相同.对于有效ID是超级用户的进程来说,可以向任何进程发送信号.
 
 
 
 
 
二.设置信号动作.
 
typedef void (*sighandler_t)(int );
 
sighandler_t signal(int signum,sighandler_t handler);
 
两个参数:signum设置型号类型.handler设置信号句柄.
 
handler其实可以取以下三种值之一:
 
SIG_DFL 默认动作
 
SIG_IGN 忽略该信号.但SIGKILL和SIGSTOP这两个信号,不能设置为忽略.尽量不要忽略系统相关的信号.
 
函数指针 就是执行这个函数体的函数.参数一般得到的是信号类型.
 
 
 
这个函数的返回值是前一次有效动作的指针.,因此它的原型与第二个参数相同.当signal调用成功时.返回值要么是SIG_DFL,SIGIGN或函数句柄.
 
 
 
如果调用出错,那么则会返回SIG_ERR并设置errno.唯一的错误吗是EINVAL
 
 
 
对于fork和exec来说,信号相关对于fork来说是继承的.
 
对exec来说则不继承.所有忽略的信号继续忽略,其他有句柄的则改为默认动作.
 
 
 
 
 
由于signal函数使用的时候存在一个时间窗口,信号很可能不可靠.所以有了下面这个信号函数.通常用它的比较多
 
int sigaction (int signum,const struct sigaction *act,struct sigaction *oact);
 
第二个参数是一个结构体,其中包含了信号句柄.第三个参数则是原来的信息.
 
struct sigaciton{
 
  void  (*sa_handler)(int);
 
  void  (*sa_sigaction)(int ,siginfo_t *,void *);
 
  sigset_t sa_mask;
 
  int    sa_flags;
 
}
 
sa_handler就是句柄.
 
sa_sigaction也是个句柄地址,当sa_flags设置了SA_SIGINFO的时候才起作用,否则用handler.
 
sa_mask指明信号句柄执行期间要阻塞的一组信号,在sigaction执行过程中会屏蔽这些信号.
 
sa_flags可取的值就多了,有以下这些:
 SA_NOCLDSTOP
 
此标志只对SIGCHLD信号有效,正常情况下,子进程被停止时,总是向父进程发送信号,有时候被停止的子进程恢复运行时,可能向父进程发送信号.当signum参数传值是SIGCHLD时,在这两种情况下都不再发送信号.
 
SA_RESTART
 
如果设置该标志并且捕获信号,系统将在信号句柄返回时自动恢复被该信号中断了的系统调用.否则被中断的系统调用将返回-1并置errno为EINTR.多数情况下sa_flags的值为SA_RESTART.
 
SA_ONSTACK
 
如果设置此标志,系统将在用sigaltstack指定的替代信号栈上运行的信号句柄.否则使用用户栈来交付信号.
 
SA_NODEFER
 
如果设置此标志并且捕获信号,在信号句柄执行期间系统不自动阻塞该信号.这对应于不可靠信号signal的情形.(估计没什么人会用这个吧...)
 
SA_RESETHAND
 
如果设置此标志并且捕获信号,在信号句柄的入口,系统将重置信号动作为SIG_DFL并清楚SA_SIGINFO标志,并且sigaction的行为就和signal差不多了.
 
SA_NOCLDWAIT
 
只对SIGCHLD起作用.如果设置此标志,并且sig是SIGCHLD,调用进程的所有子进程在终止时不转变成僵死进程.这样父进程就不必调用wait函数了.如果调用了wait则进程会一直阻塞直到所有子进程都结束才返回,返回值为-1和设置errno为ECHILD.
 
SA_SIGINFO
 
这个就是上面的情况了.未设置用sa_handler并且禁止修改sa_sigaction.
 
句柄原型必须是void f(int signo);
 
如果设置了这个标志,则必须修改sa_sigaction设置信号句柄并禁止修改sa_handler.
 
句柄原型为void f(int signo,siginfo *info,void *context);
 
这个有些复杂,不过还是可以说清楚的.
 
实际上在siginfo_t中包含了更多的信号产生的信息.context也指向了一个进程上下文环境.Linux下默认类型为sigcontext结构体.
 
这个内容好多...我就不展开了,我看APUE上有相关信息,用到再查吧.

更多详情见请继续阅读下一页的精彩内容:

  • 1
  • 2
  • 下一页

相关内容