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内核分析第四周——扒开系统调用的三层皮的更多相关文章

  1. 20135327郭皓--Linux内核分析第四周 扒开系统调用的三层皮(上)

    Linux内核分析第四周 扒开系统调用的三层皮(上) 郭皓 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/U ...

  2. linux内核分析 第四周 扒开系统调用的三层皮(上)

    一.用户态.内核态和中断处理过程 系统调用是用户通过库函数方式:库函数帮我们把系统调用封装起来. 内核态:高级别执行,可以使用特权指令,访问任意的物理地址. 用户态:低级别执行,代码范围受到限制. C ...

  3. Linux内核分析 笔记五 扒开系统调用的三层皮(下) ——by王玥

    (一)给MenuOs增加time和time-asm命令 更新menu代码到最新版 在main函数中增加MenuConfig 增加对应的Ttime和TimeAsm函数 make rootfs (二)使用 ...

  4. Linux内核及分析 第四周 扒开系统调用的三层皮(上)

    实验过程 选择20号系统调用getpid(取得进程识别码) 在网上查询getpid函数的C语言代码以及其嵌入式汇编语句 C语言代码: #include <stdio.h> #include ...

  5. Linux内核设计第四周——扒开系统调用三层皮

    Linux内核设计第四周 ——扒开系统调用三层皮 一.知识点总结 (一).系统调用基础知识 1.用户态和内核态 内核态:在高级别的状态下,代码可以执行特权指令,访问任意的物理地址: 用户态:在相应的低 ...

  6. 《Linux内核分析》第四周 扒开系统调用的“三层皮”

    [刘蔚然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] WEEK FOUR( ...

  7. linux 内核 第四周 扒开系统调用的三层皮 上

    姬梦馨 原创作品 http://mooc.study.163.com/course/USTC-1000029000 一.用户态.内核态和中断处理过程 用户通过库函数与系统调用联系起来:库函数帮我们把系 ...

  8. Linux内核分析——第四周学习笔记20135308

    第四周 扒开系统调用的“三层皮” 一.内核.用户态和中断 (一)如何区分用户态.内核态 1.一般现在的CPU有几种不同的指令执行级别 ①在高级别的状态下,代码可以执行特权指令,访问任意的物理地址,这种 ...

  9. LINUX内核分析第四周学习总结——扒开系统调用的“三层皮”

    LINUX内核分析第四周学习总结--扒开系统调用的"三层皮" 标签(空格分隔): 20135321余佳源 余佳源 原创作品转载请注明出处 <Linux内核分析>MOOC ...

随机推荐

  1. Python单元测试--unittest(一)

    unittest模块是Python中自带的一个单元测试模块,我们可以用来做代码级的单元测试. 在unittest模块中,我们主要用到的有四个子模块,他们分别是: 1)TestCase:用来写编写逐条的 ...

  2. TW实习日记:前三天

    今天是2018年7月20号,周五.从周一开始实习到现在,终于想起来要写日记这种东西了,可以记录一下自己这一天所学所做所知也是蛮不错的.先简单总结一下自己的大学生活吧,算是多姿多彩,体验了很多东西.在大 ...

  3. sql批量更新

    -----------------更新无锡医院名称 update Opt_DKI_Hospital set centerName =tmp.[医院名称] from Opt_DKI_Hospital h ...

  4. 产品需求文档(PRD)的写作 【转】

    产品需求文档(PRD)的写作   一.文章的摘要介绍 无论我们做什么事都讲究方式方法,写产品需求文档(以下称PRD文档)也是如此,之前我通过四篇文章分享了自己写PRD文档的一些方法,而这一篇文章主要是 ...

  5. 20181120-10 Beta阶段第2周/共2周 Scrum立会报告+燃尽图 7

    此作业要求参见:[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2415] 版本控制地址    [https://git.coding.n ...

  6. lintcode-422-最后一个单词的长度

    422-最后一个单词的长度 给定一个字符串, 包含大小写字母.空格' ',请返回其最后一个单词的长度. 如果不存在最后一个单词,请返回 0 . 注意事项 一个单词的界定是,由字母组成,但不包含任何的空 ...

  7. python实现进制之间的转换

    十进制转36进制: #36位映射模板 loop = '0123456789abcdefghijklmnopqrstuvwxyz' # 测试用例输入 n = a = [] : a.append( loo ...

  8. 201621123037 《Java程序设计》第6周学习总结

    作业06-接口.内部类 1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图或相关笔记,对面向对象思想进行一个总结. 注1:关键词与内容不求多 ...

  9. POSt 提交参数 实体 和字符串

    //1.后台接受是 字符串形式 [HttpPost] public int SendTaxiAudioByWX(string userid, string suid, string indexno, ...

  10. IE 代理服务器设置程序实现

    IE 代理服务器设置程序实现 分类: Delphi2003-08-02 18:42 1398人阅读 评论(0) 收藏 举报 服务器ieinternetstringconstructordelphi 本 ...