2信号处理之:信号产生原因,进程处理信号行为,信号集处理函数,PCB的信号集,sigprocmask()和sigpending(),信号捕捉设定,sigaction,C标准库信号处理函数,可重入函数,

简介:  1信号产生原因 2.进程处理信号行为 manpage里信号3中处理方式: SIG_IGN SIG_DFL                                            默认Term动作 a signal handling function 进程处理信号 A默认处理动作 term  


1信号产生原因

2.进程处理信号行为

manpage里信号3中处理方式:

SIG_IGN

SIG_DFL                                            默认Term动作

a signal handling function

进程处理信号

A默认处理动作

term   中断

core    core(调试的时候产生)

gcc –g file.c

    ulimit –c 1024

    gdb a.out core

ign      忽略

stop     停止

cont     继续

B忽略

C捕捉(用户自定义处理函数)

3信号集处理函数

sigset_t为信号集,可sizeof(sigset_t)查看

将信号集里面的每位都置0

int sigemptyset(sigset_t *set)

将所有信号集都置1

int sigfillset(sigset_t *set)

添加一个信号,也就是将Block阻塞信号集里面的某一位置成1

int sigaddset(sigset_t *set, int signo)

将信号集中某一位取消置1

int sigdelset(sigset_t *set, int signo)

测试某个信号集中的信号是否为1

int sigismember(const sigset_t *set, intsigno)

4 PCB的信号集

信号在内核中的表示示意图

如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1

许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只

计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信

号。从上图来看,每个信号只有一个bit的未决标志,非01,不记录该信号产生了多少

次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t

来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,

在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有

效”和“无效”的含义是该信号是否处于未决状态。

阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解

为阻塞而不是忽略。

5sigprocmask

调用函数sigprocmask可以读取或更改进程的信号屏蔽字。

依赖的头文件:

#include <signal.h>

函数声明:

*set是传入的信号,*oset表示原来的信号集是什么,相当于是*set的一个备份

intsigprocmask(int how, const sigset_t *set, sigset_t *oset);         

返回值:若成功则为0,若出错则为-1

如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果osetset都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据sethow参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

    how参数的含义

SIG_BLOCK set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set

SIG_UNBLOCK set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set

SIG_SETMASK 设置当前信号屏蔽字为set所指向的值,相当于mask=set

6sigpending(未决打印信号)

#include<signal.h>

int sigpending(sigset_t *set)

sigpending读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则

返回-1

6案例说明:

运行结果:

程序运行时,每秒钟把各信号的未决状态打印一遍,由于我们阻塞了SIGINT信号,按Ctrl-C将会使SIGINT信号处于未决状态,按Ctrl-\仍然可以终止程序,因为SIGQUIT信号没有阻塞。

这时按Ctrl+\结束。

7信号捕捉设定

8.sigaction

#include <signal.h>

int sigaction(int signum, const structsigaction *act,

struct sigaction *oldact);

 

struct 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:早期的捕捉函数

sa_sigaction : 新添加的捕捉函数,可以传参,sa_handler互斥,两者通过sa_flags选择采用哪种捕捉函数

sa_mask : 在执行捕捉函数时,设置阻塞其它信号,sa_mask| 进程阻塞信号集,退出捕捉函数后,还原回原有的阻塞信号集

sa_flags : SA_SIGINFO 或者0

sa_restorer:保留,已过时。

案例说明:

#include<stdio.h>

#include<signal.h>

#include<unistd.h>

 

void do_sig(int num)

{

    int n = 5;

    printf("I am do_sig\n");

    printf("num = %d\n",num);

    while(n--)

    {

         printf("num = %d\n",num);

        sleep(1);

    }

}

 

int main(void)

{

    struct sigaction act;

    act.sa_handler = do_sig;

    //act.sa_handler = SIG_DFL;

    //act.sa_handler = SIG_IGN;

    sigemptyset(&act.sa_mask);

    sigaddset(&act.sa_mask,SIGQUIT);

    act.sa_flags = 0;

 

    sigaction(SIGINT,&act,NULL);

 

     while(1)

    {

        printf("**********\n");

       sleep(1);

    }

    return 0;

}

9 C标准库信号处理函数

typedef void (*sighandler_t)(int)

sighandler_t signal(int signum,sighandler_t handler)

int system(const char *command)

system的本质是:集合forkexecwait一体

10 可重入函数

A:不含全局变量和静态变量是可重入函数的一个要素

B:可重入函数man  7  signal

C:在信号捕捉函数里应可重入函数

例如:strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱,应用strtok_r版本,r表示可重入。

11时序竞态

int pause(void)

使调用进程挂起,直到有信号递达,如果递达信号是忽略,则继续挂起

int sigsuspend(const sigset_t *mask)

以通过指定mask来临时解除对某个信号的屏蔽,然后挂起等待,sigsuspend返回时,进程的信号屏蔽字恢复为原来的值

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

void sig_alrm(int signo)

{

/* nothing to do */

}

unsigned int mysleep(unsigned int nsecs)

{

struct sigaction newact, oldact;

unsigned int unslept;

newact.sa_handler = sig_alrm;

sigemptyset(&newact.sa_mask);

newact.sa_flags = 0;

sigaction(SIGALRM, &newact, &oldact);

 

alarm(nsecs);

//alarm的时候,可能会转到其它程序,这之后永远都执行不到下面的这行,因为信号已经执行过了

pause();

 

unslept = alarm(0);

sigaction(SIGALRM, &oldact, NULL);

return unslept;

}

 

int main(void)

{

while(1){

mysleep(2);

printf("Two seconds passed\n");

}

return 0;

}

mysleep的改进版

#include<unistd.h>

#include<signal.h>

#include<stdio.h>

 

void sig_alrm(int signo)

{  

    /* nothing to do*/

}

 

unsigned int mysleep(unsigned int nsecs) {  

struct sigaction newact,oldact;

    sigset_tnewmask,oldmask,suspmask;

    unsigned int unslept;

    /*set our handler,saveprevious information*/

    newact.sa_handler =sig_alrm;

    sigemptyset(&newact.sa_mask);

    newact.sa_flags = 0;

   sigaction(SIGALRM,&newact,&oldact);

    /*block SIGALRM and savecurrent signal mask*/

    sigemptyset(&newmask);

 sigaddset(&newmask,SIGALRM);

 sigprocmask(SIG_BLOCK,&newmask,&oldmask);

      alarm(nsecs);

   suspmask = oldmask;

  /*make sure SIGALRM isn'tblocked*/

   sigdelset(&suspmask,SIGALRM);

  sigsuspend(&suspmask);/*waitfor any signal to be caught*/

    /*some signal has bencaught,SIGALRM is now blocked*/

  unslept = alarm(0);

      sigaction(SIGALRM,&oldact,NULL);/*resetprevious action*/

    /*reset signal mask,whichunblocks SIGALRM*/

   sigprocmask(SIG_SETMASK,&oldmask,NULL);

   return(unslept);

}

 

int main(void) {

while(1)

    {

         sleep(2);

   printf("Two secondspassed\n");

      }

  return 0;

}

目录
相关文章
|
16天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
79 13
|
4月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
4月前
|
Linux C语言
C语言 多进程编程(四)定时器信号和子进程退出信号
本文详细介绍了Linux系统中的定时器信号及其相关函数。首先,文章解释了`SIGALRM`信号的作用及应用场景,包括计时器、超时重试和定时任务等。接着介绍了`alarm()`函数,展示了如何设置定时器以及其局限性。随后探讨了`setitimer()`函数,比较了它与`alarm()`的不同之处,包括定时器类型、精度和支持的定时器数量等方面。最后,文章讲解了子进程退出时如何利用`SIGCHLD`信号,提供了示例代码展示如何处理子进程退出信号,避免僵尸进程问题。
|
4月前
|
NoSQL
gdb中获取进程收到的最近一个信号的信息
gdb中获取进程收到的最近一个信号的信息
|
4月前
|
编译器
【收藏】内核级利用通用Hook函数方法检测进程
【收藏】内核级利用通用Hook函数方法检测进程
|
5月前
|
Linux API
Linux源码阅读笔记07-进程管理4大常用API函数
Linux源码阅读笔记07-进程管理4大常用API函数
|
6月前
|
弹性计算 DataWorks 关系型数据库
DataWorks操作报错合集之DataX在执行过程中接收到了意外的信号15,导致进程被终止,该怎么处理
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
5月前
|
消息中间件 存储 网络协议
从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
在操作系统中,进程间通信(IPC)是至关重要的,它提供了多种机制来实现不同进程间的数据交换和同步。本篇文章将详细介绍几种常见的IPC方式,包括管道、信号、消息队列、共享内存、信号量和套接字,帮助你深入理解并合理应用这些通信方式,提高系统性能与可靠性。
490 0
|
6月前
|
Python
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
|
6月前
|
存储 NoSQL Unix
【Linux】进程信号(下)
【Linux】进程信号(下)
49 0