1 ctrl -c 只能给前台程序发送信号
而一个命令+& 表示将这个命令的进程放到后台运行,这样shell就不用等待就可以执行别的命令,启动新的进程。
2 shell可以运行一个前台进程和任意多的后台进程,但只有前台进程才能接收到类似于ctrl+c这种控制键产生的信号
3 前台进程运行中,用户可以随时用ctrl+c这样的控制键发送信号,也就是说,进程的用户空间代码执行到任何地方都有可能收到SIGINT信号而终止,所以信号对于进程的控制流程来说是异步的。
kill -l查看信号清单
kill 7 signal 查看详细说明
信号处理方式
1 忽略
2 执行默认操作
3 提供一个信号处理函数,要求内核处理信号时,切换到用户态去执行相应的操作,这种方式称为捕捉(Catch)一个信号。
SIGINT默认是退出进程
SIGQUIT默认退出进程并core dump
什么是Core Dump?
进程异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘,文件名通常是core,这就叫Core Dump 即用户空间的内存数据。
然而进程异常终止常常是因为有bug,事后我们可以gdb bt 等 事后调试错误原油,进程允许产生多大的coredump 文件 这个信息保存在进程的PCB,默认是不允许产生的,为什么 ?
因为可能会涉及到一些敏感信息,例如,用户的账户信息,密码,身份信息等。我们必要时也可以修改
eg: ulimit -a ulimit -c unlimited/指定大小
shell 不希望用户收入和段错误信息显示在一行,所以,kill发送信号后,再按一个回车才会显示段错误信息
kill命令是调用kill 函数来实现的,kill 函数给指定进程发送指定的信号,而 raise函数 ,当前进程自己给自己发信号。
#include <signal.h>
int kill(pid_t pid,int signo);
int raise(int signo);
这两个函数都是成功 0 失败 -1
#include <stdlib.h>
void abort(void)函数使当前进程接收到信号而异常终止。类似于exit()
因此abort 函数总是会成功的,因此不存在返回值
SIGPIPE 管道异常
1 只有写,没人读,
2 管道破裂
alarm函数 和 SIGALARM信号
#include <unistd.h>
unsigned int alarm (unsigned int seconds);
alarm 函数设定一个 闹钟,指定秒之后向当前进程发送一个 SIGALARM信号,该信号默认动作是终止当前进程。
定时30秒的 闹钟 ,返回值为 0或者当前定时剩余的秒数
硬件异常:
硬件发生异常就会被硬件以某种方式检测到,然后同志内核,内核就会给当前进程发送相应的信号。
eg: 除0 运算 ——————SIGFPE
非法访问——————SIGSEGV
signal 函数用于捕获信号;信号是可以自定义捕捉的
handler函数为信号的处理函数 ,他的参数就是signal函数的捕捉的信号编号
程序运行情况如上图
不要按ctrl+q 它会以关掉虚拟机的形式来结束进程。
野指针的捕获 SIGSEGV 11号信号
默认的处理 ----吐核
总结:由上面的几个例子可见C? C++中的非法操作其实在内核中都是以信号的方式传递的
1 为什么信号由内核产生?
因为OS是资源的管理者
2 信号被捕获后是不是立即被处理?
不是! 在合适的时机,例如多敲一个回车才显示从热dump 信息的例子
3 既然信号不是被进程立即处理的,那么也就是说,进程需要将信号记录下来,那么记录在什么位置合适呢?
4 如果一个进程在没有收到信号的时候,能否知道自己应对合法信号作何处理?
阻塞信号:
1 信号递达:实际处理的过程/动作称为信号递达
2 信号未决:信号从产生到递达之前都称为信号未决
3 进程可以选择阻塞某个信号,被阻塞的信号处于未决状态,直到进程解除对该信号的阻塞,该信号才能执行递达动作。
4 阻塞和忽略不是一个意思噢,
阻塞意味着暂不接受该信号,而忽略是一种递达的处理动作。
在pdb中信号会对应三张表,Block、pending和Handler,Block用来标记对应信号是否被阻塞,pending标记对应信号是否被进程接收或者递达,Handler用来存储对应信号的执行操作。
在pcb中会有对应的位置存储各信号的状态,当信号被写入时会将pending表中对应的位置标记为1,当信号递达时pending表中对应的位置就会被清0,如果信号被阻塞block表的对应位置就会标记为1。
每个信号只有一个bit来记录未决/阻塞标志,非0即1,不记录信号出现的次数,阻塞和
未决标志用相同的数据类型sigset_t来存储。
SIGINT信号产生过,但正在被阻塞,所以处于未决pending状态,虽然它的处理动作是忽略,但是在没有解除阻塞之前不能执行忽略操作,因为进程也可能先改变处理动作再解除阻塞
SIGQUIT信号未产生过,一旦产生将被阻塞,他的处理动作是用户自定义的handler函数。
若解除阻塞之前,信号出现过多次,如何处理?
POSIX 库定义:普通信号只递送一次,实时信号可以依次放入一个队列再递达。
信号集操作函数
#include <signal.h>
int sigemptyset(sigset_t *set);初始化信号集,使所有信号对应的bit位清零,表示不含有任何有效信号。
int sigfillset(sigset_t *set);//初始化信号集,所有信号对应的bit位置1,表示该信号集的有效信包含所有系统支持的信号。
int sigaddset(sigset_t *set,int signo);//add
int sigdelset(sigset_t *set,int signo;// delete
初始化之后,才可以调用sigaddset/sigdelset来添加或删除有效信号。
sigismember(const sigset* set ,int signo);//判断信号集是否包含某种信号
这 5 个 函数 成功返回0 ;失败返回 -1
sigprocmask () 可以更改信号屏蔽字(阻塞信号集)
sigpending()可以读取当前的阻塞信号集
volatile 关键字:告诉编译器每次都从内核中读取数据,不要再从寄存器读取数据了,
主要用于 一些内核调用的函数,像signal(signal,handler);
handler(int sig);
这种都都是内核调用的函数,编译器在编译器件都不会考虑的;它只加载一次到寄存器,以后直接从寄存器读的 。
所以 需要 volatile 来进行声明。省去代码优化。
来源:CSDN
作者:weixin_44030580
链接:https://blog.csdn.net/weixin_44030580/article/details/104093120