扒开系统调用的三层皮

张潇月《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

通过库函数与系统调用沟通。

用户态和内核态:高的执行级别下,代码可以执行特权指令,访问任意物理地址,这就是内核态。低级别的就是用户态。

为什么要有权限分级?让操作系统更加稳定,防止程序员的用户态代码使系统崩溃。

intel x86 CPU上有四种执行指令的级别0-3,Linux只使用两种,0是内核态,3是用户态

如何区分用户态和内核态?在内核态时我们可以访问cs eip是任意的地址,然而用户态只能访问0x0000000-0xbfffffff的地址空间(地址空间是逻辑地址不是物理地址)

中断处理是从用户态进入内核态的主要方式!系统调用只是一种特殊的中断。

寄存器上下文切换:用户态切换到内核态时,必须要要把用户态的寄存器上下文保存起来,同时要把内核态寄存器上下文的值保存在对应地方。如:用户态栈顶地址、当时的状态字、当时的cs:eip的值以及内核态的栈顶地址、当时的状态字等。

中断发生后第一件事就是保存现场 save_all,中断结束前的最后一件事就是恢复现场 restore_all,最后iret指令与中断信号发生时的CPU的做的动作只好相反。

中断处理过程

interrupt (ex:int 0x80)-save cs:eip/ss:eip/eflages(current)to kernel stack,then load cs:eip(entry of a specific ISR)and ss:esp(point to kernel stack)
int 0x80表示系统调用
保存了当前的堆栈,栈顶,标志寄存器保存到内核堆栈
同时加载了当前的系统调用相关联的中断服务历程的入口
同时把当前的堆栈段和esp也加载到CPU里
save_all
-…//内核代码,完成中断服务,发生进程调度
restore_all
iret-pop cs:eip/ss:esp/eflags from kernel stack

系统调用的意义:操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用

把用户从底层的硬件编程中解放出来

极大地提高了系统的安全性

使用户程序具有可移植性

API和系统调用(应用程序编程接口):API知识一个函数定义,系统调用通过软中断向内核发出一个明确的请求。

一般每个系统调用对应一个封装例程;不是每一个系统调用对应一个API,大部分封装都返回一个整数。

系统调用的三层皮:API、对应的中断向量、服务程序

内核实现了很多不同的系统调用,进程必须指明要哪一种系统调用,因此有了系统调用号这个参数。用eax寄存器传递参数。

寄存器传递参数具有如下限制:每个参数的长度不能超过寄存器的长度,即32位;在系统调用号之外,参数的个数不能超过六个。

 

实验:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用。

首先是c代码

其次是嵌入式代码

两种方式的比较

得到答案都是1000

原文地址:https://www.cnblogs.com/20135131zxy/p/5297795.html