工作队列是一个用于创建内核线程的接口。通过他创建的进程负责执行由内核其他部分排到队列里的任务。工作队列的最基本的形式,就是把需要推后执行的任务交给特定的通用线程的这样一种接口。
缺省的工作线程叫做event/n,n代表处理器的编号。缺省线程会从多个地方得到被推后的工作。
工作这线程用workqueue_struct来表示。
Struct workqueue_struct {
Structcpu_workqueue_struct cpu_wq[NR_CPUS];
Structlist_head list;
Constchar *name;
Intsinqlethread;
Intfreezeable;
Intrt;
}
Cpu_workqueue_struct是kernel/workqueue.c当中的核心数据结构。
Struct cpu_workqueue_struct{
Spinlock_tlock; //锁保护这种结构
Structlist_head worklist; // 工作列表
Wait_queue_head_tmore_work;
Structwork_struct *current_struct;
Structworkqueue_struct *wq;
Task_t *thread;
}
表示工作的数据结构
所有的工作者线程都是由普通线程实现的。他们都要执行work_thread()函数,在他执行完初始化函数以后,这个函数就开始执行一个死循环并且开始休眠。当有操作被插入到队列里的时候,线程就会被唤醒。
工作用linux/workqueue.h中定义的work_struct结构体表示
Struct work_struct{
Atomic_long_tdata;
Structlist_head entry;
Work_func_tfunc;
}
这些结构体被连成链表。
Worker_thread()函数的核心流程。
For( ; ; )
{
Prepare_to_wait(&cwq_morework,&more_work,&wait,TASK_INTERRUPTIBLE);
If(list_empty(&cwq->worklist))
Schedule();
Finish_wait(&cwq->more_work,&wait);
Run_workqueue(cwq);
}
该函数完成了一下的功能:
线程将自己设置成休眠状态(state被设置成TASK_INTERRUPTABLE),并把自己加到等待列队中
如果工作链表是空的,线程调用schedule()函数进入休眠状态。
如果链表当中有对象,线程不会睡眠。他会将自己设置成TASK_RUNNING,脱离等待队列。
如果链表飞控,调用run_workqueue()函数完成推后的工作。
While(!list_empty(&cwq->worklist)) {
Structwork_struct *work;
Work_func_tf;
Void*data;
Work= list_entry(cwq->worklist.next, struct work_struct,entry );
F= work.func;
List_del_init(cwq->worklist.next);
Work_clear_pending(work);
f(work);
}
当链表不为空的时候,选取下一个节点对象
获取我们希望执行的函数func以及参数data
把该节点从链表上解下来,将待处理标志位pending清零。
调用函数
重复执行
使用工作队列
首先看一下默认的events任务队列,
1、 创建退后的工作:
首先要做的是实际创建一些需要推后完成的额工作,可以通过DECLEAR_EORK在编译时静态创建该结构体。
DECLEAR_WORK(struct work_struct *work,void (*func) (void*).void*data);
同样也可以在运行时通过指针创建一个工作。
INIT_WORK(structwork_struct *work,void (*func) (void*).void *data);
2、 工作队列处理函数
void work_handler(void *data)
3、 对工作进行调度
想要把给定工作的处理函数提交给默认的工作现成,需要调用:
Schedule_work(&work);
如果需要一定的延时,可以使用:
Schedule_delayed_work(&work,delay);
4、 刷新操作
有时,在进行下一步的操作之前,必须保证一些操作已经完成,为了防止竞争条件的出现,也需要确保不再有待处理的工作。
因此内核准备了一个用于刷新制定队列的函数:
void flush_scheduled_work(void);
如果需要取消延迟执行的工作应该调用
int cancel_delayed_work(struct work_struct *work)
5、 创建新的工作队列
创建一个新的任务列队和与之相关的工作者线程,需要调用一个函数
Sstructworkqueue_struct *creat_workqueue(const char *name);
Name参数用于内核现成的命名,比如,默认的events队列的创建就是调用:
Struct_workqueue_struct*keventd_wq;
Kevents_wq =creat_workqueue(*events);
创建一个工作的时候无需考虑工作队列的类型,使用到的函数有,
Int queue_work (structworkqueue_struct *wq,struct work_struct *work)
Int queue_delayed_work(tructworkqueue_struct *wq,struct work_struct *work,unsigned long delay);
最后可以调用制定的函数刷新制定的工作队列。
flush_orkqueue(structworkqueue_struct *wq);
老的任务队列机制
下半部分机制的选择:
下下半部分的实现在2.6内核当中有三种实现的方式:软中断,tasklet和工作队列。
软中断提供的执行序列化的保障最少。这样就必须格外小心的采取一些步骤确保共享数据的安全。
一般的驱动程序编写者需要做两个选择。首先是不是需要一个可调度的实体来执行需要退后的工作。
在下半部之间加锁
枷锁问题是一个有趣且广泛的话题。
今天就这样了
爱你 YZ