Linux等待队列机制
2018-02-07 10:06
218 查看
一.概念
1.linux内核等待队列机制1.1.概念
明确:等待分为忙等待和休眠等待
"等待":期望某个事件发生
“事件”:比如按键有操作,串口有数据,网络有数据;
明确:阻塞一般是指休眠等待
明确:进程的状态
1.进程的准备就绪状态
TASK_READY;
2.进程的运行状态
TASK_RUNNING;
3.进程的休眠状态
不可中断的休眠:TASK_UNINTERRUPTIBLE
可中断的休眠:TASK_INTERRUPTIBLE
4.注意:进程的切换,调度都是利用内核的调度器来实现的,调度器管理的对象是进程;
明确:休眠只能用于进程
进程休眠的方法:
1.在用户层调用sleep函数
2.在内核层调用msleep/ssleep/schedule/schedule_timeout
说明:以上休眠的方法的缺点在于一旦事件到来,进程无法及时被唤醒(除非发信号),那么造成事件无法得到及时的处理;
问:事件一旦到来,如何及时唤醒休眠的进程呢?
问:一个进程如果发现设备不可用,进程将进入休眠等待,一旦设备可用,如何及时唤醒休眠的进程呢?
答:利用linux内核的等待队列机制;
二.等待队列机制
2.等待队列机制特点:
1.等待队列机制本质目的就是实现进程在内核空间进行休眠操作;当然休眠的原因是等待某个事件到来!
2.一旦事件到来,驱动能够主动唤醒休眠的进程,当然也可以通过信号来唤醒;
3.信号量机制就是靠等待队列机制来实现的!
使用等待队列机制实现进程休眠的步骤:
1.等待队列 = 等待 + 队列
模型:
进程调度器->老鹰(内核实现)
等待队列头->鸡妈妈(驱动实现)
要休眠的进程->小鸡(驱动实现)
2.相关的数据结构
linux内核描述等待队列头的数据类型:
wait_queue_head_t
linux内核描述装载休眠进程的容器的数据类型:
wait_queue_t
切记:此数据类型描述的装载进程的容器
linux内核描述进程(线程)的数据类型:
struct task_struct {
volatile long state;//进程的状态
pid_t pid;//进程的进程号
char comm[TASK_COMM_LEN];//进程的名称
...
};//内核会为每一个进程创建一个对应的对象
linux内核描述"当前进程"的内核全局指针变量: current
"当前进程":只是当时获取CPU资源,正在运行中中的进程,
而此时内核全局指针变量current就指向当前这个进程的struct task_struct对象;
3.使用等待队列实现进程在内核休眠的编程步骤:
3.1.定义初始化等待队列头(造鸡妈妈)
wait_queue_head_t wq;
init_waitqueue_head(&wq);
3.2.定义初始化装载休眠进程的容器(造小鸡)
wait_queue_t wait;
init_waitqueue_entry(&wait, current);
说明:把当前要休眠的进程添加到容器wait中
3.3.将当前要休眠的进程添加到休眠队列中去
add_wait_queue(&wq, &wait);
3.4.设置当前要休眠进程的休眠状态
set_current_state(TASK_INTERRUPTIBLE);
//设置为可中断休眠状态
或者
set_current_state(TASK_UNINTERRUPTIBLE);
//设置为不可中断的休眠状态
注意:此时当前进程还没有进入休眠状态,还没有释放CPU资源;
3.5.当前进程进入真正的休眠状态(释放CPU资源)
schedule();
注意:此时程序就运行到此停止不前,等待某个事件的到来!
注意:
1.此休眠函数和休眠状态(可中断的),休眠进程被唤醒的方法有两种:
第一种通过信号来唤醒
第二种通过事件到来,驱动主动唤醒
2.此休眠函数和休眠状态(不可中断的),休眠进程被唤醒的方法有一种:
第一种通过事件到来,驱动主动唤醒
总结:调用此函数,静静等待信号或者驱动主动来唤醒;
3.6.一旦休眠进程被唤醒,记得要将休眠进程从休眠队列中移除,在移除前设置当前进程的状态为运行态:
set_current_state(TASK_RUNNING);
remove_wait_queue(&wq, &wait);
3.7.判断唤醒的原因:
if (signal_pending(current)) {
printk("由于接受到了信号引起的唤醒!\n")
return -ERESTARTSYS;
} else {
printk("事件到来,驱动主动唤醒!\n");
接下来开始处理事件
}
3.8.事件到来,驱动主动唤醒的方法:
wake_up(&wq); //唤醒休眠队列中所有的休眠进程;
或者
wake_up_interruptible(&wq);//唤醒休眠队列中所有睡眠类型为可中断的休眠进程
案例:写进程唤醒读进程
实验步骤:
1.insmod led_drv.ko
2../led_test r & //启动读进程
3../led_test w //启动写进程 ,主动唤醒
4../led_test r & //启动读进程
5.kill 读进程的PID //接受信号唤醒
#include <linux/init.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/sched.h> //定义等待队列头 static wait_queue_head_t wq; static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { //让读进程进入休眠状态,等待写进程来唤醒 //1.定义初始化装载休眠进程的容器 //说明:只要进程调用read,那么就给这个进程单独分配一个容器 wait_queue_t wait; init_waitqueue_entry(&wait, current); //2.将当前进程添加到休眠队列中 add_wait_queue(&wq, &wait); //3.设置当前进程的休眠状态为可中断 set_current_state(TASK_INTERRUPTIBLE); //4.让当前进程进入休眠状态 printk("%s:读进程[%s][%d]将进入休眠状态!\n", __func__, current->comm, current->pid); schedule();//此时等待被唤醒:写进程唤醒或者接收到了信号 //5.一旦被唤醒,设置当前进程的状态为运行 set_current_state(TASK_RUNNING); //6.将当前进程从休眠队列中移除 remove_wait_queue(&wq, &wait); //7.判断唤醒的原因 if (signal_pending(current)) { printk("%s:读进程[%s][%d]是由于接收到了信号引起的唤醒!\n", __func__, current->comm, current->pid); return -ERESTARTSYS; } else { printk("%s:读进程[%s][%d]是由于写进程唤醒!\n", __func__, current->comm, current->pid); } return count; } static ssize_t led_write(struct file *file, char __user *buf, size_t count, loff_t *ppos) { //唤醒休眠的读进程 printk("%s:写进程[%s][%d]将会唤醒读进程!\n", __func__, current->comm, current->pid); wake_up_interruptible(&wq); return count; } //定义初始化操作接口 static struct file_operations led_fops = { .owner = THIS_MODULE, .read = led_read, .write = led_write }; //定义初始化混杂设备对象 static struct miscdevice led_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "myled", .fops = &led_fops }; static int led_init(void) { //注册 misc_register(&led_misc); //初始化等待队列头 init_waitqueue_head(&wq); return 0; } static void led_exit(void) { //卸载 misc_deregister(&led_misc); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL");
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char *argv[]) { int fd; int data; if (argc != 2) { printf("Usage:%s <r|w>\n", argv[0]); return -1; } fd = open("/dev/myled", O_RDWR); if (fd < 0) return -1; if (!strcmp(argv[1], "r")) { //启动一个读进程 read(fd, &data, 4); } else { //启动一个写进程 write(fd, "hello", 5); } close(fd); return 0; }
三.等待队列机制编程方法2
3.linux内核等待队列编程方法2:明确:等待队列机制是实现进程在内核空间进行休眠操作;
驱动要做的步骤:
1.定义初始化等待队列头
wait_queue_head_t wq;
init_waitqueue_head(&wq);
2.调用以下方法实现进程的休眠
wait_event(wq,condition);
说明:
参数:
wq:等待队列头
condition:
condition如果为假,表示事件没有满足,进程需要进行休眠;
condition如果为真,表示事件满足,进程不进行休眠,立即返回
1.内核会帮你定义初始化一个装载当前进程的容器
2.内核也会帮你将当前进程添加到wq的休眠队列中
3.内核也会帮你设置进程的休眠状态,此休眠状态为不可中断;
4.内核也会帮你进入真正的休眠;
5.内核也会帮你判断唤醒的原因;
6.内核也会帮你移除
或者
wait_event_interruptible(wq,condition);
说明:
参数:
wq:等待队列头
condition:
condition如果为假,表示事件没有满足,进程需要进行休眠;
condition如果为真,表示事件满足,进程不进行休眠,立即返回
1.内核会帮你定义初始化一个装载当前进程的容器
2.内核也会帮你将当前进程添加到wq的休眠队列中
3.内核也会帮你设置进程的休眠状态,此休眠状态 为可中断;
4.内核也会帮你进入真正的休眠;
5.内核也会帮你判断唤醒的原因;
6.内核也会帮你移除
编程框架:
//刚开始condition为假
wait_event_interruptible(wq, condition);
将condition再次设置为假,为了下一次休眠
...
//在别处,发现事件满足,唤醒
condition设置为真;
wake_up_interruptible(&wq);
案例:尝试阅读wait_event_interruptible内核实现
案例:编写一个真实有用的按键驱动!
相关文章推荐
- Linux内核等待队列机制介绍
- Linux内核等待队列机制介绍(转自http://plinux.org)
- Linux中等待队列机制分析
- Linux中等待队列机制分析
- Linux中等待队列机制分析
- linux中的阻塞机制及等待队列
- Linux中等待队列机制分析
- linux中的阻塞机制及等待队列
- linux中的阻塞机制及等待队列【转】
- [Linux]阻塞与非阻塞(等待队列、轮询)机制
- Linux中等待队列机制分析
- 把握linux内核设计思想(五):下半部机制之工作队列及几种机制的选择
- AbstractQueuedSynchronizer同步队列与Condition等待队列协同机制
- linux之等待队列--阻塞型驱动学习---学习笔记
- linux之等待队列
- Linux驱动学习——等待队列
- linux 消息队列机制
- linux进程的休眠(等待队列)
- linux 网络协议栈-队列机制
- linux等待队列