《Linux内核分析》第四周笔记 扒开系统调用的三层皮(上)
扒开系统调用的三层皮(上)
一、用户态、内核态和中断
库函数将系统调用封装起来。
1、什么是用户态和内核态
一般现代CPU都有几种不同的指令执行级别。
在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别就对应着内核态。
而在相应的低级别执行状态下(用户态),代码的掌控范围会受到限制。只能在对应级别允许的范围内活动。系统容易崩溃。
在intel X86CPU有四种不同的执行级别0,1,2,3,linux只使用了0级和3级分别来表示内核态和用户态。
2、在linux内核代码中区分用户态和内核态
用cs(代码段选择寄存器)和eip(偏移量寄存器)进行区分。
cs寄存器的最低两位表明了当前代码的特权级。
内核态下访问:cs和eip的值可以是任意的地址。(0xc0000000以上的地址空间)地址空间:逻辑地址。
用户态下访问:0xc0000000-0xbfffffff的地址空间。
3、中断处理是从用户态进入内核态的主要方式
系统调用只是一种特殊的中断。
a.寄存器上下文:
从用户态切换到内核态时:
必须保存用户态的寄存器上下文
将内核态的寄存器上下文保存到当前CPU中
b.中断/int指令会在堆栈上保存一些寄存器的值:
用户态栈顶地址、当时的状态字、当时的es:eip的值(中断处理程序的入口)。
同时将内核态堆栈的栈顶地址、当时的状态字、指向中断处理程序的入口,对于系统调用来讲指向system call函数。
c.中断发生后第一件事情就是保存现场
保护现场就是:就进入了中断服务程序 保存 需要用到的寄存器的数据,
恢复现场就是:退出中断程序, 恢复 保存寄存器的数据。
iret指令与中断信号(包括int指令)发生时的cpu做的动作正好相反。
d.中断处理的完整过程
/*系统调用*/
/*保存了cs:eip的值 保存了当前的堆栈段寄存器 当前的栈顶 还有标志寄存器 到内核堆栈中*/
/*同时将当前的中断信号(系统调用)相关联的中断服务例程的入口加载到cs:eip中*/
/*同时把当前的堆栈段和esp指向内核堆栈的信息也加载到CPU中*/
完成上述过程之后,当前cpu在执行下一条指令的时候,就已经在执行中断处理程序的入口了,对堆栈的操作由原来的用户态转到内核态。
在完成中断服务的过程中可能发生进程调度。
如果不发生进程调度,则执行:
返回原来的状态。
如果发生进程调度:
当前的上述状态将保存在系统中,当下一次发生进程调度在切换回当前进程的时候再执行完上图过程。
二、系统调用概述
1、系统调用的意义
操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用
- 把用户从底层的硬件编程中解放出来(操作系统为我们管理硬件,防止用户态进程直接与硬件设备接触造成系统崩溃)
- 极大的提高了系统的安全性
- 使用户程序具有可移植性(用户程序与具体的硬件解耦和,被抽象的接口替代,不会与具体的硬件有紧密关系)
2、API和系统调用
a.应用编程接口(application program interface, API) 和系统调用是不同的。
- API只是一个函数定义
- 系统调用通过软中断向内核发出一个明确的请求(例如sysrem enter指令)
b.Libc库定义的一些API引用了封装例程(wrapper routine,唯一目的就是发布系统调用让程序员在写代码的时候不需要用汇编指令来出发一个系统调用,而是直接调用一个函数)
- 一般每个系统调用对应一个封装例程
- 库再用这些封装例程定义出给用户的API
c.不是每个API都对应一个特定的系统调用。 (可能一对多或多对一)
- API可能直接提供用户态的服务 如,一些数学函数
- 一个单独的API可能调用几个系统调用
- 不同的API可能调用了同一个系统调用
d.返回值
- 大部分封装例程返回一个整数,其值的含义依赖于相应的系统调用
- -1在多数情况下表示内核不能满足进程的请求
- Libc中定义的errno变量包含特定的出错码
用户态 内核态
函数xyz() API中封装了一个系统调用, 0x80中断向量对应着system call(内核代码入口起点)
执行对应的中断服务程序sys_xyz()之后return from sys call
过程中可能发生进程调度,未发生则返回iret
系统调用会触发一个0x80的中断
系统调用对应的API
系统调用的三层皮:xyz、system_call、sys_xyz(API、中断向量对应的中断服务程序、系统调用中的多种服务程序)
3.系统调用程序及服务例程
a.当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。 (中断向量0x80与system_call绑定起来,系统调用将xyz与sys_xyz联系起来)
- 在Linux中是通过执行int $0x80来执行系统调用的,这条汇编指令产生向量为128的编程异常
- Intel Pentium II中引入了sysenter指令(快速系统调用),2.6已经支持(本课程不考虑这个)
b.传参:
内核实现了很多不同的系统调用,
进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数。
使用eax寄存器传递
系统调用的参数传递方法:
1)系统调用也需要输入输出参数,例如
- 实际的值。
- 用户态进程地址空间的变量的地址。
- 甚至是包含指向用户态函数的指针的数据结构的地址。
2)system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号
- 一个应用程序调用fork()封装例程,那么在执行int $0x80之前就把eax寄存器的值置为2(即__NR_fork)。
- 这个寄存器的设置是libc库中的封装例程进行的,因此用户一般不关心系统调用号。
- 进入sys_call之后,立即将eax的值压入内核堆栈。
3)寄存器传递参数具有如下限制:
• 每个参数的长度不能超过寄存器的长度,即32位 。
• 在系统调用号(eax)之外,参数的个数不能超过6个(ebx, ecx,edx,esi,edi,ebp) 。
• 超过6个怎么办? 将某一寄存器作为一个指针,指向一块内存,进入内核态之后可以访问所有的地址空间,通过那块内存来传递数据。
三、使用库函数API和C代码中嵌入汇编代码触发同一个系统调用
1.系统调用实验:例:使用库函数API来获取系统当前时间(time)
/*声明一个time_tt变量*/
/*声明了一个tm为了输出时变为可读*/
/*使用time系统调用,返回tt*/
/*将tt变为t格式的*/
/*输出*/
获得当前的系统时间:
实验实现使用库函数API来取得真实的用户识别码(getuid):
编译getuid.c取得当前真实的用户识别码:
使用C代码中嵌入汇编代码触发系统调用获取系统当前真实的用户识别码:
ebx归零
使用eax传递系统调用号 24
系统调用的返回值使用eax存储
结果与之前一致。
四、小结
系统调用是用户程序与内核的接口。通过系统调用进程,可由用户态转入内核谈态,在内核态下完成相应的服务;之后,再返回到用户态。这种实现方式必然跨越我们刚才提及的两个模式:内核态与用户态。系统调用是操作系统为用户态进程与硬件设备进行交互提供的一组接口,其意义是把用户从底层的硬件编程中解放出来,极大的提高了系统的安全性及使用户程序具有可移植性。在实验中分别使用使用库函数API和C代码中嵌入汇编代码触发同一个系统调用,在这个实验中我使用库函数取得真实的用户识别码(getuid),以及使用C代码中嵌入汇编代码触发系统调用获取系统当前真实的用户识别码,过程看似容易,实际上花费了很长时间,开热点做实验什么的,希望为下一次的学习打好基础吧。
《Linux内核分析》第四周笔记 扒开系统调用的三层皮(上)的更多相关文章
- 20135327郭皓--Linux内核分析第五周 扒开系统调用的三层皮(下)
Linux内核分析第五周 扒开系统调用的三层皮(下) 郭皓 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/U ...
- Linux内核分析第五周——扒开系统调用的“三层皮”(下)
Linux内核分析第五周--扒开系统调用的"三层皮"(下) 李雪琦+原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.1 ...
- Linux内核分析第五周 扒开系统调用的三层皮(下) (20135304 刘世鹏)
作者:刘世鹏20135304 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.给MenuOS增加t ...
- linux内核分析 第五周 扒开系统调用的三层皮(下)
rm menu -rf 强制删除原menu文件 git clone http://github.com/mengning/menu.git 从github中克隆 cd menu 在test.c中增加上 ...
- LINUX内核设计第五周——扒开系统调用的三层皮(下)
- Linux内核分析第四周学习总结——系统调用的工作机制
Linux内核分析第四周学习总结--系统调用的工作机制 内核态 执行级别高,可以执行特权指令,访问任意物理地址,在intel X86 CPU的权限分级为0级. 用户态 执行级别低,只能访问0x0000 ...
- Linux第五周学习总结——扒开系统调用的三层皮(下
Linux第五周学习总结--扒开系统调用的三层皮(下) 作者:刘浩晨 [原创作品转载请注明出处] <Linux内核分析>MOOC课程http://mooc.study.163.com/co ...
- 《Linux内核分析》第五周笔记 扒开系统调用的三层皮(下)
扒开系统调用的三层皮(下) 一.给menuOS增加time和time-asm 通过内核调试系统调用.将上次做的实验加入到menusOS,变成menusOS里面的两个命令. 1 int Getpid(i ...
- linux 内核 第四周 扒开系统调用的三层皮 上
姬梦馨 原创作品 http://mooc.study.163.com/course/USTC-1000029000 一.用户态.内核态和中断处理过程 用户通过库函数与系统调用联系起来:库函数帮我们把系 ...
随机推荐
- beta冲刺————第二天(2/5)
完善具体内容: 前端: (1)添加了更多设置 (2)点击后出现底栏,分别可以进行字体背景设置.收藏.分享等操作,同时可以看出对文章的排版进行了完善 后端: 对阿里云服务器中的环境进行配置,同时熟悉阿里 ...
- Python程序的执行原理
1. 过程概述 Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后虚拟机一条一条执行字节码指令,从而完成程序的执行. 2. 字节码 字节码在Python虚拟机程序里对应的是PyCo ...
- 【转】DHCP工作过程详解
DHCP动态主机配置协议的作用我想作为网管的兄弟们都应该知道了,这里我就不多废话了,今天我要谈的是DHCP的工作过程,了解了工作过程,要排除故障就容易了. 一.DHCP客户机初始化: 1. 寻找D ...
- layui 弹出层监听 判断弹出框的大小
if ($.PublicIsMobile($(window).width())) { var layerInitWidth = $("#layui-layer" + ly_dtxm ...
- $.toJSON和eval的区别
1.$.toJSON是jquery的方法.eval是javascript的方法 2.eval兼容的浏览器多,$.toJSON有可能解析不了的json格式的数据,eval可以.
- Ubuntu16.04 kaldi的简单配置
1.什么是kaldi kaldi是使用c++写的语音识别的工具,apache 授予了v2.0的证书(果真应验,apache旗下无弱将).kaldi旨在供语音识别研究员使用.kaldi在目标和范围上和H ...
- 编程使用缓冲流读取试题文件,test6_5.txt 内容如下所示。 每次显示试题文件中的一道题目,读取到字符“*”时暂停读取, 等待用户从键盘输入答案。用户做完全部题目后,程序给出用户的得分。
test6_5.txt内容如下: (1)面向对象程序设计中,把对象的属性和行为组织在同一个模块内的机制叫做( ). A.封装象 B.继承 C.抽象 D.多态 ******************** ...
- Python中 __init__的通俗解释?附修饰器contextmanager的理解
作者:匿名用户链接:https://www.zhihu.com/question/46973549/answer/103805810来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...
- linux echo设置颜色
echo要变换颜色的时候,要使用参数-e 格式: echo -e "\033[字背景颜色;字体颜色m字符串\033[0m" 例如: echo -e "\033[41;36 ...
- 20155211 网络攻防技术 Exp08 Web基础
20155211 网络攻防技术 Exp08 Web基础 实践内容 Web前端HTML,能正常安装.启停Apache.理解HTML,理解表单,理解GET与POST方法,编写一个含有表单的HTML. We ...