【Linux】自主实现my_sleep【转】
转自:http://blog.csdn.net/scenlyf/article/details/52068522
版权声明:本文为博主原创文章,未经博主允许不得转载。
首先说一下信号相关的内容。
********信号
我们在运行程序时,经常会碰到报错,这是由于操作系统触发异常,于是系统抛出某一信号产生的。例如,一个死循环的程序,处理方式如下:
1)用户输入命令,在Shell下启动这个前台进程;
2)用户按下Ctrl-C,这个键盘输入产生一个硬件中断,该进程的用户空间代码暂停执行,CPU从用户态切换到内核态处理硬件中断;
3)终端驱动程序将Ctrl-C解释成一个SIGINT信号,记在该进程PCB中(也可以说发送了一 个SIGINT信号给该进程);
4)当某个时刻要从内核返回到该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号,发现有一个SIGINT信号待处理,而这个信号的默认处理动作是终止进程,所以直接终止进程而不再返回它的用户空间代码执行。
一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到。
编号前31为普通信号,编号34以上的是实时信号。这些信号各自在什么条件下产生,默认的处理动作是什么,在signal(7)中都有详细说明:man 7 signal
********信号的产生方式
alarm(unsignal int seconds); //seconds为0时,取消设定的闹钟,返回以前设定的闹钟时间还余下的秒数
#include<signal.h> int kill(pid_t pid, int sig); //成功返回0,错误返回-1。
raise:该函数可以给当前进程发送指定的信号(自己给自己发信号)。
#include<signal.h> int raise(int sigo); //成功返回0,错误返回-1。
abort: abort函数使当前进程接收到SIGABRT信号而异常终止。
#include<stdlib.h> void abort(void); //像exit函数一样,该函数总会成功的,所以没有返回值。
********阻塞信号
实际信号的处理动作称为信号递达(Delivery);信号从产生到递达之间的状态称为信号未决(Pending);进程可以选择阻塞(Block )某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。信号的产生、未决和是否阻塞无关。
注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
Linux中:常规信号在递达之前产生多次只计一次,而实时信号在递达之前生多次可以依次放在一个队列里。这里不讨论实时信号。从上图来看,每个信号只有一 个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的据类型sigset_t来存储,sigset_t称为信号集, 阻塞信号集也叫做当前进程的信号屏蔽字(Signal
Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。
信号集不能进行位操作,必须使用相关函数;信号集是0,1序列。信号集操作函数如下:
进行读取进程的未决信号集:调用函数sigpending。
进程在收到信号不是立即去处理,保存在自己的PCD中,在合适的时候处理。什么时候处理呢?当进程由于陷阱、异常等,陷入内核态,从内核态到用户态时,进行信号检查和处理。进程的4种情况:如果block,立即返回;如果pending,先递达,处理后返回;stop信号等,直接终止;忽略,直接返回。
如下图所示:
函数sigaction:读取和修改与指定信号相关联的处理动作
#include<signal.h>
int
sigaction(int signo, const struct sigaction *act, struct sigaction *oact); //成功返回0,失败返回-1
signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体:
-----sa_handler:赋值为常数SIG_IGN传给sigaction表示忽略信号;赋值为常数SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号。 向内核注册了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。
-----sa_mask:进程的信号屏蔽字。如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
-----sa_flags:sa_flags字段包含一些选项,本文代码把sa_flags设为0。
-----sa_sigaction是实时信号的处理函数。
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。
pause函数:使调用进程挂起直到有信号递达
#include <unistd.h>
int pause(void); // pause只有出错的返回值(exec也是),错误码EINTR表示“被信号中断”。
pause函数使调用进程挂起直到有信号递达
-----如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;
-----如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;
-----如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR。
下面用alarm和pause实现sleep(3)函数,my_sleep。
1、main函数调用my_sleep函数,后者调用sigaction注册了SIGALRM信号的处理函数handler。
2、调用alarm(times)设定闹钟。
3、调用pause等待,内核切换到别的进程运行。
4、times秒之后,闹钟超时,内核发SIGALRM给这个进程。
5、从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数是handler。
6、切换到用户态执行handler函数,进入handler函数时SIGALRM信号被自动屏蔽, 从handler函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入内核,再返回用户态继续执行进程的主控制流程。
7. pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理动作。
函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入。例如链表的插入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入Reentrant
函数。
运行结果如下:
运行后,发现可以成功实现3秒打印一句语句,但上述程序存在问题。
1.、注册SIGALRM信号的处理函数。
2、调用alarm(times)设定闹钟。
3、内核调度优先级更高的进程取代当前进程执行,并且优先级更高的进程有很多个,每个都要执行很长时间
4、times秒钟之后闹钟超时了,内核发送SIGALRM信号给这个进程,处于未决状态。
5、优先级更高的进程执行完了,内核要调度回这个进程执行。SIGALRM信号递达,执行处理函数sig_alrm之后再次进入内核
6、返回这个进程的主控制流程,alarm(times)返回,调用pause()挂起等待。
7、可是SIGALRM信号已经处理完了,还等待什么呢?
出现这个问题的根本原因是系统运行的时序(Timing)并不像我们写程序时所设想的那样。虽然alarm(times)紧接着的下一行就是pause(),但是无法保证pause()一定会在调用alarm(times)之后的times秒之内被调用。由于异步事件在任何时候都有可能发生(这里的异步事件指出现更高优先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题而导致错误,这叫做竞态条件
(Race Condition)。
sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题,在对时序要求严格的场合下都应该调用sigsuspend而不是pause。
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
和pause一样,sigsuspend没有成功返回值,只有执行了一个信号处理函数之后sigsuspend才返回,返回值为-1,errno设置为EINTR。调用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时解除对某个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值,如果原来对该信号是屏蔽的,从sigsuspend返回后仍然是屏蔽的。
改后的my_sleep如下:
说明:
如果在调用my_sleep函数时SIGALRM信号没有屏蔽:
1、调用sigprocmask(SIG_BLOCK,&newmask, &oldmask)时,屏蔽SIGALRM。
2、调用sigsuspend(&suspmask)时,解除对SIGALRM的屏蔽,然后挂起等待。
3、SIGALRM递达后suspend返回,自动恢复原来的屏蔽字,也就是再次屏蔽SIGALRM。
4、调用sigprocmask(SIG_SETMASK, &oldmask, NULL)时,再次解除对SIGALRM的屏蔽。
【Linux】自主实现my_sleep【转】的更多相关文章
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- DotNetCore跨平台~linux上还原自主nuget包需要注意的问题
问题的产生的背景 由于我们使用了jenkins进行部署(jenkins~集群分发功能和职责处理),而对于.net core项目来说又是跨平台的,所以对它的项目拉取,包的还原,项目的编译和项目的发布都是 ...
- 红旗 Linux 桌面操作系统11来了:支持国产自主CPU,全新UI风格设计,兼容面广...
链接:https://reurl.cc/g8ke9X 红旗Linux桌面操作系统11将于1月10日开放预览版的下载,新版本具有良好的硬件兼容,支持多款国产自主CPU品牌,同时还具有丰富的外设支持及海量 ...
- linux 双Redis + keepalived 主从复制+宕机自主切换
主要核心思想,如果master 和 salve 全部存活的情况,VIP就漂移到 master.读写都从master操作,如果master宕机,VIP就会漂移到salve,并将之前的salve切换为ma ...
- linux MySQL 5.7+keepalived 主备服务器自主切换
一.环境准备1.关闭防火墙与selinux systemctl stop firewalld setenforce 0 sed -i 's/SELINUX=.*/SELINUX=disabled/g' ...
- 14门Linux课程,打通你Linux的任督二脉!
Linux有很多优点:安全.自主.开源--,也正是这些优点使得很多人都在学Linux. 虽说网上有大把的Linux课程资源,但是对很多小白来说网上的课程资源比较零散并不适合新手学习. 正因为此,总结了 ...
- 如何灵活运用Linux 进程资源监控和进程限制
导读 每个 Linux 系统管理员都应该知道如何验证硬件.资源和主要进程的完整性和可用性.另外,基于每个用户设置资源限制也是其中一项必备技能. 在这篇文章中,我们会介绍一些能够确保系统硬件和软件正常工 ...
- linux下文件搜索命令学习笔记
1. locate:按照文件名搜索文件 locate filename 与find在整个操作系统中遍历搜索不同,locate命令在/var/lib/mlocate这个后台数据库中按照文件名搜索,所以优 ...
- Linux上USB移植错误解决笔记
在内核目录下先配置支持USB设备,无论你是什么设备,这一步都是必须的,USB驱动和其他的驱动不同,它分为USB设备驱动,另外还有USB-Host主机控制器的驱动,配置如下: Device Driver ...
随机推荐
- Nginx认证
Nginx 的 ngx_http_auth_basic_module模块允许通过使用“HTTP基本认证”协议验证用户名和密码来限制对资源的访问. 配置举例: location / { auth_bas ...
- phpspider案例
phpspider案例 <?php require './autoload.php'; use phpspider\core\phpspider; /* Do NOT delete this c ...
- formpanel布局的学习
FormPanel有两种布局:form和column,form是纵向布局,column为横向布局.默认为后者.使用layout属性定义布局类型.对于一个复杂的布局表单,最重要的是正确分割,分割结果直接 ...
- (转)为什么在 2013 十月番中出现了很多以 3D 渲染代替传统 2D 绘画来表现人物的镜头?
一直都有的,特别是三次元这家公司一直致力于3d的风格化渲染既大家说的3d转2d.目前最厉害的商业化软件是pencil+,占领大部分的作品.而mentalray,早期用于disney的部分风格化渲染:i ...
- win7旗舰版64位java的jdk环境变量的配置(2012-12-26-bd 写的日志迁移
首先到oracle的官方网站http://www.oracle.com/technetwork/cn/java/javase/downloads/index.html下个JDK比如下图: 必须是win ...
- JDK各版本新特性浅谈
JDK 5.0 自动拆装箱 枚举 可变参数 泛型 For -each 内省 静态导入 JDK 6.0 console开发控制台程序 轻量级HTTP ServerAPI 支持脚本语言 使用Compile ...
- Hyper-V 网络虚拟化技术细节
Hyper-V 网络虚拟化技术细节 适用对象:Windows Server 2012 R2 服务器虚拟化能让多个服务器实例在同一台物理主机上同步运行,但各个服务器实例都是相互独立的. 每台虚拟机的运作 ...
- 2018安恒杯11月月赛 MISC
题目放评论了 Numeric password 这次隐写没有按照套路出牌,很强. 记录一下 看来自主学习的能力很有待提高. 打开Numeric password.txt 中华文化博大精深,近日在教小外 ...
- Python-S9-Day126——Scrapy爬虫框架
01 今日内容概要 02 内容回顾和补充:scrapy 03 内容回顾和补充:网络和并发编程 04 Scrapy爬虫框架:pipeline做持久化(一) 05 Scrapy爬虫框架:pipeline做 ...
- hnust hold不住的老师
问题 H: Hold不住的老师 时间限制: 1 Sec 内存限制: 128 MB提交: 415 解决: 63[提交][状态][讨论版] 题目描述 因为我们学校ACM集训队取得的一个个优异成绩,AC ...