信号的概念
信号在我们的生活中随处可见,如古代烽火戏诸侯中的烽火,跑步时的使用的信号枪发出的信号。
所以说,信号是信息的载体,不能够携带大量的信息
信号的共性
简单,不能够携带大量的信息,满足某个特定条件,优先级高
使用信号的目的
1.让进程知道已经发生了一个特定的事情
2.强迫进程执行它自己代码中的信号处理程序(中断机制)
信号的机制
A给B发送信号。B收到信号之前执行自己的代码,收到信号后,不管执行到程序的什么位置,都要暂停运行,立即去处理信号(信号的优先级高)。与硬件中断类似,但信号是软件层实现的中断,又成为“软中断”。
信号的特质
由于信号是通过软件方法实现,其实现手段导致信号有很强的延时性。但对于用户来说,这个延迟时间非常短,不易察觉。
每一进程收到的所有信号,都是由内核负责发送的,内核处理(请记住这句话)
引发内核为进程产生信号的各类事件:
1.对于前台进程,用户可以输入特殊的终端字符来给它发送信号
ctrl+c ctrl+z ctrl+\
2.硬件发送异常
非法访问内存(段错误) 除0 内存对齐出错(总线错误)
3.系统状态变化
4.运行kill
信号的状态
产生信号
按键产生
系统调用
软件条件产生
硬件异常产生
命令产生
未决
产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态
递答
递送并且到达进程
信号的处理方式
1.执行默认动作
2.忽略(丢弃)
3.捕捉(调用用户处理函数)
信号集
Linux内核的进程控制块PCB是一个结构体,task_struct除了包含进程id,状态,工作目录,用户id,组id,文件描述表,还包含了信号相关的信息,主要指阻塞信号集和未决信号集
1.许多信号相关的系统调用都需要能表示一组不同的信号,多个信号可使用一个称之为信号集的数据结构来表示,其系统数据类型为 sigset_t
2.在PCB中有两个非常重要的信号集。一个称为"阻塞信号集",另一个称为"未决信号集"。这两个信号集都是内核使用位图机制实现的。但操作系统不允许我们直接对这两个信号集进行位操作(我们都知道用户是不能够直接去操作内核空间里的内容的)。需自定义另一个集合,借助信号集操作函数来对PCB中的这两个信号集进行修改
3.信号的“未决”是一种状态,指的是从信号的产生到信号被处理前的这一段时间
4.信号的"阻塞"是一种开关状态,指的是阻止信号被处理,但不是阻止信号产生
5.信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感操作
未决信号集和阻塞信号集
阻塞信号集(信号屏蔽字)
将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,在收到该信号,该信号的处理将被推迟(解除屏蔽后)。说个通俗点的,阻塞信号集可以暂时影响未决信号集,未决信号集上面有讲到是一个状态,它是处于产生到递达之间的状态(递达包括了送达并且处理)。在未决信号集中标识了暂时不处理,那么就是忽略。处理信号不是有三种方式嘛
未决信号集
1.信号的产生,未决信号集中描述该信号的位立刻翻转为1,表信号处于未决状态。当信号被处理对应的位置转回0。这一时刻往往非常短暂
2.信号产生后由某些原因(主要是阻塞)不能到达。这类信号的集合称之为未决信号集。在解除屏蔽之前一直处于未决定状态
信号的编号
查看信号:kill -l
说明:
不存在0号信号。
1-31:常规信号(普通信号、标准信号)
32-64: 实时信号(驱动编程于硬件有关)
信号4要素
每个信号有4个必备的要素:
编号 名称 事件 默认处理动作
通过 man 7 signal查看
名称 编号 默认处理动作 事件
在标准信号中,有一些信号有三个“value”。这是因为不同的操作系统定义了不同的系统信号。我们只研究Linux(中间的值),为了避免歧义,直接使用名称即可
默认处理动作
Term:终止进程
Core:终止进程,生成Core文件(查验进程死亡原因,用于gdb调试) ulimit -a/ulimit -c 1024
Ing:忽略信号
Stop:暂停信号
Cont:继续运行进程
特别说明:
9)SIGKILL和19)SIGSTOP信号,不允许忽略和捕捉,只能执行默认动作。甚至不能将其设置为阻塞
只要产生信号发送的事件发送,信号一定会产生并且递送,但是由于阻塞信号集的原因,该信号不一定会被递达,信号的产生和处理都是内核做到事情。信号的处理方式是信号递达之后的处理方式
core文件的查看
core文件需要使用gdb来查看。
gdb ./a.out
core-file core.xxxx
Linux常规信号说明(1-31)
1) SIGHUP: 当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程
2) SIGINT:当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。
3) SIGQUIT:当用户按下<ctrl+\>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号。默认动作为终止进程。
4) SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件
5) SIGTRAP:该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件。
6) SIGABRT: 调用abort函数时产生该信号。默认动作为终止进程并产生core文件。
7) SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。
8) SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件。
9) SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。
10) SIGUSE1:用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。
11) SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。
12) SIGUSR2:另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。
13) SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。
14) SIGALRM: 定时器超时,超时的时间 由系统调用alarm设置。默认动作为终止进程。
15) SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行shell命令Kill时,缺省产生这个信号。默认动作为终止进程。
16) SIGSTKFLT:Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程。
17) SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。
18) SIGCONT:如果进程已停止,则使其继续运行。默认动作为继续/忽略。
19) SIGSTOP:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为暂停进程。
20) SIGTSTP:停止终端交互进程的运行。按下<ctrl+z>组合键时发出这个信号。默认动作为暂停进程。
21) SIGTTIN:后台进程读终端控制台。默认动作为暂停进程。
22) SIGTTOU: 该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。
23) SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。
24) SIGXCPU:进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程。
25) SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程。
26) SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。
27) SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。
28) SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号。
29) SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略。
30) SIGPWR:关机。默认动作为终止进程。
31) SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件。
34) SIGRTMIN ~ (64) SIGRTMAX:LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。
信号的产生
终端按键产生信号
ctrl+c ==> 2)SIGINT 用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。
ctrl+z ==> 20) SIGTSTP 停止终端交互进程的运行。默认动作为暂停进程。
ctrl+\ ==> 3) SIGQUIT 用户终端向正在运行中的由该终端启动的程序发出些信号。默认动作为终止进程。
硬件异常产生信号
除0操作 ==>8) SIGFPE 在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件。
非法访问内存 ==>20) SIGTSTP:停止终端交互进程的运行。默认动作为暂停进程。
总线错误 ==>7) SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。
kill函数/命令产生信号
kill命令产生信号:kill -信号名称或编号 进程id (eg:kill -9(SIGKILL) 12201)
kill函数:给指定进程发送信号(不一定是杀死,根据发送什么信号决定)
int kill(pid_t pid,int sig);
成功:0
失败:-1(id非法 信号非法 权限问题)
参数:
sig:使用宏名,不要使用编号(避免歧义)
pid:
>0 ==> 发送给指定的进程
=0 ==> 发送信号给与调用kill函数进程属于同一进程组的所有进程
<0 ==> 取|pid|发送给对应的进程组
-1 ==> 发送给进程有权限发送的所有进程
一些概念说明:
进程组:
每一个进程都属于一个进程组,进程组是一个或多个进程集合,他们相互关联,共同完成一个实体任务,每一个进程都有一个进程组长,默认进程组ID与进程组长ID相同。
权限保护:
超级用户可以向任意用户发送信号,而普通用户不能向系统用户发送信号。同样普通用户无法向其他普通用户发送信号。只能向自己创建的进程发送信号
#include <iostream> #include <sys/types.h> #include <unistd.h> #include <signal.h> using namespace std; int main(void) { pid_t pid; int i; int tmppid; //abort(); //raise(SIGSTOP); for(i=0;i<5;++i) { pid = fork(); if(pid == 0) { break; } if(i == 2) { tmppid = pid; } } if(i < 5) { cout<<getpid()<<" 其父:"<<getppid()<<endl; sleep(2); } else if(i == 5) { sleep(1); kill(tmppid,SIGKILL); while(1) {} } return 0; }