第章 Linux系统调用.ppt
文本预览下载声明
* * * * Fork()调用号缺省为5, 思考为什么不直接传递?寄存器的使用使得系统调用处理程序的结构与其它异常处理程序的结构类似。 * 在少数情况下,系统调用不使用任何参数 如果采用栈来传递系统参数, * 调用参数在堆栈中的压栈顺序为从左到右,各参数对应的寄存器与系统调用发生时的堆栈结构有关 * * 到此,内核已经能够正确地找到并调用sys_mysyscall.接下来实现该例程。 * 在Linux中所有系统调用服务例程都使用了asmlinkage标志。此标志是一个定义在/include/linux/linkage.h 中的一个宏: #if defined __i386__ (__GNUC__ 2 || __GNUC_MINOR__ 7) #define asmlinkage CPP_ASMLINKAGE__attribute__((regparm(0))) #else #define asmlinkage CPP_ASMLINKAGE #endif 其中涉及到了gcc的一些约定,总之,这个标志它可以告诉编译器该函数不需要从寄存器中获得任何参数,而是从堆栈中取得参数;即参数在堆栈中传递,而不是直接通过寄存器; * 在这里使用了_syscall0()宏指令,宏指令本身在程序中将扩展成名为syscall()的函数,它在main()函数内部加以调用。 在自定义函数中, 预处理程序产生所有必要的机器指令代码,包括用系统调用参数值加载相应的cpu寄存器, 然后执行int 0x80中断指令。 上面的例子是把系统调用直接载入内核,因此,需要重新编译内核。另一种方法是把系统调用以模块的形式加载到内核中。 用寄存器传递参数必须满足两个条件: 每个参数的长度不能超过寄存器的长度 参数的个数不能超过6个(包括eax中传递的系统调用号);否则,需要用一个单独的寄存器(ebx)指向进程地址空间中这些参数值所在的一个内存区即可 返回值必须写到eax寄存器中 系统调用的参数传递 当一个系统调用所需的参数个数大于 5 时,执行int 0x80 指令时仍需将系统调用功能号保存在寄存器 eax 中,所不同的只是全部参数应该依次放在一块连续的内存区域里,同时在寄存器 ebx 中保存指向该内存区域的指针。系统调用完成之后,返回值仍将保存在寄存器 eax 中。 由于只是需要一块连续的内存区域来保存系统调用的参数,因此完全可以像普通的函数调用一样使用栈(stack)来传递系统调用所需的参数。 但要注意一点,Linux 采用的是 C 语言的调用模式,这就意味着所有参数必须以相反的顺序进栈,即最后一个参数先入栈,而第一个参数则最后入栈。如果采用栈来传递系统调用所需的参数,在执行int 0x80 指令时还应该将栈指针的当前值复制到寄存器 ebx中。 参数传递举例 处理write系统调用的sys_write服务例程声明如下 该函数期望在栈顶找到fd,buf和count参数 在封装sys_write()的封装例程中,将会在ebx、ecx和edx寄存器中分别填入这些参数的值,然后在进入system_call时,SAVE_ALL会把这些寄存器保存在堆栈中,进入sys_write服务例程后,就可以在相应的位置找到这些参数 asmlinkage使得编译器不通过寄存器(x=0)而 使用堆栈传递参数 SAVE_ALL Sys_write需要的参数 系统调用的参数传递-举例 设C库中封装的系统调用号为3的函数原形如下: int sys_func(int para1, int para2) C编译器产生的汇编伪码如: …movl 0x8(%esp),%ecx /*将用户态堆栈中的para2放入ecx Movl 0x4(%esp),%ebx /*#将用户态堆栈中的para1放入ebx Movl $0x3,%eax /*系统调用号保存在eax中int$0x80 #引发系统调用 … Movl %eax,errno /*将结果存入全局变量errno中 Movl $-1,%eax /*eax置为-1,表示出错注 5.5 练习:添加一个系统调用mysyscall 功能要求 首先,自定义一个系统调用mysyscall ,它的功能是使用户的uid等于0 。然后,编写一段测试程序进行该系统调用的使用。 执行步骤如下: 添加系统调用号 在系统调用表中添加相应的表项 实现系统调用服务例程 重新编译内核,启动新内核 编写一段测试程序检验实验结果 添加一个系统调用mysyscall (1)添加系统调用号:它位于unistd.h,每个系统调用号都以“_NR_开头”, 系统调用的编号命名为
显示全部