信号的处理方式
①信号忽略
signal(signum, SIGINT)
②信号缺省(执行默认动作)
signal(signum, SIG_DFL)
③信号捕捉(去执行指定的行为)
signal(signum, function)
④信号阻塞
linux系统信号集
什么是信号集?
信号集是一个集合,而每一个成员都是一个信号,通过将信号加入到信号集中,再设置阻塞状态给信号集,那么整个信号集里面的信号都是阻塞状态。
信号阻塞是什么?
进程使用信号集在阻塞某个信号的前提下,收到了这个信号,不会马上响应,而是等到解除阻塞之后,才会响应这个信号。
如果一个进程会收到多个信号,但是进程正在执行其他任务,需要执行完当前任务再去处理这些信号,那么可以使用信号集将收到的所有信号设置为阻塞态,等到解除阻塞,再去执行收到的信号。
信号集处理函数
#include <signal.h>
sigset_t //信号集的数据类型
int sigemptyset(sigset_t *set); //清空信号集
int sigfillset(sigset_t *set); //将Linux下所有的信号加入信号集
int sigaddset(sigset_t *set, int signum); //将signum信号加入信号集
int sigdelset(sigset_t *set, int signum); //将signum信号从信号集里删除
int sigismember(const sigset_t *set, int signum); //判断signum是否在信号集里
参数:
set------>信号集的地址
signum:----->待处理的信号值
返回值:
sigismember(const sigset_t *set, int signum)信号在信号集里返回1,不在信号集里返回0,出错返回-1;
其余的信号集处理函数成功返回0,失败返回-1;
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
函数功能:
将信号集设置为阻塞状态 或 解除阻塞状态
参数:
how----->SIG_BLOCK(将信号集设置为阻塞态) / SIG_UNBLOCK(解除信号集的阻塞态)
set----->信号集的地址
oldset----->保留信号集之前的状态的指针,如果不保留之前的状态,就设置为NULL
返回值:
成功返回0,失败返回-1
练习1:练习1:创建一个子进程,父进程将SIGUSR1加入到信号集中,判断信号在不在信号集中,再设置该信号为阻塞状态,该状态持续10s,10s解除阻塞,看看会不会响应信号?子进程在10s之内发送信号SIGUSR1给父进程。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
void signal_handle(int arg)
{
printf("parent catch %d, i am handling \n", arg);
}
int main(int argc, char const *argv[])
{
//捕捉信号
signal(SIGUSR1, signal_handle);
pid_t id;
id = fork();
if(id > 0) //父进程
{
printf("i am parent process [%d]\n", getpid());
sigset_t set; //信号集
sigemptyset(&set); //清空信号集
sigaddset(&set, SIGUSR1); //将SIGUSR1信号加入到信号集
if(sigismember(&set, SIGUSR1) == 1) //判断SIGUSR1是否在信号集中
{
printf("yes, SIGUSR1 is member of set\n");
//将信号集设置为阻塞状态
sigprocmask(SIG_BLOCK, &set, NULL);
printf("set is blocked\n");
sleep(10); //阻塞10秒
//将信号集接触阻塞
printf("set is unblocked\n");
sigprocmask(SIG_UNBLOCK, &set, NULL);
}
else
printf("no, SIGUSR1 is not member of set\n");
}
else if(0 == id) //子进程
{
sleep(1);
printf("i am child process[%d]\n", getpid());
if(0 == kill(getppid(), SIGUSR1)) //向父进程发送SIGUSR1信号
{
printf("child send SIGUSR1 to parent\n");
}
}
return 0;
}
结果分析:在父进程将set信号集设置为阻塞状态后,当父进程收到子进程发送来的SIGUSR1信号
后,将会把信号挂起,直到接触信号集阻塞,才会执行信号响应函数。
练习2:向进程发送相同的几个信号,验证在进程的信号集里,相同的信号将会被舍弃。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void signal_handle(int arg)
{
printf("%d i am running\n", arg);
}
int main(int argc, char const *argv[])
{
signal(SIGUSR1, signal_handle);
//定义一个信号集
sigset_t set;
//初始化信号集(清空)
sigemptyset(&set);
//将信号添加到信号集
sigaddset(&set, SIGUSR1);
//判断SIGUSR1是否在信号集中
if(sigismember(&set, SIGUSR1) == 1)
{
printf("SIGUSR1在set信号集中\n");
}
else
{
printf("SIGUSR1不在set信号集中\n");
}
//将信号集设置为阻塞状态 设置的阻塞对象是信号,不是进程
sigprocmask(SIG_BLOCK, &set, NULL);
sleep(5);
//向进程自己发送三次SIGUSR1信号
raise(SIGUSR1);
raise(SIGUSR1);
raise(SIGUSR1);
//在sleep(5)的期间,收到信号SIGUSR1后,进程将信号挂起来,直到解除阻塞,执行信号响应函数
//接触信号集阻塞
sigprocmask(SIG_UNBLOCK, &set, NULL);
return 0;
}
结果分析:在信号集中,不会存在相同的信号,相同的信号将会被舍弃。
练习3:验证阻塞属性会不会被子进程继承。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
void signal_handle(int arg) //信号响应函数
{
printf("[%d] is handling signal of %d\n", getpid(), arg);
}
int main(int argc, char const *argv[])
{
pid_t id;
signal(SIGUSR1, signal_handle); //信号捕捉函数
sigset_t set; //信号集
sigemptyset(&set); //清空信号集
sigaddset(&set, SIGUSR1); //将SIGUSR1加入到信号集set
if(1 == sigismember(&set, SIGUSR1)) //判断SIGUSR1是否在信号集set中
printf("SIGUSR1 is member of set\n");
else
printf("SIGUSR1 is not member of set\n");
sigprocmask(SIG_BLOCK, &set, NULL); //将信号集里的信号设置为阻塞状态
id = fork();
if(id > 0)
{
printf("i am parent process[%d]\n", getpid());
//在父进程里将信号集接触阻塞状态
sigprocmask(SIG_UNBLOCK, &set, NULL);
while(1);
}
else if(0 == id)
{
printf("i am child process[%d]\n", getpid());
while(1);
}
else
{
perror("fork");
exit(1);
}
return 0;
}
结果分析:子进程继承了父进程的信号集里的阻塞信号,当子进程收到该信号后,不会去执行响应函数,因为该信号被阻塞了;而父进程在接触信号集的阻塞后,可以正常的去执行信号响应函数。