【进程管理】系统调用nanosleep()和pause()u

前面所讲的sched_yield()只是让内核有一次调度,而当前进程继续保持可运行状态;而是用nanosleep()和pause()是让当前进程睡眠,使它的进程状态变为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE,并且从可执行队列中移除,调度结果一定是其他进程可以运行;进程一旦进入睡眠状态,就需要经过唤醒才能将状态恢复成TASK_RUNNING,并可以回到可执行队列中;


(1)CPU主调放弃有两种,一种是真正的目的没有达到,比如read()或send()等,几乎所有与外设有关的系统调用都可能在执行过程中受阻而进入睡眠,让出CPU;一种就是真正目的就是进入睡眠,如nanosleep()和pause();

(2)nanosleep()是将当前进程进入睡眠,但是在指定的时间以后由内核将该进程唤醒,通常是实现周期性的应用,如库函数sleep()就是通过它来实现的;

(3)pause()也是将当前进程进入睡眠,可是与时间无关,要接收到一个信号时才会被唤醒,所以常常用来协调若干进程的运行;它要在接收到特定信号SIGCHLD并且满足若干特殊条件时才会被唤醒;

(4)在sys_nanosleep()中,rqtp指向指定睡眠时间的数据结构,rmtp指向返回剩余睡眠时间的数据结构(睡眠中的进程有可能因接收到信号而提前被唤醒,这时候返回-1,并rmtp所指向数据结构中返回剩余的时间,然后进程可以决定是是否睡眠把剩余时间消耗掉),如果要求的睡眠时间小于某个1/HZ的1/5,而且它还是实时进程的话,这样为了保持实时性,就不能真正的让其睡眠,而是让它(udelay)延迟;在udelay()中是通过计数循环达到延迟的,这种延迟通常是在某些外设要求连续两次操作之间的间隔不得小于某个特定值;

(5)在sys_nanosleep()中,对于正常的睡眠要求,先调用timespec_to_jiffies(),将数据结构t中的数值转换成时钟中断的次数;然后将当前进程状态改为TASK_INTERRUPT,并调用schedule_timeout()进入睡眠;然后执行schedule_timeout();

(6)在schedule_timeout()中,若时间是MAX_SCHEDULE_TIMEOUT,就是无限期睡眠,内核并不负责将它唤醒,而是睡眠到另一个进程向其发送一个信号时才会被唤醒;若是不MAX_SCHEDULE_TIMEOUT,由内核负责唤醒该进程了,为此,内核为此设置了一个定时器timer,并将其挂入到一个定时器队列中,而每次时钟中断要检查这些定时器是否到点;设置好timer的调用函数process_timeout以及超时时间等;调用add_timer();

(7)在add_timer()中,要进行关中断保护核心队列操作即timerlist_lock,在internal_add_timer()中,timer_jiffies表示当前定时器队列的处理时间已经推迟到哪个点了,可能不同与jiffies;

(8)在linux中设置了五个定时器队列数组,而不是一个杂凑表),timer_vec(由index和list_head vec[]构成)tv1,tv2......tv5;这些tv中都包含了一个timer_list指针数组,这就是所谓的杂凑表,指针指向一个定时器队列;tv1的数组大小为2^8,其它是2^6,这样队列的数量总共有512;每一个数组都与一个变量index相联系,用来指示下一个时钟中断发生时要处理的队列;将32为到点的时间分成五段,最低8位与tv1对应,其他的4段则是与6位相对应的;要将一个定时器挂入到队列去时,先根据到点时间和当前时间计算出这个定时器应该在多少次时钟中断以后到点;如果这个时间小于256,就去最低8位作为其杂凑值,然后使用这个杂凑值作为下标在tv1数组中找到相应的队列,并将此定时器挂入到这个队列中,由于tv1中有256个队列,每一个队列中都有相同的到点时间;当数值大于等于256时,就看数值是否小于2^14了,如果是就取到点时间的第二段6位,作为杂凑值(下标),插入到tv2的某个队列;显然tv2中队列与tv1不同,tv2可能有分属于256个不同到点时间的定时器;

(9)在internal_add_timer()中,就是根据(8)的思想将定时器链入到vec所指定的队列的尾部中去的,对于tv1来说,每一个队列都有相同的到点时间,所以插入位置就无所谓了,而对于其他队列其实也没关系,因此插入位置很简单,与队列长度无关;当时钟中断发生时,从而将jiffies向前推进一步,只要在tv1将对应队列中所有定时器处理一遍,并释放,然后将index的指示也向前推进一步;当tv1.index到256时,再次将它设为0,开始另外一轮的256次时钟中断,就从tv2根据tv2.index将tv2的一个队列搬运到tv1宏;搬运过程中对每一个定时器还要调用internal_add_timer(),这个队列将会被分散到tv1队列中了;因此tv5的队列要经过5步才能进入tv1中,显然这个办法比线性搜索要好;

(10)将定时器链入队列后,schedule_timeout()就会调用schedule(),使当前进程真正的进入到睡眠,等待唤醒;在时钟中断返回之前要执行与时钟有关的bh函数timer_bh(),而timer_bh()要调用run_timer_list函数;在run_timer_list()中,当tv1.index不为0时,就按次序将定时器通过detach_timer()从队列中摘除下来,然后执行指定的函数,执行完之后,就将tv1.index取模和times_jiffies加一个计数;当tv1.index变为0时,就要通过cascade_timers()来搬运了;cascade_timers()中,会有循环搬运的情况,即tv2到tv5的index都为1的时候

(11)在process_timeout()中,唤醒睡眠的进程;然后又到了schedule_timeout(),然后调用了del_timer_sync要安全性的删除这个定时器,因为run_timer_list()并不是唯一可以del_timer()的,通过向睡眠中的进程发送一个信号,同样可以将其唤醒;timer是在堆栈空间中,并不需要我们来分配和释放(编译器来处理),但一定要保证其删除了,否则再定时器队列很危险;

(12)如果不被其他进程信号唤醒,它是可能睡过头的(jiffies不是单步的和唤醒进程也不能保证进程就开始运行了),其他进程信号唤醒时,返回的是尚未睡够的剩余时间

(13)在sys_pause()中,直接改变运行状态为TASK_INTERRUPTIBLE,然后执行调度schedule();只有接受到信号时才会被唤醒;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值