Linux 信号

信号的概念

· 信号的目的是用来通讯
· 信号是异步的,信号就类似于软件中断
· 信号是发生事件时对进程的一种通知机制
· 信号本质上是 int 类型的数字编号,只不过一般使用宏把具体的数值封装起来,方便开发者使用
· 在子进程调用 exec 类函数之前,子进程会继承父子进程的信号处理绑定的信号处理函数,但是调用 exec 类函数后就不会继承信号处理函数

信号的种类

· 使用 kill -l 命令查询信号

不可靠信号 非实时信号

· Linux 下的不可靠信号的主要问题是指信号可能会丢失
· 信息阻塞等待在信号队列集合中,集合中不允许有相同的元素存在,所以相同的元素会被丢弃掉,造成信号丢失
· 一般不可靠信号都是进程管理相关的信号,比如终止进程,即使有多个终止进程的信号,进程也只能终止一次,所以忽略掉未必就不好

 1) SIGHUP	 
 2) SIGINT	 终止前台进程组,通常是使用 ctrl + c 时,内核将发送 SIGINT 信号给前台进程组中的每一个进程,该信号的默认处理方式是终止进程的运行
 3) SIGQUIT	终止前台进程组,并生成可调试的核心转储文件,当进程陷入无限循环时,调用  SIGQUIT 信号就非常合适,对应的快捷键是 ctrl + \
 4) SIGILL	 非法指令终止,当进程试图执行非法(格式不正确)的机器语言指令,系统会向该进程发送 SIGILL 信号,终止程序的运行
 5) SIGTRAP
 6) SIGABRT	 进程异常终止 当进程调用 abort 系统调用是(进程异常终止),系统会向该进程发送 SIGABRT 信号,终止进程并产生核心转储文件
 7) SIGBUS	 总线错误,内存访问错误 产生该信号(总线错误)表示发生了某种内存访问的错误,默认是终止进程的运行
 8) SIGFPE	 算术错误 当进程出现算术错误时产生,比如除 0 或是是对浮点数取 % 的操作,默认系统操作是终止进程
 9) SIGKILL	用于杀死进程,此信号无法被进程阻塞、忽略或是捕获,但对于僵尸进程即使是 kill -9 也无法杀死,他和 windows 的任务管理器杀死进程一样,是最后杀死进程的手段,而且大部分情况一击必杀
10) SIGUSR1 程序员自定义使用信号,内核绝对不会产生这种信号,默认系统处理方式是终止进程
11) SIGSEGV	内存无效终止程序 当程序对内存的引用无效时,操作系统会向该程序发送 SIGSEGV ,默认处理方式的终止程序的运行。如 C 中引用的事件中包含了错误的地址或是传递了一个无效的参数提供给函数调用等
12) SIGUSR2	程序员自定义使用信号,内核绝对不会产生这种信号,默认系统处理方式是终止进程
13) SIGPIPE	管道、FIFO或是套接字已关闭时,还写入数据操作系统就会向线程发送终止信号,默认处理是终止进程
14) SIGALRM	内核定时器定时执行完成标志 与系统调用 alarm 和 setitimer 有关,应用程序可以调用 alarm 和 setitimer 设置一个定时器,定时器的定时时间到了,内核就会将 SIGALRM	 信号发送给该应用程序,默认的信号处理是终止程序
15) SIGTERM  标准的进程终止信号 良好的应用程序应该捕获该信号,并绑定程序终止处理函数,将程序对应的前台进程组先释放掉,再释放后台进程组还有其他动态申请的资源或是注册到内核的对象要注销掉等等,正确的终止程序的方法就应该是先发送  SIGTERM  信号到程序中,程序依旧无法正常终止才使用 kill -9 强制杀死应用程序
16) SIGSTKFLT	
17) SIGCHLD	子进程终止运行 当父进程的某一个子进程终止运行时,内核会向父进程发送 SIGCHLD	 信号;当父进程的某一个子进程因收到信号而停止或是恢复运行时,内核也可能向父进程发送该信号;默认信号处理是忽略此信号
18) SIGCONT	恢复信号 将该信号发送给已停止进程,进程就会恢复运行。如果进程收到此信号并不处于停止状态,默认处理信号的方式是忽略该型信号
	由于 SIGCONT 信号是不可靠信号,非实时的,所以通过 SIGCONT 信号回收子进程,就可能会出现部分子进程未回收的情况发生,未回收的子进程变成僵尸进程,但是可以使用轮询的方式,每次捕获 SIGCONT 就轮询回收子进程,直到无子进程终止为止
19) SIGSTOP	强制停止信号 用于进程的强制停止,进程无法将该信号忽略或是捕获,所以是强制停止
20) SIGTSTP 停止前台进程组 快捷键 ctrl + z 时,系统发送 SIGTSTP 停止前台进程组的运行
21) SIGTTIN	
22) SIGTTOU	
23) SIGURG	
24) SIGXCPU	进程的 CPU 时间超出对应资源限制 当程的 CPU 时间超出对应资源限制,内核会将 SIGXCPU	信号发送给该进程
25) SIGXFSZ
26) SIGVTALRM	虚拟定时器定时到了的标志 应用程序调用 setitimer 函数设置一个虚拟定时器,定时器的定时时间到了,内和发送 SIGVTALRM 信号到进程中
27) SIGPROF	
28) SIGWINCH	终端窗口尺寸变化时	比如用户手动调整了终端窗口的大小或是进程调用了 ioctl 函数设置了终端窗口的大小,操作系统都会向前台进程组中的每个进程发送 SIGWINCH	信号
29) SIGIO 异步 IO 事件的发生,和SIGPOLL 是同义的	比如应用程序打开的文件描述符发生了 IO 事件时,内核会向应用程序发送 SIGIO 信号
30) SIGPWR
31) SIGSYS	系统调用有误 如果进程发起的系统调用有误,那么内核将会发送 SIGSYS 信号到该进程

可靠信号 实时信号

· 可靠信号支持队列,不会丢失
· RT 表示 实时的
· 实时信号都可以做程序自定义使用
· 实时信号对阻塞的信号是使用队列管理,而不是像非实时信号那样使用信号集管理,所以实时信号不存在丢失的情况发送
· 发送应该实时信号时还可以指定信号的伴随数据(一个整型的数据或指针),供接收信号的进程在信号处理函数中获取
· 优先传递信号编号小的信号(信号的优先级),同编号的信号保持传递顺序不变

34) SIGRTMIN	
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	

sigpending 查询阻塞信号

· sigpending 函数用来查询阻塞的信号

#include <signal.h>

int sigpending(sigset_t *set);

· int :执行成功返回 0,执行失败返回 -1,并设置 errno 的值
· sigset_t *set :将处于等待阻塞队列集合中的信号放在信号集缓冲区中

发送非实时信号

kill 向进程发送信号

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

· int :执行成功返回 0,执行失败返回负值,并设置 errno 的值
· pid_t pid:指定发送的进程对象的 PID,PID 也可以为负值或是 0
· PID > 0,则将信号发送给指定的进程
· PID = 0,则将信号发送给当前进程的进程组中的每个进程
· PID = -1,则将信号发送给当前进程有权发送信号的每个进程,但进程 1(init)除外
· PID < -1,则将信号发送给 ID 为 -PID 的进程组中的每个进程
· int sig:要发送的信号,也可以设置为 0,表示不发送信号,但仍然会进行错误检测,这通常用于检测参数 PID 指定的进程是否存在

raise 向自身发送信号

· raise 函数等效于 kill(getpid(), sig);

#include <signal.h>

int raise(int sig);

· int :执行成功返回 0,执行失败返回负值,并设置 errno 的值
· int sig:要发送的信号

alarm 闹钟定时器

· alarm 是进程的闹钟定时器,一个进程只能拥有一个闹钟定时器
· alarm 闹钟定时器时间到时,会向当前进程发送一个 SIGALRM 的信号,该信号的默认处理是终止进程;但绝大多数使用 alarm 闹钟都是会捕获该信号,执行自定义的信号处理函数

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

· unsigned int:执行成功返回上次设置闹钟的闹钟剩余时间,如果是第一次设置闹钟则返回 0;旧的闹钟将被新的闹钟取缔
· iunsigned int seconds:设置闹钟定时的时间,以秒为单位;如果 seconds 对于 0 的话,取消之前设置的 alarm 闹钟

pause 暂停等待信号

· pause 是系统调用可以使进程暂停运行,进入休眠状态,直到进程捕获到一个信号为止,只有执行了信号处理函数并返回后,pause 函数才返回,这种情况下,pause 函数返回 -1,并设置 errno 的值

#include <unistd.h>

int pause(void);

· int:pause 在信号处理函数执行完成后,返回 -1

发送实时信号

sigqueue 发送信号

· sigqueue 函数用于实时信号和伴随数据的发送

#include <signal.h>

union sigval {
    int sival_int;    /* 整数类型的值 */
    void *sival_ptr;  /* 指针类型的值 */
};

int sigqueue(pid_t pid, int sig, const union sigval value);

· int : 执行成功返回 0,执行失败返回 -1
· pid_t pid : 指定要发送的进程 pid
· int sig : 指定要发送的信号;也可以将信号设置为 0 检查进程是否存在
· const union sigval value : 伴随数据

信号处理

信号处理的方式

· 默认信号处理:默认使用操作系统的默认方式处理信号
· 忽略信号处理:忽略信号,但 SIGKILL 和 SIGSTOP 信号绝不能做忽略处理
· 捕获信号处理:捕获信号,自定义信号的处理方式

signal 通用信号处理模式选择

· signal 函数是一个系统调用,执行出错会修改 errno 的值

#include <signal.h>

typedef void (*sig_t)(int);		/* int 形参是接收信号 */
#define	SIG_ERR	((sig_t) -1)	/* error return */
#define	SIG_DFL	((sig_t) 0)		/* default action */
#define	SIG_IGN	((sig_t) 1)		/* ignore signal */

sig_t signal(int signum, sig_t handler);
· sig_t : 执行成功返回指向参数的信号处理函数的指针,执行失败返回 SIG_ERR,并设置 errno
· int signum : 指定要设置的信号,除了 SIGKILL 和 SIGSTCOP 外的信号
· sig_t handler : 指定信号的处理方式,可以是宏定义的系统默认处理模式或是忽略处理模式,也可以自行指定信号处理函数

sigaction 高级信号处理模式选择

· sigaction 函数有更高的灵活性和可移植性
· sigaction 允许单独获取信号的处理函数而不是设置,并且还可以设置各种属性对调用信号处理函数时的行为施加更精准的控制
· 处理实时信号时需要使用 sa_sigaction 信号处理函数,并设置好信号选择标志位,因为实时信号还有伴随数据
· sigaction 获取指定信号的之前的捕获处理方式

#include <signal.h>

struct siginfo_t {
	int si_signo; 	/* Signal number */
	int si_errno; 	/* An errno value */
	int si_code; 	/* Signal code 触发信号的原因,有具体的宏定义触发信号的原因*/
	int si_trapno; 	/* Trap number that caused hardware-generated signal(unused on most
	architectures) */
	pid_t si_pid; 	/* Sending process ID */
	uid_t si_uid; 	/* Real user ID of sending process 发送信号的用户 ID*/
	int si_status; 	/* Exit value or signal 只对 SIGCHLD 信号有效,表示子进程退出时的状态*/
	clock_t si_utime; 	/* User time consumed 只对 SIGCHLD 信号有效,表示子进程终止时消耗的 用户CPU时间 */
	clock_t si_stime;	/* System time consumed 只对 SIGCHLD 信号有效,表示子进程终止时消耗的 系统CPU时间 */
	sigval_t si_value; 	/* Signal value 伴随数据 */
	int si_int; 		/* POSIX.1b signal 伴随数据中整型变量 */
	void *si_ptr; 		/* POSIX.1b signal 伴随数据中指针 */
	int si_overrun; 	/* Timer overrun count; POSIX.1b timers */
	int si_timerid; 	/* Timer ID; POSIX.1b timers */
	void *si_addr; 		/* Memory location which caused fault */
	long si_band; 		/* Band event (was int in glibc 2.3.2 and earlier) 只对 SIGIO 有效, */
	int si_fd; 			/* File descriptor 只对 SIGIO 有效, */
	short si_addr_lsb; 	/* Least significant bit of address(since Linux 2.6.32) */
	void *si_call_addr; /* Address of system call instruction(since Linux 3.5) */
	int si_syscall; 	/* Number of attempted system call(since Linux 3.5) */
	unsigned int si_arch; 	/* Architecture of attempted system call(since Linux 3.5) */
};

struct sigaction {
	void (*sa_handle)(int)	/* 指定要信号要处理的函数 */
	void (*sa_sigaction)(int, siginfo_t *, void *contex)	/* 指定要信号要处理的函数,与 sa_handle 是互斥的,他提供了更多的参数传递 */
	/* contex 是一个进程的上下文 */
	sigset_t sa_mask;	/* 信号集掩码 */
	int sa_flags;		/* 选择信号处理函数的类型 */
/*	
	· sa_flags 可以 | 多个宏定义来进行标志
		· SA_NOCLDSTOP 不再接收 SIGCHLD 信号
			· 子进程停止、恢复或是终止运行时,父进程都会收到内核发送的 SIGCHLD 信号,使用此标志位内核将不会发送 SIGCHLD 信号提示父进程子进程的运行状态发生变化
		· SA_NOCLDWAIT 无法转换为僵尸进程
			· 使用此标志子进程在运行终止时不会转化为僵尸进程,而是在子进程终止运行时直接由系统回收资源,不等待父进程获取子进程运行终止的状态
		· SA_NODEFER 不阻塞当前处理的信号
			· 当进程在处理某个信号时,默认会将该信号加入该进程的信号掩码字段中, 从而阻塞信号处理函数期间阻断该信号。默认情况下我们是期望进程在处理一个信号时阻塞同种信号的,否则会引发一些竞态的问题。如果设置了 SA_NODEFER 将不对当前正在处理的信号设置阻塞
		· SA_RESETHAND 单次处理模式
			· 执行完信号处理函数后,将信号的处理方式设置为默认处理方式
		· SA_RESTART 信号中断的系统调用重新执行
			· 被信号中断的系统调用,将信号处理完成之后将自动重新发起
		· SA_SIGINFO 使用 sa_sigaction 信号处理函数
*/
	void (*sa_restorer)(void);	/* 已废弃 */
};


int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
· int : 执行成功返回 0, 执行失败返回负值,并设置 errno
· int signum : 指定要设置的信号,除了 SIGKILL 和 SIGSTCOP 外的信号
· const struct sigaction *act : struct sigaction 描述了信号的处理方式,如果为 NULL 表示无需改变信号当前的处理方式
· struct sigaction *oldact : struct sigaction 如果 old 不为 NULL,则会将信号之前的处理方式等信息通过 oldact 返回来; 如果为 NULL,则不获取该信息

sigset_t 信号集合 / 信号掩码

· sigset_t 信号集合也是集合中的一种,集合中不允许有相同的元素出现
· 使用信号集合的一般是不可靠信号,信号有丢失的现象存在,就是因为阻塞的信号集不允许有相同的元素出现在集合中
· 信号掩码只包含非实时信号,实时信号不允许被加入到信号掩码
· 即使你暂时移除了某个信号掩码中的信号,在进程接收到该信号时,系统会直到将该信号加入到信号掩码中

初始化信号集

sigemptyset 空初始化

· sigemptyset 函数初始化信号集,使其不包含任何信号

#include <signal.h>

int sigemptyset(sigset_t *set);
· int:执行成功返回 0,执行失败返回 -1,并设置 errno 的值
· sigset_t *set :要初始化的 sigset_t 对象

sigfillset 全包含初始化

· sigfillset 函数初始化信号集,使其包含所有信号,包括所有的实时信号

#include <signal.h>

int sigfillset(sigset_t *set);
· int:执行成功返回 0,执行失败返回 -1,并设置 errno 的值
· sigset_t *set :要初始化的 sigset_t 对象

修改信号集

sigaddset 添加信号

· sigaddset 函数可以向信号集合中添加信号

#include <signal.h>

int sigaddset(sigset_t *set, int signum);
· int:执行成功返回 0,执行失败返回 -1,并设置 errno 的值
· sigset_t *set :操作的 sigset_t 信号集对象
·  int signum :要从信号集添加的信号

sigadelset 删除信号

· sigadelset 函数可以向信号集合中删除信号

#include <signal.h>

int sigadelset(sigset_t *set, int signum);
· int:执行成功返回 0,执行失败返回 -1,并设置 errno 的值
· sigset_t *set :操作的 sigset_t 信号集对象
·  int signum :要从信号集删除的信号

sigismember 测试信号集

· sigismember 函数可以测试该信号是否在对应的信号集中

#include <signal.h>

int sigismember(const igset_t *set, int signum);
· int:如果信号在对应的信号集中,则返回 1;不存在返回 0,失败返回 -1,并设置 errno 的值
· const sigset_t *set :要测试的 sigset_t 信号集对象
·  int signum :要测试的信号对象

abort 异常信号终止进程

· abort 会使调用的进程,异常终止
· abort 会发送 SIGABRT 信号到当前进程,即使是捕获 SIGABRT 或是忽略还是添加信号集中,进程都会异常终止
· abort 函数会在 SIGABRT 信号处理函数执行完成后,将 SIGABRT 信号处理的方式设置为系统默认处理方式,也就是异常终止进程,并产生转储文件,然后再次向本进程发送 SIGABRT 信号,然后进程异常终止

#include <stdlib.h>

void abort(void);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值