在linux内核中进程的状态主要有几种状态:
1.运行态:即进程正在CPU上进行运行,它此刻正在占有CPU;
2.就绪态:即进程除了CPU之外,已经具备了运行的所有条件,在就绪队列中等待调度器(schedule)的调度;
3.阻塞态:即进程除了缺少CPU外,还缺少其他条件,在等待队列中等待所需要的条件;
介绍linux内核中的等待队列的组织结构以及各种对等待队列的操作:
等待队列由两部分构成:等待队列头 + 等待队列中的等待项;
等待队列头的结构:
struct __wait_queue_head
{
spinlock_t lock; // 自选锁,用于保护等待队列,防止并发访问
struct list_head task_list;//用于构建等待队列
}
typedef struct __wait_queue_head wait_queue_head_t;
等待队列中的等待项:
struct __wait_queue
{
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
}
每个字段的介绍:
flags : 用于判断当等待条件满足时,是唤醒等待队列中的等待该条件的所有
等待进程,还是只唤醒一个;
flags 的最后一位为 1 时表示每次只唤醒一个等待队列中的进程;
flags 的最后一位为 0 时表示每次唤醒等待该条件的所有进程;
#define WQ_FLAG_EXCLUSIVE 0x01
private: 用于存放 struct task_struct 的地址;
wait_queue_func_t :等待队列中的进程被唤醒的方式;
typedef int (*wait_queue_func_t)(wait_queue_t *wait,unsigned mode,
int flags, void *key)
等待的队列的结构图:
对等待队列的各种操作:
1.等待队列头的静态初始化:
#define __WAIT_QUEUE_INITIALIZER(name) { \
.lock = __SPIN_LOCK_UNLOCK(name.lock),// lock = 1; \
.task_list = {&(name).task_list, &(name).task_list} }
#define DECLARE WAIT_QUEUE_HEAD(name) \
wait_queue_head_t name = __WAIT_QUEUE_INITIALIZER(name)
所以可以直接通过 : DECLARE_WAIT_QUEUE_HEAD(name) 来完成一个等待队列头的定义以及初始化操作;
2. 等待队列头的动态初始化操作:
void __init_waitqueue_head(wait_queue_head_t *p, struct lock_class_key *key)
{
spin_lock_init(&p->lock); // 初始化spinlock_t lock lock = 1;
INIT_LIST_HEAD(&p->task_list);// 初始化链表头;
}
#define init_waitqueue_head(name) \
do { \
static struct lock_class_key __key; \
__init_wait_queue_head((name), &__key);\
} while(0)
所以如果使用 init_waitqueue_head()的用法为:
wait_queue_head_t name;
init_waitqueue_head(&name);
DECLARE_WAIT_QUEUE_HEAD() 与 init_waitqueue_head()的区别:
1.DECLARE_WAIT_QUEUE_HEAD完全时用宏定义实现的,因此程序在编译的时候就完成了变量的定义与初始化。而 init_waitqueue_head()本质上来说是对 __init_waitqueue_head()的一次封装。虽然使用了宏定义,其仍然是一个函数,它对变量的初始化是在程序运行的时候进行的;
3.对等待队列中的等待项进行静态定义与初始化:
#define __WAITQUEUE_INITIALIZER(name, task) { \
.private = task, \
.func = default_wake_function, \
.task_list = {NULL,NULL} }
#define DECLARE_WAITQUEUE(name,task) \
wait_queue_t name = __WAITQUQUE_INITIALIZER(name, task)
相当与:
wait_queue_t entry;
struct task_struct task;
entry = {
.private = &task,
.func = default_wake_function,
.taks_list = {NULL, NULL}
}
4.动态初始化等待队列中的数据项:
static inline void init_waitqueue_entry(wait_queue_t *p, struct task_struct *task)
{
p->flags = 0;
p->private = task;
p->func = default_wake_function;
}
用法:wait_queue_t wait;
struct task_struct task;
init_waitqueue_entry(&wait, &task);
5.使用自定义的函数作为等待队列中的进程唤醒做处理:
static inline void init_waitqueue_func_entry(wait_queue_t *wait,
wait_queue_func_t func)
{
p->flags = 0;
p->private = NULL;
p->func = func;
}
6.判断一个等待队列是否可用:
static inline int waitqueue_active(wait_queue_head_t *p)
{
return !list_empty(&p->task_list);
}
waitqueue_active()本质上实在判断一个等待队列是否为空。
如果为空的话,等待队列不可用返回 0;
如果不为空,等待队列可用返回 1;
7.向等待队列中添加一个数据项:
static inline void __add_wait_queue(wait_queue_head_t *head,
wait_queue_t *new)
{
list_add(&new->task_list, &head->task_list);
}
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned int flags ;
wait->flags &= ~WQ_FLAG_EXCLUSIVE; // define WQ_FLAG_EXCLUSIVE 0x01
spin_lock_irqsave(&q->lock, flags);
__add_wait_queue(q, wait);
spin_unlock_irqstore(&q->lock, flags);
}
所以 add_wait_queue()是__add_wait_queue()的封装,并且加入了一些保护等待队列防止并发访问的措施, 所以在向等待队列中添加元素时使用 add_wait_queue()会更安全;
8.向等待队列中添加一个每次只能唤醒一个数据项:
static inline void __add_wait_queue_tail(wait_queue_head_t *head,
wait_queue_t *new)
{
list_add_tail(&new->task_list, &head->task_list);
}
void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned int flags ;
wait->flags |= WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
__add_wait_queue_tail(q,wait);
spin_unlock_irqstore(&q->lock, flags);
}
可以看出 add_wait_queue(wait_queue_head_t *head, wait_queue_t *wait)
每次向等待队列的头后面添加一个非互斥唤醒的等待项;
add_wait_queue_exclusive(wait_queue_head_t *head, wait_queue_t *wait)
每次向等待队列的尾部添加一个互斥唤醒的等待项;
9.将一个等待项从等待队列中删除:
static inline void __remove_wait_queue(wait_queue_head_t *head,
wait_queue_t *wait)
{
list_del(&wait->task_list);
}
void remove_wait_queue(wait_queue_head_t *head, wait_queue_t *wait)
{
unsigned int flags;
spin_lock_irqsave(&head->lock, flags);
__remove_wait_queue(head,wait);
spin_unlock_irqstore(&head->lock, flags);
}
小总结: add_wait_queue(); EXPORT_SYMBOL(add_wait_queue);
add_wait_queue_exclusive(); EXPORT_SYMBOL(add_wait_queue_exclusive);
remove_wait_queue(); EXPORT_SYMBOL(remove_wait_queue);
这三个对等待队列的操作是内核提过的函数,
内核中的等待队列中的进程是需要在一定条件下给唤醒的,然后加入到就绪队列中的。以及正在运行的进程如何将其加入到等待队列之中的。
内核也提供了一些函数,帮助我们将进程正在运行的进程阻塞,让后将其加入到等待队列中。以及在等待队列中唤醒唤醒阻塞进程。
阻塞正在运行的进程,然后将其加入到等待队列中的函数:
1. void prepare_to_wait(wait_queue_head_t *head, wait_queue_t *wait, int state)
state :表示进程的状态
取值 TASK_RUNNING, TASK_INTERRUPTIBLE, TASK_UNINTERRUPTIBLE
void prepare_to_wait(wait_queue_head_t *head, wait_queue_t *wait, int state)
{
unsigned int flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE; // 非互斥唤醒进程;
spin_lock_irqsave(&head->lock, flags);
if( list_empty(&wait->task_list) ) // 如果为空,说明其之前并不在等待对列中
__add_wait_queue(head, wait); // 将wait entry 加入到等待队列之中
set_current_state(state); // 使用 set_current_state()改变进程的运行状态
spin_unlock_irqstore(&head->lock, flags);
}
EXPORT_SYMBOL(prepare_to_wait);
void prepare_to_wait_exclusive(wait_queue_head_t *head,
wait_queue_head *wait, int state)
{
unsigned int flags;
wait->flags |= WQ_FLAG_EXCLUSIVE; // 互斥唤醒进程;
spin_lock_irqsave(&head->lock, flags);
if( list_empty(&wait->task_list) )
__add_wait_queue_tail(head, wait); //添加对尾
set_current_state(state);
spin_unlock_irqstore(&head->lock, flags);
}
EXPORT_SYMBOL(prepare_to_wait_exclusive);
从阻塞队列中删除一个等待进程的函数:
void finish_wait(wait_queue_head_t *head, wait_queue_t *wait)
{
unsigned int flags;
__set_current_state(TASK_RUNNING); //将当前的进程状态改为运行态;
if( !list_empty_careful(&wait->task_list) ) // 确定进程在等待队列之中
{
spin_lock_irqsave(&head->lock, flags);
list_del_init(&wait->task_list);
spin_unlock_irqstore(&head->lock, flags);
}
}
EXPORT_SYMBOL(finish_wait);