linux内核部件分析(十一)——waitqueue与线程的阻塞

    当你必须一个复杂的系统,协调系统的方方面面,灵活地支持各种机制和策略,即使很简单的问题也会变得很复杂。linux绝对就是这样一个复杂的系统。所以我们要理解它,尽量从原理的角度去理解事务的处理流程,尽量避免各种细枝末节的干扰,尽量规避那些足以压垮自己的庞然大物。(尽管细致末节和庞然大物很可能就是linux闪光的地方,但我们还是小心为上。)

原理

    现在我们来考虑linux中线程的阻塞。它的原理很简单。我们有一个要阻塞的线程A和要唤醒它的线程B(当然也可以是中断处理例程ISR),有一个两者共知的等待队列Q(也许这个等待队列属于一个信号量什么的)。首先是线程A阻塞,要加入等待队列Q,需要先申请一个队列节点N,节点N中包含指向线程A的线程控制块(TCB)的指针,然后A就可以将自己的线程状态设为阻塞,并调用schedule()将自己踢出CPU的就绪队列。过了一定时间,线程B想要唤醒等待队列Q中的线程,它只需要获得线程A的TCB指针,将线程A状态设为就绪即可。等线程A恢复运行,将节点N退出等待队列Q,完成整个从阻塞到恢复的流程。

    原理讲起来总是没意思的,下面我们还是看代码吧。我们规避复杂的任务状态转换和调度的内容,即使对等待队列的分析,也是按照从基础到扩展的顺序。代码出现在三个地方:include/linux/wait.h , kernel/wait.c, kernel/sched.c。不用说wait.h是头文件,wait.c是实现的地方,而sched.c则体现了waitqueue的一种应用(实现completion)。为了更好地分析completion,我们还需要include/linux/completion.h。


waitqueue实现

我们仍然先看数据结构。

struct __wait_queue_head {
	spinlock_t lock;
	struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);

int default_wake_function(wait_queue_t *wait, unsigned mode, int flags, void *key);

struct __wait_queue {
	unsigned int flags;
#define WQ_FLAG_EXCLUSIVE	0x01
	void *private;
	wait_queue_func_t func;
	struct list_head task_list;
};

typedef struct __wait_queue wait_queue_t;



其中,wait_queue_head_t 就是等待队列头,wait_queue_t 就是队列节点。

wait_queue_head_t 包括一个自旋锁lock,还有一个双向循环队列task_list,这在我们的预料之内。

wait_queue_t 则包括较多,我们先来剧透一下。

    flags变量只可能是0或者WQ_FLAG_EXCLUSIVE。flags标志只影响等待队列唤醒线程时的操作,置为WQ_FLAG_EXCLUSIVE则每次只允许唤醒一个线程,为0则无限制。

    private指针,其实就是指向TCB的指针。

    func是一个函数指针,指向用于唤醒队列中线程的函数。虽然提供了默认的唤醒函数default_wake_function,但也允许灵活的设置队列的唤醒函数。

    task_list是一个双向循环链表节点,用于链入等待队列的链表。


依照旧例,waitqueue在数据结构之后为我们提供了丰富的初始化函数。因为太多了,我们只好分段列出。

#define __WAITQUEUE_INITIALIZER(name, tsk) {				\
	.private	= tsk,						\
	.func		= default_wake_function,			\
	.task_list	= { NULL, NULL } }

#define DECLARE_WAITQUEUE(name, tsk)					\
	wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

#define __WAIT_QUEUE_HEAD_INITIALIZER(name) {				\
	.lock		= __SPIN_LOCK_UNLOCKED(name.lock),		\
	.task_list	= { &(name).task_list, &(name).task_list } }

#define DECLARE_WAIT_QUEUE_HEAD(name) \
	wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

这是用宏定义,在声明变量时进行的初始化。

void __init_waitqueue_head(wait_queue_head_t *q, struct lock_class_key *key)
{
	spin_lock_init(&q->lock);
	lockdep_set_class(&q->lock, key);
	INIT_LIST_HEAD(&q->task_list);
}

#define init_waitqueue_head(q)				\
	do {						\
		static struct lock_class_key __key;	\
							\
		__init_waitqueue_head((q), &__key);	\
	} while (0)

#ifde
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值