linux 下实现sleep详解及简单实例

linux 下实现sleep详解及简单实例

sleep:

普通版本

1、基本设计思路:

1>注册SIGALRM信号的处理函数;
   2>调用alarm(nsecs)设定闹钟;

3>调⽤pause等待,内核切换到别的进程运行;

4>nsecs秒之后,闹钟超时,内核发SIGALRM给这个进程 ;

5>从内核态返回这个进程的⽤户态之前处理未决信号,发现有SIGALRM信号,其处理函数是sig_alrm;

6> 切换到用户态执行sig_alrm函数,进⼊sig_alrm函数时SIGALRM信号被⾃动屏蔽,从sig_alrm函数返回SIGALRM信 号⾃动解除屏蔽。然后⾃动执⾏系统调用sigreturn再次进入内核,再返回用户态继续执行进程的主控制流程(main函数调⽤的mysleep函数);

7>pause函数返回-1,然后调⽤alarm(0)取消闹钟,调⽤sigaction恢复SIGALRM信号以前的处理动作。

2、实现代码

#include<stdio.h>
#include<signal.h> 

void handler(int signo)
{} 

int mysleep(int timeout)
{
  struct sigaction act,oact;
  act.sa_handler = handler;
  act.sa_flags = 0;
  sigemptyset(&act.sa_mask); 

  sigaction(SIGALRM,&act,&oact);
  alarm(timeout);
  pause();
  int ret = alarm(0);
  sigaction(SIGALRM,&oact,NULL);
  return ret;
} 

int main()
{
  while(1)
  {
    printf("using musleep!\n");
    mysleep(3);
  }
  return 0;
}

相关函数分析:

#include <unistd.h>
int pause(void);

pause函数使调⽤进程挂起直到有信号递达。如果信号的处理动作是终⽌进程,则进程终⽌,pause函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;如果信号的处理动作是捕捉,则调⽤了信号处理函数之后pause返回-1,errno设置为EINTR, 所以pause只有出错的返回值 。

sigaction函数

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct
sigaction *oact); 

sigaction函数可以读取和修改与指定信号相关联的处理动作。调⽤成功则返回0,出错则返回- 1。 signo是指定信号的编号。若act指针⾮空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。

int sigemptyset(sigset_t *set); 

函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表⽰该信号集不包含 任何有效信号。

二、优化版本

所需函数分析

#include <signal.h>

int sigsuspend(const sigset_t *sigmask); 

sigsuspend没有成功返回值,只有执⾏了⼀个信号处理函数之后sigsuspend才返回,返回值为-1,errno设置为EINTR。调⽤sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时解除对某 个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值,如果原来对该信号是屏蔽的,sigsuspend返回后仍然是屏蔽的。
sigsuspend函数与pause函数:都可以将程序挂起,但是sigsuspend函数可以实现对信号屏蔽字的解除与挂起。

sigprocmask

调⽤函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 

如果oset是⾮空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是⾮空指针,则 更改进程的信号屏蔽字,参数how指⽰如何更改。如果oset和set都是⾮空指针,则先将原来的信号 屏蔽字备份到oset⾥,然后根据set和how参数更改信号屏蔽字。

how的选项意义

如果调⽤sigprocmask解除了对当前若⼲个未决信号的阻塞,则在sigprocmask返回前,⾄少将其中⼀个信号递达。

代码实现:

#include<stdio.h>
#include<signal.h> 

void handler(int signo)
{} 

int mysleep(int timout)
{
  struct sigaction act,oact;
  sigset_t newmask,oldmask,suspmask;
  act.sa_handler = handler;
  act.sa_flags = 0;
  sigemptyset(&act.sa_mask); 

  sigaction(SIGALRM,&act,&oact);
  sigemptyset(&newmask);
  sigaddset(&newmask,SIGALRM);
  sigprocmask(SIG_BLOCK,&newmask,&oldmask); 

  alarm(timout); 

  suspmask = oldmask;
  sigdelset(&suspmask,SIGALRM);
  sigsuspend(&suspmask); 

  int unslept = alarm(0);
  sigaction(SIGALRM,&oact,NULL);
  sigprocmask(SIG_SETMASK,&oldmask,NULL);
  return(unslept);
}
int main()
{
  while(1)
  {
    printf("using musleep!\n");
    mysleep(3);
  }
  return 0;
}

优化版本解决了普通版本存在的竞态问题。我们重新审视一下普通版本的时序问题。

1、设置SIGALRM信号的处理函数;

2、调用alarm()函数设置闹钟;

3、内核选取更高优先级的进程来取代当前进程,并且这样的进程很多,同时执行时间又很长;

4、闹钟超时了,内核发送SIGALRM信号给该进程,并且处于未决状态;

5、优先级更高的进程结束后,内核要调度回这个进程执⾏。 SIGALRM信号递达,执⾏处理函 数sig_alrm之后再次进⼊内核。

6、返回这个进程的主控制流程,alarm(nsecs)返回,调⽤pause()挂起等待。

7、可是现在SIGALRM信号已经被处理,进程会导致错误。

在一个进程运行过程中,因为由于异步,所以可能被其他优先级更高的进程,由于时序问题而引发的错误问题。这样的问题称为竞态问题。

优化版本中,先将设置SIGALRM信号的处理函数,然后将SIGALRM信号进行屏蔽,然后调用alarm()函数设置闹钟,然后调用sigprocmask()函数对SIGALRM信号解除屏蔽然后挂起等待,这样就解决了竞态问题。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Linux模拟实现sleep函数

    先来说说工作原理,linux中的sleep函数能够让程序休眠一定的秒数,到时间后自动恢复运行. 实现思路 设定睡眠的秒数 睡眠(挂起) 恢复运行 实现机制 设定睡眠的秒数:采用alarm()函数设定需要睡眠的秒数,到时间后闹钟会发送SIGALRM信号给当前进程.但SIGALRM信号的默认操作是杀死进程,所以我们需要对SIGALRM信号进行自定义处理. 睡眠:pause()函数会让当前进程挂起,直到收到信号才会出错返回. 示例程序代码:模拟实现sleep使当前进程每2秒打印"hello yingy

  • linux 下实现sleep详解及简单实例

    linux 下实现sleep详解及简单实例 sleep: 普通版本 1.基本设计思路: 1>注册SIGALRM信号的处理函数:    2>调用alarm(nsecs)设定闹钟: 3>调⽤pause等待,内核切换到别的进程运行: 4>nsecs秒之后,闹钟超时,内核发SIGALRM给这个进程 ; 5>从内核态返回这个进程的⽤户态之前处理未决信号,发现有SIGALRM信号,其处理函数是sig_alrm; 6> 切换到用户态执行sig_alrm函数,进⼊sig_alrm函数时

  • linux 中open()函数详解及简单实例

    open()函数创建文件时便捷的权限设置 头文件 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> 函数原型 int open( const char * pathname, int flags); int open( const char * pathname,int flags, mode_t mode); 当flags参数包含O_REEAT时,需要对mode参数进行指定 S_IRWXU,

  • Linux中多线程详解及简单实例

    Linux中多线程详解及简单实例 1.概念 进程:运行中的程序. 线程:一个程序中的多个执行路径.更准确的定义是:线程是一个进程内部的一个控制序列. 2.为什么要有线程? 用fork调用进程代价太高,需要让一个进程同时做多件事情,线程就非常有用. 3.线程的优点和缺点. 优点: (1)有时,让程序看起来是在同时做两件事是非常有用的. 比如在编辑文档时,还能统计文档里的单词个数. (2)一个混杂着输入.计算.输出的程序,利用线程可以将这3个部 分分成3个线程来执行,从而改变程序执行的性能. (3)

  • Linux 下xargs命令详解及xargs与管道的区别

    为什么要用xargs,问题的来源 在工作中经常会接触到xargs命令,特别是在别人写的脚本里面也经常会遇到,但是却很容易与管道搞混淆,本篇会详细讲解到底什么是xargs命令,为什么要用xargs命令以及与管道的区别.为什么要用xargs呢,我们知道,linux命令可以从两个地方读取要处理的内容,一个是通过命令行参数,一个是标准输入.例如cat.grep就是这样的命令,举个例子: echo 'main' | cat test.cpp 这种情况下cat会输出test.cpp的内容,而不是'main'

  • 基于python中pygame模块的Linux下安装过程(详解)

    一.使用pip安装Python包 大多数较新的Python版本都自带pip,因此首先可检查系统是否已经安装了pip.在Python3中,pip有时被称为pip3. 1.在Linux和OS X系统中检查是否安装了pip 打开一个终端窗口,并执行如下命令: Python2.7中: zhuzhu@zhuzhu-K53SJ:~$ pip --version pip 8.1.1 from /usr/lib/python2.7/dist-packages (python 2.7) Python3.X中: z

  • C语言中getch()函数详解及简单实例

    C语言中getch()函数详解及简单实例 前言: 这个函数是一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车,有的C语言命令行程序会用到此函数做游戏,但是这个函数并非标准函数,要注意移植性! 所以有这样的一个接口,那就很牛逼了,至少可以做个游戏来玩下,结合ASCII码,很容易写个方向键控制的2048或者贪吃蛇等等有趣的游戏出来. 以下是以一个简单的例子: 你会发现当你按下对应的按键的时候就会打印相应的语句. #include <stdio.h> #include <fcnt

  • Android 中Seekbar详解及简单实例

    Android 中Seekbar详解及简单实例 做到音频播放和音乐播放时,大多数都要用到Seekbar.现在我先简单介绍下Seekbar的几个重要属性. android:max 设置值的大小 . android:thumb="@drawable/" 显示的那个可拖动图标,如果没有设置该参数则为系统默认,如果自己需要重新定义,则将自己需要的图标存放在资源目录 /res/drawable下,然后调用即可. android:thumbOffset 拖动图标的偏量值,可以让拖动图标超过bar的

  • jax-ws handler 的详解及简单实例

     jax-ws handler 的详解及简单实例 aop技术一般用于某个对象的函数调用的日志,认证等. webservice是远程的函数调用,也需要类似的aop方法,举例jax-ws的webservice,handler就相当于aop. 举一例jax-ws handler例子 先写个webservice import javax.jws.HandlerChain; import javax.jws.WebMethod; import javax.jws.WebService; @WebServi

  • JSP 注释的详解及简单实例

     JSP 注释的详解及简单实例 一 三种格式 二 举例 <body> <h1>大家好</h1> <hr> <!-- 我是HTML注释,在客户端可见 --> <%-- 我是JSP注释,在客户端不可见 --%> <% //单行注释 /*多行注释*/ out.println("大家好,欢迎大家学习JAVAEE开发."); %> <br> 你好,<%=s %><br> x+y

  • mybatis分页插件pageHelper详解及简单实例

    mybatis分页插件pageHelper详解及简单实例 工作的框架spring springmvc mybatis3 首先使用分页插件必须先引入maven依赖,在pom.xml中添加如下 <!-- 分页助手 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5

随机推荐