目录

signal函数

信号的产生

信号处理

相关操作函数

信号捕捉


信号是进程之间事件异步通知的一种方式,属于软中断

首先看看有哪些信号 kill -l

编号为34以后的信号为实时信号。

之前在进程操作的文章中讲过kill -9是用来杀掉一个进程,给指定的进程发送SIGKILL信号,好比告诉进程你该结束了。

还有在进程间通信中,在利用管道进行进程间通信时,当读数据的进程退出后,写数据的进程会收到系统发来的13号信号SIGPIPE。

或者平时在命令行输入ctrl c 其实是前台向正在运行的进程发送了2号信号SIGINT。

其实每个信号都有自己的默认的处理函数,对捕捉到的信号进行执行默认的处理函数,部分信号可以自定义处理函数。


signal函数

第一个参数signum是信号的编号,表明要重新定义几号信号

第二个参数是一个函数指针,返回值是void,参数是int类型。这个函数指针就是指向用户要自定义处理动作的函数。

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;void handler(int sig) //自定义处理函数
{cout<<"catch a signal "<<sig<<endl;
}int main()
{signal(2,handler);//自定义捕捉while(true){cout<<"i am running..."<<endl;sleep(1);}return 0;
}

此时在ctrl c发现终止不了程序,原因是我们已经修改了2号信号默认的处理函数。(此时在退出用ctrl \(这个是发送的3号信号SIGQUIT))

注意:9号信号不能自定义捕捉

所有的信号都必须经过OS发出给各个进程。

信号的产生

1、通过键盘输入

例如ctrl c,ctrl \

2、通过系统函数

kill函数

kill -  信号名或者信号编号   进程pid

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;void handler(int sig)
{cout<<"catch a signal "<<sig<<endl;
}int main()
{//把2、3号信号自定义捕捉signal(2,handler);signal(3,handler);while(true){cout<<"i am running..."<<endl;sleep(1);}return 0;
}

运行程序,先看一下他的pid,

此时ctrl c和ctrl \都不可以结束掉进程。

可以发送9号信号,kill -9

3、通过软中断

SIGPIPE信号(管道相关)

SIGALRM信号

通过alarm函数进行定时,时间到了会给当前进程发送SIGALRM信号,该信号的默认操作是终止当前进程。

4、硬件错误

比如说越界发生的段错误

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;void handler(int sig)
{cout<<"catch a signal "<<sig<<endl;sleep(1);
}int main()
{//signal(11,handler);//将11号信号自定义捕捉int *p=nullptr;*p = 10;return 0;
}

如果自定义捕捉,可以发现是11号信号。

虚拟地址映射真实地址的过程出错,实际上是硬件mmu等报错,由操作系统接收。


信号处理

当信号产生时,并不是立即处理的,会先进入一个状态(未决),等到合适的时机才会进行处理。

信号递达(handler)

信号的处理动作,包括默认自定义捕捉忽略

信号未决(pending)

是一种状态(产生到递达之间的状态)。

信号阻塞(block)

进程可以阻塞一个信号,被阻塞的信号一旦产生,将一直处于未决状态,直到进程解除对此信号的阻塞,才会执行递达。

由于信号是发送给进程的,进程肯定有相关的数据结构

举个例子:

例如1号信号SIGHUP的block =0,pending = 0,说明信号未被阻塞,信号也未产生。

2号信号SIGINT的block = 1,pending = 1,说明信号已经产生,处于未决状态,但是又由于信号被阻塞了,所以在等待解除阻塞。

3号信号SIGQUIT的block = 1,pending = 0,信号未产生,但是可以被阻塞。

相关操作函数

在用户这一端,我们只能去修改block标志位,使得让某些信号进入阻塞状态。

sigprocmask函数

用来修改block的函数

第一个参数how决定要以哪种方式修改block。

假设当前信号屏蔽字为mask

SIG_BLOCK方式的意思是:mask = mask | set (add set)

SIG_UNBLOCK :mask = mask&~set (remove set)

SIG_SETMASK:mask= set (equal)

第二个参数set,用户传入设置block,具体意思根据第一个参数

第三个参数oldset,记录前一次的屏蔽字

信号集操作函数

sigset_t声明的变量,sigset_t其实是 unsigened long

接下来这一组函数本质上是用来位操作的,设置好block的值之后,传入上面的sigprocmask函数的set。

sigpending函数

用来读取当前进程未决信号集。

例子

#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;void show_pending(sigset_t pending)//按位显示pending信号集
{for(int i=1;i < 32;i++){if(sigismember(&pending,i))cout<<"1";else cout<<0;}cout<<endl;
}
void handler(int sig)
{cout<<"catch a signal "<<sig<<endl;
}int main()
{//定义信号集sigset_t pending;sigset_t block,oldblock;//初始化sigemptyset(&pending);sigemptyset(&block);sigemptyset(&oldblock);//将2号信号阻塞sigaddset(&block,2);sigprocmask(SIG_BLOCK,&block,&oldblock);cout<<"开始阻塞"<<endl;
//打印pending信号集for(int i=0;i < 5;i++){sigpending(&pending);show_pending(pending);sleep(1);}//自定义捕捉signal(2,handler);//解除阻塞sigprocmask(SIG_UNBLOCK,&block,nullptr);cout<<"解除阻塞"<<endl;while(1);  return 0;
}

可以看到当发送2号信号后,2号信号处于pending状态。一旦解除阻塞,就会递达(合适的时机)

相对应的未决信号就会被清空

信号捕捉

合适的时机:从内核态切换到用户态时,才进行相关的检测。

捕捉信号:信号处理动作是用户自定义的函数。

如果不是用户自定义的函数,即系统默认的处理函数,只需要一次用户到内核状态切换的过程。

如果是用户自定义的捕捉函数

在自定义捕捉函数时,总共需要四次切换,用户到内核、内核到用户切换各两次。

举个例子: 用户程序注册了SIGQUIT信号的处理函数sighandler。当前正在执行main函数,这时发生中断或异常切换到内核态。在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数, sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行。

问题

为什么要这么复杂而不直接在内核调用信号处理函数?

内核权限更高,调用用户自定义的处理函数有风险,之所以引入内核和用户两种模式,也是为了操作系统的安全。

Linux操作系统 - 信号相关推荐

  1. linux操作系统---信号

    linux操作系统---信号 信号 定义 信号的响应方式 修改信号的响应方式 注意事项 练习 信号的发送 练习 信号 定义 系统预先定义好的某些特定的事件,信号可以被发送,也可以被接受,发送和接受的主 ...

  2. linux操作系统信号捕捉函数之回调函数小结

    (1)signal 信号捕捉函数:注册一个信号捕捉函数(不参与捕捉,那是内核的事情) 函数实现: typedef   void(*sighandler_t)(int);   //声明了一个函数指针(代 ...

  3. linux操作系统信号捕捉函数之sigaction用法小结

    (1)sigaction函数:注册一个信号捕捉函数(不参与捕捉信号,信号由内核捕捉),并修改原来的信号处理动作 (2)函数原型及头文件 头文件:#include<signal.h> 函数原 ...

  4. Linux操作系统多线程信号总结(转)

    linux 多线程信号编程总结 linux 多线程信号总结(一) 1. 在多线程环境下,产生的信号是传递给整个进程的,一般而言,所有线程都有机会收到这个信号,进程在收到信号的的线程上下文执行信号处理函 ...

  5. 实验报告Linux操作系统基本命令,linux操作系统实验报告全部.doc

    linux操作系统实验报告全部 计算机操作系统 实验报告 学 号:姓 名:提交日期:2014.12.15成 绩: 东北大学秦皇岛分校 [实验题目]熟悉Linux/UNIX操作系统[实验目的]1.熟悉L ...

  6. linux六种进程状态,Linux操作系统中进程的七种状态

    Linux操作系统中进程的七种状态 发布时间:2018-05-07 20:43, 浏览次数:741 , 标签: Linux 1 Linux中进程的七种状态(1)R运行状态(runing):并不意味着进 ...

  7. Linux操作系统上lsof命令详解

    Linux操作系统上lsof命令详解 2011-10-08 18:31:31 http://xjsunjie.blog.51cto.com/999372/682865 标签:Linux lsof命令 ...

  8. Linux 操作系统原理 — 多处理器架构

    目录 文章目录 目录 计算平台体系结构 单核 CPU 和超线程 多核架构的出现 SMP 对称多处理结构 NUMA 非统一内存访问结构 MPP 大规模并行处理结构 Linux 上的 NUMA 基本对象概 ...

  9. Linux 操作系统原理 — 内核态与用户态

    目录 文章目录 目录 Linux 的内核态与用户态 系统调用(System Call) Shell 用户态和内核态的切换 进程的用户空间和内核空间的内存布局 内核空间 用户空间 Linux 的内核态与 ...

最新文章

  1. 再谈 iptables 防火墙的 指令配置
  2. Apache Mina2.x网络通信框架使用入门
  3. 类型转换出现在赋值运算符左边的情况
  4. 使用微软分布式缓存服务Velocity Part 3
  5. minecraft正版整合包服务器,我的世界1.7.2基佬整合包
  6. 《c语言从入门到精通》看书笔记——第16章 网络套接字编程(上)——网络
  7. powermockito教程_Mockito与PowerMock的使用基础教程
  8. ajax入门体会(转)
  9. 一步步Netty的基石 - Reactor模式
  10. python进阶17炫技巧
  11. SQL中的5种常用的聚集函数
  12. RESTful Web Service - JAX-RS Annotations
  13. PCWorld:流量日趋集中 大公司影响整个互联网
  14. 不错的离线IP地址定位库
  15. D5 登录抽屉新热榜
  16. 为什么移动端跨平台开发不靠谱?
  17. FPGA:什么是IO单元、IO标准、Bank、VCCO、VREF
  18. 索骥馆-DIY操作系统之《30天自制操作系统》扫描版[PDF]
  19. windows系统下ip地址无法修改,亲测可用imdam博客之家
  20. SEM标准品、对照品的管理大全

热门文章

  1. 2019年寒假 纪中培训总结
  2. 163邮箱申请,163.net邮箱成2021年黑马品牌!
  3. Python入门 类型转换
  4. 国密算法Go语言实现(详解)(九) ——SM2(椭圆曲线公钥密码算法)
  5. 安装jieba库的解决办法及简单使用
  6. mysql多条新增字段sql合并为一条新增
  7. 图像恢复(加噪与去噪)
  8. JDK8经典特性回顾
  9. Kubernetes容器编排引擎
  10. 烽火ExMobi移动应用平台能源行业初体验