1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        一文讀懂 | Linux延時(shí)隊(duì)列工作原理

        共 3068字,需瀏覽 7分鐘

         ·

        2021-10-22 01:53

        當(dāng)進(jìn)程要獲取某些資源(例如從網(wǎng)卡讀取數(shù)據(jù))的時(shí)候,但資源并沒有準(zhǔn)備好(例如網(wǎng)卡還沒接收到數(shù)據(jù)),這時(shí)候內(nèi)核必須切換到其他進(jìn)程運(yùn)行,直到資源準(zhǔn)備好再喚醒進(jìn)程。

        waitqueue (等待隊(duì)列)?就是內(nèi)核用于管理等待資源的進(jìn)程,當(dāng)某個(gè)進(jìn)程獲取的資源沒有準(zhǔn)備好的時(shí)候,可以通過調(diào)用?add_wait_queue()?函數(shù)把進(jìn)程添加到?waitqueue?中,然后切換到其他進(jìn)程繼續(xù)執(zhí)行。當(dāng)資源準(zhǔn)備好,由資源提供方通過調(diào)用?wake_up()?函數(shù)來喚醒等待的進(jìn)程。

        等待隊(duì)列初始化

        要使用?waitqueue?首先需要聲明一個(gè)?wait_queue_head_t?結(jié)構(gòu)的變量,wait_queue_head_t?結(jié)構(gòu)定義如下:

        struct?__wait_queue_head?{
        ????spinlock_t?lock;
        ????struct?list_head?task_list;
        };

        waitqueue?本質(zhì)上是一個(gè)鏈表,而?wait_queue_head_t?結(jié)構(gòu)是?waitqueue?的頭部,lock?字段用于保護(hù)等待隊(duì)列在多核環(huán)境下數(shù)據(jù)被破壞,而?task_list?字段用于保存等待資源的進(jìn)程列表。

        可以通過調(diào)用?init_waitqueue_head()?函數(shù)來初始化?wait_queue_head_t?結(jié)構(gòu),其實(shí)現(xiàn)如下:

        void?init_waitqueue_head(wait_queue_head_t?*q)
        {
        ????spin_lock_init(&q->lock);
        ????INIT_LIST_HEAD(&q->task_list);
        }

        初始化過程很簡單,首先調(diào)用?spin_lock_init()?來初始化自旋鎖?lock,然后調(diào)用?INIT_LIST_HEAD()?來初始化進(jìn)程鏈表。

        向等待隊(duì)列添加等待進(jìn)程

        要向?waitqueue?添加等待進(jìn)程,首先要聲明一個(gè)?wait_queue_t?結(jié)構(gòu)的變量,wait_queue_t?結(jié)構(gòu)定義如下:

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

        struct?__wait_queue?{
        ????unsigned?int?flags;
        ????void?*private;
        ????wait_queue_func_t?func;
        ????struct?list_head?task_list;
        };

        下面說明一下各個(gè)成員的作用:

        1. flags: 可以設(shè)置為?WQ_FLAG_EXCLUSIVE,表示等待的進(jìn)程應(yīng)該獨(dú)占資源(解決驚群現(xiàn)象)。
        2. private: 一般用于保存等待進(jìn)程的進(jìn)程描述符?task_struct。
        3. func: 喚醒函數(shù),一般設(shè)置為?default_wake_function()?函數(shù),當(dāng)然也可以設(shè)置為自定義的喚醒函數(shù)。
        4. task_list: 用于連接其他等待資源的進(jìn)程。

        可以通過調(diào)用?init_waitqueue_entry()?函數(shù)來初始化?wait_queue_t?結(jié)構(gòu)變量,其實(shí)現(xiàn)如下:

        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;
        }

        也可以通過調(diào)用?init_waitqueue_func_entry()?函數(shù)來初始化為自定義的喚醒函數(shù):

        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?結(jié)構(gòu)變量后,可以通過調(diào)用?add_wait_queue()?函數(shù)把等待進(jìn)程添加到等待隊(duì)列,其實(shí)現(xiàn)如下:

        void?add_wait_queue(wait_queue_head_t?*q,?wait_queue_t?*wait)
        {
        ????unsigned?long?flags;

        ????wait->flags?&=?~WQ_FLAG_EXCLUSIVE;
        ????spin_lock_irqsave(&q->lock,?flags);
        ????__add_wait_queue(q,?wait);
        ????spin_unlock_irqrestore(&q->lock,?flags);
        }

        static?inline?void?__add_wait_queue(wait_queue_head_t?*head,?wait_queue_t?*new)
        {
        ????list_add(&new->task_list,?&head->task_list);
        }

        add_wait_queue()?函數(shù)的實(shí)現(xiàn)很簡單,首先通過調(diào)用?spin_lock_irqsave()?上鎖,然后調(diào)用?list_add()?函數(shù)把節(jié)點(diǎn)添加到等待隊(duì)列即可。

        wait_queue_head_t?結(jié)構(gòu)與?wait_queue_t?結(jié)構(gòu)之間的關(guān)系如下圖:

        waitqueue

        休眠等待進(jìn)程

        當(dāng)把進(jìn)程添加到等待隊(duì)列后,就可以休眠當(dāng)前進(jìn)程,讓出CPU給其他進(jìn)程運(yùn)行,要休眠進(jìn)程可以通過一下方式:

        set_current_state(TASK_INTERRUPTIBLE);
        schedule();

        代碼?set_current_state(TASK_INTERRUPTIBLE)?可以把當(dāng)前進(jìn)程運(yùn)行狀態(tài)設(shè)置為?可中斷休眠?狀態(tài),調(diào)用?schedule()?函數(shù)可以使當(dāng)前進(jìn)程讓出CPU,切換到其他進(jìn)程執(zhí)行。

        喚醒等待隊(duì)列

        當(dāng)資源準(zhǔn)備好后,就可以喚醒等待隊(duì)列中的進(jìn)程,可以通過?wake_up()?函數(shù)來喚醒等待隊(duì)列中的進(jìn)程。wake_up()?最終會(huì)調(diào)用?__wake_up_common(),其實(shí)現(xiàn)如下:

        static?void?__wake_up_common(wait_queue_head_t?*q,?
        ????unsigned?int?mode,?int?nr_exclusive,?int?sync,?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,?sync,?key)?&&
        ????????????????(flags?&?WQ_FLAG_EXCLUSIVE)?&&?!--nr_exclusive)
        ????????????break;
        ????}
        }

        可以看出,喚醒等待隊(duì)列就是變量等待隊(duì)列的等待進(jìn)程,然后調(diào)用喚醒函數(shù)來喚醒它們。


        瀏覽 31
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            日本护士HD高清18禁成人片 | 60分钟一级毛片免费看 | 骚逼www | 很黄很黄的曰逼视频 | 新疆女人1级毛片 | 天天看A| 亚洲国产精品99久久久久久久 | 欧洲三级片 | 亚洲视频免费在线 | 人人妻人人澡人人爽人人DVD |