Linux操作系统下信号处理函数

Linux系统下的信号

一、信号概念

1. 什么是信号
信号是软件中断,信号提供了一种异步处理事件的方法,它允许进程和内核中断其他进程。一个信号就是一条消息,它通知进程系统发生了一个某种类型的事件,在Linux系统中支持31中不同类型的信号
在我这篇文章中给大家列出了Linux系统支持的31个信号(https://blog.csdn.net/qq_34934140/article/details/116550027)。

每种信号都对应不同的系统类型,底层硬件异常是由内核异常处理程序处理的,正常情况下对用户是不可见的,信号提供了一种机制,通知用户进程发生了这些异常。每个信号都有自己的名字,他们都是以SIG开头,这些信号都包含在<signal.h>中。

产生信号的事件相对于进程来说是不可预见的,所以进程不可以测试一个变量来判断信号的产生,而必须告诉内核当信号发生时进程该如何处理。在信号出现时有以下三种处理方式
1)忽略信号,大多数信号采用这种处理方式,但是有两种信号不可用忽略,SIGKILL 和 SIGSTOP,因为他们向内核和超级用户提供了可靠的进程终止或停止的方法。
2)捕捉信号,通知内核在信号发生时调用一个用户函数,在用户函数中执行用户希望对该事件的处理方式。同样SIGKILL 和 SIGSTOP不可以被捕捉。
3)执行默认动作,系统会给所有信号一个默认动作,在(https://blog.csdn.net/qq_34934140/article/details/116550027)里面我已经详细列出。

2.信号术语
1)发送信号
内核通过更新目的进程上下文中的某个状态,发送一个信号给目的进程。发送信号的原因
a. 内核检测都一个系统事件,如硬件异常或子程序终止
b. 一个进程调用kill函数,显示地要求内核发送一个进程给目的进程,或者进程自己给自己发送信号。
2)接收信号
当目的进程被内核强迫以某种方式对信号的发送做出反应时,它就接收了信号。进程可以忽略这个信号也可以捕捉它做一个信号处理函数。
3)待处理信号
一个发出而没有被接收的信号,任一时刻,一种类型最多只会有一个待处理信号,如果一个进程此时有一个类型为k的处理信号,那么接下来发送到此进程的所有k类型信号都不会排队等待
,他们都会被丢弃,进程可以选择阻塞接收某种信号,当一个信号被阻塞时,它仍然可以被发送,但是不会被接收,所有产生了待处理信号,直到进程取消对该进程的阻塞。一个待处理信号最多被
接收一次,在内核中有一个pending位向量维护着待处理信号集合,在blocked位向量中维护着被阻塞的信号集合,只要传送一个k类型信号,内核就会设置pending的第k位,接收一个k信号就会清楚
pending的k位。
4)阻塞信号
Linux提供阻塞信号的隐式和显式机制
隐式阻塞机制:内核默认阻塞任何当前处理程序正在处理信号类型的待处理信号
显式阻塞机制:应用程序可以使用sigprocmask函数和它的辅助函数,明确地阻塞和解除阻塞选定的信号
5)不可靠的信号
不可靠指的是信号可能会丢失,一个信号发生了,但你是进程却不知道。
6)可靠信号术语和语义
未决:信号产生和递送之间的时间间隔内,称信号是未决的,调用sigpending函数可以查看哪些信号被设置为阻塞并且是未决的。
信号屏蔽字:每个信号都有,它规定了当前阻塞递送到该进程的信号集,每种信号对应屏蔽字中的一位,使用sigprocmask函数来查看、设置、修改。
7)作业控制信号
SIGCHLD 子进程已停止或终止
SIGCONT 如果进程停止则继续运行
SIGSTOP 停止信号
SIGTSTP 交互式停止信号
SIGTTIN 后台进程组成员读控制终端
SIGTTOU 后台进程组成员写控制终端
除了SIGCHLD之外,大多数应用程序不处理这些信号,交互式shell通常会处理这些信号。
8)信号集
一个可以表示多个信号的数据类型

3.编写信号处理函数基本规则
处理程序和主程序并发执行,他们共享全局变量,因此可能与主程序和其他处理程序相互干扰,所以需要编写安全、正确、可移植的信号处理程序。
1)安全的信号处理
a.处理程序尽可能简单,
b.在处理程序中只调用异步信号安全的函数
c.保存和恢复,在处理程序时把errno保存在一个局部变量中,返回时恢复。
d.阻塞所有信号,保护对共享全局数据结构的访问。
e.用volatile声明全局变量,
f.用sig_atomic_t声明标志,保证读写不可中断

二、信号调用函数

1. signal函数
	#include <signal.h>
	typedef void (*sighandler_t)(int);
	
	sighandler_t signal(int signum, sighandler_t handler);
	功能:设置某一信号对应动作
	参数:signum:信号名
		  handler:信号处理函数,当信号发生时调用此函数。
	返回值:成功返回处理函数的指针,失败返回SIG_ERR。
		
2. 发送信号函数(kill函数和raise函数)
	#include <sys/types.h>
	#include <signal.h>

	2.1 int kill(pid_t pid, int sig);
		功能:将信号发送给进程或者进程组
		参数:pid > 0:发送给进程的进程ID
			  pid = 0: 将信号发送给发送进程所属进程组的所有进程
			  pid < 0: 发送给pid绝对值的进程
			  pid = -1:发送给可发送的所有进程
			  sig:信号名
		返回值:成功 0, 失败 -1.
		
	2.2 int raise(int sig);
		功能:允许进程 向自己发送信号
		参数:sig:信号名
		返回值:成功 0, 失败 -1.
		
3. 定时函数(alarm函数和pause函数)
	#include <unistd.h>
	 
	3.1 unsigned int alarm(unsigned int seconds);
		功能:设置一个定时器,当超时之后产生SIGALRM信号,如果忽略此信号,默认动作是终止此进程
		参数:seconds:时间,单位 秒
		返回值:0或者一起设置闹钟时间剩余的秒数
		注释:每个进程只能设置一个闹钟,如果调用alarm函数时,进程已经调用过了此函数则返回剩余时间数,时间被新值替换。
		
	3.2 int pause(void);
		功能:进程挂起直到捕捉到一个信号
		参数:无
		返回值:-1, errno 设置为 EINTR.
		
4. 信号集函数
	#include <signal.h>
	
	4.1	int sigemptyset(sigset_t *set);
		功能:初始化set指向的信号集,清除其中所有信号。所有应用程序在使用信号集前必须调用sigfillset或者sigemptyset
		参数:set;信号集
		返回值:成功 0, 失败 -1.

	4.2	int sigfillset(sigset_t *set);
		功能:初始化set指向的信号集,使其包括所有信号。所有应用程序在使用信号集前必须调用sigfillset或者sigemptyset
		参数:set;信号集
		返回值:成功 0, 失败 -1.

	4.3	int sigaddset(sigset_t *set, int signum);
		功能:添加一个信号
		参数:set;信号集
			  signum:信号
		返回值:成功 0, 失败 -1.

	4.4	int sigdelset(sigset_t *set, int signum);
		功能:删除一个信号
		参数:set;信号集
			  signum:信号
		返回值:成功 0, 失败 -1.

	4.5	int sigismember(const sigset_t *set, int signum);
		功能:测试一个指定位,因为信号没有编号为0,所有从信号编号中减去1得到要处理的位编号。
		参数:set;信号集
			  signum:信号
		返回值:成功 0, 失败 -1.

5.检测和修改信号屏蔽字

	#include <signal.h>

	int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
	功能:可以检测和修改进程的信号屏蔽字
	参数:how: SIG_BLOCK:该进程新的信号屏蔽字是set指向的信号集和当前信号屏蔽字的并集,阻塞信号
				SIG_UNBLOCK:该进程新的信号屏蔽字是set指向的信号集和当前信号屏蔽字的补集的交集,解除阻塞
				SIG_SETMASK:该进程新的信号屏蔽字是set指向的值
		  set:非空指针,则how修改当前信号屏蔽字
			    空指针:则how无意义
	
6.返回信号集函数:sigpending	
	#include <signal.h>

    int sigpending(sigset_t *set);
	功能:返回一信号集
	参数:set:信号集
	返回值:成功 0,失败 -1.
	
7.检测或修改以及指定信号相关联的处理动作
	#include <signal.h>

    int sigaction(int signum, const struct sigaction *act,
                 struct sigaction *oldact);
	功能:检测或修改以及指定信号相关联的处理动作
	参数:signum:要修改或检测的信号
		  act:非空指针:则要修改动作
			   为空:	 则由oldact指针返回信号上一个动作。
		  oldact:指针   
	返回值:成功 0,失败 -1.
	
	struct sigaction {
	   void     (*sa_handler)(int);
	   void     (*sa_sigaction)(int, siginfo_t *, void *);
	   sigset_t   sa_mask;
	   int        sa_flags;
	   void     (*sa_restorer)(void);
	};
	当更改信号动作时,sa_handler是信号处理函数,sa_sigaction是替代的信号处理程序,sa_mask是一个信号集,调用信号处理函数之前新将信号集加入到信号屏蔽字中,
	这样当正在处理信号时候,再来这种信号就会被阻塞,sa_flags指对信号进程处理的各个选项,有以下选项。
	SA_NOCLDSTOP:若signo是SIGCHLD,当子进程终止产生此信号。
	SA_NOCLDWAIT:若signo是SIGCHLD,当子进程终止不创建僵尸进程,若调用wait则阻塞到所有子进程都终止
	SA_NODEFER:当捕捉到信号时,在执行其信号处理函数时,系统不自动阻塞此信号
	SA_ONSTACK:此信号递送给替换栈上的进程。
	SA_RESETHAND:此信号捕捉函数入口处,将此信号处理方式重置为SIG_DFL,清除SA_SIGINFO标准
	SA_RESTART:此信号中断的系统调用自动重启
	SA_SIGINFO:提供了一个指向siginfo结构的指针和指向进程上下文的标识符指针。
	
8.非局部转移函数 sigsetjmp函数 和 siglongjmp函数		
	同setjmp函数longjmp函数功能一样,在 信号处理程序中调用siglongjmp跳回到主程序。不同之处是多了一个参数,如果savemask非0,则sigsetjmp在env中保存进程的当前信号屏蔽字
	。调用siglongjmp时,如果带非0 savemask的sigsetjmp调用已经保存env,则siglongjmp从其中恢复保存的信号屏蔽字。
	
	#include <setjmp.h>

    int sigsetjmp(sigjmp_buf env,int savemask);
	功能:可以保存当前执行线索状态
	参数:	env:存放调用siglongjmp时可以恢复栈状态的所有信息,通常为全局变量
			savemask:非0:env中保存当前进程信号屏蔽字
	返回值:直接调用返回 0,若从siglongjmp调用返回0.

    int siglongjmp(sigjmp_buf env, int val);
	功能:跳转函数
	参数:env:sigsetjmp调用的env
		  val:非0值
	
9.恢复信号屏蔽字的值
	#include <signal.h>

    int sigsuspend(const sigset_t *mask);
	功能:如果捕捉到一个信号而且信号处理函数返回,则sigsuspend返回,并且该信号屏蔽字设置为调用sigsuspend之前的值
	参数:由sigmask指向的值
	返回值:此函数没有成功返回值,如果返回到调用者则总是-1.
	
10.程序异常终止函数

	#include <stdlib.h>

    void abort(void);
	功能:此函数将SIGABRT信号发送给调用进程
11.信号名和信号编号		
    #include <signal.h>

    11. void psignal(int sig, const char *s);
		void psiginfo(const siginfo_t *pinfo, const char *s);
		功能:打印与信号编号对应的字符串
		参数:sig:信号名
			  s:  对应字符串
		
	11.2 #include <string.h>

		char *strsignal(int sig);
		功能:给定信号编号,返回信号字符串
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值