目录

  • 信号
  • 信号的阻塞
  • 信号的注册
  • 信号的发送

信号

  信号是信息的载体,怎么理解呢?就类似于烽火戏诸侯的周幽王点燃了烽火台发出了这个信号,然后各路诸侯收到了这个信号就过来救驾勤王了。

  进程也是可以收到来自四面八方的信号然后做收到信号之后要做的事情,要做的事也称处理信号。就好比我们正在学习,接到了一个电话然后去聊天了,电话结束后继续回来学习。进程也是一样,收到信号时无论执行到哪一步都要先处理信号,然后继续做自己的事情。

  信号分为三个状态,分别是产生,未决和递达。
    产生:产生就是产生信号,比如我们用案件CTRL+C可以终止程序 还有使用kill命令 都是产生一个信号。
    未决:未决是产生和递达中间的一个状态,主要是因为信号发送过去中间被阻塞了,被拦住了这种状态。
    递达:递达就是收到这个信号了,然后处理完它了之后这个信号的状态。

  信号有三种处理方式:
    ①执行默认动作:大部分信号,进程对于这种信号执行的默认处理方式就是终止进程。
    ②忽略信号:信号来了直接丢掉,不做任何处理,相当于信号从来没来过一样
    ③捕捉信号:用户自定义这种信号的处理方式,信号来了就按照用户自定义的来处理。(注意:SIGKILL和SIGSTOP这两个信号是不能忽略,捕获和阻塞的。这两个相当于进程用来结束的克星法宝,如果这两个也可以随便定义的话,进程就没法结束,进程就真的无法无天了。)

信号的阻塞

信号的阻塞这里要提及两个概念,分别是未决信号集和阻塞信号集。这两个信号集存放在内核的PCB中。信号集是一个能表示多种信号的数据类型,能表示1-64一共是64个信号。不用想的太复杂,就把它当作简单的位图。

  当有信号产生的时候不会立刻处理这个信号,前面我们有提到未决状态其实就是这里了,信号产生之后内核区的这个未决信号集就要进行相应的改变了。每个信号都有其相应的编号,哪个信号来了就让位图相应的位置上的0变成1。这里比如说收到了一个SIGINT信号(信号编号为2)

  在未决信号集的时候,会判断阻塞信号集有没有阻塞它,如果阻塞了就继续先留在未决信号集中,如果没有阻塞它那么就进行下一步信号的处理了。处理完之后这个未决信号集上的1就变回原来的0了。
  那么怎么看有没有阻塞,其实这两个集合是一 一对应的,对应位置上的值如果是1就代表着阻塞,是0就代表着不阻塞,就可以进行下一步的操作。这里显然是没有阻塞的,所以这个信号可以进行下一步的处理。

  如果阻塞了,未决信号集上的值就不会变化,直到阻塞信号集解除阻塞,也就是将对应的1变成0之后,信号再进行下一步操作,处理完之后未决信号集相应的位置就从1变回0,此时信号也就处于上面说的递达状态了。
  这里需要注意的一点是,如果在阻塞期间这个信号产生到了多次,最后解除阻塞之后也就执行一次信号处理操作。因为这个位图不会记录次数只会记录有没有这个信号产生。

  
  信号集是内核区的内容,我们没办法操作,但是系统为我们提供了api接口来间接的操作。
  int sigemptyset(sigset_t *set);  作用:将某个信号集清0
  int sigfillset(sigset_t *set);  作用:将某个信号集置1
  int sigaddset(sigset_t *set, int signum);  作用:将某个信号加入信号集合中
  int sigdelset(sigset_t *set, int signum);  作用:将某信号从信号清出信号集
以上函数的返回值都是成功返回0 失败返回-1并设置errno值
  int sigismember(const sigset_t *set, int signum); 作用:判断某个信号是否在信号集中。  当信号信号集上返回1,不在返回0,出错了就会返回-1也会设置相应errno值

你可能会想,不是不能操作内核里的吗,连集合都没拿到手,怎么传参这个集合怎么做相应操作?
信号集本质上是一个sigset_t类型结构体,我们需要在栈上先创建好一个这样类型的结构体(创建之后记得要初始化,栈上的内容不初始化,默认值是随机的,不要以为默认都是0)做好相应的设置然后就是接下来看这个函数的操作了。这个函数可以修改内核区真正的阻塞信号集。

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

  函数成功返回0,失败返回-1并设置相应errno值。
  第二个是传入参数,也就是你栈上创建的信号集,第三个是传出参数,如果不关心可以填NULL,如果想要没有改变前的内核区的阻塞信号集的内容可以填一个,函数运行结束之后,传入的这个oldset的内容就变成了没调用这个sigprocmask函数之前的阻塞信号集的内容。
这个how的传入有三个选项:
        SIG_BLOCK:表示要增加的阻塞的信号都在传入的set里了,内核区的阻塞信号集会和这个set做一个或运算,也就是原来内核区阻塞信号集位置的1还是1,0的话看这个set上的设置 0|1=1 0|0=0.
        SIG_UNBLOCK:表示要解除阻塞的信号都在传入的set里了,内核区的阻塞信号集会和这个set取反之后集合做一个与运算。0&0 0&1结果都是0,也就是说阻塞信号集0的位置不需要改变,1的话就是1 & (~1)或 1 &(~0)。简单来看就是说两个信号集相应位置都有1,那么就把阻塞信号集的1解除阻塞变成0;传入的信号集对应位置是0,阻塞信号集上的不需要做任何改变,原来是多少,现在还是多少。
        SIG_SETMASK:这个杀伤力就比较大了,直接将内核区的阻塞信号集直接变成你传入的这个信号集。是一种赋值操作,阻塞信号集=set。

  int sigpending(sigset_t *set); 作用:读取当前进程的未决信号集。
  返回值为0表示成功,返回值为-1表示失败并设置相应errno值。
这个set是一个传出参数,把未决信号集的内容赋值给你传入的这个set。

信号的注册

  信号的捕获就需要我们自己在内核中注册信号捕捉,注册了信号捕捉之后,有相对应的信号来的时候就对它进行处理,就可以按照我们用户自定义的来了。
  系统为我们提供了相应的api接口让我们来操作
    signal函数:

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

  第一个参数为信号的编号(注意这个编号最好写宏值,不同系统中编号不一样,但是宏值是一样的),第二个参数为用户自定义的函数名。这里要严格按照函数的类型传参,比如说第一个必须传int型数据,第二个必须传sighandler类型的函数指针,函数的名字本身就是一个函数指针,所以这里可以直接传函数的名字。
    sigaction函数:

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

第一个参数和上面的函数一样传的是信号的编号,第二个参数传一个结构体,第三个参数和上面说的sigprocmask用法一致,这里重点讲下第二个参数。
传入的结构体具体原型:

struct sigaction {void  (*sa_handler)(int);  // 信号处理函数void  (*sa_sigaction)(int, siginfo_t *, void *); //信号处理函数sigset_t  sa_mask; //信号处理函数执行期间需要阻塞的信号int   sa_flags; //通常为0,表示使用默认标识void  (*sa_restorer)(void);
};

结构体的第一个元素和signal一样,设置自定义的函数名即可。也可以将sa_handler设置为SIG_IGN表示忽略,设置为SIG_DFL表示执行默认动作。

int main()
{struct sigaction act;act.sa_handler=SIG_IGN;sigemptyset(&act.sa_mask);act.sa_flags=0;sigaction(SIGINT,&act,NULL);while(1){sleep(1);}return 0;
}

第二个元素和第一个元素一样,基本上不用。

第三个参数是设置信号处理函数期间需要阻塞的信号(注意,这个并没有涉及到内核的阻塞信号函数)默认情况下也就是不阻塞任何信号,一个信号先执行此时处于信号处理函数执行期间,之后收到了多种相同的信号,这个执行期间不会被后面来的信号所打断,而是等这个信号处理函数执行完之后再执行一次这个信号处理函数。那如果来的不是同一个信号呢?这时候就会打断这个信号处理函数,处理这个不同的信号,这个不同的信号处理完了之后再回到被打断的地方继续执行。

void sighandler(int sig)
{printf("signal is [%d]\n",sig);sleep(10);printf("hello word!!!\n");
}
int main()
{struct sigaction act;act.sa_handler=sighandler;sigemptyset(&act.sa_mask);act.sa_flags=0;sigaction(SIGINT,&act,NULL);sigaction(SIGQUIT,&act,NULL);while(1){sleep(1);}return 0;
}

设置阻塞之后,就不会在这个信号处理函数期间打断这个信号处理函数,而是先阻塞起来,等这个信号处理函数执行完毕后,再处理这个被阻塞的信号,同样,如果有多个同样的信号也只会执行一次。相应的设置信号集函数可以参考未决信号集。

第四个参数通常设置为0,表示使用默认表示。
第五个已经舍弃了,也就是不用了。

在不同的linux版本是signal这个函数表现出的行为是不一样的,要避免它的使用,应使用sigaction函数来代替它。

信号的发送

常用的信号发送函数是kill函数,它本身也是外部的一个命令,也是系统提供给我们的一个函数。它可以给指定进程发送信号。
signal函数:

int kill(pid_t pid, int sig);

  函数的返回值 0代表着成功 -1代表着失败还会设置相应的errno

  •   pid的传入分为以下几种情况:

    • pid>0:发送信号给指定进程
      pid=0:发送信号给调用这个函数的进程所在的组的全部进程也就是全部组员,也包括它自己
      pid<-1:发送信号给这个pid的绝对值对应的进程组的全部成员。
      pid=-1:发送信号给进程有权限发送的系统中全部的进程,也就是能发的都发。

sig的传入就是传信号的编号。

信号 信号的注册 信号的发送 信号的阻塞相关推荐

  1. 【genius_platform软件平台开发】第六十八讲:linux系统驱动开发之-驱动程序发送信号给应用程序

    大家好,我是峰哥,今天给大家解说一下:驱动层发送信号给应用程序.在上一篇文章中,我讲过:应用层发送指令来控制驱动层的GPIO状态,[genius_platform软件平台开发]第六十七讲:linux系 ...

  2. Qt知识点梳理 —— 静态函数发送信号

    文章目录 应用场景 思路原理 项目案例 项目源码 开发环境 应用场景 在编写相机程序时,使用了相机的回调函数,此回调函数为静态成员函数,在需要发送的信号时发现静态成员函数直接发送信号会有问题: 非静态 ...

  3. linux系统发送信号的系统调用是,linux系统编程之信号:信号发送函数sigqueue和信号安装函数sigaction...

    信号发送函数sigqueue和信号安装函数sigaction sigaction函数用于改变进程接收到特定信号后的行为. sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然 ...

  4. cserialport 循环发送信号_C++信号处理

    免费C语言教程:阿里云大学--开发者课堂(点击文章最下方"了解更多") 信号是由操作系统传给进程的中断,会提早终止一个程序.在 UNIX.LINUX.Mac OS X 或 Wind ...

  5. 进程信号(信号、信号的注册与注销、信号的处理方式)

    进程信号 信号的概念 信号的产生 信号的注册 非可靠信号的注册 可靠信号的注册 信号的注销 非可靠信号的注销 可靠信号的注销 信号的处理方式 默认处理方式 忽略处理方式 自定义处理方式 信号的捕捉流程 ...

  6. C语言 信号集回调函数 避免子进程在信号回调注册完成之前全部结束

    // ..使用内存映射可以拷贝文件 /* 对原始文件进行内存映射 创建一个新文件 把新文件的数据拷贝映射到内存中 通过内存拷贝将第一个文件的内存映射区拷贝到第二个文件的内存映射区 释放资源 */// ...

  7. Qt次线程向主程序发送信号收不到的问题

    问题的提出: Qt次线程向主程序发送信号收不到,信号槽connect返回也是true,排查原因如下: 信号或槽函数中的参数用到了自定义类型,如果要在Qt信号槽中使用自定义类型,需要注意使用qRegis ...

  8. 向流程组的所有成员发送信号的最佳方法是什么?

    我想杀死整个进程树. 使用任何常用脚本语言执行此操作的最佳方法是什么? 我正在寻找一个简单的解决方案. #1楼 志刚答案的修改版: #!/usr/bin/env bash set -eukilltre ...

  9. Linux C编程--进程间通信(IPC)3--信号集和发送信号介绍

    Linux信号集 1.信号集概念 信号集是一个能表示多个信号的数据类型,sigset_t set :set即一个信号集. 既然是一个集合,就需要对集合进行添加/删除等操作. int sigemptys ...

最新文章

  1. Lintcode 655解题思路和c++代码
  2. 我是如何打败拖延症的
  3. Git复习(二)之远程仓库、注册GitHub账号、SSH警告、使用GitHub
  4. 什么是 SAP Spartacus UI 的 feature level
  5. 小甲鱼 OllyDbg 教程系列 (七) :VB 程序逆向分析
  6. php读取doc pdf文件,PHP读取创建txt,doc,xls,pdf类型文件
  7. 出现身份验证错误,要求的函数不受支持,远程计算机:X.X.X.X,这可能是由于CredSSP加密Oracle修正
  8. 基于网络的 Red Hat 无人值守安装
  9. YOLOv2论文理解
  10. FFT算法(Java实现)
  11. 服务器系统怎么安全驱动精灵,服务器驱动精灵
  12. 保龄球记分程序c语言,老鸟救命~关于保龄球记分规则的程序
  13. 最近流行的暴寒经典语句
  14. keystone WSGI流程
  15. 互联网业务实战(一)--今日头条文章发布实现
  16. 集合长度可变的实现原理(解析为什么集合长度可变)
  17. C/C++编程学习 - 第7周 ⑥ 合影效果
  18. 【真相】网易暴力裁员事件 企业暴力开除重病员工事件绝非孤例
  19. 尹博学:OceanBase Cloud正式开服,助力全球中小企业数智化升级
  20. Ubuntu连接不了网络的解决方法亲测可行经验

热门文章

  1. PTA 10-53 查询部分专业的学生
  2. 2.1 Linux基础(命令-文件系统)
  3. 马上着手开发 iOS 应用程序 (Start Developing iOS Apps Today)(中文)
  4. 英语口语练习四十七之幸福满满的词语
  5. 密歇根大学计算机科学与工程,密歇根大学计算机专业有何独到之处
  6. repo manifest文件
  7. 百度被黑了,百度被真相曝光
  8. 微信壁纸小程序V1.2.0(自带后台上传图片)
  9. 基于dwr2.0的Push推送技术详细解析以及实例
  10. gc2000导出丝印和坐标_【干货】GC2000(GC-PowerStation)软件导出贴片坐标视频教程