参考资料:《Linux设备驱动程序》第3版 LDD3e, LKD3e, 《 Linux per-CPU实现分析 》,linux-2.6.27,irq_balance
要使用workqueue当然逃不了per-CPU,per-CPU顾名思义,每个CPU,很多地方直接翻译为“每CPU"。关于per-CPU的接口操作就是对于所有CPU的操作,即每个CPU都有一个数据副本或者线程等,如果这个接口传入指定CPU编号,那么就是对某个指定的CPU操作。具体的per-CPU内核实现请看之前转载的文章《Linux per-CPU实现分析 》见
1,创建一个per-CPU
*编译期间静态创建一个per-CPU
DEFINE_PER_CPU(type, name)
创建一个名为name,数据类型为type的per-CPU,比如static DEFINE_PER_CPU(struct sk_buff_head, bs_cpu_queues),此时每个CPU都有一个名叫bs_cpu_queues,数据结构为sk_buff_head的变量副本。每个副本都是在自己的CPU上工作。
* 动态创建per-CPU,以下代码是内核create_workqueue实现的片断
struct workqueue_struct *__create_workqueue(const char *name,
int singlethread)
{
int cpu, destroy = 0;
struct workqueue_struct *wq;
struct task_struct *p;
wq = kzalloc(sizeof(*wq), GFP_KERNEL);
if (!wq)
return NULL;
wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct);
if (!wq->cpu_wq) {
kfree(wq);
return NULL;
}
……
}
2,使用工作队列
*编译时静态创建,因为暂时用不到也就没去看具体实现过程和用法。
#define DECLARE_WORK(name, void(*func)(void *), void *data);
其使用的默认处理函数为work_handler(void *data)。
使用schedule_work(&work)进行工作调度。
*动态创建
INIT_WORK(struct work_struct *work,void(*func)(void *), void *data);
比如如下代码
static DEFINE_PER_CPU(struct sk_buff_head, bs_cpu_queues);
int netif_receive_skb(struct sk_buff *skb)//接收到网卡数据
{
……
……
static inline int bs_dispatch(struct sk_buff *skb)//分发网卡数据
{
struct sk_buff_head *q;
q = &per_cpu(bs_cpu_queues, cpu);//从CPU为cpu处取一个sk_buff_head数据结构
//把数据skb插入到双向循环链表q中。这样子这个q就是待处理的数据了。
bs_works = &per_cpu(bs_works, cpu);//从CPU为cpu处取一个work_struct结构
if (!bs_works->func) {//假如当前工作处理函数指针为空
INIT_WORK(bs_works, bs_func, q);//创建工作队列,工作队列函数指为bs_func,处理的数据为q。
queue_work(per_cpu_ptr(keventd_wq->cpu_wq, cpu), bs_works);//在CPU编号为cpu的默认队列keventd_wq中插入一个bs_works工作任务。具体看下面。
}
}
……
……
}
3,创建新的工作队列已经在《Linux工作队列workqueue实现分析》讲过了,是通过create_workqueue实现,这里不再重复。