模拟实现sleep函数

先来介绍几个函数:

1,sleep
这里写图片描述
若进程/线程挂起到参数所指定的时间则返回0,若有信号中断则返回剩余秒数。

2,pause
这里写图片描述

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

3,alarm
这里写图片描述
alarm也称为闹钟函数,它可以在进程中设置一个定时器,当定时器指定的时间到时,它向进程发送SIGALRM信号。可以设置忽略或者不捕获此信号,如果采用默认方式其动作是终止调用该alarm函数的进程。
要注意的是,一个进程只能有一个闹钟时间,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。需要注意的是,经过指定的秒数后,信号由内核产生,由于进程调度的延迟,所以进程得到控制从而能够处理该信号还需要一些时间。
如果有以前为进程登记的尚未超时的闹钟时钟,而且本次调用的seconds值是0,则取消以前的闹钟时钟,其余留值仍作为alarm函数的返回值。

通过上面函数的介绍,我们可以用alarm函数和pause函数来模拟实现一个sleep函数的功能。

初步的设计流程如下:
这里写图片描述

这里写图片描述

但这个流程图存在致命的错误。

例如在调用alarm之后,进程切换到内核态去处理别的,有时间差,但操作系统已经发送了SIGALRM,所以当程序返回用户态执行pause后就永远挂起。这种情况就出现了竞态条件,导致程序运行和预期不同。

改进之后的流程图如下:

这里写图片描述

这样的改进似乎解决了刚才提出的问题,但程序仍然存在问题。在取消信号屏蔽字之后,可能去调用其他的信号处理函数,这样导致pause就永远挂起。

所以我们需要一种原子操作,能够取消信号屏蔽字的同时调用pause挂起进程。
sigsuspend函数可以帮助我们达到这样的效果。
这里写图片描述

这里写图片描述

sigsuspend用于在接收到某个信号之前,临时用mask替换进程的信号掩码,并暂停进程执行,直到收到信号为止。
进程执行到sigsuspend时,sigsuspend并不会立刻返回,进程处于TASK_INTERRUPTIBLE状态并立刻放弃CPU,等待UNBLOCK(mask之外的)信号的唤醒。进程在接收到UNBLOCK(mask之外)信号后,调用处理函数,然后把现在的信号集还原为原来的,sigsuspend返回,进程恢复执行。

这里写图片描述

可重入函数:
如果一个函数正在执行过程中,另外一个执行流也调用了这同一个函数,如果这个过程中不会出现逻辑上的问题,就称为可重入,否则称为不可重入。

如果一个函数符合下列条件之一则是不可重入的:
(1)使用了全局变量/静态变量,但是未使用同步互斥机制
(2)调用了malloc或free,因为malloc也是全局链表来管理堆的
(3)调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局变量结构
(4)调用了其他不可重入的函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值