LINUX系统编程_信号

不定期补充、修正、更新;欢迎大家讨论和指正

概览

kill -l 命令查看信号的宏名,前面的数字就是信号的编号
#多数信号系统默认处理方式为终止  下面只列出产生core文件和忽略处理的

1) SIGHUP	 	#终端线路挂断
2) SIGINT		#终止进程(Ctrl+C)	
3) SIGQUIT		#终止进程(Ctrl+\)	 产生core文件
4) SIGILL	 	#非法指令 			 产生core文件
5) SIGTRAP		#跟踪自陷
6) SIGABRT		#调用abort函数自杀终止程序   终止并产生core文件
7) SIGBUS		#总线故障 终止并产生core文件
8) SIGFPE	 	#算术运算异常,例如除以0,浮点溢出等
9) SIGKILL		#无条件终止指定进程,不可被捕获和忽略
10) SIGUSR1  	#用户自定义信号1,可用于应用程序
11) SIGSEGV		#访问内存出错,段错误
12) SIGUSR2 	#用户自定义信号2,可用于应用程序
13) SIGPIPE 	#写没有读权限的管道文件
14) SIGALRM		#Linux三种间隔定时器:real,按实际经过的时间计时,无论进程在什么状态下运行;调用alarm函数
15) SIGTERM		#终止进程 kill PID 默认就是发送这个信号
16) SIGSTKFLT		
17) SIGCHLD		#子进程状态改变    忽略,可以用wait函数来处理这个信号
18) SIGCONT		#继续执行		 忽略
19) SIGSTOP		#非终端发送的停止信号
20) SIGTSTP 	#终端发送的停止信号 Ctrl+Z
21) SIGTTIN		#后端进程读终端
22) SIGTTOU		#后端进程写终端	
23) SIGURG		#I/O紧急信号		  忽略
24) SIGXCPU		#CPU时限超时
25) SIGXFSZ		#文件长度过长
26) SIGVTALRM	#Linux三种间隔定时器:virtual,仅在进程在用户态下执行才计时,定时到达发送信号
27) SIGPROF		#Linux三种间隔定时器:profile,进程在用户态或内核态执行才计时;统计分布图用计时器到时
28) SIGWINCH	#窗口大小发生变化    忽略
29) SIGIO   	#异步通知信号
30) SIGPWR     
31) SIGSYS	
34) SIGRTMIN	

#总共62个信号,32和33没有
#35~64为Linux后期增设的信号,所以不需要关心。

35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

接受信号

当进程接受其他进程或OS发送的信号,可以在进程内部对接受的信号作处理,分为默认,忽略和捕获。

signal(2)

NAME
       signal - ANSI C signal handling //处理信号

SYNOPSIS
       #include <signal.h>

       typedef void (*sighandler_t)(int);
		//它定义了一个类型sighandler_t,表示指向返回值为void型(参数为int型)的函数(的)指针。
		//它可以用来声明一个或多个函数指针。
        //sighandler_t sig1, sig2; 这个声明等价于下面这种晦涩难懂的写法:
        //void (*sig1)(int), (*sig2)(int);
        
	   sighandler_t signal(int signum, sighandler_t handler);
	   //signum信号编号,也可以用宏名
	   
	   // sighandler_t handler 即 void (*handler)(int)
	   //如果为SIG_IGN,则信号处理为忽略  宏名为((void (*) (int)) 0)
	   //如果为SIG_DFL,则信号处理为默认  宏名为((void (*) (int)) 1)
	   //如果是一个函数,则信号处理为捕获,执行函数内的语句
	   //SIGKILL 和 SIGSTOP信号不能被捕获和忽略

	   //返回值为上一次信号处理的方式,错误时返回SIG_ERR 宏名为((void (*) (int)) -1)
将SIGINT(即在终端Ctrl+C终止进程)的处理方式改为忽略,按下Ctrl+C之后没有效果,按下Ctrl+\结束进程
记得写上typedef void (*sighandler_t)(int); 不然使用sighandler_t会报错

在这里插入图片描述
在这里插入图片描述

当handler为函数地址时,SIGINT的处理方式会执行函数内的语句
同时观察返回值,成功则为返回上一次设置的处理方式

在这里插入图片描述
在这里插入图片描述

将第一次改为捕获,第二次修改为默认,第二个pre_sig的确是函数的地址

在这里插入图片描述

SIGKILL 和 SIGSTOP信号不能被捕获和忽略

将所有信号设置为忽略处理

在这里插入图片描述

无论是发送SIGINT(Ctrl+C)还是SIGQUIT(Ctrl+\)都无法结束进程
在另一个终端kill进程 即默认发送SIGTEM命令,也无法终止
发送SIGKILL命令才能终止进程
SIGKILL和SIGSTOP信号不能被捕获和忽略就是为了防止所有信号被设置成忽略或者捕获,导致没有信号能够终止进程

在这里插入图片描述
在这里插入图片描述

子进程继承信号控制设定

在fork()之前对信号进行处理,子进程会继承父进程对信号控制的设定

在这里插入图片描述

对父子进程分别发送SIGTERM命令,父进程对SIGTERM命令做了捕获处理,当kill PID时会打印其进程号
可以看到kill子进程(PID=20180),子进程的信号处理也是捕获
对父子进程分别发送SIGINT终止进程

在这里插入图片描述

exec对信号控制设定的影响

子进程调用exec函数族加载新程序时,若信号的处理方式为捕获,则被改为默认动作
但是信号处理方式为默认和忽略时,子进程依然继承。
因为exec运行新的程序后会覆盖从父进程继承来的存储映像,那么信号捕捉函数在新程序中已无意义
所以exec会将原先设置为要捕捉的信号都更改为默认动作。

子进程执行while程序,while程序是自己编写的死循环程序

在这里插入图片描述
在这里插入图片描述

对父进程发送SIGTERM信号,处理方式为捕获

在这里插入图片描述

对子进程发送SIGTERM信号,子进程终止,因为此时父进程还未终止,所以子进程为僵尸进程,状态为Z

在这里插入图片描述

发送信号

进程也有向自己或其他进程发送信号的需求,但大多数还是接受信号的情况比较多。

alarm(2)

NAME
       alarm - set an alarm clock for delivery of a signal

SYNOPSIS
       #include <unistd.h>

       unsigned int alarm(unsigned int seconds);
       //当设定的时间倒数结束,alarm()会像当前进程发送SIGALRM信号。
       //函数会返回在新调用alarm时前一次调用alarm剩余的秒数,如果之前没有调用alarm则返回0
       //比如   alarm(5) ;
       			sleep(2);
       			alarm(6);
       			alarm(4);
       //第一个alarm返回值为0,因为之前没有调用过alarm,
       //第二个alarm返回值为3,因为第一个倒数两秒后重新设置了闹钟,剩余三秒
       //第三个alarm返回值为6,因为还没等第二个开始倒计时就被设置了闹钟,还剩余六秒

pause(2)

NAME
       pause - wait for signal

SYNOPSIS
       #include <unistd.h>

       int pause(void);
		//只在捕获信号并返回信号捕获函数时返回-1,并且error被设置为EINTR

kill(2)

NAME
       kill - send signal to a process

SYNOPSIS

       int kill(pid_t pid, int sig);
       //向pid进程发送sig信号
       //成功返回0,失败返回-1

raise(3)

NAME
       raise - send a signal to the caller //调用kill(2)实现的,向当前进程发送信号

SYNOPSIS
       #include <signal.h>

       int raise(int sig);
       //等价于kill(getpid(),sig);
	   //成功返回0,失败返回非零数

abort(3)

NAME
       abort - cause abnormal process termination

SYNOPSIS
       #include <stdlib.h>

       void abort(void);
       //向当前进程发送SIGABRT信号,默认处理方式是终止

信号集

信号有几个状态,产生,递达(信号到达进程),未决(到达进程,但是进程还不能处理该信号)

为了处理递达和未决时内核对信号的处理方式,每个进程的PCB都有信号屏蔽字和未决信号集两张信号集表。
信号屏蔽字,0表示信号是打开的,可以立即处理,1表示信号被屏蔽,阻塞等待。
未处理信号集,结构与信号屏蔽字相同,专门用来记录未处理的信号。

如图,信号递达,通过该进程的信号屏蔽字查询该信号是否可以处理,如果可以处理则将该信号在信号屏蔽字的位置设为1
当处理完后设为零

如果此时信号在处理中,然后又发送了同样的信号,这时信号屏蔽字为1,内核还处理不了后来的信号,所以在未决信号集设1
当之前的信号处理完毕,信号屏蔽字设置为0,内核会查看未决信号集是否有状态为1的信号,如果有就处理,并置0。

在这里插入图片描述

设置SIGINT为捕获后,在捕获函数还未结束,不管发送多少次SIGINT信号都不会有反应

在这里插入图片描述
在这里插入图片描述

修改信号屏蔽字

如果需要发送信号立即处理或其他操作,可以手动修改信号屏蔽字将指定位置设为0或1
以上面的情景,如果想使得只要SIGINT发送就执行捕获函数,就要每次发送一个SIGINT信号就手动将信号屏蔽字置0,使后面的发送的信号直接处理
先创建一个变量,将信号所在信号集的位置置为0,其他位置置1,然后将此变量和信号屏蔽字作与运算,若想置1,就其他位置置0,然后作或运算
利用系统和库的API实现
NAME
       sigemptyset, sigfillset, sigaddset, sigdelset, sigismember - POSIX sig‐
       nal set operations

SYNOPSIS
       #include <signal.h>

       int sigemptyset(sigset_t *set);
		//将变量set的64位全部设为0
       int sigfillset(sigset_t *set);
		//全部设为1
       int sigaddset(sigset_t *set, int signum);
		//将signum信号所在的那一位设置为1,其余不变
       int sigdelset(sigset_t *set, int signum);
		//将signum信号所在的那一位设置为0,其余不变
      

		//成功返回0,失败返回01

sigprocmask(2)

NAME
       sigprocmask, rt_sigprocmask - examine and change blocked signals  
       							//将设置好的set变量修改信号屏蔽字     							

SYNOPSIS
       #include <signal.h>

       /* Prototype for the glibc wrapper function */
       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  
       //how修改方式,有三种
       //	SIG_BLOCK:屏蔽某个信号   即屏蔽字=屏蔽字|set
       //	SIG_UNBLOCK:打
[video(video-33XzawTr-1591968177877)(type-edu_course)(url-https://edu.csdn.net/course/blogPlay?goods_id=14277&blog_creator=weixin_44568462&marketing_id=84)(image-https://img-bss.csdnimg.cn/2020422112656862_91070.png?imageMogr2/auto-orient/thumbnail/400x269!/format/png)(title-150讲轻松搞定Python网络爬虫)]
[video(video-fQHJVgqC-1591968196507)(type-edu_course)(url-https://edu.csdn.net/course/blogPlay?goods_id=14277&blog_creator=weixin_44568462&marketing_id=84)(image-https://img-bss.csdnimg.cn/2020422112656862_91070.png?imageMogr2/auto-orient/thumbnail/400x269!/format/png)(title-150讲轻松搞定Python网络爬虫)]

开某个信号 即屏蔽字=屏蔽字|(~set)
       //	SIG_SETMASK:直接使用set的值替换掉屏蔽字
       
	   //oldset保存之前屏蔽字的值,NULL为不保存
在捕获函数中修改信号屏蔽字

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值