http://www.cnblogs.com/charlesblc/p/6277848.html

注意,sleep是会被信号唤醒的。

 
sleep函数:
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
此函数使调用进程被挂起,直到满足以下条件之一:
1)已经过了seconds所指定的墙上时钟时间
2)调用进程捕捉到一个信号并从信号处理程序返回
注:由于其他系统活动,实际返回时间比所要求的会迟一些,像alarm一样。
sleep的返回值:
1)在上述第一种情形中,返回值是0
2)当由于捕捉到某个信号sleep提前返回时,返回值是未睡够的时间(所要求的时间减去实际休眠时间)
 

内核对信号的处理方式

参考 http://blog.csdn.net/lina_acm/article/details/51510783

内核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。这里要补充的是,如果信号发送给一个正在睡眠的进程,那么要看该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。这一点比较重要,因为进程检查是否收到信号的时机是:一个进程在即将从内核态返回到用户态时;或者,在一个进程要进入或离开一个适当的低调度优先级睡眠状态时。

感觉,sleep函数,都是可被中断的吧。不是很确定。

内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。

处理信号有三种类型:进程接收到信号后退出;进程忽略该信号;进程收到信号后执行用户设定用系统调用signal的函数。当进程接收到一个它忽略的信号时,进程丢弃该信号,就象没有收到该信号似的继续运行。如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。

而且执行用户定义的函数的方法很巧妙,内核是在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方。这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。

在BSD系统中,内核模拟了对硬件中断的处理方法,即在处理某个中断时,阻止接收新的该类中断。

第二个要引起注意的是,如果要捕捉的信号发生于进程正在一个系统调用中时,并且该进程睡眠在可中断的优先级上,这时该信号引起进程作一次longjmp,跳出睡眠状态,返回用户态并执行信号处理例程。当从信号处理例程返回时,进程就象从系统调用返回一样,但返回了一个错误代码,指出该次系统调用曾经被中断。这要注意的是,BSD系统中内核可以自动地重新开始系统调用。

具体可以参考下一篇文章:http://www.cnblogs.com/charlesblc/p/6277921.html

第三个要注意的地方:若进程睡眠在可中断的优先级上,则当它收到一个要忽略的信号时,该进程被唤醒,但不做longjmp,一般是继续睡眠。但用户感觉不到进程曾经被唤醒,而是象没有发生过该信号一样。

第四个要注意的地方:内核对子进程终止(SIGCLD)信号的处理方法与其他信号有所区别。当进程检查出收到了一个子进程终止的信号时,缺省情况下,该进程就象没有收到该信号似的,如果父进程执行了系统调用wait,进程将从系统调用wait中醒来并返回wait调用,执行一系列wait调用的后续操作(找出僵死的子进程,释放子进程的进程表项),然后从wait中返回。SIGCLD信号的作用是唤醒一个睡眠在可被中断优先级上的进程。如果该进程捕捉了这个信号,就象普通信号处理一样转到处理例程。如果进程忽略该信号,那么系统调用wait的动作就有所不同,因为SIGCLD的作用仅仅是唤醒一个睡眠在可被中断优先级上的进程,那么执行wait调用的父进程被唤醒继续执行wait调用的后续操作,然后等待其他的子进程。

2、setjmp和longjmp的作用

前面在介绍信号处理机制时,多次提到了setjmp和longjmp,但没有仔细说明它们的作用和实现方法。这里就此作一个简单的介绍。

在介绍信号的时候,我们看到多个地方要求进程在检查收到信号后,从原来的系统调用中直接返回,而不是等到该调用完成。这种进程突然改变其上下文的情况,就是使用setjmp和longjmp的结果。setjmp将保存的上下文存入用户区,并继续在旧的上下文中执行。这就是说,进程执行一个系统调用,当因为资源或其他原因要去睡眠时,内核为进程作了一次setjmp,如果在睡眠中被信号唤醒,进程不能再进入睡眠时,内核为进程调用longjmp,该操作是内核为进程将原先setjmp调用保存在进程用户区的上下文恢复成现在的上下文,这样就使得进程可以恢复等待资源前的状态,而且内核为setjmp返回1,使得进程知道(注:知道是从longjmp调用的)。这就是它们的作用。

时间单位:  
  毫秒(ms)、微秒 (μs)、纳秒(ns)、皮秒(ps)、飞秒(fs)、阿秒、渺秒  
  1 s = 10^3 ms = 10^6 us = 10^9 ns = 10^12 ps = 10^15 fs=10^18阿秒=10^21渺秒=10^43普朗克常数

在Linux Driver开发中,经常要用到延迟函数:msleep,mdelay/udelay.

虽然msleep和mdelay都有延迟的作用,但他们是有区别的.

mdeday还忙等待函数(相当于for循环)在延迟过程中无法运行其他任务.这个延迟的时间是准确的.是需要等待多少时间就会真正等待多少时间.而msleep是休眠函数,它不涉及忙等待.你如果是msleep(10),那实际上延迟的时间,大部分时候是要多于10ms的,是个不定的时间值.

他们的差异,平时我也讲的出来,可是真正用起来的时候,就忘记了.曾在两个driver的i2c的code中,需要用到delay函数,而我用了msleep函数,一直I2C速度超慢.而我又不知道哪里出了问题,我潜意识中,认为我只delay了1ms,可是,实际上是十几毫秒.

这几个函数都是内核的延时函数:

1.

udelay(); mdelay(); ndelay();实现的原理本质上都是忙等待,ndelay和mdelay都是通过udelay衍生出来的,我们使用这些函数的实现往往会碰到编译器的警告implicit declaration of function'udelay',这往往是由于头文件的使用不当造成的。在include/asm-???/delay.h中定义了udelay(),而在include/linux/delay.h中定义了mdelay和ndelay.(这点弄错了吧,应该是ndelay最小吧)

udelay一般适用于一个比较小的delay,如果你填的数大于2000,系统会认为你这个是一个错误的delay函数,因此如果需要2ms以上的delay需要使用mdelay函数。

2.由于这些delay函数本质上都是忙等待,对于长时间的忙等待意味这无谓的耗费着cpu的资源,因此对于毫秒级的延时,内核提供了msleep,ssleep等函数,这些函数将使得调用它的进程睡眠参数指定的时间

应用层:
   #include <unistd.h>
   1、unsigned int sleep(unsigned int seconds); 秒级
   2、int usleep(useconds_t usec);              微秒级:1/10^-6
   #define _POSIX_C_SOURCE 199309
   #include <time.h>
   3、int nanosleep(const struct timespec *req, struct timespec *rem);
       struct timespec {
                  time_t tv_sec;        /* seconds */
                  long   tv_nsec;       /* nanoseconds */
              };
       // The value of the nanoseconds field must be in the range 0 to 999999999.
 
 内核层:
   include <linux/delay.h>
   1、void ndelay(unsigned long nsecs);         纳秒级:1/10^-10
   2、void udelay(unsigned long usecs);         微秒级: 1/10^-6
   3、void mdelay(unsigned long msecs);         毫秒级:1/10^-3

sleep_on(), interruptible_sleep_on(); 
sleep_on_timeout(), interruptible_sleep_on_timeout(); 
根据你的情况选用这些函数,注意: sleep操作在kernel必须小心、小心。。。 
udelay()等函数是cpu忙等,没有传统意义上的sleep。这些函数相当于我们平时的阻塞读、写之类的语义,主要用于等外设完成某些操作

------

nanosleep:

struct timespec
              {
                      time_t  tv_sec;         /* seconds */
                      long    tv_nsec;        /* nanoseconds */
              };

这个函数功能是暂停某个进程直到你规定的时间后恢复,参数req就是你要暂停的时间,其中req->tv_sec是以秒为单位,而tv_nsec以毫微秒为单位(10的-9次方秒)。由于调用nanosleep是是进程进入TASK_INTERRUPTIBLE,这种状态是会相应信号而进入TASK_RUNNING状态的,这就意味着有可能会没有等到你规定的时间就因为其它信号而唤醒,此时函数返回-1,切还剩余的时间会被记录在rem中。

看到这里刚刚看到他的实现是:将其状态设置成TASK_INTERRUPTIBLE,脱离就绪队列,然后进行一次进程调度再由内核在规定的时间后发送信号来唤醒这个进程。

在我刚开始学习编程时候,那时候我也曾试图使上下2条指令相隔一定时间来运行,那时我的做法是在这2条指令之间加上了一个400次的循环。这也算一种实现方式,我管它叫作延迟,但没有利用进程休眠来实现的好。但有一种特殊情况,使用休眠就无法实现了。

我们知道这里肯定脱离不了时钟中断,没有时钟中断的计时我们是无法实现这一功能的。那么假设时钟种中断是10毫秒一次(这种CPU还是有的),那么我们可以看到在函数调用的时候我们可以以毫微秒来暂停,如果我tv_sec = 0, tv_nsec = 2,那么时钟中断一定是在10微秒后来唤醒这个进程的,如果非实时性任务差个8微秒估计没什么大不了,不幸的是LINUX支持实时性任务SCHED_FIFO和SCHED_RR.(我们以前谈到过)。

这时8微秒的差距就是不能容忍了,这是就不能靠休眠和时钟中断来实现了,这是linux采用就是延迟办法,执行一个循环来达到暂停的目的。

这2种实现的差别就是休眠实现的话,进程会进入休眠状态,而延迟实现的话,CPU是在执行循环不会进入休眠态。所以可以说虽然名为nanosleep,但它不一定会使进程进入sleep状态,当然不进入sleep 态的条件太苛刻(没多少人会写实时任务,且还是暂停要小于CPU时钟频率,加上现在CPU的频率是如此之高,这种情况一般发生在要求外设中断不小于某个特定值,而且应该是采用比较老的CPU或者嵌入式中)。

唤醒问题:

msleep:睡眠之后不可唤醒;

msleep_interuptible:睡眠之后可唤醒;

ssleep:s延时,睡眠时候不可唤醒;

转载于:https://www.cnblogs.com/virusolf/p/6526607.html

sleep与信号唤醒的问题 内核对信号的处理方式 udelay相关推荐

  1. 内核实现信号捕捉原理

    信号捕捉特性 进程正常运行时,默认PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信号.当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数.而该函数有可能执行很长时间,在这期间所屏蔽 ...

  2. linux 切换用户_Linux 用户态切换到内核态的 3 种方式

    系统调用 这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如 fork() 实际上就是执行了一个创建新进程的系统调用.而系统调用的机制其核 ...

  3. 2信号处理之:信号产生原因,进程处理信号行为,信号集处理函数,PCB的信号集,sigprocmask()和sigpending(),信号捕捉设定,sigaction,C标准库信号处理函数,可重入函数,

     1信号产生原因 2.进程处理信号行为 manpage里信号3中处理方式: SIG_IGN SIG_DFL                                            默 ...

  4. 进程信号(信号、信号的注册与注销、信号的处理方式)

    进程信号 信号的概念 信号的产生 信号的注册 非可靠信号的注册 可靠信号的注册 信号的注销 非可靠信号的注销 可靠信号的注销 信号的处理方式 默认处理方式 忽略处理方式 自定义处理方式 信号的捕捉流程 ...

  5. 信号完整性研究系列--什么是信号完整性

    来源:于争博士<信号完整性研究>http://www.sig007.com 如果你发现,以前低速时代积累的设计经验现在似乎都不灵了,同样的设计,以前没问题,可是现在却无法工作,那么恭喜你, ...

  6. Linux信号编程实践(二) 信号发送函数和可重入函数

    在早期的UNIX中信号是不可靠的,不可靠在这里指的是:信号可能丢失,一个信号发生了,但进程却可能一直不知道这一点. 现在Linux 在SIGRTMIN实时信号之前的都叫不可靠信号,这里的不可靠主要是不 ...

  7. 【Linux】进程信号“疑问?坤叫算信号吗?“

    鸡叫当然也算信号啦~ 文章目录 前言 一.认识信号量 二.信号的产生 1.调用系统函数向进程发信号 2.由软件条件产生信号 3.硬件异常产生信号 总结 前言 信号在我们生活中很常见,下面我们举一举生活 ...

  8. linux中的信号是什么意思,linux中信号的基本概念

    1.信号的基本概念: 信号全称为软中断信号,也有人称作软中断,信号机制是进程之间相互传递消息的一种方法. 2.信号的作用: 进程之间可以互相通过系统调用kill发送软中断信号,通知进程发生了某个事件. ...

  9. v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码

    Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https ...

  10. Unix信号详解(Signal的信号说明)

    signal信号机制是属于计算机异常处理机制中的一种. signal信号属于一种异步处理异常的机制之一. 类似于我们平常在命令行上对于死循环的程序,按下ctrl-z暂时挂起,ctrl-c程序终止,这些 ...

最新文章

  1. Silverlight实用窍门系列:52.Silverlight中的MVVM框架极速入门(以MVVM Light Toolkit为例)...
  2. 如何区分山寨版网管软件
  3. 算法导论学习笔记 第6章 堆排序
  4. 你必须了解Spring的生态
  5. 二叉树的创建_大多数人都不会手写创建并遍历二叉树,一航这里帮你终结了
  6. 三分钟掌握Go mod常用与高级操作
  7. 关于meta http-equiv=Content-Type content=text/html:charset=UTF-8
  8. HTML 转 PDF的两种实现方式
  9. TSPL学习笔记(2):过程和变量绑定
  10. 全网首发:明明已安装也存在,报错找不到lualib.h
  11. Knoll Light Factory 3.2 for Mac 完整汉化版 灯光工厂 for Mac中文版 支持PSCC2019
  12. ANSYS之翼型NACA4412流场计算系列:Profili、ICEM CFD、Fluent
  13. hive —— 分区表
  14. 第三方支付API支付宝支付申请流程 支付宝新老版本
  15. 一键导出ChatGPT聊天记录:让备份更简单
  16. 互联网快讯:诺基亚与微软拓展合作;极米Z6X Pro、极米H3S成爆款;美的宣布收购武汉天腾动力
  17. c语言中八进制和十六进制
  18. 解决linux系统下U盘变成只读,无法新建文件夹
  19. Caj文件怎么转换成pdf?Caj转pdf在线转换器推荐
  20. spring框架学习 - 使用 Spring 的面向切面编程 补充1

热门文章

  1. Google深度学习TensorFlow最好的入门文章!
  2. 微信红包订单存储架构变迁的最佳实践
  3. 打造世界领先的智能运维大脑,必示科技获顺为资本领投数千万A轮融资
  4. 互联网基础运维分工、职责和技能要求
  5. 从技术的角度看区块链
  6. java 并发编程实战代码_「Java并发编程实战」对象的组合
  7. 2018/4/7 Mybatis源码结构概览
  8. ArrayList源码解析
  9. iOS端im实时音视频功能快速开发实操指导!
  10. Jquery的parent和parents(找到某一特定的祖先元素)