linux内核等待队列机制:
当串口设备没有接收到数据,应用程序发现,利用内核提供的睡眠机制,应用在内程序核空间进入休眠状态;(前提:1.应用程序在用户空间没有权利访问硬件设备,只有通过系统调用跑到内核空间才有权限访问硬件设备!2.外设的处理速度要远远慢于CPU!)一旦串口设备给CPU产生中断信号,中断信号的到来也就代表这数据的到来,这时只需唤醒休眠的应用程序,让应用程序读取串口数据。
进程的状态:
运行:TASK_RUNNING
准备就绪:TASK_READY
可中断休眠:TASK_INTERRUPTIBEL
不可中断休眠:TASK_UNINTERRUPTIBLE
进程要“休眠":要休眠进程会将CPU资源全部从当前进程中撤
下来,将CPU资源给别的任务去使用,比如另外一个进程;
进程(CPU资源的切换)之间的切换:又内核调度器来实现,这
个调度器就是用来管理进程的!
调度器:当一个进程休眠,CPU资源需要需要切换的其他进程
中,执行其他任务,转换到哪个进程中是通过内核中的调度器
实现的,并不是进程自己 去找。
linux内核等待队列机制实现过程(类似于老鹰抓小鸡):
老鹰-》调度器:内核已经实现
鸡妈妈-》等待队列头
小鸡-》休眠的进程
内核描述等待队列头涉及的数据类型:
wait_queue_head_t
内核描述休眠的进程,装载休眠进程的容器的数据类型:
wait_queue_t(描述一个进程)
步骤:
1.分配等待队列头
wait_queue_head_t wq;
2.初始化等待队列头
init_waitqueue_head(&wq);
3.如果一个进程要访问设备,发现设备不可用,进入休眠,此
时分配这个进程的休眠的容器,将当前进程加入到wait容器中。
DECLARE_WAITQUEUE(wait, current);//仅仅是一个进程
加入的多了,就形成了队列
wait:表示保存当前进程的容器(需要休眠进程的名字,小鸡名)
current:它是内核的全局变量,在linux内核中,内核用
struct task_struct结构体来描述每一个进程,那么当进程
获取CPU资源是,current就指向当前进程(哪个进程获取CPU
资源,current就指向这个进程对应的task_struct结构体对象)
例如打印出当前进程的name和pid
printk("current process name is %s pid is %d\n",
current->comm, current->pid);
或者:
wait_queue_t wait;
init_waitqueue_entry(&wait, current);
注意:如果有多个休眠的进程,必须为每一个进程分配一个容
器,并且current也会分别执行不同的进程!
4.然后将当前进程添加到休眠的队列中去
add_wait_queue(&wq, &wait);
注意:仅仅是将当前进程添加到这个队列中去,但进程还处于运行状态!
remove_wait_queue(&wq, &wait);
将进程从等待队列中去除
5.设置当前进程的休眠状态(可中断或者不可中断)
可中断的休眠状态:
current->state = TASK_INTERRUPTIBLE;
不可中断的休眠状态:
current->state = TASK_UNINTERRUPTIBLE;
6.让进程进入真正的休眠状态
schedule(); //启动调度器,并且让CPU资源从当前进程撤下来,给别的进程去使用,至此当前进程运行到这个地方就
停止不动!一旦被唤醒,这个函数返回,当前进程接着执行
7.如果进程被设置为可中断的休眠状态,进程被唤醒的方法有
两种(设置为可中断休眠状态,可以通过中断和信号唤醒进程):
硬件设备可用,产生中断,由中断来唤醒;
进程接收到了信号引起的唤醒,所以要判断唤醒的原因:
判断是否接收到信号引起的唤醒:
if (signal_pending(current)) {
printk("当前进程是由于接收到了信号引起的唤醒");
printk("给用户返回-ERESTARTSYS");
} else {
printk("中断引起的唤醒,说明数据可用");
printk("后续继续获取数据");
}
8.不管是硬件中断引起的唤醒还是信号引起的唤醒,重新设置
当前进程的状态为运行状态
current->state = TASK_RUNNING;
9.将当前进程从休眠队列中移除
remove_wait_queue(&wq, &wait);
唤醒的方法有两种:
1.接收到信号引起的唤醒
2.驱动主动唤醒休眠的进程,方法如下:
wake_up(wait_queue_head_t *queue);
唤醒由queue指向的等待队列数据链中的所有睡眠类
型的等待进程
wake_up_interruptible(wait_queue_head_t *queue);
唤醒由queue指向的等待队列数据链中的所有睡眠类
型为TASK_INTERRUPTIBLE的等待进程