Linux系統調用

Linux系統調用實現原理舉例

Linux系統調用的原理很簡單:將系統調用號放在eax寄存器,將參數放在相關寄存器,然後通過int指令產生一個軟中斷便進入內核。從內核返回後,eax寄存器裏面存放的是返回值。以下針對常用的x86以及x86_64體系結構,用sys_write系統調用進行舉例說明:

x86版本

這個例子利用sys_write系統調用往標準輸出輸出一個"Hello world!"字符串。x86下,sys_write系統調用號是4,需要的參數分別是文件描述符,字符串地址,還有字符串長度,依次放在ebx、ecx以及edx寄存器。通過int指令產生一個軟中斷,從而進入內核。內核處理完系統調用後,將返回值放在eax寄存器。例子中實現了一個myprint函數,該函數實現了參數準備,激發系統調用以及結果處理等邏輯,功能層次跟libc類似。x86系統調用號請參看:點擊打開鏈接
#include <stdint.h>  // ① 包含幾個頭文件,獲得64位變量定義,strlen定義,以及errno定義等基本功能
#include <string.h>
#include <errno.h>

int myprint(char *msg)
{
  uint32_t syswrite_no=4, ret;  // ②  參數聲明並初始化
  uint32_t write_fd=1, msg_len=strlen(msg);

  asm("int $0x80;"  // ③ 準備參數(設置相關寄存器),int指令產生一個0x80軟中斷,進入內核
      :"=a"(ret)
      :"a"(syswrite_no), "b"(write_fd), "c"(msg), "d"(msg_len));

  if (ret < 0)
  {
    errno = -ret;  // ④ 如果系統調用出錯,設置errno,返回-1
    return -1;
  }

  return 0;
}

int main(int argc, char *argv[])
{
  char *msg = "Hello world!\n";  // ⑤ 準備參數,然後調用myprint,跟調用printf參不多
  if (myprint(msg) == -1)
    perror("myprint");

  return 0;
}
說明:asm第一個冒號表示輸出寄存器,最後把eax寄存器的值輸出到ret變量;第二個冒號表示輸入寄存器,把syswrite_no、write_fd、msg,以及msg_len變量的值分別放到eax,ebx,ecx,以及edx寄存器。這裏由於x86的通用寄存器有限,就沒有進行展開,下面x86_64的例子有做展開。

x86_64版本

在x86_64機器下,跟x86差不多,只有一點小差別。系統調用號放在rax寄存器,參數依次放在rdi,rsi和rdx等寄存器裏面,最後用syscall指令進入內核。注意到,應該使用64位寄存器。從內核返回後,返回值就在rax寄存器裏面。再次注意,不能用int 0x80指令進入內核,x86_64下是使用一條更專用的指令:syscall。系統調用號跟x86下也有區別,具體請看這裏:點擊打開鏈接

#include <stdint.h>  // ① 包含幾個頭文件,獲得64位變量定義,strlen定義,以及errno定義等基本功能
#include <string.h>
#include <errno.h>

int myprint(char *msg)
{
  uint64_t syswrite_no=1, ret;  // ②  參數聲明並初始化
  uint64_t write_fd=1, msg_len = strlen(msg);

  asm("movq %1, %%rax;"  // ③ 準備參數(設置相關寄存器),syscall指令進行系統調用
      "movq %2, %%rdi;"
      "movq %3, %%rsi;"
      "movq %4, %%rdx;"
      "syscall;"
      "movq %%rax, %0;"
      : "=r"(ret)
      : "r"(syswrite_no), "r"(write_fd), "r"(msg), "r"(msg_len)
      : "%rax", "%rdi", "%rsi", "%rdx");

  if (ret < 0)
  {
    errno = -ret;  // ④ 如果系統調用出錯,設置errno,返回-1
    return -1;
  }

  return 0;
}

int main(int argc, char *argv[])
{
  char *msg = "Hello world!\n";  // ⑤ 準備參數,然後調用myprint,跟調用printf參不多
  if (myprint(msg) == -1)
    perror("myprint");

  return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章