HIT计算机系统 大作业 程序人生
摘 要
通过分析hello.c的从程序到进程的过程,展示本学期计算机系统课所学的部分内容:程序的编译、链接,进程,内存,I/O。hello.c经过预处理、编译、汇编、链接成为一个可执行程序,shell进程创建子进程,在子进程中运行hello程序,对hello进程进行管理,在hello进程终止后回收它。hello程序运行时,Linux系统的内存系统和I/O也在发挥作用。
关键词:计算机系统;C语言
第1章 概述
1.1 Hello简介
源程序文本文件hello.c经预处理、编译、汇编、链接变为可执行目标文件hello。在bash内,执行 ./hello <学号> <姓名> 命令后,shell先使用fork创建子进程,然后用execve在子进程的上下文中加载并运行可执行目标文件hello。这是hello的P2P(from Program to Process)。
hello进程终止后,shell会回收它。这是hello的020(from zero to zero)。
1.2 环境与工具
硬件环境:X64 CPU;2.3GHz;8G RAM;256GHD Disk
软件环境:macOS 11.2.3;VMware Fusion 12.1.2;Ubuntu 20.04.2
开发与调试工具:gcc;objdump;readelf;as;ld;Sublime;gedit;edb;gdb
1.3 中间结果
hello.i 是hello.c的预处理结果
hello.s 是使用gcc将hello.i编译得到的
hello.o 是将hello.s进行汇编得到的
hello 是将hello.o与库链接得到的
hello_dump_dr.txt 对hello用objdump -dr输出重定位得到的
helloo_dump_dr.txt 对hello.o用objdump -dr输出重定位得到的
1.4 本章小结
本章简要介绍了hello的P2P、020,列出了完成本次大作业所使用的环境与工具以及产生的中间结果。
第2章 预处理
2.1 预处理的概念与作用
预处理的作用:预处理指令一般被用来使源代码在不同的执行环境中被方便的修改或者编译。
2.2在Ubuntu下预处理的命令
2.3 Hello的预处理结果解析
2.4 本章小结
本章介绍了预处理的概念和作用,对hello.c的预处理结果进行了解析。
第3章 编译
3.1 编译的概念与作用
编译是预处理后的.i文件翻译成ASCII汇编语言.s文件。编译主要进行的是词法分析和语法分析,又称为源程序分析。若分析过程中发现有语法错误,则会给出提示信息。
3.2 在Ubuntu下编译的命令
3.3 Hello的编译结果解析
hello.c中出现的常量有printf中的格式串"Usage: Hello 学号 姓名!\n"和"Hello %s %s\n"。
格式串中的每个中文字符根据UTF-8的编码规则被编码为3个字节的二进制码。
hello.c中出现的变量有全局变量sleepsecs,main的命令行参数argc、argv以及局部变量i。argc存在%edi中,
argv存在%rsi中,用偏移的方式引用argv[1]和argv[2],
hello.c中发生了两次赋值,分别是sleepsecs和i的初始化。sleepsecs被赋值2,在汇编代码中直接用它的变量名引用它,
对int类型的sleepsecs赋值时发生了隐式类型转换,浮点数2.5被隐式转换为2。
用偏移的方式引用argv[1]和argv[2]。一开始指针argv存在%rsi中,它先被movl到%rbp中,然后通过%rbp加上偏移量引用argv[1]和argv[2]。
用比较和跳转指令实现if语句,当argc不等于3时,控制会跳转到标号.L6处。
for循环被翻译为guarded-do形式。首先使用条件分支,若初始条件不成立就跳过循环。
用call指令进行函数调用。hello.c中的两个printf函数中的第一个被翻译为puts。movl指令将字符串的地址放到%rdi,作为puts的第一个参数。用call指令将控制转移给puts。
格式串的地址、argv+1、argv+2分别被放在%rdi、%rsi、%rdx中作为printf的三个参数。用call指令将控制转移给printf。
3.4 本章小结
本章介绍了编译的概念和作用,将hello.i编译为hello.s后对编译结果进行了分析。
第4章 汇编
4.1 汇编的概念与作用
汇编是指将编译后的.s文件翻译为机器语言二进制可重定位目标.o文件。
4.2 在Ubuntu下汇编的命令
gcc -c -m64 -Og -no-pie -fno-PIC hello.s -o hello.o
4.3 可重定位目标elf格式
.rodata 只读数据,比如printf语句中的格式串和开关语句的跳转表
.bss 未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量。
.symtab 符号表,它存放在程序中定义和引用的函数和全局变量的信息。
.strtab 一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。
节头部表:描述了不同节的位置和大小。目标文件中每个节都有一个固定大小的条目。
这是调用第一个printf函数时对"Usage: Hello 学号 姓名!\n"的引用:
这是调用第二个printf时对"Hello %s %s\n"的引用:
4.4 Hello.o的结果解析
机器语言是由二进制码组成的,每条机器指令的字节数不等,由操作码字段和操作数字段组成。
在.o文件中,原本.s文件中的跳转指令用到的语句标号被换成了.text节的偏移量,比如
movl语句中用于引用.rodata和.data段中的标号被换为了数字0,表示等待链接器根据重定位条目进行重定位,比如
此外,所有在.s文件中用十进制表示的数在.o文件中都用十六进制表示。
4.5 本章小结
本章介绍了汇编的概念与作用,介绍了ELF可重定位目标文件的格式,并分析了hello.s的汇编结果。
第5章 链接
5.1 链接的概念与作用
链接的作用:把可重定位目标文件和命令行参数作为输入,产生一个完全链接的,可以加载运行的可执行目标文件。链接器在软件开发中扮演一个重要的角色,它使得分离编译成为可能。
5.2 在Ubuntu下链接的命令
5.3 可执行目标文件hello的格式
.rodata 只读数据,比如printf语句中的格式串和开关语句的跳转表
.bss 未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量。
.symtab 符号表,它存放在程序中定义和引用的函数和全局变量的信息。
.debug 一个调试符号表,其条目时程序中定义的全局变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。
.line 原始C源程序的行号和.text节中机器指令之间的映射
.strtab 一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。
段头部表描述了文件中各段的位置与其在内存中运行时位置的映射关系:
此时还存在重定位节说明重定位还没有完成,程序加载/运行时会完成最后的链接。
5.4 hello的虚拟地址空间
.text节中,main在0x401105到0x40116f,
5.5 链接的重定位过程分析
经过链接后,hello的代码段比hello.o长了很多,这是因为链接器将hello.o和库中的许多目标文件进行了合并。.init节定义了一个小函数_init,程序的初始化会调用它。
在.text节中,多了一个函数 _start。加载程序时,加载器会跳转到_start的地址,它会调用main函数。
通过.plt中的数据结构与.got的合作,程序运行时可以调用动态链接器与printf、puts等库函数链接。
在hello中,.text节的main中的重定位已经完成了。main的起始位置变为0x401105,而不是hello.o中的0。
两次对.rodata节中的字符串的32位绝对地址引用已经生成了。
对printf、sleep、puts、exit、getchar的调用被重定位到.plt节。
此时.got中还是空的,程序运行时,动态链接器会将调用的库函数的运行时地址写入.got。
5.6 hello的执行流程
main调用了puts、printf、sleep、getchar
__run_exit_handlers调用了很多函数,它执行完之后程序结束。
5.7 Hello的动态链接分析
5.8 本章小结
本章介绍了链接的概念与作用,分析了hello的链接与执行过程。
第6章 hello进程管理
6.1 进程的概念与作用
进程是一个执行中程序的实例。系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行的状态组成的。
作用:进程给应用程序提供两个关键抽象:一个独立的逻辑控制流,一个私有的存储空间。在操作系统上运行一个程序时,就好像程序是系统中当前运行的唯一的程序,好像是在独占处理器和内存。
6.2 简述壳Shell-bash的作用与处理流程
处理流程:shell执行一系列的读/求值步骤,然后终止。读步骤读取来自用户的一个命令行;求值步骤解析命令行,并代表用户运行程序。
6.3 Hello的fork进程创建过程
6.4 Hello的execve过程
execve函数在当前进程的上下文中加载并运行一个新的程序。它会覆盖当前进程的地址空间。但并没有创建一个新的进程。新的程序仍然有相同的PID,并且继承了调用execve函数时已打开的所有文件描述符。
6.5 Hello的进程执行
多个流并发地执行的现象被称为并发。一个进程执行它的控制流的一部分的每一时间段叫时间片。一个进程和其他进程轮流运行被称为时间分片,也叫多任务。
在内核执行的某些时刻,内核可以决定抢占当前进程,并通过上下文切换重新开始一个先前被抢占的进程,这就是调度。
处理器用某个控制寄存器中的一个模式位来描述进程当前享有享有的权限。设置了模式位时,进程处在内核模式,否则处在用户模式。
6.6 hello的异常与信号处理
不停乱按(包括回车):在乱按过程中,shell会把输入的字符存到缓冲区。当hello运行结束后,shell会把hello执行时用户输入的字符当作命令(以回车为间隔,每个回车一个命令)。
ctrl+c:内核会发送一个SIGINT信号给前台进程组的每个进程,用ps命令发现hello进程已被回收。
ctrl+z:内核发送一个SIGSTOP信号给前台进程组的每个进程,将hello进程挂起,
6.7本章小结
本章介绍了进程的概念和作用,简述了shell的作用和处理流程,介绍了hello的进程执行,hello的异常与信号处理。
第7章 hello的存储管理
7.1 hello的存储器地址空间
线性地址:是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
20位线性地址是这样计算的,08F1: 0100 = 08F1H*10H + 0100H = 09010H。
7.3 Hello的线性地址到物理地址的变换-页式管理
7.4 TLB与四级页表支持下的VA到PA的变换
MMU中有一个关于PTE的小的缓存,称为翻译后备缓冲器(TLB),通过这个缓存,MMU可以加速对PTE的查询,从而加速地址翻译。
7.5 三级Cache支持下的物理内存访问
7.6 hello进程fork时的内存映射
7.7 hello进程execve时的内存映射
execve函数在当前进程中加载并运行包含在可执行目标文件hello中的程序,用hello程序有效地替代了当前程序。加载并运行hello需要以下几个步骤:
1. 删除hello进程虚拟地址空间的用户部分中的已存在的vm_area_struct;
2. 为新程序的代码、数据、bss、和栈区域创建新的vm_area_struct,其中bss区域、栈和堆是请求二进制零的;
3. 当hello程序与共享目标链接时,这些共享目标动态链接到hello程序,然后再映射到用户虚拟地址空间中的共享区域中;
内核下一次调度hello进程时,hello程序会从入口点开始执行。内核将根据需要换入代码和数据页面。
7.8 缺页故障与缺页中断处理
DRAM缓存不命中称为缺页。在下图的时刻,CPU引用了VP3中的一个字,VP3并未缓存在DRAM中。地址翻译硬件从内存中读取PTE3,从有效位推断出VP3未被缓存,并且触发了一个缺页异常。
当异常处理程序返回时,内核会重新执行导致缺页的指令。现在页命中。
7.9动态存储分配管理
显示分配器要求应用显示地释放任何已分配的块。C标准库提供的malloc程序包就是显示分配器。
隐式分配器会检测到一个已分配块不再被程序所使用,就会释放这个块。隐式分配器也叫垃圾收集器,自动释放未使用的已分配块的过程叫垃圾收集。
空闲链表中块的排序策略决定了释放一个块的时间是线性的还是常数。
可以用后进先出(LIFO)的顺序维护链表,将新释放的块放在链表的开始处。使用LIFO的顺序和首次适配的放置策略,分配器会最先检查最近使用过的块。使用这种策略,释放一个块可以在常数时间内完成。
还可以按照地址顺序来维护链表,即链表中每个块的地址都小于它后继的地址。使用这种策略,释放一个块需要线性时间的搜索来定位合适的前驱。使用按照地址排序的首次适配比LIFO排序的首次适配有更高的内存利用率。
7.10本章小结
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
一个Linux文件就是一个m个字节序列,所有的I/O设备都被模型化为文件,所有的输入和输出都被当作相应文件的读和写来执行。
普通文件包含任意数据。文本文件是只含有ASCII和Unicode字符的普通文件;二进制文件是所有其他的文件。
目录是包含一组链接的文件。其中每个链接都将一个文件名映射到一个文件。
Linux内核将所有文件都组织成一个目录层次结构(directory hierarchy)。
将设备映射为文件的方式,允许Linux内核引出一个简单低级的Unix I/O应用接口,这使得所有的输入和输出都能以一种统一的方式来执行。
8.2 简述Unix IO接口及其函数
进程通过调用open函数来打开一个已存在的文件或创建一个新文件。
int open(char *filename, int flags, mode_t mode)
将filename转换为一个文件描述符(是一个小的非负整数),并返回这个描述符数字。返回的描述符总是在该进程中当前没有打开的最小描述符。
ssize_t read(int fd, void *buf, size_t n)
从描述符为fd的当前文件位置复制最多n个字节到内存位置buf。返回-1表示错误,返回0表示当前文件位置是EOF,否则返回实际传送的字节数量。
size_t被定义为unsigned long,ssize_t被定义为long。
ssize_t write(int fd, const void *buf, size_t n)
从内存位置bug复制至多n个字节到描述符fd的当前文件位置。
8.3 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;}
typedef char *va_list说明它是一个字符指针
(char*)(&fmt) + 4) 表示的是...中的第一个参数的地址
int vsprintf(char *buf, const char *fmt, va_list args){char* p;char tmp[256];va_list p_next_arg = args;for (p = buf; *fmt; fmt++) {if (*fmt != '%') {*p++ = *fmt;continue;}fmt++;switch (*fmt) {case 'x':itoa(tmp, *((int*)p_next_arg));strcpy(p, tmp);p_next_arg += 4;p += strlen(tmp);break;case 's':break;default:break;}}return (p - buf);}
返回值是字符串的长度,在vsprintf后write把buf中的i个元素的值输出。
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
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.c经预处理、编译、汇编、链接变为可执行目标文件hello。
- 在bash内,执行 ./hello <学号> <姓名> 命令后,shell先使用fork创建子进程,然后用execve在子进程的上下文中加载并运行可执行目标文件hello。
- hello进程终止后,shell会回收它。
计算机系统是一个复杂系统,硬件与软件的深入协作是任何一个程序能运行的基础。计算机系统是复杂的,同时也是精巧的。计算机系统的每一部分都经历了从基础理论开始的漫长研发过程,凝聚着许多人的心血与智慧。
附件
hello_dump_dr.txt 对hello用objdump -dr输出重定位得到的
helloo_dump_dr.txt 对hello.o用objdump -dr输出重定位得到的
参考文献
[2] https://baike.baidu.com/item/预处理/7833652?fr=aladdin
[3] https://baike.baidu.com/item/编译/1258343?fr=aladdin
[4] https://blog.csdn.net/u012927281/article/details/51649477
[5] https://baike.baidu.com/item/逻辑地址/3283849?fr=aladdin
[6] https://baike.baidu.com/item/线性地址/9013682?fr=aladdin
[7] https://baike.baidu.com/item/虚拟地址/1329947?fr=aladdin
[8] https://www.cnblogs.com/pianist/p/3315801.html
[9] https://baike.baidu.com/item/getchar/919709?fr=aladdin
HIT计算机系统 大作业 程序人生相关推荐
- HIT计算机系统大作业-程序人生-Hello’s P2P
计算机系统大作业 ** 由于采用静态部署,需要看图片详细分析的小伙伴请移步个人博客网站:** 个人博客 题目:程序人生-Hello's P2P 学号: 姓名:熊峰 摘要: hello程序作为最简单的. ...
- HIT 计算机系统 大作业 程序人生-Hello’s P2P
计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算机类 学 号 1180300223 班 级 1803002 计算机科学与技术学院 2019年12月 摘 要 本文主要介绍了一个 ...
- HIT计算机系统大作业程序人生
目 录 第1章 概述 - 4 - 1.1 HELLO简介 - 4 - 1.2 环境与工具 - 4 - 1.3 中间结果 - 4 - 1.4 本章小结 - 4 - 第2章 预处理 - 5 - 2.1 预 ...
- HIT 深入理解计算机系统 大作业 程序人生-Hello’s P2P
HIT 深入理解计算机系统 大作业 程序人生-Hello's P2P 本论文旨在研究 hello 在 linux 系统下的整个生命周期.结合 CSAPP 课本, 通过 gcc 等工具进行实验,从而将课 ...
- 【2022】哈工大计算机系统大作业——程序人生Hello’s P2P
2022哈工大计算机系统大作业--程序人生Hello's P2P 摘要 第1章 概述 1.1 Hello简介 1.2 环境与工具 1.3 中间结果 1.4 本章小结 第2章 预处理 2.1 预处理的概 ...
- 2022计算机系统大作业——程序人生-Hello’s P2P
计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算机 学 号 120L021716 班 级 2003005 学 生 蔡泽栋 指 导 ...
- 哈工大2022计算机系统大作业---程序人生
计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算机类 学 号 120L021923 班 级 2003006 学 生 甄自镜 指 导 ...
- 哈工大计算机系统大作业——程序人生-Hello’s P2P
计算机系统 大作业 题 目程序人生-Hello's P2P 专 业 计算机科学与技术 学 号120L022401 班 级 200300 ...
- 哈工大2022年春季学期计算机系统大作业——程序人生
计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 人工智能(未来技术) 学 号 7203610716 班 级 20WJ102 学 生 孙铭蔚 ...
最新文章
- vue ajax highcharts,在vue项目中引入highcharts图表的方法(详解)
- 平面设计师如何掌握色彩心理学(实用技巧)
- 安卓application_阿里面试官刁钻连问:安卓 UID的分配、查看及相关知识
- struts1.x心得1--struts入门介绍
- Python学习笔记_读Excel去重
- MaxCompute作业日常监控与运维实践
- ASP.NET操作Word的IIS权限设置
- java 屏幕键盘io
- 有哪些朋友圈励志说说短句?
- 日期对象Date的计算
- ldap 统一认证 java_LDAP统一用户认证
- 数据分析师,数据挖掘工程师和数据研发工程师有什么区别?
- 行人重识别实验笔记3-JDAI fast-reid项目配置
- android app应用签名生成工具,Android APK生成证书并签名方法
- [Maven实战-许晓斌]-[第二章]-2.7-2.8 Mave安装的最优建议和安装小结
- 超级实用的分时图指标 有了本分时图你根本不用看K线了
- buu crypto 变异凯撒
- 计算机word知识试题及答案,计算机二级考试word试题及答案
- 2组语法,1个函数,教你学会用Python做数据分析!
- 温度传感器的c语言程序,DS18B20数字温度传感器C语言程序实例