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

 更新时间:2017年06月08日 09:37:05   作者:BabysBreath_hl  
这篇文章主要介绍了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信号解除屏蔽然后挂起等待,这样就解决了竞态问题。

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

您可能感兴趣的文章:

相关文章

  • shell脚本读取命令行参数的实现

    shell脚本读取命令行参数的实现

    本文主要介绍了shell脚本读取命令行参数的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • linux基础之Shell Script入门介绍

    linux基础之Shell Script入门介绍

    本文介绍下,学习shell script编程的入门知识,通过几个入门实例,带领大家走进shell script的神圣殿堂,呵呵,有需要的朋友参考下
    2013-11-11
  • 一文详解Linux du命令的使用方法

    一文详解Linux du命令的使用方法

    在Linux系统中,du命令是一款功能强大且实用的工具,可用于查看文件和目录的磁盘使用情况,本文将深入探讨du命令的使用方法和一些常见选项,以协助您更全面地理解和有效地管理系统中的存储空间,需要的朋友可以参考下
    2024-02-02
  • shell中循环调用hive sql 脚本的方法

    shell中循环调用hive sql 脚本的方法

    今天小编就为大家分享一篇shell中循环调用hive sql 脚本的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06
  • shell之定时周期性执行脚本的方法示例

    shell之定时周期性执行脚本的方法示例

    这篇文章主要介绍了shell之定时周期性执行脚本的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • linux目录管理方法介绍

    linux目录管理方法介绍

    这篇文章介绍了linux目录管理的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • Shell脚本实现memcache缓存命中率监控

    Shell脚本实现memcache缓存命中率监控

    这篇文章主要介绍了Shell脚本实现memcache缓存命中率监控,这个脚本比较简单,通过check_tcp脚本获取memcached的stats信息,然后将该信息格式化成一个字符串,然后将get_hits比上cmd_gets就能得到缓存命中率,需要的朋友可以参考下
    2014-12-12
  • awk 九九乘法表 shell实现代码

    awk 九九乘法表 shell实现代码

    这篇文章主要介绍了awk 九九乘法表 shell实现代码,需要的朋友可以参考下
    2016-03-03
  • Linux下find 命令的 7 种用法

    Linux下find 命令的 7 种用法

    Linux 下 find 命令在目录结构中搜索文件,并执行指定的操作,Linux 下 find 命令提供了相当多的查找条件,功能很强大,这篇文章主要介绍了find 命令的 7 种用法,需要的朋友可以参考下
    2022-12-12
  • shell脚本中执行python脚本并接收其返回值的例子

    shell脚本中执行python脚本并接收其返回值的例子

    这篇文章主要介绍了shell脚本中执行python脚本并接收其返回值的例子,本文重点在于如何接收python脚本的返回值,需要的朋友可以参考下
    2014-08-08

最新评论