浅谈Linux中的信号处理机制(三)
一晃眼,已经到9月底了,都来不及去感慨时间匆匆。最近常常会想明年的今天我将会在那里干着什么样的工作?对未来又是憧憬又是担忧,压力山大。无论如何现在还是踏踏实实的学习吧,能这样安安静静学习的日子也不多了。不扯了,还是接着前面的写吧。
在上篇提到过,SA_RESTART标志的作用是重启系统调用。其作用是建立在这样的基础上的:在Linux系统上,如果进程正在执行一个低速系统调用期间捕捉到一个信号,那么该系统调用会被中断,在处理完信号之后,这个系统调用将不会继续执行。随后返回错误,errno被设置为EINTR。所谓的慢速系统调用包括但不局限于以下:
- 对慢速设备(pipe、terminal、FIFO、socket)的读取操作,当其上不存在数据时,可能会阻塞当前的系统调用
- 如果数据不能被相同的类型文件立即接受,写操作可能会使调用者永远阻塞
- 在某种条件发生之间打开某些类型的文件,可能会发生阻塞(是的open()也会阻塞,打开FIFO的时候就有可能)
- pause()、wait()系列函数
- 某些ioctl()操作
- 某些IPC操作(mq_receive()等)
- 设置文件锁的函数flock()、fcntl()等
- epoll_wait() epoll_pwait()
- 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 1024 void 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了。于是可能会出现一些意料之外的输出。
一般来讲,更新全局数据结构的函数,是不可重入的函数。通常有这几类:
- 使用静态数据结构保存返回信息的函数,有 getlogin() gethostbyname() crypt()...
- malloc() free()因为他们在内部维护了一个全局的链表来记录分配和释放的内存的相关信息
- 标准IO函数,他们大都是行缓冲的,而所使用的缓冲区是一个全局的buffer
当我们所编写的函数要更新全局变量该怎么办呢?sig_atomic_t这种数据类型是C语言标准所规定的一种原子操作的数据类型,关于原子操作的内容可移步:《c++11 Thread库之原子操作》。具体用法和c++11中的std::atomic类型类似,不再赘述。值得一提的是,使用这个数据类型时,应当使用volatile关键字声明,以防止编译器把其优化到寄存器之中。
在使用gdb调试程序时,缺省情况下信号会被gdb截获,导致要调试的程序无法接收到信号,我们可以使用info handle来查看信号的缺省处理方式,同样info signals可以查看接受到的信号。要想在调试的程序中使用信号,我们需要使用gdb中的handle这个命令,具体用法如这个形式 :handle signal keywords。keywords的取值如下:
| keywords | 说明 | keywords | 说明 |
| stop | 当GDB收到signal,停止被调试程序的执行 | nostop | 当GDB收到指定的信号,不会应用停止程序的执行,只会打印出一条收到信号的消息 |
| 如果收到signal,打印出一条信息 | noprint | 不会打印信息 | |
| pass | 如果收到signal,把该信号通知给被调试程序 | nopass | 不会告知被调试程序收到signal |
| ignore | 同nopass | noignore | 同pass |
handle命令还是比较简单的,设置完以后,可以像普通的程序那样调试了。
关于信号暂时先总结这么多吧,以后用到了什么再慢慢往里边塞吧!
浅谈Linux中的信号处理机制(三)的更多相关文章
- 浅谈Linux中的信号处理机制(二)
首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号处理机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Lin ...
- 浅谈Linux中的信号处理机制(一)
有好些日子没有写博客了,自己想想还是不要荒废了时间,写点儿东西记录自己的成长还是百利无一害的.今天是9月17号,暑假在某家游戏公司实习了一段时间,做的事情是在Windows上用c++写一些游戏英雄技能 ...
- Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理
Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博 ...
- 浅谈linux中shell变量$#,$@,$0,$1,$2,$?的含义解释
浅谈linux中shell变量$#,$@,$0,$1,$2,$?的含义解释 下面小编就为大家带来一篇浅谈linux中shell变量$#,$@,$0,$1,$2的含义解释.小编觉得挺不错的,现在就分享给 ...
- 浅谈Linux中的各种锁及其基本原理
本文首发于:https://mp.weixin.qq.com/s/Ahb4QOnxvb2RpCJ3o7RNwg 微信公众号:后端技术指南针 0.概述 通过本文将了解到如下内容: Linux系统的并行性 ...
- 浅谈Linux的内存管理机制
转至:http://ixdba.blog.51cto.com/2895551/541355 一 物理内存和虚拟内存 我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此, ...
- 转:浅谈Linux的内存管理机制
一 物理内存和虚拟内存 我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概 ...
- 【Unity3d游戏开发】浅谈UGUI中的Canvas以及三种画布渲染模式
一.Canvas简介 Canvas画布是承载所有UI元素的区域.Canvas实际上是一个游戏对象上绑定了Canvas组件.所有的UI元素都必须是Canvas的自对象.如果场景中没有画布,那么我们创建任 ...
- 浅谈 Linux 内核无线子系统
浅谈 Linux 内核无线子系统 本文目录 1. 全局概览 2. 模块间接口 3. 数据路径与管理路径 4. 数据包是如何被发送? 5. 谈谈管理路径 6. 数据包又是如何被接收? 7. 总结一下 L ...
随机推荐
- React Native at first sight
what is React Native? 跟据官方的描述, React Native是一套使用 React 构建 Native app 的编程框架. 推出不久便引发了广泛关注, 这也得益于 Java ...
- 30行代码让你理解angular依赖注入:angular 依赖注入原理
依赖注入(Dependency Injection,简称DI)是像C#,java等典型的面向对象语言框架设计原则控制反转的一种典型的一种实现方式,angular把它引入到js中,介绍angular依赖 ...
- WindowsCE project missing Microsoft.CompactFramework.CSharp.targets in Visual Studio 2008
00x0 前言 之前在Windows 7系统中开发的WindowsCE项目,最近换成Windows 10系统,需要将项目进行修改,打开项目后提示如下错误: 无法读取项目文件"App.cspr ...
- LINQ to SQL语句(15)之String
LINQ to SQL支持以下String方法.但是不同的是默认情况下System.String方法区分大小写.而SQL则不区分大小写. 1.字符串串联(String Concatenation) v ...
- 趣味python编程之经典俄罗斯方块
国庆期间闲不住,用python把经典俄罗斯方块实现了一遍,找到了些儿时的乐趣.因此突发奇想,打算用python写点经典又确实有趣的小程序形成系列.正统编程之余也给自己找点儿乐趣,换个角度写程序. 原计 ...
- HttpWebRequest的GetResponse或GetRequestStream偶尔超时 + 总结各种超时死掉的可能和相应的解决办法
[问题] 用C#模拟网页登陆,其中去请求几个页面,会发起对应的http的请求request,其中keepAlive设置为true,提交请求后,然后会有对应的response: resp = (Http ...
- GJM :C#开发 异步处理是目的,多线程是手段
但是BeginAccept和EndAccept不就是system.net.socket封装好的异步socket吗如果用多线程来实现的话那就不叫异步了吧 1.再次强调,异步是目的,多线程是手段. 所谓异 ...
- 巧用CSS实现分隔线
下面是几种简单实现分隔线的方法,个人比较喜欢第二种,我也给出了最后第五种比较2的写法,请大家拍砖,或者提供其他好的方法. 单个标签实现分隔线: 点此查看实例展示 .demo_line_01{ padd ...
- browserify学习总结
前言 在未接触browserify,虽然我知道它是一个前端构建工具,但还是有几个疑问: 1. browserify出现的日期? 2. 能构建哪些文件? 3. 附加的browserify代码体积是多大? ...
- H5学习
1.html{font-size:62.5%;}//不用font-size:10px的原因:(因为设了62.5%后就有1rem = 10px,便于用rem来指定元素的尺寸,这样响应式的时候可以直接改变 ...