《Linux内核分析》第四周学习总结编程
——扒开系统调用的三层皮数据结构
姓名:王玮怡 学号:20135116app
理论总结部分:函数
第一节 用户态、内核态和中断学习
1、用户态、内核态和中断处理过程spa
通常现代CPU都有几种不一样的指令执行级别操作系统
一、用户态指针
在相应的低级执行状态下,代码的掌控范围会受到限制,此时为用户态。blog
二、内核态接口
在高执行级别下,代码能够执行特权指令,访问任意的物理地址,这种CPU执行级别就对应着内核态。
三、举例
Intel x86 CPU有四种不一样的执行级别0-3,Linux只使用了0级和3级分别表示内核态和用户态。
四、Linux代码中如何区分用户态和内核态
(1)cs寄存器的最低两位代表当前代码的特权级
(2)CPU每条指令的读取都是经过cs:eip这两个寄存器,其中cs是代码段选择寄存器,eip是偏移量寄存器
(3)上述判断有硬件完成
(4)通常来讲在Linux中,地址空间是一个显著的标志:
0xc0000000以上的地址空间只能在内核态下访问
0x00000000-oxbfffffff的地址空间在两种状态下都能访问
(注意:这里说的地址空间为逻辑地址而不是物理地址)
五、中断处理是从用户态进入内核态的主要方式
系统调用只是一种特殊的中断
(1)从内核态切换到用户态时必须保存用户态的寄存器上下文,同时也要将内核态的寄存器相应值放入当前CPU中
(2)中断/int指令会在堆栈上保存一些寄存器的值,如用户态栈顶地址、当前的状态字、当时的cs:eip的值,同时还保存内核态栈顶地址、当前的状态字以及中断处理程序的入口(cs:eip的值)
(3)中断信号/int指令发生后进入中断处理程序,执行的第一个动做是SAVE_ALL,将其余寄存器的值push到内存堆栈中去,保存现场;中断处理程序结束前最后一件事是恢复现场RESTORE_ALL,将用户态的寄存器再popl出来到当前CPU中;iret指令与中断信号(包括int指令)发生时的CPU的动做正好相反
(4)中断处理的完整过程
第二节 系统调用概述
1、系统调用概述和系统调用的三层皮
一、系统调用的意义
二、操做系统提供的API(应用编程接口)和系统调用的关系
(1)API只是一个函数定义;系统调用经过软中断(rap)向内核发出一个明确的请求
(2)Libc库定义的一些API引用了封装例程(wrapper routine,惟一目的就是发布系统调用)
通常每一个系统调用对应一个封装例程
库再调用这些封装例程定义出给用户的API
注意:
三、系统调用的三层皮:xyz、system_call和sys_xyz
(1)当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数
在Linux中经过int$0x80来执行系统调用——产生向量为128的编程异常
(2)传参:内核实现了不少不一样的系统调用,进程须要经过传递一个名为系统调用编号的参数来指明须要哪一个系统调用(经过eax寄存器来实现传递)——系统调用号将xyz和sys_xyz关联起来
(3)系统调用的参数传递方法
a.系统调用也须要输入输出参数,如实际值、用户态进程地址空间的变量的地址、包含指向用户态函数的指针的数据结构的地址
b.system_call是Linux中全部系统调用的入口点,每一个系统调用至少有一个参数,即由eax传递的系统调用号
c.寄存器传递参数的限制:
每一个参数的长度不能超过寄存器的长度,即32位
在系统调用号(eax)以外,参数的个数不能超过6个(ebx、ecx、edx、esi、edi、ebp),若是超过6个,就把其中某一个寄存器做为一个指针,指向一块内存,进入内核态能够访问全部的地址空间,经过内存传递数据
实验部分 :使用库函数API和C代码嵌入汇编代码触发同一个系统调用
1、使用库函数API获取当前进程的进程号(getpid())
运行结果以下:
使用getpid(),定义int型的数值变量tt(返回值)
2、使用C代码嵌入汇编代码触发系统调用获取当前进程的进程号(getpid())
mov $0,%%ebx\n\t 系统调用传递第一个参数使用容ebx,这里是NULL(将ebx寄存器清零)
mov $0x14,%%eax\n\t 使用eax传递系统调用号,这里getpid是20
mov %%eax,%0\n\t 系统调用的返回值使用eax存储,tt变量
运行结果以下: