<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux系統(tǒng)調(diào)用原理

          共 2625字,需瀏覽 6分鐘

           ·

          2020-11-11 22:07

          一、什么是系統(tǒng)調(diào)用

          系統(tǒng)調(diào)用?跟用戶自定義函數(shù)一樣也是一個函數(shù),不同的是?系統(tǒng)調(diào)用?運行在內(nèi)核態(tài),而用戶自定義函數(shù)運行在用戶態(tài)。由于某些指令(如設置時鐘、關(guān)閉/打開中斷和I/O操作等)只能運行在內(nèi)核態(tài),所以操作系統(tǒng)必須提供一種能夠進入內(nèi)核態(tài)的方式,系統(tǒng)調(diào)用?就是這樣的一種機制。

          系統(tǒng)調(diào)用?是 Linux 內(nèi)核提供的一段代碼(函數(shù)),其實現(xiàn)了一些特定的功能,用戶可以通過?int 0x80?中斷(x86 CPU)或者?syscall?指令(x64 CPU)來調(diào)用?系統(tǒng)調(diào)用

          二、進入系統(tǒng)調(diào)用

          本文主要介紹的是 x86 CPU 進入系統(tǒng)調(diào)用的方式

          Linux 提供了?int 0x80?中斷來讓用戶程序進入?系統(tǒng)調(diào)用,我們來看看 Linux 對?int 0x80?中斷的處理初始化過程:

          void __init trap_init(void)
          {
          ...
          set_system_gate(SYSCALL_VECTOR, &system_call);
          ...
          }

          系統(tǒng)初始化時,會在?trap_init()?函數(shù)中對?int 0x80?中斷處理進行初始化,設置其中斷處理過程入口為?system_callsystem_call?是一段由匯編語言編寫的代碼,我們看看關(guān)鍵部分,如下:

          ENTRY(system_call)
          ...
          call *SYMBOL_NAME(sys_call_table)(,%eax,4)
          movl %eax,EAX(%esp) # save the return value
          ...

          我們把上面的匯編改寫成 C 代碼如下:

          void system_call()
          {
          ...
          // 變量 eax 代表 eax 寄存器的值
          syscall = sys_call_table[eax];
          eax = syscall();
          ...
          }

          sys_call_table?變量是一個數(shù)組,數(shù)組的每一個元素代表一個?系統(tǒng)調(diào)用?的入口,其定義如下(在文件 arch/i386/kernel/entry.S 中):

          .data
          ENTRY(sys_call_table)
          .long SYMBOL_NAME(sys_ni_syscall)
          .long SYMBOL_NAME(sys_exit)
          .long SYMBOL_NAME(sys_fork)
          .long SYMBOL_NAME(sys_read)
          .long SYMBOL_NAME(sys_write)
          .long SYMBOL_NAME(sys_open)
          .long SYMBOL_NAME(sys_close)
          ...

          翻譯成 C 代碼如下:

          long sys_call_table[] = {
          sys_ni_syscall,
          sys_exit,
          sys_fork,
          sys_read,
          sys_write,
          sys_open,
          sys_close,
          ...
          };

          用戶調(diào)用?系統(tǒng)調(diào)用?時,通過向?eax?寄存器寫入要調(diào)用的?系統(tǒng)調(diào)用?編號,這個編號就是?sys_call_table?數(shù)組的下標。?system_call?過程獲取?eax?寄存器的值,然后通過?eax?寄存器的值找到要調(diào)用的?系統(tǒng)調(diào)用?入口,并且進行調(diào)用。調(diào)用完成后,系統(tǒng)調(diào)用?會把返回值保存到?eax?寄存器中。

          原理如下圖(圖片來源?https://developer.ibm.com/zh/technologies/linux/tutorials/l-system-calls/?):

          三、系統(tǒng)調(diào)用實現(xiàn)

          當用戶要調(diào)用?系統(tǒng)調(diào)用?時,需要通過向?eax?寄存器寫入要調(diào)用的?系統(tǒng)調(diào)用?編號。因為?用戶態(tài)?和?內(nèi)核態(tài)?使用的棧不同,而調(diào)用?系統(tǒng)調(diào)用?是在用戶態(tài)調(diào)用的,而進入?系統(tǒng)調(diào)用?后會變成內(nèi)核態(tài),所以參數(shù)就不能通過棧來傳遞。Linux 使用寄存器來傳遞參數(shù),參數(shù)與寄存器的關(guān)系如下:

          • 第1個參數(shù)放置在?ebx?寄存器。

          • 第2個參數(shù)放置在?ecx?寄存器。

          • 第3個參數(shù)放置在?edx?寄存器。

          • 第4個參數(shù)放置在?esi?寄存器。

          • 第5個參數(shù)放置在?edi?寄存器。

          • 第6個參數(shù)放置在?ebp?寄存器。

          而 Linux 進入中斷處理程序時,會把這些寄存器的值保存到內(nèi)核棧中,這樣?系統(tǒng)調(diào)用?就能通過內(nèi)核棧來獲取到參數(shù)。

          下面我們通過?sys_open()?系統(tǒng)調(diào)用來說明一下?系統(tǒng)調(diào)用?的運作方式,sys_open()?實現(xiàn)如下:

          asmlinkage long sys_open(const char *filename, int flags, int mode)
          {
          ...
          }

          一般?系統(tǒng)調(diào)用?都需要使用?asmlinkage?編譯選項,asmlinkage?編譯選項是告訴編譯器從棧中讀取參數(shù),其實際是封裝了 GCC 的編譯選項,如下:

          #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

          __attribute__((regparm(0)))?就是告訴 GCC 所有參數(shù)都從棧中讀取,而 Linux 進入中斷處理上下文時,會把?ebxecxedxesiediebp?寄存器的值保存到內(nèi)核棧中,那么?系統(tǒng)調(diào)用?就可以從內(nèi)核棧獲取到參數(shù)的值。

          但由于寄存器只能傳遞 32 位的整型值(x86 CPU),所以參數(shù)一般只能傳遞指針或者整型的數(shù)值,如果要獲取指針對應結(jié)構(gòu)的數(shù)據(jù),就必須通過從用戶空間復制到內(nèi)核空間,如?sys_open()?系統(tǒng)調(diào)用獲取要打開的文件路徑:

          asmlinkage long sys_open(const char *filename, int flags, int mode)
          {
          char * tmp;
          ...
          tmp = getname(filename);
          ...
          }

          getname()?函數(shù)就是用于從用戶空間復制數(shù)據(jù)到內(nèi)核空間。


          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久国产一级毛片高 | 大香蕉国产在线一区 | 黄色色播网站 | 国产亚洲精品久久久久动 | 99高清免费视频 |