• <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>
            隨筆-80  評(píng)論-24  文章-0  trackbacks-0
            該文件是系統(tǒng)調(diào)用實(shí)現(xiàn)的主要文件。為了弄清楚系統(tǒng)調(diào)用,首先應(yīng)該拿一個(gè)實(shí)例來(lái)看。
            WinixJ系統(tǒng)僅僅實(shí)現(xiàn)了一個(gè)系統(tǒng)調(diào)用getpid(),但是其他系統(tǒng)調(diào)用的框架應(yīng)是完全相同的,只在函數(shù)實(shí)現(xiàn)細(xì)節(jié)上有所不同,因此我們打算以getpid()作為案例講解。
            先看看getpid()呈現(xiàn)給用戶的調(diào)用接口吧:
            POSIX規(guī)范的getpid()聲明如下:
            pid_t getpid();等同于int getpid();為了簡(jiǎn)單起見(jiàn)我們就實(shí)現(xiàn)為int getpid();
            它的代碼也十分簡(jiǎn)單:

             1 int getpid()
             2 {
             3     int res;
             4     __asm__ __volatile__ (
             5             "movl $0x0, %%eax\n\t"
             6             "movl $0x0, %%ebx\n\t"
             7             "movl $0x0, %%ecx\n\t"
             8             "movl $0x0, %%edx\n\t"
             9             "int $0x30\n"
            10             :"=a" (res)
            11             :);
            12     return res;
            13 }
            14 

            雖然有嵌入?yún)R編,但是也不難理解,看似是將eax、ebx、ecx、edx四個(gè)寄存器均傳值為0,然后調(diào)用軟中斷int 0x30(實(shí)際上應(yīng)該稱之為陷阱,陷阱和中斷還是有細(xì)微區(qū)別的,見(jiàn)此博文),最后將返回的結(jié)果由eax賦值給res變量。很簡(jiǎn)單,不是嗎?
            但是為何eax、ebx、ecx、edx都賦值了呢?調(diào)用int 0x30之后又發(fā)生什么了呢?
            先說(shuō)調(diào)用int 0x30之后會(huì)發(fā)生的事情。我們看下面這段代碼,其實(shí)它在此博文已經(jīng)出現(xiàn)過(guò),就是sys_call函數(shù)的實(shí)現(xiàn):

             1 ; 系統(tǒng)調(diào)用框架,系統(tǒng)調(diào)用采用0x30號(hào)中斷向量,利用int 0x30指令產(chǎn)
             2 ; 生一個(gè)軟中斷,之后便進(jìn)入sys_call函數(shù),該函數(shù)先調(diào)用save_all框
             3 ; 架保存所有寄存器值,然后調(diào)用對(duì)應(yīng)系統(tǒng)調(diào)用號(hào)的入口函數(shù)完成系統(tǒng)調(diào)用
             4 ; 注意!?。。?!系統(tǒng)調(diào)用默認(rèn)有三個(gè)參數(shù),分別利用ebx、ecx、edx來(lái)
             5 ; 傳遞,其中eax保存系統(tǒng)調(diào)用號(hào)
             6 sys_call:
             7     save_all
             8 
             9     sti
            10 
            11     push ebx
            12     push ecx
            13     push edx
            14     call [sys_call_table + eax * 4]
            15     add esp, 4 * 3
            16 
            17     recover_from_sys_call
            18 
            19     cli
            20 
            21     iretd
            22 

            再看看save_all:

             1 ; 一個(gè)宏,因?yàn)樗械膇rq中斷函數(shù)以及系統(tǒng)調(diào)用都是先保存現(xiàn)場(chǎng)并將數(shù)據(jù)段等堆棧段
             2 ; 切換到內(nèi)核態(tài),因此,該操作所有的irq中斷入口函數(shù)均相同
             3 ; 故寫成宏節(jié)省空間
             4 %macro save_all 0
             5     push eax
             6     push ecx
             7     push edx
             8     push ebx
             9     push ebp
            10     push esi
            11     push edi
            12     push ds
            13     push es
            14     push fs
            15     push gs
            16     mov si, ss
            17     mov ds, si
            18     mov es, si
            19     mov gs, si
            20     mov fs, si
            21 %endmacro
            22 

            有了這一段代碼再說(shuō)int 0x30后都發(fā)生了什么就比較簡(jiǎn)單了,首先和發(fā)生時(shí)鐘中斷等硬件中斷類似,發(fā)生陷阱的時(shí)候CPU同樣先從當(dāng)前進(jìn)程對(duì)應(yīng)的TSS段中找到SS0和ESP0,然后將當(dāng)前進(jìn)程的SS/ESP/EFLAGS/CS/EIP五個(gè)寄存器值壓入SS0:ESP0,然后再去執(zhí)行save_all的代碼,save_all同樣是將所有的常規(guī)寄存器壓入堆棧,這樣一個(gè)過(guò)程之后就完成了從用戶態(tài)到內(nèi)核態(tài)的切換,之后再看sys_call代碼,壓入三個(gè)寄存器作為形參,然后調(diào)用sys_call_table[eax * 4]函數(shù)。到這兒肯定就明白了,原來(lái)getpid()函數(shù)是通過(guò)四個(gè)通用寄存器來(lái)向內(nèi)核態(tài)的系統(tǒng)調(diào)用函數(shù)傳遞參數(shù)的,這樣做是為了快捷。
            還有一點(diǎn)就是install_sys_call_handler和install_sys_call兩個(gè)函數(shù)功能是不同的,說(shuō)白了,WinixJ就只有一個(gè)系統(tǒng)調(diào)用,sys_call,它是所有POSIX規(guī)定的系統(tǒng)調(diào)用的框架,所有系統(tǒng)調(diào)用的實(shí)現(xiàn)都在sys_call_table[]函數(shù)數(shù)組里。這樣,將getpid()作為第0號(hào)系統(tǒng)調(diào)用的意思就是將sys_call_table[0] = sys_call_getpid();然后getpid()函數(shù)在發(fā)生陷阱的時(shí)候會(huì)通過(guò)eax傳入系統(tǒng)調(diào)用號(hào)0,這樣在sys_call中就會(huì)調(diào)用sys_call_table[eax * 4],即sys_call_table[0],即sys_call_getpid();  而install_sys_call_handler函數(shù)的作用是將IDT中0x30這一中斷描述符項(xiàng)設(shè)置成DPL = 3的陷阱門,該陷阱門的入口為sys_call,這樣,當(dāng)用戶程序調(diào)用int 0x30的時(shí)候就會(huì)調(diào)用sys_call;而install_sys_call的作用是將getpid()函數(shù)與第0號(hào)系統(tǒng)調(diào)用對(duì)應(yīng)起來(lái),即完成這一工作:sys_call_table[0] = sys_call_getpid();這樣,當(dāng)通過(guò)eax = 0傳入sys_call中時(shí),sys_call知道需要調(diào)用sys_call_table[]中的第幾個(gè)函數(shù)指針。
            到此基本就明白了,還有最后一個(gè)細(xì)節(jié):為什么在系統(tǒng)調(diào)用完成后返回的時(shí)候不使用所有中斷返回時(shí)都使用的宏recover_all,而使用recover_from_sys_call?我們看看代碼:

             1 ; 一個(gè)宏,恢復(fù)現(xiàn)場(chǎng)
             2 %macro recover_all 0
             3     pop gs
             4     pop fs
             5     pop es
             6     pop ds
             7     pop edi
             8     pop esi
             9     pop ebp
            10     pop ebx
            11     pop edx
            12     pop ecx
            13     pop eax
            14 %endmacro
            15 
            16 ; 注意從系統(tǒng)調(diào)用返回時(shí)不需要從棧中彈出eax的值,因?yàn)閑ax保存著調(diào)用
            17 ; 對(duì)應(yīng)系統(tǒng)調(diào)用之后的返回值
            18 %macro recover_from_sys_call 0
            19     pop gs
            20     pop fs
            21     pop es
            22     pop ds
            23     pop edi
            24     pop esi
            25     pop ebp
            26     pop ebx
            27     pop edx
            28     pop ecx
            29     add esp, 4 * 1
            30 %endmacro

            仔細(xì)研究代碼應(yīng)該已經(jīng)明白了,其實(shí)getpid()是需要返回值的,恰好我們就使用eax作為返回值,這樣,在從堆棧中恢復(fù)現(xiàn)場(chǎng)的時(shí)候就不能將eax的值恢復(fù),因?yàn)閑ax還需要傳遞返回值。
            至此,所有的系統(tǒng)調(diào)用細(xì)節(jié)應(yīng)該明白了,這里只是講述的一種系統(tǒng)調(diào)用的實(shí)現(xiàn),而有些操作系統(tǒng)的系統(tǒng)調(diào)用采用完全不同的風(fēng)格實(shí)現(xiàn)的,它沒(méi)有sys_call_table[]這樣的函數(shù)數(shù)組,而是將每個(gè)系統(tǒng)調(diào)用都設(shè)置成一個(gè)陷阱門,在IDT中都占有一個(gè)描述符項(xiàng),至于優(yōu)劣,其實(shí)沒(méi)有大的差別。WinixJ使用的方法正是Linux的方法。在此向Linus和Linux致敬。
            posted on 2012-02-14 15:41 myjfm 閱讀(519) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 操作系統(tǒng)
            无码精品久久一区二区三区| 人妻丰满AV无码久久不卡| 精品水蜜桃久久久久久久| 久久久青草青青国产亚洲免观| 久久精品国产99国产精品导航| 久久久久久国产精品无码超碰| 国产一区二区精品久久岳| 国产偷久久久精品专区| 精品久久人人妻人人做精品| 亚洲综合伊人久久大杳蕉| 久久久久国产一区二区| av无码久久久久不卡免费网站| 久久久受www免费人成| 久久久久久久人妻无码中文字幕爆 | 国产精品99久久久久久宅男小说 | 国内精品久久久久久久久电影网| 久久人人爽人人爽人人片AV不 | 激情伊人五月天久久综合| 亚洲人成无码久久电影网站| 91精品国产91久久久久久青草| 超级碰碰碰碰97久久久久| 久久国产精品免费| 久久精品人人做人人爽97| 精品国产99久久久久久麻豆| 久久亚洲电影| 久久久久国产| 激情五月综合综合久久69| 秋霞久久国产精品电影院| 久久国产精品99国产精| 色综合久久中文字幕无码| 久久精品日日躁夜夜躁欧美| 亚洲伊人久久综合影院| 三级片免费观看久久| 亚洲国产精品无码久久青草| 久久久久亚洲av毛片大| 久久人人爽人人爽AV片| 久久久亚洲精品蜜桃臀| 日本久久中文字幕| 思思久久99热只有频精品66| 亚洲AV日韩精品久久久久久久| 99久久夜色精品国产网站|