本文共 4065 字,大约阅读时间需要 13 分钟。
信号:
1:信号(signal)机制是Linux系统中最为古老的进程之间的通信机制,解决进程在正常运行过程中被中断的问题,导致进程的处理流程会发生变化
2:信号是软件中断 3:信号是异步事件a:不可预见b:信号有自己的名称和编号c:信号和异常处理机制
4:信号发生的来源
a:硬件来源,如按键的事件,或者硬件故障,是由驱动程重点内容序产生b:软件来源:最常用发送信号的系统函数 kill(),raise(),alarm()和setitimer()等函数软件来源还包括一些非法运算等操作,软件设置条件如(gdb调试),信号由内核产生;
5:查看linux系统内置的信号,在前面博客中其实也提到相关信号:
使用kill -l来查看信号列表信号是没有优先级之分的;1-31称为非实时信号,发送的信号可能会丢失,不支持信号排队,32-64成为实时信号,支持信号排队,发送的多个实时信号都会被接受前面31个信号类型在linux中的/usr/include/bits/signum.h文件中会有详细定义
6: 信号的历史变革
信号出现在早期的Unix中 早期信号模型是不可靠的 BSD和System V分别对早期信号进行扩展,但是相互不兼容 POSIX统一了上述两种模型,提供了可靠信号模型
进程可以通过三种方式来响应和处理一个信号 1:忽略信号 SIGKILL和SIGSTOP永远不能忽略 忽略硬件异常 进程启动时SIGUSR1和SIGUSR2两个信号会被忽略 2:执行默认操作 每个信号有默认动作,大部分信号默认动作都是终止进程 3:捕获信号 告诉内核出现信号时调用自己的处理函数 SIGKILL和SIGSTOP不能被捕获
信号注册(接收)函数
1. signal函数signal函数
#includevoid (*signal(int signo,void(*func)(int))) (int);返回:若成功则返回先前的信号处理函数指针(func),出错返回SIG_ERR功能:向内核登记信号处理函数参数: signo 要登记的信号值,一般用信号的宏来 func 信号处理函数指针 SIG_IGN 忽略信号 SIG_DFL 采用系统默认的方式处理信号,执行默认操作 使用man signal可以查看这个函数的具体的函数定义
非常简单的实例代码
#include#include #include #include #include void signal_handler(int signo){ printf("signo:%d\n",signo);}int main(int agrc,char * argv[]){ //ctrl+c if(signal(SIGINT,signal_handler) == SIG_ERR){ perror("sigint error"); } if(signal(SIGSEGV,signal_handler) == SIG_ERR){ perror("sigsegv error"); } //SIGUSR1,SIGUSR2在不捕获的状态下,默认是忽略的 if(signal(SIGUSR2,signal_handler) == SIG_ERR){ perror("siguser1 error"); } if(signal(SIGUSR1,signal_handler) == SIG_ERR){ perror("siguser2 error"); } //sigkill和sigstop是不允许被忽略和捕获的,在注册的时候就已经会报错 if(signal(SIGKILL,SIG_DFL) == SIG_ERR){ perror("sigkill error"); } if(signal(SIGSTOP,SIG_DFL) == SIG_ERR){ perror("signal stop error"); } //ctrl+z if(signal(SIGTSTP,signal_handler) == SIG_ERR){ perror("signal tstp error"); } int i = 0; while(++i < 50){ printf("pid:%d,count:%d\n",getpid(),i); sleep(1); } return 0;}
怎么使用信号来处理僵尸进程
SIGCHILD信号 子进程状态发生改变(子进程结束)产生该信号,父进程需要使用wait调用来等待子进程结束并回收它。 避免僵尸进程子进程结束之后自动产生的该信号;当父进程捕获到SIGCHILD信号之后,一定要去调用wait(0)函数,否则子进程会成为僵尸进程优点:只有当子进程终止之后,父进程才调用wait函数,而如果父进程直接去调用wait函数的话,那这个时候可能就会导致父亲进程会直接阻塞住。这样就会导致父进程执行的效率极低
父亲进程通过注册去监听回收僵尸进程部分的代码
#include#include #include #include #include #include void singal_handler(int signo){ printf("%d occred %d\n",getpid(),signo); //释放子进程(僵尸进程) wait(NULL);}void outNum(int num){ int i = 0; for(i = 0;i 0){ //注册等级子进程deaded的时候的信号 if(signal(SIGCHLD,singal_handler)==SIG_ERR){ perror("signal child error"); } outNum(20); }else { outNum(10); } return 0;}
信号注册(接收)函数
信号发送:除了内核和超级用户,并不是每个进程都可以向其他进程发送信号一般的进程只能向具有相同uid和gid的进程发送信号或相同进程组中的其他进程发送信号常用的发送信号的函数有1:kill(),2:raise(),3:alarm(),4:setitimer(),5:abort()等
1:kill函数和raise函数
#includeint kill(pid_t pid,int signo);返回:成功返回0,出错返回-1功能,向指定的进程发送某一个信号int raise(int signo);返回:成功返回0,出错返回-1功能:向进程本身发送一个信号,相当于kill(getpid(),signo);参数: pid 接受信号的pid signo 要发送的信号值kill函数将信号发送给进程或者进程组 0为空信号,常常用来检测特定的进程是否存在参数pid取值: pid > 0 将信号发给进程ID为pid的进程 pid==0将信号发给与发送进程同一进程组的所有进程 pid<0将信号发送给进程组ID等于pid的绝对值 pid==-1将信号发送给发送进程有权限向他们发送信号的系统上的所有进程
2:alarm函数:
#includeunsigned int alarm(unsigned int seconds);useconds_t ualarm(useconds_t usecs, useconds_t interval);返回:0或以前设置的定时器时间余留的秒数alarm函数可设置定时器 ,当定时器超时,产生一个SIGALRM信号该信号由内核产生,在制定的seconds秒之后,给进程本身发送 一个SIGALRM信号参数为0,取消以前设置的定时器ualarm是微秒为单位的
上面信号发送的两个函数的调用其实也相对来说比较简单。在这里就不去进行演示了。
总结一下进程的信号量问题: 其实linux进程的信号量问题给我的感觉就好像是android里面的广播机制一样,如果你想要去接受到广播的话,那么就必须提前去注册一个广播的监听,这样才会有接受广播的资格,而进程信号其实也是,要想在进程中去监听信号的话,也必须要先通过singal去注册,而发广播其实就是linux进程信号的发送方式,可以通过kill,raise,等函数调用。去向指定进程(注册过信号的)发送消息。而指定进程根据消息类型类选择执行的方式。 谢谢大家的观看。写的不好的地方,希望能够给予指出。一起交流学习的qq群号为324652573