linux進程調度 - 組織進程

1. 進程的運行狀態


進程的運行狀態有下面這麼幾種:

進程運行狀態 描述
TASK_RUNNING 運行的進程被設置
TASK_INTERRUPTIBLE 進程被掛起,除非有硬件中斷或者信號傳遞來喚醒進程
TASK_UNINTERRUPTIBLE 與可中斷類似,但是信號不能喚醒進程
TASK_STOPPED 進程的執行被暫停

進程被創建後,會被設置各種運行狀態,然後會被不同方式進行處理,分下面兩種情況

1.1 可運行進程處置方式

TASK_RUNNING狀態的進程會被掛入一個運行隊列的鏈表中,然後被調度程序來選擇合適的時機進行調度運行,這個運行隊列鏈表是runqueues,會在以後的文章中介紹

1.2 不運行進程處置方式

當要把其他狀態的進程分組時,不同的狀態要求不同的處理,對於處於暫停、僵死、死亡狀態的進程沒有專門的鏈表來組織它們,對於處於TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE狀態的進程會把它們加入不同的等待隊列中,等待合適的條件來把他們轉化爲TASK_RUNNING狀態並加入運行隊列,然後被調度執行。

2. 進程等待隊列


等待隊列在內核中有很多用途,比如在中斷處理、進程同步及定時等,等待隊列由雙向鏈表實現,每個等待隊列都有一個等待隊列頭(wait queue head),數據結構如下所示:

typedef struct __wait_queue_head wait_queue_head_t;

struct __wait_queue_head {
    spinlock_t      lock;
    struct list_head    task_list;-----------等待隊列鏈表頭,隊列元素都加入此list
};

每個等待隊列頭中後期都會加入進程,這些進程由**隊列元素**wait_queue_t來包裝。

typedef struct __wait_queue wait_queue_t;

struct __wait_queue {
    unsigned int        flags;---------------區分互斥進程(1)和非互斥進程(0void            *private;----------------一般是task_struct
    wait_queue_func_t   func;----------------喚醒此隊列元素的函數
    struct list_head    task_list;-----------list
};

一般來說互斥進程會加入到隊尾,非互斥進程在隊列頭

2.1 初始化等待隊列頭

定義的每個隊列元素,都會加入一個已經初始化好的等待隊列頭中,初始化wait_queue_head_t的宏定義爲DECLARE_WAIT_QUEUE_HEAD(name),如下所示:

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

2.2 初始化隊列元素

初始化一個wait_queue_t的宏定義爲#define DECLARE_WAITQUEUE(name, tsk),第二個參數是進程描述符,如下所示:

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

另外還有一些函數能初始化wait_queue_t,如下所示:

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)
{
    q->flags    = 0;
    q->private  = p;
    q->func     = default_wake_function;----------------內核默認的一個喚醒函數
}

static inline void
init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func)
{-------------------------------------------------------和進程無關的隊列元素
    q->flags    = 0;
    q->private  = NULL;
    q->func     = func;
}

當然也可以自定義函數來初始化wait_queue_t

3. 把進程加入等待隊列


等待隊列頭和隊列元素初始化後,就可以把前者加入後者中了,相應會有多個函數能做這項任務

  1. 非互斥進程調用的函數

(kernel/sched/wait.c : wait.h)

extern void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
上面的函數最後會調用函數__add_wait_queue
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
    list_add(&new->task_list, &head->task_list);
}
  1. 互斥進程調用的函數:

(kernel/sched/wait.c : wait.h)

extern void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait);
上面的函數最後會調用函數__add_wait_queue_tail
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);
}

4. 喚醒等待隊列中的進程


喚醒等待隊列中的元素有一些宏定義:

#define wake_up(x)          __wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_nr(x, nr)       __wake_up(x, TASK_NORMAL, nr, NULL)
#define wake_up_all(x)          __wake_up(x, TASK_NORMAL, 0, NULL)
#define wake_up_locked(x)       __wake_up_locked((x), TASK_NORMAL, 1)
#define wake_up_all_locked(x)       __wake_up_locked((x), TASK_NORMAL, 0)

#define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
#define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
#define wake_up_interruptible_all(x)    __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
#define wake_up_interruptible_sync(x)   __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)

最終調用到函數__wake_up -> __wake_up_common

static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
            int nr_exclusive, int wake_flags, void *key)
{
    wait_queue_t *curr, *next;

    list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
        unsigned flags = curr->flags;

        if (curr->func(curr, mode, wake_flags, key) &&
                (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
            break;
    }
}

遍歷鏈表中的每項隊列元素,執行其func函數,默認的一個喚醒函數是default_wake_function

int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags,
              void *key)
{
    return try_to_wake_up(curr->private, mode, wake_flags);
}

change log


date content linux kernel
2016.11.20 原始寫作 linux4.6.3
發佈了96 篇原創文章 · 獲贊 33 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章