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

天衣有縫

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

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


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

譯者: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)核中同時運(yùn)行多個任務(wù)(本文中可以暫時理解成進(jìn)程)。每個任務(wù)都有自己的LDT表,并且是基于優(yōu)先級的任務(wù)調(diào)度。


現(xiàn)在我們開始skelix內(nèi)核的多任務(wù)支持工作了。首先要澄清的是:在單處理器上面,進(jìn)程只能并發(fā),而不是并行。就是說同一時刻只能有一個任務(wù)在運(yùn)行,cpu會劃分很多很小的時間片來運(yùn)行各個任務(wù),這樣看起來就像多個任務(wù)在同時運(yùn)行一樣。i386從硬件上就支持多任務(wù)了,但是也可以通過編程來實(shí)現(xiàn),本文就是一個例子,每個任務(wù)輪流運(yùn)行一個小的時間片。我們知道,單個cpu只有1組寄存器,這些可以用于所有任務(wù)。當(dāng)由一個任務(wù)切換到另一個任務(wù)時,必須先保存原來任務(wù)的運(yùn)行環(huán)境(就是一堆寄存器和其他信息),通常稱這個運(yùn)行環(huán)境叫上下文i386使用一個叫TSS的段來存儲這些信息,這個TSS段至少104字節(jié)(不包括IOPL位圖的話),對應(yīng)它的是一個TSS描述符。TSS描述符只能在GDT表中,這意味著任務(wù)不能自己通過LDT來切換到其他任務(wù)。當(dāng)時鐘中斷到來切換任務(wù)時,CPU會自動保存相關(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ù),就是說當(dāng)任務(wù)1切換到任務(wù)2時,任務(wù)2 Back Link 域被設(shè)置成任務(wù)1的選擇子,且任務(wù)2EFLAGS中的NT(嵌套任務(wù)標(biāo)識)置位,這樣任務(wù)2返回時,cpu就知道切換回任務(wù)1。我們知道,i3864中特權(quán)級,所以可以在TSS中設(shè)置這四種特權(quán)級下的堆棧。比如一個任務(wù)運(yùn)行在ring3特權(quán)級下,它使用用戶態(tài)堆棧,使用系統(tǒng)調(diào)用可以讓這個任務(wù)切換到ring0的內(nèi)核態(tài),這是堆棧也必須切換到內(nèi)核態(tài)堆棧。這些堆棧指針就是存放在TSS中的。現(xiàn)在我們來看看TSS描述符(它只能放在GDT中):

 

                   圖-2TSS描述符定義

 

 63_______________56__55__54__53__52__51_____________48_

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

|_______________________________________________________|

 

 _47__46__45__44________41__40____39_________________32_

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

|_______________________________________________________|

 

 31____________________________________________________16

|                    基地址(150位)                  |

|_______________________________________________________|

 

 16_____________________________________________________

|                    長度(150位)                    |

|_______________________________________________________|

 

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

                   圖-3通用描述符定義

 

 63_______________56__55__54__53__52__51_____________48_

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

|_______________________________________________________|

 

 _47__46__45__44____41______40____39_________________32_

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

|_______________________________________________________|

 

 31____________________________________________________16

|                    基地址(150位)                  |

|_______________________________________________________|

 

 16_____________________________________________________

|                    長度(150位)                    |

|_______________________________________________________|

 

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

 

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

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

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

OK
,讓程序來說明一起吧。先看看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 平臺的話,就需要查找相關(guān)的資料了。

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

即進(jìn)程信息(process control block),以后簡稱為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é)入口項
    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)先級,新建任務(wù)默認(rèn)的優(yōu)先級是下面的 INITIAL_PRIO。skelix內(nèi)核中所有的任務(wù)都用一個單向鏈表來表示,這樣做起來比較簡單。我們現(xiàn)在來看一個任務(wù)的例子,就是任務(wù)0:內(nèi)核初始化完成后,就執(zhí)行任務(wù)0


06/task.c

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

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

struct TASK_STRUCT TASK0 = {

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

        上面定義會使任務(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
:見后面說明 */
        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)在,我們定義了一個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

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


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

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

屬性00808900,0000,0067分析:0x67是長度103(從0開始計算,即長104),89p位為1,dpl位為0b位為1,80是粒度G位為1


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

LDT

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

            圖-4

 15______________________________3___2____1___0__

|              Index              | TI |   RPL  |

|_______________________________________________|

 

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

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

畫個圖來說明好了:

            圖-5

 

 ________________                            ________________

|      TASK      |                          |      GDT       |

|________________|                          |                |

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

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

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

|________________|                        / |                |

|                |                       /  |                |

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

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

|                |        /                 |________________|

|                |       /

|                |      /

|                |     /

|                |    /

|                |   /

|                |  /

|________________| /

|      LDT       |/

|________________|

|________________|

GDT
中的第三項用于任務(wù)TSS,第四項用于任務(wù)LDT。通過選擇子0x180x20分包可以索引到它們。和設(shè)置TSS一樣,對應(yīng)也寫有兩個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è)計字節(jié)的OS,這不會是個好主意。但是后面會為這些任務(wù)通過虛擬內(nèi)存機(jī)制來設(shè)置不同的內(nèi)存空間,后續(xù)課程會講到。

06/include/task.h

 

#define DEFAULT_LDT_CODE    0x00cffa000000ffffULL
#define DEFAULT_LDT_DATA    0x00cff2000000ffffULL

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

 

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

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

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

struct TASK_STRUCT *current = &TASK0;

 

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

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

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

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

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

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

    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)識
    task->tss.esp = stack3;

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

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

// 因為沒有內(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先跑起來
    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ù)時,中斷是使能的
            (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上了,通過iret指令長跳轉(zhuǎn)到任務(wù)0

    // (先加載正確的EIPCSSS,ESP,圖如下:)
    +--------------------+
    | 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)有了,萬事具備只欠東風(fēng)了,下面加入時鐘中斷代碼進(jìn)行任務(wù)調(diào)度。
06/timer.c

void do_timer(void) {        // 時鐘中斷處理函數(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)先級
        if (v->state == TS_RUNNING) {
            if ((v->priority+=30) <= 0)
                v->priority = 0xffffffff;
        } else
            v->priority -= 10;    // 
值越低,優(yōu)先級越高(等待的任務(wù)優(yōu)先級會變高)

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

    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)先級最高的任務(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通過長跳轉(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語言來寫這兩個任務(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)行一把。截圖就不貼出來了(結(jié)果是看不到任務(wù)1和任務(wù)2的打?。?span lang="EN-US">ring3任務(wù)當(dāng)然不能使用ring0函數(shù))

ok,現(xiàn)在改一下這兩個任務(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: 自己動手寫內(nèi)核(第6課:多任務(wù))(原創(chuàng))  回復(fù)  更多評論   

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

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

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

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

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

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

skelix....

請注明 :(


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

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

歡迎批評指正
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美r片在线| 亚洲特级毛片| 麻豆精品在线视频| 亚洲黄色片网站| 亚洲国产一区二区精品专区| 麻豆精品视频在线| 在线中文字幕不卡| 午夜精品久久| 亚洲国产日本| 亚洲一区二区三区视频| 国内综合精品午夜久久资源| 欧美成人一区二免费视频软件| 欧美激情精品久久久| 亚洲自拍电影| 免费观看亚洲视频大全| 中文国产成人精品久久一| 亚洲欧美色一区| 亚洲欧洲一区二区三区在线观看 | 另类尿喷潮videofree| 99视频日韩| 亚洲欧美日韩另类| 在线欧美三区| 亚洲网在线观看| 在线播放一区| 亚洲视频中文字幕| 亚洲人成网站色ww在线| 亚洲午夜视频在线观看| 亚洲东热激情| 亚洲欧美国产77777| 亚洲成人影音| 欧美一区二区三区四区高清| 亚洲精品无人区| 午夜在线不卡| 亚洲天堂成人在线视频| 久热精品在线| 老司机精品久久| 夜色激情一区二区| 欧美a级片一区| 国产精品入口夜色视频大尺度| 亚洲欧美日韩国产一区二区| 另类春色校园亚洲| 欧美怡红院视频| 欧美日本亚洲视频| 亚洲人成网站影音先锋播放| 久久精品国产91精品亚洲| 一区二区三区鲁丝不卡| 久久免费视频观看| 久久久视频精品| 国产欧美日韩亚洲| 亚洲视频电影图片偷拍一区| 亚洲黄网站黄| 美女图片一区二区| 免费观看一区| 伊人久久亚洲热| 亚洲高清视频的网址| 国产精品成人久久久久| 亚洲国内精品在线| 亚洲人成艺术| 欧美理论电影网| 亚洲黄色免费电影| 亚洲人在线视频| 欧美电影免费| 亚洲激情电影在线| 亚洲美女黄色片| 欧美日韩一区二区欧美激情| 亚洲精品一区二区三区四区高清 | 欧美一区二区三区视频| 久久xxxx精品视频| 国产一区白浆| 久久久久一本一区二区青青蜜月| 久久亚洲一区二区三区四区| 激情文学综合丁香| 女女同性精品视频| 亚洲激情国产| 亚洲在线视频观看| 国产欧美日韩免费| 久久成人人人人精品欧| 欧美国产日韩一区二区| 亚洲美女诱惑| 国产伦精品一区| 久久精品中文字幕一区| 欧美激情麻豆| 亚洲午夜精品福利| 国产亚洲综合在线| 老鸭窝91久久精品色噜噜导演| 亚洲国产高清在线观看视频| 亚洲精品视频一区二区三区| 欧美三级电影一区| 久久精品水蜜桃av综合天堂| 欧美激情亚洲自拍| 亚洲欧美一区二区原创| 加勒比av一区二区| 欧美日韩a区| 欧美在线精品免播放器视频| 欧美激情视频给我| 午夜一区二区三视频在线观看| 国内成人自拍视频| 欧美人成在线视频| 久久se精品一区二区| 亚洲激情欧美激情| 久久高清免费观看| 在线视频精品一区| 国精品一区二区| 欧美日韩视频第一区| 久久视频一区二区| 亚洲一区在线观看视频| 亚洲高清自拍| 久久这里只有| 欧美一级电影久久| 亚洲精品欧美日韩| 一区二区自拍| 午夜精品福利视频| 国产亚洲亚洲| 欧美在线日韩| 亚洲视频在线一区观看| 亚洲大片精品永久免费| 久久av在线看| 亚洲综合欧美| 正在播放欧美一区| 亚洲国产天堂久久综合网| 国产在线观看91精品一区| 国产精品va在线播放我和闺蜜| 老巨人导航500精品| 欧美在线啊v一区| 亚洲永久视频| 亚洲网站在线观看| 99精品热视频| 亚洲美女毛片| 亚洲人人精品| 亚洲欧洲日韩在线| 亚洲黄网站黄| 亚洲激情六月丁香| 亚洲黄色在线视频| 亚洲激情专区| 亚洲激情av在线| 91久久香蕉国产日韩欧美9色| 免费短视频成人日韩| 蜜桃av一区二区三区| 麻豆成人91精品二区三区| 久久九九热免费视频| 欧美在线看片a免费观看| 欧美一区二区国产| 久久av一区二区三区漫画| 欧美专区一区二区三区| 欧美在线高清视频| 久久精品女人| 嫩草影视亚洲| 亚洲国产精品久久久久久女王| 亚洲福利视频一区二区| 亚洲电影免费观看高清完整版| 亚洲国产成人精品视频| 亚洲精品欧美精品| 一区二区国产日产| 久久精品91久久香蕉加勒比| 久久免费精品视频| 国产一区二区三区四区hd| 国产精品美女www爽爽爽| 国产精品日韩专区| 国模一区二区三区| 亚洲国产成人精品久久久国产成人一区| 在线视频国产日韩| 一区二区三区成人| 久久精品中文| 久久综合国产精品| 亚洲全部视频| 亚洲自拍偷拍色片视频| 香蕉亚洲视频| 欧美激情综合网| 国产欧美日韩精品丝袜高跟鞋| 国内精品久久久久久| 亚洲精品日韩综合观看成人91| 亚洲视频在线免费观看| 久久久久久一区| 亚洲人成网站色ww在线| 午夜精品久久| 欧美激情综合色| 国一区二区在线观看| 夜夜嗨av一区二区三区四季av | 国产精品久久久久久久久久妞妞| 国产一区二区0| 在线一区二区三区四区五区| 久久av一区二区三区| 亚洲精品国产精品乱码不99| 午夜精品一区二区三区在线| 欧美暴力喷水在线| 国产自产女人91一区在线观看| 日韩亚洲欧美一区二区三区| 欧美在线观看日本一区| 亚洲人成网在线播放| 欧美资源在线| 欧美一区二区在线播放| 久久久人成影片一区二区三区| 小黄鸭精品密入口导航| 亚洲电影下载| 久久精品亚洲一区| 国产欧美精品xxxx另类| 在线一区欧美| 亚洲精品一二区| 欧美福利精品| 亚洲国产老妈|