经验说:
工作队列使用上和 tasklet最大的不同是工作队列的函数可以使用休眠,而tasklet的函数是不允许使用休眠的。
工作队列一般用来做滞后的工作,比如在中断里面要做很多事,但是比较耗时,这时就可以把耗时的工作放到等待队列。就是系统延时调度的一个自定义函数。
在2.6.20之后的版本,这INIT_WORK(struct work_struct *work, void (*function)(void *), void *data)改为现在的两个参数,最后的那个data参数不带
了。data数据直接进入一个包含work_struct的自己定义的结构体了。
struct my_work_stuct{
int test;
struct work_stuct save;
};
使用步骤:
1、定义struct my_work_stuct irq_queue;
2、初始化INIT_WORK(&iirq_queue.save,do_irq_queuework);
3、调用方法:schedule_work(&irq_queue);
注,调用完毕后系统会释放此函数,所以如果想再次执行的话,就再次调用schedule_work()即可。
另外,内核必须挂载文件系统才可以使用工作队列。我的理解是:工作队列也属于调度,如果内核挂了,他就不调度了,当然就不能用工作队列了。
工作队列的使用又分两种情况,一种是利用系统共享的工作队列来添加自己的工作,这种情况处理函数不能消耗太多时间,这样会影响共享队列中其他任务的处
理;另外一种是创建自己的工作队列并添加工作。
(一)利用系统共享的工作队列添加工作:
第一步:编写一个工作队列结构体,
struct my_struct_t {
struct mcukey_data *data;
//私有数据
struct work_struct work;
//work_struct
};
void my_func();
第三步:程序运行时用INIT_WORK()创建
INIT_WORK(&key_work.work,my_func,&data);
第三步:将工作结构体变量添加入系统的共享工作队列
schedule_work(&key_work.work); //添加入队列的工作完成后会自动从队列中删除
或schedule_delayed_work(&key_work.work,tick); //延时tick个滴答后再提交工作
(二)创建自己的工作队列来添加工作
第一步:编写一个工作队列结构体,
struct my_struct_t {
struct mcukey_data *data;
//私有数据
struct work_struct work;
//work_struct
};
static struct my_struct_t key_work;
第二步:编写一个工作处理函数
void my_func();
第三步:创建自己的工作队列和工作结构体变量(通常在open函数中完成)
p_queue=create_workqueue("my_queue"); //创建一个名为my_queue的工作队列并把工作队列的入口地址赋给声明的指针
INIT_WORK(&key_work.work, my_func); //创建一个工作结构体变量并初始化,和第一种情况的方法一样
queue_work(p_queue, &(key_workk.work)); //将工作添加入自己创建的工作队列等待执行,作用与schedule_work()类似,不同的是将工作添加入p_queue指针
指向的工作队列而不是系统共享的工作队列
第四步:删除自己的工作队列
工作队列使用上和 tasklet最大的不同是工作队列的函数可以使用休眠,而tasklet的函数是不允许使用休眠的。
工作队列一般用来做滞后的工作,比如在中断里面要做很多事,但是比较耗时,这时就可以把耗时的工作放到等待队列。就是系统延时调度的一个自定义函数。
在2.6.20之后的版本,这INIT_WORK(struct work_struct *work, void (*function)(void *), void *data)改为现在的两个参数,最后的那个data参数不带
了。data数据直接进入一个包含work_struct的自己定义的结构体了。
struct my_work_stuct{
int test;
struct work_stuct save;
};
使用步骤:
1、定义struct my_work_stuct irq_queue;
2、初始化INIT_WORK(&iirq_queue.save,do_irq_queuework);
3、调用方法:schedule_work(&irq_queue);
注,调用完毕后系统会释放此函数,所以如果想再次执行的话,就再次调用schedule_work()即可。
另外,内核必须挂载文件系统才可以使用工作队列。我的理解是:工作队列也属于调度,如果内核挂了,他就不调度了,当然就不能用工作队列了。
工作队列的使用又分两种情况,一种是利用系统共享的工作队列来添加自己的工作,这种情况处理函数不能消耗太多时间,这样会影响共享队列中其他任务的处
理;另外一种是创建自己的工作队列并添加工作。
(一)利用系统共享的工作队列添加工作:
第一步:编写一个工作队列结构体,
struct my_struct_t {
struct mcukey_data *data;
//私有数据
struct work_struct work;
//work_struct
};
static struct my_struct_t key_work;
void my_func();
第三步:程序运行时用INIT_WORK()创建
INIT_WORK(&key_work.work,my_func,&data);
第三步:将工作结构体变量添加入系统的共享工作队列
schedule_work(&key_work.work); //添加入队列的工作完成后会自动从队列中删除
或schedule_delayed_work(&key_work.work,tick); //延时tick个滴答后再提交工作
(二)创建自己的工作队列来添加工作
第一步:编写一个工作队列结构体,
struct my_struct_t {
struct mcukey_data *data;
//私有数据
struct work_struct work;
//work_struct
};
static struct my_struct_t key_work;
第二步:编写一个工作处理函数
void my_func();
第三步:创建自己的工作队列和工作结构体变量(通常在open函数中完成)
p_queue=create_workqueue("my_queue"); //创建一个名为my_queue的工作队列并把工作队列的入口地址赋给声明的指针
INIT_WORK(&key_work.work, my_func); //创建一个工作结构体变量并初始化,和第一种情况的方法一样
queue_work(p_queue, &(key_workk.work)); //将工作添加入自己创建的工作队列等待执行,作用与schedule_work()类似,不同的是将工作添加入p_queue指针
指向的工作队列而不是系统共享的工作队列
第四步:删除自己的工作队列
destroy_workqueue(p_queue); //一般是在close函数中删除
函数处理中container_of()这个宏,在function函数中通过带进来的work地址参数获取包含了work的设备描述符的地址,进而使用这个地址;
container_of作用:
container_of实现了根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针的功能。
container_of说明:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
container_of(kernel.h)在Linux Kernel中的应用非常广泛,它用于获得某结构中某成员的入口地址.
代码示例:
1. struct my_work_stuct{
2. int test;
3. struct work_stuct save;
4. };
5.
6. struct my_work_stuct test_work;
7. struct workqueue_struct *test_workqueue;
8.
9. void do_save(struct work_struct *p_work)
10. {
11. /*由数据结构中的work——stuct解析出整个strct,到处data*/
12. struct my_work_struct *p_test_work = container_of(p_work, struct my_work_stuct, save);
13. printk("%d\n",p_test_work->test);
14. }
15.
16. void test_init()
17. {
18. test_workqueue = create_workqueue("test_workqueue");
19. if (!test_workqueue)
20. panic("Failed to create test_workqueue\n");
21.
22. INIT_WORK(&(test_work.save), do_save);
23.
24. queue_work(test_workqueue, &(test_work.save));
25. }
26.
27. void test_destory(void)
28. {
29. if(test_workqueue)
30. destroy_workqueue(test_workqueue);
31. }
2. int test;
3. struct work_stuct save;
4. };
5.
6. struct my_work_stuct test_work;
7. struct workqueue_struct *test_workqueue;
8.
9. void do_save(struct work_struct *p_work)
10. {
11. /*由数据结构中的work——stuct解析出整个strct,到处data*/
12. struct my_work_struct *p_test_work = container_of(p_work, struct my_work_stuct, save);
13. printk("%d\n",p_test_work->test);
14. }
15.
16. void test_init()
17. {
18. test_workqueue = create_workqueue("test_workqueue");
19. if (!test_workqueue)
20. panic("Failed to create test_workqueue\n");
21.
22. INIT_WORK(&(test_work.save), do_save);
23.
24. queue_work(test_workqueue, &(test_work.save));
25. }
26.
27. void test_destory(void)
28. {
29. if(test_workqueue)
30. destroy_workqueue(test_workqueue);
31. }

本文探讨了Android驱动开发中的HAL层与工作队列的使用,特别提到了`destroy_workqueue()`通常在关闭函数中删除工作队列。另外,详细解释了`container_of`宏的作用,它能够通过结构体中某个成员的指针获取整个结构体的指针,这对于驱动编程至关重要。

4047

被折叠的 条评论
为什么被折叠?



