itop4412 设备树 驱动 kthread_worker
大概概念
- 这个主要是在驱动中传输大量数据时候用的,利用内核线程,来传输大量的数据,提高效率,这样就可以避免一个进程出现阻塞的情况。
主要的结构体
-
kthread_worker
/* 这个可以理解为工人,一直在检查自己的 work_list 里面的工作有没有,如果有 * 就执行,如果没有就还是检查,相当于一个死循环,一直检查有没有任务添加到 work_list * 列表中,如果添加到了,就执行里面的工作 */ struct kthread_worker { unsigned int fl ags; 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_worker hiworker; kthread_init_worker(&hiworker); // 实际这个函数只初始化了其中一部分变量,其中大部分变量并没有真正的给他赋值
-
-
kthread_work
/* 这个结构体可以理解为具体的工作,里面有func,这个就是执行的具体的函数 * 那个worker的指针 里面存放的是哪个工人 * 可以理解为哪个工人,执行我这个工作。 */ struct kthread_work { struct list_head node; // 连接到 kthread_worker->work_list kthread_work_func_t func; struct kthread_worker *worker; /* Number of canceling calls that are running at the moment. */ int canceling; };
-
kthread_flush_work
/* 这个结构体主要是为了清空,工人的所有任务, * 也就是 让 kthread_worker结构体里面的 work_list 里面的任务全部执行完 * 这个实际也是一个具体的工作,把该工作插入work_list 列表的队尾 * 它的工作Linux 内核已经写好了 kthread_flush_work_fn,就是这个工作, * 而且有专门的函数去掉用,一行代码就调用成功了 */ struct kthread_flush_work { struct kthread_work work; // 工作 struct completion done; // 完成量 };
主要的函数
-
kthread_init_worker()
/* 这个函数主要是初始化 kthread_worker 结构体 * 相当于初始化工人 下面这个宏,实际调用了下面的这个初始化函数 */ #define kthread_init_worker(worker) \ do { \ static struct lock_class_key __key; \ __kthread_init_worker((worker), "("#worker")->lock", &__key); \ } while (0) /* 下面的代码很好看懂,就是初始化 kthread_worker 里面的变量, * 里面并没有放任何有价值的数据 */ void __kthread_init_worker(struct kthread_worker *worker, const char *name, struct lock_class_key *key) { memset(worker, 0, sizeof(struct kthread_worker)); spin_lock_init(&worker->lock); lockdep_set_class_and_name(&worker->lock, key, name); INIT_LIST_HEAD(&worker->work_list); INIT_LIST_HEAD(&worker->delayed_work_list); }
-
kthread_run(kthread_worker_fn, &hiworker, …)
/* 这个函数也是个宏, 展开就是下面的样子, * 返回一个一个任务结构体指针 注意第一个参数,传入进去的是一个函数, * 也就是工人要干的任务,这个任务Linux 内核已经提供了,我们只要调用就好了 * 来看看这个函数 */ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), void *data, int node, const char namefmt[], ...); // 传入的参数 就是我们定义的那个工人 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; // 把kthread_worker 里面的 task指向当前线程 if (worker->flags & KTW_FREEZABLE) set_freezable(); repeat: // 设置下当前的状态 set_current_state(TASK_INTERRUPTIBLE); /* mb paired w/ kthread_stop */ // 判断当前线程是否要停止,停止之后,把task 变量赋值为NULL if (kthread_should_stop()) { __set_current_state(TASK_RUNNING); spin_lock_irq(&worker->lock); worker->task = NULL; spin_unlock_irq(&worker->lock); return 0; } work = NULL; spin_lock_irq(&worker->lock); // 加上自旋锁 if (!list_empty(&worker->work_list)) { /* 遍历 work_list里面的具体任务 * 如果有任务就执行,把该任务的函数指针赋值到work结构体里面 * 赋值完之后 把它从work_list列表里面删除。 */ work = list_first_entry(&worker->work_list, struct kthread_work, node); list_del_init(&work->node); } // 设置当前执行的工作 worker->current_work = work; spin_unlock_irq(&worker->lock); // 解锁 if (work) { // 如果work 里面有任务 // 设置当前为运行态 __set_current_state(TASK_RUNNING); // 这个地方才是真正执行任务的地方 work->func(work); } else if (!freezing(current)) schedule(); // 调度 try_to_freeze(); cond_resched(); goto repeat; // 再次循环下去 }
-
kthread_init_work()
/* 这个就是个宏定义,可以看到 * 先初始化节点链表 * 把函数赋值给 (work)->func */ #define kthread_init_work(work, fn) \ do { \ memset((work), 0, sizeof(struct kthread_work)); \ INIT_LIST_HEAD(&(work)->node); \ (work)->func = (fn); \ } while (0)
-
kthread_queue_work()
/* 这个函数主要是把work 添加到 worker 里面 * */ bool kthread_queue_work(struct kthread_worker *worker, struct kthread_work *work) { bool ret = false; unsigned long flags; spin_lock_irqsave(&worker->lock, flags); // 锁 if (!queuing_blocked(worker, work)) { // 添加work 到 worker kthread_insert_work(worker, work, &worker->work_list); ret = true; } spin_unlock_irqrestore(&worker->lock, flags); // 解锁 return ret; }
-
kthread_flush_worker()
/* 这个函数主要是清空 worker 里面的任务, * KTHREAD_WORK_INIT() 这个主要也是向 worker 里面添加一个 work * work 的内容是 kthread_flush_work_fn ,这个函数在Liunx 内核定义了 */ void kthread_flush_worker(struct kthread_worker *worker) { struct kthread_flush_work fwork = { KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn), COMPLETION_INITIALIZER_ONSTACK(fwork.done), }; // 添加到 worker的链表下面,实际就是 添加到work_list的最后面 kthread_queue_work(worker, &fwork.work); wait_for_completion(&fwork.done); } static void kthread_flush_work_fn(struct kthread_work *work) { struct kthread_flush_work *fwork = container_of(work, struct kthread_flush_work, work); complete(&fwork->done); // 完成 }
-
kthread_stop()
// 这个部分大概就是释放资源,通知该线程停止 int kthread_stop(struct task_struct *k) { struct kthread *kthread; int ret; trace_sched_kthread_stop(k); get_task_struct(k); kthread = to_kthread(k); // 可以看到这个设置了 KTHREAD_SHOULD_STOP 这个变量,刚好对应 // kthread_run() 这个函数里面检测,线程是否结束的标志 set_bit(KTHREAD_SHOULD_STOP, &kthread->flags); kthread_unpark(k); wake_up_process(k); wait_for_completion(&kthread->exited); ret = k->exit_code; put_task_struct(k); trace_sched_kthread_stop_ret(ret); return ret; }
使用的大概流程
-
先初始化 worker(工人)这个结构体调用 kthread_init_worker()
-
在open函数中,创建一个线程 调用kthread_run()
-
在write 函数中,加入一个work(工作),kthread_init_work();
kthread_queue_work();
-
在release函数中,停止该线程 kthread_flush_worker()
kthread_stop
-
用这几个函数就完全够用了。
总结
- 大概就是 创建一个线程,把数据处理的工作,放在线程中,这样应用程序,就不会太阻塞的太严重,从而影响应用程序。
- … 以后想起来 再补充,其实我想画图呢。。 太懒了。。