董涛
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
基于实验楼网站提供的《Linux内核分析》实验平台,以库函数uid_t getuid()和C代码中嵌入汇编代码两种方式,返回一个调用程序的真实用户ID,并在此基础上,仔细分析汇编代码调用系统调用的工作过程。
首先,创建包含getuid()函数的getuid.c文件和包含汇编代码的文件getuid_asm.c,两个文件的截图如下所示:
将这两个程序文件分别使用gcc命令编译成可执行文件,然后运行,先来看一下运行的结果,截图如下:
可见,这两个程序都返回了用户ID1000,运行正确。下面来分析一下这两个程序的运行机制,从而理解操作系统对系统调用的调用:
对包含库函数的getuid.c程序,程序在运行中通过调用库函数uid=getuid()获得了用户的ID并赋值给变量uid,在这一过程中,库函数使用了24号封装例程,使得用户不用考虑硬件层面的细节,这一过程将和下面我们要分析的包含汇编代码的程序形成对比。
对于包含汇编代码的getuid_asm.c程序,其汇编代码及其注释如下:
asm volatile(
"mov $0x0,%%ebx\n\t" \\系统调用的第一个参数时ebx,这里是NULL
"mov $0x18,%%eax\n\t" \\将24号系统调用,即16进制的18号系统调用作为参数,存放到参数传递寄存器eax中
"int $0x80\n\t" \\使用中断调用系统调用;
"mov %%eax,%0\n\t" \*系统调用返回值放在eax中,并将eax中的值赋给0号变量,即内存中的uid变量
:"=m"(uid) *\
);
好了,至此我们大致了解了操作系统系统调用的机制,在使用库函数的时候,库函数通过使用系统调用的封装例程来实现系统调用,在不使用库函数的时候,程序通过系统调用入口点system_call进入系统调用,然后根据系统调用参数实现具体的调用,最终将计算结果返回给主程序。