青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

天衣有縫

冠蓋滿京華,斯人獨(dú)憔悴~
posts - 35, comments - 115, trackbacks - 0, articles - 0
   :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

6課:多任務(wù)    下載源代碼


聲明:轉(zhuǎn)載請(qǐng)保留:

譯者:http://www.shnenglu.com/jinglexy

原作者:xiaoming.mo at skelix dot org

MSN & Email: jinglexy at yahoo dot com dot cn


目標(biāo)

 

在本課中,我們將在skelix內(nèi)核中同時(shí)運(yùn)行多個(gè)任務(wù)(本文中可以暫時(shí)理解成進(jìn)程)。每個(gè)任務(wù)都有自己的LDT表,并且是基于優(yōu)先級(jí)的任務(wù)調(diào)度。


現(xiàn)在我們開始skelix內(nèi)核的多任務(wù)支持工作了。首先要澄清的是:在單處理器上面,進(jìn)程只能并發(fā),而不是并行。就是說(shuō)同一時(shí)刻只能有一個(gè)任務(wù)在運(yùn)行,cpu會(huì)劃分很多很小的時(shí)間片來(lái)運(yùn)行各個(gè)任務(wù),這樣看起來(lái)就像多個(gè)任務(wù)在同時(shí)運(yùn)行一樣。i386從硬件上就支持多任務(wù)了,但是也可以通過(guò)編程來(lái)實(shí)現(xiàn),本文就是一個(gè)例子,每個(gè)任務(wù)輪流運(yùn)行一個(gè)小的時(shí)間片。我們知道,單個(gè)cpu只有1組寄存器,這些可以用于所有任務(wù)。當(dāng)由一個(gè)任務(wù)切換到另一個(gè)任務(wù)時(shí),必須先保存原來(lái)任務(wù)的運(yùn)行環(huán)境(就是一堆寄存器和其他信息),通常稱這個(gè)運(yùn)行環(huán)境叫上下文i386使用一個(gè)叫TSS的段來(lái)存儲(chǔ)這些信息,這個(gè)TSS段至少104字節(jié)(不包括IOPL位圖的話),對(duì)應(yīng)它的是一個(gè)TSS描述符。TSS描述符只能在GDT表中,這意味著任務(wù)不能自己通過(guò)LDT來(lái)切換到其他任務(wù)。當(dāng)時(shí)鐘中斷到來(lái)切換任務(wù)時(shí),CPU會(huì)自動(dòng)保存相關(guān)信息到TSS中。

TSS
定義了任務(wù)的上下文環(huán)境,結(jié)構(gòu)如下:

 

                  (圖-1

 

         31       16,15          0
        +------------------------+                |
        |   NO use  |  Back Link |                |
高地址
        +------------------------+                |
        |         ESP0           |                |
        +------------------------+                |
        |   NO use  |    SS0     |                |
        +------------------------+                |
        |         ESP1           |                |
        +------------------------+                |
        |   NO use  |    SS1     |                |
低地址
        +------------------------+               \|/
        |         ESP2           |
        +------------------------+
        |   NO use  |    SS2     |
        +------------------------+
        |         CR3            |
        +------------------------+
        |         EIP            |
        +------------------------+
        |        EFLAGS          |
        +------------------------+
        |         EAX            |
        +------------------------+
        |         ECX            |
        +------------------------+
        |         EDX            |
        +------------------------+
        |         EBX            |
        +------------------------+
        |         ESP            |
        +------------------------+
        |         EBP            |
        +------------------------+
        |         ESI            |
        +------------------------+
        |         EDI            |
        +------------------------+
        |   NO use  |    ES      |
        +------------------------+
        |   NO use  |    CS      |
        +------------------------+
        |   NO use  |    SS      |
        +------------------------+
        |   NO use  |    DS      |
        +------------------------+
        |   NO use  |    FS      |
        +------------------------+
        |   NO use  |    GS      |
        +------------------------+

        |   NO use  |    LDT     |
        +------------------------+
        |  I/O
位圖 NO use | T |
        +------------------------+

i386
處理器可以使用嵌套任務(wù),就是說(shuō)當(dāng)任務(wù)1切換到任務(wù)2時(shí),任務(wù)2 Back Link 域被設(shè)置成任務(wù)1的選擇子,且任務(wù)2EFLAGS中的NT(嵌套任務(wù)標(biāo)識(shí))置位,這樣任務(wù)2返回時(shí),cpu就知道切換回任務(wù)1。我們知道,i3864中特權(quán)級(jí),所以可以在TSS中設(shè)置這四種特權(quán)級(jí)下的堆棧。比如一個(gè)任務(wù)運(yùn)行在ring3特權(quán)級(jí)下,它使用用戶態(tài)堆棧,使用系統(tǒng)調(diào)用可以讓這個(gè)任務(wù)切換到ring0的內(nèi)核態(tài),這是堆棧也必須切換到內(nèi)核態(tài)堆棧。這些堆棧指針就是存放在TSS中的。現(xiàn)在我們來(lái)看看TSS描述符(它只能放在GDT中):

 

                   圖-2TSS描述符定義

 

 63_______________56__55__54__53__52__51_____________48_

基地址(3124位) | G | 0 | 0 |AVL| 長(zhǎng)度(1916位) |

|_______________________________________________________|

 

 _47__46__45__44________41__40____39_________________32_

| P |  DPL | 0 | 1 | 0 | B |  1  |  基地址(2316位)  |

|_______________________________________________________|

 

 31____________________________________________________16

|                    基地址(150位)                  |

|_______________________________________________________|

 

 16_____________________________________________________

|                    長(zhǎng)度(150位)                    |

|_______________________________________________________|

 

*********************************************************

                   圖-3通用描述符定義

 

 63_______________56__55__54__53__52__51_____________48_

基地址(3124位) | G |D/B| X | U | 長(zhǎng)度(1916位) |

|_______________________________________________________|

 

 _47__46__45__44____41______40____39_________________32_

| P |  DPL |     類型    |  A   |  基地址(2316位)   |

|_______________________________________________________|

 

 31____________________________________________________16

|                    基地址(150位)                  |

|_______________________________________________________|

 

 16_____________________________________________________

|                    長(zhǎng)度(150位)                    |

|_______________________________________________________|

 

我們現(xiàn)在來(lái)比較一下通用描述符和TSS描述符,在TSS描述符中,我們看到D位(數(shù)據(jù)還是代碼段)和X位(未使用)都被置為0,而且AVL位有效。類型type010B,第41位,及B位是TSS描述符的忙標(biāo)識(shí),因?yàn)橐粋€(gè)進(jìn)程只能有一個(gè)TSS,所以當(dāng)進(jìn)程執(zhí)行時(shí),B位將被設(shè)置,表示這個(gè)TSS正在被使用,除了進(jìn)程調(diào)度外,不能再使用它了。A位被置為1,即便A位被置為可寫,TSS也不能被進(jìn)程讀或?qū)憽?span lang="EN-US">TSS的其他域和通用描述符一樣,只是要注意一點(diǎn):就是TSS的長(zhǎng)度界限至少要有104字節(jié)。另外需要注意的是,skelix暫時(shí)不支持fpummxsse,如果讀者想擴(kuò)展的話需要注意每個(gè)進(jìn)程都有自己的fpusse環(huán)境。

 

在本課中,TSS描述符的DPL將被置為0,所以只有內(nèi)核可以進(jìn)程任務(wù)切換。和GDTRLDTR一樣,TSS描述符也有自己的用于切換任務(wù)的寄存器,就是TR。可以用LTR指令來(lái)加載這個(gè)寄存器。GDT中的TSS描述符不能被加載到任何其他寄存器,否則會(huì)發(fā)生一個(gè)異常。

切換任務(wù)有多種方法,我們的做法是使用jmp指令跳轉(zhuǎn)到TSS描述符。(還有其他方法,如從中斷返回,使用任務(wù)門(起始也相當(dāng)于jmpTSS上))。趙博的linux內(nèi)核完全剖析上講到了切換任務(wù)的幾種方法,并詳細(xì)討論了,不清楚的同學(xué)可以購(gòu)買該書。

在切換任務(wù)時(shí),CPU首先保存當(dāng)前進(jìn)程的上下文環(huán)境到當(dāng)前TSS中,然后加載新任務(wù)的TSSTR寄存器中,然后從新任務(wù)的TSS中加載上下文到寄存器中,最后跳轉(zhuǎn)到新任務(wù)的第一條指令并開始執(zhí)行。不知道這樣有沒有說(shuō)清楚,這個(gè)是比較關(guān)鍵的地方,歡迎和我討論:jinglexy at yahoo dot com dot cnMSN)。

OK
,讓程序來(lái)說(shuō)明一起吧。先看看TSS數(shù)據(jù)結(jié)構(gòu)的定義:
06/include/task.h

 

struct TSS_STRUCT {
    int    back_link;
    int    esp0, ss0;
    int    esp1, ss1;
    int    esp2, ss2;
    int    cr3;
    int    eip;
    int    eflags;
    int    eax,ecx,edx,ebx;
    int    esp, ebp;
    int    esi, edi;
    int    es, cs, ss, ds, fs, gs;
    int    ldt;
    int    trace_bitmap;
};

沒有pad的數(shù)據(jù)結(jié)構(gòu)是104字節(jié),如果你使用或模擬 IA64 平臺(tái)的話,就需要查找相關(guān)的資料了。

對(duì)于一個(gè)正在運(yùn)行的OS,上面的信息顯然是不夠的,所有我定義了另外一個(gè)包裝TSS的數(shù)據(jù)結(jié)構(gòu),

即進(jìn)程信息(process control block),以后簡(jiǎn)稱為pcb

#define TS_RUNNING    0                // 任務(wù)的三種狀態(tài)
#define TS_RUNABLE    1
#define TS_STOPPED    2

struct TASK_STRUCT {                   // pcb
定義
    struct TSS_STRUCT tss;
    unsigned long long tss_entry;      // tss
描述符在gdt中的8字節(jié)入口項(xiàng)
    unsigned long long ldt[2];
    unsigned long long ldt_entry;
    int state;
    int priority;
    struct TASK_STRUCT *next;
};
#define DEFAULT_LDT_CODE    0x00cffa000000ffffULL
#define DEFAULT_LDT_DATA    0x00cff2000000ffffULL
#define INITIAL_PRIO        200

 

priority 表示任務(wù)優(yōu)先級(jí),新建任務(wù)默認(rèn)的優(yōu)先級(jí)是下面的 INITIAL_PRIOskelix內(nèi)核中所有的任務(wù)都用一個(gè)單向鏈表來(lái)表示,這樣做起來(lái)比較簡(jiǎn)單。我們現(xiàn)在來(lái)看一個(gè)任務(wù)的例子,就是任務(wù)0:內(nèi)核初始化完成后,就執(zhí)行任務(wù)0


06/task.c

static unsigned long TASK0_STACK[256] = {0xf};

上面數(shù)據(jù)是任務(wù)0在特權(quán)級(jí)0下使用的堆棧,0xf值如果不設(shè)置的話,這塊內(nèi)存就會(huì)發(fā)生一些錯(cuò)誤,我也不清楚是為什么,所以第一個(gè)元素隨便給了一個(gè)非0值。

struct TASK_STRUCT TASK0 = {

    /* tss */
    {
        0,
        /* esp0                                    ss0 */
        (unsigned)&TASK0_STACK+sizeof TASK0_STACK, DATA_SEL,

        上面定義會(huì)使任務(wù)的esp0執(zhí)行棧底,使用內(nèi)核數(shù)據(jù)段選擇子就可以了
        /* esp1 ss1 esp2 ss2 */
        0, 0, 0, 0,
        /* cr3 */
        0,
        /* eip eflags */
        0, 0,
        /* eax ecx edx ebx */
        0, 0, 0, 0,
        /* esp ebp */
        0, 0,
        /* esi edi */
        0, 0,
        /* es          cs             ds */
        USER_DATA_SEL, USER_CODE_SEL, USER_DATA_SEL,
        /* ss          fs             gs */
        USER_DATA_SEL, USER_DATA_SEL, USER_DATA_SEL, 
        /* ldt
:見后面說(shuō)明 */
        0x20,
        /* trace_bitmap */
        0x00000000},
    /* tss_entry */
    0,
    /* ldt[2] */
    {DEFAULT_LDT_CODE, DEFAULT_LDT_DATA},
    /* ldt_entry */
    0,
    /* state */
    TS_RUNNING,
    /* priority */
    INITIAL_PRIO,
    /* next */
    0,
};

現(xiàn)在,我們定義了一個(gè)TSS,再在GDT中加入描述符索引:


06/bootsect.s

gdt:
        .quad    0x0000000000000000 # null descriptor
        .quad    0x00cf9a000000ffff # cs
        .quad    0x00cf92000000ffff # ds
        .quad    0x0000000000000000 # reserved for further use
        .quad    0x0000000000000000 # reserved for further use

第四項(xiàng)(0x3)用于存放該任務(wù)的TSS,所以定義了一個(gè)宏:  CURR_TASK_TSS = 3來(lái)索引GDT中的這個(gè)TSS。當(dāng)任務(wù)釋放控制權(quán)后,會(huì)加載自己的TSS描述符索引到pcbtss_entry域。不管多少任務(wù),都可以只使用兩個(gè)描述符,切換任務(wù)前更新GDT中的描述符即可。由于GDT有長(zhǎng)度限制,只能存放8096個(gè)描述符,linux操作系統(tǒng)限制了任務(wù)的數(shù)量,我不知道為什么要這樣做,因?yàn)橥黄七M(jìn)程數(shù)限制看起來(lái)如此的簡(jiǎn)單。


06/task.c

unsigned long long set_tss(unsigned long long tss) {             // 參數(shù)為tss在內(nèi)存中的地址
    unsigned long long __tss_entry = 0x0080890000000067ULL;
    __tss_entry |= ((tss)<<16) & 0xffffff0000ULL;                //
基地址低24
    __tss_entry |= ((tss)<<32) & 0xff00  0000  0000   0000ULL;   //
基地址高8
    return gdt[CURR_TASK_TSS] = __tss_entry;
}

這個(gè)函數(shù)產(chǎn)生TSS描述符,并存放它到GDT中,可以看到,描述符的DPL設(shè)置為0,所以只有內(nèi)核可以用這個(gè)描述符。

屬性0080890000000067分析:0x67是長(zhǎng)度103(從0開始計(jì)算,即長(zhǎng)104),89p位為1dpl位為0b位為180是粒度G位為1


unsigned long long get_tss(void) {
    return gdt[CURR_TASK_TSS];
}

LDT

我們看了這么多關(guān)于GDTLDT的代碼后,LDT非常容易理解了。它和GDT差不多,區(qū)別是具有局部特性,每個(gè)任務(wù)都可以有自己的LDT,我們?cè)?span lang="EN-US">skelix內(nèi)核中為每個(gè)任務(wù)在LDT中設(shè)置兩個(gè)描述符項(xiàng),第一項(xiàng)是代碼段,第二項(xiàng)是數(shù)據(jù)段和堆棧段,描述符選擇子格式如下:

            圖-4

 15______________________________3___2____1___0__

|              Index              | TI |   RPL  |

|_______________________________________________|

 

我們?cè)O(shè)置所有任務(wù)的特權(quán)級(jí)為3,即RPL 11b,且TI 1(表示使用LDT而不是GDT),和GDT不同的是,LDT的第一項(xiàng)可以使用而不是保留,所以代碼段的選擇子為0x7,而數(shù)據(jù)段和堆棧段選擇子為0xf

TSS
數(shù)據(jù)結(jié)構(gòu)中,有一個(gè)LDT域,保存的是GDT表中的描述符。聽起來(lái)可能昏菜,我們現(xiàn)在來(lái)搞明白她。首先,每個(gè)進(jìn)程都有自己的LDT表,而且這個(gè)表可以在內(nèi)存的任何地方(暫時(shí)不考慮虛擬內(nèi)存),所以需要一個(gè)描述符來(lái)索引這個(gè)內(nèi)存地址(即LDT表),這個(gè)描述符就放在GDT中,并且在TSS中存放一份該描述符的選擇子。

畫個(gè)圖來(lái)說(shuō)明好了:

            圖-5

 

 ________________                            ________________

|      TASK      |                          |      GDT       |

|________________|                          |                |

TSS  __________|/    選擇子存于此         |________________|

|     | LDT field|--------------------------|     描述符     |

|      ----------|\                        /|________________|

|________________|                        / |                |

|                |                       /  |                |

|                |     描述符索引該 LDT /   |                |

|                |         -------------    |                |

|                |        /                 |________________|

|                |       /

|                |      /

|                |     /

|                |    /

|                |   /

|                |  /

|________________| /

|      LDT       |/

|________________|

|________________|

GDT
中的第三項(xiàng)用于任務(wù)TSS,第四項(xiàng)用于任務(wù)LDT。通過(guò)選擇子0x180x20分包可以索引到它們。和設(shè)置TSS一樣,對(duì)應(yīng)也寫有兩個(gè)LDT操作的函數(shù)。

06/task.c

 

unsigned long long
set_ldt(unsigned long long ldt) {
    unsigned long long ldt_entry = 0x008082000000000fULL;        // DPL
3而不是0

    ldt_entry |= ((ldt)<<16) & 0xffffff0000ULL;
    ldt_entry |= ((ldt)<<32) & 0xff00000000000000ULL;
    return gdt[CURR_TASK_LDT] = ldt_entry;
}

unsigned long long
get_ldt(void) {
    return gdt[CURR_TASK_LDT];
}

現(xiàn)在,我們將設(shè)置所有任務(wù)使用相同的LDT,這些任務(wù)共享相同的內(nèi)存空間。如果你要設(shè)計(jì)字節(jié)的OS,這不會(huì)是個(gè)好主意。但是后面會(huì)為這些任務(wù)通過(guò)虛擬內(nèi)存機(jī)制來(lái)設(shè)置不同的內(nèi)存空間,后續(xù)課程會(huì)講到。

06/include/task.h

 

#define DEFAULT_LDT_CODE    0x00cffa000000ffffULL
#define DEFAULT_LDT_DATA    0x00cff2000000ffffULL

上面就是任務(wù)的LDT描述符定義,注意DPLdescriptor priority level)值為3

 

上面已經(jīng)提到,所有任務(wù)使用一個(gè)單向鏈表連接起來(lái),其中有兩個(gè)重要指針:一個(gè)是任務(wù)0TASK0)的next指針,它是所有任務(wù)鏈表的頭指針;另一個(gè)是current指針,指向當(dāng)前正在運(yùn)行的任務(wù)。

創(chuàng)建任務(wù)并調(diào)度

首先,我們定義有:
06/task.c

struct TASK_STRUCT *current = &TASK0;

 

現(xiàn)在我們來(lái)看看新任務(wù)是如何創(chuàng)建的:
06/init.c

static void
new_task(struct TASK_STRUCT *task, unsigned int eip,
                unsigned int stack0, unsigned int stack3) {

// 這個(gè)函數(shù)有4個(gè)參數(shù):第一個(gè)參數(shù)是任務(wù)數(shù)據(jù)結(jié)構(gòu)的內(nèi)存地址

// 第二個(gè)參數(shù)是任務(wù)入口地址

// 第三個(gè)和第四個(gè)參數(shù)是0環(huán)和3環(huán)特權(quán)級(jí)下堆棧地址

// 由于堆棧地址的描述符選擇子是固定的,所以就不用傳進(jìn)來(lái)了

    memcpy(task, &TASK0, sizeof(struct TASK_STRUCT));        // TASK0
作為任務(wù)模板
    task->tss.esp0 = stack0;
    task->tss.eip = eip;
    task->tss.eflags = 0x3202;        //
合適的狀態(tài)標(biāo)識(shí)
    task->tss.esp = stack3;

    task->priority = INITIAL_PRIO;    //
新任務(wù)默認(rèn)優(yōu)先級(jí)

    task->state = TS_STOPPED;
    task->next = current->next;
    current->next = task;
    task->state = TS_RUNABLE;
}
extern void task1_run(void);          // 
任務(wù)入口函數(shù)
extern void task2_run(void);

static long task1_stack0[1024] = {0xf, };
static long task1_stack3[1024] = {0xf, };
static long task2_stack0[1024] = {0xf, };
static long task2_stack3[1024] = {0xf, };

// 因?yàn)闆]有內(nèi)核中沒有實(shí)現(xiàn)內(nèi)存管理,所以固定設(shè)置一些任務(wù)和她們使用的堆棧。

// 任務(wù)0運(yùn)行在內(nèi)核態(tài),任務(wù)1和任務(wù)2運(yùn)行在ring3

void
init(void) {
    char wheel[] = {'\\', '|', '/', '-'};
    int i = 0;
    struct TASK_STRUCT task1;                      //
移到全局定義較合適
    struct TASK_STRUCT task2;

    idt_install();
    pic_install();
    kb_install();
    timer_install(100);

    set_tss((unsigned long long)&TASK0.tss);       // 讓任務(wù)0先跑起來(lái)
    set_ldt((unsigned long long)&TASK0.ldt);

    __asm__ ("ltrw    %%ax\n\t"::"a"(TSS_SEL));    // 加載任務(wù)0使用的TRLDTR寄存器
    __asm__ ("lldt    %%ax\n\t"::"a"(LDT_SEL));    //
使用ltrwlldt指令

 

    sti();                                         // 從現(xiàn)在開始捕獲中斷或異常
    new_task(&task1,                               //
注意:創(chuàng)建任務(wù)時(shí),中斷是使能的
            (unsigned int)task1_run,
            (unsigned int)task1_stack0+sizeof task1_stack0,
            (unsigned int)task1_stack3+sizeof task1_stack3);
    new_task(&task2,
            (unsigned int)task2_run,
            (unsigned int)task2_stack0+sizeof task2_stack0,
            (unsigned int)task1_stack3+sizeof task2_stack3);

 

    __asm__ ("movl %%esp,%%eax\n\t" \
             "pushl %%ecx\n\t" \         //
任務(wù)0內(nèi)核棧ss
             "pushl %%eax\n\t" \         //
任務(wù)0內(nèi)核棧esp0
             "pushfl\n\t" \              // eflags
             "pushl %%ebx\n\t" \         //
任務(wù)0代碼段選擇子cs
             "pushl $1f\n\t" \           // 
任務(wù)0函數(shù)地址eip:就是下面第2行匯編
             "iret\n" \
             "1:\tmovw %%cx,%%ds\n\t" \
             "movw %%cx,%%es\n\t" \
             "movw %%cx,%%fs\n\t" \
             "movw %%cx,%%gs" \
             ::"b"(USER_CODE_SEL),"c"(USER_DATA_SEL));

    // 注意:現(xiàn)在開始內(nèi)核運(yùn)行在任務(wù)0上了,通過(guò)iret指令長(zhǎng)跳轉(zhuǎn)到任務(wù)0

    // (先加載正確的EIPCSSSESP,圖如下:)
    +--------------------+
    | LDT stack selector |
    +--------------------+
    |        ESP         |
    +--------------------+
    |       EFLAGS       |
    +--------------------+
    | LDT code selector  |
    +--------------------+
    |        EIP         |
    +--------------------+

 

    for (;;) {
        __asm__ ("movb    %%al,    0xb8000+160*24"::"a"(wheel[i]));
        if (i == sizeof wheel)
            i = 0;
        else
            ++i;
    }
}

現(xiàn)在,任務(wù)已經(jīng)有了,萬(wàn)事具備只欠東風(fēng)了,下面加入時(shí)鐘中斷代碼進(jìn)行任務(wù)調(diào)度。
06/timer.c

void do_timer(void) {        // 時(shí)鐘中斷處理函數(shù)
    struct TASK_STRUCT *v = &TASK0;
    int x, y;
    ++timer_ticks;
    get_cursor(&x, &y);
    set_cursor(71, 24);
    kprintf(KPL_DUMP, "%x", timer_ticks);
    set_cursor(x, y);
    outb(0x20, 0x20);
    cli();
    for (; v; v=v->next) {   //
遍歷鏈表,調(diào)整任務(wù)優(yōu)先級(jí)
        if (v->state == TS_RUNNING) {
            if ((v->priority+=30) <= 0)
                v->priority = 0xffffffff;
        } else
            v->priority -= 10;    // 
值越低,優(yōu)先級(jí)越高(等待的任務(wù)優(yōu)先級(jí)會(huì)變高)

        // *nix內(nèi)核通常這樣做:較小的值優(yōu)先級(jí)較高
    }

    if (! (timer_ticks%1))
        scheduler();

    sti();
}

 

調(diào)度函數(shù)實(shí)現(xiàn):
06/task.c

void scheduler(void) {
    struct TASK_STRUCT *v = &TASK0, *tmp = 0;
    int cp = current->priority;

    for (; v; v = v->next) {
        if ((v->state==TS_RUNABLE) && (cp>v->priority)) {
            tmp = v;
            cp = v->priority;
        }
    }    //
遍歷鏈表,找尋優(yōu)先級(jí)最高的任務(wù)(即值最小的任務(wù))

    if (tmp && (tmp!=current)) {           // tmp
是遍歷的結(jié)果
        current->tss_entry = get_tss();    //
TSS LDT描述符
        current->ldt_entry = get_ldt();
        tmp->tss_entry = set_tss((unsigned long long)((unsigned int)&tmp->tss));
        tmp->ldt_entry = set_ldt((unsigned long long)((unsigned int)&tmp->ldt));


        current->state = TS_RUNABLE;
        tmp->state = TS_RUNNING;
        current = tmp;

        __asm__ __volatile__("ljmp    $" TSS_SEL_STR ",    $0\n\t");

        // skelix通過(guò)長(zhǎng)跳轉(zhuǎn)到TSS描述符上(偏移應(yīng)為0)切換任務(wù)
    }
}


下面是任務(wù) A & B

06/isr.s

task1_run:
        call    do_task1
        jmp     task1_run
task2_run:
        call    do_task2
        jmp     task2_run

這里使用匯編而不是c語(yǔ)言來(lái)寫這兩個(gè)任務(wù)的入口函數(shù)的原因,

是不想在跳轉(zhuǎn)前后改變內(nèi)核棧。

 

我們現(xiàn)在看看能不能在任務(wù)1和任務(wù)2中使用kprintf
06/init.c

void
do_task1(void) {
    unsigned int cs;
    __asm__ ("movl    %%cs,    %%eax":"=a"(cs));
    kprintf(KPL_DUMP, "%x", cs);
    for (;;)
        ;
}

void
do_task2(void) {
    unsigned int cs;
    __asm__ ("movl    %%cs,    %%eax":"=a"(cs));
    kprintf(KPL_PANIC, "%x", cs);
    for (;;)
        ;
}

 

修改Makefile 中的 KERNEL_OBJS
06/MakefileKERNEL_OBJS= load.o init.o isr.o timer.o libcc.o scr.o kb.o task.o kprintf.o exceptions.o

編譯,運(yùn)行一把。截圖就不貼出來(lái)了(結(jié)果是看不到任務(wù)1和任務(wù)2的打印:ring3任務(wù)當(dāng)然不能使用ring0函數(shù))

ok,現(xiàn)在改一下這兩個(gè)任務(wù)函數(shù):
06/init.cvoid
do_task1(void) {
    print_c('A', BLUE, WHITE);
}

void
do_task2(void) {
    print_c('B', GRAY, BROWN);
}

 

編譯,運(yùn)行,正是我們想要的結(jié)果。

 

 

Feedback

# re: 自己動(dòng)手寫內(nèi)核(第6課:多任務(wù))(原創(chuàng))  回復(fù)  更多評(píng)論   

2007-05-18 19:06 by raywill
加油哦~

快快去實(shí)現(xiàn)內(nèi)存管理,文件系統(tǒng)~

那才是最有挑戰(zhàn)的地方!Fighting!

------------
raywill.blog.sohu.com

# re: 自己動(dòng)手寫內(nèi)核(第6課:多任務(wù))(原創(chuàng))  回復(fù)  更多評(píng)論   

2007-05-18 19:19 by raywill
不過(guò),發(fā)現(xiàn)你這都是轉(zhuǎn)載吧.....

skelix....

請(qǐng)注明 :(


# re: 自己動(dòng)手寫內(nèi)核(第6課:多任務(wù))(原創(chuàng))[未登錄]  回復(fù)  更多評(píng)論   

2007-05-19 09:32 by 天衣有縫
沒錯(cuò),翻譯文章,原作者只在第一篇聲明了,所有文章整理完的時(shí)候加上去把,免得對(duì)不起作者了,相對(duì)來(lái)說(shuō)我做的事情就少很多。

歡迎批評(píng)指正
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲电影免费在线| 久久久久成人网| 亚洲免费观看高清在线观看| 伊人久久婷婷色综合98网| 国产伦精品一区二区三区在线观看| 欧美日韩一区视频| 欧美日韩亚洲系列| 国产精品久久久久久久久久免费看 | 亚洲图片你懂的| 日韩一级大片在线| 在线午夜精品自拍| 亚洲一区二区在线播放| 午夜精品久久久久久99热软件| 亚洲一区国产| 亚洲女性裸体视频| 亚洲最黄网站| 一区二区av| 亚洲一区国产精品| 午夜精品三级视频福利| 亚洲一区视频在线| 亚洲欧美日韩中文视频| 一区二区三区欧美成人| 一区二区三区精品| 亚洲综合色激情五月| 中文国产成人精品| 亚洲欧美中文在线视频| 亚欧成人精品| 欧美伊人久久久久久久久影院| 欧美在线不卡视频| 久久久91精品国产一区二区精品| 久久国产精品网站| 老司机67194精品线观看| 麻豆91精品91久久久的内涵| 蜜臀久久99精品久久久画质超高清| 免费成人性网站| 欧美成人亚洲| 欧美日韩另类国产亚洲欧美一级| 欧美午夜在线| 国产一区二区中文| 亚洲成色精品| 99精品国产高清一区二区| 亚洲影院污污.| 久久国产乱子精品免费女| 乱码第一页成人| 亚洲三级网站| 亚洲综合第一页| 久久亚洲欧美| 欧美日韩国产区一| 国产手机视频一区二区| 国产亚洲福利一区| 亚洲高清不卡av| 亚洲视频你懂的| 久久九九国产精品| 欧美国产日韩亚洲一区| 99成人免费视频| 久久精品国产亚洲精品| 免费不卡欧美自拍视频| 欧美午夜a级限制福利片| 国内揄拍国内精品少妇国语| 亚洲国产婷婷综合在线精品| 亚洲婷婷在线| 欧美一区二区福利在线| 麻豆免费精品视频| 日韩一区二区电影网| 久久久久99| 欧美三级网址| 在线电影国产精品| 亚洲天堂男人| 美日韩免费视频| 亚洲午夜激情免费视频| 麻豆精品视频在线| 国产精品视频1区| 亚洲乱码精品一二三四区日韩在线| 亚洲欧美日本视频在线观看| 久久一本综合频道| 一区二区三区久久网| 久久福利视频导航| 欧美色综合天天久久综合精品| 黄色成人av| 性欧美1819性猛交| 亚洲第一搞黄网站| 久久国产精品久久久| 欧美三级电影网| 91久久精品国产91性色tv| 欧美一区二区成人| 亚洲精品国产精品国产自| 久久精品国产精品亚洲| 国产精品v欧美精品∨日韩| 亚洲高清av在线| 久久九九免费视频| 在线性视频日韩欧美| 欧美日韩亚洲另类| 在线视频国产日韩| 欧美在线亚洲在线| 亚洲精品综合久久中文字幕| 久久久久久香蕉网| 国产精品毛片| 亚洲一区综合| 亚洲美女淫视频| 欧美大片在线看| 1024精品一区二区三区| 久久久99免费视频| 亚洲欧美另类中文字幕| 欧美性视频网站| 99精品久久| 亚洲人被黑人高潮完整版| 久久亚洲欧美| 国产精品swag| 亚洲精品久久久久久久久| 狂野欧美激情性xxxx| 亚洲无亚洲人成网站77777| 欧美福利视频一区| 亚洲精品免费一区二区三区| 久久综合九色综合网站| 欧美一区二区三区免费观看| 国产精品乱看| 亚洲综合首页| 亚洲天堂成人| 欧美日韩成人网| 一区二区久久| 亚洲肉体裸体xxxx137| 欧美激情在线观看| 99re亚洲国产精品| 亚洲二区在线视频| 欧美精品成人在线| 亚洲免费观看在线视频| 亚洲日本理论电影| 欧美日韩在线观看一区二区三区| 亚洲一级免费视频| 亚洲免费观看高清完整版在线观看熊| 欧美日产国产成人免费图片| 亚洲欧洲一区二区三区久久| 美女任你摸久久| 久久久久一区| 亚洲另类自拍| 日韩一区二区久久| 国产精品女人网站| 久久一区精品| 欧美成人午夜| 亚洲欧美日韩视频一区| 先锋亚洲精品| 亚洲国产欧美日韩另类综合| 亚洲高清在线播放| 国产精品国产三级国产a| 一级成人国产| 新狼窝色av性久久久久久| 黄色成人在线免费| 亚洲国产精品国自产拍av秋霞| 欧美日韩国产综合久久| 亚洲一区二区三区免费视频| 99在线精品观看| 国产精品久久久久一区| 久久av免费一区| 狂野欧美性猛交xxxx巴西| 一区二区欧美日韩视频| 一本色道久久88精品综合| 国产精自产拍久久久久久蜜| 欧美91视频| 欧美日韩裸体免费视频| 久久九九99视频| 欧美精品综合| 久久久精品日韩欧美| 欧美国产国产综合| 亚洲欧美韩国| 欧美成人免费大片| 欧美亚洲尤物久久| 免费成人黄色片| 欧美有码视频| 欧美二区不卡| 久久久久久夜| 欧美另类99xxxxx| 久久手机免费观看| 欧美巨乳在线| 久久噜噜亚洲综合| 欧美激情在线播放| 久久久国产精品一区| 欧美精品在欧美一区二区少妇| 在线视频欧美日韩精品| 久久精品国产第一区二区三区| 在线视频欧美日韩精品| 久久一区二区精品| 亚洲天堂免费在线观看视频| 欧美大尺度在线| 久久久亚洲影院你懂的| 欧美日韩视频专区在线播放 | 欧美在线观看视频一区二区三区| 亚洲精品视频一区二区三区| 亚洲欧美伊人| 亚洲卡通欧美制服中文| 午夜免费在线观看精品视频| 亚洲乱码国产乱码精品精可以看| 亚洲特级毛片| 99精品视频网| 老司机精品福利视频| 久久久www| 国产精品视频一区二区三区| 亚洲美女色禁图| 激情五月综合色婷婷一区二区| 亚洲综合国产激情另类一区| 一区二区黄色|