实现sleep函数
来源:互联网 发布:出版社待遇知乎 编辑:程序博客网 时间:2024/06/05 15:38
sleep()函数
函数原型:
unsigned int sleep(unsigned int seconds);
函数功能:
使调用程序挂起等待seconds秒,或者直到有信号到达使之中断。
函数返回值:
若程序挂起到参数所指定的时间则返回0;若有信号中断则返回剩余秒数。
实现mysleep()
版本1
所需函数:
1)
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功返回0,出错返回-1。
signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体:
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void);};
sa_handler表示普通信号的处理函数。
- 将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号;
- 赋值为常数SIG_DFL表示执行系统默认处理动作;
- 赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。
显然,这是一个回调函数,不是被main函数调用,而是被系统所调用。当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。
如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
sa_flags字段包含一些选项,一般把sa_flags设为0
sa_sigaction是实时信号的处理函数
2)
int pause(void);
pause函数使调用进程挂起直到有信号递达。
- 如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;
- 如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;
- 如果信号的处理动作是捕捉,则调⽤用了信号处理函数之后pause返回-1,errno设置为EINTR(表示被信号中断)。
所以pause只有出错的返回值(exec函数也是哦)
具体实现:
#include <stdio.h>#include <signal.h>#include <unistd.h>#include <stdlib.h>void sig_alarm(int signo){ //do nothing}unsigned int mysleep(unsigned int nsecs){ struct sigaction new, old; unsigned int unslept = 0; new.sa_handler = sig_alarm; sigemptyset(&new.sa_mask); new.sa_flags = 0; sigaction(SIGALRM, &new, &old); //注册信号处理函数 alarm(nsecs); //设置闹钟 pause(); //使调用进程挂起等待,直到有信号递达 unslept = alarm(0); //清除闹钟 sigaction(SIGALRM, &old ,NULL); //恢复默认信号处理动作 return unslept;}int main(){ while(1) { mysleep(5); printf("5 seconds passed\n"); } return 0;}
运行效果:每隔5秒打印一句
1)main函数调用mysleep函数,后者调用sigaction注册了SIGALRM信号的处理函数sig_alarm;
2)调用alarm(nsecs)设定闹钟;
3)调用pause等待,内核切换别的进程运行;
4)nsecs秒之后,闹钟超时,内核发SIGALRM给这个进程;
5)从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数是sig_alarm;
6)切换到用户态执行sig_alarm函数,进入sig_alrm函数时SIGALRM信号被自动屏蔽,从sig_alarm函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入内核,再返回用户态继续执行进程的主控制流程(main函数调用的mysleep函数);
7)pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理动作。
需要注意的是虽然sig_alarm函数什么都没干,但还是得注册作为SIGALRM的处理函数,因为SIGALRM信号的默认处理是终止进程,这也是在mysleep函数返回时要恢复SIGALRM信号原来的sigaction的原因。
此外,mysleep函数的返回值unslept,当计时时间到了,返回0;当尚未到时间而pause函数接收到中断信号,则mysleep返回非0值,表示闹钟剩余秒数。
版本2
但是系统运行的时序(Timing)并不像我们写程序时所设想的那样。
虽然alarm(nsecs)紧接着的下一行就是pause(),但是无法保证pause()一定会在调用alarm(nsecs)之后的nsecs秒之内被调用。
若某个程序以一下时序运行:
1) 注册SIGALRM信号的处理函数。
2) 调用alarm(nsecs)设定闹钟。
3) 内核调度优先级更高的进程取代当前进程执行,并且优先级更高的进程可能有很多个,每个都要执行很长时间
4) nsecs秒钟之后闹钟超时了,内核发送SIGALRM信号给这个进程,处于未决状态。
5) 优先级更高的进程执行完了,内核要调度回这个进程执行。SIGALRM信号递达,执行处理函数sig_alarm之后再次进入内核。
6) 返回这个进程的主控制流程,alarm(nsecs)返回,调用pause()挂起等待。
7) 可是SIGALRM信号已经处理完了,只能永远的等下去了。。。
由于异步事件在任何时候都有可能发生(这里的异步事件指出现更高优 先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题而导致错误,这叫做竞态条件 (Race Condition)。
所需函数:
1)
int sigsuspend(const sigset_t *sigmask);
sigsuspend函数把“解除信号屏蔽”和“挂起等待信号”这两步合并成一个原子操作,sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题,在对时序要求严格的场合下都应该调用sigsuspend而不是pause。
和pause一样,sigsuspend没有成功返回值,只有执行了一个信号处理函数之后sigsuspend才返回,返回值为-1,errno设置为EINTR。
调用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时解除对某个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值,如果原来对该信号是屏蔽的,从sigsuspend返回后仍然是屏蔽的。
2)
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。
- 如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出;
- 如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改;
- 如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。
假设当前的信号屏蔽字为mask,下表说明了how参数的可选值:
具体实现:
unsigned int mysleep(unsigned int nsecs){ //注册SIGALRM信号的处理函数 struct sigaction act, oact; sigset_t newmask, oldmask, suspmask; unsigned int unslept = 0; act.sa_handler = sig_alarm; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGALRM, &act, &oact); sigemptyset(&newmask); sigaddset(&newmask, SIGALRM); sigprocmask(SIG_BLOCK, &newmask, &oldmask); suspmask = oldmask; sigdelset(&suspmask, SIGALRM); alarm(nsecs); sigsuspend(&suspmask); unslept = alarm(0); sigaction(SIGALRM, &oact, NULL); sigprocmask(SIG_SETMASK, &oldmask, NULL); return unslept;}
- 调用sigprocmask(SIG_BLOCK, &newmask, &oldmask);时屏蔽SIGALRM。
- 调用sigsuspend(&suspmask);时解除对SIGALRM的屏蔽,然后挂起等待。
- SIGALRM递达后suspend返回,自动恢复原来的屏蔽字,也就是再次屏蔽SIGALRM。
- 调用sigprocmask(SIG_SETMASK, &oldmask, NULL);时再次解除对SIGALRM的屏蔽。
- sleep(n)函数实现
- 模拟实现sleep函数
- sleep函数的实现
- 模拟实现sleep函数
- 实现sleep函数
- javascript模拟实现sleep函数
- Sleep 函数的一种实现
- 【Linux】模拟实现sleep函数
- 【Linux】模拟实现sleep函数
- 【linux】:模拟实现sleep函数
- 自主实现sleep函数(代码实现)
- sleep函数,sleep(5);
- 在 VBA host 中实现 Sleep() 函数
- javascript中实现Sleep函数的功能
- JavaScript实现Sleep函数的代码
- Linux中模拟实现sleep函数
- 【Linux】利用信号实现sleep函数
- Linux环境下模拟实现sleep函数
- button按钮的四种监听及实例
- HDU
- 数组最大子序列和
- Genymotion中VirtualBox无法发开虚拟设备的解决方法
- luogu1017【2000提高】进制转换(数学)
- 实现sleep函数
- Linux network I/O
- 函数对象
- 快速幂
- Qt的label显示OpenCV的Mat图像
- ubuntu pppoe自动上网设置 及导致的路由器联网问题
- 一个简单的SpringMVC需要哪些jar包[Spring4]
- 31、互斥锁与进程间通信
- 腾讯大牛教你web前后端漏洞分析与防御-点击劫持,传输安全