内核 kthread_worker 和 kthread_work 机制

目录

1、数据结构

2、使用方式

2.1、准备 kthread_worker

2.2、准备 kthread_work

2.3、启动 work

2.4、flush 指定 worker 上所有 work

2.5、停止当前的线程

3、实现源码

3.1、kthread_init_worker

3.2、执行线程 kthread_worker_fn

3.3、kthread_init_work

3.4、kthread_queue_work

4、小结


 

为什么会想到看这部分的工作机制呢,正好在看最新版本 Linux Spi 驱动的时候,在发送数据的时候用到了这个东东,所以这里需要先来分析一下这个 kthread_workerkthread_work 机制。

本质上来说他们是一种内核线程使用和管理的方式,类似 work_struct 和 workqueue_struct 机制。我们依然从结构体、使用方式,以及具体实现,几个角度来进行分析。

 

1、数据结构

kthread_workerkthread_work 的数据结构定义在:include/linux/kthread.h

struct kthread_worker {
	unsigned int		flags;
	spinlock_t		lock;      //保护work_list链表的自旋锁
	struct list_head	work_list;    //kthread_work 链表,相当于流水线
	struct list_head	delayed_work_list;   // 延时工作 list
	struct task_struct	*task;      //为该kthread_worker执行任务的线程对应的task_struct结构
	struct kthread_work	*current_work;    //当前正在处理的kthread_work
};

struct kthread_work {
    struct list_head    node;  //kthread_work链表的链表元素
    kthread_work_func_t    func;  //执行函数,该kthread_work所要做的事情
    struct kthread_worker    *worker;  //处理该kthread_work的kthread_worker
    /* Number of canceling calls that are running at the moment. */
    int			canceling;
};

根据结构体定义,不难看出他们之间的关系:

 

2、使用方式

看得出来 kthread_worker 是大哥,接下来看看怎么使用这两个东西。

2.1、准备 kthread_worker

既然 kthread_worker 是大哥,那么要让他转起来,肯定需要先定义 kthread_worker 并初始化它:

struct kthread_worker hi_worker; // 声明一个kthread_worker
kthread_init_worker(&hi_worker); // 初始化kthread_worker

初始化完成后,为 kthread_worker 创建一个内核线程来处理 work.:

struct task_struct *kworker_task = kthread_run(kthread_worker_fn, &hi_worker, "nvme%d", dev->instance);

注意,这里传入的 kthread_worker_fn 函数是固定的。

 

2.2、准备 kthread_work

定义一个 kthread_work 并初始化他,为他指定工作函数 xxx_work_fn:

struct kthread_work hi_work;//声明一个kthread_work
kthread_init_work(&hi_work, xxx_work_fn); //初始化kthread_work,设置work执行函数.

2.3、启动 work

上面两步准备好了 worker 和 work,如果有需要处理的 work 的话,那么需要把这个 work 挂到 worker 上:

kthread_queue_work(&hi_worker, &hi_work);

2.4、flush 指定 worker 上所有 work

将指定的 worker 上的所有 work 全部执行

kthread_flush_worker(&hi_worker);

2.5、停止当前的线程

kthread_stop(kworker_task);

 

3、实现源码

上面列举了结构和基本用法,接着咱们来欣赏源码。照样,一步一步来看

3.1、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_init_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);
}
EXPORT_SYMBOL_GPL(__kthread_init_worker);

其实就是初始化他的成员变量为 0, 然后就他的 work_list 和 delayed_work_list 初始化;

 

3.2、执行线程 kthread_worker_fn

定义并初始化完 kthread_worker 后,就要调用前面说的 thread_run 一个 kthread_worker_fn 函数,传入的参数是 kthread_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);
		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_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) {
		__set_current_state(TASK_RUNNING);
		work->func(work);
	} else if (!freezing(current))
		schedule();

	try_to_freeze();
	cond_resched();
	goto repeat;
}
EXPORT_SYMBOL_GPL(kthread_worker_fn);

代码的逻辑比较简单:

1、首先是赋值了传入的 worker->task 为 current,当前进程,设置状态为 TASK_INTERRUPTIBLE;

2、Check 标志位,看是否需要关闭这个 kthread_worker_fn 内核线程,如果需要关闭,则进程状态,并清空 worker 下对应的 work

3、判断当前的 worker 的 work_list 上是否为空,如果非空,那么取出它,设置成为 worker->current_work,即当前的 work

4、执行取出的 work->func 

5、如果 worker 的 work_list 上为空,也就是没有任务,那么就会调用 schedule(),这个 kthread_worker_fn 执行线程进入睡眠。

6、如果没有退出,则 goto repeat,继续执行

 

3.3、kthread_init_work

kthread_init_work 用于初始化一个已经定义了的 kthread_work:

#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_work 类型的 work,并将他的链表元素初始化,最后将 work 的执行函数的指针挂接上去

 

3.4、kthread_queue_work

这个函数用于将已经完成了初始化的 kthread_worker 和 kthread_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)) {
		kthread_insert_work(worker, work, &worker->work_list);
		ret = true;
	}
	spin_unlock_irqrestore(&worker->lock, flags);
	return ret;
}
EXPORT_SYMBOL_GPL(kthread_queue_work);

进而调用到 kthread_insert_work

/* insert @work before @pos in @worker */
static void kthread_insert_work(struct kthread_worker *worker,
				struct kthread_work *work,
				struct list_head *pos)
{
	kthread_insert_work_sanity_check(worker, work);

	list_add_tail(&work->node, pos);
	work->worker = worker;
	if (!worker->current_work && likely(worker->task))
		wake_up_process(worker->task);
}

将 work->node 添加进入 worker->work_list,并指定了这个 work 所属的 worker,最后唤醒那个沉睡了的 worker->task,也就是 kthread_worker_fn 执行函数,此刻 worker 上的 work 上的 work->func 得以执行!

 

4、小结

Linux Kernel 提供的 kthread_worker 和 kthread_work 机制,主要是定义了一种方便使用和管理内核线程的一种手段,依托 kthread_worker,内核的其他模块开发者(比如驱动开发)能够简单的实现内核线程的高效使用。

 

  • 7
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
kthread_park和kthread_init_worker都是Linux内核提供的用于创建内核线程的函数,但它们的功能和用法不同。 kthread_park是一个较新的函数,它可以创建一个内核线程,并使其处于休眠状态,直到有任务需要执行时再被唤醒。kthread_park的函数原型如下: ``` struct task_struct *kthread_park(struct kthread_park_info *park); ``` 其中,kthread_park_info结构体定义了休眠等待的条件和超时时间等信息。kthread_park会返回一个指向task_struct结构体的指针,该结构体描述了内核线程的各种属性。 kthread_park适用于需要等待事件发生的场景,比如等待硬件中断、等待网络数据等。使用kthread_park创建的线程会自动进入休眠状态,不会占用CPU资源,等待唤醒后才会再次执行。 kthread_init_worker是一个较早的函数,它创建的内核线程主要用于执行一些后台任务,比如文件系统的后台清理、网络数据包的处理等。kthread_init_worker的函数原型如下: ``` struct task_struct *kthread_init_worker(int (*func)(void *data), void *data, const char *namefmt, ...); ``` 其中,func是内核线程的入口函数,data是传递给线程的参数,namefmt是线程的名称。kthread_init_worker会返回一个指向task_struct结构体的指针,该结构体描述了内核线程的各种属性。 kthread_init_worker创建的线程会立即执行,不会进入休眠状态。它通常用于执行一些需要在后台运行的任务,可以通过多线程编程技术来实现并发执行。 需要注意的是,kthread_park和kthread_init_worker都需要在内核中使用,不能直接在用户空间中调用。使用这两个函数创建内核线程时,需要注意线程同步和互斥的问题,比如使用信号量、互斥锁等机制来保护共享资源,避免竞态条件等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值