LINUX内核分析第四周——扒开系统调用的三层皮
2016-03-19 21:45
357 查看
LINUX内核分析第四周——扒开系统调用的三层皮
李雪琦 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000一、用户态、内核态和中断处理过程
1. 用户态和内核态
CPU指令执行级别:执行特权指令,访问任意的物理地址——内核态。
低级别:代码只能在级别允许的特定范围内活动——用户态。在日常操作下,执行系统调用的方式是通过库函数,库函数封装系统调用,为用户提供接口以便直接使用。
Intel x86 CPU有四种不同的执行级别0~3,Linux只用其中的0和3来表示内核态和用户态。
区分内核态和用户态:CPU每条指令的读取都是通过cs:eip,cs寄存器最低两位表明了当前代码的特权级。
内核态下可访问所有地址空间。
0xc0000000(逻辑地址)以上的空间只能在内核态下访问。
0x00000000 ~ 0xbfffffff 内核态和用户态均可访问。
用户态转换为内核态的主要方式:中断。
2. 中断处理
用户态到内核态的切换:必须保存用户态的寄存器上下文,包括用户态栈顶地址、当时的状态字、cs:eip的值,以及内核态的栈顶地址、当时的状态字、中断处理程序入口。中断发生后的第一件事:保存现场(SAVE_ALL:保存需要用到的寄存器数据)。
中断处理结束前的最后一件事:恢复现场(RESTORE_ALL:退出中断程序,恢复保存寄存器的数据)。
二、系统调用概述
1. 系统调用的意义:
操作系统为用户态进程与硬件设备进行交互提供了一组接口,就是系统调用。远离底层硬件编程
安全性
可移植性
2. API - 应用编程接口
与系统调用区别:API只是一个函数定义。
系统调用是通过软中断向内核发出一个明确的请求。
一般每个系统调用对应一个封装例程,库再用这些封装例程定义出用户的API,方便用户使用。也就是说,API与系统调用不是一一对应的。
API可以:
直接提供用户态服务
一个单独的API可能调用几个系统调用
不同的API可能调用了同一个系统调用
返回值:
大部分封装例程返回一个整数
-1表示失败,不能满足请求
errno 特定出错码
3.所谓“扒开系统调用的三层皮”
API(xyz)中断向量(system_call)
中断服务程序(sys_xyz)
1.系统调用的服务例程中,中断向量0x80与system_call绑定起来。(Linux中可以通过执行int $128来执行系统调用。)
2.system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即系统调用号。
3.系统调用号将xyz与sys_xyz关联起来。调用号在eax中。
系统调用的参数传递:
函数调用——压栈
用户态到内核态——寄存器传递。
每个参数长度不能超过32位,个数不能超过6个。
超过的话,使某个寄存器中存储指针,指向内存,内存中存储参数。
三、使用库函数API和C代码中嵌入汇编代码触发同一个系统调用
1.使用库函数API获取系统当前时间
使用time(),代码如下:#include<stdio.h> #include<time.h> int main() { time_t tt; struct tm *t;//构造一个结构体,方便读取 tt = time(NULL);//time系统调用 t = localtime(&tt); printf("time:%d:%d:%d:%d:%d:%d\n", t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); return 0; }
2.使用C代码中嵌入汇编代码触发系统调用获取系统当前时间
代码如下:#include<stdio.h> #include<time.h> int main() { time_t tt; struct tm *t; asm volatile( "mov $0,%%ebx\n\t" # 把ebx清零,相当于传参数 "mov $0xd,%%eax\n\t"# 把0xd放入eax中,即系统调用号13,指time "int $0x80\n\t" "mov %%eax,%0\n\t" # 返回值是在eax中,%0指tt,把返回值放到tt中去。 : "=m" (tt) ); t = localtime(&tt); printf("time:%d:%d:%d:%d:%d:%d\n", t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); return 0; }
四、使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
在这里我选择的是第7号系统调用,waitpid。1.使用库函数API:
2.嵌入汇编:
3.运行结果:
五、总结
即便是最简单的程序,在进行输入输出等操作时也会需要调用操作系统所提供的服务,也就是系统调用。Linux下的系统调用是通过中断(int 0x80)来实现的。
在执行int 80指令时,寄存器 eax 中存放的是系统调用的功能号,而传给系统调用的参数则必须按顺序放到寄存器 ebx,ecx,edx,esi,edi 中,当系统调用完成之后,返回值可以在寄存器 eax 中获得。
Linux 采用的是 C 语言的调用模式,这就意味着所有参数必须以相反的顺序进栈,即最后一个参数先入栈,而第一个参数则最后入栈。
相关文章推荐
- Linux-进程间通信(一): 管道
- 与一线Linux嵌入式开发工程师的对话
- Linux 运行级别
- CentOS通过删除旧内核解决/boot空间不足的问题
- Linux操作系统下的三种Java环境配置方法
- 《Linux内核分析》 第四周
- linux之sed命令
- 2013337朱荟潼 Linux第五章读书笔记——系统调用
- Linux下cpuinfo详解
- linux grep 和 find 命令
- 从事LINUX驱动或者是安卓驱动工作的话,初学者应具备哪些专业技能呢?
- Linux启动流程
- Linux下sqlplus显示乱码
- linux下导入、导出mysql数据库命令
- Linux字符驱动开发学习总结
- Linux程序管理之rpm,yum,编译安装详解
- Linux ./configure --prefix 命令是什么意思?
- debian ubuntu linux系好用的包管理工具 aptitude
- Linux 文件传输 lrzsz,scp
- CentOS6.5中修改yum源