149 彻底掌握kthread_worker队列化机制

一、init_kthread_worker()宏

include/linux/kthread.h

初始化 kthread_worker 结构体

#define init_kthread_worker(worker)					\
	do {								\
		static struct lock_class_key __key;			\
		__init_kthread_worker((worker), "("#worker")->lock", &__key); \
	} while (0)

1、__init_kthread_worker()函数

include/linux/kthread.h

void __init_kthread_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_worker_fn()函数

kernel/kthread.c

int kthread_worker_fn(void *worker_ptr)
{
	// 指针类型转化
	struct kthread_worker *worker = worker_ptr;
	struct kthread_work *work;
	...
	// current是内核的一个全局变量,专门用来表示当前运行的进程或者线程
	// 当某个线程运行这个函数时,线程会被保存到task指针
	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);
		// task原来是指向当前线程,现在要停止运行了,指针设置为指向空
		worker->task = NULL;
		spin_unlock_irq(&worker->lock);
		return 0;
	}
	// 若线程没有结束运行,则接着执行下面的代码
	// struct kthread_work
	work = NULL;
	spin_lock_irq(&worker->lock);
	// 遍历工人的work_list成员
	if (!list_empty(&worker->work_list)) {
		// 根据链表结点获取具体工作结构体 kthread_work
		work = list_first_entry(&worker->work_list,
					struct kthread_work, node);
		// 将work中的链表结点从worker的链表里面删除
		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();
	// 其实是个死循环,每一次循环都会有一个链表节点对应的node被提取出来
	goto repeat;
}

1、kthread_should_stop()函数

kernel/kthread.c

bool kthread_should_stop(void)
{
	// 判断当前线程的标志位
	return test_bit(KTHREAD_SHOULD_STOP, &to_kthread(current)->flags);
}
  • 调用kthread_stop()函数后,设置线程flags为KTHREAD_SHOULD_STOP

二、init_kthread_work()函数

include/linux/kthread.h

初始化kthread_work,为工作变量结构体指定一个具体的函数fn

#define kinit_thread_work(work, fn)					\
	do {								\//初始化work成员变量
		memset((work), 0, sizeof(struct kthread_work));		\ 
		INIT_LIST_HEAD(&(work)->node);				\//初始化work中的链表节点
		(work)->func = (fn);					\//fn就是我们自定义的
	} while (0)

三、kthread_queue_work()函数

kernel/kthread.c
把具体的工作交付给worker
其实就是把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;
}

1、queuing_blocked()函数

kernel/kthread.c

static inline bool queuing_blocked(struct kthread_worker *worker,
				   struct kthread_work *work)
{
	lockdep_assert_held(&worker->lock);
	// 关键,判断节点是否为空,不为空表示已经挂载到worker,返回后不需要再挂载
	// work->canceling非0表示要退出,返回后也不需要挂载了
	return !list_empty(&work->node) || work->canceling;
}

2、kthread_insert_work()函数

kernel/kthread.c
work到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);
	// work对应的结点串到worker的结点链表中
	list_add_tail(&work->node, pos);
	work->worker = worker;
	// current_work 表示当前worker正在执行的具体工作,为0表示还没开始工作
	if (!worker->current_work && likely(worker->task))
		wake_up_process(worker->task);// 唤醒worker开始工作,通过参数来唤醒,所以上面要先判空
}
// 然后具体工作等待被worker处理

四、kthread_flush_worker()函数

kernel/kthread.c

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),
	};
	// 两件事:work中的node加入,唤醒worker工作
	// 其实相当于在worker的链表最末尾新加了一个链表节点,这个具体工作最后被处理(就是最后执行kthread_flush_work_fn)
	kthread_queue_work(worker, &fwork.work);
	// 参数类型为 struct completion 完成量
	// 进程进入休眠状态
	// 会在 kthread_flush_work_fn 中唤醒此完成量,其实就是执行到最后kthread_work对应的函数就是这个,那么前面的都执行完毕了
	wait_for_completion(&fwork.done);
}

1、KTHREAD_WORK_INIT()宏

#define KTHREAD_WORK_INIT(work, fn)	{				\
	.node = LIST_HEAD_INIT((work).node),				\
	.func = (fn),							\
	}

2、COMPLETION_INITIALIZER_ONSTACK()宏

include/linux/completion.h

// 初始化完成量,初始化完赋值给 kthread_flush_work对应的done成员
(*({ init_completion(&work); &work; }))

3、kthread_flush_work_fn()函数

kernel/kthread.c

static void kthread_flush_work_fn(struct kthread_work *work)
{
	struct kthread_flush_work *fwork =
		container_of(work, struct kthread_flush_work, work);
	// 唤醒阻塞在kthread_flush_worker的线程
	complete(&fwork->done);
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值