用asm内联汇编实现系统调用

原创内容(cxsmarkchan 陈晓爽)
转载请注明出处
《Linux内核分析》MOOC课程学习笔记
为保证系统的稳定运行,CPU运行状态被分为内核态和用户态。操作系统在内核态下运行,因此拥有所有计算机资源的操作权限。而一般的应用程序则在用户态下运行,它们不能直接操作底层的硬件设备,从而保证应用软件不会破坏系统的稳定。但是,应用程序在运行时常常需要和各种资源打交道,为此,操作系统提供了“系统调用”的功能,提供一组API,由用户态程序调用。本文通过asm内联汇编,分析系统调用的全过程。

1 系统调用的概念

下图是一个典型的系统调用图示:
用asm内联汇编实现系统调用_第1张图片
从该函数可以看到,系统调用分成如下过程:
1. 用户态程序调用API函数xyz
2. 在xyz接口函数内部,通过中断号0x80进入系统调用,此时CPU进入内核态。
3. CPU开始执行中断处理程序,根据用户传入的信息(系统调用号和相关参数),执行相应的内核态函数,并返回结果。
这里有两个问题:
1. 内核态的切换是通过中断方式进入的,而产生中断时只能传入一个中断向量(即0x80),而系统调用有大量的API函数,系统如何知道调用哪一个函数呢?
2. 有一些API函数带有参数,而系统调用并没有才有函数调用(call)方式,那么参数如何传递到被调用函数?
答案是:在调用int 0x80进入系统调用前,预先把系统调用号和相关参数存入指定的寄存器中。这样,系统调用函数只需要访问相应的寄存器,就可以获得所有的信息。
事实上,在进入系统调用前,首先需要将系统调用号传入eax寄存器中,并将参数依次传入ebxecxedxesiediebp寄存器中。系统调用最多只能传入6个参数,如果参数多于6个,则需要将参数预存在内存中,然后将参数指针传入寄存器。系统调用结束后,eax会被替换为系统调用返回值。

2 用asm实现系统调用的实例

本文运行平台为实验楼Linux内核分析的第4个实验,运行环境为linux系统。
为了验证系统调用的全过程,我们以exit函数为例,给出代码如下:

#include 
#include 
int main(){
    int id;
    scanf("%d", &id);
    switch(id){
        case 0:
            //用C语言的方式调用exit函数
            printf("C exit\n");
            exit(0);
            break;
        case 1:
            //用内联汇编的方式调用exit函数
            printf("asm exit\n");
            asm volatile(
                    "mov $0, %%ebx\n\t"
                    "mov $0x1, %%eax\n\t"
                    "int $0x80\n\t"
                    :
                    :
                    );
            break;
        default:
            printf("others\n");
            break;
    }
    printf("before return\n");
    return 0;
}

该函数中,首先输入参数id。如果id为0,则调用C代码的exit函数。如果id为1,则调用汇编代码的exit函数。如果id为其他值,则顺序执行至main函数结尾。
事实上,上文的代码采用C代码和内联汇编实现了等价的功能,即exit(0)功能。可以分析一下内联汇编的工作方式:
1. mov $0, %ebx:exit函数的第1个参数(也是唯一一个参数)为0,按照寄存器顺序,应该放在ebx中。
2. mov $0x1, %eax:exit函数的系统调用号为0x1,因此把系统调用号放入eax中。
3. int 0x80:产生中断,中断号0x80表示系统调用。
执行了以上3步,即为执行了exit(0)函数。程序的运行结果如下:
用asm内联汇编实现系统调用_第2张图片
可见,输入0或1时,都没有输出"before return",说明exit被成功调用,程序提前退出。

3 小结

系统调用既保证了操作系统的安全运行,也方便了用户态程序使用系统资源。用内联汇编的方式处理系统调用,可以很清晰地看出系统调用的过程,以及系统调用的参数传递方式。系统调用通过寄存器传递参数,因此在进行系统调用前,通常还需要备份相关寄存器中的信息。不过,在内联汇编中,这个工作会被编译器代劳。

你可能感兴趣的:(Linux,汇编)