linux里 常提到的 程序(进程)的睡眠 是什么意思?_第1页
已阅读5页,还剩2页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、linux里 常提到的 程序(进程)的睡眠 是什么意思?进程的睡眠和唤醒1 linux进程的睡眠和唤醒在linux中,仅等待cpu时光的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状态标记位为task_running。一旦一个运行中的进程时光片用尽, linux内核的调度器会剥夺这个进程对cpu的控制权,并且从运行队列中挑选一个合适的进程投入运行。固然,一个进程也可以主动释放cpu的控制权。函数schedule()是一个调度函数,它可以被一个进程主动调用,从而调度其它进程占用cpu。一旦这个主动放弃cpu的进程被重新调度占用cpu,那么它将从上次停止执行的位置开头执行,也就是说

2、它将从调用schedule()的下一行代码处开头执行。有时候,进程需要等待直到某个特定的大事发生,例如设备初始化完成、i/o 操作完成或定时器到时等。在这种状况下,进程则必需从运行队列移出,加入到一个等待队列中,这个时候进程就进入了睡眠状态。linux 中的进程睡眠状态有两种:一种是可中断的睡眠状态,其状态标记位task_interruptible;另一种是不行中断的睡眠状态,其状态标记位为task_uninterruptible。可中断的睡眠状态的进程会睡眠直到某个条件变为真,比如说产生一个硬件中断、释放进程正在等待的系统资源或是传递一个信号都可以是唤醒进程的条件。不行中断睡眠状态与可中断睡

3、眠状态类似,但是它有一个例外,那就是把信号传递到这种睡眠状态的进程不能转变它的状态,也就是说它不响应信号的唤醒。不行中断睡眠状态普通较少用到,但在一些特定状况下这种状态还是很实用的,比如说:进程必需等待,不能被中断,直到某个特定的大事发生。在现代的linux操作系统中,进程普通都是用调用schedule()的办法进入睡眠状态的,下面的代码演示了如何让正在运行的进程进入睡眠状态。sleeping_task = current;set_current_state(task_interruptible);schedule();func1();/* rest of the code . */在第一个语

4、句中,程序存储了一份进程结构指针sleeping_task,current 是一个宏,它指向正在执行的进程结构。set_current_state()将该进程的状态从执行状态task_running 变成睡眠状态task_interruptible。假如schedule()是被一个状态为task_running的进程调度,那么schedule()将调度另外一个进程占用cpu;假如schedule()是被一个状态为task_interruptible或task_uninterruptible的进程调度,那么还有一个附加的步骤将被执行:当前执行的进程在另外一个进程被调度之前会被从运行队列中移出,这

5、将导致正在运行的那个进程进入睡眠,由于它已经不在运行队列中了。我们可以用法下面的这个函数将刚才那个进入睡眠的进程唤醒。wake_up_process(sleeping_task);在调用了wake_up_process()以后,这个睡眠进程的状态会被设置为task_running,而且调度器会把它加入到运行队列中去。固然,这个进程惟独在下次被调度器调度到的时候才干真正地投入运行。2 无效唤醒几乎在全部的状况下,进程都会在检查了某些条件之后,发觉条件不满足才进入睡眠。可是有的时候进程却会在判定条件为真后开头睡眠,假如这样的话进程就会无限期地休眠下去,这就是所谓的无效唤醒问题。在操作系统中,当多个

6、进程都企图对分享数据举行某种处理,而最后的结果又取决于进程运行的挨次时,就会发生竞争条件,这是操作系统中一个典型的问题,无效唤醒恰恰就是因为竞争条件导致的。设想有两个进程a 和b,a 进程正在处理一个链表,它需要检查这个链表是否为空,假如不空就对链表里面的数据举行一些操作,同时b进程也在往这个链表添加节点。当这个链表是空的时候,因为很多据可操作,这时a进程就进入睡眠,当b进程向链表里面添加了节点之后它就唤醒a 进程,其代码如下:a进程:1 spin_lock(&list_lock);2 if(list_empty(&list_head) 3 spin_unlock(&l

7、ist_lock);4 set_current_state(task_interruptible);5 schedule();6 spin_lock(&list_lock);7 89 /* rest of the code . */10 spin_unlock(&list_lock);b进程:100 spin_lock(&list_lock);101 list_add_tail(&list_head, new_node);102 spin_unlock(&list_lock);103 wake_up_process(processa_task);这里会浮

8、现一个问题,如果当a进程执行到第3行后第4行前的时候,b进程被另外一个处理器调度投入运行。在这个时光片内,b进程执行完了它全部的命令,因此它试图唤醒a进程,而此时的a进程还没有进入睡眠,所以唤醒操作无效。在这之后,a进程继续执行,它会错误地认为这个时候链表仍然是空的,于是将自己的状态设置为task_interruptible然后调用schedule()进入睡眠。因为错过了b进程唤醒,它将会无限期的睡眠下去,这就是无效唤醒问题,由于即使链表中有数据需要处理,a 进程也还是睡眠了。3 避开无效唤醒如何避开无效唤醒问题呢?我们发觉无效唤醒主要发生在检查条件之后和进程状态被设置为睡眠状态之前,原来b进

9、程的wake_up_process()提供了一次将a进程状态置为task_running的机会,惋惜这个时候a进程的状态仍然是task_running,所以wake_up_process()将a进程状态从睡眠状态改变为运行状态的努力没有起到预期的作用。要解决这个问题,必需用法一种保障机制使得推断链表为空和设置进程状态为睡眠状态成为一个不行分割的步骤才行,也就是必需消退竞争条件产生的根源,这样在这之后浮现的wake_up_process ()就可以起到唤醒状态是睡眠状态的进程的作用了。找到了缘由后,重新设计一下a进程的代码结构,就可以避开上面例子中的无效唤醒问题了。a进程:1 set_curre

10、nt_state(task_interruptible);2 spin_lock(&list_lock);3 if(list_empty(&list_head) 4 spin_unlock(&list_lock);5 schedule();6 spin_lock(&list_lock);7 8 set_current_state(task_running);910 /* rest of the code . */11 spin_unlock(&list_lock);可以看到,这段代码在测试条件之前就将当前执行进程状态转设置成task_interrupti

11、ble了,并且在链表不为空的状况下又将自己置为task_running状态。这样一来假如b进程在a进程进程检查了链表为空以后调用wake_up_process(),那么a进程的状态就会自动由本来task_interruptible变成task_running,此后即使进程又调用了schedule(),因为它现在的状态是task_running,所以仍然不会被从运行队列中移出,因而不会错误的进入睡眠,固然也就避开了无效唤醒问题。4 linux内核的例子在linux操作系统中,内核的稳定性至关重要,为了避开在linux操作系统内核中浮现无效唤醒问题,linux内核在需要进程睡眠的时候应当用法类似如

12、下的操作:/* q是我们希翼睡眠的等待队列 */declare_waitqueue(wait,current);add_wait_queue(q,set_current_state(task_interruptible);/* 或task_interruptible */while(!condition) /* condition 是等待的条件*/schedule();set_current_state(task_running);remove_wait_queue(q,上面的操作,使得进程通过下面的一系列步骤平安地将自己加入到一个等待队列中举行睡眠:首先调用declare_waitqueue

13、()创建一个等待队列的项,然后调用add_wait_queue()把自己加入到等待队列中,并且将进程的状态设置为task_interruptible或者task_interruptible。然后循环检查条件是否为真:假如是的话就没有须要睡眠,假如条件不为真,就调用schedule()。当进程检查的条件满足后,进程又将自己设置为task_running 并调用remove_wait_queue()将自己移出等待队列。从上面可以看到,linux的内核代码维护者也是在进程检查条件之前就设置进程的状态为睡眠状态,然后才循环检查条件。假如在进程开头睡眠之前条件就已经达成了,那么循环会退出并用set_cu

14、rrent_state()将自己的状态设置为就绪,这样同样保证了进程不会存在错误的进入睡眠的倾向,固然也就不会导致浮现无效唤醒问题。下面让我们用linux 内核中的实例来看看linux 内核是如何避开无效睡眠的,这段代码出自linux2.6的内核(linux-2.6.11/kernel/sched.c: 4254):4253 /* wait for kthread_stop */4254 set_current_state(task_interruptible);4255 while (!kthread_should_stop() 4256 schedule();4257 set_curren

15、t_state(task_interruptible);4258 4259 _set_current_state(task_running);4260 return 0;上面的这些代码属于迁移服务线程migration_thread,这个线程不断地检查kthread_should_stop(),直到kthread_should_stop()返回1它才可以退出循环,也就是说只要kthread_should_stop()返回0该进程就会向来睡眠。从代码中我们可以看出,检查kthread_should_stop()的确是在进程的状态被置为task_interruptible后才开头执行的。因此,假如在条件检查之后

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论