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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值