中断底半部机制有三种:1.工作队列 2.tasklet 3.软中断
注:软中断和tasklet运行于软中断上下文,仍然属于原子上下文的一种,而工作队列则运行于进程上下文,因此,软中断和taeklet处理函数中不能睡眠
而工作队列处理函数中允许睡眠。
总结:
第一种:利用系统共享的工作队列,自己创建节点并添加到系统共享工作队列
(对于多cpu而言,会在所有cpu上创建线程,执行挂在工作队列的工作节点上的函数。)
第一步:声明或编写一个工作处理函数
void my_func();
法一:DECLARE_WORK(my_work,my_func,&data); //编译时创建名为my_work的结构体变量并把函数入口地址和参数地址赋给它; 如果不想要在编译时就用DECLARE_WORK()创建并初始化工作结构体变量,也可以在程序运行时再用INIT_WORK()创建
法二:struct work_struct my_work; //创建一个名为my_work的结构体变量,创建后才能使用INIT_WORK()
或struct delayed_work my_work;//创建后用INIT_DELAYED_WORK()
INIT_WORK(&my_work,my_func,&data); //初始化已经创建的my_work,其实就是往这个结构体变量中添加处理函数的入口地址和data的地址,或INIT_DELAYED_WORK();
schedule_work(&my_work); //添加入队列的工作完成后会自动从队列中删除
或schedule_delayed_work(&my_work,tick); //延时tick个滴答后再提交工作
(对于多cpu而言,会在调用执行挂在工作队列的工作节点上的函数上的那个cpu上,创建线程,函数如下:
需要对自己创建的工作队列开个线程:workqueue = create_singlethread_workqueue("dsx_exp_workqueue");)
第一步:声明工作处理函数和一个指向工作队列的指针
void my_func();
struct workqueue_struct *p_queue;
第二步:创建自己的工作队列和工作节点
(1).p_queue=create_workqueue("my_queue"); //创建一个名为my_queue的工作队列并把工作队列的入口地址赋给声明的指针
或:p_queue = create_singlethread_workqueue("dsx_exp_workqueue");//哪个cpu调用工作处理函数,为该cpu创建处理线程
(2).struct work_struct my_work;//创建工作队列上的一个工作节点
或struct delayed_work my_work;//创建后用INIT_DELAYED_WORK()初始化
(3).INIT_WORK(&my_work, my_func, &data); //创建一个工作结构体变量并初始化,或INIT_DELAYED_WORK();
第三步:将工作节点添加入自己创建的工作队列等待执行
queue_work(p_queue, &my_work); //将自定义工作节点加入自定义工作队列,并调用线程执行。
//作用与schedule_work()类似,不同的是将工作添加入p_queue指针指向的工作队列而不是系统共享的工作队列
第四步:删除自己的工作队列
destroy_workqueue(p_queue); //一般是在close函数中删除
示例:
于是就让我们来仔细看看INIT_WORK和INIT_DELAYED_WORK.其实前者是后者的一个特例,它们涉及到的就是传说中的工作队列
如果我们将来某个时刻希望能够调用led_work()这么一个我们自己写的函数,那么我们所要做的就是利用工作队列.如何利用呢?第一步就是使用INIT_WORK()或者INIT_DELAYED_WORK()来初始化这么一个工作,或者叫任务,初始化了之后,将来如果咱们希望调用这个led_work()函数,那么咱们只要用一句schedule_work()或者schedule_delayed_work()就可以了,特别的,咱们这里使用的是INIT_DELAYED_WORK(),那么之后我们就会调用schedule_delayed_work(),这俩是一对.它表示,您希望经过一段延时然后再执行某个函数,所以,咱们今后会见到schedule_delayed_work()这个函数的,而它所需要的参数,一个就是咱们这里的&hub->leds,另一个就是具体自己需要的延时
,它们是cancel_delayed_work(struct delayed_work *work)和flush_scheduled_work().其中cancel_delayed_work()的意思不言自明,对一个延迟执行的工作来说,这个函数的作用是在这个工作还未执行的时候就把它给取消掉.而flush_scheduled_work()的作用,是为了防止有竞争条件的出现,虽说哥们儿也不是很清楚如何防止竞争,可是好歹大二那年学过一门专业课,数字电子线路,尽管没学到什么有用的东西,怎么说也还是记住了两个专业名词,竞争与冒险.您要是对竞争条件不是很明白,那也不要紧,反正基本上每次cancel_delayed_work之后您都得调用flush_scheduled_work()这个函数,特别是对于内核模块,
中断上下文:
1.新建等待队列头和flag标志
2.在进程中执行:wait_event_interruptible(waiter, tpd_flag != 0);
目的:将本进程置为可中断的挂起状态,条件满足后,即把本进程状态置为运行态(此时如果不执行下面的函数 wake_up_interruptible,wait_event_interruptible还会使进程继续休眠)。
3.在中断中执行:tpd_flag = 1;
4解释案列 :
Linux使用wake_up_interruptible()唤醒注册到等待队列上的进程
(2013-02-18 22:24:53)原型:
说明:
变量:
q :
在驱动中实现读取操作时,使用了
- wait_event_interruptible(button_waitq, ev_press);
- ev_press = 1;
//表示中断发生了
- wake_up_interruptible(&button_waitq);
//唤醒休眠的进程
查找资料,阅读源代码。
- #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); \ -
\ -
for (;;) { \ -
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ -
if (condition) \ -
break; \ -
if (!signal_pending(current)) { \ -
schedule(); \ -
continue; \ -
} \ -
ret = -ERESTARTSYS; \ -
break; \ -
} \ -
finish_wait(&wq, &__wait); \ - } while (0)
- //唤醒 q 指定的注册在等待队列上的进程。该函数不能直接的立即唤醒进程,而是由调度程序转换上下文,调整为可运行状态。
- wake_up_interruptible (wait_queue_head_t *q);
不知道内核这样设计是基于什么原因?