计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 计算机科学与技术
学 号
班 级
学 生
指 导 教 师
计算机科学与技术学院
2023年4月
摘 要
一个看似简单的程序hello,实际上在计算机中经历了复杂的生命周期。本文将跟踪hello的生命周期,剖析其从预处理、编译、汇编、链接生成可执行文件到在系统上运行,最后运行完毕被回收的过程。这个过程将揭示计算机系统的奇妙之处,借此加深对计算机系统的理解。
关键词:计算机系统;hello可执行程序;编译;汇编;链接;进程;存储;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本章小结
结论
附件
参考文献
020过程
硬件环境:
主机:
虚拟机:
软件环境:
主机:
Windows11 64位操作系统
虚拟机:
Ubuntu 22.04.3 LTS(64位)
开发与调试工具:
gedit、gcc、gdb、readelf、objdump、edb
文件名 |
功能 |
hello.c |
c语言源程序 |
hello.i |
预处理生成的文本文件 |
hello.s |
编译器生成的汇编语言文本文件 |
hello.o |
汇编器生成的二进制可重定位目标文件 |
hello |
链接生成的二进制可执行目标文件 |
本章叙述了hello程序的P2P和020过程,介绍了编写大作业用到的实验环境及工具,列出了实验过程中的中间结果文件。
概念:
预处理是C语言的一个重要功能,它由预处理程序负责完成。当编译一个程序时,系统将自动调用预处理程序对程序中的“#”号开头的预处理部分进行处理,处理完毕之后可以进入源程序的编译阶段。C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。
作用:
查看hello.i文件,发现hello.c中的注释和编译预处理指令消失,main函数位于文件的最后,前面插入了所有指定头文件的内容。
本章简述了预处理的概念和作用,并在untunbu下实现了hello.c的预处理,对预处理的结果进行了分析。
概念:
编译是指将高级语言源代码转换为机器可执行代码的过程。在这个过程中,编译器会经历多个阶段,包括预处理、词法分析、语法分析、语义分析、中间代码生成、优化和目标代码生成等。在预处理阶段,源代码经过宏展开、头文件包含等处理操作生成预处理后的代码(.i文件)。而从预处理后的文件生成汇编语言程序(.s文件)是编译的一部分。
作用:
3.3.1数据
(1)字符串常量
定义了标签.LC0、.LC1,并分别将一个字符串常量存储在标签对应的内存位置上。前面的.section .rodata表示只读数据区,.alion 8表示对齐方式为8字节对齐。
观察易发现存储的字符串常量为源程序中的以下部分:
.LC0是一个字符串常量的标签,(%rip)表示相对于指令指针(RIP)的偏移量。因此,.LC0(%rip)获取了字符串常量.LC0的地址。leaq .LC0(%rip), %rax的作用是将字符串常量.LC0的地址加载到寄存器%rax中。同理,leaq .LC1(%rip), %rax的作用是将字符串常量.LC1的地址加载到寄存器%rax中。
(2)局部变量
subq$32,%rsp为函数的栈帧分配了32字节的空间,局部变量、函数参数、返回地址等信息将会存储在这片区域中。将寄存器%edi中的值(即argc的值)存储在堆栈帧的偏移量为-20(%rbp)的位置,将寄存器%rsi中的值(即argv的地址)存储在堆栈帧的偏移量为-32(%rbp)的位置。argc和argv的初值是由调用main函数时传递的实际参数决定的。根据源代码可以得知argc被定义为int类型,在64位环境中占4个字节,所以用movl,64位环境中地址占8个字节,所以用movq。
源程序中定义了整型局部变量i但没有初始化。movl $0, -4(%rbp)的将立即数0赋值给位于-4(%rbp)地址处的32位(4字节)变量,即赋值给i。
后序执行过程中也有大量的mov,lea等赋值操作,再此不赘述。
(3)有符号整型常数
源程序中出现的这类数据用立即数($+数字)表示。
3.3.2算术操作
如上图为汇编代码中出现的算术运算,其中addl $1, -4(%rbp) 的作用是将立即数1加到相对于栈帧指针(%rbp)的偏移量为-4的内存地址中存储的值,对应源程序中的i++。
subq$32,%rsp的作用前面已经说过,为函数的栈帧分配了32字节的空间。
movq -32(%rbp), %rax将argv[0]的地址存入%rax,因此addq $16, %rax获取argv数组中的第三个元素(即argv[2])的地址。同理,addq $8, %rax获取argv[1]的地址,addq $24, %rax获取argv[3]的地址。
3.3.3逻辑/位操作
源程序中没有出现逻辑/位操作。
3.3.4关系操作
-20(%rbp)处的值,即argc和4比较,若相等,则跳转到.L2,执行for循环,否则顺序执行if范围内的操作。-4(%rbp)处的值,即i和7比较,若i<=7(对应源代码中的i<8),则跳转到.L4,执行for循环语句内的操作,否则退出循环。
3.3.5数组/指针/结构操作
源程序中涉及到了对数组的操作。
前面已经分析过addq $16, %rax获取argv[2]的地址,addq $8, %rax获取argv[1]的地址,addq $24, %rax获取argv[3]的地址。由此可知movq (%rax), %rdx的作用是将argv[2]的值存入寄存器%rdx。同理,movq (%rax), %rax将argv[1]的值存入%rsi,movq %rax, %rdi将argv[3]的值存入%rdi。
3.3.6控制转移
源程序中出现的控制转移为if语句和for循环语句,对应到汇编代码如上图所示。可以看出汇编代码中的控制转移由关系操作和条件跳转指令结合实现。
3.3.7函数操作
源程序中进行了5次函数调用,在汇编代码中的对应指令如图所示。
函数调用时,前六个参数保存在寄存器中,其余的参数保存在栈中(此处没有用到),栈顶存放返回地址。main函数调用其他函数时,将被调用函数的第一条指令的地址写入程序指令寄存器%rip中,同时将返回地址压栈,执行完毕后,指令ret将返回地址从栈中弹出,写入程序指令寄存器%rip,函数返回,继续执行main函数的操作。
本章简述了编译的概念和作用,并在untunbu环境下对hello.c进行编译。之后结合源程序,从数据、算术操作、关系操作、数组操作、控制转移、函数操作这些角度分析了汇编代码的实现方式。
概念:
汇编过程是将汇编语言代码转换为机器语言的过程。在这个过程中,汇编器(assembler)将汇编代码翻译成机器指令,并生成可执行的二进制文件。
作用:
通过readelf -a hello.o > hello_o.elf生成hello.o的ELF格式文件。
下图为典型的ELF可重定向目标文件格式:
4.3.1节
各个节对应的功能如下表:
节 |
功能 |
.text |
已编译程序的机器代码 |
.rodata |
只读数据 |
.data |
已初始化的全局变量和局部静态变量 |
.bss |
未初始化的全局变量和局部静态变量,仅是占位符,不占据任何实际磁盘空间 |
.symtab |
符号表,存放函数和全局变量(符号表)信息,不包括局部变量 |
.rel.text |
一个.tex节中位置的列表 |
.rel.data |
被模块引用或定义的所有全局变量的重定位信息 |
.debug |
一个调试符号表 |
.line |
原始C源程序中的行号和.text节中机器指令之间的映射 |
.strtab |
一个字符串表(包括.symtab和.debug节中的符号表) |
4.3.2节头部表
读取ELF头:
ELF(Executable and Linkable Format)头是一种二进制文件格式的组成部分,用于描述可执行文件、可重定位文件和共享目标文件的结构和属性。ELF头位于二进制文件的起始位置,包含了文件的基本信息和元数据。ELF头是ELF格式的核心部分,它提供了关于文件的重要信息,以便操作系统和链接器能够正确加载、解析和执行文件。通过解析ELF头,操作系统可以了解文件的类型,确定入口点,并加载和执行文件的各个段和节。链接器可以使用ELF头的段表和节表信息来进行符号解析和重定位。
读取节头部表:
节头部表(Section Header Table)是ELF(Executable and Linkable Format)文件格式中的一个重要部分。它是位于ELF文件中的一个特殊节(称为.section headers)中的一个表,包含了关于各个节(sections)的信息和属性,如名称、大小、偏移量、对齐方式等。通过节头部表,解析程序可以了解ELF文件中各个节的属性和信息,从而正确加载、解析和执行文件。链接器可以使用节头部表的信息进行符号解析、重定位和链接。
读取可重定位节:
可重定位节(Relocatable Section)是在可重定位文件(Relocatable File)中存储数据或代码的一种数据结构。可重定位节是ELF(Executable and Linkable Format)文件格式中的一个重要组成部分。可重定位节包含了程序的各个部分,如代码段、数据段、符号表、字符串表等。每个可重定位节都有一个唯一的名称,用于标识和识别该节。通过可重定位节,链接器可以将不同的模块合并在一起,并根据重定位表和符号表进行符号解析和重定位。可重定位节在编译、链接和执行过程中起到了重要作用。
读取符号表:
符号表(Symbol Table)是在可执行文件、目标文件或共享库中存储符号信息的一种数据结构。符号表记录了程序中使用的函数、变量、类型等符号的名称、地址、大小和其他属性。符号表通常由编译器、汇编器和链接器生成和使用。编译器在编译过程中收集源代码中的符号信息,并将其记录在符号表中。链接器在链接过程中合并不同模块的符号表,并解决符号冲突和重定位。程序在运行时,可以通过符号表来查找和访问各个符号。
反汇编代码:
汇编代码:
反汇编语言的构成和汇编语言的语法结构很相似,以下为二者的映射关系:
语法结构 |
映射关系 |
指令助记符 |
反汇编语言中的指令助记符与汇编语言中的指令助记符是一一对应的。例如,汇编语言中的"MOV"指令对应的反汇编语言中的"Mov"。 |
操作数 |
反汇编语言中的操作数通常是以符号形式表示,用于代表操作数的地址或值。操作数的具体表示形式取决于机器指令的特点和反汇编器的规则。例如,一个32位寄存器可能在汇编语言中表示为"EAX",在反汇编语言中可能表示为"eax"。 |
内存地址 |
在反汇编语言中,内存地址通常使用符号表示,例如"[eax]"表示eax寄存器中存储的内存地址。 |
标号 |
标号在反汇编语言中用于表示跳转目标的位置,它们可以与汇编语言中的标号相对应。反汇编器会根据机器语言指令中的跳转地址或偏移量来生成标号。 |
注释 |
注释在反汇编语言中用于提供对反汇编代码的解释和说明,帮助理解代码的功能和逻辑。注释与汇编语言中的注释相似。 |
二者的差异:
反汇编代码 |
汇编代码 |
可以发现在汇编代码中,分支跳转是使用段名作为标识的,而在反汇编代码中,是用地址的偏移量来表示的。
反汇编代码 |
汇编代码 |
可以发现,汇编代码中,函数调用的具体目的地是用函数名标识的,而在反汇编代码中是该条命令的下一条命令的地址。但是因为调用的函数还在其它库中,具体调用的地址无法确定,只有在经过链接后才能确定函数调用的准确位置。
本章先介绍了汇编的概念和作用,对hello.s进行汇编,生成二进制可重定位目标文件hello.o,然后对hello.o的ELF格式进行分析,说明了机器语言和汇编语言的映射关系,以及二者在对分支转移和函数调用的处理上的差异。
概念:
链接的概念是将多个目标文件(Object File)合并为一个可执行文件的过程。在从 hello.o 到 hello 可执行文件的生成过程中,链接器会解析目标文件中的符号引用,将符号解析为实际的地址,并将代码和数据段合并到一个地址空间中,最终生成可执行文件。
作用:
ELF头 |
只读内存段(代码段) |
段头部表 (将连续的文件节映射到运行时代码段) |
|
.init |
|
.text |
|
.rodata |
|
.data |
读/写内存段(数据段) |
.bss |
|
.symtab |
不加载到内存的符号表和调试信息 |
.debug |
|
.line |
|
.strtab |
|
节头部表 (描述目标文件的节) |
读取ELF头:
可执行程序文件的 ELF(Executable and Linkable Format)头是一种特殊的数据结构,位于可执行文件的开头。ELF 头包含了关于可执行文件的元信息,用于描述文件的结构和属性。
读取节头部表:
可执行程序文件的节头部表(Section Header Table)是一种数据结构,用于描述可执行文件中的各个节(section)的信息。每个节都是可执行文件中的一个区域,包含了不同类型的数据,如代码、数据、符号表、字符串表等。节头部表是一个由多个节头部表项(Section Header Entry)组成的数组,每个节头部表项对应一个节。每个节头部表项包含了关于该节的元信息,如节的名称、类型、大小、偏移量等。
读取可重定位节:
可执行程序文件中的可重定位节(Relocatable Section)是一种特殊类型的节,用于存储重定位信息。可重定位节包含了指令或数据的地址重定位所需的信息,用于在链接时进行符号解析和地址重定位。当可执行程序被加载到内存中时,这些重定位信息将被使用来修正指令和数据的地址,以确保程序能够正确地执行。
读取符号表:
可执行程序文件的符号表(Symbol Table)是一种数据结构,用于存储程序中定义和引用的符号(Symbol)信息。符号可以是变量、函数、类、结构体等标识符的名称,用于在程序中引用和定位特定的代码或数据。
Data Dump区域显示了虚拟地址空间的信息,可以看到hello程序从虚拟空间0x401000载入,查看Symbols发现是.init的地址,与5.3中节头部表中的地址一致。
其他节的地址也是一一对应的。.text节与虚拟地址空间中的_start函数有相同的地址,是程序的入口地址。根据.rodata节的地址,可以找到虚拟地址空间中对应地址存放的是两个连续的字符串。虚拟地址空间中没有找到.symtab、.debug、.line、.strtab的对应位置,因为它们不加载到内存。
hello.o反汇编结果:
hello反汇编结果:
观察hello.o和hello的反汇编结果,主要有以下几个方面不同:
出现以上结果是因为hello.o没有经过链接,main的地址从0开始,并且不存在库函数的代码。而链接后的hello重定位得到虚拟空间中的绝对地址,并添加了库函数。
重定位的一般过程:
以exit函数为例说明重定位的过程。
hello.o:
hello:
可以看出未重定位前call命令的地址码为0占位。hello中call命令的地址是0x401203,执行call指令时,PC的值为下一条指令的地址0x401208,而exit函数地址为0x4010d0,两者之差为0xfffffec8e8,因为Linux里用的是小端法,因此call指令的地址码为e8c8feffff。
子程序名 |
地址 |
_init |
0x0000000000401000 |
.plt |
0x0000000000401020 |
puts@plt |
0x0000000000401090 |
printf@plt |
0x00000000004010a0 |
getchar@plt |
0x00000000004010b0 |
atoi@plt |
0x00000000004010c0 |
exit@plt |
0x00000000004010d0 |
sleep@plt |
0x00000000004010e0 |
_start |
0x00000000004010f0 |
_dl_relocate_static_pie |
0x0000000000401120 |
deregister_tm_clones |
0x0000000000401130 |
register_tm_clones |
0x0000000000401160 |
_do_global_dtors_aux |
0x00000000004011a0 |
frame_dummy |
0x00000000004011d0 |
main |
0x00000000004011d6 |
_fini |
0x0000000000401270 |
延迟绑定是通过GOT(Global Offset Table)和PLT(Procedure Linkage Table)实现的。GOT是数据段的一部分,而PLT是代码段的一部分。下面是两个表的详细内容:
PLT:PLT是一个数组,其中每个条目是16字节的代码。PLT[0]是一个特殊条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有自己的PLT条目。每个条目负责调用一个具体的函数。
GOT:GOT是一个数组,其中每个条目是8字节的地址。当与PLT结合使用时,GOT[0]和GOT[1]包含动态链接器在解析函数地址时所需的信息。GOT[2]是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被解析。每个条目都有一个与之对应的PLT条目。
在之前的节头部表中找到got的地址:
在edb的data dump中找到对应位置:
发现运行dl_int后的信息发生了变化。
本章介绍了链接的概念与作用,在untunbu下将hello.o链接成hello,然后生成hello的ELF格式文件,并对其进行了分析。接着利用edb查看了hello的虚拟地址空间,与头部表进行了对照。最后分析了链接的重定位过程,展示了hello的执行流程,对hello的动态链接进行了分析。
概念:
进程是计算机中正在运行的程序的实例。它是操作系统分配和管理资源的基本单位,用于执行各种任务和操作。每个进程都有自己的内存空间、代码、数据和系统资源。进程可以包含一个或多个线程,每个线程都是进程中执行的独立单元。进程之间是相互隔离的,每个进程都在自己的地址空间中运行,互不干扰。
作用:
壳(Shell)是计算机操作系统中的一个命令行解释器,它是用户与操作系统内核之间的接口。其中,Bash(Bourne Again SHell)是一种常见的Unix和Linux系统下的壳。通过Bash壳,用户可以与操作系统进行交互,并执行各种操作和任务。
作用:
处理流程:
在终端中输入“./hello 2022113385 张馨方”后,父进程shell会对这条命令进行解析。shell判断hello不是内置指令,则认为是可执行文件,调用fork()创建子进程,子进程通过fork函数获得与父进程用户级虚拟地址空间相同的但是独立的副本,拥有不同的PID。接下来将hello加载到这个进程中执行。如果子进程结束时父进程仍然存在,那么将由父进程进行回收,反之则由init进行回收。进程结束时,子进程返回0,父进程返回子进程的PID。
调用fork()函数创建了一个子进程后,execve函数在当前进程的上下文中加载并运行一个原型为int execve(const char *filename, const char *argv[], const char *envp[])的新程序(hello)。具体步骤:
需要注意的是,execve函数执行后,子进程的上下文会被新程序所替换,子进程的PID保持不变。也就是说,子进程的执行环境和资源被新程序所取代,但其进程标识符(PID)不会改变。如果新程序加载和执行成功,execve函数不会返回。如果加载和执行失败,execve函数会返回-1,并且可以通过errno全局变量获取错误码。通过调用fork函数创建子进程,再调用execve函数加载并运行新程序,可以实现进程的替换和执行不同的程序。
Hello程序的进程执行过程是一个循环过程,操作系统根据调度算法分配时间片给进程,进程在用户态下执行循环中的代码,时间片耗尽后进行进程切换,切换到下一个进程执行。进程的具体执行过程如下:
异常的种类:
hello执行过程中可能出现的情况:
正常运行:
乱按,但没有按到系统指令:
Ctrl+Z:
对应信号SIGSTP。
Ctrl+C:
对应信号SIGINT。
ps:
ps命令用于查看当前系统中运行的进程信息,它不直接发送信号给进程。
jobs:
jobs命令用于查看当前shell会话中的作业(即在后台运行的进程),它也不直接发送信号给进程。
pstree:
pstree命令用于以树形结构显示进程之间的关系,它是一种进程查看工具,不发送信号给进程。
fg:
fg命令用于将一个后台作业(即在后台运行的进程)切换到前台运行,它不直接发送信号给进程。
kill:
kill命令用于向指定的进程发送信号,通过kill命令可以发送各种不同的信号给进程,常见的信号包括:
本章简述了进程的概念与作用,然后介绍了壳Shell-bash的作用与处理流程,并以可执行程序hello为例,介绍了其fork进程创建过程、execve过程、进程执行、异常与信号处理。
Intel处理器使用段式管理机制来将逻辑地址转换为线性地址。段式管理机制包括以下步骤:
段式管理机制在保护模式下广泛应用,能够提供内存访问权限控制和隔离。然而,段式管理机制也存在一些缺点,比如段的大小固定和内存碎片化等问题。为了克服这些问题,现代操作系统通常会结合分页机制来进行地址转换。
Hello程序中,线性地址到物理地址的变换是通过页式管理机制完成的。这个过程涉及到将线性地址分配到页表中的页表项,并通过页表项中的物理页框地址和线性地址的偏移量计算得到物理地址。通过页式管理,可以将内存空间划分为固定大小的页,并将线性地址映射到相应的物理页框,实现对物理内存的访问。这种机制具有高效的内存管理和保护机制,可以有效地管理大容量的内存。
TLB(Translation Lookaside Buffer)是一个硬件高速缓存,用于加速虚拟地址(Virtual Address)到物理地址(Physical Address)的转换过程。在四级页表支持下,TLB与VA到PA的变换过程如下:
TLB与多级页表的结合可以提高地址转换的效率。TLB中存储了最近使用的虚拟地址和物理地址的映射关系,可以避免频繁地访问页表,从而加速地址转换过程。
在三级Cache支持下,物理内存的访问是通过多级Cache层次来加速的。这种层次化的Cache结构由L1、L2和L3三级组成,每一级的Cache容量逐级增大,但速度逐级降低。当CPU需要访问物理内存时,它首先会在L1 Cache中查找是否存在所需数据,如果未命中,则会进一步在L2 Cache中进行查找。如果在L2 Cache中也未命中,则会继续在L3 Cache中进行查找。如果依然未命中,则需要从主存中加载数据到L3 Cache,并通过L3 Cache传递给L2 Cache和L1 Cache,供CPU访问。通过这种三级Cache的层次化结构,可以大大提高CPU对物理内存的访问速度和效率。
在hello进程执行fork()系统调用时,会创建一个子进程,子进程的内存映射是通过复制父进程的内存映射来实现的。以下是fork()时内存映射的简要过程:
在hello进程执行execve()系统调用时,会替换当前进程的内存映射,加载一个新的可执行文件。下面是execve()时内存映射的简要过程:
缺页故障(Page Fault)是指当程序访问某个虚拟地址所对应的页面不在物理内存中时发生的错误。当发生缺页故障时,操作系统会进行缺页中断处理,以下是缺页故障和缺页中断处理的简要过程:
动态内存管理是指在程序运行时动态地分配和释放内存空间,以满足程序的内存需求。C语言中,常用的动态内存管理方法是使用malloc()函数分配内存空间,使用free()函数释放内存空间。以下是动态内存管理的基本方法和策略:
本章对hello的存储管理机制进行了梳理。具体介绍了hello的存储器地址空间,Intel逻辑地址到线性地址的变换-段式管理,Hello的线性地址到物理地址的变换-页式管理,TLB与四级页表支持下的VA到PA的变换,三级Cache支持下的物理内存访问,hello进程fork时的内存映射,hello进程execve时的内存映射,缺页故障与缺页中断处理,动态存储分配管理。
设备的模型化:
在Linux中,设备被抽象为文件,即设备文件。每个设备都被视为一个文件,可以通过访问设备文件来进行设备的读取、写入和控制操作。设备文件通常位于/dev目录下,以文件名的形式表示设备。例如,硬盘设备可以表示为/dev/sda,串口设备可以表示为/dev/ttyS0。通过将设备模型化为文件,使得设备可以像操作文件一样进行读写操作,方便了设备的管理和访问。
设备管理:
Linux中的设备管理主要通过Unix I/O接口进行。Unix I/O接口是一组标准的系统调用函数,用于对设备进行读取、写入和控制操作。
常用的Unix I/O接口函数包括:
通过这些接口函数,程序可以直接对设备进行读写操作,并通过ioctl()函数进行设备的控制和设置。这使得设备的管理变得简单和灵活。此外,Linux还提供了设备驱动程序机制,允许开发者编写特定设备的驱动程序,以实现对设备的底层操作和管理。设备驱动程序通过与内核进行交互,实现设备的初始化、中断处理、数据传输等功能,从而提供给应用程序一个统一的接口。
Unix I/O接口是一组用于对设备进行读取、写入和控制操作的标准接口函数。这些函数通过系统调用来实现,提供了对文件和设备的统一访问方式。下面是常用的Unix I/O接口函数:
printf函数是C语言中用于格式化输出的函数。下面是printf函数的实现分析:
getchar函数是C语言中用于从输入设备中读取一个字符的函数。下面是getchar函数的实现分析:
本章先介绍了Linux的IO设备管理方法,然后简述了Unix IO接口及其函数,对printf函数和getchar函数的实现进行了具体分析。
Hello所经历的过程:
通过本次大作业,我对计算机系统的结构有了更深刻的认识,在对hello生命周期的追溯中感受到了计算机的奇妙,对本专业领域的学习有了更加浓厚的兴趣。
文件 |
描述 |
hello.i |
hello.c预处理后的文件 |
hello.s |
hello.i编译后的文件 |
hello.o |
hello.s汇编后的文件 |
hello |
hello.o链接后的文件 |
hello. elf |
hello用readelf -a hello指令生成的文件 |
hello_o. elf |
hello.o用readelf -a hello.o指令生成的文件 |
[1] (CPU三级缓存技术解析_l3cache-CSDN博客.)
CPU三级缓存技术解析_l3cache-CSDN博客
[2] (深入理解计算机系统-之-内存寻址(三)--分段管理机制(段描述符,段选择子,描述符表)_计算机的段描述-CSDN博客)
深入理解计算机系统-之-内存寻址(三)--分段管理机制(段描述符,段选择子,描述符表)_计算机的段描述-CSDN博客
[3] (linux edb使用手册,反汇编及linux下edb的下载-CSDN博客)
linux edb使用手册,反汇编及linux下edb的下载-CSDN博客
[4] (可执行目标文件的ELF格式)
可执行目标文件的ELF格式
[5](ELF可重定位目标文件 - mjy66 - 博客园)
https://www.cnblogs.com/mjyrise/p/17747539.html
[6] (c语言编译过程详解,预处理,编译,汇编,链接(干货满满)_预编译编译汇编链接-CSDN博客)
c语言编译过程详解,预处理,编译,汇编,链接(干货满满)_预编译编译汇编链接-CSDN博客