博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
信号处理
阅读量:4982 次
发布时间:2019-06-12

本文共 3791 字,大约阅读时间需要 12 分钟。

理解概念

​ 可以用来处理进程间的异步事件——即进程间可以通过系统调用来发送信号,只是告知某进程发生了什么事,使得被告知的进程去做对应的事件(信号处理),要注意的是,发送信号的过程并不会传送任何数据。通过kill -l可以看到信号的名字和序号。

可以通过这个案例来说明:

​ 在终端运行top来查看系统运行的一些相关信息,可以看到终端的数据一直是变化的,同事通过ps -aux|grep top来查看现在系统是否正在运行该指令,可以得到运行该指令的进程号,然后用kill -9 进程号将该进程杀掉,我们此时通过ps -aux|grep top来发现此时top的运行相关信息已经没有了。这个过程就是一个进程给另一个进程发送了SIGKILL的信号。(注意:kill,就是送出一个特定的信号给某个进程,而该进程根据信号做出相应的动作(sigqueue也是),-9可以通过kill -l 看出是SIGKILL

gqx@gqx-Lenovo-Product:~$ kill -l 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1 ......

信号处理方式

一般信号的处理可以分为三种:

  • 忽略信号

    ​ 大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILLSIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景

  • 捕捉信号

    ​ 需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。

  • 系统的默认动作

    ​ 对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。

信号处理的注册函数

1. signal函数

/* Type of a signal handler.  */typedef void (*__sighandler_t) (int);/* Set the handler for the signal SIG to HANDLER, returning the old   handler, or SIG_ERR on error.   By default `signal' has the BSD semantic.  */extern __sighandler_t signal (int __sig, __sighandler_t __handler);

第一个参数表示信号量类型(对应的kill -l里的数据),第二个参数则表示该进程被告知该信号后的处理函数。参考案例如下:

#include 
#include
#include
#include
using namespace std;void timeout(int sig){ if(sig == SIGALRM){ puts("Time out!"); } alarm(2);}void keycontrol(int sig){ if(sig == SIGINT){ puts("CTRL + C pressed"); }}int main(){ int i; signal(SIGALRM, timeout); //到达通过了alarm函数设置的时间,调用函数timeout signal(SIGINT, keycontrol); //键盘键入Ctrl+后,调用keycontrol函数 alarm(2); for(i = 0; i < 6; i++){ puts("wait..."); sleep(100); } return 0;}

​ 这段代码要注意的是,在signal中注册信号函数后,调用信号函数的则是操作系统,但进程处于睡眠状态的时间为100s,而alarm函数等待的时间是2秒,即2秒后会产生SIGALRM信号,此时将唤醒处于休眠状态的进程,而且进程一旦被唤醒,则不会再进入休眠状态,所以上述程序运行时间比较短。

2. sigaction

该函数已经完全取代了上述signal函数。

struct sigaction {   void       (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作   void       (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用   sigset_t   sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。   int        sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据 };/* Get and/or set the action for signal SIG.  */extern int sigaction (int __sig, const struct sigaction *__restrict __act,              struct sigaction *__restrict __oact) __THROW;

第一个参数表示信号量类型;第二个参数信号处理函数;第三个参数:通过此参数获取之前注册的信号处理函数指针,若不需要,则传递0;

程序示例如下,改程序用来消除由父进程产生的两个子进程会导致僵尸进程的产生的情况,当子进程的生命周期结束后,回收子进程的内存信息,而不用等到父进程结束才去回收销毁子进程:

#include 
#include
#include
#include
using namespace std;void read_childproc(int sig){ int status; pid_t id = waitpid(-1, &status, WNOHANG); //消灭子进程结束后产生的僵尸进程 if(WIFEXITED(status)){ printf("Remove proc id: %d \n", id); printf("Child send: %d \n", WEXITSTATUS(status)); }}int main(){ pid_t pid; struct sigaction act; act.sa_handler = read_childproc; sigemptyset(&act.sa_mask); //将sa_mask所有位初始化为0(初始化sa_mask中传入的信号集,清空其中所有信号) act.sa_flags = 0; sigaction(SIGCHLD, &act, 0); //SIGCHLD 子进程结束信号 pid = fork(); if(pid == 0){ puts("Hi, I am a child process!"); sleep(6); return 12; }else{ printf("Child proc id: %d \n", pid); pid = fork(); if(pid == 0){ puts("Hi, I am a child process!"); sleep(13); exit(24); }else{ int i; printf("Child proc id: %d \n", pid); for(i = 0; i < 4; i++){ puts("wait..."); sleep(5); } } } return 0;}

参考资料

转载于:https://www.cnblogs.com/helloworldcode/p/10759440.html

你可能感兴趣的文章
给列表单元格加背景色
查看>>
knockoutjs 静动态数据、行为绑定,计算属性及Sync同步更新 Value值更新事件控制...
查看>>
关于.NET编程中各种事务的实现
查看>>
spring Boot加载bean
查看>>
学习笔记 UpdateXml() MYSQL显错注入
查看>>
51nod1455(dp)
查看>>
正则:校验名字,不严格校验手机号
查看>>
软件测试作业二 —— 对于Faults,Errors,Failures的认识的习题
查看>>
.net 给前台元素设置样式
查看>>
WPF学习:控件的模板
查看>>
小数据池 深浅拷贝 集合
查看>>
Hash_Map 原理
查看>>
mysql函数大全pdf
查看>>
Asp.net 2.0在Windows 2003 Server 上配置Microsoft Excel、Microsoft Word应用程序权限时 error: 8000401a 的解决方法!...
查看>>
SDUT 识别浮点常量问题 编译原理作业
查看>>
BZOJ 2819: Nim dfs序维护树状数组,倍增
查看>>
WinRAR(5.21)-0day漏洞-始末分析
查看>>
终端检测HTTPS服务端
查看>>
证件照换底色
查看>>
Candies
查看>>