《Linux操作系统原理分析之Linux 进程管理 5》(9)
- [4 Linux 进程管理](#4 Linux 进程管理)
4 Linux 进程管理
4.5 Linux 信号
4.5.1 信号的作用和种类
1.信号机制
概念 | 说明 |
---|---|
信号机制通信传输的本质 | 传输称为信号的数值。 |
信号主要作用 | 把系统中发生的某些事件通知给进程。 |
信号主要特征 | 异步性:即什么时候出现信号是不可预知的。 |
信号数量 | 它与硬件机器的字长相对应。如 80x86 的字长 32 位,则信号有32 种。 |
信号数量定义在哪里 | include/asm-i386/signal.h |
信号值 | 系统中每个信号都是一个整数,称为信号值。 |
信号名 | 为了清楚地表示信号的意义,linux 通过宏定义给每个信号都定义了一个符号常量,称为信号名。 |
信号名格式 | 。Linux 的信号名的组成以 SIG 打头,后面跟着表示信号意义的英文缩写。 |
2.信号种类
信号值 | 信号名 | 信号意义 | 缺省处理 |
---|---|---|---|
0 | 用作特殊情况处理 | ||
1 | SIGHUP | 进程的控制终端或控制进程已结束 | 终止进程 |
2 | SIGINT | 用户键入ctrl-c | 终止进程 |
3 | SIGQUIT | 从键盘来的终止信号(quit) | 终止进程、core转储 |
4 | SIGILL | 进程执行了非法指令或企图执行数据段 | 终止进程、core转储 |
5 | SIGTRAP | 跟踪中断、执行trap指令 | 终止进程、core转储 |
6 | SIGABRT | 进程发现错误并调试abort | 终止进程、core转储 |
7 | SIGBUS | 进程访问非法地址、地址对齐出错 | 终止进程、core转储 |
8 | SIGFPE | 进程浮点运算错误、溢出、除数为0等 | 终止进程、core转储 |
9 | SIGKILL | 强制终止进程(本信号不能屏蔽) | 终止进程(不能忽视) |
10 | SIGUSR1 | 保留给用户自行定义信号 | 终止进程 |
11 | SIGSEGV | 进程访问内存越界,或无访问权限 | 终止进程、core转储 |
12 | SIGUSR2 | 保留给用户自行定义信号 | 终止进程 |
13 | SIGPIPE | 进程向无读者的管道执行写操作 | 终止进程 |
14 | SIGALRM | 时钟定时信号,由系统调用alarm发出 | 终止进程 |
15 | SIGTERM | 结束信号,由kill命令产生 | 终止进程 |
16 | SIGSTKFLT | 进程发现堆栈溢出错误 | 终止进程、core转储 |
17 | SIGCHLD | 子进程结束或终止 | 忽视 |
18 | SIGCONT | 让暂停的进程继续执行 | 终止进程 |
19 | SIGSTOP | 暂停进程的执行(不能屏蔽) | 暂停进程(不能忽视) |
20 | SIGTSTP | 用户键入暂停(通常是ctrl-z) | 暂停进程 |
21 | SIGTTIN | 后台作业要从用户终端(stdin)读数据 | 暂停进程 |
22 | SIGTTOU | 后台作业写用户终端(stdout) | 暂停进程 |
23 | SIGURG | 套接字(socket)有"紧急"数据到达 | 忽视 |
24 | SIGXCPU | 进程使用CPU超时 | 终止进程、core转储 |
25 | SIGXFSZ | 进程处理文件超长 | 终止进程、core转储 |
26 | SIGVTALRM | 虚拟时钟信号(计算进程占用CPU时间) | 终止进程 |
27 | SIGPROF | 类似SIGALRM/SIGVTALRM(计算进程占用CPU时间以及系统调用的时间) | 终止进程 |
28 | SIGWINCH | 终端窗口大小已改变 | 忽视 |
29 | SIGIO | I/O准备就绪,可以进行输入/输出操作 | 忽视 |
30 | SIGPWR | 系统电源失效 | |
31 | SIGUNUSED | 未使用 |
信号产生的三种情况:
1.进程在执行过程中发生了某种错误,标志被置位,系统内核识别到错误标志,向有关进程发送相应信号,通知进程发生了运行错误。
2.系统或用户发出的控制进程终止或暂停的信号。
3.内核需要控制进程的运行而产生的信号。
4.5.2 信号的处理
1. 在进程的任务结构体 task_struct 中有两个成员项用于处理接收的信号:
java
Unsigned long signal;
Unsigned long blocked;
它们都是位域(Bitmap)形式的 32 位 unsigned long 型变量,每一位(bit)对应一种信号。变量的第 0位对应信号值为 1 的 SIGHUP,第 1 位对应信号值为 2 的 SIGINT,依此类推。
1)Signal:存放进程收到且尚未处理的信号。
进程可以同时接收多个信号
每种信号在 signal 中只有一位,故不能识别接收了一个还是多个同一个信号
信号没有优先级,可以以任意顺序处理接受到的信号
2) Blocked:通过将 blocked 中的某一位设置为 1,来屏蔽某种信号的处理。但是有两个不能屏蔽的信号(SIGKILL 和 SIGSTOP)是不能被屏蔽的,blocked 中它们对应的位始终为 0;
2.进程接收到信号后的两种处理方式:
交给内核进行处理(缺省方式)
由进程自行处理
1)其中 core 转储指把该进程内存中的有关信息进行转储(dump),生成 core 文件。在使用 gdb 调试工具对程序进行调试时,通常需要使用 core 文件。
2)进程接收到信号后有其自行处理成为信号的捕获,但是信号 SIGKILL 和 SIGSTOP 不能有进程捕获,他们必须由内核进行处理。
3)信号无论是由内核或是进程处理,都可以 被忽视,即不进行任何处理,但是信号 SIGKILL 和SIGSTOP 不能被忽视。
4.5.3 信号处理函数
1.数据结构
当进程接收到信号,并且该信号没有被阻塞的话,进程就执行信号处理函数完成对信号的处理,每种信号都有其对应的处理函数,进程对所有信号处理函数集中由 signal_struct 结构体来管理,进程任务结构体中成员项 sig 指向该结构体。在 include/linux/sched.h 中定义了 signal_struct 结构体:
java
Struct signal_struct{
Int count;
Struct sigaction action[32];
};
count:共享处理信号函数的计数值。一般是子进程继承父进程的信号机制时的计数。
action[]:是该进程的信号处理函数表,32个元素对应 32 种信号。该数组是 sigaction 结构体,它定义在/ include/asm-i386/signal.h 中
java
Struct sigaction {
_sighandler_t sa_handler;
Sigset_t sa_mask;
Unsigned long sa_flags;
Void(*sa_restorer)(void);
};
sa_handler 是指向信号处理函数的指针,通常是用户自行设定的信号处理函数。当 sa_handler 的值是系统定义的以下符号常量时,它不是信号处理函数的入口地址,其值和意义如下:
SIG_DEL 0 缺省处理,由内核执行系统设定的信号处理函数
SIG_IGN 1 忽视信号,不进行信号处理
SIG_ERR -1 信号处理时返回的错误,一般用于判断函数的返值是 否正确。
sa_mask 是一个信号屏蔽码,当进程处理某一个信号时,它被逻辑加(OR)到接收进程的信号 屏蔽码 blocked 上,进程信号屏蔽码的这种改变只是在信号处理期间有效,其目的是在进程执行 信号处理过程中屏蔽其它到达的信号。
sa_flags 是信号处理标志,主要有SA_ONESHOT 信号到达时,启动信号处理函数
SA_NOMASK 不使用 sa_mask改变进程的信号屏蔽码
sa_restorer 是一个函数指针,目前未用,保留以供扩充。
2. 处理函数 signal
Linux 系统提供了用户自己设置信号处理函数的方法,它由系统调用 signal()完成。在 signal()中进一步调用内核函数 sys_signal()实现函数设置的功能。该内核函数定义在 kernal/signal.c 中:
java
Asmlinkage unsigned long sys_signal(int signum,_sighandler_t handler);
参数说明:
signum:信号值,指明要设置哪个信号的函数;
handler:用户设置的处理函数的首地址。(也可以是 SIG_DEL、SIG_IGN)
函数简要说明:
java
Struct sigaction tmp;/*用于暂存信号处理函数的有关信息。*/
...
If(signum<1|| signum>32) return --EINVAL; /*判断 signum 给定的信号值是否合理*/
If(signum==SIGKILL||signum==SIGSTOP)return --EINVAL; /*若为这两个信号,则不能被捕获,即用户不能为它们设定处理函数*/
If(handler!=SIG_DFL&& handler!=SIG_IGN) /*若信号不是指定为缺省处理或
{ 忽视,则确认给定的处理函数使用存储空间的有效性*/
Err = verify_area(VERIFY_READ,handler,1);
If(err)return err;
}
经过上面的检查确认后,开始使用 tmp 设置进程的 sigaction 结构体。
java
Memset(&tmp,0,sizeof(tmp)); /* 首先把该结构的存储空间全部清 0*/
Tmp.sa_handler =handler;/*把参数 handler 指定的信号函数处理函数首地址置入 tmp 的sa_handler*/
Tmp.sa_flags = SA_ONESHOT|SA_NOMASK; /*设置 sa_flag*/
Current->sig->action[signum-1]=tmp; /*把 tmp 的内容复制到当前进程的处理信号函数表中与指定信号对应的数组元素中。*/
Check_pending(signum); /*设置当前进程任务结构体的 signal 成员项*/
Return(unsigned long)handler; /*返回 handler 的值,即原信号处理函数的首地址*/
3.程序例
java
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
Int count=0;
Void ctrl_c_count(int);
Main()
{
int c;
void(*old_handler)(int);
old_handler=signal(SIGINT,ctrl_c_count);
while((c=getch()!="\n");
printf("Ctrl_C count=%d\n",count);
signal(SIGINT,old_handler);
}
Void ctrl_c_count(int dump)
{
Printf("Ctrl_C\n");
Count++;
}