Chinaunix首页 | 论坛 | 博客
  • 博客访问: 452039
  • 博文数量: 72
  • 博客积分: 3186
  • 博客等级: 中校
  • 技术积分: 1039
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-07 16:53
文章分类

全部博文(72)

文章存档

2012年(1)

2011年(5)

2010年(10)

2009年(56)

我的朋友

分类: LINUX

2012-02-21 23:34:39

sleepy

ldd3中有一个例子 sleepy, 可以直接编译它并在本机上运行测试。

核心代码如下

  1. ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
  2. {
  3.     printk(KERN_DEBUG "process %i (%s) going to sleep\n",
  4.             current->pid, current->comm);
  5.     wait_event_interruptible(wq, flag != 0);
  6.     flag = 0;
  7.     printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
  8.     return 0; /* EOF */
  9. }

  10. ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,
  11.         loff_t *pos)
  12. {
  13.     printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
  14.             current->pid, current->comm);
  15.     flag = 1;
  16.     wake_up_interruptible(&wq);
  17.     return count; /* succeed, to avoid retrial */
  18. }

代码很简单,当read这个设备时,进程会在wait_event_interruptible()中检查 (fail != 0),如果不成功则进程进入休眠,

另一个进程write这个设备时,会唤醒之前休眠的read进程。

测试,打开两个终端A和B 在A中:

  1. sudo insmod ./sleepy.ko
  2. sudo mknod /dev/sleepy c 251
  3. cat /dev/sleepy
  4. 此时终端不动了,cat进程进入了休眠
在B中以root身份执行
  1. ls > /dev/sleepy

可以发现a中的cat进程成功退出了。 查看 dmesg | tail 得到:

  1. [15625.488701] process 3283 (cat) going to sleep
  2. [15631.216986] process 3284 (ls) awakening the readers...
  3. [15631.216993] awoken 3283 (cat)

wait_event_interruptible(wq, flag != 0);

展开宏后得到如下代码

  1. {                            
  2.     int __ret = 0;                            
  3.     if (!(condition)) {                        
  4.         wait_queue_t __wait = {                        
  5.             .private    = current,                
  6.             .func        = autoremove_wake_function,        
  7.             .task_list    = LIST_HEAD_INIT((__wait).task_list),    
  8.         }

  9.         for (;;) {                            
  10.             prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);    
  11.             if (condition)                        
  12.                 break;                        
  13.             if (!signal_pending(current)) {                
  14.                 schedule();                    
  15.                 continue;                    
  16.             }
  17.             ret = -ERESTARTSYS;
  18.             break;
  19.         }                                
  20.         finish_wait(&wq, &__wait);                    
  21.     }
  22.     __ret;                                
  23. }

主要做了几件事: 

1. wait中记录了当前进程(current)和wakeup时要执行的函数autoremove_wake_function
2. prepare_to_wait, 将wait加入到wq的链表中, 并将当前进程的state设置为TASK_INTERRUPTIBLE 
3, schedule() 之后,当前进程失去CPU


wake_up_interruptible(&wq);

展开得到  __wake_up(&wq, TASK_INTERRUPTIBLE, 1, NULL)

调用过程为 

__wake_up(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, void *key)

     -> __wake_up_common(q, mode, nr_exclusive, 0, key);

  1. static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
  2.             int nr_exclusive, int wake_flags, void *key)
  3. {
  4.     wait_queue_t *curr, *next;

  5.     list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
  6.         unsigned flags = curr->flags;

  7.         if (curr->func(curr, mode, wake_flags, key) &&
  8.                 (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
  9.             break;
  10.     }
  11. }

对 wq.task_list 上的所有wait执行 wait->func函数,func是前面初始化__wait时添加的 autoremove_wake_function 在wait->private中记录了等待进程的id, autoremove_wake_function 中拿到该进程, 对该进程做了以下几件事:

  1. autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
  2.   -> default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags, void *key)
  3.      -> try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
  4.         -> ttwu_remote(struct task_struct *p, int wake_flags)
  5.            -> ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int wake_flags)
  6.                 p->state = TASK_RUNNING;

1. 确认该进程的状态state是TASK_INTERRUPTIBLE

  (这里检查一次,所以wake_up()唤醒队列上所有的进程,而wake_up_interruptible()只唤醒队列上的"状态为TASK_INTERRUPTIBLE"的进程)

2. 将进程的状态置为TASK_RUNNING

3. 将wait从wq->task_list链表中删除

重新调度,重新检测

之前休眠的进程状态现在变成TASK_RUNNING, 待调度器重新调度之后,该进程就可以重新 运行。 紧接着运行在上面的schedule()之后

  1.        for (;;) {                            
  2.             prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);    
  3.             if (condition)                        
  4.                 break;                        
  5.             if (!signal_pending(current)) {                
  6.                 schedule();                    
  7.                 continue;                    
  8.             }
  9.             ret = -ERESTARTSYS;
  10.             break;
  11.         }                                
  12.         finish_wait(&wq, &__wait);

continue之后又是prepare_to_wait, 进程又开始准备等待。但如果紧接着看到condition 为真的话,则跳出结束等待finish_wait()

finish_wait();

  1. void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
  2. {
  3.     unsigned long flags;

  4.     __set_current_state(TASK_RUNNING);
  5.     if (!list_empty_careful(&wait->task_list)) {
  6.         spin_lock_irqsave(&q->lock, flags);
  7.         list_del_init(&wait->task_list);
  8.         spin_unlock_irqrestore(&q->lock, flags);
  9.     }
  10. }

finish_wait 主要做了两件事: 

1. 将进程状态置为TASK_RUNNING 

2. 将wait从wq->task_list链表中删除

阅读(2738) | 评论(0) | 转发(0) |
0

上一篇:zImage解压(arch/arm/boot/compressed/head.S)

下一篇:没有了

给主人留下些什么吧!~~