自定义实现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;
}

 

posted @ 2017-06-26 16:53  Linux专题站  阅读(318)  评论(0编辑  收藏  举报