目录
信号
信号共性
信号特质
产生信号
信号相关概念
默认处理动作
信号4要素
常规信号 编辑
注意
kill
参数pid
测试代码1
测试结果
测试代码2
测试结果
alarm
参数seconds
返回值
取消闹钟
测试代码3
测试结果1
测试结果2 编辑
setitimer
参数which
参数new_value
参数old_value
测试代码4
测试结果
信号集操作函数
信号
信号共性
简单
不能携带大量信息
满足条件才能发送
信号特质
信号是软件层面上的“中断”,一旦信号产生,无论程序执行到什么位置,都必须立即停止运行,处理信号,处理完成后,再继续执行后续程序。所有信号的产生及处理都是由内核完成。
产生信号
按键产生
系统调用产生
软件条件产生
硬件异常产生
命令产生
信号相关概念
未决:产生与递达之间的状态。
递达:产生并且送达到进程,直接被内核处理掉。
信号处理方式:
执行默认处理动作。
忽略。
捕捉。
阻塞信号集(信号屏蔽字):本质是位图。用来记录信号的屏蔽状态。一旦被屏蔽的信号,在解除屏蔽之前,一直处于未决态。
未决信号集:本质是位图。用来记录信号的处理状态。该信号集中的信号表示已经产生,但未被处理。
默认处理动作
Term:终止进程。
lgn:忽略信号。
Core:终止进程,生成Core文件。可以查验进程死亡原因,用gdb进行调试。
Stop:停止进程或暂停进程。
Cont:继续运行进程。
信号4要素
信号使用之前,必须先确定其4要素,再进行使用。
信号编号
信号名称
信号对应时间
信号默认处理动作
man 7 signal
在标准信号中,有一些信号是有三个“Value”,第一个值通常对alpha和sparc架构有效,中间值针对x86、arm和其他架构,最后一个应用于mips架构。一个‘-’表示在对应架构上尚未定义该信号。
不同的操作系统定义了不同的系统信号。因此有些信号出现在Unix系统内,也出现在Linux 中,而有的信号出现在FreeBSD或Mac OS中却没有出现在Linux下。
常规信号
SIGHUP:当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程。
SIGIN:当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。
SIGQUIT:当用户按下<ctrl+\>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号。默认动作为终止进程。
SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件。
SIGTRAP:该信号由断点指令或其他trap指令产生。默认动作为终止进程并产生core文件。
SIGABRT:调用abort函数时产生该信号。默认动作为终止进程并产生core文件。
SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。
SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。 默认动作为终止进程并产生core文件。
SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。
SIGUSE1:用户定义的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。
SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。
SIGUSR2:另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。
SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。
SIGALRM:定时器超时,超时的时间由系统调用alarm设置。默认动作为终止进程。
SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行 shell命令Kill时,缺省产生这个信号。默认动作为终止进程。
SIGSTKFLT:Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程。
SIGCHLD:子进程状态发生变化时,父进程会收到这个信号。默认动作为忽略这个信号。
SIGCONT:如果进程已停止,则使其继续运行。默认动作为继续/忽略。
SIGSTOP:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为暂停进程。
SIGTSTP:停止终端交互进程的运行。按下<ctrl+z>组合键时发出这个信号。默认动作为暂停进程。
SIGTTIN:后台进程读终端控制台。默认动作为暂停进程。
SIGTTOU:该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。
SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。
SIGXCPU:进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程。
SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程。
SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。
SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。
SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号。
SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略。
SIGPWR:关机。默认动作为终止进程。
SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件。
34~64. LINUX 的实时信号,它们没有固定的含义,可以由用户自定义。所有的实时信号的默认动作都为终止进程。
注意
9) SIGKILL和19) SIGSTOP信号,不允许忽略和捕捉,只能执行默认动作。甚至不能将其设置为阻塞。
只有每个信号所对应的事件发生了,该信号才会被递送,但不一定递达,不应乱发信号!
kill
man 2 kill
参数pid
大于0:发送信号给指定进程。
等于0:发送信号给跟调用kill函数的那个进程处于同一进程组的进程。
小于-1:取绝对值,发送信号给该绝对值所对应的进程组的所有进程。
等于-1:发送信号给有权限发送的所有进程,普通用户基本规则是,发送者实际或有效用户ID==接收者实际或有效用户ID。
测试代码1
用子进程杀了父进程。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>int main(int argc, char *argv[])
{pid_t JinCheng_ID;JinCheng_ID = fork();if (JinCheng_ID > 0) //父进程{printf("这是父进程,该进程ID是%d。开始进入死循环。\n", getpid());while (1){printf("这是父进程,已在死循环。\n");sleep(1);}}else if (JinCheng_ID == 0) //子进程{printf("这是子进程,该进程ID是%d。先睡一会再杀父进程。\n", getpid());sleep(3);printf("这是子进程,该进程ID是%d。睡醒了,我杀。\n", getpid());kill(getppid(),SIGKILL);printf("这是子进程,该进程ID是%d。杀完了,拜拜。\n", getpid());}return 0;
}
测试结果
测试代码2
杀进程组
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>int main(int argc, char *argv[])
{pid_t JinCheng_ID;JinCheng_ID = fork();if (JinCheng_ID > 0) //父进程{printf("这是父进程,该进程ID是%d。开始进入死循环。\n", getpid());while (1){printf("这是父进程,已在死循环。\n");sleep(1);}}else if (JinCheng_ID == 0) //子进程{printf("这是子进程,该进程ID是%d。先睡一会再杀进程组。\n", getpid());sleep(3);printf("这是子进程,该进程ID是%d。睡醒了,我杀。\n", getpid());kill(0, SIGKILL);printf("这是子进程,该进程ID是%d。杀完了,拜拜。\n", getpid());}return 0;
}
测试结果
进程组被杀后,子进程最后的输出不再进行输出。
alarm
定时发送SIGALRM给当前进程。
实际时间=用户时间+内核时间+等待时间,代码优化,优先从IO下手。
man 2 alarm
参数seconds
定时秒数。
返回值
上次剩余的时间。
取消闹钟
alarm(0);
测试代码3
查看程序1秒钟的计数次数。
#include <stdio.h>
#include <unistd.h>int main(int argc, char *argv[])
{int i = 0;alarm(1); //定时1秒while (1){printf("%d\n", i++);}return 0;
}
测试结果1
测试结果2
setitimer
设置定时器(闹钟)。 可代替alarm函数。精度微秒us,可以实现周期定时。
man 2 setitimer
参数which
指定定时方式。
ITIMER_REAL:自然定时,计算自然时间,信号是14)SIGLARM。
ITIMER_VIRTUAL:虚拟空间计时(用户空间),只计算进程占用cpu的时间,信号是26)SIGVTALRM。
ITIMER_PROF:运行时计时(用户+内核),计算占用cpu及执行系统调用的时间,信号是27)SIGPROF。
参数new_value
定时秒数。
struct itimerval {struct timeval it_interval; //后续每个中断的间隔执行时间struct timeval it_value; //第一次执行中断的时间
};struct timeval {time_t tv_sec; //秒suseconds_t tv_usec; //微秒
};
struct itimerval {struct timeval { //后续每个中断的间隔执行时间time_t tv_sec; //秒suseconds_t tv_usec; //微秒}it_interval;struct timeval { //第一次执行中断的时间time_t tv_sec; //秒suseconds_t tv_usec; //微秒}it_value;
};
参数old_value
传出参数,上次定时剩余的时间。
测试代码4
周期性输出内容。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>void ChuLi_HanShu()
{printf("你好,世界!\n");
}int main(int argc, char *argv[])
{int i = 0;struct itimerval new_value, old_value;signal(SIGALRM, ChuLi_HanShu); //注册SIGALRM信号的捕捉处理函数//2秒后开始第1次中断new_value.it_value.tv_sec=2;new_value.it_value.tv_usec=0;//每5秒来一次中断new_value.it_interval.tv_sec=5;new_value.it_interval.tv_usec=0;if(setitimer(ITIMER_REAL,&new_value,&old_value)==-1){perror("设置中断错误");exit(1);}while (1){printf("%d秒\n", i++);sleep(1);}return 0;
}static
测试结果
信号集操作函数
sigset_t set; //自定义信号集
sigemptyset(sigset_t *set); //清空信号集
sigfillset(sigset_t *set); //全部置1
sigaddset(sigset_t *set,int signum); //每一个信号添加到集合中
sigdelset(sigset_t *set,int signum); //将一个信号从集合中移除
sigismember(const sigset_t *set,int signum); //判断一个信号是否在集合中。返回值:1:在;0:不在。