Linux0.11内核--汇编和C语言混合编程之参数传递

<!-- @page { margin: 0.79in } P { margin-bottom: 0.08in } -->

汇编语言中也需要通过某些途径来使用操作系统提供的服务,也就是系统调用;系统调用就是通过与操作系统内核通信来完成;系统调用会把用户态程序的调用转换成对系统内核服务的调用;
Linux
平台下有两种方式来使用系统调用:一种是利用封装后的C(libc),另一种是通过汇编直接调用;其中,通过汇编语言来直接调用系统调用,是最高效地使用Linux内核服务的方法,因为最终生成的程序不需要与任何库进行连接,而是直接与内核通信;
DOS一样,Linux下的系统调用也是通过中断(int 0x80)方式来实现的;
在执行"int $0x80"指令时,寄存器eax中存放的是系统调用的功能号(:中断功能号,DOS下存放在AH),所有的系统调用功能号都可以再文件/usr/include/bits/syscall.h中找到,为了便于使用,它们都是用"SYS_<name>"这样的宏来定义的;:SYS_writeSYS_exit;
系统调用的参数传递规则:
传递给系统调用的参数则必须按照参数顺序依次存放到寄存器ebx,ecx,edx,esi,edi,当系统调用完成之后,返回值存放在eax;
A.当系统调用所需参数的个数不超过5个的时候,执行"int $0x80"指令时,需在eax中存放系统调用的功能号,传递给系统调用的参数则按照参数顺序依次存放到寄存器ebx,ecx,edx,esi,edi,当系统调用完成之后,返回值存放在eax;
比如,经常用到的write函数的定义如下:
ssize_t write(int fd, const void* buf, size_t count);
该函数的功能最终通过SYS_write这一系统调用来实现的;根据上面的参数传递规则可知,参数fd存放在ebx,参数buf存放在ecx,参数count存放在edx,而系统调用功能号SYS_write则存放在寄存器eax;系统调用执行完成之后,返回值可以从eax中得到;
B.当系统调用的参数超过5个的时候,执行"int $0x80"指令,需在eax中存放系统调用的功能号,所不同的只是全部的参数应该依次存放在一块连续的内存区域里,同时在寄存器ebx中保存指向该内存区域的指针(:该连续内存块的首地址),返回值仍然保存在寄存器eax;由于只是需要一块连续的内存区域来保存系统调用所需要的参数,因此,完全可以像普通的函数调用一样使用栈来传递系统调用所需要的参数;但是要注意一点:Linux采用的是C语言的调用模式,这就意味着所有参数必须以相反的顺序进栈,:最后一个参数最先进栈,而第一个参数最后进栈;如果采用栈来传递系统调用所需要的参数,在执行"int $0x80"指令时,还应将栈指针的当前值(栈顶地址)复制到寄存器ebx;
例如,系统调用mmap()的参数个数就超过5:
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
使用这个系统调用时,系统调用功能号保存到eax,mmap()所需要的所有参数存放到一块连续的内存区域中,这块连续内存区域的首地址存放到ebx,即可;
C库函数的参数传递规则:
1
、参数从右向左依次入栈(push);
2
、库函数的返回值存放在寄存器eax;
3
、系统调用exit()的参数值在exit()调用结束程序退出的时候会被传递给系统shell,通过打印$?的值可看到;
汇编函数的返回值:
只要是函数,一般都需要返回值;汇编语言中约定:对于32位的返回值,存放在寄存器eax中返回;对于64位的返回值,存放在寄存器eaxedx中返回,高位字放在edx,低位字放在eax;

例如:

 sys_fork()调用,用于创建子进程,是system_call 功能2。原形在include/linux/sys.h 中。
 首先调用C 函数find_empty_process(),取得一个进程号pid。若返回负数则说明目前任务数组
 已满。然后调用copy_process()复制进程。
Linux0.11内核--汇编和C语言混合编程之参数传递

你可能感兴趣的:(linux)