欢迎光临
我们一直在努力

Linux系统编程-信号2

一. 信号集操作函数

        内核通过读取未决信号集来判断信号是否应被处理。信号屏蔽字mask可以影响未决信号集。而我们可以在应用程序中自定义set来改变mask。已达到屏蔽指定信号的目的。

        PCB中有阻塞信号集(可操作)和未决信号集,但是未决信号集不可直接进行操作,要通过操作阻塞信号集来影响未决信号集。

1.1 信号集设定相关函数

前四个函数都是成功返回0        失败返回-1且设置errno

最后一个,若信号在信号集中返回1        不在则返回0

sigset_t  set;// typedef unsigned long sigset_t;

1.1.1 sigemptyset

        将某个信号集清0

1.1.2 sigfillset

        将某个信号集置1

1.1.3 sigaddset

        将某个信号加入信号集

1.1.4 sigdelset

        将某个信号清出信号集

1.1.5 sigismember

        判断某个信号是否在信号集中

1.2 sigprocmask函数

作用:用于获取或者更改调用线程的信号屏蔽集(signal mask),使自己设置的set与PCB中的阻塞信

           号集产生关系

参数:how:取值与说明如下

SIG_BLOCK set表示需要屏蔽的信号,mask = mask|set
 SIG_UNBLOCK set表示需要解除屏蔽的信号,mask = mask & ~set
SIG_STEMASK  set表示用于替代原始屏蔽及的新屏蔽集,mask = set

          set:传入参数,是一个位图,set中哪位置1,就表示当前进程屏蔽哪个信号。

          oldset:传出参数,保存旧的信号屏蔽集。

返回值:成功返回0        失败返回-1且errno

1.3 sigpending函数

作用:读取当前的未决信号集

参数:传出参数,传出当前的未决信号集

返回值:成功0        失败-1且errno

查看时可以使用函数sigismember函数,在的话打印1    不在的话打印0

1.4 示例代码

注意:9号SIGKILL 和19号 SIGSTOP信号,不允许忽略和捕捉,只能执行默认动作。甚至不能将

           其设置为阻塞。

void sys_error(const char *str)
{
perror(str);
exit(1);
}

void print_set(sigset_t *set)
{
int i = 0;
for(i = 1; i < 32; i++)
{
if(sigismember(set,i))//查看信号是否在信号集中
printf("%d ",1);
else
printf("%d ",0);
}
puts("");
}

int main()
{
sigset_t set, oldset, pset;
int ret;
sigemptyset(&set);
sigaddset(&set, SIGINT);//添加信号到信号集中
sigaddset(&set, SIGQUIT);
sigaddset(&set, SIGBUS);
sigaddset(&set, SIGKILL);//这个信号不可被阻塞,忽略,捕捉
sigaddset(&set, SIGSTOP);//同上

ret = sigprocmask(SIG_BLOCK, &set, &oldset);//设置阻塞状态
if(ret == -1)
sys_error("sigprocmask()");

while(1)//循环打印,循环查看
{
ret = sigpending(&pset);//查看未决信号集
if(ret == -1)
sys_error("sigpending()");

print_set(&pset);
sleep(1);

}
return 0;
}

结果:

二. 信号捕捉

2.1 signal函数

作用:注册一个信号捕捉函数

参数:signum:对应的信号

           handler:对应的信号处理函数,int代表信号的编号

返回值:返回信号处理函数

注意:该函数由ANSI定义,由于历史原因在不同版本的Unix和不同版本的Linux中可能有不同的行

           为。因此应该尽量避免使用它,取而代之使用sigaction函数

示例代码:

void sys_error(const char *str)
{
perror(str);
exit(1);
}

void fun(int signo)
{
printf("catch you: %d\\n",signo);
}

int main()
{
signal(SIGINT, fun);
while(1);//主函数不结束,死循环
return 0;
}

结果

2.2 sigaction函数

作用:注册信号捕捉函数

参数:signum:要捕捉的信号

           act:新的信号处理状态

           oldact:保存旧有的信号处理状态

返回值:成功0        失败-1且errno

           struct sigaction结构体:

struct sigaction {
void (*sa_handler)(int);/信号处理函数,int代表信号的编号
void (*sa_sigaction)(int, siginfo_t *, void *);/一般不使用
sigset_t sa_mask;/此信号屏蔽字只工作于信号捕捉函数执行期间
int sa_flags;/通常为0,默认属性
void (*sa_restorer)(void);/一般不使用
};

         ① sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为SIG_IGN表忽略 或 SIG_DFL表执行默认动作

         ② sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。

         ③ sa_flags:通常设置为0,表使用默认属—本信号默认屏蔽。

示例代码:一个捕捉函数可以捕捉多个信号

void sys_error(const char *str)
{
perror(str);
exit(1);
}

void fun(int signo)
{
if(signo == SIGINT)//判断是哪个信号被捕捉到
printf("catch you: %d\\n",signo);
else if(signo == SIGQUIT)//
printf("—catch you: %d\\n",signo);
}

int main()
{
struct sigaction act, oldact;
act.sa_handler = fun;//设置回调函数
sigemptyset(&act.sa_mask);//晴空sa_mask屏蔽字,只在回调函数工作期间有效
act.sa_flags = 0;//默认值,回调函数执行期间,被捕捉信号自动屏蔽
//只会屏蔽当前被捕捉的一个信号,多个信号共用一个sa_handler
//时,不会互相屏蔽,要想屏蔽,必须手动设置sa_mask

int ret = sigaction(SIGINT, &act, &oldact);//注册捕捉信号
if(ret == -1)
sys_error("sigaction()");
ret = sigaction(SIGQUIT, &act, &oldact);//可以注册捕捉多个信号
if(ret == -1)
sys_error("sigaction()");

while(1);//死循环

return 0;
}

2.3 信号捕捉特性

1、进程正常运行时,默认PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信

     号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很

     长时间,在这期间所屏蔽的信号不由☆来指定。而是用sa_mask来指定。调用完信号处理函

     数,再恢复为☆。

2、XXX信号捕捉函数执行期间,XXX信号自动被屏蔽,需要sa_flags设置为0。

3、阻塞的常规信号不支持排队,产生多次只记录一次。(后32个实时信号支持排队)

2.3.1 不支持排队示例代码

void sys_error(const char *str)
{
perror(str);
exit(1);
}

void fun(int signo)
{
printf("catch :%d\\n",signo);
sleep(5);//当sleep被信号打断后,再次回来时,会从sleep的下一条语句执行
//不会继续睡眠剩余的时间
printf("—-end :%d\\n",signo);
}

int main()
{
struct sigaction act, oldact;
act.sa_handler = fun;//设置回调函数
sigemptyset(&act.sa_mask);//晴空sa_mask屏蔽字,只在回调函数工作期间有效
act.sa_flags = 0;//默认值,回调函数执行期间,被捕捉信号自动屏蔽
//只会屏蔽当前被捕捉的一个信号,多个信号共用一个sa_handler
//时,不会互相屏蔽,要想屏蔽,必须手动设置sa_mask

int ret = sigaction(SIGINT, &act, &oldact);//注册捕捉信号
if(ret == -1)
sys_error("sigaction()");
ret = sigaction(SIGQUIT, &act, &oldact);//可以注册捕捉多个信号
if(ret == -1)
sys_error("sigaction()");

while(1);//死循环

return 0;
}

2.3.2 示例代码2

void sys_error(const char *str)
{
perror(str);
exit(1);
}

void fun1(int signo)
{
printf("catch :%d\\n",signo);
sleep(3);
}

void fun2(int signo)
{
printf("catch :%d\\n",signo);
sleep(3);
}

int main()
{
struct sigaction act1, act2, oldact1, oldact2;
act1.sa_handler = fun1;//设置回调函数
sigemptyset(&act1.sa_mask);//清空sa_mask屏蔽字,只在回调函数工作期间有效
act1.sa_flags = 0;//默认值,回调函数执行期间,被捕捉信号自动屏蔽
//只会屏蔽当前被捕捉的一个信号,多个信号共用一个sa_handler
//时,不会互相屏蔽,要想屏蔽,必须手动设置sa_mask

act2.sa_handler = fun2;//设置回调函数
sigemptyset(&act2.sa_mask);//晴空sa_mask屏蔽字,只在回调函数工作期间有效
act2.sa_flags = 0;//默认值,回调函数执行期间,被捕捉信号自动屏蔽

int ret = sigaction(SIGINT, &act1, &oldact1);//可以注册捕捉多个信号
if(ret == -1)
sys_error("sigaction()");
ret = sigaction(SIGQUIT, &act2, &oldact2);//可以注册捕捉多个信号
if(ret == -1)
sys_error("sigaction()");

while(1);//死循环

return 0;
}

2.4 内核实现信号捕捉过程

1、主控流程进入内核才会检查是否有信号产生

2、为什么信号处理函数执行完之后还要再次回到内核中,而不是直接返回到主控制流程?因为信

     号处理函数是由内核调用的,函数必须返回至调用者处,所以返回至了内核中

三. 借助信号捕捉回收子进程

3.1 SIGCHLD(17)信号产生的条件

1、子进程终止时

2、子进程接收到SIGSTOP信号停止时

3、子进程处在停止态,接受到SIGCONT后唤醒时

3.2 借助SIGCHLD信号回收子进程

        子进程结束运行,其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号,在捕捉函数中完成子进程状态的回收。

3.3 示例代码

这里有几点需要注意的:

        1、在回调函数中,要循环的对子进程进程回收,防止一次有多个子进程结束,而父进程只回

             收了其中一个。

        2、另外在父进程注册信号捕捉函数之前,一定要对SIGCHLD这个信号设置屏蔽,避免在父

              进程还未fork完就有子进程死亡。—应该在fork之前,阻塞SIGCHLD信号。注册完捕捉函

              数后解除阻塞

        3、子进程继承父进程的信号屏蔽字和信号处理动作,但子进程没有继承未决信号集spending

#define FORK_SIZE 10

void child_catch(int signum)
{
pid_t wpid;
int wstatus;
while(0 < (wpid = waitpid(-1, &wstatus, WNOHANG))){//使用非阻塞回收
if(WIFEXITED(wstatus)){
printf("——wait pid :%d,returnval:%d\\n",
wpid,WEXITSTATUS(wstatus));
}
else if(WIFSIGNALED(wstatus)){
printf("——wait pid :%d,returnval:%d\\n",
wpid,WTERMSIG(wstatus));
}
}
return ;
}

int main()
{
pid_t pid;
//设置对17号信号屏蔽,防止父进程还没有fork完就有子进程结束
sigset_t set, oldset;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, &oldset);

int i = 0;
for(i = 0; i < FORK_SIZE; i++){
pid = fork();
if(pid == 0){
break;
}
else if(pid < 0){
perror("fork()");
exit(1);
}
}
if(i == FORK_SIZE){
printf("parent,pid :%d\\n",getpid());
//注册信号捕捉
struct sigaction act;
act.sa_handler = child_catch;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);
//完了之后解除屏蔽,解除屏蔽
sigprocmask(SIG_SETMASK, &oldset, NULL);

while(1);
}
else{

printf("[%d]:child,pid:%d\\n",i,getpid());
return i;
}
return 0;
}

赞(0)
未经允许不得转载:171主机测评 » Linux系统编程-信号2
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址