使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 《Linux内核分析》笔记
内核态和系统调用
操作系统将进程划分为内核态和用户态,用户的程序运行在用户态下,而涉及低级和设备资源的调用在内核态中完成。为了方便用户程序使用各种系统资源,操作系统为我们提供了系统调用,而避免用户之间操作各种系统资源所带来的隐患。
实验原理
使用系统调用的方式通常有使用库函数调用和中断传入系统调用号的方式两种。
选取getuid和getgid这两个系统调用作为例子,功能分别是获取用户ID和用户组ID。
调用库函数版本:
#include <stdio.h>
#include <unistd.h>
int main()
{
int uid, gid;
uid = getuid();
gid = getgid();
printf("uid = %d, gid = %d\n", uid, gid);
return 0;
}
C代码嵌入汇编触发中断:
#include <stdio.h>
#include <unistd.h>
int main()
{
int uid, gid;
//uid = getuid();
asm volatile(
"mov $0x0, %%ebx\n\t"
"mov $0x18, %%eax\n\t"
"int $0x80\n\t"
"mov %%eax, %0\n\t"
: "=m" (uid)
);
//gid = getgid();
asm volatile(
"mov $0x0, %%ebx\n\t"
"mov $0x2f, %%eax\n\t"
"int $0x80\n\t"
"mov %%eax, %0\n\t"
: "=m" (gid)
);
printf("uid = %d, gid = %d\n", uid, gid);
return 0;
}
实验结果
实验分析
通过实验执行结果可知,程序成功完成了系统调用获取当前用户uid和gid的操作,通过内嵌汇编代码可以清晰的看出调用系统调用的工作过程。
首先将ebx寄存器清零,表示无参数传入。
然后分别将0x18和0x2f(十进制24和47)赋值给eax寄存器,表示需要调用的系统调用号,24为getuid,47为getgid。
执行int 0x80来执行系统调用。
之后eax寄存器保存了返回值,将它分别赋值给输出uid或gid变量。
完成整个汇编代码的系统调用。
在Linux系统中是通过激活0x80中断来触发系统调用的,需要调用的系统调用号实现赋值给eax存储器,如果有传入参数可赋值给ebx寄存器,如果多于1个则按顺序赋值给ebx、ecx、edx、esi、edi、ebp,如果超过6个则通过指针变量指向另一片堆栈区,如果无参数传入则赋值为0。
总结
对系统调用机制的理解:
系统调用是用户态进程访问硬件的一种方式,它通过中断(int 0x80)由用户态陷入内核态。
当一个用户态程序进行系统调用的时候,CPU进入内核态并执行内核函数
系统调用的过程:函数库提供的封装函数接口(API)->system_call(是所有系统调用的入口),这个入口会根据系统调用号(eax入栈),调用对应的系统调用例程
系统调用最多使用6个寄存器作为参数(除eax外)-ebx,ecx,edx,esi,edi,ebp,如果超过6个参数,寄存器中将会保存一个指向内核态可以操作的一块的内存,参数保存在内存上面。
Jin Youzhi 原创作品转载请注明出处
Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000