queue_work
/**
向工作队列提交新的工作节点。
*/
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
int ret;
/**
调用了这个函数
get_cpu() 获取当前cpu,并禁止内核抢占
*/
ret = queue_work_on(get_cpu(), wq, work);
/**
开启内核抢占
*/
put_cpu();
return ret;
}
/**
返回 0 表示这个工作节点已经处于队列中了。
如果该工作节点还没有被处理,你再次提交,就会直接返回,不允许。
*/
int queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work)
{
int ret = 0;
/**
#define work_data_bits(work) ((unsigned long *)(&(work)->data))
将data的第0位设置为 1,并且返回第0位原来的值
这里为什么要将第0位设置为 1,而且还要返回原来的第0位的值呢?
如果第0位原来的值是 1,表示该工作节点已经提交 还没有被处理。
要把它置1,是为了说明,这个工作节点已经提交了,正在等待处理。
上面说过,这样提交是不允许的。
在看看这个函数 :
#define INIT_WORK(_work, _func) \
do { \
__INIT_WORK((_work), (_func), 0); \
} while (0)
#define __INIT_WORK(_work, _func, _onstack) \
do { \
__init_work((_work), _onstack); \
(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \
INIT_LIST_HEAD(&(_work)->entry); \
PREPARE_WORK((_work), (_func)); \
} while (0)
WORK_DATA_INIT()将work_struct的data初始化为0。
所以,在我们调用queue_work()之前,使用INIT_WORK()后,work_struct的data是0了,
无需我们手动的对data的位清零。
在后面:
在执行工作节点上的函数之前,会使用work_clear_pending(work)将该位清零。
#define PREPARE_WORK(_work, _func) \
do { \
(_work)->func = (_func); \
} while (0)
初始化一个工作项的函数指针
WORK_STRUCT_PENDING_BIT = 0, 等待执行的工作项
*/
if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work)))
{
__queue_work(cpu, wq, work);
ret = 1;
}
return ret;
}
static void __queue_work(unsigned int cpu, struct workqueue_struct *wq, struct work_struct *work)
{
...
insert_work(cwq, work, worklist, work_flags);
}
/**
添加一个工作节点到 队列中
看看这个cpu_workqueue_struct,
当使用create_singlethread_workqueue()创建工作队列时,创建一个工作队列,
只为当前cpu创建一个内核线程。
如果是使用create_workqueue()创建工作队列时,创建一个工作队列,为系统中的每个cpu都创建一个内核线程。
struct cpu_workqueue_struct
{
spinlock_t lock; //自旋锁。这里为什么要定义自旋锁:工作者会频繁的处理挂接再worklist上的工作节点,所以需要加锁
struct list_head worklist; //任务队列,提交的工作节点就是挂在这个链表上的
wait_queue_head_t more_work; //等待队列,如果没有工作要处理,那么工作者线程就会在这个队列上睡眠
struct work_struct *current_work; //工作者线程正在处理的工作节点
struct task_struct *thread; //指向工作者线程的进程描述符
}
*/
static void insert_work(struct cpu_workqueue_struct *cwq,
struct work_struct *work, struct list_head *head,
unsigned int extra_flags)
{
....
/**
添加到worklist链表的尾部。
*/
list_add_tail(&work->entry, head);
if (__need_more_worker(gcwq))
{
/**
唤醒在该等待队列上睡眠的线程。
唤醒后,最终会执行run_workqueue()函数来处理工作节点。
*/
wake_up_worker(gcwq);
}
}
/**
工作者线程 :
它的作用是处理工作队列中的工作节点的。
如果工作队列中有待处理的工作节点,他会将该工作节点从worklist链表上删除
然后调用该工作节点上的函数。
如果工作队列中没有工作节点了,它会睡眠,有上面的cpu_workqueue_struct注释可知
它睡眠在more_work等待队列上
*/
static int worker_thread(void * __cwq)
{
struct cpu_workqueue_struct *cwq = __cwq;
/**
这个宏的分析已经在我的前面博文中讲过了,
它就是定义了一个wait_queue_t 类型的等待队列项
且该进程被唤醒后会执行默认的唤醒函数autoremove_wake_function()
*/
DEFINE_WAIT(wait);
/**
设置该工作者线程可以被冻结
*/
if(cwq->wq->freezeable)
{
set_freezable();
}
/**
一个死循环。
在什么时候 会跳出该转换呢?
*/
for(;;)
{
/**
1 : 将上面定义的等待队列项插入到等待队列
2 : 将进程设置为 TASK_INTERRUPTIBLE状态
*/
prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE);
/**
如果cwq->worklist上没有工作节点等待处理而且也没有被stop而且该工作者线程已经被冷冻了
那么就睡眠,睡眠在cwq->more_work等待队列上
*/
if (!freezing(current) &&!kthread_should_stop() &&list_empty(&cwq->worklist))
{
schedule();
}
/**
如果向cwq->worklist上提交了一个工作节点,那么该工作者线程会被唤醒,唤醒后执行下面的代码:
1 : 将进程的状态设置为TASK_RUNNING
2 : 从等待队列中删除该进程
*/
finish_wait(&cwq->more_work, &wait);
try_to_freeze();
/**
如果工作者线程被stop的话 ,就退出本次循环
*/
if (kthread_should_stop())
{
break;
}
/**
遍历cwq->worklist上的工作节点,执行它的函数。
*/
run_workqueue(cwq);
}
return 0;
}
static void run_workqueue(struct cpu_workqueue_struct *cwq)
{
spin_lock_irq(&cwq->lock);
while (!list_empty(&cwq->worklist))
{
/**
获取
*/
struct work_struct *work = list_entry(cwq->worklist.next,struct work_struct, entry);
/**
获取工作节点上绑定的的处理函数指针
这个函数是再 INIT_WORK()宏中绑定的。前面开头的时候说过。
*/
work_func_t f = work->func;
/**
从worklist链表上删除这个工作节点
*/
list_del_init(cwq->worklist.next);
spin_unlock_irq(&cwq->lock);
...
/**
清除work_struct->data的第0位。
在提交工作节点到工作队列的时候 说过。
*/
work_clear_pending(work);
....
/**
终于调用了。
*/
f(work);
.....
}
/**
delay: 工作节点延迟挂到工作队列上的时间
*/
int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay)
{
/**
如果delay为0,还是调用queue_work()提交函数
*/
if (delay == 0)
{
return queue_work(wq, &dwork->work);
}
return queue_delayed_work_on(-1, wq, dwork, delay);
}
int queue_delayed_work_on(int cpu, struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay)
{
int ret = 0;
struct timer_list *timer = &dwork->timer;
struct work_struct *work = &dwork->work;
/**
还是判断data的0位是否是0.和上面说的queue_work()的一样。
*/
if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work)))
{
....
/**
看,他是利用定时器来延时的。
*/
timer->expires = jiffies + delay;
timer->data = (unsigned long)dwork;
/**
时间到后,会执行这个函数
*/
timer->function = delayed_work_timer_fn;
....
}
}
/**
剩下的函数调用流程。和queyr_work()的一样了。
*/
static void delayed_work_timer_fn(unsigned long __data)
{
struct delayed_work *dwork = (struct delayed_work *)__data;
struct cpu_workqueue_struct *cwq = get_wq_data(&dwork->work);
struct workqueue_struct *wq = cwq->wq;
__queue_work(wq_per_cpu(wq, smp_processor_id()), &dwork->work);
}
![这里写图片描述](https://img-blog.csdn.net/20160114184711531)