20135320赵瀚青LINUX内核分析第四周学习笔记
赵瀚青原创作品转载请注明出处《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
概述
本周的内容主要是讲解系统调用中的一些步骤,主要有三步,而主要是采用中断机制,也是老师讲解的重点,而实验的内容则是用汇编代码代替一下系统调用的C代码,看能不能产生相同的结果,总体来说本周学习难度不大,但是通过看视频,看书,做实验,感觉学到了很多东西。
课本内容
一、用户态、内核态和中断处理过程
- 用户态和内核态
CPU指令执行级别
- 内核态:执行特权指令,访问任意的物理地址
- 用户态:代码只能在级别允许的特定范围内活动。在日常操作下,执行系统调用的方式是通过库函数,库函数封装系统调用,为用户提供接口以便直接使用。
- Linux只用其中的0和3来表示内核态和用户态
区分内核态和用户态:CPU每条指令的读取都是通过cs:eip,cs寄存器最低两位表明了当前代码的特权级。
0xc0000000(逻辑地址)以上的空间只能在内核态下访问
0x00000000 ~ 0xbfffffff 内核态和用户态均可访问
- 中断处理
用户态到内核态的切换:必须保存用户态的寄存器上下文,包括用户态栈顶地址、当时的状态字、cs:eip的值,以及内核态的栈顶地址、当时的状态字、中断处理程序入口。
中断发生后的第一件事:保存现场(SAVE_ALL:保存需要用到的寄存器数据)。
中断处理结束前的最后一件事:恢复现场(RESTORE_ALL:退出中断程序,恢复保存寄存器的数据)。
完整过程:
二、系统调用
系统调用的意义
- 系统调用是操作系统为用户态进程与硬件设备进行交互提供的一组接口
- 把用户从底层的硬件编程中解放出来
- 极大的提高了系统的安全性
- 使用户程序具有可移植性
- 一般每个系统调用对应一个封装例程,库再用这些封装例程定义出用户的API,方便用户使用。所以,API与系统调用不是一一对应的
系统调用“三层皮”
- API
- 中断向量
- 中断服务程序
Linux中是通过执行int $0x80来执行系统调用,这条汇编指令产生向量为128的编程异常 —— 即中断向量0x80与System_call绑定起来。
系统调用号将函数xyz()和中断服务程序sys_xyz关联起来。
- 参数传递
内核实现了很多不同的系统调用,进程用系统调用号这个参数指明需要哪个系统调用。
每个参数的长度不能超过寄存器的长度,即32位
在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp)
超过6个的情况下,使用某一个寄存器作为指针,进入内核态之后可以访问所有的地址空间,通过某一片区域传递参数。
一、实验概述
本周实验大致是先写一个C代码,使用系统调用,然后看输出的结果,然后使用老师教我们的方法更改成一段汇编代码,看实验结果相不相同
二、实验的主要步骤
1、先看系统调用代码getpid
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t zhq;
zhq = getpid();
printf("pid = %d \n",zhq);
return 0;
}
2、使用vim编辑文件
3、gcc一下,然后看看结果
4、嵌入汇编代码
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid;
pid = getpid();
asm volatile(
"mov $0x14,%%eax\n\t" /* 将系统调用号20放入eax寄存器中。 */
"int $0x80\n\t" /* 中断向量号0x80,使用int 128 执行系统调用。 */
"mov %%eax,%0\n\t" /* 返回值保存在eax中 */
: "=m" (pid)
);
printf("pid = %d \n",pid);
return 0;
}
5、继续看编译以后的结果
遇到的问题
1、如第一张图所示,一开始进入Code目录一直进错,后面才正确进入
总结
1. 系统调用
即便是最简单的程序,在进行输入输出等操作时也会需要调用操作系统所提供的服务,也就是系统调用。
Linux下的系统调用是通过中断(int 0x80)来实现的。
2. 传递参数
在执行int 80指令时,寄存器 eax 中存放的是系统调用的功能号,而传给系统调用的参数则必须按顺序放到寄存器 ebx,ecx,edx,esi,edi 中,当系统调用完成之后,返回值可以在寄存器 eax 中获得。
Linux 采用的是 C 语言的调用模式,这就意味着所有参数必须以相反的顺序进栈,即最后一个参数先入栈,而第一个参数则最后入栈
心得与体会
本周实验总体来说不是很难,只需自己更改一段汇编代码即可,但是学习到系统调用的知识也感到收获颇丰,希望在未来的日子里,也能够这么顺利的完成任务。