• <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  評論-24  文章-0  trackbacks-0
            該文件是系統調用實現的主要文件。為了弄清楚系統調用,首先應該拿一個實例來看。
            WinixJ系統僅僅實現了一個系統調用getpid(),但是其他系統調用的框架應是完全相同的,只在函數實現細節上有所不同,因此我們打算以getpid()作為案例講解。
            先看看getpid()呈現給用戶的調用接口吧:
            POSIX規范的getpid()聲明如下:
            pid_t getpid();等同于int getpid();為了簡單起見我們就實現為int getpid();
            它的代碼也十分簡單:

             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 

            雖然有嵌入匯編,但是也不難理解,看似是將eax、ebx、ecx、edx四個寄存器均傳值為0,然后調用軟中斷int 0x30(實際上應該稱之為陷阱,陷阱和中斷還是有細微區別的,見此博文),最后將返回的結果由eax賦值給res變量。很簡單,不是嗎?
            但是為何eax、ebx、ecx、edx都賦值了呢?調用int 0x30之后又發生什么了呢?
            先說調用int 0x30之后會發生的事情。我們看下面這段代碼,其實它在此博文已經出現過,就是sys_call函數的實現:

             1 ; 系統調用框架,系統調用采用0x30號中斷向量,利用int 0x30指令產
             2 ; 生一個軟中斷,之后便進入sys_call函數,該函數先調用save_all框
             3 ; 架保存所有寄存器值,然后調用對應系統調用號的入口函數完成系統調用
             4 ; 注意!!!!!系統調用默認有三個參數,分別利用ebx、ecx、edx來
             5 ; 傳遞,其中eax保存系統調用號
             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 ; 一個宏,因為所有的irq中斷函數以及系統調用都是先保存現場并將數據段等堆棧段
             2 ; 切換到內核態,因此,該操作所有的irq中斷入口函數均相同
             3 ; 故寫成宏節省空間
             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 

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

             1 ; 一個宏,恢復現場
             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 ; 注意從系統調用返回時不需要從棧中彈出eax的值,因為eax保存著調用
            17 ; 對應系統調用之后的返回值
            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

            仔細研究代碼應該已經明白了,其實getpid()是需要返回值的,恰好我們就使用eax作為返回值,這樣,在從堆棧中恢復現場的時候就不能將eax的值恢復,因為eax還需要傳遞返回值。
            至此,所有的系統調用細節應該明白了,這里只是講述的一種系統調用的實現,而有些操作系統的系統調用采用完全不同的風格實現的,它沒有sys_call_table[]這樣的函數數組,而是將每個系統調用都設置成一個陷阱門,在IDT中都占有一個描述符項,至于優劣,其實沒有大的差別。WinixJ使用的方法正是Linux的方法。在此向Linus和Linux致敬。
            posted on 2012-02-14 15:41 myjfm 閱讀(524) 評論(0)  編輯 收藏 引用 所屬分類: 操作系統
            亚洲精品99久久久久中文字幕| 午夜福利91久久福利| 久久精品麻豆日日躁夜夜躁| 久久精品夜夜夜夜夜久久| 国产一区二区三区久久精品| 国产巨作麻豆欧美亚洲综合久久| 久久狠狠一本精品综合网| 久久久久久国产a免费观看黄色大片| 亚洲欧美一区二区三区久久| 99久久婷婷免费国产综合精品| 中文字幕亚洲综合久久| 久久精品国产亚洲AV蜜臀色欲| 精品久久久久久无码专区不卡| segui久久国产精品| 性欧美丰满熟妇XXXX性久久久 | 精品精品国产自在久久高清| 欧美大战日韩91综合一区婷婷久久青草 | 亚洲国产精品无码成人片久久| 久久青青草原国产精品免费| 亚洲精品无码专区久久同性男| 1000部精品久久久久久久久| 精品伊人久久久| 久久99精品久久久久久水蜜桃| 少妇高潮惨叫久久久久久| 久久精品国产一区二区三区| 狠狠干狠狠久久| 亚洲精品蜜桃久久久久久| 久久乐国产精品亚洲综合| 99久久人妻无码精品系列蜜桃| 久久精品成人欧美大片| 久久国产成人精品国产成人亚洲| 国产成人精品久久免费动漫| 精品久久久久久无码中文字幕一区| 伊人久久无码精品中文字幕| 久久久久久亚洲精品无码| 91久久精品电影| 青青草国产精品久久久久| 狠狠色丁香久久婷婷综合五月| 亚洲精品国精品久久99热一| 无码日韩人妻精品久久蜜桃 | 久久久久亚洲av无码专区导航|