自定义实现mysleep
今天偶尔发现一个函数,很神奇,当我在考虑其用途时,灵光一现!突然好像明白了sleep底层的实现机制,因此我准备利用此函数来模拟实现一下自己的sleep函数!
核心函数一:pause
#include<unistd.h> int pause(void);
当该函数被调用时,进程会自动被挂起,直到有信号递达!
如果收到的信号的动作是结束进程,那么进程将会被结束.
如果该信号处理动作是忽略,那么该进程会继续被挂起.
如果该信号处理动作是捕捉的自定义方法,该函数返回-1.
乍一看,感觉这个函数屁用没有!但如果结合下面这个函数:
核心函数二:alarm
#include<unistd.h> int alarm(unsigned int seconds);
该函数可以设置一个闹钟,表示在seconds秒之后操作系统将会给该进程发送一个SIGALRM信号.
该函数返回值为0,或者剩余的秒数.
如果该函数参数为0,则表示取消原有闹钟,并返回原闹钟的剩余时间.
有了这两件"法宝",想实现自己的sleep函数简直是手到擒来,大致思考一下,写出代码如下:
mysleep版本一
#include<signal.h> #include<unistd.h> void handler(int sig){ //自定义SIGALRM信号处理 } //(不完善版)函数执行完毕后,出现SIGALRM的信号,都变成自定义操作了 int mysleep1(unsigned int scds){ signal(SIGALRM,handler); //捕捉SIGALRM信号 int ret = alarm(scds); //开始定时 pause(); //进程挂起,直到定时结束 return ret; }
看着这样的代码,运行起来好像没什么问题,但略微思考一下,就会发现:SIGALRM信号默认操作已经变成捕捉的自定义操作了,函数执行完后,SIGALRM信号没有恢复到默认操作.
因此,在捕捉信号的"武器",不得不升级一下了!
升级捕捉信号"武器"
#include<signal.h> int sigaction(int signo,const struct sigaction *act,struct sigaction *oact);
signo:信号
act:要采取的操作
oact:输出型参数,原有的操作(可为NULL)
struct sigaction{ void (*sa_handler)(int); //信号处理自定义函数 sigset_t sa_mask; //除了将捕捉信号屏蔽之外,还需要额外屏蔽的信号屏蔽字 int sa_flag; //不关心,默认为0 void (*sa_sigaction)(int,siginfo_t*,void*); //处理实时信号的函数 }
这个函数的优点在于,你修改信号捕捉函数后,它有一个输出型参数,把原有的设置备份了一份.因此,当我们程序运行结束时,可以将原有信号处理恢复.
升级mysleep函数
#include<stdio.h> #include<signal.h> #include<unistd.h> void handler(int sig){ } int mysleep2(unsigned int scds){ //捕捉信号 struct sigaction set,oset; set.sa_handler = handler; sigemptyset(&set.sa_mask); set.sa_flags = 0; sigaction(SIGALRM,&set,&oset); //计时 int ret = alarm(scds); //bug pause(); //恢复信号原有操作 sigaction(SIGALRM,&oset,NULL); return ret; }
然而,在这种情况下,该程序还是存在bug.
如果alarm函数执行完毕后,该进程被切出去,cpu去处理了优先级更高的进程,然后再切回该进程时,有可能出现计时已经结束的场景.
那么pause便会该进程一直挂起!!!!
要想解决这个问题,我们又得升级武器!
再次升级武器
#include<signal.h> int sigsuspend(const sigset_t *sigmask);
这个函数是pause函数的升级版,调用该函数时,进程的信号屏蔽字由参数sigmask指定,可以通过指定sigmask参数来解除对某个信号的屏蔽,然后挂起等待.
为了解决竞态条件的问题,我们需要再次对程序进行升级!
再次程序升级
#include<stdio.h> #include<signal.h> #include<unistd.h> void handler(int sig){ } int mysleep3(unsigned int scds){ //捕捉信号 struct sigaction act,oact; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGALRM,&act,&oact); //屏蔽信号 sigset_t sem,osem,sussem; sigemptyset(&sem); //初始化 sigaddset(&sem,SIGALRM); sigprocmask(SIG_BLOCK,&sem,&osem); //设置阻塞信号 alarm(scds); sussem = osem; sigdelset(&sussem,SIGALRM); //确保SIGALRM信号不会被阻塞 sigsuspend(&sussem); int ret = alarm(0); //恢复信号屏蔽字 sigprocmask(SIG_BLOCK,&osem,NULL); //恢复捕捉信号 sigaction(SIGALRM,&oact,NULL); return ret; }
完整测试代码
//////////////////////////////////// //文件说明:mysleep.c //作者:高小调 //创建时间:2017年06月26日 星期一 15时02分06秒 //开发环境:Kali Linux/g++ v6.3.0 //////////////////////////////////// #include<stdio.h> #include<signal.h> #include<unistd.h> void handler(int sig){ } int mysleep3(unsigned int scds){ //捕捉信号 struct sigaction act,oact; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGALRM,&act,&oact); //屏蔽信号 sigset_t sem,osem,sussem; sigemptyset(&sem); //初始化 sigaddset(&sem,SIGALRM); sigprocmask(SIG_BLOCK,&sem,&osem); //设置阻塞信号 alarm(scds); sussem = osem; sigdelset(&sussem,SIGALRM); //确保SIGALRM信号不会被阻塞 sigsuspend(&sussem); int ret = alarm(0); //恢复信号屏蔽字 sigprocmask(SIG_BLOCK,&osem,NULL); //恢复捕捉信号 sigaction(SIGALRM,&oact,NULL); return ret; } int mysleep2(unsigned int scds){ struct sigaction set,oset; set.sa_handler = handler; sigemptyset(&set.sa_mask); set.sa_flags = 0; sigaction(SIGALRM,&set,&oset); int ret = alarm(scds); //bug pause(); sigaction(SIGALRM,&oset,NULL); return ret; } int mysleep1(unsigned int scds){ signal(SIGALRM,handler); int ret = alarm(scds); pause(); return ret; } int main(){ while(1){ printf("i came to test mysleep function!\n"); mysleep3(1); } return 0; }