爱技术 & 爱分享
爱蛋蛋 & 爱生活

系统调用

Linux上可以使用的系统调用有很多。通常随着每个新的内核版本的发布,都会有新的系统调用添加到这个清单中。在/usr/include/asm/unistd.h中记录了当前系统所支持的系统调用号,当然目前一般是根据宏定义include
/usr/include/asm/unistd_32.h/usr/include/asm/unistd_64.h中的调用号。

/usr/include/asm/unistd_64.h中的部分内容为例:

#ifndef _ASM_X86_UNISTD_64_H
#define _ASM_X86_UNISTD_64_H 1

#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_stat 4
#define __NR_fstat 5
#define __NR_lstat 6
#define __NR_poll 7
#define __NR_lseek 8
#define __NR_mmap 9
#define __NR_mprotect 10
#define __NR_munmap 11
#define __NR_brk 12
....

每个系统调用都被定义为一个名称(前面加上NR),和它的系统调用号。

在系统上的系统调用清单中找到需要的系统调用之后,通常可以在系统的man 页中找到定义。例如:

man 2 read

man 页包含4个主要部分:

  • Name (名称) 显示这个系统调用的名称
  • Synopsis (提要):显示如何使用这个系统调用
  • Description (描述):对这个系统调用的简要描述
  • Return Value (返回值) 系统调用完成时返回的值

启动系统调用,需要使用INT指令。Linux 系统调用位于中断0x80 。执行INT指令时,所有操作转移到内核中的系统调用处理程序。系统调用完成时,执行转移回INT指令之后的下一条指令(当然,除非执行了exit系统调用)。

unistd.h文件中系统调用名称旁边列出的整数就是系统调用号。每个系统调用都被分配了唯一的数字以便标识它。在执行INT指令之前需要将期望执行的系统调用号传送到EAX寄存器中。

例如exit系统调用:

movl $1, %eax
int Ox80

unistd.h 文件中exit 系统调用的定义如下

#define _NR_exit 1

__cdecl函数中,输入值被存放在堆栈中,系统调用与之不同,需要输入值被存放在寄存器中。每个输入值要按照特定的顺序存放到寄存器中。把错误的输入值存放在错误的寄存器中可能导致灾难性的结果。

EAX寄存器用于保存要执行的系统调用值。不能使用EIP、EBP和ESP寄存器,因为这样可能对程序操作产生有害影响。这样就只剩下5个寄存器可以用于保存输入值。

系统调用期望的输入值顺序如下:

  • EBX (第1 个参数)
  • ECX (第2 个参数)
  • EDX (第3 个参数)
  • ESI (第4 个参数)
  • EDI (第5 个参数)

需要超过6个输入参数的系统调用使用不同的方法把参数传递给系统调用。EBX 寄存器用于保存指向输入参数的内存位置的指针,输入参数按照连续的顺序存储。系统调用使用这个指针访问内存位置以便读取参数。

系统调用的返回值存放在EAX 寄存器中。

###复杂的系统调用###

系统调用sysinfo 可以用于返回关于系统如何配置以及有什么可用资源的信息。系统调用sysinfo使用单一输入值,它指向保存包含返回数据的结构的内存位置。

struct sysinfo {
    long uptime;             /* Seconds since boot */
    unsigned long loads[3];  /* 1, 5, and 15 minute load averages */
    unsigned long totalram;  /* Total usable main memory size */
    unsigned long freeram;   /* Available memory size */
    unsigned long sharedram; /* Amount of shared memory */
    unsigned long bufferram; /* Memory used by buffers */
    unsigned long totalswap; /* Total swap space size */
    unsigned long freeswap;  /* swap space still available */
    unsigned short procs;    /* Number of current processes */
    unsigned long totalhigh; /* Total high memory size */
    unsigned long freehigh;  /* Available high memory size */
    unsigned int mem_unit;   /* Memory unit size in bytes */
    char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding to 64 bytes */
};

每个系统值被返回到结构内的特定位置中。必须在一个内存位置创建这个结构,以便值可以被返回到这里:

.section .data
result:
uptime :
.int 0
loadl:
.int 0
load5:
.int 0
loadl5:
.int 0
totalram:
.int 0
freeram:
.int 0
sharedram:
.int 0
bufferram:
.int 0
total swap:
.int 0
freeswap:
.int 0
totalhigh:
.int 0
freehigh:
.int 0
memunit:
.int 0

在数据定义的开头有两个标签。当对程序进行汇编时,它们都指向相同的内存位置。标签result可以用于引用整个结构,标签uptime可以用于引用结构中的第一个值。

上述笔记内容学习自 AT&T Professional Assembly Language — Chapter XII

赞(2)
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。编程日志 » 系统调用
分享到: 更多 (0)

游戏 && 后端

传送门传送门