一晃眼,已经到9月底了,都来不及去感慨时间匆匆。最近常常会想明年的今天我将会在那里干着什么样的工作?对未来又是憧憬又是担忧,压力山大。无论如何现在还是踏踏实实的学习吧,能这样安安静静学习的日子也不多了。不扯了,还是接着前面的写吧。

SA_RESTART语义

在上篇提到过,SA_RESTART标志的作用是重启系统调用。其作用是建立在这样的基础上的:在Linux系统上,如果进程正在执行一个低速系统调用期间捕捉到一个信号,那么该系统调用会被中断,在处理完信号之后,这个系统调用将不会继续执行。随后返回错误,errno被设置为EINTR。所谓的慢速系统调用包括但不局限于以下:

  1. 对慢速设备(pipe、terminal、FIFO、socket)的读取操作,当其上不存在数据时,可能会阻塞当前的系统调用
  2. 如果数据不能被相同的类型文件立即接受,写操作可能会使调用者永远阻塞
  3. 在某种条件发生之间打开某些类型的文件,可能会发生阻塞(是的open()也会阻塞,打开FIFO的时候就有可能)
  4. pause()、wait()系列函数
  5. 某些ioctl()操作
  6. 某些IPC操作(mq_receive()等)
  7. 设置文件锁的函数flock()、fcntl()等
  8. epoll_wait() epoll_pwait()
  9. select() poll()

以我现在的功力总结全面是不可能的,平时当我们遇上进程要处理会阻塞的系统调用时,就需要留个心眼儿,要考虑一下被信号中断的情况。在不使用SA_RESTART的时候,我们要重启系统调用时,可以这样组织代码:

int cnt;
while((cnt = read(fd,buf,BUFSIZE))==-1 && errno== EINTR)               //read()如果被中断返回错误,就会自动重启continue;
...
if(cnt == -1)exit(-1);                                                          //其他使read()出错的情况

  我反正是不喜欢的这样的代码风格的,有了SA_RESTART这个标志,我们本可以把代码写得更加优雅:

#include <errno.h>
#include <signal.h>
#include <unistd.h>
#define BUFSIZE 1024void handler(int sig)
{
}int main()
{struct sigaction act;act.sa_flags = SA_RESTART;sigemptyset(&act.sa_mask);act.sa_handler = handler;if(sigaction(SIGINT,&act,0) == -1)exit(-1);    char buf[BUFSIZE] = {0};read(0,buf,BUFSIZE-1);write(1,buf,BUFSIZE);
}

  在之前的一篇博客上,曾使用过这个标志,应该说这个标志位还是比较常用的一个,特别是在socket编程中。

可重入函数与不可重入函数

在《c++11 Thread库之原子操作》中提到了多线程程序中多个线程之间数据共享所引起的问题。其实在有信号处理的程序中也存在着这样的问题,因为信号可能会在程序执行的某一时间点异步中断程序,转而去执行信号处理函数。和多线程程序一样,这时候程序就有了两个执行的线程,虽然不是并发的。如果一个进程的多条线程可以同时安全地(能产生预期的效果)执行某一函数,那么我们称这个函数是可重入函数,反之则为不可重入函数。

我做了一个gif图来表示不可重入函数,就拿我们最熟悉的printf()函数来举例,我们已经知道printf()函数是行缓冲的IO函数,而这个缓冲区是一个全局的buffer。当主线程中正在执行printf()的时候,一个信号过来了,那么进程会把这个当前线程暂停,转而去执行信号处理函数,恰巧这个信号处理函数中,也调用了printf()函数,于是buf就被修改了(图中用变了颜色来表示),当信号处理函数返回以后,主线程恢复执行,而此时它正在使用的buf已经不是之前的那个buf了。于是可能会出现一些意料之外的输出。

  一般来讲,更新全局数据结构的函数,是不可重入的函数。通常有这几类:

  1. 使用静态数据结构保存返回信息的函数,有 getlogin() gethostbyname() crypt()...
  2. malloc() free()因为他们在内部维护了一个全局的链表来记录分配和释放的内存的相关信息
  3. 标准IO函数,他们大都是行缓冲的,而所使用的缓冲区是一个全局的buffer

当我们所编写的函数要更新全局变量该怎么办呢?sig_atomic_t这种数据类型是C语言标准所规定的一种原子操作的数据类型,关于原子操作的内容可移步:《c++11 Thread库之原子操作》。具体用法和c++11中的std::atomic类型类似,不再赘述。值得一提的是,使用这个数据类型时,应当使用volatile关键字声明,以防止编译器把其优化到寄存器之中。

GDB调试与信号

在使用gdb调试程序时,缺省情况下信号会被gdb截获,导致要调试的程序无法接收到信号,我们可以使用info handle来查看信号的缺省处理方式,同样info signals可以查看接受到的信号。要想在调试的程序中使用信号,我们需要使用gdb中的handle这个命令,具体用法如这个形式   :handle  signal keywords。keywords的取值如下:

keywords 说明 keywords 说明
stop 当GDB收到signal,停止被调试程序的执行 nostop GDB收到指定的信号,不会应用停止程序的执行,只会打印出一条收到信号的消息
print 如果收到signal,打印出一条信息 noprint 不会打印信息
pass 如果收到signal,把该信号通知给被调试程序 nopass 不会告知被调试程序收到signal
ignore 同nopass noignore 同pass

handle命令还是比较简单的,设置完以后,可以像普通的程序那样调试了。

关于信号暂时先总结这么多吧,以后用到了什么再慢慢往里边塞吧!

转载于:https://www.cnblogs.com/ittinybird/p/4845394.html

浅谈Linux中的信号处理机制(三)相关推荐

  1. 浅谈Linux中ldconfig和ldd的用法

    ldd 查看程序依赖库 ldd 作用:用来查看程式运行所需的共享库,常用来解决程式因缺少某个库文件而不能运行的一些问题. 示例:查看test程序运行所依赖的库: /opt/app/todeav1/te ...

  2. ldconfig mysql_浅谈Linux中ldconfig和ldd的用法

    ldd 查看程序依赖库 ldd 作用:用来查看程式运行所需的共享库,常用来解决程式因缺少某个库文件而不能运行的一些问题. 示例:查看test程序运行所依赖的库: /opt/app/todeav1/te ...

  3. linux中sh+$0,浅谈linux中shell变量$#,$@,$0,$1,$2的含义解释

    摘抄自:ABS_GUIDE 下载地址:http://www.tldp.org/LDP/abs/abs-guide.pdf linux中shell变量$#,$@,$0,$1,$2的含义解释: 变量说明: ...

  4. 浅谈linux中shell变量$#,$@,$0,$1,$2,$?的含义解释

    浅谈linux中shell变量$#,$@,$0,$1,$2,$?的含义解释 下面小编就为大家带来一篇浅谈linux中shell变量$#,$@,$0,$1,$2的含义解释.小编觉得挺不错的,现在就分享给 ...

  5. linux中whoami命令的作用是,浅谈linux中的whoami与 who指令

    浅谈linux中的whoami与 who指令 whoami 功能说明: 显示用户名称 语法: whoami 补充说明: 显示自身的用户名称,本指令相当于执行  id -un 指令 whoami 与 w ...

  6. 实现Linux的whoami命令,浅谈linux中的whoami与 who指令

    whoami 功能说明: 显示用户名称 语法: whoami 补充说明: 显示自身的用户名称,本指令相当于执行  id -un 指令 whoami 与 who am i的区别 who这个命令重点在用来 ...

  7. 浅谈linux中的根文件系统(rootfs的原理和介绍)

    转自:点击打开 linux中有一个让很多初学者都不是特别清楚的概念,叫做"根文件系统".我接触linux前前后后也好几年了,但是对这个问题,至今也不是特别的清楚,至少没法给出一个很 ...

  8. 转:浅谈Linux的内存管理机制

    一 物理内存和虚拟内存          我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概 ...

  9. 浅谈Linux的内存管理机制

    一 物理内存和虚拟内存          我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概 ...

最新文章

  1. Linux---管理网络
  2. Web 服务器-Apache详解
  3. hadoop1.2.1伪分布模式配置
  4. JUnit4用法详解
  5. HDU 4332 Constructing Chimney [状态压缩+矩阵]
  6. SpringBoot登录登出切面开发
  7. R语言学习笔记(4)
  8. c 程序中的注释相当于空白字符_Python专题 | (三)注释、变量与输出
  9. 用Jenkins自动化搭建测试环境_jenkins基础搭建_入门试炼02
  10. SpringMVC框架搭建的步骤
  11. SQL_alter_table已有表操作
  12. 为什么数据可视化很重要
  13. php log日志管理,PHP日志LOG类定义与用法示例
  14. CUDA 学习(十四)、纹理内存
  15. vf计算机教程,VF教程,打印版.pdf
  16. Ps和Sai文件自动保存,自动备份
  17. lync前段服务器证书安装,Lync Server 2013企业版部署测试六:前端服务器安装Lync Server系统...
  18. 2018年俄罗斯世界杯之Java数据爬虫(一)
  19. Codeforces 1090C New Year Presents
  20. Visio绘制跨职能流程图示例

热门文章

  1. Codeforces Round #542 [Alex Lopashev Thanks-Round] (Div. 1)
  2. linux用户及用户组管理
  3. 【VMCloud云平台】SCVMM配置(九)创建Web服务模板
  4. HDU2699+Easy
  5. asp.net 动态绑定html表格
  6. IT专家经验教训分享: 我犯过的九件大错
  7. js代码实现购物车效果
  8. Node.js aitaotu图片批量下载Node.js爬虫1.00版
  9. OpenMP并行化实例----Mandelbrot集合并行化计算
  10. HDU OJ 5437 Alisha’s Party 2015online A