2019-2020-1 20199329《Linux内核原理与分析》第五周作业

《Linux内核原理与分析》第五周作业


一.上周问题总结:

  • 虚拟机将c文件汇编成汇编文件时忘记添加include<stdio.h>
  • gdb跟踪汇编过程不熟练

二.本周学习内容:

1.课本学习

1.1 用户态、内核态和中断

  • 内核态:处于高的执行级别下,代码可以执行特权指令,访问任意的物理地址,这时的CPU就对应内核态,对所有的指令包括特权指令都可以执行。
  • 用户态:处于低的执行级别下,代码只能在级别允许的特定范围内活动。在日常操作下,执行系统调用的方式是通过库函数,库函数封装系统调用,为用户提供接口以便直接使用。
  • Intel x86 CPU有4种不同的执行级别0、1、2、3,Linux只使用了其中的0和 3两个级別分别表示内核态和用户态。用户态和内核态很显著的区别方法就是CS:EIP的指向范围,内核态cs:eip的值是任意的,即可以访问所有的地址空间。用户态只能访问其中的一部分内存地址(0x00000000-0xbbbbbbbf),0xc0000000以上的地址只能在内核态下访问。
  • 中断处理是从用户态进入内核态的主要方式,系统调用是一种特殊的中断。从用户态切换到内核态时,中断/int指令会在堆栈上保存用户态的寄存器上下文,其中包括用户态栈顶地址、当时的状态字、当时的cs:eip的值,还有内核态的栈顶地址、内核态的状态字、中断处理程序的入口。中断发生后的第一件事就是保存现场,保存一系列的寄存器的值;中断处理结束前的最后一件事就是恢复现场,退出中断程序,恢复保存寄存器的数据。

1.2 系统调用

  • 系统调用的意义:把用户从底层的硬件编程中解放出来、极大地提高系统的安全性、使用户程序具有可移动性。
  • 系统调用的库函数就是读者使用的操作系统提供的API(应用程序编程接口),API只是函数定义。系统调用是通过软中断向内核发出了中断请求,int指令的执行就会触发一个中断请求。Libc库函数定义的一些API内部使用了系统调用的封装例程,其主要目的是发布系统调用,使程序员在写代码时不需要用汇编指令和寄存器 传递参数来触发系统调用。一般每个系统调用对应一个系统调用的封装例程,函数库再用这些封装例程定义出给程序员调用的API,这样把系统调用最终封装成方便程序员使用的库函数。
  • 如下图所示,User mode表示用户态,kernel mode表示内核态。Xyz()就是一个API函数,是系统调用对应的API,其中封装了一个系统调用,会触发int $0x80的中断,对应system_call内核代码的起点,即中断向量0X80对应的中断服务程序入口,内部会有sys_xyz()系统调用处理函数,执行完sys_xyz()后会ret_from_sys_call,这里是进程调度最常见的调度时机点。如果没有发生进程调度,就会执行iret再返回到用户态接着执行。系统调用的3层机制分别为xyz(),system_call和sys_xyz()。

2019-2020-1 20199329《Linux内核原理与分析》第五周作业

  • 当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。在Linux中是通过执行int$0x80来执行系统调用的,这条汇编指令产生向量为128的编程异常,Intel Pentium II 中引入了sysenter指令(快速系统调用),2.6已经支持。除了系统调用外,系统调用也可能需要传递参数。内核实现了很多不同的系统调用,进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数,使用eax寄存器。系统调用也需要输入输出参数,例如:实际的值,用户态进程地址空间的变量的地址,甚至是包含指向用户态函数的指针的数据结构的地址。system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号。一个应用程序调用fork()封装例程,那么在执行into$0x80之前就把eax寄存器的值置为2(即_NR_fork)。这个寄存器的设置是libc库中的封装例程进行的,因此用户一般不关心系统调用号,进入sys_call之后,立即将eax的值压入内核堆栈。寄存器传递参数具有如下限制: 1)每个参数的长度不能超过寄存器的长度,即32位 2)在系统调用号(eax)之外,参数的个数不能超过6个(ebx, ecx,edx,esi,edi,ebp) 3)超过6个怎么办?超过6个的话就把某一个寄存器作为一个指针,指向某一块内存。

2.实验楼实验

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

使用库函数API进行触发

调用系统库函数getpid()来获取进程识别码如下图:

2019-2020-1 20199329《Linux内核原理与分析》第五周作业

执行结果如下:

2019-2020-1 20199329《Linux内核原理与分析》第五周作业

C代码中嵌入汇编代码进行触发

2019-2020-1 20199329《Linux内核原理与分析》第五周作业

执行结果如下:

2019-2020-1 20199329《Linux内核原理与分析》第五周作业

2.2 含两个参数的系统调用rename

首先创建文件zxf.c

嵌入式汇编代码进行触发

2019-2020-1 20199329《Linux内核原理与分析》第五周作业

执行结果如下:

成功将zxf.c改名为zxf20199329.c

2019-2020-1 20199329《Linux内核原理与分析》第五周作业

库函数API进行触发

2019-2020-1 20199329《Linux内核原理与分析》第五周作业

执行结果如下:

成功将zxf20199329.c改回为zxf.c

2019-2020-1 20199329《Linux内核原理与分析》第五周作业


三.总结与疑难

  • 系统调用的意义是操作系统为用户态进程与硬件设备进行交互提供了一组接口。
  • 一个API可能只对应一个系统调用,也可能内部由多个系统调用实现,一个系统调用也可能被多个API调用。
  • 系统调用的三层机制分别为xyz(),system_call,sys_xyz()。
  • 内核通过给每个系统调用一个编号来区分,即系统调用号,将API函数xyz()和系统调用内核函数sys_xyz()关联起来了。
  • EAX用于传递系统调用号。
  • 传递参数时参数按顺序赋值给EBX、ECX、EDX、ESI、EDI、EBP。如果参数超过6个,就把某一个寄存器作为指针指向内存,这样可以通过内存来传递更多的参数。
  • 应用程序系统调用(API)和系统调用不同的API知识函数定义。系统调用是通过软中断向内核发出中断请求。

QUSTION:

  • 只要字符串建在堆栈空间(而不是堆空间),通过汇编的系统调用都无法正确输出。

四.下周计划

  • [ ] 完成书本上的课后习题
  • [ ] 复习之前学习的汇编和反汇编

2019 年 10月 20日

上一篇:2017-2018-1 JaWorld 第四、五周作业


下一篇:格式化输出