信号的处理包括信号的发送、 捕获及信号的处理, 它们有各自相对应的常见函数。
 发送信号的函数: 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

首先给出了 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

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

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

问题:

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

解决:

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

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

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

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

注意1:

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

注意2:

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

 

注意3:

当正在处理一个信号时,有进程收到多个信号,这时会对这些信号进行合并,一般会只留第一个到达的信号,因为1~32的信号时Linux的不可靠信号。

 

实例代码:

当运行处理信号函数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

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void showpending(sigset_t *pending)
{
int i = 0;
for( ; i <= 31; i++)
{
if(sigismember(pending,i)) //判断是否包含某个信号
printf("1");
else
printf("0");
}
printf("\n");
}

int main()
{
sigset_t blockSet,oblockSet,pending;
sigemptyset(&blockSet); //清空信号集
sigemptyset(&oblockSet);
sigaddset(&blockSet,2); //添加2号信号Ctrl+c
sigprocmask(SIG_SETMASK, &blockSet, &oblockSet); //阻塞2号信号

while(1)
{
sigpending(&pending); //读取未决信号集
showpending(&pending); //显示未决信号集
sleep(1);
}
}

运行结果:

10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000
^C10100000000000000000000000000000  //按下ctr  +  c  后信号2 处于未决信号
10100000000000000000000000000000
 

 

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

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

实例代码:

功能:

该实例首先把 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