阻塞睡眠实现机制
2015-08-02 21:56
218 查看
在看阻塞睡眠实现机制前,我们来看一下内核中广泛用到的等待队列。
Linux内核的等待队列为双循环链表结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。它有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。等待队列头和等待队列项中都包含一个list_head(双链表)。通过这样一个双链表把等待进程链接起来。
下面来看两者数据结构:
![](http://img.blog.csdn.net/20150802214910528)
我们知道缺省状态下IO是阻塞的(除非设定O_NONBLOC),如果一个进程调用 read 但是没有数据可用, 这个进程必须阻塞. 这个进程在有数据达到时被立刻唤醒, 并且那个数据被返回给调用者, 反之,如果一个进程调用 write 并且在缓冲中没有空间, 这个进程必须阻塞,并且它必须在一个与用作 read 的不同的等待队列中. 当一些数据被写入硬件设备, 并且在输出缓冲中的空间变空闲, 这个进程被唤醒并且写调用成功。
我们来看一个读操作的例子:
从上面的例子我们可以看到,是通过调用wait_event_interruptible()实现阻塞等待的,来看一下wait_event_interruptible()实现
总结起来, 阻塞睡眠步骤一般为:
1)分配和初始化一个 wait_queue_t 结构, 随后将其添加到正确的等待队列. 当所有东西都就位了, 负责唤醒工作的人就可以找到正确的进程
2)设置进程的状态来标志它为睡眠(TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE)
3)调用schedule(),让出CPU。
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 但是没有数据可用, 这个进程必须阻塞. 这个进程在有数据达到时被立刻唤醒, 并且那个数据被返回给调用者, 反之,如果一个进程调用 write 并且在缓冲中没有空间, 这个进程必须阻塞,并且它必须在一个与用作 read 的不同的等待队列中. 当一些数据被写入硬件设备, 并且在输出缓冲中的空间变空闲, 这个进程被唤醒并且写调用成功。
我们来看一个读操作的例子:
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。
相关文章推荐
- MongoDB大文件存储流程
- Android输入法的关闭打开
- Java中的内存泄漏分析说明
- hdu5305 线上朋友和线下朋友(dfs暴力)
- C++对象生命周期(未完)
- Android多线程中UI线程和其他线程进行交互的几种方法小结
- uC/OS-II 函数之任务相关函数
- Oracle经典20题
- STL学习篇:初始了解
- 【LeetCode】202 - Happy Number
- ant配置
- escape、encodeURI、encodeURIComponent不编码的字符
- 单向循环链表实例
- ndk变量会文件找不到解决办法
- 什么是框架(包括前端框架和后端框架)
- 【effective c++读书笔记】【第4章】设计与声明(1)
- [2015hdu多校联赛补题]hdu5296 Annoying problem
- 【effective c++读书笔记】【第4章】设计与声明(1)
- poj 3436 网络流构图经典
- STM32串口程序(寄存器版)