陈琛+ 原创作品转载请注明出处《Linux内核分析》MOOC课
1.基础知识
用户态、内核态
为保证系统内核安全,cpu执行指令分为用户态和系统态两种,表现在用户态的指令有些特权指令不能执行、某些内存区域不能访问;而内核态可以执行特权指令,可以访问所有内存空间。
Intel x86CPU有四种级别的,分别为0,1,2,3.Linux只使用0,3级其中0表示内核态,3表示用户态。
中断
中断是从用户态进入内核态的主要方式,系统调用知识一种特殊的中断。
中断时上下文切换
从用户态切换到内核态时,必须保存用户态的寄存器上下文。
保存在哪里?
保存在内核堆栈中要保存什么?
用户态栈顶地址(esp), psw , cs:eip
中断处理的完整过程
interrupt(int 0x80) -- save
cs:eip/ss:esp/eflags tp kernel stack then load
cs:eip(entry of a spefic ISR) and ss:esp(point to kernel stack)
SAVE_ALL
//内核代码,完成终端服务,发生进程调度(暂时保存,下一次切换回该进程时执行返回)
RESTORE_ALL
iret pop cs:eip /ss:esp/eflags from kernel stack to CPU
系统调用
意义:
- 程序员从硬件编程中解放出来
- 提高系统安全性
- 用户应用程序具有更高地可移植性
API和系统调用的不同点:
- 系统调用通过软件中断向内核发出一个明确地请求
- API只是一个函数定义,封装了系统调用
2.实验过程
选择63号系统调用,获取当前进程的父进程pid.代码比较简单,必要处已做注释.
- 使用库函数getppid,获取父进程pid
#include <stdio.h>
#include <unistd.h>
int main()
{
long ppid;
ppid = getppid();
printf("the parent pid is:%ld\n",ppid);
return 0;
}
- 使用C代码中嵌入汇编方式
#include <stdio.h>
/*
*将系统调用号通过eax寄存器传递
*int 0x80触发中断
*返回值放在eax寄存器,写入到内存变量ppid中
*/
int main()
{
long ppid;
asm volatile(
"mov $64,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
:"=m"(ppid)
);
printf("the parent pid is:%ld\n",ppid);
return 0;
}
- 运行截图
- 总结
通过本周的课程和本次的实验,熟悉了系统调用的本质,以及系统调用和中断的关联。系统调用是用户态和内核态的桥梁,而具体的措施就是中断。实验中采用内嵌汇编编写的代码,在运行时,通过eax准备系统调用号,使用ebx、ecx等传递具体参数,当我们触发0x80中断时,经过中断处理程序,我们就进入了内核态。