【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途】
在前面章节我们已经看到,启动系统调用需要使用INT指令。linux系统调用位于中断0x80,执行INT指令时,所有操作转移到内核中的系统调用处理程序,完成后执行转移到INT指令之后的下一条指令。
linux的系统调用在如下文件(32位系统)可以查看:
$ cat /usr/include/asm/unistd_32.h
#ifndef _ASM_X86_UNISTD_32_H
#define _ASM_X86_UNISTD_32_H
/*
* This file contains the system call numbers.
*/
#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
#define __NR_waitpid 7
#define __NR_creat 8
#define __NR_link 9
#define __NR_unlink 10
......
上面文件中系统调用名称后面的整数就是系统调用值,每个系统调用都被分配了唯一的数字以便标识它。在前面章节的范例中我们已经知道eax寄存器用于保存系统调用值。在执行INT指令之前,期望的值被传送到eax寄存器中,这时,系统调用已经准备好了,接下来就是给系统调用准备参数的时候了。和我们自己写的函数输入值存放在堆栈中不同,系统调用的输入参数存放在寄存器中。输入值存放到寄存器中的顺序也是很重要的,系统调用期望的输入值顺序(第1到第5个参数)如下:ebx、ecx、edx、esi、edi。需要超过6个输入参数的系统调用使用不同方法吧参数传递给系统调用,使用ebx寄存器用于保存指向输入参数的内存位置的指针。
在本系列文章前面我们打印“hello world!”一节中使用到write系统调用代码如下:
_start: #函数在屏幕上输出hello world!输入值被依次分配到了ebx、ecx、edx寄存器中,而系统调用号4被分配到了eax寄存器中。
movl $len, %edx #第三个参数: 字符串长度
movl $msg, %ecx #第二个参数: hello world!字符串
movl $1, %ebx #第一个参数: 输出文件描述符
movl $4, %eax #系统调用号sys_write
int $0x80 #调用内核功能
系统调用的返回值存放在eax寄存器中,在实际使用时,可以判断系统调用的返回值。
注意到系统调用函数和C库函数的是由区别的,C库函数的输入参数是在堆栈中,而系统调用的输入参数在寄存器中。在是使用C库函数还是使用系统调用函数问题上,原则是看哪一种方法更适合时间的应用。C库的优点是在操作系统之间方便移植、C库可以在程序之间利用共享库,减少内存需求。linux系统调用的优点是不需要吧外部库链接到程序中,可以尽可能创建最短、最快的代码。