第四周:扒开系统调用的三层皮

吕松鸿 + 原创做品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000linux

1、用户态、内核态和中断处理过程

  • 用户态:当进程在执行用户本身的代码时,则称其处于用户态,即此时处理器在特权级最低的(3级)用户代码中运行。
  • 内核态:当一个进程执行系统调用而陷入内核代码中执行时,咱们就称进程处于内核态,此时处理器处于特权级最高的(0级)内核代码中执行。
  • PS:CPU指令执行级别

中断:中断处理是从用户态进入内核态主要的方式。编程

用户态和内核态的转换:安全

 

中断处理的完整过程
进入中断程序,保存寄存器数据 SAVE_ALL -..//内核代码,完成中断服务,发生进程调度
RESTORE_ALL//退出中断程序,恢复寄存器数据
iret(pop cs:eip/ss:esp/eflags from kernel stack)//对应着中断信号或int指令,与发生时CPU动做相反
 

2、系统调用概述

1.系统调用:系统调用只是一种特殊的中断。app

这是用户态进程主动要求切换到内核态的一种方式,用户态进程经过系统调用申请使用操做系统提供的服务程序完成工做。而系统调用的机制其核心仍是使用了操做系统为用户特别开放的一个中断来实现。函数

系统调用的意义:spa

把用户从底层的硬件编程中解放出来
极大的提升了系统的安全性
使用户程序具备可移植性

API和系统调用:操作系统

应用编程接口(application program interface,API)和系统调用是不一样的指针

API只是一个函数定义
系统调用经过软中断向内核发出一个明确求

 Libc库定义的一些API引用了封装例程:code

通常每一个系统调用对应一个封装例程
库再用这些封装例程定义出给用户的API

不是每一个API都对应一个特定的系统调用:blog

API可能直接提供用户态的服务(如一些数学函数)
一个单独的API可能调用几个系统调用
不一样的API可能调用了同一个系统调用

返回值

大部分封装例程返回一个整数,其值的含义依赖于相应的系统调用 -1在多数状况下表示内核不能知足进程的请求 Libc中定义的errno变量包含特定的出错码

应用程序、封装例程、系统调用处理程序、系统调用服务例程之间的关系

2.系统调用的三层皮

•API(xyz)
•中断向量(system_call)
•中断服务程序(sys_xyz)
  • 当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。

  • Linux中是经过执行int $0x80来执行系统调用,这条汇编指令产生向量为128的编程异常 —— 即中断向量0x80与System_call绑定起来。

  • 系统调用号将函数xyz()和中断服务程序sys_xyz关联起来。

3. 参数传递

  • 内核实现了不少不一样的系统调用,进程用系统调用号这个参数指明须要哪一个系统调用。

  • system_call是linux中全部系统调用的入口点,每一个系统调用至少有一个参数,使用eax寄存器传递系统调用号。

  • 寄存器传递参数的限制

    - 每一个参数的长度不能超过寄存器的长度,即32位 - 在系统调用号(eax)以外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp) - 超过6个的状况下,使用某一个寄存器做为指针,进入内核态以后能够访问全部的地址空间,经过某一片区域传递参数。

     

3、实验:使用库函数API和c代码中嵌入汇编代码触发同一系统调用

  • 使用库函数API获取系统当前时间
  • C代码中嵌入汇编代码的方法
  • 使用C代码中嵌入汇编代码触发系统调用获取系统当前时间

1.使用库函数API获取系统当前进程pid值

在这里我使用的是获取进程pid的库函数getpid()

 

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h>
int main(int argc, const char *argv[]) { pid_t tt; tt = getpid(); printf("%u\n", tt); return 0; }

2.使用c代码中嵌入汇编代码

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h>
int main(int argc, const char *argv[]) { pid_t tt; asm volatile( "mov $0x14, %%eax\n\t"//系统调用号14放在eax中
      "int $0x80\n\t" //系统调用中断
      "mov %%eax, %0\n\t"//取出eax中返回的值
      :"=m" (tt) ); printf("%u\n", tt); return 0; }

 

 

 

 

4、总结:

Linux下的系统调用是经过中断(int 0x80)来实现的。

能够经过库函数API使用系统调用或者用汇编方式触发系统调用。

相关文章
相关标签/搜索