2019-2020-20199316《Linux内核原理与分析》第五周作业
视频学习
一、用户态、内核态和中断
-
内核态:处于高的执行级别下,代码可以执行特权指令,访问任意的物理地址,这时的CPU就对应内核态
-
用户态:处于低的执行级别下,代码只能在级别允许的特定范围内活动。在日常操作下,执行系统调用的方式是通过库函数,库函数封装系统调用,为用户提供接口以便直接使用。
-
Intel x86 CPU有四种不同的执行级别0-3,Linux只使用了其中的0 3级分别表示内核态和用户态。cs寄存器的最低两位表明了当前代码的特权级,00或者11。
-
内核态cs:eip的值是任意的,即可以访问所有的地址空间。用户态只能访问其中的一部分内存地址(0x00000000-0xbbbbbbbf),0xc0000000以上的地址(逻辑地址而不是物理地址)只能在内核态下访问。
-
中断处理是从用户态进入内核态的主要方式,系统调用是一种特殊的中断。从用户态切换到内核态时,中断/int指令会在堆栈上保存用户态的寄存器上下文,其中包括用户态栈顶地址、当时的状态字、当时的cs:eip的值,还有内核态的栈顶地址、内核态的状态字、中断处理程序的入口。中断发生后的第一件事就是保存现场,保存一系列的寄存器的值;中断处理结束前的最后一件事就是恢复现场,退出中断程序,恢复保存寄存器的数据。特别说明: 保护现场:就是进入中断程序,保存需要用到的寄存器的数据;恢复现场:就是退出中断程序,恢复保存寄存器的数据。
二、使用库函数API和C代码中嵌入汇编代码触发同一个系统调用
学习了通过使用系统调用time这个库函数来获取当前系统时间
如图所示的,"=m"表示写入到内存变量val3里面,"c"表示ecx接,"d"edx。下面的输入输出分别用0,1,2...编号表示,比如"addl %1,%%eax\n\t"就表示将输入输出的第2个,即"c"(ecx)的值加到eax中去,eax前面为什么有两个%,前一个%是转义符号。
实验内容
- 先编译mkdir.c文件,若编译成功则返回0。
#include <stdio.h>
int main()
{
int flag;
flag = mkdir("/home/shiyanlou/testdir");
if (flag == -1)
printf("mkdir failed!\n");
else
printf("make dirctory success!\n");
return 0;
}
- 使用gcc进行编译
gcc -o makedir mkdir.c
- 然后对mkdir.c文件进行加入汇编语言修改,修改为以下代码并汇编
gcc -o makedir2 mkdir.c -m32
- 如此便可编译成功!
遇到的问题
就是关于系统调用函数编号的问题,mkdir的编号是39,我在看实验楼给的那些系统调用列表,将前面的编号48当成编程里面的系统调用号了,总是出错,后来仔细看才发现是39,对应十六进制为0x27。
教材学习
1.系统调用功能特性
- 把用户从底层的硬件硬件编程中解放出来。操作系统为我们管理硬件,用户进程不用直接与硬件设备打交道。
- 极大的提高系统的安全性。如果用户态进程直接与硬件设备打交道,会产生安全隐患,可能引起系统崩溃。
- 使用户程序具有可移植性。用户程序与集体的硬件已经解耦合并用接口代替了,不会有紧密的关系,便于在不同的系统间移植。
2.API和系统调用的关系
- 系统调用的库函数就是读者使用的操作系统提供的API(应用程序编程接口),API只是函数定义。系统调用是通过软中断向内核发出了中断请求,int指令的执行就会触发一个中断请求。Libc库函数定义的一些API内部使用了系统调用的封装例程,其主要目的是发布系统调用,使程序员在写代码时不需要用汇编指令和寄存器 传递参数来触发系统调用。一般每个系统调用对应一个系统调用的封装例程,函数库再用这些封装例程定义出给程序员调用的API,这样把系统调用最终封装成方便程序员使用的库函数。