信号的处理包括信号的发送、 捕获及信号的处理, 它们有各自相对应的常见函数。
发送信号的函数: kill()、 raise()。
捕捉信号的函数: alarm()、 pause()。
处理信号的函数: signal()、 sigaction()。
信号处理的方法主要有两种,
- 使用 signal()和sigation()函数
- 使用信号集函数组
下面分别介绍这两种处理方式。
1) 使用 signal()函数
使用 signal()函数处理时, 只需指出要处理的信号和处理函数即可。 它主要用于前 32 种非实时信号的处理, 不支持信号传递信息, 但是由于使用简单、 易于理解, 因此也受到很多程序员的欢迎。 Linux 还支持一个更健壮更新的信号处理函数 sigaction(), 推荐使用该函数。
signal()函数语法要点
函数原型typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数传入值
signum: 指定信号代码
handler:
- SIG_IGN: 忽略该信号
- SIG_DFL: 采用系统默认方式处理信号
- 自定义的信号处理函数指针
函数返回值
成功: 以前的信号处理配置
出错: 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:决定函数的操作方式
- SIG_BLOCK: 增加一个信号集到当前进程的阻塞集中
- SIG_UNBLOCK: 从当前的阻塞集中删除一个信号集
- 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