java開發系統內核:進程的掛起和恢復

更詳細的講解和代碼調試演示過程,請參看視頻
Linux kernel Hacker, 從零構建本身的內核vue

有了進程的自動調度後,接下來的任務在於,如何將空閒進程掛起,空閒進程每每是那些沒有具體任務須要處理的進程,所以,若是繼續讓其運行的話,那麼必然會耗費寶貴的CPU資源,若是能讓它先掛起,等到它須要執行具體任務時,再把它調度到前臺,那纔是一種合理的進程管理機制。數組

咱們實現的進程調度,是依賴於進程控制器,也就是taskctl中的任務數組來實現的,當咱們想要啓動某個進程時,在該數組中找到對應的任務對象,而後把它加載到CPU那就能夠了。微信

同理,若是要讓某個進程休眠,那麼只要把它對應的TASK對象從任務數組中移除,那麼它天然就不會獲得調度的機會。markdown

所以,修改multi_task.c 增長一個task_sleep函數:數據結構

void task_sleep(struct TASK *task) {
   int  i;
   char ts = 0;
   if (task->flags == 2) {
        if (task == taskctl->tasks[taskctl->now]) {
            ts = 1;
        }

    for (i = 0; i < taskctl->running; i++) {
        //在任務數組中找到要掛起的進程對象
        if (taskctl->tasks[i] == task) {
            break;
        }
    }

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

    for(; i < taskctl->running; i++) {
        //經過把後面的任務往前覆蓋,實現將當前任務從任務列表中移除的目的
        taskctl->tasks[i] = taskctl->tasks[i+1];
    }

    task->flags = 1;
    if (ts != 0) {
        //若是當前掛起的任務正好是當前正在前臺運行的任務,那麼將第0個任務調度到前臺
        if (taskctl->now >= taskctl->running) {
            taskctl->now = 0;
        }

       farjmp(0, taskctl->tasks[taskctl->now]->sel);
    }

   }

   return;
}

該函數的邏輯是,根據要掛起的任務,在整個任務數組中查找,找到其對應的數組下標,而後把後面的任務向前覆蓋,這樣的話,要移除的任務就在數組中就會被覆蓋掉,從而實現將任務從數組中移除的目的。app

須要注意的是,若是要掛起的任務,正好是當前正在前臺運行的進程,那麼ts==1,咱們就把下標爲0的任務調度到前臺,而且把任務的數量也就是running的值減一,這樣,處於數組最後的那個任務將不會有機會被調度。函數

任務掛起是實現了,那麼當咱們想從新把任務調度到前臺時,該怎麼作呢?咱們能夠利用現有的隊列機制,回憶一下,一旦鼠標,鍵盤的事件發生時,咱們會把硬件產生的數據加入到他們對應的隊列中,而後在CMain主循環中,將隊列中的數據取出來處理。同理,當咱們掛起一個任務時,咱們把掛起的任務對象放入到一個隊列中,當想要從新調度這個對象時,咱們往隊列裏發送一個數據,而後在主循環中對該隊列進行檢查,一旦發現隊列中含有數據的話,那麼就把隊列中寄存的任務從新加入調度數組。代碼修改以下,在golbal_define.c中:ui

void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf, 
    struct TASK *task) {
    fifo->size = size;
    fifo->buf = buf;
    fifo->free = size;
    fifo->flags = 0;
    fifo->p = 0;
    fifo->q = 0;
    fifo->task = task;
    return ;
}

在初始化一個隊列時,把一個任務對象添加進去,若是隊列不須要寄存任務對象,那麼把task設置爲0就能夠。lua

int fifo8_put(struct FIFO8 *fifo, unsigned char data) {
    if (fifo == 0) {
        return -1;
    }    

    if (fifo->free ==0) {
        fifo->flags |= FLAGS_OVERRUN;
        return -1;
    }

    fifo->buf[fifo->p] = data;
    fifo->p++;
    if (fifo->p == fifo->size) {
        fifo->p = 0;
    }

    fifo->free--;

    if (fifo->task != 0) {
        if (fifo->task->flags != 2) {
            task_run(fifo->task);
        }
    }

    return 0;
}

當隊列中有數據加入時,順便查看該隊列是否寄存着一個任務對象,若是是,那麼把該任務對象加入調度數組。url

因爲timer.c中,對計時器的運行須要使用到隊列,既然隊列的數據結構有變更,所以timer.c中,須要作一點小改動:

static struct TIMERCTL timerctl;
extern struct TIMER *task_timer;

void  init_pit(void) {
    io_out8(PIT_CTRL, 0x34);
    io_out8(PIT_CNT0, 0x9c);
    io_out8(PIT_CNT0, 0x2e);

    timerctl.count = 0;
    int i;
    for (i = 0; i < MAX_TIMER; i++) {
        timerctl.timer[i].flags = 0; //not used
        timerctl.timer[i].fifo = 0;
    }
}

上面的改動在於,把每一個timer對象的fifo隊列成員設置爲0。

接下來的改動主要在主入口函數中:

void CMain(void) {
    ....
    fifo8_init(&timerinfo, 8, timerbuf, 0);
    ....
    fifo8_init(&keyinfo, 32, keybuf, 0);
    ....
    task_a = task_init(memman);
    keyinfo.task = task_a;
    ....
}

上面代碼的邏輯是,先經過task_init獲得CMain函數所對應的任務對象,並把該任務對象寄存在鍵盤事件列表中,也就是keyinfo.task = task_a;

void CMain(void) {
    ....
    task_run(task_b);
    ...
    int pos = 0;
    int stop_task_A = 0;
     for(;;) {

       io_cli();
       ....
       else if (fifo8_status(&timerinfo) != 0) {
           io_sti();
           int i = fifo8_get(&timerinfo);
           if (i == 10) {
                showString(shtctl, sht_back, pos, 144, COL8_FFFFFF,
                 "A"); 

                timer_settime(timer, 100);
                pos += 8;
                if (pos > 40 && stop_task_A == 0) {
                    io_cli();
                    task_sleep(task_a);
                    io_sti();
                 }
           }
       ....
    }
}

上面代碼的邏輯時,當CMain函數在主循環中,連續打印字符」A」,當打印的字符超過5個時,經過task_sleep(task_a)把CMain進程掛起。這樣的話,系統運行時,咱們會發現原來是字符A和B 是同時打印到桌面上的,此時便只剩下字符B在繼續打印了。


因爲咱們把task_A寄存到鍵盤隊列,那麼當咱們點擊鍵盤,因而鍵盤數據就會存儲到鍵盤隊列中,因爲鍵盤隊列存儲了任務Ad的任務對象,那麼此時他會把對應任務對象從新加入到調度隊列中,由此字符A會從恢復打印狀態,也就是說,打印字符A的進程從新得到了被調度的機會。

參看視頻,能夠得到更加生動的演示展現。

本文分享自微信公衆號 - Coding迪斯尼(gh_c9f933e7765d)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索