mysleep的实现
来源:互联网 发布:java集合框架中抽象类 编辑:程序博客网 时间:2024/05/21 19:32
用信号机制实现sleep,我们称之为mysleep()
在代码实现mysleep()之前,先来了解一下如下几个函数:
#include <unistd.h> unsigned int alarm(unsigned int seconds);//设定闹钟,终止进程
alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号,该信号的默认处理动作是终止当前进程。这个函数的返回值是0(意味着闹钟准时响了)或者是以前设定的闹钟时间还余下的秒数(可能由于某些原因,闹钟提前响了)。打个比方,某人要小睡一觉,设定闹钟为30分钟之后响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍是以前设定的闹钟时间还余下的秒数。如果余下的秒数为0,就是说操作系统在senconds秒之后准时的给当前进程发送了SIGALRM信号。如果余下的秒数大于0,说明操作系统由于某些原因在没有到senconds秒的时候给当前进程发送了SIGALRM信号,而返回值就是离senconds秒还差多少秒。
#include <signal.h> int sigaction(int signo, const struct sigaction *act, structsigaction *oact);
sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回-1。signo 是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非空,则通过oact传出该信号原来的处理动作。act 和 oac t指向 sigaction 结构体。
将 sa_handler 赋值为常数 SIG_IGN 传给 sigaction 表示忽略信号,赋值为常数 SIG_DFL 表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数,该函数返回值为 void,可以带一个 int 参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。
接下来,解释一下结构体中的 sa_mask 是什么意思。当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么,它会被阻塞到当前处理结束为止。如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用 sa_mask 字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
sa_flags 字段包含一些选项,本章的代码都把 sa_flags 设为0,sa_sigaction 是实时信号的处理函数,在这里不详细介绍这个字段了
#include <unistd.h> int pause(void);
pause函数使调用进程挂起直到有信号递达。如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR,所以pause只有出错的返回值。
#include<stdio.h> #include<signal.h> #include<unistd.h> void myhandler(int sig) //自定义SIGALRM信号的处理函数myheadler {} int mysleep(int timeout) { struct sigaction act,oact; act.sa_handler = myhandler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGALRM,&act,NULL); alarm(timeout); //设定闹钟 pause(); //等待 int ret = alarm(0); //取消闹钟 sigaction(SIGALRM,&oact,NULL); //恢复SIGALRM信号以前的处理动作 return ret; } int main() { while(1) { printf("using mysleep!\n"); mysleep(3); } return 0; }
竞态条件
这个程序存在很大的一个bug,设想一下这样的时序:注册SIGALRM信号的处理函数,调用alarm(nsecs)设定闹钟,内核调度优先级更高的进程取代当前进程执行,并且优先级更高的进程有很多个,每个都要执行很长时间 nsecs秒钟之后闹钟超时了,内核发送SIGALRM信号给这个进程,处于未决状态。优先级更高的进程执行完了,内核要调度回这个进程执行。SIGALRM信号递达,执行处理函 数sig_alrm之后再次进入内核。返回这个进程的主控制流程,alarm(nsecs)返回,调用pause()挂起等待,可是SIGALRM信号已经处理完了,那么这个进程就会一直挂起等待了。
出现这个问题的根本原因是系统运行的时序(Timing)并不像我们写程序时所设想的那样。由于异步事件在任何时候都有可能发生(这里的异步事件指出现更高优先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题而导致错误,这叫做竞态条件
解决方法:
1. 屏蔽SIGALRM信号
2. alarm(nsecs)
3. 解除对SIGALRM信号的屏蔽
4. pause()
但是在3和4之间也有一段时间,不保证这段时间信号不会被递达。如果3和4是同时执行的,是一个原子操作。这里就用到下面这个函数sigsuspend();将解除信号屏蔽与pause();结合为一步,并且时原子操作,避免竞态条件的发生。
#include <signal.h> int sigsuspend(const sigset_t *sigmask);
和pause一样,sigsuspend没有成功返回值,只有执行了一个信号处理函数之后sigsuspend才返回,返回值为-1,errno设置为EINTR。调用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时解除对某个信号屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值,如果原来对该信号是屏蔽的,从sigsuspend返回后仍然是屏蔽的。
#include<stdio.h> #include<signal.h> #include<unistd.h> void handler(int sig) {} int mysleep(int timeout) { struct sigaction act,oact; sigset_t newmask,oldmask,suspmask; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGALRM,&act,&oact); sigemptyset(&newmask); sigaddset(&newmask,SIGALRM); sigprocmask(SIG_BLOCK,&newmask,&oldmask); alarm(timeout); suspmask = oldmask; sigdelset(&suspmask,SIGALRM); sigsuspend(&suspmask); int ret = alarm(0); sigaction(SIGALRM,&oact,NULL); sigprocmask(SIG_SETMASK,&oldmask,NULL); return ret; } int main() { while(1) { printf("using mysleep!\n"); mysleep(3); } return 0; }
- mysleep的实现
- mysleep 的两种实现
- Linux下mysleep的实现
- 【Linux】信号的基础知识&mysleep的实现
- 【Linux】sleep函数的简单实现--mysleep
- linux编程之mysleep的实现
- linux mysleep实现
- 实现sleep-mysleep
- 实现睡眠函数mysleep
- mysleep与竞态条件下的mysleep
- Linux下的mysleep
- mysleep
- mysleep
- 实验五:实现mysleep函数
- Linux C 实现mysleep数
- 【Linux】实现睡眠函数mysleep
- 普通版本mysleep和规避竞态条件的mysleep
- mysleep()的多个版本实现及竟态条件的认知
- 二叉树
- linux的jdk安装
- eclipse中启动tomcat而浏览器无法访问http://localhost:8080
- sturts2: jsp 与action之间的list传送
- iOS AFN 3.0版本前后区别 01
- mysleep的实现
- C++专项训练-1
- tensorFlow测试及屏蔽warning信息
- 内部类概念简单练习
- Redux-devtools 使用
- 阿里+百度+CVTE面试集
- 提车注意事项清单
- css-如何让你的元素居中?
- 基于CentOS搭建 Git 服务