目 录
2.2在Ubuntu下预处理的命令............................................................................. - 5 -
5.3 可执行目标文件hello的格式....................................................................... - 8 -
6.2 简述壳Shell-bash的作用与处理流程........................................................ - 10 -
6.3 Hello的fork进程创建过程........................................................................ - 10 -
7.2 Intel逻辑地址到线性地址的变换-段式管理............................................... - 11 -
7.3 Hello的线性地址到物理地址的变换-页式管理.......................................... - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换................................................ - 11 -
7.5 三级Cache支持下的物理内存访问............................................................. - 11 -
7.6 hello进程fork时的内存映射..................................................................... - 11 -
7.7 hello进程execve时的内存映射................................................................. - 11 -
7.8 缺页故障与缺页中断处理.............................................................................. - 11 -
8.2 简述Unix IO接口及其函数.......................................................................... - 13 -
第1章 概述
1.1 Hello简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
由高级语言编写生成的hello.c文件,经过预处理后生成hello.i文件,经过编译后得到hello.s文件,将其通过汇编程序处理后得到hello.o的可重定位目标程序二进制文件,最后将其与函数库链接得到可执行目标程序。
之后通过shell执行目标程序,系统会使用fork函数形成一个子进程,为其分配相应的内存资源,随后使用execve函数加载进程,完成了从程序到进程的转变。在CPU工作时,通过取指、译码、执行等微程序,逐步执行目标文件中的程序。hello程序终止后, shell与系统将其回收,释放其运行过程中占用的内存空间,完成o2o
1.2 环境与工具
硬件环境:Intel(R)_Core(TM)_i5-7300HQ_CPU_@_2.50GHz;12GB RAM; 128GB SSD + 1TB;
软件环境:Windows10 家庭版;WMware14; Ubuntu 18.04.1 LTS 64位
开发工具: gcc;gdb;objdump
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
中间结果 | 作用 |
hello.i | 预处理的结果 |
hello.s | 编译后得到的汇编文件 |
hello.o | 汇编后得到的可重定位目标文件 |
hello.out | 链接后得到的可执行目标文件 |
1.4 本章小结
从hello.c到可执行文件,期间经历了预处理、编译、汇编、链接的步骤,展现了一个程序变化的完整过程。
第2章 预处理
2.1 预处理的概念与作用
2.1.1概念
预处理是指在编译的第一遍扫描之前所进行的工作。
预处理指令是在程序正式编译前就由编译器进行的操作,可放在程序中任何位置。它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。
合理使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。
2.1.2作用
在编译代码的第一时间,就把其设定的标识符,全部一一替代,成为了中间码后,再进行正式的编译工作
2.2在Ubuntu下预处理的命令
命令:gcc -E hello.c -o hello.i
hello.c预处理命令及执行结果
2.3 Hello的预处理结果解析
图2.3 hello.c预处理后得到的hello.i内容(部分)
hello.i中,将原本.c文件中的include<stdio.h>的头文件内容引入到原有的代码内容中,实现了编译之前对代码的初步处理过程。
2.4 本章小结
介绍了代码预处理的概念,操作等。预处理是代码实现成功便以前的操作,用于替换一部分代码指令等。
第3章 编译
3.1 编译的概念与作用
3.1.1概念
利用编译程序从源语言编写的源程序产生目标汇编程序的过程。
3.1.2作用
通过词法分析、语法分析、语义检查,在检查无误后,将源程序变成汇编程序。
3.2 在Ubuntu下编译的命令
gcc -S hello.c -o hello.s
图3.2 hello.c编译成hello.s的指令及结果
3.3 Hello的编译结果解析
图3.3 hello.s的内容截图
3.3.1变量
hello.s中有一个全局变量,在hello.c中为sleepsecs。在s文件中,sleepsecs被存放在.rodata中,由于sleepsecs被定义为int,所以原来c文件中的2.5被转换为2。
图3.3.1 变量
3.3.2类型转换
在3.3.1中,变量sleepsecs本被赋值2.5,但是定义为int,所以被隐式转换为了2.
3.3.3算术操作
在c文件中有i++的操作,通过addl $1, -4(%rbp)操作实现。
图3.3.3 算术操作i++
3.3.4 关系操作
c文件中出现了!=,<等多处关系操作,通过cmp指令来实现关系操作,之后通过jmp来进行比较后的跳转。
图3.3.4 关系操作i<10
3.3.5数组操作
数组的索引:对数组的索引实际上就是在第一个元素地址的基础上通过加索引值乘以数据大小来实现。
在c文件中,有数组char *argv[],char*大小8字节,所以argv[1]和argv[2]分别为8和16(十进制下16)。
图3.3.5数组操作
3.3.6控制转移
在c文件中有if和for,均属于控制转移。
if的实现:
图3.3.6.1源文件中的代码
图3.3.6.2s文件中的if
3.4 本章小结
本章介绍了编译器的编译操作,将C语言预处理后转换成汇编代码,为下一步做准备。
(以下格式自行编排,编辑时删除)
第4章 汇编
4.1 汇编的概念与作用
4.1.1概念
汇编是指系统将hello.s翻译转换为机器语言,生成可重定位目标程序,及hello.o。
4.1.2作用
将汇编代码转换为机器指令,使链接之后能直接执行。
4.2 在Ubuntu下汇编的命令
gcc -c hello.s -o hello.o
图4.2 汇编命令及执行结果
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。
图4.3.1 可重定位的目标文件ELF头
图4.3.2节头
图4.3.3重定位节
图4.3.4 .symtab节
4.4 Hello.o的结果解析
图4.4.1 hello.s截图
图4.4.2反汇编得到的结果
对比两个文件,汇编与反汇编比较后发现,两者之间主要的流程没有区别。
机器语言是由二进制代码构成的计算机能够直接识别和执行的一种机器指令的集合,与处理器紧密相关。机器语言由操作码和操作数组成,操作码与汇编语言符号存在的对应关系。由于操作数类型、寻址方式等的不同,同一个汇编语言符号可能对应着不同的机器语言操作码。机器语言与汇编语言具有一一对应的映射关系,一条机器语言程序对应一条汇编语言语句,但不同平台之间不可直接移植。
4.5 本章小结
本章介绍了程序的生成过程中的会变相关内容,汇编过程将汇编语言转换为机器代码,生成可重定位的目标文件。
第5章 链接
5.1 链接的概念与作用
5.1.1概念
链接是指将文件中调用的各种函数跟静态库及动态库链接,并将它们打包合并形成目标文件,即可执行文件。
5.1.2作用
通过链接可以实现将头文件中引用的函数并入到程序中。
注意:这儿的链接是指从 hello.o 到hello生成过程。
5.2 在Ubuntu下链接的命令
ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o hello.o /usr/lib/x86_86-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o
图5.2 链接命令
5.3 可执行目标文件hello的格式
图5.3.1 ELF头
图5.3.2节头
5.4 hello的虚拟地址空间
(使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。)
5.5 链接的重定位过程分析
图5.5 hello可执行文件反汇编
使用objdump -d -r hello得到反汇编结果与hello.o的反汇编结果不同。可执行文件的反汇编结果中给出了重定位结果,但hello.o的反汇编结果中,各部分的开始地址均为0
5.6 hello的执行流程
载入:
_dl_start
_dl_init
开始执行:
_start
_libc_start_main
_init
执行main:
_main
_printf
_exit
_sleep
_getchar
_dl_runtime_resolve_xsave
_dl_fixup
_dl_lookup_symbol_x
退出:
exit
5.7 Hello的动态链接分析
global_offset之前为全0状态,_dl_init之后有了相应偏移量。开始初始化程序。
5.8 本章小结
本章介绍了链接的概念和作用,查看了hello的ELF格式,还用了无语的EDB。
第6章 hello进程管理
6.1 进程的概念与作用
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
6.2 简述壳Shell-bash的作用与处理流程
6.2.1作用
shell作为一个程序,提供了一个用户与系统内核进行交互的功能。
6.2.2处理流程
执行一系列的读/求值步骤,然后终止。读步骤读取来自用户的一个命令行。求值步骤解析命令行,并代表用户运行程序。
6.3 Hello的fork进程创建过程
fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程。父进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程(父进程)的所有值都复制到新的新进程(子进程)中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
6.4 Hello的execve过程
在父进程中fork一个子进程,在子进程中调用exec函数启动新的程序。exec函数一共有六个,其中execve为内核级系统调用。execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用数组指针来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。
6.5 Hello的进程执行
各种进程之间的调度又有很多方法,主要有先进先出、时间片轮转等方式。进程的从睡眠状态到唤醒状态,完成了一次进程的调度,中间有保存相应的进程信息,有相应的队列进行保存。
进程调度中还有一个现象是抢占,就是优先级高的进程进行抢占低优先级的执行机会,用户抢占发生在两种情况下,一个是从系统调用返回用户空间,另一个是从中断处理程序返回用户空间。
6.6 hello的异常与信号处理
程序在执行过程中可能出现中断、陷阱、故障和终止的异常。
图6.6.1 CTRL-Z
图6.6.2 CTRL-C
图6.6.3 CTRL-Z后执行命令
6.7本章小结
本章介绍了程序在shell执行及进程的相关概念。
第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址:程序运行由CPU产生的与段相关的偏移地址部分。
物理地址:程序运行时加载到内存地址寄存器中的地址,内存单元的真正地址。
线性地址:这个和虚拟地址是同一个东西,是经过段机制转化之后用于描述程序分页信息的地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
一个逻辑地址由两部份组成,段标识符: 段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节。给定一个完整的逻辑地址1、看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。2、拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,这样,它了Base,即基地址就知道了。3、把Base + offset,就是要转换的线性地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
控制寄存器CR3的高20位作为页目录表所在物理页的页码。首先把线性地址的最高10位(即位22至位31)作为页目录表的索引,对应表项所包含的页码指定页表;然后,再把线性地址的中间10位(即位12至位21)作为所指定的页目录表中的页表项的索引,对应表项所包含的页码指定物理地址空间中的一页;最后,把所指定的物理页的页码作为高20位,把线性地址的低12位不加改变地作为32位物理地址的低12位。
为了避免在每次存储器访问时都要访问内存中的页表,以便提高访问内存的速度,80386处理器的硬件把最近使用的线性—物理地址转换函数存储在处理器内部的页转换高速缓存中。在访问存储器页表之前总是先查阅高速缓存,仅当必须的转换不在高速缓存中时,才访问存储器中的两级页表。页转换高速缓存也称为页转换查找缓存,记为TLB。
7.4 TLB与四级页表支持下的VA到PA的变换
VA:virtual address称为虚拟地址,PA:physical address称为物理地址。转换:首先将CPU内核发送过来的32位VA分成三段,前两段VA和VA作为两次查表的索引,第三段VA作为页内的偏移,之后进行称为Translation Table Walk的查表过程。
7.5 三级Cache支持下的物理内存访问
cache是为了解决处理器与慢速DRAM设备之间巨大的速度差异而出现的.cache属于硬件系统,linux不能管理cache.但会提供flush整个cache的接口.cache分为一级cache,二级cache,三级cache等等.一级cache与cpu处于同一个指令周期.
7.6 hello进程fork时的内存映射
fork创建虚拟内存的时候先创建当前进程的mm_struct,vm_area_struct和页表的原样副本,再将两个进程的每个页面都标记为只读页面,最后将两个进程的每个vm_area_struct都标记为私有。
7.7 hello进程execve时的内存映射
删除已存在的用户区域,映射私有区域,创建新的共享区域,设置程序计数器,使之指向hello程序的代码入口。
7.8 缺页故障与缺页中断处理
进程线性地址空间里的页面不必常驻内存,在执行一条指令时,如果发现他要访问的页没有在内存中(即存在位为0),那么停止该指令的执行,并产生一个页不存在的异常,对应的故障处理程序可通过从外存加载该页的方法来排除故障,之后,原先引起的异常的指令就可以继续执行,而不再产生异常。缺页中断处理函数为arch/arm/mm/fault.c文件中的do_page_fault函数。
7.9动态存储分配管理
在编写程序时,通常并不知道需要处理的数据量,或者难以评估所需处理数据量的变动程度。在这种情况下,要达到有效的资源利用,必须在运行时动态地分配所需内存,并在使用完毕后尽早释放不需要的内存,这就是动态内存管理原理。printf调用了malloc。 (1) malloc()、calloc()分配新的内存区域。(2) realloc()调整已分配的内存区域。(3) free()释放已分配的内存区域。
7.10本章小结
本章介绍了储存器的地址空间,fork和execve等内容,以及malloc。
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
IO设备与主机(CPU、内存)之间的通信不是直接的,而是通过设备控制器,设备控制器是IO设备和主机之间的中介。设备通过设备的模型化,均为一个文件之后通过unix io接口来管理。
8.2 简述Unix IO接口及其函数
将设备映射为文件的方式,允许Unix内核引出一个简单、低级的应用接口。
共有open(打开)、close(关闭)、read(读)、write(写)、lseek(定位)五个函数。
8.3 printf的实现分析
printf函数的代码:
int printf(const char *fmt, ...)
{
int i;
char buf[256];
va_list arg = (va_list)((char*)(&fmt) + 4);
i = vsprintf(buf, fmt, arg);
write(buf, i);
return i;
}
调用了两个外部函数, vsprintf和write。vsprintf是用来格式化的函数,write函数是将元素写到终端的函数
8.4 getchar的实现分析
getchar包含在stdio.h中。
int getchar(void)
{
static char buf[BUFSIZ];
static char* bb=buf;
static int n=0;
if(n==0)
{
n=read(0,buf,BUFSIZ);
bb=buf;
}
return(--n>=0)?(unsigned char)*bb++:EOF;
}
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章节讲述了一下linux的I/O设备管理机制,还有printf和getchar函数。
结论
hello悲惨的人生正如本人一样:
1、经过预处理、编译、汇编、链接等多个烹调过程变成可执行文件。
2、执行,里面还要经受CRTL-Z CTRL-C以及一连串的蹂躏(一次结束多好……)
3、回收……结束,说再见。
磕磕碰碰的学习过程也许就像一个完整的程序的编写吧。
(结论0分,缺少 -1分,根据内容酌情加分)
附件
hello.i(hello.c预处理之后的结果)
hello.s(hello.i编译成汇编语言之后的结果)
hello.o(hello.s生成的二进制文件)
hello(可执行文件)
参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] 林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.
[2] 辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.
[3] 赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[4] 谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.
[5] KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.
[6] CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.
[7] https://www.cnblogs.com/pianist/p/3315801.html
[8] https://blog.csdn.net/danwuxie/article/details/80936237
[8] https://blog.csdn.net/danwuxie/article/details/80936237
(参考文献0分,确实 -1分)