码迷,mamicode.com
首页 > 其他好文 > 详细

阻塞睡眠实现机制

时间:2015-08-02 23:28:01      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:linux kernel   阻塞   睡眠   

  在看阻塞睡眠实现机制前,我们来看一下内核中广泛用到的等待队列。
  Linux内核的等待队列为双循环链表结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。它有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。等待队列头和等待队列项中都包含一个list_head(双链表)。通过这样一个双链表把等待进程链接起来。
  
  下面来看两者数据结构:

struct __wait_queue_head {
    spinlock_t lock; //自旋锁,实现对等待队列的互斥访问
    struct list_head task_list; //双向循环链表,存放等待的进程。
};
typedef struct __wait_queue_head wait_queue_head_t;

struct __wait_queue {
    unsigned int flags;
    void *private;
    wait_queue_func_t func;
    struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;

   技术分享

  我们知道缺省状态下IO是阻塞的(除非设定O_NONBLOC),如果一个进程调用 read 但是没有数据可用(尚未), 这个进程必须阻塞. 这个进程在有数据达到时被立刻唤醒, 并且那个数据被返回给调用者, 即便小于在给方法的 count 参数中请求的数量,反之,如果一个进程调用 write 并且在缓冲中没有空间, 这个进程必须阻塞,并且它必须在一个与用作 read 的不同的等待队列中. 当一些数据被写入硬件设备, 并且在输出缓冲中的空间变空闲, 这个进程被唤醒并且写调用成功, 尽管数据可能只被部分写入如果在缓冲只没有空间给被请求的 count 字节。

  我们来看一个读操作的例子:

static ssize_t scull_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
        struct scull_pipe *dev = filp->private_data;
        if (down_interruptible(&dev->sem)) //加锁
                return -ERESTARTSYS;

        while (dev->rp == dev->wp) //无东西可读
        { 
                up(&dev->sem); //解锁
                if (filp->f_flags & O_NONBLOCK) //非阻塞方式,立即返回
                        return -EAGAIN;

                //阻塞访问,睡眠等待,等到读条件满足时继续执行。
                if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))
                        return -ERESTARTSYS;
                if (down_interruptible(&dev->sem)) //重新加锁
                        return -ERESTARTSYS;
        }

        //读取数据。
        ……
        up (&dev->sem); 

        wake_up_interruptible(&dev->outq);
        return count;
}

  从上面的例子我们可以看到,是通过调用wait_event_interruptible()实现阻塞等待的,来看一下wait_event_interruptible()实现

   #define wait_event_interruptible(wq, condition)
        ({
               int __ret = 0;
                if ( (!condition) )
                        __wait_event_interruptible(wq, condition, __ret);
                __ret;
        })
        #define __wait_event_interruptible(wq, condition, __ret)
        do {
                DEFINE_WAIT(__wait); // 定义等待队列__wait
                for(;;) {
                        prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);  //将等待队列__wait加入以wq为首的等待队列链表中,并且将进程状态设置为TASK_INTERRUPTIBLE
                        if (condition) //如果condition满足则跳出
                                break; 
                       if (!signal_pending(current)) { //没被信号唤醒
                               schedule(); // 放弃CPU,调度其它进程执行
                                continue;
                        }
                          ret = - ERESTARTSYS;
                          break;
                }
                finish_wait(&wq, &__wait); //将等待队列__wait从等待队列头wq指向的等待队列链表中移除,将进程状态设置为TASK_RUNNING
        }while(0)

总结起来, 阻塞睡眠步骤一般为:
  1)分配和初始化一个 wait_queue_t 结构, 随后将其添加到正确的等待队列. 当所有东西都就位了, 负责唤醒工作的人就可以找到正确的进程
  2)设置进程的状态来标志它为睡眠(TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE)
  3)调用schedule(),让出CPU。

版权声明:本文为博主原创文章,未经博主允许不得转载。

阻塞睡眠实现机制

标签:linux kernel   阻塞   睡眠   

原文地址:http://blog.csdn.net/kzq_qmi/article/details/47212407

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!