進(jìn)程優(yōu)先級隊列

更詳細(xì)的講解和代碼調(diào)試演示過程爱谁,請參看視頻
Linux kernel Hacker, 從零構(gòu)建自己的內(nèi)核

前幾節(jié)茅逮,我們實現(xiàn)了進(jìn)程的調(diào)度憔购,并給每個進(jìn)程賦予相應(yīng)優(yōu)先級焦匈,優(yōu)先級高的進(jìn)程赘淮,能夠得到更多的CPU時間辕录。但在演示時,我們發(fā)現(xiàn)一個問題梢卸,就是走诞,當(dāng)進(jìn)程發(fā)送切換時,鼠標(biāo)鍵盤的響應(yīng)會被卡死蛤高,這是因為響應(yīng)鼠標(biāo)鍵盤事件的進(jìn)程被調(diào)到后臺蚣旱,無法獲得CPU運(yùn)行時間,從而不能處理鼠標(biāo)或鍵盤事件戴陡。

這種情況對用戶來說塞绿,是不可接受的。對用戶輸入給予及時回應(yīng)恤批,直接關(guān)系到用戶體驗异吻。所以負(fù)責(zé)相應(yīng)用戶輸入的進(jìn)程,其重要性要比那些只需要在后臺運(yùn)行喜庞,不用和用戶直接交互的進(jìn)程高很多诀浪。由此,我們需要實現(xiàn)的是延都,只要用戶有輸入雷猪,那么負(fù)責(zé)處理用戶輸入的進(jìn)程就不能被打斷,為了實現(xiàn)這個目的窄潭,我們需要實現(xiàn)進(jìn)程的優(yōu)先級隊列春宣。

這里寫圖片描述

如上圖,我們把所有進(jìn)程根據(jù)level分成三個隊列嫉你,進(jìn)程越重要月帝,它對應(yīng)的level值越低,這樣幽污,當(dāng)進(jìn)行進(jìn)程調(diào)度時嚷辅,進(jìn)程調(diào)度器先查看level等于0的隊列是否還有進(jìn)程,有的話距误,只執(zhí)行該隊列的進(jìn)程簸搞,如果該隊列不止一個進(jìn)程扁位,那么每個進(jìn)程根據(jù)他們的優(yōu)先級獲得相應(yīng)的CPU時間。

如果levelw為0的隊列沒有可調(diào)度的進(jìn)程趁俊,那么level為1的隊列中的進(jìn)程才有獲得調(diào)度的機(jī)會域仇,以此類推。

由此寺擂,我們看看相關(guān)數(shù)據(jù)結(jié)構(gòu)的定義暇务,首先是multi_task.h:

struct TASK {
    int sel, flags;
    int priority;
    int level;
    struct TSS32 tss;
};

#define  MAX_TASKS  5
#define  MAX_TASKS_LV   3
#define  MAX_TASKLEVELS 3

#define  TASK_GDT0  7
#define  SIZE_OF_TASK  120

struct TASKLEVEL {
    int running;
    int now;
    struct TASK *tasks[MAX_TASKS_LV];
};

#define SIZE_OF_TASKLEVEL  (8+ 4*MAX_TASKS_LV)

struct TASKCTL {
    int  now_lv;
    int  lv_change;
    struct TASKLEVEL  level[MAX_TASKLEVELS];
    struct TASK tasks0[MAX_TASKS];
};

TASK結(jié)構(gòu)體增加了一個level變量,用于表明進(jìn)程的重要性怔软。TASKLEVEL對應(yīng)于上圖的進(jìn)程隊列垦细,相同重要性的進(jìn)程,都存儲在對應(yīng)TASKLEVEL的tasks數(shù)組中挡逼。

TASKCTL 也就是進(jìn)程管理器括改,其存儲的不再是進(jìn)程,而是進(jìn)程的優(yōu)先級隊列家坎,它要找到重要性最高的進(jìn)程隊列嘱能,從中取出進(jìn)程進(jìn)行調(diào)度。

multi_task.c 需要進(jìn)行相應(yīng)改動:

struct TASKCTL *get_taskctl() {
    return taskctl;
}

void init_task_level(int level) {
    taskctl->level[level].running = 0;
    taskctl->level[level].now = 0;
    int i;
    for (i = 0; i < MAX_TASKS_LV; i++) {
        taskctl->level[i].tasks[i] = 0;
    }
}

init_task_level 對進(jìn)程優(yōu)先級隊列進(jìn)行初始化虱疏,running表示改對了中有幾個進(jìn)程焰檩,now表示隊列中,哪個進(jìn)程正在被調(diào)度到前臺進(jìn)行運(yùn)行订框。

struct TASK  *task_init(struct MEMMAN *memman) {
    int  i;
    ....
    
    task = task_alloc();
    task->flags = 2;  //active
    task->priority = 100;
    task->level = 0;
    task_add(task);
    task_switchsub();
    ....
}

上面代碼用于初始化運(yùn)行CMain函數(shù)的進(jìn)程,它把CMain進(jìn)程的level設(shè)置為0兜叨,也就是該進(jìn)程的重要性最高穿扳,只要它不被掛起,那么它始終擁有被調(diào)度的權(quán)利国旷。task_add會根據(jù)進(jìn)程的重要性隆圆,將其加入對應(yīng)的優(yōu)先級隊列壕吹,一旦有新進(jìn)程加入,task_switchsub 則修改相應(yīng)調(diào)度信息,以便進(jìn)程調(diào)度器做出適當(dāng)調(diào)動荸百。

void task_run(struct TASK *task,int level, int priority) {
    if (level < 0) {
        level = task->level;
    }

    if (priority > 0) {
        task->priority = priority;
    }

    if (task->flags == 2 && task->level != level) {
        task_remove(task); //change task flags
    }

    if (task->flags != 2) {
        task->level = level;
        task_add(task);
    }

    taskctl->lv_change = 1;
    return;
} 

task_run 的作用是修改進(jìn)程重要性或優(yōu)先級,或者把新的進(jìn)程根據(jù)其重要性添加到相應(yīng)隊列看杭。如果進(jìn)程的重要性有改動慨绳,那么通過task_remove把進(jìn)程從原有的優(yōu)先級隊列中去除,然后再通過task_add將進(jìn)程添加到對應(yīng)的隊列被环。

void task_switch(void) {
    struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];
    struct TASK *new_task, *now_task = tl->tasks[tl->now];
    tl->now++;
    if (tl->now == tl->running) {
        tl->now = 0;
    }
 
    if (taskctl->lv_change != 0) {
        task_switchsub();
        tl = &taskctl->level[taskctl->now_lv];
    }

    new_task = tl->tasks[tl->now];
    timer_settime(task_timer, new_task->priority);
    if (new_task != now_task && new_task != 0) {
        farjmp(0, new_task->sel);
    }

    return;
}

task_switch 被時鐘中斷調(diào)用糙及,它的邏輯發(fā)送不小變化。taskctl->now_lv表示當(dāng)前正則被調(diào)度的優(yōu)先級隊列筛欢,例如浸锨,如果now_lv的值是0唇聘,那么表示當(dāng)前l(fā)evel等于0的隊列中的進(jìn)程才可以獲得被調(diào)度的機(jī)會。task_switch 通過taskctl->now_lv得知哪個進(jìn)程隊列應(yīng)該被調(diào)度柱搜,然后從該隊列中迟郎,取出task對象進(jìn)行執(zhí)行。如果有新的進(jìn)程加入聪蘸,或有進(jìn)程的重要性被改變了宪肖,那么taskctl->lv_change的值就不等于0。假設(shè)當(dāng)前獲得調(diào)度機(jī)會的是level值為1的隊列中的進(jìn)程宇姚,但是有l(wèi)evel等于0的進(jìn)程對象添加到了level等于0的隊列中匈庭,那么此時,調(diào)度算法就會停止從level為1的隊列中去調(diào)度進(jìn)程浑劳,而是切換到level為0的隊列阱持,從中獲取要調(diào)度的進(jìn)程。

int  task_sleep(struct TASK *task) {
   struct TASK *cur_task = 0;

   if (task->flags == 2) {
       cur_task = task_now();
       task_remove(task);
  
       if (task == cur_task) {
          task_switchsub();
          cur_task = task_now();
        
          if (cur_task != 0)
          {
              farjmp(0, cur_task->sel);
          }
       }
   }

   return 0;
}

struct TASK *task_now(void) {
    struct TASKLEVEL *tl = &taskctl->level[taskctl->now_lv];
    return tl->tasks[tl->now];
}

void task_add(struct TASK *task) {
    struct TASKLEVEL *tl = &taskctl->level[task->level];
    tl->tasks[tl->running] = task;
    tl->running++;
    task->flags = 2;
    return;
} 

task_sleep 用于刪除進(jìn)程魔熏,如果需要?dú)⒌裟硞€進(jìn)程衷咽,那么可以使用該函數(shù)剝奪指定進(jìn)程對象獲得調(diào)度的權(quán)利。task_remove負(fù)責(zé)把進(jìn)程從它所在的隊列中刪除蒜绽,如果當(dāng)前被掛起的進(jìn)程是正在運(yùn)行的進(jìn)程镶骗,那么task_sleep會選擇下一個合適的進(jìn)程進(jìn)行調(diào)度執(zhí)行。

task_now 用于返回當(dāng)前正在被調(diào)用的進(jìn)程對象躲雅,task_add把給定進(jìn)程加入對應(yīng)的優(yōu)先級隊列鼎姊。

void task_remove(struct TASK *task) {
    int i ;
    struct TASKLEVEL *tl = &taskctl->level[task->level];
    for (i = 0; i< tl->running; i++) {
        if (tl->tasks[i] == task) {
            tl->tasks[i] = 0;
            break;
        }
    }

    tl->running--;
    if (i < tl->now) {
        tl->now--;
    }

    if (tl->now >= tl->running) {
        tl->now = 0;
    } 

    task->flags = 1;

    for (; i < tl->running; i++) {
        tl->tasks[i] = tl->tasks[i+1];
    }

    return;
}

void task_switchsub(void) {
    int i;
    for (i = 0; i < MAX_TASKLEVELS; i++) {
        if (taskctl->level[i].running > 0) {
           break;
        }
    }

    taskctl->now_lv = i;
    taskctl->lv_change = 0;
}

task_remove主要負(fù)責(zé)把進(jìn)程對象從隊列中刪除,并修改相應(yīng)的隊列數(shù)據(jù)相赁。一旦有進(jìn)程刪除或添加相寇,那么進(jìn)程調(diào)度需要作出對應(yīng)的調(diào)整,task_switchsub的作用是根據(jù)進(jìn)程的添加或刪除钮科,修改進(jìn)程調(diào)度器的相應(yīng)信息唤衫,進(jìn)而改變進(jìn)程調(diào)度器的調(diào)度行為。

最后是主入口函數(shù)CMain也做相應(yīng)改動:

void CMain(void) {
    ....
    for (i = 0; i < 2; i++) {
       ....

       ....
       task_run(task_b[i], 1, (i+1)*5);
    }
    ....
}

主要到绵脯,我們把運(yùn)行兩個窗體的進(jìn)程其重要性設(shè)置成1佳励,也就是只要運(yùn)行主入口函數(shù)的進(jìn)程不掛起,那么運(yùn)行兩個窗體的進(jìn)程就不能得到執(zhí)行蛆挫。

本節(jié)代碼改動雖多赃承,但邏輯簡單,理解起來應(yīng)該不難悴侵,經(jīng)過上面的改動后楣导,系統(tǒng)運(yùn)行的情況如下:


這里寫圖片描述

從上圖看出,如果CMain進(jìn)程運(yùn)行時畜挨,上面兩個窗體原來的計數(shù)字符串沒有顯示筒繁,這是因為兩個窗體的進(jìn)程得不到調(diào)度的機(jī)會噩凹。由于CMain進(jìn)程負(fù)責(zé)相應(yīng)鼠標(biāo)鍵盤,因此毡咏,此時我們移動鼠標(biāo)就不再出現(xiàn)卡頓的情形驮宴。

當(dāng)CMain進(jìn)程被掛起后,兩個窗體的進(jìn)程獲得執(zhí)行機(jī)會呕缭,因而窗體中的計算字符串就出現(xiàn)了:


這里寫圖片描述

更多技術(shù)信息堵泽,包括操作系統(tǒng),編譯器恢总,面試算法迎罗,機(jī)器學(xué)習(xí),人工智能片仿,請關(guān)照我的公眾號:


這里寫圖片描述
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纹安,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子砂豌,更是在濱河造成了極大的恐慌厢岂,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阳距,死亡現(xiàn)場離奇詭異塔粒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)筐摘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門卒茬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人咖熟,你說我怎么就攤上這事扬虚。” “怎么了球恤?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荸镊。 經(jīng)常有香客問我咽斧,道長,這世上最難降的妖魔是什么躬存? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任张惹,我火速辦了婚禮,結(jié)果婚禮上岭洲,老公的妹妹穿的比我還像新娘宛逗。我一直安慰自己,他們只是感情好盾剩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布雷激。 她就那樣靜靜地躺著替蔬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屎暇。 梳的紋絲不亂的頭發(fā)上承桥,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機(jī)與錄音根悼,去河邊找鬼凶异。 笑死,一個胖子當(dāng)著我的面吹牛挤巡,可吹牛的內(nèi)容都是我干的剩彬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼矿卑,長吁一口氣:“原來是場噩夢啊……” “哼喉恋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起粪摘,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤瀑晒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后徘意,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苔悦,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年椎咧,在試婚紗的時候發(fā)現(xiàn)自己被綠了玖详。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡勤讽,死狀恐怖蟋座,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情脚牍,我是刑警寧澤向臀,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站诸狭,受9級特大地震影響券膀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驯遇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一芹彬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叉庐,春花似錦舒帮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肢执。三九已至,卻和暖如春瓦宜,著一層夾襖步出監(jiān)牢的瞬間蔚万,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工临庇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留反璃,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓假夺,卻偏偏與公主長得像淮蜈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子已卷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容