李辰希无转载 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
一.用户态、内核态和中断处理过程
1.我们与系统调用打交道是通过库函数的方式
2.一般现代CPU都有几种不同的指令执行级别
因为如果所有程序员写的代码都可以有特权指令的话,系统就会很容易崩溃。
3.区别:
- 在高级别的状态下,代码可以执行特权指令,访问任意的物理地址。
- 在相应的低级别执行状态下,代码的掌控范围会受到限制。
- Intel x86 CPU有四种不同的执行级别0——3,Linux只是用了其中的0和3来表示内核态和用户态。
二.中断处理(中断处理是从用户态进入内核态主要的方式)
1.从用户态进入内核态:必须保存用户态的寄存器上下文
2.中断/int指令在堆栈上保存寄存器的值:用户态/内核态栈顶地址(ss:esp)、状态字(eflags)、cs:eip值(内核态时指向中断服务程序入口)
三.系统调用概述和系统调用的三层皮
1.系统调用的意义:
操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用:
把用户从底层的硬件编程中解放出来;极大的提高了系统的安全性;使用户程序具有可移植性。
2.API和系统调用
API:应用编程接口,是一个函数定义
系统调用:通过软中断向内核发出明确请求
3.Libc库定义的一些API引用了封装例程(唯一目的就是发布系统调用)
一般每个系统调用对应一个封装例程
库用封装例程定义出给用户的API
4.不是每个API都对应一个特定的系统调用
API可直接提供用户态服务,如数学函数
API可调用几个系统调用
不同API可调用同一系统调用
5.返回值
封装例程返回一个整数,含义依赖与相应系统调用
-1表示内核不能满足进程的要求
Libc定义的errno变量包含特定出错码
6.系统调用的三层皮:
xyz,sysytem_call和sysytem_xyz
(1)中断向量0x80与system_call绑定起来
(2)系统调用号将xyz与sys_xyz关联起来了(使用eax)
四.使用库函数API和C代码中嵌入汇编代码触发同一个系统调用
代码如下:
代码:
time.c
#include <stdio.h>
#include <time.h>
int main()
{
time_t tt;//int型数值
struct tm *t;
tt = time(NULL);
t = localtime(&tt);//强制类型转换,便于输出
printf("time:%d:%d:%d:%d:%d:%d:\n",t->tm_year+1960,t->tm_mon,t->tm_mda,t->tm_hour,t->tm_min,t->tm_sec);
return 0;
}
1.使用库函数API获取系统当前时间
2.C代码中嵌入汇编代码的写法:
_asm_(
汇编语句模板:
输出部分:
输入部分:
破坏描述部分);
3.使用C代码中嵌入汇编代码触发系统调用获取系统当前时间
结果为打印出的系统时间下的年、月、日、时、分、秒。
实验(选择2号系统调用fork)
结果:
编译:
五.总结
1.系统调用是操作系统为用户态进程与硬件设备进行交互提供的一组接口,也是一种特殊的中断,可使用户态切换到内核态。当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。
2.系统调用的工作机制,就是将诸多系统调用视作按照序号排好顺序的黑箱,只要知道了一个系统调用的号,按照其API或者汇编格式的调用方法,就可以使用这个黑箱的功能,完成一定的任务,而黑箱的内容正是系统给我们提供的服务,帮我们从硬件操作中抽离出来,提高系统的安全性。