实验:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
席金玉+《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”
系统调用:系统调用只是一个特殊的中断。我们通过库函数和系统调用打交道,库函数把系统调用封装起来。
1、储备知识——内核态和用户态
内核态:在高执行级别下,代码可以执行特权指令,访问任意的物理内存,这种CPU执行级别就对应着内核态。
用户态:在用户态级别下,代码的掌控范围会受到限制,只能在对应级别允许的范围内活动。
注:Intel x86CPU有四种不同的执行级别0-3,Linux只使用了其中的0级和3级分别来表示内核态和用户态。
2、为什么有权限级别的划分?
若没有用户态和内核态的划分,用户写的不健壮的程序就可以执行特权指令时,就很容易是系统崩溃。操作系统发展过程中划分了用户态和内核态,是系统更稳定的机制。
3、寄存器在系统调用中的作用
Cs寄存器的最低两位表明了当前代码的特权级。CPU每条指令的读取都是通过CS:EIP这两个寄存器:其中CS是代码段选择寄存器,EIP是偏移量寄存器。
4、内存地址空间
一般在Linux中,地址空间是一个显著的标志:0xc0000000以上的地址空间只能在内核态下访问,0x0000000--0xbfffffff的地址空间在两种状态下都可以访问。
注:产生中断是从用户态进入内核态的主要方式。
5、寄存器上下文:
5.1从用户态切换到内核态时:
5.2中断/int指令会在堆栈上保存一些寄存器的值:
保存现场:就是进入中断程序,保存需要用到的寄存器的数据。
5.4中断处理结束前最后一件事就是恢复现场:
恢复现场:就是退出中断程序恢复保存寄存器的数据。
注:Iret指令和中断信号(包括int指令)发生时的CPU做的动作整好相反。
Libc库定义的一些API引用了封装例程(wrapper routine,唯一的目的就是发布系统调用):一般每个系统调用对应一个封装例程。库再用这些封装例程定义给出用户的API。
注:系统调用的三层皮:API,中断向量,中断服务程序
System call是Linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax
系统调用号将xyz和sys_xyz关联起来;用eax寄存器来传递参数;
编译并运行该文件,输出如下结果:
该程序实现用库函数方式触发系统调用获取系统当前时间,时间为:2016年3月16日14:27:5
3. 新建一个名为 time-asm.c的源文件,并输入代码,如下图
编译并运行该文件,输出结果如下:
该程序是用汇编方式触发系统调用获取系统当前时间,时间为:2016年3月16日14:28:30
4. 完成了对time.c和time-asm.c的编译和运行,下面将这两个程序保存到实验楼代码库中:
如图所示,已经成功将time.c和time-asm.c文件保存到实验课代码库中。