Linux信号

本文详细探讨了Linux信号的相关知识,包括kill命令的使用、alarm信号的触发、信号的种类和处理行为,以及信号在进程间通信中的角色。重点讲解了signal函数、SIGUSR1/SIGUSR2的用法,并讨论了信号处理中可能遇到的时序竞争和异步I/O问题,还提到了SIGCHLD信号的处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.kill:

	kill -l									查看所有信号
	man 7 signal					查看man手册信号章节

前32个信号是Unix系统经典信号,与软件,操作系统相关, 后32个是实时信号, 驱动编程时使用

	#include <sys/types.h>
	#include <signal.h>
	int raise(int sig);				向自己发送信号
	void abort(void);				向自己发送SIGABRT信号
	int kill(pid_t pid, int sig);
		pid > 0
			sig发送给ID为pid的进程
		pid == 0 
			sig发送给与发送进程同组的所有进程
		pid < 0
			sig发送给组ID为|-pid|的进程, 并且发送进程具有向其发送信号的权限
		pid == -1
			sig发送给发送进程有权限向他们发送信号的系统上的所有进程
		sig为0时, 用于检测, 特定为pid进程是否存在, 若不存在, 返回-1
	若成功,至少一个信号发送成功,返回0, 否则返回-1, 并设置errno

2.alarm:

	#include <unistd.h>
	
	unsigned int alarm(unsigned int seconds);
	alarm函数用来计时, 计时seconds秒, 返回值是未计时的秒数,比如计时60秒, 已经计时了10秒,
	由于某种原因停止计时, 那么就返回50,当计时完毕, alarm会向调用它的进程发送SIGALRM信号

信号产生种类

	ctl + c		SIGINT						从键盘的输入中断
	ctl + z		SIGTSTP					终端暂停信号
	CTL + \		SIGQUIT					从键盘输入退出

进程处理信号的行为

	默认动作(Action):
			A      缺省动作是结束进程.
			B      缺省动作是忽略这个信号.
			C      缺省动作是结束进程, 并且核心转储.
			D      缺省动作是停止进程.
			E      信号不能被捕获.
			F      信号不能被忽略.
	忽略:
	捕捉:
	SIGKILL, SIGSTOP这两个信号不能被捕捉, 阻塞, 忽略

信号作用的过程:

	产生的信号先记录在PEND未决信号集中,再查看Block阻塞信号集(信号屏蔽字)的对应位, 
	若对应位为1,则阻塞该信号,否则进程执行该信号默认的Action
	未决态 : 信号产生未被响应
	递达态 : 信号产生且被响应
	注意: 1. 未决信号集由内核自动设置, 用户只能读未决信号集, 但是不能设置
			2. 用户可以设置阻塞信号集

3.信号集处理函数:

	sigset_t为信号集, 可用sizeof(sigset_t)查看大小
	#include <signal.h>

	int sigemptyset(sigset_t *set);					信号集全部位置为0
	int sigfillset(sigset_t *set);					信号集全部位置为1				
	int sigaddset(sigset_t *set, int signum);		信号集指定信号位置为1
	int sigdelset(sigset_t *set, int signum);		信号集指定信号位置为0
	int sigismember(const sigset_t *set, int signum);	判断指定信号位是否置1
	函数不直接操作信号集, 而是构造一个sigset_t信号集, 然后通过"注册"的方式, 将构造的
	信号集变成Block阻塞信号集

信号集注册函数:

	调用函数可以读取或更改进程的Block阻塞信号集(信号屏蔽字)
	#include <signal.h>
	
	int  sigprocmask(int  how, const sigset_t *set, sigset_t *oldset);
	若成功则返回0, 否则返回-1
		how的取值:
			SIG_BLOCK		set包含了我们希望添加到当前信号屏蔽字的信号, 相当于mask=mask | set
			SIG_UNBLOCK		set包含了我们希望从当前信号屏蔽字中解除阻塞的信号, 相当于mask=mask &~set
			SIG_SETMASK		设置当前信号屏蔽字为set所指向的值, 相当于mask=set
	如果调用sigprocmask解除了对当前若干个未决信号的阻塞, 则在sigprocmask返回前, 至少将其中一个信号递达

4.sigaction
收到指定信号后, 执行用户定义的操作
#include <signal.h>

	int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
			sigaction结构体:
				struct sigaction {
				void     (*sa_handler)(int);	//可以被设置SIG_DFL执行默认动作,SIG_IGN忽略该信号
				void     (*sa_sigaction)(int, siginfo_t *, void *);
				sigset_t   sa_mask; //阻塞信号集
				int        sa_flags;
				void     (*sa_restorer)(void);
			};
			sa_handler是早期的捕捉函数, sa_sigaction是新加入的捕捉函数,二者不可同时调用
			通过设置sa_flags, 置0调用第一个, 置1调用第二个
	注意:   1.在执行sa_handler时, 操作系统会自动把捕捉的信号的信号屏蔽字置1,再次到来的被捕捉信号记录在未决信号集中
			sa_handler执行完毕后, 被捕捉信号的信号屏蔽字又被置为0, 未决信号集中的信号立即响应再次执行sa_handler
			2.用户设置的sa_mask是临时的, 只有在执行sa_handler时会生效, 在sa_handler执行时, 产生的被临时阻塞的信号
			会记录在未决信号集中, sa_handler执行完毕后, 被临时阻塞的信号会立即生效

5.SIGUSR1/SIGUSR2

	利用操作系统提供给用户来自定义动作的信号, 可以实现父子进程同步

6.signal(C标准库提供的函数)

	#include <signal.h>
	
	typedef void (*sighandler_t)(int);
	sighandler_t signal(int signum, sighandler_t handler);
	signal更适合跨平台项目, 功能不如sigaction强大

7.system(C标准库提供的函数)

	#include <stdlib.h>
	
	int system(const char *command);
	集fork, exec, wait 于一体

8.可重入函数/不可重入函数

	所谓可重入函数是指一个可以被多个任何调用的过程,任务在调用时不必担心数据是否出错。因为进程在收到信号后,就将跳转到信号处理函数
	去接着执行,如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函
	数返回接着执行时,可能会出现不可预料的后果,不可重入函数在信号处理函数中被视为不安全函数
	信号处理函数禁止使用不可重入函数
	
	不含全局变量和静态变量是可重入函数的一个要素
	strtok_r
			#include <string.h>
			
			char *strtok(char *str, const char *delim);
			tok 是一个不可重入函数
			char *strtok_r(char *str, const char *delim, char **saveptr);
			strtok_r 是一个可重入函数

9. 信号引起的时序竟态和异步IO问题

	pause 引发的问题: 时序竟态
			#include <unistd.h>
			int pause(void);
					使调用进程挂起, 直到有信号递达, 如果递达信号是忽略, 则继续挂起
			#include <signal.h>
			int sigsuspend(const sigset_t *mask);
					以通过指定mask来临时解除对某个信号的屏蔽, 然后挂起等待, 当sigsuspend返回,mask恢复为原来的值
	避免异步IO的类型
			sig_atomic_t
					平台下的原子类型, 平台有多少位, 该类型就占多少位
			volatile
					防止编译器开启优化选项时, 优化对内存对齐的读写

10.SIGCHLD信号处理

	SIGCHLD产生条件:
			子进程终止时
			子进程接收到SIGSTOP信号停止时
			子进程处在停止态, 接收到SIGCONT后唤醒时
	
	子进程在以上情况时都会发送SIGCHLD信号, 父进程想知道是那种情况, 需要用到status
	status处理方式:
			pid_t waitpid(pid_t pid, int *status, int options);
			
			status:
			WIFEXITED(status)				如果子进程正常终止,返回真
			WEXITSTATUS(status)		返回子进程的正常退出值
			WIFSIGNALED(status)		子进程被信号终止, 返回真
			WTERMSIG(status)			返回终止子进程的信号值
			WIFSTOPPED(status)			子进程被停止, 返回真
			WSTOPSIG(status)			返回停止子进程的信号值
			WIFCONTINUED(status)	子进程由停止态转为就绪态, 返回真
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值