Linux信号处理方法signal,sigaction和 信号集

版权声明:转载请声明 https://blog.csdn.net/qq_40732350/article/details/82867483

信号的处理包括信号的发送、 捕获及信号的处理, 它们有各自相对应的常见函数。
 发送信号的函数: kill()、 raise()。
 捕捉信号的函数: alarm()、 pause()。
 处理信号的函数: signal()、 sigaction()。


信号处理的方法主要有两种,

  1. 使用 signal()和sigation()函数
  2. 使用信号集函数组


下面分别介绍这两种处理方式。
1) 使用 signal()函数


使用 signal()函数处理时, 只需指出要处理的信号和处理函数即可。 它主要用于前 32 种非实时信号的处理, 不支持信号传递信息, 但是由于使用简单、 易于理解, 因此也受到很多程序员的欢迎。 Linux 还支持一个更健壮更新的信号处理函数 sigaction(), 推荐使用该函数。


signal()函数语法要点
函数原型

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数传入值
signum: 指定信号代码
handler:

  1. SIG_IGN: 忽略该信号
  2. SIG_DFL: 采用系统默认方式处理信号
  3. 自定义的信号处理函数指针

函数返回值
成功: 以前的信号处理配置
出错: 1

sigaction()函数语法要点

函数原型

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
函数传入值
signum: 信号代码, 可以为除 SIGKILL 及 SIGSTOP 外的任何一个特定有效的信号
act: 指向结构 sigaction 的一个实例的指针, 指定对特定信号的处理
oldact: 保存原来对相应信号的处理
函数返回值
成功: 0
出错: 1

扫描二维码关注公众号,回复: 3429018 查看本文章

首先给出了 sigaction 的定义, 代码如下:
 

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

是一个函数指针, 指定信号处理函数, 这里除可以是用户自定义的处理函数外, 还可以为 SIG_DFL(采用默认的处理方式) 或 SIG_IGN(忽略信号)。 它的处理函数只有一个参数, 即信号值
sa_mask

是一个信号集, 它可以指定在信号处理程序执行过程中哪些信号应当被屏蔽,
在调用信号捕获函数前, 该信号集要加入到信号的信号屏蔽字中。
sa_flags

其中包含了许多标志位, 是对信号进行处理的各个选择项。 它的常见可选值如表 所示。

******************要了解sigaction 和 信号集  就应该先了解信号屏蔽:******************

问题:

由于当执行一个信号的处理函数时,会被其他信号给打断,这样就会让信号的处理不完整

解决:

利用信号屏蔽功能,当正在执行一个信号的处理时,凡是添加到信号屏蔽集中的信号,都会被阻塞,直到正在执行的信号处理完后才去处理阻塞的信号。

信号屏蔽就是临时阻塞信号发送至某个进程,它包含一个被阻塞的信号的信号集。

信号屏蔽与信号忽略不同,当进程屏蔽某个信号时,内核将不发送信号至屏蔽该信号的进程,当该信号屏蔽解除,就会执行该信号,

而对于被忽略的信号,内核发送该信号至进程,只是进程对忽略的信号不处理

注意:

信号屏蔽不是不会执行该信号处理,而是等当前进程处理的信号处理完,才执行

注意:

同一个信号集的信号之间是不会屏蔽

实例代码:

当运行处理信号函数my_fuction时,按下ctr+c,没有反应,当函数执行完后进程会直接结束

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>

void my_function(int arg)
{
	int i = 5;
	printf("this is %s \n", __FUNCTION__);
	for(; i > 0; i--)
	{
		printf("倒计时 [%d] second \n", i);
		sleep(1);		
	}

}
int
main(void)
{
	pid_t pid;
	int stat;
	struct sigaction new;
	struct sigaction old;
	
	new.sa_handler = my_function;
	new.sa_flags = 0;
	sigemptyset(&new.sa_mask);
	//sigaddset(&new.sa_mask, SIGINT);
	sigaction(SIGQUIT, &new, &old);
	printf("按下 (CTR + '\') 发送信号\n");
	for(;;)
	{
		pause();		
	}

	exit(0);
}

结果:

# ./a.out 
按下 (CTR + '') 发送信号
^\this is my_function 
倒计时 [5] second 
倒计时 [4] second 
^C
# ./a.out

当我加上sigaddset(&new.sa_mask, SIGINT);  

结果:

# ./a.out 
按下 (CTR + '') 发送信号
^\this is my_function 
倒计时 [5] second 
倒计时 [4] second 
^C倒计时 [3] second 
倒计时 [2] second 
倒计时 [1] second 


 

2) 信号集函数组

使用信号集函数组处理信号时涉及一系列的函数, 这些函数按照调用的先后次序可分为以下几大功能模块:

创建信号集、 注册信号处理函数及检测信号。
其中, 创建信号集主要用于处理用户感兴趣的一些信号, 其函数包括以下几个。
 sigemptyset(): 将信号集初始化为空。
 sigfillset(): 将信号集初始化为包含所有已定义的信号集。
 sigaddset(): 将指定信号加入到信号集中。
 sigdelset(): 将指定信号从信号集中删除。
 sigismember(): 查询指定信号是否在信号集中。


注册信号处理函数主要用于决定进程如何处理信号。 这里要注意的是, 信号集里的信号并不是真正可以处理的信号, 只有当信号的状态处于非阻塞状态时才会真正起作用。 因此,首先使用 sigprocmask()函数检测并更改信号屏蔽字(信号屏蔽字是用来指定当前被阻塞的一组信号, 它们不会被进程接收), 然后使用 sigaction()函数来定义进程接收到特定信号后的行为。

检测信号是信号处理的后续步骤, 因为被阻塞的信号不会传递给进程, 所以这些信号就处于“未处理” 状态(也就是进程不清楚它的存在)。 sigpending()函数允许进程检测“未处理” 信号, 并进一步决定对它们做何处理。


首先介绍创建信号集的函数格式, 表 4.14 列举了这一组函数的语法要点。

函数原型
int sigemptyset(sigset_t *set)
int sigfillset(sigset_t *set)
int sigaddset(sigset_t *set, int signum)
int sigdelset(sigset_t *set, int signum)
int sigismember(sigset_t *set, int signum)
函数传入值
set: 信号集
signum: 指定信号代码
函数返回值

成功: 0(sigismember 成功返回 1, 失败返回 0)
出错: 1

sigprocmask()函数语法要点
函数原型 
int sigprocmask(int how, const sigset_t *set, sigset_t *oset)
函数传入值
how:决定函数的操作方式

  1. SIG_BLOCK: 增加一个信号集到当前进程的阻塞集中
  2. SIG_UNBLOCK: 从当前的阻塞集中删除一个信号集
  3. SIG_SETMASK: 将当前的信号集设置为信号阻塞集

set: 指定信号集
oset: 信号屏蔽字
函数返回值
成功: 0
出错: 1

此处, 若 set 是一个非空指针, 则参数 how 表示函数的操作方式; 若 how 为空, 则表示忽略此操作。
 

sigpending()函数语法要点
函数原型

int sigpending(sigset_t *set)
函数传入值

set: 要检测的信号集
函数返回值
成功: 0
出错: 1

总之, 在处理信号时, 一般遵循如图 4.6 所示的操作流程。

实例代码:

功能:

该实例首先把 SIGQUIT、 SIGINT 两个信号加入信号集, 然后将该信号集设为阻塞状态,并进入用户输入状态。 用户只需按任意键, 就可以立刻将信号集设置为非阻塞状态, 再对这两个信号分别操作, 其中 SIGQUIT 执行默认操作, 而 SIGINT 执行用户自定义函数的操作。
源代码如下:


注意:

同一个信号集的信号之间是不会屏蔽

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>

/* 自定义的信号处理函数 */
void my_func1(int signum)
{
	int i = 5;
	printf("this is %s \n", __FUNCTION__);
	for(; i > 0; i--)
	{
		printf("SIGINT 信号处理倒计时 [%d] second \n", i);
		sleep(1);		
	}
}
/* 自定义的信号处理函数 */
void my_func2(int signum)
{
	int i = 5;
	printf("this is %s \n", __FUNCTION__);
	for(; i > 0; i--)
	{
		printf("SIGQUIT 信号处理倒计时 [%d] second \n", i);
		sleep(1);		
	}
}
int main()
{
	sigset_t set,pendset;
	struct sigaction action1,action2;
	
	//注册SIGINT 信号
	struct sigaction new;
	struct sigaction old;
	
	new.sa_handler = my_func1;
	new.sa_flags = 0;
	sigemptyset(&new.sa_mask);
	sigaction(SIGINT, &new, &old);
	
	/* 初始化信号集为空 */
	sigemptyset(&set);
	
	sigaddset(&set, SIGQUIT);  //add  SIGQUIT signal
	
	if (sigismember(&set, SIGQUIT))
	{
		sigemptyset(&action1.sa_mask);
		action1.sa_handler = my_func2;
		action1.sa_flags = 0;
		sigaction(SIGQUIT, &action1, NULL);  //register signal SIGQUIT
	} 
	
	/* 设置信号集屏蔽字*/
	sigprocmask(SIG_BLOCK, &set, NULL) ;
	
	printf("睡眠 5 second \n");
	sleep(5);

	printf("清空信号集 \n");
	/* 在信号屏蔽字中删除 set 中的信号 */
	if (sigprocmask(SIG_UNBLOCK, &set, NULL) < 0)
	{
		perror("sigprocmask");
		exit(1);
	}else
	{
		printf("SIGQUIT信号已经清除\n");
	} 

	while(1);
	exit(0);
}

结果:

# ./a.out 
睡眠 5 second 
^\清空信号集 
this is my_func2 
SIGQUIT 信号处理倒计时 [5] second 
SIGQUIT 信号处理倒计时 [4] second 
SIGQUIT 信号处理倒计时 [3] second 
^Cthis is my_func1                             //没加入信号屏蔽的信号会马上中止现在的信号处理,转而处理才发生的信号
SIGINT 信号处理倒计时 [5] second 
SIGINT 信号处理倒计时 [4] second 
SIGINT 信号处理倒计时 [3] second 
^\SIGINT 信号处理倒计时 [2] second   //加入信号屏蔽集的信号会阻塞
SIGINT 信号处理倒计时 [1] second 
SIGQUIT 信号处理倒计时 [2] second 
SIGQUIT 信号处理倒计时 [1] second 
this is my_func2 
SIGQUIT 信号处理倒计时 [5] second 
SIGQUIT 信号处理倒计时 [4] second 
SIGQUIT 信号处理倒计时 [3] second 
SIGQUIT 信号处理倒计时 [2] second

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/82867483