linux系统工作队列

来源:互联网 发布:淘宝卖家用淘客推广 编辑:程序博客网 时间:2024/04/29 04:32

最近一直再调试一个基于spi的打印机,一路心酸坎坷啊。

在这个打印的驱动中,需要不断读取打印数据写入spi,在时序方面要达到每1ms往spi写入一次数据,所以用到了工作队列以及定时器来实现。


1、工作队列

工作队列提供一个通用的办法将任务延迟到 bottom halves。处于核心的是工作队列(结构体 workqueue_struct), 任务被安排到该结构体当中。任务由结构体 work_struct 来说明,用来鉴别哪些任务被延迟以及使用哪个延迟函数。 events/X 内核线程(每 CPU 一个)从工作队列中抽取任务并激活一个 bottom-half 处理程序(由处理程序函数在结构体 work_struct 中指定)。

数据结构:
1
2
3
4
5
6
7
8
struct work_struct {
    unsignedlong pending;
    struct list_head entry;
    void (*func)(void *);
    void *data;
    void *wq_data;
    struct timer_list timer;
};

pending是用来记录工作是否已经挂在队列上;

entry是循环链表结构;

func作为函数指针,由用户实现;

data用来存储用户的私人数据,此数据即是func的参数;

wq_data一般用来指向工作者线程(工作者线程参考下文);

timer是推后执行的定时器。

work_struct的这些变量里,funcdata是用户使用的,其他是内部变量,我们可以不用太过关心。

 

API

1
2
3
4
5
INIT_WORK(_work, _func, _data);
int schedule_work(struct work_struct *work);
int schedule_delayed_work(struct work_struct *work, unsigned long delay);
void flush_scheduled_work(void);
int cancel_delayed_work(struct work_struct *work);

1、初始化指定工作,目的是把用户指定的函数_func_func需要的参数_data赋给work_structfuncdata变量。

2、对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。工作者线程本质上是一个普通的内核线程,在默认情况下,每个CPU均有一个类型为“events”的工作者线程,当调用schedule_work时,这个工作者线程会被唤醒去执行工作链表上的所有工作。

3、延迟执行工作,与schedule_work类似。

4、刷新缺省工作队列。此函数会一直等待,直到队列中的所有工作都被执行。

5、flush_scheduled_work并不取消任何延迟执行的工作,因此,如果要取消延迟工作,应该调用cancel_delayed_work

 

以上均是采用缺省工作者线程来实现工作队列,其优点是简单易用,缺点是如果缺省工作队列负载太重,执行效率会很低,这就需要我们创建自己的工作者线程和工作队列。

API

1
2
3
4
5
struct workqueue_struct *create_workqueue(const char *name);
int queue_work(struct workqueue_struct *wq, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay);
void flush_workqueue(struct workqueue_struct *wq);
void destroy_workqueue(struct workqueue_struct *wq);

1、创建新的工作队列和相应的工作者线程,name用于该内核线程的命名。

2、类似于schedule_work,区别在于queue_work把给定工作提交给创建的工作队列wq而不是缺省队列。

3、延迟执行工作。

4、刷新指定工作队列。

5、释放创建的工作队列。

下面一段代码可以看作一个简单的实作:

1.创建工作队列并与工作任务函数绑定:

struct delayed_work ms_queue_work;
struct workqueue_struct *ms_wqueue;        ms_wqueue = create_rt_workqueue("1ms");INIT_DELAYED_WORK(&ms_queue_work, queue_work_task);if (!ms_wqueue) {pr_err("failed to create workqueue\n");return -1;}
工作队列的使用有两个办法,一是使用系统默认的工作队列,它对应一个默认的工作者线程,这个线程是什么策略我没有查过,应该是SCHED_NORMAL ,第二种方法就是自己创建一个队列
#define create_workqueue(name) __create_workqueue((name), 0, 0, 0)#define create_rt_workqueue(name) __create_workqueue((name), 0, 0, 1)#define create_freezeable_workqueue(name) __create_workqueue((name), 1, 1, 0)#define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0, 0)</span>
这里的create_rt_workqueue是为了创建一个调度策略为SCHED_FIFO的工作队列。
2.延时调度:
queue_delayed_work(ms_wqueue, &ms_queue_work, 1 );schedule_delayed_work(&ms_queue_work,usecs_to_jiffies(1000));
以上2个函数都可以实现延时调度,第一个是指一个时钟节拍执行一次,一个节拍即1/HZ 的时间,默认值是100,可以在arch/arm/KConfig里面将默认值改成1000,这样时钟节拍就变成了1ms,如无需要还是建议不要去修改这个值。如果要循环调度,则需要每次都调用一次延时调度函数。



0 0
原创粉丝点击