深入理解计算机系统

计算机系统

大作业

题     目  程序人生-Hellos P2P  

专       业   计算学部               

学     号                

班   级                   

学       生                

指 导 教 师   史先俊               

计算机科学与技术学院

2021年5月

摘  要

本论文系统的描述了hello.c的一生,程序员输入hello.c的源代码,预处理器将其处理成hello.i,编译器将其处理成hello.s,汇编器将其处理成hello.o,最后链接生成可执行文件hello。然后shell输入./hello,hello开始执行,fork开辟进程,execve加载进程(P2P)。然后程序由cpu控制,加载、运行此程序代码,完成后由父进程回收,完成它的一生(O2O)。

关键词:计算机系统、P2P、O2O、hello程序;                            

(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分

目  录

第1章 概述 - 4 -

1.1 Hello简介 - 4 -

1.2 环境与工具 - 4 -

1.3 中间结果 - 4 -

1.4 本章小结 - 4 -

第2章 预处理 - 5 -

2.1 预处理的概念与作用 - 5 -

2.2在Ubuntu下预处理的命令 - 5 -

2.3 Hello的预处理结果解析 - 5 -

2.4 本章小结 - 5 -

第3章 编译 - 6 -

3.1 编译的概念与作用 - 6 -

3.2 在Ubuntu下编译的命令 - 6 -

3.3 Hello的编译结果解析 - 6 -

3.4 本章小结 - 6 -

第4章 汇编 - 7 -

4.1 汇编的概念与作用 - 7 -

4.2 在Ubuntu下汇编的命令 - 7 -

4.3 可重定位目标elf格式 - 7 -

4.4 Hello.o的结果解析 - 7 -

4.5 本章小结 - 7 -

第5章 链接 - 8 -

5.1 链接的概念与作用 - 8 -

5.2 在Ubuntu下链接的命令 - 8 -

5.3 可执行目标文件hello的格式 - 8 -

5.4 hello的虚拟地址空间 - 8 -

5.5 链接的重定位过程分析 - 8 -

5.6 hello的执行流程 - 8 -

5.7 Hello的动态链接分析 - 8 -

5.8 本章小结 - 9 -

第6章 hello进程管理 - 10 -

6.1 进程的概念与作用 - 10 -

6.2 简述壳Shell-bash的作用与处理流程 - 10 -

6.3 Hello的fork进程创建过程 - 10 -

6.4 Hello的execve过程 - 10 -

6.5 Hello的进程执行 - 10 -

6.6 hello的异常与信号处理 - 10 -

6.7本章小结 - 10 -

第7章 hello的存储管理 - 11 -

7.1 hello的存储器地址空间 - 11 -

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 -

7.9动态存储分配管理 - 11 -

7.10本章小结 - 12 -

第8章 hello的IO管理 - 13 -

8.1 Linux的IO设备管理方法 - 13 -

8.2 简述Unix IO接口及其函数 - 13 -

8.3 printf的实现分析 - 13 -

8.4 getchar的实现分析 - 13 -

8.5本章小结 - 13 -

结论 - 14 -

附件 - 15 -

参考文献 - 16 -


第1章 概述

1.1 Hello简介

P2P:程序员输入hello.c的源代码,预处理器将其处理成hello.i,编译器将其处理成hello.s,汇编器将其处理成hello.o,最后链接生成可执行文件hello。然后shell输入./hello,hello开始执行,fork开辟进程,execve加载进程。

020:操作系统映射虚拟内存,为hello创建新的结构,运行此程序代码,载入物理内存,完成后由父进程回收,内核删除相关结构。

1.2 环境与工具

(1)硬件环境:X64 CPU;2GHz;4GRAM;256Disk
(2)软件环境:Windows10 64位;Vmware 10;Ubuntu 16.04 LTS 64位
(3)使用工具:Codeblocks;Objdump;Gdb;Hexedit

1.3 中间结果

hello.c:源代码

hello.i:预处理后的文本文件

hello.s:编译之后的汇编文件

hello.o:汇编之后的可重定位目标执行文件

hello:链接之后的可执行文件

hello.elf:hello.o的ELF格式

Hello1.elf:hello的ELF格式

Hello1.txt:hello.o反汇编代码

Hello2.txt:hello的反汇编代码

1.4 本章小结

介绍了P2P,020,环境与工具以及为完成论文生成的各种文件

第2章 预处理

2.1 预处理的概念与作用

预处理,就是程序源文件被编译之前根据预处理指令对程序源文件进行处理。

预处理器根据#开头的命令,修改原始c程序,命令高速预处理器读取系统文件的内容,并把它直接插入程序文本中。

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

如图,可以看到,三个头文件被放入文本当中,而其他变化不大。

2.4 本章小结

本章讲述了.c文件到.i文件的过程,分析了.i文件

(第2章0.5分)


第3章 编译

3.1 编译的概念与作用

以下格式自行编排,编辑时删除

编译:是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序的过程

作用:转化为较低级的汇编语言以便于后面更进一步转化为机器语言

        

3.2 在Ubuntu下编译的命令

得到类似的代码如图

3.3 Hello的编译结果解析

对比源代码有

3.31变量,对其赋值和算术操作

如图,可以看到,在main里,%rsp-32,开辟了空间,而接下来的两句话就分别是argc和argv[],而其实此时i也在前面分配了内存,但此时未被赋值。

转到L2,此时为i(-4(%rbp))赋值为0,

转到L4,对i进行算术操作

可以看到最后一句,有addl操作,实现了i++的操作。

3.32关系和控制转移

常见的控制转移指令,

常见的关系有cmp和test,

Cmp类似于sub,而test类似于and,而相应的,二元操作符也会改变条件的值。

在此代码中,首先出现的是main中判断argc和4的关系,即源码中的第一个if,相等就继续运行,跳转到L2处

L2处的jmp L3意为直接跳转到L3

而L3处,最后的ret意为从调用中返回。

3.33各类运算符

  1. 加: x=x+y汇编语言是addq y,x
    (2)减: x=x-y 汇编语言是subq y,x
    (3)乘: x=x*y 汇编语言是imulq y,x
    (4)除: z=x/y 汇编语言是
    movq x, z
    cqto
    idivq y

左移:sal (shl)

右移:sar (shr)

Leaq a b意为b=&(a),

此类计算构成了该汇编语言的大部分,如图

3.34数组、指针、结构体的操作

数组:首地址上加上偏移量来处理。

指针:指针通常用寄存器来储存,如果要寻址,就在寄存器外面加括号,例如(%rax)。

结构体:通过内部的偏移量来处理。

在这个汇编语言中,有数组通过类似于 -32(%rbp)的方式来处理

3.35函数的调用与返回

函数调用用call,返回用ret,向函数传递参数需要预先传入寄存器中。

此汇编语言中,

第一个printf就是call puts@PLT,把.L0段的立即值传入%rdi,然后call跳转到puts。这里的exit是把立即数1传入到%edi中,然后call跳转到exit。

第二个printf有三个参数,%eax,%rdi,%rsi,然后跳转到printf
sleep有一个参数传到%edi中,之后call跳转到 sleep中。

getchar没有参数

返回值:如果有返回值,要先把返回值存到%rax中,再用ret返回。源程序中有主函数的return 0;就是先把返回值立即数0存到%eax中,再用ret返回。

3.4 本章小结

分析了编译过程,逐步分析了C语言的数据与操作在汇编语言的中对应语句。

(第3章2分)


第4章 汇编

4.1 汇编的概念与作用

汇编器将hello.s生成机器语言指令,保存在hello.o中

作用:生成机器指令,便于机器识别并执行

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

输入指令readelf -a hello.o > hello.elf生成.elf文件

格式如图

夹在ELF头和节头部表之间的都是节。一个典型的ELF可重定位目标文件包含下面几个节:

.text:已编译程序的机器代码。

.rodata:只读数据,比如printf语句中的格式串和开关(switch)语句的跳转表。

.data:已初始化的全局C变量。局部C变量在运行时被保存在栈中,既不出现在.data中,也不出现在.bss节中。

.bss:未初始化的全局C变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分初始化和未初始化变量是为了空间效率在:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。

.symtab:一个符号表(symbol table),它存放在程序中被定义和引用的函数和全局变量的信息。一些程序员错误地认为必须通过-g选项来编译一个程序,得到符号表信息。实际上,每个可重定位目标文件在.symtab中都有一张符号表。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的表目。

.rel.text:当链接噐把这个目标文件和其他文件结合时,.text节中的许多位置都需要修改。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面调用本地函数的指令则不需要修改。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非使用者显式地指示链接器包含这些信息。

.rel.data:被模块定义或引用的任何全局变量的信息。一般而言,任何已初始化全局变量的初始值是全局变量或者外部定义函数的地址都需要被修改。

.debug:一个调试符号表,其有些表目是程序中定义的局部变量和类型定义,有些表目是程序中定义和引用的全局变量,有些是原始的C源文件。只有以-g选项调用编译驱动程序时,才会得到这张表。

.line:原始C源程序中的行号和.text节中机器指令之间的映射。只有以-g选项调用编译驱动程序时,才会得到这张表。

.strtab:一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串序列。

  1. ELF头:ELF头(ELF header)以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含了帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如x86-64)、节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量。不同节的位置和大小是有节头部表描述的,其中目标文件中每个节都有一个固定大小的条目(entry)

  1. 节头:记录各节名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。

(3)重定位节:

.rela.text,保存的是.text节中需要被修正的信息;任何调用外部函数或者引用全局变量的指令都需要被修正;调用外部函数的指令需要重定位;引用全局变量的指令需要重定位; 调用局部函数的指令不需要重定位;在可执行目标文件中不存在重定位信息。本程序需要被重定位的是printf、puts、exit、sleepsecs、getchar、sleep和.rodata中的.L0和.L1。

.rela.eh_frame节是.eh_frame节重定位信息。

  1. 符号表:.symtab,一个符号表,它存放在程序中定义和引用的函数和全局变量的信息,一些程序员错误地认为必须通过-g选项来编译一个程序,才能得到符号表信息。实际上每个可重定位目标文件在.symtab中都有一张符号表(除非程序员特意用STRIP命令去掉它)。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的条目。

4.4 Hello.o的结果解析

以下格式自行编排,编辑时删除

objdump -d -r hello.o  分析hello.o的反汇编。

得到的结果如下图

对比发现:

1、分支转移:.s文件中分支是直接跳转到某处如图(有利于读)

而反汇编的文件,则是跳转到main的某个偏置处

2、函数调用:.s文件直接call某个函数

而反汇编文件,call main的偏置

  1. 访问全局变量:

.s文件使用.LC0(%rip)

反汇编代码中为0x0(%rip),因为访问时需要重定位,所以初始化为0并添加重定位条目。

4.5 本章小结

分析了汇编的过程,分析了ELF文件和格式,比较了重定位前汇编和重定位后的反汇编的不同

(第4章1分)


5链接

5.1 链接的概念与作用

(链接(linking)是将各种代码和数据片段收集并合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行

链接器在软件开发过程中扮演着一个关键的角色,因为它们使得分离编译(separate compilation)成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。

5.2 在Ubuntu下链接的命令

执行ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o命令

5.3 可执行目标文件hello的格式

分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。

ELF头,节头数量变成了27个

节头:

程序头:

5.4 hello的虚拟地址空间

使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。   

PHDR:保存程序头表

INTERP:动态链接器的路径

LOAD:可加载的程序段

DYNAMIC:保存了由动态链接器使用的信息

NOTE保存辅助信息

GNU_STACK:标志栈是否可执行

GNU_RELRO:指定重定位后需被设置成只读的内存区域

对应于edb的页面

可以看出Load从400000从开始到403e50保存着程序头,段头部表,init,text,.radata的内容。

Init起始于400010,也就是edb截图开始的地方,

.radata开始于402000,保存着只读数据

DYNAMIC开始于403e50

PHDR开始于400040

INTERP开始于400270

.text开始于401080

5.5 链接的重定位过程分析

objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。

hello比hello.o多出的节头表。

.interp:保存ld.so的路径

.note.ABI-tag

.note.gnu.build-i:编译信息表

.gnu.hash:gnu的扩展符号hash表

.dynsym:动态符号表

.dynstr:动态符号表中的符号名称

.gnu.version:符号版本

.gnu.version_r:符号引用版本

.rela.dyn:动态重定位表

.rela.plt:.plt节的重定位条目

.init:程序初始化

.plt:动态链接表

.fini:程序终止时需要的执行的指令

.eh_frame:程序执行错误时的指令

.dynamic:存放被ld.so使用的动态链接信息

.got:存放程序中变量全局偏移量

.got.plt:存放程序中函数的全局偏移量

.data:初始化过的全局变量或者声明过的函数

库函数的代码都已经链接到了程序中,程序各个节变的更加完整,跳转的地址也具有参考性。

以截图为例计算重定位

  1. offset = 0x21
  2. addend = -0x4

查询得,ADDR(S) = 0X401125,ADDR(r.symbol) = 0x401090

*refptr = 401090 - 401125 -21 - 4 = -0xba = 0xffffff46

汇编语言改为 e846ffffff,如下截图,

结合hello.o的重定位项目,分析hello中对其怎么重定位的。

5.6 hello的执行流程

(1) 载入:_dl_start、_dl_init

(2)开始执行:_start、_libc_start_main

(3)执行main:_main、_printf、_exit、_sleep、

_getchar、_dl_runtime_resolve_xsave、_dl_fixup、_dl_lookup_symbol_x

  1. 退出:exit

5.7 Hello的动态链接分析

  

得到PLTGOT的地址是0X404000

所以参看地址,

此时为空,调用dl_init

所以GOT[1] = 7f1928983190

GOT[2] = 7f192896cdb0

查看对应地址

GOT[1]指向重定位表

GOT[2]:GOT指向动态链接器

5.8 本章小结

概括了链接的概念和作用,分析了.elf文件,重点分析了hello程序的虚拟地址空间、重定位、动态链接和执行过程


6hello进程管理

6.1 进程的概念与作用

定义:进程是正在运行的程序的实例

作用:系统中每个程序的都在某个进程的上下文中,进程提供一个假象,我们的程序中的数据和代码好像就是系统内存中唯一的对象

6.2 简述壳Shell-bash的作用与处理流程

作用:提供了一个界面,用户可以通过这界面访问操作系统内核。

处理流程:

(1)读取输入

(2)处理输入内容,获得输入参数

(3)如果是内置命令则直接执行,否则调用程序执行

(4)程序运行时,shell监视用户输入并响应

6.3 Hello的fork进程创建过程

1、在终端中输入命令行,shell判断是否为内置命令,不是就进行下一步

2、父进程通过调用fork函数来创建一个新的运行的子进程

3、新创建的子进程几乎和父进程相同,子进程得到与父进程用户级虚拟地址空间相同的(独立)一份副本,包括代码、数据段、堆、共享库以及用户栈,它们的最大区别是它们有不同的PID。

4、子进程会返回两次,父进程中返回PID,子进程中返回0

6.4 Hello的execve过程

1. 陷入内核

2. 加载新的可执行文件并进行可执行性检查

3. 将新的可执行文件映射到当前运行进程的进程空间中,并覆盖原来的进程数据

4. 将EIP的值设置为新的可执行程序的入口地址。如果可执行程序是静态链接的程序,或不需要其他的动态链接库,则新的入口地址就是新的可执行文件的main函数地址;如果可执行程序还需要其他的动态链接库,则入口地址是加载器ld的入口地址

5. 返回用户态,程序从新的EIP出开始继续往下执行。至此,老进程的上下文已经被新的进程完全替代了,但是进程的PID还是原来的。从这个角度来看,新的运行进程中已经找不到原来的对execve调用的代码了,所以execve函数的一个特别之处是他从来不会成功返回,而总是实现了一次完全的变身。

6.5 Hello的进程执行

逻辑控制流:如果想用调试器单步执行程序,我们会看到一系列的程序计数器(PC)的值,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或是包含在运行时动态链接到程序的共享对象中的指令。这个PC值的序列叫做逻辑控制流,或者简称为逻辑流。

上下文切换:如果系统调用因为等待某个事件发生而阻塞,那么内核可以让当前进程休眠,切换到另一个进程,上下文就是内核重新启动一个被抢占的进程所需要的状态,是一种比较高层次的异常控制流。。

时间片:一个进程执行它的控制流的一部分的每一时间段叫做时间片。

内核模式与用户模式:开始Hello运行在用户模式,收到信号后进入内核模式,运行信号处理程序,之后再返回用户模式。运行过程中,cpu不断切换上下文,使运行过程被切分成时间片,与其他进程交替占用cpu,实现进程的调度。

6.6 hello的异常与信号处理

以下格式自行编排,编辑时删除

 hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。

 程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

执行过程可能出现的异常一共有四种:中断、陷阱、故障、终止。

中断:来自I/O设备的信号,异步发生,总是返回到下一条指令。

陷阱:有意的异常,同步发生,总是返回到下一条指令。

故障:潜在可恢复的错误,同步发生,可能返回到当前指令或终止。

终止:不可恢复的错误,同步发生,不会返回。

  1. 正常运行

  1. ctrl+z暂停,输入ps看到hello没有被回收

  1. ctrl+c终止,输入jobs指令,发现hello不在列表里

  1. 在输出过程中乱按,无关输入被缓存到stdin,输出到结果

6.7本章小结

本章分析了进程的概念,shell-bash的概念和作用,分析了fork和execve进程的具体过程,hello的执行过程和异常信号处理

7hello的存储管理

7.1 hello的存储器地址空间

以下格式自行编排,编辑时删除

结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。

逻辑地址:程序代码经过编译后出现在汇编程序中的地址,逻辑地址由选择符和偏移量组成。

线性地址:逻辑地址向物理地址转化过程中的一步,逻辑地址经过段机制后转化为线性地址。

虚拟地址:保护模式下程序访问存储器所用的逻辑地址

物理地址:CPU通过地址总线的寻址,找到真实的物理内存对应的地址。

7.2 Intel逻辑地址到线性地址的变换-段式管理

首先,给定一个完整的逻辑地址[段选择符:段内偏移地址],

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位。

7.4 TLB与四级页表支持下的VA到PA的变换

Core i7采用四级页表的层次结构。CPU产生VA,VA传送给MMU,MMU使用VPN高位作为TLBT和TLBI向TLB中寻找匹配。如果命中,则得到PA。如果TLB中没有命中,MMU查询页表,CR3确定第一级页表的起始地址,VPN1确定在第一级页表中的偏移量,查询出PTE,以此类推,最终在第四级页表中找到PPN,与VPO组合成PA,添加到PLT。

7.5 三级Cache支持下的物理内存访问

得到物理地址之后,先将物理地址拆分成CT(标记)+CI(索引)+CO(偏移量),然后在一级cache内部找,如果未能寻找到标记位为有效的字节(miss)的话就去二级和三级cache中寻找对应的字节,找到之后返回结果。

7.6 hello进程fork时的内存映射

在shell输入命令行后,内核调用fork创建子进程,为hello程序的运行创建上下文,并分配一个与父进程不同的PID。通过fork创建的子进程拥有父进程相同的区域结构、页表等的一份副本,同时子进程也可以访问任何父进程已经打开的文件。当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同,当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间。

7.7 hello进程execve时的内存映射

7.8 缺页故障与缺页中断处理

缺页处理程序从磁盘加载适当的页面,然后将控制返回给引起故障的指令。当指令再次执行时,相应的物理页面已经驻留在内存中了。

7.9动态存储分配管理

隐式:

显示:

分割空闲块:

合并空闲块:

7.10本章小结

   本章分析了在计算机中的虚拟内存管理,虚拟地址、物理地址、线性地址、逻辑地址的区别以及它们之间的变换模式,以及段式、页式的管理模式,在了解了内存映射的基础上重新认识了共享对象、fork和execve,同时简述了动态内存分配的方法与原理。
8hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件

普通文件(包含任意数据的文件)、目录(文件夹,包含一组链接的文件,每个链接都将一个文件名映射到一个文件)、套接字(用来与另一个进程进行跨网络通信的文件)、命名通道、符号链接以及字符和块设备。

设备管理:unix io接口

操作:打开和关闭文件、读取和写入文件以及改变当前文件的位置。

8.2 简述Unix IO接口及其函数

(1)打开文件:open(),打开一个已经存在的文件或者创建一个新文件。

(2)关闭文件:close(),关闭一个打开的文件。

(3)读取文件:read(),从当前文件位置复制字节到内存。

(4)写入文件:write(),从内存复制字节到当前文件位置。

(5)改变文件位置:lseek()

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;
    }

我们来看看vsprintf的函数

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);

   }

write(buf, i):

write,顾名思义:写操作,把buf中的i个元素的值写到终端。

vsprintf的作用就是格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。

字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。

显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

异步异常-键盘中断的处理:当用户按键时,键盘接口会得到一个代表该按键的键盘扫描码,同时产生一个中断请求,中断请求抢占当前进程运行键盘中断子程序,键盘中断子程序先从键盘接口取得该按键的扫描码,然后将该按键扫描码转换成ASCII码,保存到系统的键盘缓冲区之中。getchar函数落实到底层调用了系统函数read,通过系统调用read读取存储在键盘缓冲区中的ASCII码直到读到回车符然后返回整个字串,getchar进行封装,大体逻辑是读取字符串的第一个字符然后返回。

8.5本章小结

概述了一下linux的IO设备管理方法,列出开、关、读、写、转移文件的接口及相关函数,简要分析了printf和getchar函数的实现。

结论

Hello程序的生命周期是从一个高级c语言程序开始的

我们自己输入源代码,使hello开始生命

经过预处理阶段,

编译阶段,

汇编阶段,

链接阶段。程序不断靠近“出生的阶段”,婉如一个孕育的婴儿

最终程序出生了,

在壳(Bash)里,进程管理为程序fork(Process),为它execve,为它mmap,分它时间片,让它得以在Hardware(CPU/RAM/IO)上驰骋(取指译码执行/流水线等)

绽放出自己生命的灿烂。

    程序也有自己的权利:动态申请内存:printf会调用malloc向动态内存分配器申请堆中的内存。

也会对外界信号做出反应:信号:如果运行途中键入ctr-c ctr-z则调用shell的信号处理函数分别停止、挂起。

最后,它会走完自己的一生。结束:shell父进程回收子进程,内核删除为这个进程创建的所有数据结构。

感悟:写大作业的时间有些微妙,我觉得这就像我的知识一样不断的补充完善。Hello,一个最简单的程序竟然要经过如此复杂的过程(其实所有程序的过程应该是相似的)才能变成我们所期望的样子。正是前辈几十年的努力才造就了这一切。


附件

hello.c:源代码

hello.i:预处理后的文本文件

hello.s:编译之后的汇编文件

hello.o:汇编之后的可重定位目标执行文件

hello:链接之后的可执行文件

hello.elf:hello.o的ELF格式

Hello1.elf:hello的ELF格式

Hello1.txt:hello.o反汇编代码

Hello2.txt: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.

(参考文献0分,缺失 -1分)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值