• <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>

            天衣有縫

            冠蓋滿京華,斯人獨(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)在我們開(kāi)始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ù)的第一條指令并開(kāi)始執(zhí)行。不知道這樣有沒(méi)有說(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;
            };

            沒(méi)有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
            :見(jiàn)后面說(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開(kāi)始計(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表中的描述符。聽(tīng)起來(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)闆](méi)有內(nèi)核中沒(mé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)在開(kāi)始捕獲中斷或異常
                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)在開(kāi)始內(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))[未登錄](méi)  回復(fù)  更多評(píng)論   

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

            歡迎批評(píng)指正
            久久精品视频免费| 四虎国产精品免费久久久| 久久久午夜精品| 一本色道久久88精品综合| 麻豆亚洲AV永久无码精品久久| 久久久无码人妻精品无码| 久久综合九色综合97_久久久| 日批日出水久久亚洲精品tv| 丁香色欲久久久久久综合网| 国产巨作麻豆欧美亚洲综合久久| 久久成人小视频| 久久www免费人成精品香蕉| 精品久久久中文字幕人妻| 久久精品男人影院| av无码久久久久久不卡网站| 亚洲欧美精品一区久久中文字幕 | 精品多毛少妇人妻AV免费久久| 久久久久久精品成人免费图片| 国产亚洲精久久久久久无码| 欧美伊人久久大香线蕉综合69| 中文字幕成人精品久久不卡| 99久久久国产精品免费无卡顿| 久久精品桃花综合| 亚洲欧美日韩久久精品| 久久午夜无码鲁丝片午夜精品| 亚洲国产天堂久久综合网站| 精品久久久久久久无码| MM131亚洲国产美女久久| 久久一日本道色综合久久| 亚洲色欲久久久综合网| 久久国产劲爆AV内射—百度| 久久人人爽人人爽人人片AV不| 武侠古典久久婷婷狼人伊人| 久久93精品国产91久久综合| 国产农村妇女毛片精品久久| 久久99精品久久久久久9蜜桃| 国产激情久久久久影院老熟女免费 | 久久久久久久尹人综合网亚洲| 97r久久精品国产99国产精| 久久久久国产精品| 国产激情久久久久影院老熟女|