系统调用的三层机制(上)
一、知识积累
1、 内核态:高执行级别下,代码可以执行特权指令,访问任意物理地址,CPU执行级别就对应内核态。 用户态:低级别指令,只能在对应级别允许的范围内活动。 中断处理是从用户态进入内核态主要的方式 2、寄存器上下文
- 从用户态切换到内核态时,必须保存用户态的寄存器上下文。
- 中断/int指令会在堆栈上保存一些寄存器的值,如:用户态栈顶地址、当时的状态字、当时的CS:eip的值。
3、应用编程接口API和系统调用是不同的 API只是一个函数定义。 系统调用通过软中断向内核发出明确请求。 4、系统调用的三层皮 API XYZ,中断向量system_call,中断服务程序sys_xyz
二、实验楼实验
使用库函数 API 和 C 代码中嵌入汇编代码两种方式使用同一个系统调用
1、查看系统调用号信息
选择getuid进行此次实验,其系统调用号为102,即0x66。 2、通过man getuid查看getuid系统调用相关信息,可以看到其功能是返回当前调用进程的用户ID: 3、库函数API的方式进行系统调用 编写getuid.c文件,通过getuid()方法获取用户ID:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int usrid;
usrid=getuid();
printf("The user id is %d\n",usrid);
return 0;
}
编译执行 4、C 代码中嵌入汇编代码方式进行系统调用 修改getuid.c,嵌入汇编代码,将系统调用号0x66赋给eax寄存器,系统调用后将返回值赋给变量usrid
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int usrid;
asm volatile(
"mov $0,%%ebx\n\t"
"mov $0x66,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
:"=m"(usrid)
);
printf("The user id is %d\n",usrid);
return 0;
}
编译执行 这里出错了,查询资料后发现,linux系统中64位汇编和32位汇编的系统调用主要有以下不同: (1)系统调用号不同.linux系统调用号实际上定义在/usr/include/asm/unistd_32.h和/usr/include/asm/unistd_64.h中。 (2)系统调用所使用的寄存器不同,x86_64中使用与eax对应的rax传递系统调用号,但是 x86_64中分别使用rdi/rsi/rdx传递前三个参数,而不是x86中的ebx/ecx/edx。 (3)系统调用使用“syscall”而不是“int 80”。 修改成32位汇编系统下的调用
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int usrid;
asm volatile(
"mov $0,%%ebx\n\t"
"mov $0x18,%%eax\n\t" #getuid在32位系统下系统调用号为24,即0x18
"int $0x80\n\t"
"mov %%eax,%0\n\t"
:"=m"(usrid)
);
printf("The user id is %d\n",usrid);
return 0;
}
三、总结
本节主要学习了系统调用机制,通过分别使用库函数 API 和 C 代码中嵌入汇编代码两种方式使用同一个系统调用,来进行练习。 系统调用流程:第一步,代码调用系统调用( xyz ),该函数是一个包装系统调用的库函数;库函数 ( xyz )负责准备向内核传递的参数,并触发软中断,切换到内核;CPU被软中断打断后,执行中断处理函数 ,即系统调用处理函数 ( system_call);系统调用处理函数调用系统调用服务例程 (sys_xyz ),真正开始处理该系统调用。
|