63、【OS】【Nuttx】任务休眠与唤醒:sleep
背景
之前的 blog 分析了 Nuttx 编码规范
62、【OS】【Nuttx】编码规范解读(十)
接下来继续分析下 Nuttx OS 的一个核心功能,任务休眠与唤醒
任务休眠
先来看任务休眠,关键函数 sleep,sleep函数是 C 标准库中的一个函数(不是 Nuttx 专用),用于使调用它的线程暂停执行一段指定的秒数,当然也有 usleep(微秒级别),sleep 函数头注释很长,这里直接看下输入输出描述
- 如果 sleep() 函数因为指定的休眠时间(参数 seconds 给出)已经结束而返回,返回值为 0,此时程序按照预期暂停了指定的秒数,没有被任何信号提前唤醒
- 如果 sleep() 函数因为在指定的睡眠时间内有信号传递给调用线程而导致提前返回,那么返回值为未休眠的时间量,即请求的休眠时间减去实际已经休眠的时间,单位秒。比如如果原本计划休眠 10 秒,但在 5 秒后由于接收到一个信号而提前醒来,那么sleep() 将返回5(表示还有5秒未睡眠)
信号传递
在调用 sleep() 函数期间如果有信号传递给调用线程,sleep() 会提前返回
- 信号是操作系统用来通知进程某些事件发生的机制,事件包括硬件异常、定时器到期、输入/输出事件等等,而且信号是异步的,可以在任何时候发生,并且会中断当前正在执行的代码流程
- 当一个信号被发送到某个进程时,如果该进程为此信号设置了回调函数,那么操作系统会在适当的时机(调度点)暂停当前执行的代码,转而执行信号处理函数,如果设置信号处理函数,默认的行为可能是忽略信号或者终止进程
- 调用 sleep() 时,请求的是让当前线程暂停执行一段时间,如果在这段时间内有任何未被屏蔽的信号发送到这个线程,并且该信号有相应的处理器或默认动作不是忽略,则会导致 sleep() 提前返回,因为信号处理具有较高的优先级,能够中断包括睡眠在内的大多数系统调用
信号传递后的行为
sleep() 函数在处理信号时,遇到信号被忽略、被阻塞或有信号处理函数的情况下,由于这些情况涉及到操作系统的信号处理机制和具体实现细节,某些行为是未定义的(unspecified),不同操作系统可能会有不同的行为,不是统一的
- 当信号被忽略或阻塞时,sleep() 是否在预定的时间返回,这个情况是不确定的(按正常逻辑推理,应该不影响返回时间,毕竟信号已经被忽略了),而且在 sleep() 返回之后这个信号是继续被挂起还是被丢弃也是不确定的,要看具体的操作系统
- 当信号没有被忽略也没有被阻塞时,除了导致 sleep() 提前返回之外,这个信号是否还要做其他动作是不确定的,要看具体的操作系统
- 有信号处理函数时,如果信号处理函数做了一些其他事情,比如修改调度时间等,那结果也是不确定的
- longjmp 那个场景这里先不分析了
函数定义
下面来看下函数定义
首先这里有两个变量 rqtp 和 rmtp
- rqtp:表示请求休眠的时间
- rmtp:表示剩余未休眠的时间
时间类型 timespec 有两个成员 tv_sec 和 tv_nsec
- tv_sec:单位秒
- tv_nsec:单位纳秒,可以看到 121 行,当 tv_nsec 超过 500ms,时,将被视为 1s,用了四舍五入的计算方法,因为 sleep 处理的时间单位是秒
核心的休眠逻辑被封装在 clock_nanosleep,这里对 clock_nanosleep 提出的要求是只有当 sleep 休眠功能被信号中断时,clock_nanosleep 才能返回失败,并且处理方法是一致的,即返回剩余未休眠时间
下一篇开始分析 clock_nanosleep