Linux-wait_queue/work_queue
首先,我們得明白,linux中的所有的進程都由task_struct這個結構管理。在產生進程的時候將會分配一個task_struct結構,之後將通過這個結構對進程進行管理。task_struct結構存在於平坦地址空間內,任何時候Linux核心都可以參照所有進程的所有管理情報。核心堆棧也同樣位於平坦地址空間內。(平坦 的意思是"獨立的連續區間")
下面是tesk_struct的主要成員:
struct task_struct {
struct files_struct* files; //檔案描述符
struct signal_struct* sig; //訊號控制signal handler
struct mm_struct* mm; //記憶體管理模組
long stat //進程狀態
struct list_head runlist; //用於連接RUN隊列
long priority; //基本優先權
long counter; //變動優先權
char comm[]; //命令名
struct thread_struct tss; //上下文儲存領域
...
};
我們現在只需瞭解它裡面的state就可以,state有下面幾種狀態:
狀態 說明
TASK_RUNNING 執行可能狀態
TASK_INTERRUPTIBLE 等待狀態。可接受訊號
TASK_UNINTERRUPTIBLE 等待狀態。不能接受訊號
TASK_ZOMBIE 殭屍狀態。exit後的狀態
TASK_STOPPED 延緩狀態
TASK_STACE 追蹤狀態
一. 等待隊列
等待隊列的使用分為如下兩部分:
(1)為使當前進程在一個等待隊列中睡眠,需要調用wait_event函數(或者某個等價函數),進程加入睡眠,將控制權釋放給調度器。
(2)在核心的另一處,必須調用wake_up函數(或某個等價函數)喚醒等待隊列中的睡眠進程。 一.1. 重要結構體:
1、等待隊列
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;
2、等待隊列頭
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
一.2. 定義、初始化等待隊列頭和等待隊列
1、struct wait_queue_head_t wq; /* global variable */
DECLARE_WAIT_QUEUE_HEAD (wq);
2、/*靜態聲明並初始化一個wait_queue_t 等待隊列*/
#define DEFINE_WAIT_FUNC(name, function) \
wait_queue_t name = { \
.private = current, \
.func = function, \
.task_list = LIST_HEAD_INIT((name).task_list), \
}
#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
/*動態分配一個wait_queue_t 執行個體*/
97 static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)
98 {
99 q->flags = 0;
100 q->private = p;
101 q->func = default_wake_function;
102 }
一.3. 進程睡眠
包括兩個層次,第一個層次是將一個等待隊列(wait_queue_t)(通常是某個進程)加入到等待隊列(wait_queue_head_t)鏈表中。第二個層次是將當前進程進入睡眠。
1、進程添加到某個等待隊列。
Kernel/wait.c
extern void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
extern void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait);
extern void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
其中add_wait_queue將等待隊列加入到等待隊列鏈表尾部(預設)中,並設定flag標誌~WQ_FLAG_EXCLUSIVE。
22 void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
23 {
24 unsigned long flags;
25
26 wait->flags &= ~WQ_FLAG_EXCLUSIVE;
27 spin_lock_irqsave(&q->lock, flags);
28 __add_wait_queue(q, wait); /*list_add(&new->task_list, &head->task_list);*/
29 spin_unlock_irqrestore(&q->lock, flags);
30 }
將等待隊列加入到等待隊列鏈表另外一種方法是prepare_to_wait。除了add_wait_queue所需要的參數外,還需要進程的狀態。
void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state);
void prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state);
void finish_wait(wait_queue_head_t *q, wait_queue_t *wait);
void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait,
unsigned int mode, void *key);
68 prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
69 {
70 unsigned long flags;
71
72 wait->flags &= ~WQ_FLAG_EXCLUSIVE;
73 spin_lock_irqsave(&q->lock, flags);
74 if (list_empty(&wait->task_list))
75 __add_wait_queue(q, wait);