linux系统工作队列

最近一直再调试一个基于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 {
     unsigned long  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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值