程涵 

原创博客

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

使用库函数API和C代码中嵌入汇编代码触发同一个系统调用】 


知识点整理

一、用户态、内核态和中断

处理过程

1. 通过库函数完成系统调用:库函数将系统调用封装起来。

2. 用户态与内核态

  • 内核态:一般现代CPU有几种指令执行级别。在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别对应着内核态。
  • 用户态:在相应的低级别执行状态下,代码的掌控范围有限,只能在对应级别允许的范围内活动。

3. 区分用户态与内核态的方法

  • cs寄存器的最低两位表明了当前代码特权级
  • CPU每条指令的读取都是通过cs:eip这两个寄存器

4. 中断处理

  • 中断处理是从用户态到内核态的主要方式(系统调用是中断的一种特殊形式)
  • 系统调用只是一种特殊的中断
  • 寄存器上下文,从用户态切换到内核态时:必须保存用户态的寄存器上下文,同时将内核态的寄存器相应的值放入当前CPU
  • 中断/int指令会在堆栈上保存一些寄存器的值:如用户态栈顶地址、当前的状态字、当时cs:eip的值(当前中断程序的入口)

5. 保护现场与恢复现场

  • 保护现场:进入中断程序,保存需要用到的寄存器的数据(中断发生后的第一件事)

    #define SAVE_ALL  //将其他寄存器的值push到内核堆栈中
  • 恢复现场:退出中断程序,恢复保存寄存器的数据(中断处理结束前最后一件事)

    
    
    #RESTORE_ALL  //将用户态保存的寄存器pop到当前CPU中
  • iret指令:iret指令与中断信号(包括int指令)发生时的CPU的动作相反

6. 中断处理的完整过程

  • 第一步

    interrupt(ex:int 0x80)-save //int 0x80指系统调用
    cs:eip/ss:esp/eflags(current)to kernel stack,then //中断将cs:eip、ss:esp(当前堆栈段栈顶)、eflags(当前标志寄存器)保存到内核堆栈中
    load cs:eip(entry of a specific ISR)and //将当前中断信号相关联的中断服务入口加载到cs:eip
    ss:esp(point to kernel stack). //同时将当前指向内核信息的的堆栈段和esp也加载到CPU中
  • 第二步

    SAVE_ALL
    -... //内核代码,完成中断服务,(完成中断服务后可能)发生进程调度
    //如果发生了进程调度,则当前的状态都会暂时保存在系统中。当其他进程调度切换回当前进程时,则接着执行RESTORE_ALL
  • 第三步

    RESTORE_ALL
  • 第四步

    iret -pop cs:eip/ss:esp/eflags from kernel stack

二、系统调用概述和系统调用的三层皮

1. 系统调用概述

系统调用是操作系统为用户态进程与硬件设备进行交互提供的一组接口。

意义:

  • 把用户从底层的硬件编程中解放出来
  • 极大的提高了系统的安全性
  • 使用户程序具有可移植性(用户程序与具体硬件被抽象的接口替代,没有非常紧密的关系)

2. API和系统调用

  • 应用程序接口(API)与系统调用不同

    • API只是一个函数定义
    • 系统调用通过软件中断trap向内核发出一个明确的请求
  • Libc库定义的一些API引用了封装例程(唯一目的是发布系统调用,直接调用函数就可以出发系统调用)
    • 一般每个系统调用对应一个封装例程
    • 库再用这些封装例程定义出给用户的API
  • 不是每个API都对应一个特定的系统调用
    • API可能直接提供用户态的服务,如一些数学函数
    • 一个单独的API可能调用几个系统调用
    • 不同的API可能调用了同一个系统调用
  • 返回值
    • 大部分封装例程返回一个整数,其值的含义依赖于相应的系统调用
    • -1在多数情况下表示内核不能满足进程的请求
    • Libc中定义的errno变量包含特定的出错码

3. 系统调用的三层皮

xyz(API)、system_ call(中断向量)、sys_xyz(中断向量对应的中断服务程序)

4. 系统调用程序及服务例程

  • 当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数

    • 在Linux中是通过执行int $0x80来执行系统调用的, 这条汇编指令产生向量为128的编程异常
    • Intel Pentium II中引入了sysenter指令(快速系统调用),2.6已经支持
    • (系统调用号将xyz和sys_xyz关联起来)
  • 传参:内核实现了很多不同的系统调用,进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数
    • 使用eax寄存器

5. 参数传递

  • 系统调用也需要输入输出参数,例如

    • 实际的值
    • 用户态进程地址空间的变量的地址
    • 甚至是包含指向用户态函数的指针的数据结构的地址
  • system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号
    • 一个应用程序调用fork()封装例程,那么在执行int $0x80之前就把eax寄存器的值置为2(即_NRfork)。
    • 这个寄存器的设置是libc库中的封装例程进行的,因此用户一般不关心系统调用号
    • 进入sys_call之后,立即将eax的值压入内核堆栈
  • 寄存器传递参数具有如下限制:
    • 每个参数的长度不能超过寄存器的长度,即32位
    • 在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp)
    • 超过6个则将某一个寄存器作为一个指针指向一块内存,进入内存态后可以访问所有地址空间,可以通过那块内存传递数据

三、使用库函数API和C代码中嵌入汇编代码触发同一个系统调用

1.使用库函数API获取系统当前时间

time.c

#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+, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
return ;
}

2.使用C代码中嵌入汇编代码触发系统调用获取系统当前时间

time_asm.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+, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
return ;
}

用户态向内核态:
1.传递了一个系统调用号 - eax
2.传递了参数 - ebx

实验过程及截图

要求:

  • 选择一个系统调用(13号系统调用time除外),系统调用列表参见http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/syscalls/syscall_32.tbl
  • 参考视频中的方式使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

我选择的是24号getuid。

getuid.c

#include <unistd.h>
#include <stdio.h> int main()
{
pid_t uu;
uu=getuid();
printf("uu = %d \n", uu);
return ;
}

getuid_asm.c

#include <unistd.h>
#include <stdio.h> int main()
{ pid_t uu;
uu = getuid();
asm volatile(
"mov $0x24,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
:"=m"(uu)
);
printf("uu = %d \n",uu);
return ; }

执行结果:

分析过程:

我们使用int 0x80触发中断,然后中断处理程序保存现场,我们的进程陷入内核态。

系统调用的工作机制是:当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数,由API、中断向量和中断处理程序协调完成。

而在系统调用的过程中,系统调用号使用eax寄存器传递参数。

寄存器在传递参数时的限制:

1)每个参数的长度不能超过寄存器的长度,即32位。

2)在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp),若超过6个,就将某一个寄存器作为指针,指向一块内存,在进入内核态之后,可以访问所有的地址空间,通过内存来传递参数。

【MOOC EXP】Linux内核分析实验四报告的更多相关文章

  1. 【MOOC EXP】Linux内核分析实验八报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 进程的切换和系统的一般执行过程 知识点 ...

  2. 【MOOC EXP】Linux内核分析实验七报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 可执行程序的装载 知识点梳理 一.预处 ...

  3. 【MOOC EXP】Linux内核分析实验六报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 进程的描述和进程的创建 知识点梳理: ...

  4. 【MOOC EXP】Linux内核分析实验一报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000  [反汇编一个简单的C程序]   实验 ...

  5. 【MOOC EXP】Linux内核分析实验二报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000  [操作系统是如何工作的]   教学内 ...

  6. 【MOOC EXP】Linux内核分析实验三报告

     程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 [跟踪分析Linux内核的启动过程] ...

  7. 【MOOC EXP】Linux内核分析实验五报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 分析system_call中断处理过程 ...

  8. “Linux内核分析”实验一报告

    张文俊 + 原创作品转载请注明出处 + <Linux 内核分析> MOOC 课程 实验要求: 1.总结部分要求阐明自己对“计算机是如何工作的”理解: 2.博客中需要使用实验截图: 实验内容 ...

  9. “Linux内核分析”实验二报告

    张文俊 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.第二周学习内 ...

随机推荐

  1. Swift 并行编程现状和展望 - async/await 和参与者模式

    这篇文章不是针对当前版本 Swift 3 的,而是对预计于 2018 年发布的 Swift 5 的一些特性的猜想.如果两年后我还记得这篇文章,可能会回来更新一波.在此之前,请当作一篇对现代语言并行编程 ...

  2. 死磕nginx系列--nginx 限流配置

    限流算法 令牌桶算法 算法思想是: 令牌以固定速率产生,并缓存到令牌桶中: 令牌桶放满时,多余的令牌被丢弃: 请求要消耗等比例的令牌才能被处理: 令牌不够时,请求被缓存. 漏桶算法 算法思想是: 水( ...

  3. 添加外键式异常 1215-cannot add foreign key constranint

    添加外键时报错,原因是添加外键的表的字段的字段类型不一致 比如我的第一张表id是int类型,添加约束的dep_id是bigint类型,所以报错,只要把两张表添加约束的字段类型改成统一的即可 本人大学生 ...

  4. 欢迎来到Curl的世界

    一.Curl 简介 curl命令是一个利用URL规则在命令行下工作的文件传输工具.它支持文件的上传和下载,所以是 综合传输工具,但按传统,习惯称curl为下载工具.作为一款强力工具,curl支持包括H ...

  5. Python2.7-string模块

    string模块 1.模块内定义的常数 字母(ascii_letters.letters),数字(digits.hexdigits.octdigits),空格(whitespace),大小写(asci ...

  6. Python2.7-pickle, cpickle

    pickle, cpickle模块,用于序列化和反序列化 python 对象数据,可以被序列化的有:布尔值,数值,字符串,包含以上三类的容器,定义在模块顶层的函数.内置函数和类,实例对象的 __dic ...

  7. POJ 3660 Cow Contest(Floyd求传递闭包(可达矩阵))

    Cow Contest Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 16341   Accepted: 9146 Desc ...

  8. 详解大数据采集引擎之Sqoop&采集oracle数据库中的数据

    一.Sqoop的简介: Sqoop是一个数据采集引擎/数据交换引擎,采集关系型数据库(RDBMS)中的数据,主要用于在RDBMS与HDFS/Hive/HBase之间进行数据传递,可以通过sqoop i ...

  9. 20155339 Exp4 恶意代码分析

    20155339 Exp4 恶意代码分析 实验后回答问题 (1)如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些,用什么方法来监控 ...

  10. Hiberante持久化对象的3种状态

        近日一同事问我关于Hibernate中对象的3种状态的问题,因此特意总结一下.在Hibernate中对象是有以下3中状态: 瞬时态(transient object): 没有OID值,没有被s ...