龙空技术网

一步步编写操作系统 68 系统调用的实现2

郑大米刚 114

前言:

如今我们对“c语言调用系统命令”都比较注意,看官们都想要学习一些“c语言调用系统命令”的相关知识。那么小编在网上搜集了一些关于“c语言调用系统命令””的相关文章,希望咱们能喜欢,同学们一起来了解一下吧!

调用“系统调用”有两种方式:

将系统调用指令封装为c库函数,通过库函数进行系统调用,操作简单。不依赖任何库函数,直接通过汇编指令int与操作系统通信。

以上的c代码就是用的第一种方式,不知道您是否对write函数的内部实现感兴趣,其实我也没研究过,不过万变不离其宗,核心思想是必须与进行内核沟通才能获得内核提供的功能。所以,write内部封装的一定是系统调用指令,按照这种设想,一会咱们会模拟一下它的实现。

我们这里要介绍下第二种:跨过库函数直接与系统内核通信,这样最终的程序是不需要与任何库文件链接,这是获得系统功能效率最高的方式。

我相信,如果曾经学过汇编语言,老师都给咱们演示过第二种方式,但大多数同学还是觉得云里雾里,即使照葫芦画瓢完成了打印字符串的工作,也有部分同学不清楚自己在做什么,所以我在这里尽量多说一点。

前面我们已经知道了write系统调用函数的c语言使用方式,我们要用汇编代码直接与内核通信该怎么做?我们要看看系统调用输入参数的传递方式。

当输入的参数小于等于5个时,linux是用寄存器传递参数。当参数个数大于5个时,把参数按照顺序放入连续的内存区域,并将该区域的首地址放到ebx寄存器。这里我们只演示参数小于等于5个的情况。

eax寄存器用来存储子功能号(寄存器eip、ebp、esp是不能使用的)。5个参数是存放在以下寄存器中,传送参数的顺序是:

ebx存储第1个参数ecx存储第2个参数edx存储第3个参数esi存储第4个参数edi存储第5个参数

好啦,理论知识够用啦,现在赶紧实践一把,见以下代码syscall_write.S

 1 section .data 2 str_c_lib: db "c library says: hello world!", 0xa ;0xa为LF ascii码 3 str_c_lib_len equ $-str_c_lib 4 5 str_syscall: db "syscall says: hello world!", 0xa 6 str_syscall_len equ $-str_syscall 7 8 section .text 9 global _start 10 _start: 11 ;;;;;;;;;;;;; 方式1: 模拟c语言中系统调用库函数write ;;;;;;;;;;;;; 12 push str_c_lib_len ;按照c调用约定压入参数 13 push str_c_lib 14 push 1 15 16 call simu_write ;调用下面定义的simu_write 17 add esp,12 ;回收栈空间 18 19 ;;;;;;;;;;;;; 方式2: 跨过库函数,直接进行系统调用 ;;;;;;;;;;;;; 20 mov eax,4 ;第4号子功能是write系统调用(不是c库函数write) 21 mov ebx, 1 22 mov ecx, str_syscall 23 mov edx, str_syscall_len 24 int 0x80 ;发起中断,通知linux完成请求的功能。 25 26 ;;;;;;;;;;;;; 退出程序 ;;;;;;;;;;; 27 mov eax,1 ;第1号子功能是exit 28 int 0x80 ;发起中断,通知linux完成请求的功能。 29 30 ;;;;;;;下面自定义的simu_write用来模拟c库中系统调用函数write, ;;;;;;这里模拟它的实现原理 31 simu_write: 32 push ebp ;备份ebp 33 mov ebp,esp 34 mov eax,4 ;第4号子功能是write系统调用(不是c库函数write)  35 mov ebx, [ebp+8] ;第1个参数 36 mov ecx, [ebp+12] ;第2个参数 37 mov edx, [ebp+16] ;第3个参数 38 int 0x80 ;发起中断,通知linux完成请求的功能 39 pop ebp ;恢复ebp 40 ret

代码syscall_write.S中,我们演示了系统调用的两种方式。程序开头定义了两种方式下打印的字符串,其中0xa为LF(LineFeed)ascii码,这样就会输出一个换行符。

第11~17行是在演示方式1,模拟调用c库函数write的方式。因为write是c库函数,按一般的做法是,汇编程序需要与c代码生成的目标文件链接才能调用c的代码。在这个例子中我们并没有这样帮做,因为我想让大家了解write函数的本质,所以,在这里为大家定义了simu_write来代替c库函数write,用它来简单解释write的原理,它定义在31~40行。这里是按照c调用约定将参数从右到左依次入栈,随后调用simu_write实现字符串打印功能。

第19~24行是在演示第2种系统调用的方式,这是最简单直接可依赖的方式。20~24行是在eax中赋予子功能号、将参数按照顺序依次写入对应的寄存器。

第31~40行是simu_write的实现,它内部在本质上是和第2种方式一样,都是在内部调用int指令直接和系统通信实现系统调用。此函数只是为了试图揭开c库函数的实现原理,良苦用心您懂的。

好啦,编译链接过程如下:

nasm -f elf -o syscall_write.o syscall_write.S

其中-f参数是用来指定编译输出的文件格式,这里需要指定为elf,目的是将来要和gcc编译的elf格式的目标文件链接,所以格式必须相同。nasm输出为目标文件,已经用-o指定文件名为syscall_write.o。

最后用ld程序将syscall_write.o链接成elf格式的二进制可执行文件。

ld -o syscall_write.bin syscall_write.o

程序执行后的效果如图

顺便说一句,syscall_write.bin如果因为权限不足而无法执行时,可以用以下指令增加执行权限:

chmod u+x syscall_write.bin

本文摘自《操作系统真象还原》,请大家支持正版,多谢。

【再续】

标签: #c语言调用系统命令