对Linux系统休眠的理解

发表于:2016-11-11 09:55

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:tekkamanninja    来源:51Testing软件测试网采编

#
Linux
分享:
  今天看了一个关于中断例程为什么不能休眠的文章,引发了我的思考。其实这个问题在学习驱动的时候早就应该解决了,但是由于5年前学驱动的时候属于Linux初学者,能力有限,所以对这个问题就知其然,没有能力知其所以然。现在回头看这个问题的时候,感觉应该可以有一个较为清晰的认识了。
  首先必须意识到:休眠是一种进程的特殊状态(即task->state= TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)]
  一、休眠的目的
  简单的说,休眠是为在一个当前进程等待暂时无法获得的资源或者一个event的到来时(原因),避免当前进程浪费CPU时间(目的),将自己放入进程等待队列中,同时让出CPU给别的进程(工作)。休眠就是为了更好地利用CPU。
  一旦资源可用或event到来,将由内核代码(可能是其他进程通过系统调用)唤醒某个等待队列上的部分或全部进程。从这点来说,休眠也是一种进程间的同步机制。
  二、休眠的对象
  休眠是针对进程,也就是拥有task_struct的独立个体。
  当进程执行某个系统调用的时候,暂时无法获得的某种资源或必须等待某event的到来,在这个系统调用的底层实现代码就可以通过让系统调度的手段让出CPU,让当前进程处于休眠状态。
  进程什么时候会被休眠?
  进程进入休眠状态,必然是他自己的代码中调用了某个系统调用,而这个系统调用中存在休眠代码。这个休眠代码在某种条件下会被激活,从而让改变进程状态,说到底就是以各种方式包含了:
  1、条件判断语句
  2、进程状态改变语句
  3、schedule();
  三、休眠操作做了什么
  进程被置为休眠,意味着它被标识为处于一个特殊的状态(TASK_UNINTERRUPTIBLE或 TASK_INTERRUPTIBLE),并且从调度器的运行队列中移走。这个进程将不在任何 CPU 上被调度,即不会被运行。 直到发生某些事情改变了那个状态(to TASK_WAKING)。这时处理器重新开始执行此进程,此时进程会再次检查是否需要继续休眠(资源是否真的可用?),如果不需要就做清理工作,并将自己的状态调整为TASK_RUNNING。
  四、谁来唤醒休眠进程
  进程在休眠后,就不再被调度器执行,就不可能由自己唤醒自己,也就是说进程不可能“睡觉睡到自然醒”。唤醒工作必然是由其他进程或者内核本身来完成的。唤醒需要改变进程的task_struct中的状态等,代码必然在内核中,所以唤醒必然是在系统调用的实现代码中(如你驱动中的read、write方法)以及各种形式的中断代码(包括软、硬中断)中。
  如果在系统调用代码中唤醒,则说明是由其他的某个进程来调用了这个系统调用唤醒了休眠的进程。
  如果是中断中唤醒,那么唤醒的任务可以说是内核完成了。
  如何找到需要唤醒的进程:等待队列
  上面其实已经提到了:休眠代码的一个工作就是将当前进程信息放入一个等待队列中。它其实是一个包含等待某个特定事件的所有进程相关信息的链表。一个等待队列由一个wait_queue_head_t 结构体来管理,其定义在中。
  wait_queue_head_t 类型的数据结构如下:
  struct __wait_queue_head {
  spinlock_t lock;
  struct list_head task_list;
  };
  typedef struct __wait_queue_head wait_queue_head_t;
  它包含一个自旋锁和一个链表。这是一个等待队列链表头,链表中的元素被声明做wait_queue_t。自旋锁用于包含链表操作的原子性。
  wait_queue_t包含关于睡眠进程的信息和唤醒函数。
  typedef struct __wait_queue wait_queue_t;
  typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);
  int default_wake_function(wait_queue_t *wait, unsigned mode, int flags, void *key);
  struct __wait_queue {
  unsigned int flags;
  #define WQ_FLAG_EXCLUSIVE 0x01/* 表示等待进程想要被独占地唤醒 */
  void *private;/* 指向等待进程的task_struct结构图 */
  wait_queue_func_t func;/* 用于唤醒等待进程的处理例程,在其中实现了进程状态的改变和将自己从等待队列中删除的工作 */
  struct list_head task_list;/* 双向链表结构体,用于将wait_queue_t链接到wait_queue_head_t */
  };
  他们在内存中的结构大致如下图所示:
  等待队列头wait_queue_head_t一般是定义在模块或内核代码中的全局变量,而其中链接的元素 wait_queue_t的定义被包含在了休眠宏中。
  休眠和唤醒的过程如下图所示:
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号