Linux信号
来源:互联网 发布:上交所网络投票系统 编辑:程序博客网 时间:2024/06/05 08:21
当我们在终端运行这样的程序时
#include<stdio.h>int main(){ int count = 0; while(1) { sleep(1); printf("%d\n", count++); }}
我们可以看到每隔一秒会将count++然后输出到显示器,但是这个时候要结束掉这个进程该怎样做呢?很简单,我们可以使用Ctrl+c组合键来直接结束掉这个进程。
那么为什么这样做会让进程直接结束运行呢?
当按下Ctrl+c时,会产生一个硬件中断,这时cpu如果在执行这个代码,则会暂停执行,cpu由用户态切换到内核态处理这个中断。这时操作系统会将这个中断解释成一个SIGINT信号,记录在进程的pcb中的特定位置,当从内核态返回到用户态继续执行进程时,会处理进程中记录的信号,而这个信号的默认动作是终止进程,进而进程被终止运行。
综上我们可以得到这样的结论:
1.进程可以接收到信号,并有特定的位置保存信号。
2.对于不同的信号都有自己的默认处理方式,所以说进程尽管没有收到信号时,但是他仍然知道遇到信号时该怎样去处理。
3.进程在收到信号时,先将信号保存,并不是立刻就去进行处理,而是等到合适的时间在去处理。
注意:
虽然说是给进程发送信号,但是期间的实际过程是操作系统自己在进程的pcb中的特定位置写入。
信号的种类:
可以使用 kill -l 查看系统中所有的信号
注意:没有32和33号信号
前32个信号是一般信号,后32个信号是实时信号。
信号的产生:
1.用户在终端按下某些键时,终端程序会发送信号给前台进程。
注意:这样发送只能给前台进程发送信号,不能给后台进程发送信号。
2.硬件异常产生信号,这些条件有硬件检测到并通知操作系统,然后操作系统向当前进程发送适当的信号。
3.通过调用系统函数向进程发送信号
#include<signal.h>
int kill(pid_t pid, int signo);
函数说明:
可以发送signo信号给进程号为pid的进程,kill指令就是通过调用kill()函数实现的。
int raise(int signo);
这两个函数成功返回0,失败返回-1。
函数说明:
raise()可以给当前进程发送signo信号,也就是自己给自己发送信号。
#include<stdlib.h>
void abort(void);
调用这个函数,进程将给自发送一个信号SIGABRT,而这个信号的默认动作就是结束进程,这个信号虽然可以捕捉但是进程仍然会退出。
4.由软件条件产生
SIGPIPE是一种由软件条件产生的信号,关闭管道的读端时,写端进程在往管道里写入时进程就会收到这个信号。
alarm函数也会产生一个同样性质的信号,SIGALRM信号。
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,在seconds秒后会给当前进程发送SIGALRM信号,该信号默认动作是终止当前进程。这个函数的返回值是0或者是以前是定闹钟时间还剩下的秒数。
模拟实现kill指令
#include<stdio.h>#include<signal.h>void usage(const char *arr){ printf("usage : %s sig pid", arr);}int main(int argc, char *argv[]){ if(argc != 3) { usage(argv[0]); return -1; } //获取到pid int pid = atoi(argv[2]); //获取到要发送的信号 int sig = atoi(argv[1]); //调用kill函数实现 kill(pid, sig); return 0;}
信号的处理:
当一个进程收到信号时会有三种处理方式:
1.忽略该信号
2.执行默认动作,每一个信号都对应了自己默认的动作
3.自定义捕捉信号,大部分信号都可以进行捕捉,但是有个别信号不能对其捕捉
信号的捕捉函数:
参数描述;
signum:标识要捕捉的信号
handler:用来替代默认动作的函数
信号在内核中的储存:
要了解信号是如何在内存中存储的,先来了解几个概念:
信号递达(Delivery):实际执行信号的处理动作。
信号未决(Pending):信号在产生到递达之间的状态。
信号阻塞(Block):信号可以被阻塞,一旦有信号被阻塞,那么该信号就会一直处于未决状态,直到该信号被解除阻塞,才会执行递达的动作。
在pdb中信号会对应三张表,Block、pending和Handler,Block用来标记对应信号是否被阻塞,pending标记对应信号是否被进程接收或者递达,Handler用来存储对应信号的执行操作。
在pcb中会有对应的位置存储各信号的状态,当信号被写入时会将pending表中对应的位置标记为1,当信号递达时pending表中对应的位置就会被清0,如果信号被阻塞block表的对应位置就会标记为1。
注意:Block和Pending底层是用位图来实现的。
信号集操作函数:
#include<signal.h>
信号集的初始化:
//将所有信号对应的bit清0,表示该信号集不包含任何有效的信号
int sigemptyset(sigset_t *set);
//将所有信号对应的bit置1,表示该信号集的有效信号包括系统支持的所有信号。
int sigfillset(sigset_t *set);
//信号的添加
int sigaddset(sigset_t *set, int signo);
//信号的删除
int sigdelset(sigset_t *set, int signo);
//判断信号集的有效信号中是否包含某种信号,若包含返回1,不包含返回0
int sigismember(const sigset_t *set, int signo);
读取、更改进程的信号屏蔽字(阻塞信号集)
#include<signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:成功返回0,出错返回-1
how:
SIG_BLOCK:set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set
SIG_UNBLOCK:set包含了我们希望从当前信号屏蔽字中解除阻塞的信号mask=mask&~set
SIG_SETMASK:设置当前信号屏蔽字为set所指向的值,相当于mask = set
读取当前进程的未决信号集
int sigpending(sigset_t *set);
将进程的未决信号集通过set参数传出。成功返回,出错返回-1。
使用上面的信号集操作函数,实现,将2号信号阻塞,然后每隔一秒打印每个信号的未决状态,在向进程发送2号信号,会发现该信号一直处于未决状态。
#include<stdio.h>#include<signal.h>#include<stdlib.h>void showpending(sigset_t *pending){ int i = 1; for(; i <= 31; i++) { //如果信号集中的有效信号包含信号i,打印1,否则打印0 if(sigismember(pending, i)) { printf("1"); } else { printf("0"); } } printf("\n");}int main(){ //定义信号集 sigset_t set, oset; //初始化信号集 sigemptyset(&set); sigemptyset(&oset); //将2号信号加进信号集set中 sigaddset(&set, 2); //将mask设置为set指向的值,用oset接收之前信号集的内容 //因为sigprocmask的属性为SIG_SETMASK所以会将添加set中的所有信号阻塞 sigprocmask(SIG_SETMASK, &set, &oset); //程序运行到这里时,当进程收到2号信号时不会递达,因为2号信号被阻塞 sigset_t pending; while(1) { sleep(1); sigpending(&pending); showpending(&pending); } return 0;}
运行结果:
因为阻塞了2号信号,所以当发送2号信号时,会一直处于未决状态,而其他信号没有被阻塞,可以递达
阅读全文
0 0
- linux 信号&信号处理
- linux 信号&信号处理
- linux 信号&信号处理
- linux 信号 信号集
- linux信号--阻塞信号
- Linux信号
- linux信号
- Linux信号
- linux 信号
- linux 信号
- linux 信号
- Linux 信号
- linux信号
- linux 信号
- linux 信号
- linux信号
- Linux信号
- Linux 信号
- Hibernate中遇到的 NullPointerException问题和IllegalArgumentException
- [noi2015] 软件包管理器(树链剖分)
- Mac 下 Chrome 谷歌浏览器 快捷键
- ReactNative实战之仿微信客户端
- mybatis处理and、or关系的方法
- Linux信号
- 哈夫曼编码
- 35. Search Insert Position
- jQuery AJAX简单应用
- 《Javascript高级编程》 数据类型
- spring 注解@component 学习
- linux字符驱动之中断按键
- IOIO OTG开发板
- 网络加载图片