LINUX下实现sleep

LINUX下实现sleep2>调用alarm设定闹钟;。3>调⽤pause等待,内核切换到别的进程运行;。5>从内核态返回这个进

欢迎大家来到IT世界,在知识的湖畔探索吧!

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、实现代码

[cpp] view plain copy

  1. #include<signal.h>

  2. void handler(int signo)

  3. {}

  4. int mysleep(int timeout)

  5. {

  6. struct sigaction act,oact;

  7. act.sa_handler = handler;

  8. act.sa_flags = 0;

  9. sigemptyset(&act.sa_mask);

  10. sigaction(SIGALRM,&act,&oact);

  11. alarm(timeout);

  12. pause();

  13. int ret = alarm(0);

  14. sigaction(SIGALRM,&oact,NULL);

  15. return ret;

  16. }

  17. int main()

  18. {

  19. while(1)

  20. {

  21. printf(“using musleep!\n”);

  22. mysleep(3);

  23. }

  24. return 0;

  25. }

相关函数分析:

[cpp] view plain copy

  1. int pause(void);

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

sigaction函数

[cpp] view plain copy#include <signal.h>

  1. int sigaction(int signo, conststruct sigaction *act, struct

  2. sigaction *oact);

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

[cpp] view plain copy

  1. int sigemptyset(sigset_t *set);

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

二、优化版本

所需函数分析

[cpp] view plain copy

  1. int sigsuspend(const sigset_t *sigmask);

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

sigsuspend函数与pause函数:都可以将程序挂起,但是sigsuspend函数可以实现对信号屏蔽字的解除与挂起。

sigprocmask

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

[cpp] view plain copy

  1. #include <signal.h>

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

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

how的选项意义

LINUX下实现sleep

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

代码实现:

[cpp] view plain copy

  1. #include<stdio.h>

  2. #include<signal.h>

  3. void handler(int signo)

  4. {}

  5. int mysleep(int timout)

  6. {

  7. struct sigaction act,oact;

  8. sigset_t newmask,oldmask,suspmask;

  9. act.sa_handler = handler;

  10. act.sa_flags = 0;

  11. sigemptyset(&act.sa_mask);

  12. sigaction(SIGALRM,&act,&oact);

  13. sigemptyset(&newmask);

  14. sigaddset(&newmask,SIGALRM);

  15. sigprocmask(SIG_BLOCK,&newmask,&oldmask);

  16. alarm(timout);

  17. suspmask = oldmask;

  18. sigdelset(&suspmask,SIGALRM);

  19. sigsuspend(&suspmask);

  20. int unslept = alarm(0);

  21. sigaction(SIGALRM,&oact,NULL);

  22. sigprocmask(SIG_SETMASK,&oldmask,NULL);

  23. return(unslept);

  24. }

  25. int main()

  26. {

  27. while(1)

  28. {

  29. printf(“using musleep!\n”);

  30. mysleep(3);

  31. }

  32. return 0;

  33. }

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

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

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

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

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

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

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

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

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

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

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/40668.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信