计算机系统

大作业

题     目  程序人生-Hello’s P2P  

专       业   计算机科学与技术     

学     号      2022111789        

班   级       2203102          

学       生       王冠翀           

指 导 教 师        史先俊             

计算机科学与技术学院

2023年4月

摘  要

本文借助hello程序的运行,介绍了Linux在x86-64系统下一个程序的完整的运行过程。分析了程序的预处理、编译、汇编、链接的过程,以及进程管理、存储管理和IO管理

关键词:计算机系统,Linux,预处理,编译,汇编,链接,进程,存储,IO                            

目  录

第1章 概述

1.1 Hello简介

1.2 环境与工具

1.3 中间结果

1.4 本章小结

第2章 预处理

2.1 预处理的概念与作用

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

2.4 本章小结

第3章 编译

3.1 编译的概念与作用

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

3.4 本章小结

第4章 汇编

4.1 汇编的概念与作用

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

4.4 Hello.o的结果解析

4.5 本章小结

第5章 链接

5.1 链接的概念与作用

5.2 在Ubuntu下链接的命令

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

5.4 hello的虚拟地址空间

5.5 链接的重定位过程分析

5.6 hello的执行流程

5.7 Hello的动态链接分析

5.8 本章小结

第6章 hello进程管理

6.1 进程的概念与作用

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

6.3 Hello的fork进程创建过程

6.4 Hello的execve过程

6.5 Hello的进程执行

6.6 hello的异常与信号处理

6.7本章小结

第7章 hello的存储管理

7.1 hello的存储器地址空间

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

7.3 Hello的线性地址到物理地址的变换-页式管理

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

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

7.6 hello进程fork时的内存映射

7.7 hello进程execve时的内存映射

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

7.9动态存储分配管理

7.10本章小结

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

8.2 简述Unix IO接口及其函数

8.3 printf的实现分析

8.4 getchar的实现分析

8.5本章小结

结论

附件

参考文献

第1章 概述

1.1 Hello简介

Hello的P2P:程序Hello.c经过预处理(生成Hello.i),编译(生成Hello.s),汇编(生成Hello.o),链接(生成Hello)后生成可执行目标文件,Hello也就从program转化成了process。

Hello的O2O:这是一个从无到有和从有到无的过程。从无到有:Hello程序运行时,内核在虚拟内存中加载Hello的代码和相关数据。从有到无:Hello程序运行完后,父进程回收Hello并释放数据。

1.2 环境与工具

硬件环境:X64 CPU;2.30GHz;16G RAM;256GHD Disk 以上

软件环境:Windows11 64位

开发与调试工具:VMware 17;Ubuntu 20.04 LTS 64位;Codeblocks 64位;vi/vim/gedit+gcc

1.3 中间结果

文件 作用 hello.c hello程序的源代码 hello.i hello程序预处理后产生的代码 hello.s hello程序编译产生的汇编代码 hello.o hello程序汇编产生的可重定位目标文件 hello 经过链接后的可执行文件 dump_txt hello.o的反汇编文件 dump2_txt hello的反汇编文件

1.4 本章小结

本章简单介绍了一下Hello程序是如何运行的,并说明了Hello的P2P和O2O以及该过程的中间结果。

第2章 预处理

2.1 预处理的概念与作用

在C语言编程中,.c文件是源文件,包含了程序员编写的代码。预处理器在编译之前对源文件进行处理,这个过程称为预处理。预处理器执行一些特定的规则,如宏替换、文件包含等,以生成.i文件,即预处理后的文件。

预处理的概念与作用:

(1)宏替换:预处理器将代码中的宏定义替换为相应的值。这可以简化代码,便于维护。

(2)文件包含:预处理器可以将其他文件的内容导入到当前文件中。这可以方便地组织代码,使代码更易于管理和维护。

(3)条件编译:预处理器可以根据特定的条件决定编译哪些代码。这可以使程序在不同的环境下表现出不同的行为,例如在不同操作系统间的移植。

(4)注释处理:预处理器可以删除代码中的注释,以便编译器专注于实际的代码。

总之,预处理有助于提高代码的可读性、可维护性和可移植性。通过使用预处理器,程序员可以更有效地组织代码,提高编程效率。

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

我们发现,相比于hello.c,hello.o增加了大量的代码,但hello.i的主函数部分与hello.c的一致,是因为hello.c的主函数中没有使用宏定义。

2.4 本章小结

本章介绍了预处理的概念与作用,以及实践了在ubuntu下hello.c的预处理操作并对其结果做了一定解析。

 

第3章 编译

3.1 编译的概念与作用

在C语言编程中,从.i文件到.s文件的过程是指编译器将预处理后的.i文件转换为汇编语言文件.s的过程。这个过程称为编译。编译的概念与作用如下:

(1)语法分析:编译器首先对.i文件中的源代码进行语法分析,以确保代码遵循C语言的语法规则。

(2)语义分析:编译器检查代码是否有语义错误,例如变量类型不匹配、函数调用参数个数错误等。

(3)中间代码生成:编译器将经过语法和语义分析的源代码转换为中间代码。中间代码通常与目标机器和操作系统无关,因此可以轻松地在不同的平台上进行移植。

(4)优化:编译器对中间代码进行优化,以提高程序的执行效率。优化过程可能包括代码重排、删除冗余代码、循环展开等。

(5)汇编代码生成:编译器将优化后的中间代码转换为汇编代码。汇编代码是机器语言的助记符,它比机器语言更容易阅读和理解。

通过编译过程,C语言源代码被转换为汇编语言文件.s。这个过程有助于确保代码遵循语法和语义规则,提高程序执行效率,并为后续的链接和执行做好准备。

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

3.3.1伪指令

第一行声明源文件,第二行指示代码段,第三行指示rodata节等

3.3.2 rodata节

LC0存储的是第一条printf打印的内容:"用法: Hello 学号 姓名 秒数!\n",LC1存储的是第二条printf打印的内容。

3.3.3赋值

mov操作分为movb(1字节),movw(2字节),movl(4字节),movq(8字节),mov的源操作数可以是立即数,寄存器和内存,目的操作数可以是寄存器和内存,但源操作数和目的操作数不能同时是内存。

3.3.4算术运算

常用运算指令:

3.3.5数组运算

-32(%rbp)的地址就是argv[0]的地址(也即argv的地址),-32(%rbp)+ 8为argv[1]的地址,-32(%rbp)+ 16为argv[2]的地址,这是因为argv数组中的元素为char *类型,占8个字节。

3.3.6 函数调用

倒数第三行call指令就是在调用函数printf。

3.3.7 跳转

这段汇编代码即为hello.c程序中的if(argc!=4)的判断。

意思是:如果-20(%rbp)的值与4相等,就会跳转到L2处继续执行代码。

3.4 本章小结

本章介绍了编译的概念与作用,并对Hello的编译结果进行了详细的说明,包括伪指令,rodata节,赋值,算术运算,数组运算,函数调用,跳转等内容。

第4章 汇编

4.1 汇编的概念与作用

在C语言编程中,从.s文件到.o文件的过程称为汇编。汇编器将汇编语言文件.s转换为目标文件.o。这个目标文件是机器语言的二进制表示,可以被链接器链接到可执行文件中。

汇编的概念与作用:

(1)汇编器读取汇编语言文件.s,并将其转换为机器语言指令。

(2)汇编器处理汇编语言程序中的标签、变量和宏定义,将它们转换为相应的机器语言代码。

(3)汇编器处理汇编语言程序中的汇编指令,如MOV、ADD、JMP等,并将其转换为机器语言代码。

(4)汇编器将机器语言代码打包成目标文件.o,其中包含代码段、数据段和符号表等信息。

通过汇编和生成目标文件的过程,C语言源代码被转换为机器语言的二进制表示。这个目标文件可以作为链接器的输入,与其他目标文件一起生成可执行文件或动态库文件,以便在计算机上运行和执行。

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

文件头信息:

各个节的信息:

重定位信息:

符号表信息:

4.4 Hello.o的结果解析

通过对比可知,汇编代码与反汇编代码大体相同,但也有很多区别。

①数的进制不同:汇编代码使用的是十进制,而反汇编代码用的是十六进制。

②函数调用不同:汇编代码在call后直接是函数名称,而反汇编代码在call后是一个地址。

③跳转不同:汇编代码在跳转时使用的是段名称(如L1,L2等),而反汇编代码使用的是地址。

4.5 本章小结

本章介绍了汇编的概念与作用,可重定位目标elf格式,以及对反汇编代码和汇编代码进行了详细的比对,找出了它们的区别。

第5章 链接

5.1 链接的概念与作用

链接阶段使用gcc命令将.o目标文件链接成可执行文件。在这个阶段,链接器(ld)将.o目标文件中的代码与库文件(如标准库、动态库等)中的代码合并,生成可执行文件。链接器将不同的目标文件和库文件合并,生成一个完整的可执行文件。在这个过程中,链接器会解决不同目标文件之间的符号引用问题,确保所有符号都在可执行文件中被定义。最终,从.o文件到可执行文件的构建过程生成了一个可以在特定硬件平台上运行的可执行文件。

5.2 在Ubuntu下链接的命令

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

5.3.1头部分

hello的数据为补码、小端序,类型是shared object file,入口点地址为0x1100,以及文件各部分的大小。

5.3.2头部表

这里展示了hello中所有节的大小(size)和偏移量(offset),地址(Address)是hello被加载到虚拟地址时各个节的初始地址。

5.3.3符号表

5.4 hello的虚拟地址空间

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

5.5 链接的重定位过程分析

hello.o只有main函数的反汇编代码,而hello中还有其他函数的反汇编代码;

hello.o从0开始,而hello从0x1000开始。

在hello.o的主函数中全局变量的偏移量都是零,而在hello的主函数中全局变量都有了不为零的偏移量。

当汇编器遇到位置不确定的符号引用时,它就产生一个重定位条目,其功能是用来告诉链接器在合成可执行文件时应该如何修改这个引用。代码的重定位条目放在.rel.text中,已初始化的数据的重定位条目放在.rel.data中。

5.6 hello的执行流程

载入:_dl_start、_dl_init

执行:_start、_libc_start_main 运行:_main,_printf,_exit,_sleep,_getchar,_dl_runtime_resolve_xsave,_dl_fixup,_dl_lookup_symbol_x

退出:exit

5.7 Hello的动态链接分析

   

5.8 本章小结

本章介绍了链接的概念与作用,展示了可执行目标文件hello的格式,并对链接的重定位过程进行了分析,对hello的执行流程进行了分析。

第6章 hello进程管理

6.1 进程的概念与作用

6.1.1进程的概念

一个正在执行的程序实例。

6.1.2进程的作用

在现代系统上运行一个程序,我们可以得到程序是系统中唯一运行的程序的假象,程序独占处理器和内存的假象,和程序的代码和数据是系统内存中唯一的对象的假象。这些假象都是通过进程实现的。

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

Bash是Unix系统中的一种命令解释器,也是Linux系统中的默认Shell。Bash shell的作用是解释用户输入的命令,并将它们传递给操作系统执行。Bash可以处理各种不同类型的命令,例如内部命令、外部命令、别名和函数等。

Bash的处理流程可以分为以下几个步骤:

(1)读取输入:Bash首先从终端或文件中读取用户输入的命令。

(2)语法解析:Bash对输入的命令进行语法解析,确定命令的结构和组成。

(3)历史替换:Bash检查输入的命令是否存在于命令历史中,如果有,则使用最新的版本替换命令。

(4)别名替换:Bash检查输入的命令是否是一个别名,如果是,则用相应的命令替换别名。

(5)扩展通配符:Bash将输入的命令中的通配符(如*、?、[]等)扩展为相应的文件列表。

(6)参数扩展:Bash将输入的命令中的变量和参数进行替换。

(7)命令查找:Bash在环境变量 $PATH 中搜索对应的命令。如果找到,则执行该命令;如果没有找到,则报告命令未找到。

(8)命令执行:Bash执行找到的命令,并将输出返回给用户。如果命令执行过程中发生错误,Bash会报告错误信息。

(9)处理管道和重定向:Bash处理命令中的管道(|)和重定向(>、>>、<、<<)操作。

(10)继续读取输入:Bash处理完当前命令后,继续从终端或文件中读取用户输入的下一条命令,并重复以上步骤。

Bash shell的处理流程可以让用户方便地与系统进行交互,执行各种任务和管理文件。

6.3 Hello的fork进程创建过程

使用fork函数,父进程调用fork函数创建一个子进程,子进程得到与父进程用户完全相同的一个副本。子进程返回0,父进程返回子进程的PID。

6.4 Hello的execve过程

在进程中调用execve函数即可载入并运行程序,新程序会覆盖当前进程的数据、代码和栈,并和原进程有相同的PID。execve函数调用一次从不返回。

6.5 Hello的进程执行

hello程序执行了sleep系统调用,引发陷阱异常。此时从用户模式进入内核模式,这时程序开始休眠,将控制转给其他进程。sleep完成后,给内核发送信号,进入内核状态处理异常,hello程序重新回到用户模式。执行getchar函数时,会使用read系统调用,产生上下文切换。

6.6 hello的异常与信号处理

四类异常:

类别 原因 同步/异步 返回行为 中断 来自I/O设备的信号 异步 返回到下一条指令 陷阱 有意的异常 同步 返回到下一条指令 故障 潜在可恢复的错误 同步 可能返回到当前指令 终止 不可恢复的错误 同步 不返回

Hello在执行过程中,这四种异常都有可能出现。不同种类的异常会发出不同种类的信号。如中断时会发出SIGINT信号。

①随便乱按时(不包括Ctrl-Z,Ctrl-C),不影响程序执行。

②按ctrl-c时,前台进程组终止。

③按ctrl-z时,前台进程组暂停。

此时运行ps  jobs  pstree  fg  kill 等命令:

6.7本章小结

本章介绍了进程的概念与作用,简述了壳Shell-bash的作用与处理流程,说明了Hello的进程执行以及异常与信号处理。

第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:编译器进行编译时,产生汇编汇编代码,每局代码以及每条数据都会有自己的逻辑地址。逻辑地址用来指定一个操作数或者是一条指令的地址。是由一个段标识符加上一个指定段内相对地址的偏移量。

线性地址:CPU加载程序后,会为程序分配内存,分配的内存分为代码段数据和数据段数据。代码段基地址在CS中,数据段基地址在DS中。线性地址=段基址+逻辑地址。

虚拟地址:线性地址的别称。

物理地址:出现在CPU外部地址总线上的寻址物理内存的地址。

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

在Intel x86处理器中,段式内存管理是一种常用的方法,用于将逻辑地址转换为线性地址。段式管理是通过将内存划分为不同的段(例如代码段、数据段、堆栈段等)来实现的。每个段都有自己的基地址和界限,逻辑地址由段号和段内偏移量组成。

段式管理的主要步骤如下:

(1)将逻辑地址分解为段号和段内偏移量。

(2)找到对应的段描述符。段描述符存储了段的基地址、界限等信息。

(3)检查段内偏移量是否超过段的界限,如果超过则产生异常。

(4)将段基地址加上段内偏移量,得到线性地址。

7.3 Hello的线性地址到物理地址的变换-页式管理

在页式内存管理中,线性地址通过页表映射到物理地址。页表将虚拟地址(线性地址)划分为固定大小的页(通常为4KB),并为每个页分配一个物理帧号。页表存储在主存中,每个进程都有一个页表。

从线性地址到物理地址的变换过程如下:

(1)将线性地址划分为页号和页内偏移量。页号表示页表内的索引,页内偏移量表示页内的位置。

(2)查找页表,找到对应的物理帧号。

(3)将物理帧号与页内偏移量合并,得到物理地址。

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

当使用TLB和四级页表进行虚拟地址(VA)到物理地址(PA)的转换时,地址转换过程会变得更加复杂。以下是转换过程的概述:

(1)TLB查询:CPU会首先检查TLB,查看是否缓存了与此虚拟地址相对应的物理地址。如果命中,则直接返回物理地址。否则,执行下一步。

(2)四级页表查询:

   a. 获取虚拟地址的最高12位(例如)作为PGD(Page Global Directory)索引,在PGD表中查找相应的表项。

   b. 使用虚拟地址的中间12位(例如)作为PMD(Page Middle Directory)索引,在PMD表中查找相应的表项。

   c. 使用虚拟地址的中下12位(例如)作为PTE(Page Table Entry)索引,在PTE表中查找相应的表项。

   d. 使用虚拟地址的最低12位(例如)作为PPE(Page Page Directory)索引,在PPE表中查找相应的表项。

在上述过程中,每一步都会检查对应的表项是否存在或合法。如果某一步未命中,则产生页错误异常。如果所有步骤均命中,将得到最终的物理帧号。

(3)物理地址生成:将物理帧号与虚拟地址的页内偏移量合并,生成最终的物理地址。

通过上面的过程,实现了虚拟地址到物理地址的转换。

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

在三级缓存(三级Cache)支持下的物理内存访问过程中,CPU通常使用缓存来提高内存访问的速度。三级缓存的结构和原理如下:

(1)L1 Cache(一级缓存):位于CPU核心内部,速度最快,容量最小。每个CPU核心都有自己的L1缓存,只缓存当前核心使用的数据。

(2)L2 Cache(二级缓存):位于CPU核心外部,速度中等,容量较大。L2缓存通常为多个CPU核心共享,用于缓存频繁访问的数据。

(3)L3 Cache(三级缓存):位于CPU外部,速度较慢,容量最大。L3缓存通常为整个CPU共享,用于缓存大量数据。

当CPU需要访问物理内存时,会首先查询所需的数据是否在缓存中。如果缓存命中,则直接从缓存中读取数据;否则,会从物理内存中读取数据,并将数据缓存到相应的缓存级别中。

以下是三级缓存支持下的物理内存访问过程:

(1)CPU生成物理地址:CPU将虚拟地址经过页表和TLB转换得到物理地址。

(2)L1 Cache查询:CPU首先查询L1 Cache,查看所需的数据是否在L1缓存中。如果命中,则直接读取数据。

(3)L2 Cache查询:如果L1 Cache未命中,则CPU查询L2 Cache。如果命中,则直接读取数据,并更新。

(4)L3 Cache查询:如果L2 Cache未命中,则CPU查询L3 Cache。如果命中,则直接读取数据,并更新。

(5)物理内存访问:如果L3 Cache未命中,则CPU直接访问物理内存,获取所需的数据,并将数据缓存到L3 Cache、L2 Cache和L1 Cache中。

需要注意的是,缓存一致性是三级缓存支持下的物理内存访问需要解决的重要问题。在多处理器系统中,每个处理器都有自己的缓存,当同一个内存位置被多个处理器缓存时,需要确保每个处理器在看到相同的内存数据。通常,硬件和操作系统会使用协议和数据一致性方法来维护缓存一致性,例如MESI协议或目录协议。

7.6 hello进程fork时的内存映射

进程fork时的内存映射指的是在UNIX操作系统(如Linux)中,当一个进程fork创建出新的子进程时,子进程和父进程之间的内存映射关系。这种内存映射有助于节省内存资源,并提高子进程启动速度。

在fork过程中,子进程和父进程共享相同的内存空间。这意味着,子进程将获得父进程内存空间的一份拷贝,包括进程的代码段、数据段和堆栈段等。然而,需要注意的是,这并不是说我们同时拥有了两份相同的内存空间,而是父子进程共享同一块物理内存。

7.7 hello进程execve时的内存映射

(1)删除已存在的用户区域

(2)映射私有区域:所有这些新的区域都是私有的、写时复制的。代码和数据被映射至.text区和.data区。bss区域,堆是请求二进制零的区域。

(3)映射共享区域:hello程序与共享对象链接,这些对象动态链接到hello程序,然后映射到用户虚拟地址空间。

(4)设置程序计数器:设置当前进程上下文的程序计数器,使之指向代码区域的入口点。

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

7.8.1缺页故障:

当进程访问的页面不在物理内存时,就会发生缺页故障。这可能是因为页面从未被加载到物理内存,或者因为它已经被置换到磁盘上以腾出物理内存空间。缺页故障会导致处理器产生一个异常,称为缺页中断。

7.8.2缺页中断处理:

当操作系统接收到缺页中断时,它会执行以下步骤来处理故障:

(1)确定需要访问的虚拟页面。这通常是由处理器提供的信息来识别的。

(2)检查虚拟页面是否属于当前进程。如果不是,操作系统可能会触发一个段错误,因为它可能意味着进程试图访问不属于它的内存。

(3)如果虚拟页面属于当前进程,操作系统会在物理内存中查找一个空闲的页面帧。如果找不到空闲的页面帧,操作系统可能会将内存中的一个或多个页面置换到磁盘上,以便为所需的页面腾出空间。这可能会导致其他进程的缺页中断。

(4)一旦找到了空闲的页面帧,操作系统会将所需的虚拟页面加载到该页面帧中。这可能需要从磁盘上的页面交换文件读取页面,或者从进程的工作集中加载页面,如果它在工作集中已被置换到磁盘上。

(5)更新页表,将虚拟页面映射到新的物理地址。

(6)操作系统会返回到发生缺页中断的指令,处理器将继续执行该指令。

(7)如果在物理内存中找不到空闲的页面帧,操作系统可能会选择牺牲一些系统资源,如缓存或缓冲区,以便为所需的页面腾出空间。

缺页中断处理是操作系统内存管理的关键部分,因为它有助于确保进程能够有效地访问其虚拟地址空间,并在物理内存不足的情况下仍然可以运行。

7.9动态存储分配管理

动态内存管理是计算机内存管理中的一种手段,用于在运行时动态分配和释放内存,以适应程序的内存需求。动态内存管理的基本方法和策略包括:

(1)堆和栈:动态内存管理通常使用堆和栈来实现。栈用于存储局部变量、函数参数和返回值,由编译器自动管理。堆用于动态分配内存,允许程序在运行时请求和释放内存。程序可以使用malloc、calloc、realloc等函数在堆上申请内存,使用free函数释放内存。

(2)内存分配策略:动态内存管理的一个重要策略是确定何时分配和释放内存。常见的策略包括:

  a.首次适配:在堆中搜索第一个能满足请求大小的空闲块,并将其分配。

  b.下次适配:从上一次分配的位置开始搜索,找到第一个能满足请求大小的空闲块。

  c.最佳适配:在堆中搜索一个满足请求大小且大小最小的空闲块。

  d.最差适配:在堆中搜索一个能满足请求大小且大小最大的空闲块。

(3)内存碎片:动态内存管理可能导致内存碎片,这是由于频繁地分配和释放不连续的内存块导致的。内存碎片有两种类型:内部碎片和外部碎片。内部碎片是指已分配内存块之间的未使用空间,外部碎片是指未分配的、不连续的内存空间。

(4)内存紧凑:这是一种用于减少内存碎片的技术。通过将存活的对象移动到一起,紧凑算法可以减少内部碎片。

(5)垃圾回收:这是一种用于自动释放不再使用的内存的技术。垃圾回收器会跟踪内存的使用情况,识别不再使用的内存并释放它们。垃圾回收算法有多种,如标记-清除、复制、标记-整理等。

(6)内存池:这是一种用于优化动态内存管理的技术。内存池在程序启动时预先分配一大块内存,然后根据需求将其分割为更小的内存块。内存池可以通过重用已分配的内存块来减少内存分配和释放的开销。

以上这些基本方法和策略可以用来实现动态内存管理,并根据具体的应用场景和需求进行优化。

7.10本章小结

本章介绍了hello的存储器地址空间,Intel逻辑地址到线性地址的变换-段式管理,Hello的线性地址到物理地址的变换-页式管理,TLB与四级页表支持下的VA到PA的变换,三级Cache支持下的物理内存访问,hello进程fork时的内存映射,hello进程execve时的内存映射,缺页故障与缺页中断处理以及动态存储分配管理。第8章 hello的IO管理

8.1 Linux的IO设备管理方法

在linux中所有的I/O设备都被模型化为文件,所有的输入和输出都被当做对相应文件的读和写来执行。

8.2 简述Unix IO接口及其函数

UNIX操作系统提供了一套IO接口,用于实现对各种设备的访问和控制。这些接口被称为UNIX IO接口,通常通过一组函数来实现。这些函数可以用于文件、设备等资源的打开、关闭、读写等操作。以下是一些常见的UNIX IO接口及其函数:

(1)文件打开和关闭:

  int open(const char *path, int flags, mode_t mode);

  int close(int fd);

 这些函数用于打开和关闭文件。open函数返回一个文件描述符,用于后续对该文件的操作。close函数用于关闭一个打开的文件。

(2)读和写:

ssize_t read(int fd, void *buf, size_t count);

  ssize_t write(int fd, const void *buf, size_t count);

 这些函数用于从文件中读取数据和向文件中写入数据。read函数从文件中读取最多count个字节到缓冲区buf中,返回实际读取的字节数。write函数将缓冲区buf中的count个字节写入文件,返回实际写入的字节数。

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(buf, fmt, arg)函数能够返回想要打印的字符串的长度并对格式化字符串进行解析。当获取到字符串的长度后,便能打印出字符串。

从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.

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

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

8.4 getchar的实现分析

getchar能够读取stdin,然后获取输入的字符。

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。

getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

本章介绍了Linux的IO设备管理方法,简述了Unix IO接口及其函数,并分析了printf和getchar的实现。

结论

(1)hello.c由预处理器进行预处理,形成hello.i文件。

(2)hello.i经过编译,形成汇编代码hello.s。

(3)hello.s经过汇编,生成可重定位目标文件hello.o。

(4)hello.o经过链接,生成可执行文件hello

(5)shell-bash程序调用fork函数为hello生成进程,并调用execve函数

(6)execve使hello载入内存并运行。

(7)程序运行的效果通过I/O设备呈现。

(8)运行终止后被shell回收,内核清空hello的信息。

通过对hello一生的追寻,我认识到一个简单程序的执行也需要很多很多的步骤,并发现了计算机系统的奇妙之处。此外通过本学期对一本如此厚重的书的学习,我的自信心大大提升。

附件

文件 作用 hello.c hello程序的源代码 hello.i hello程序预处理后产生的代码 hello.s hello程序编译产生的汇编代码 hello.o hello程序汇编产生的可重定位目标文件 hello 经过链接后的可执行文件 dump_txt hello.o的反汇编文件 dump2_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分)

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: