Linux设备驱动器之一 工作线程
数据结构
struct kthread_worker {
unsigned int flags;
raw_spinlock_t lock;
struct list_head work_list;
struct list_head delayed_work_list;
struct task_struct *task;
struct kthread_work *current_work;
};
struct kthread_work {
struct list_head node;
kthread_work_func_t func;
struct kthread_worker *worker;
/* Number of canceling calls that are running at the moment. */
int canceling;
};
struct kthread_delayed_work {
struct kthread_work work;
struct timer_list timer;
};
Linux APIs
产生工作线程 kthread_create_worker
struct kthread_worker *kthread_create_worker(unsigned int flags, const char namefmt[], …)
- unsigned int flags : 指定任务工作线程默认行为
- const char namefmt[] : kthread 任务工作线程的printf样式名称。
- … : 变量参数
如果成功,则返回一个指针, 它指向已产生的任务工作线程;
如果无法分配所需的结构,则返回 ERR_PTR(-ENOMEM);
如果调用者收到一个致命信号,则返回 ERR_PTR(-EINTR)。
初始化工作 kthread_init_work
kthread_init_work(work, fn)
这是一个宏定义,它初始化struct kthread_work结构变量work, 并设置fn为完成该工作的程序代码。就是调用fn去完成需要的工作。
排队工作 kthread_queue_work
bool kthread_queue_work(struct kthread_worker *worker, struct kthread_work *work)
功能: 排队一个kthread_work
参数:
- struct kthread_worker *worker : 任务工作线程
- struct kthread_work *work: 需要排队的工作
将工作排队到工作处理器任务以进行异步执行。任务必须已使用 kthread_worker_create() 创建。如果工作已成功排队,则返回 true;如果工作已处于挂起状态,则返回 false。
如果工作需要由其他任务工作线程使用,请重新初始化该工作。例如,当任务工作线程停止并再次启动时。
在Linux中的应用实列
SPI 驱动器与imx SPI
spi_init_queue调用kthread_create_worker,kthread_init_work, 去产生SPI任务工作线程,细节见下面的代码段。
static int spi_init_queue(struct spi_controller *ctlr)
{
ctlr->running = false;
ctlr->busy = false;
ctlr->kworker = kthread_create_worker(0, dev_name(&ctlr->dev));
if (IS_ERR(ctlr->kworker)) {
dev_err(&ctlr->dev, "failed to create message pump kworker\n");
return PTR_ERR(ctlr->kworker);
}
kthread_init_work(&ctlr->pump_messages, spi_pump_messages);
/*
* Controller config will indicate if this controller should run the
* message pump with high (realtime) priority to reduce the transfer
* latency on the bus by minimising the delay between a transfer
* request and the scheduling of the message pump thread. Without this
* setting the message pump thread will remain at default priority.
*/
if (ctlr->rt)
spi_set_thread_rt(ctlr);
return 0;
}
imx SPI 驱动器支持NXP i.MX 8M Nano。
下列的流程图描绘了imx SPI 驱动器如何调用spi_init_queue去初始化任务工作线程。
spi_register_master 由下列的宏定义
#define spi_register_master(_ctlr) spi_register_controller(_ctlr)
任务工作线程代码
static void spi_pump_messages(struct kthread_work *work)
如果SPI任务工作线程忙,则调用kthread_queue_work,将任务工作加入到等待队列。
启动任务工作线程
工作线程(worker)
int kthread_worker_fn(void *worker_ptr)
{
struct kthread_worker *worker = worker_ptr;
struct kthread_work *work;
/*
* FIXME: Update the check and remove the assignment when all kthread
* worker users are created using kthread_create_worker*() functions.
*/
WARN_ON(worker->task && worker->task != current);
worker->task = current;
if (worker->flags & KTW_FREEZABLE)
set_freezable();
repeat:
set_current_state(TASK_INTERRUPTIBLE); /* mb paired w/ kthread_stop */
if (kthread_should_stop()) {
__set_current_state(TASK_RUNNING);
raw_spin_lock_irq(&worker->lock);
worker->task = NULL;
raw_spin_unlock_irq(&worker->lock);
return 0;
}
work = NULL;
raw_spin_lock_irq(&worker->lock);
if (!list_empty(&worker->work_list)) {
work = list_first_entry(&worker->work_list,
struct kthread_work, node);
list_del_init(&work->node);
}
worker->current_work = work;
raw_spin_unlock_irq(&worker->lock);
if (work) {
kthread_work_func_t func = work->func;
__set_current_state(TASK_RUNNING);
trace_sched_kthread_work_execute_start(work);
work->func(work);
/*
* Avoid dereferencing work after this point. The trace
* event only cares about the address.
*/
trace_sched_kthread_work_execute_end(work, func);
} else if (!freezing(current))
schedule();
try_to_freeze();
cond_resched();
goto repeat;
}
这个线程的流程图如下
它检查这个任务线程的任务工作链表,如果非空,那么就从任务工作链表上取下一个任务工作 并运行它的回调函数。这个回调函数在任务工作初始化是设置。
从这个程序段看出,任务工作一定要初始化。
Linux管理线程
Linux系统初始时,产生了一个工作线程的管理线程,这里称其为任务线程产生的监视线程。
kthread_create_worker产生一个struct kthread_create_info变量,并将这个变量加入到一个待产生线程链表中,这个链表的表头是kthread_create_list
监视线程监视这个待产生线程链表,一旦新的变量加入这个链表,这个监视线程就产生一个新的任务工作(worker)线程。