计算机系统
大作业
题 目 程序人生-Hello’s P2P
专 业 未来技术人工智能
学 号 2022110811
班 级 22WL023
学 生 李嘉莹
指 导 教 师 刘宏伟
计算机科学与技术学院
2024年5月
本论文的目的是通过跟踪hello.c文件预处理、编译、汇编、链接得到hello这个可执行目标程序来了解hello的生命周期,进而学习一个系统。我们从他的进程的开始到回收,在存储器之间交流,见证系统整齐一致的IO管理方式。我们从Hello的一生中,见证了计算机系统的神奇。
关键词:hello.c;预处理;编译;汇编;链接;进程管理;存储管理;I/O管理;
(摘要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.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简介
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
P2P(Program to Process):描述了程序从编写到被操作系统加载并执行为进程的过程。
O2O(From Zero-0 to Zero-0):描述了程序从初始状态到执行任务,最后回到结束状态的完整生命周期。
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
软件环境
Windows 11
硬件环境
Intel(R) Core(TM) i7-11370H @ 3.30GHz 3.30 GHz
RAM 16.0 GB (15.8 GB 可用) DISK 1TB CPU X64
开发与调试工具
GDB;EDB;gcc;CodeBlocks 64位;Visual Studio 2022 64 位;
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.i 处理hello.c生成的文件
hello.s 对hello.i 进行编译生成的文件
hello.o 对hello.s 进行汇编生成的文件
hello.elf 对hello.o 进行readelf生成的文件
helloo 对hello 进行objdump反汇编生成的文件
hello 对hello.o进行链接生成的文件
hello.out.asm 对hello进行objdump反汇编生成的文件
hello1.elf 对hello 进行readelf生成的文件
1.4 本章小结
hello.c程序的一生从源代码编写开始,通过编译生成可执行文件,然后在用户运行时被加载为进程,执行其逻辑,最后在完成任务后退出并由操作系统回收资源。这一生命周期体现了计算机系统中从程序到进程再到终止的整个过程。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
概念:预处理是C和C++编译过程的第一步,它由预处理器完成,负责在编译之前对源代码进行一些基本的文本替换和宏处理。预处理器处理以#开头的预处理指令,如宏定义、文件包含和条件编译等,使源代码在正式编译前得到简化和扩展。
作用:预处理的作用主要包括定义和替换宏、包含头文件、条件编译、行控制和注释删除等。通过预处理,代码中的宏被替换成实际的值,必要的文件内容被插入,特定条件下的代码块被有选择地编译,从而简化代码,提高可读性和维护性,并减少编译错误。
2.2在Ubuntu下预处理的命令
图 2.0.1预处理
输入指令:gcc -E hello.c -o hello.i 生成hello.i文件
2.3 Hello的预处理结果解析
2.3.1开始
图 2.0.2基本信息
2.3.2中间主要部分
展开的头文件 #include <stdio.h>
, #include <unistd.h>
, 和 #include <stdlib.h>
,显示它们的内容,这些头文件会引入标准输入输出函数、系统调用函数以及通用实用函数。
图 2.0.3stdio.h
stdio.h
头文件主要包含标准输入输出函数的声明。
图 2.0.4unistd.h
unistd.h
头文件主要包含了对POSIX操作系统 API 的访问声明。这些 API 提供对系统调用的接口,如文件操作、进程控制、环境参数等。
图 2.0.5stdlib.h
stdlib.h
头文件主要包含了通用的工具函数的声明,如动态内存管理、程序执行、整数转换、伪随机数生成等。
2.3.3末尾
与hello.c相同,没有注释
图 2.0.6主函数
2.4 本章小结
预处理器会对源代码进行一系列的文本替换和宏展开,以生成一个扩展文件(即.i
文件),这个文件包含了所有的头文件展开和宏替换之后的代码。预处理之后的.i
文件中每段语句的功能和在原始源代码中的功能是一样的,只是包含了更多的上下文信息,如宏定义和类型定义。通过分析.i
文件,可以更好地理解编译器如何处理和理解源代码。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
概念:编译是将高级编程语言编写的源代码转换为机器语言(或中间代码)的过程,这一过程由编译器完成。编译包括多个阶段,如词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成,最终生成可执行文件或目标文件。
作用:编译的主要作用是将人类可读的源代码转换为计算机可以执行的机器代码,从而使程序能够在计算机上运行。编译器在此过程中还进行错误检查和代码优化,提高程序的正确性和运行效率,并将程序分解为可管理的模块,便于调试和维护
3.2 在Ubuntu下编译的命令
图 3.0.1编译
输入指令:gcc -S hello.i -o hello.s 生成hello.s文件
3.3 Hello的编译结果解析
3.3.1文件名指令
指示该汇编代码对应的源文件 hello.c
图 3.0.2.file
3.3.2 .section .rodata定义只读数据段
图 3.0.3只读数据段
包含两个字符串常量:
.LC0 是中文提示信息 "用法: Hello 学号 姓名 手机号 秒数!\n" 的 UTF-8 编码字符串。.LC1 是格式字符串 "Hello %s %s %s\n"。
3.3.3主函数
main 函数标记为全局可见,.globl main 指示链接器全局符号
图 3.0.4主函数
endbr64 是用于Intel CET的指令;
pushq %rbp 保存旧的基址指针,movq %rsp, %rbp 设置新的基址指针,subq $32, %rsp 为局部变量在栈上分配32字节空间。
3.3.4处理参数argc和argv
图 3.0.5argc.argv
movl %edi, -20(%rbp) 将argc保存到局部变量-20(%rbp),movq %rsi, -32(%rbp) 将argv保存到局部变量-32(%rbp),cmpl $5, -20(%rbp) 比较argc是否等于5,je .L2 如果 argc 等于5,跳转到 .L2;
如果 argc 不等于5:leaq .LC0(%rip), %rax 加载 .LC0 的地址到 %rax,movq %rax, %rdi 设置 puts 的参数,call puts@PLT 调用 puts,movl $1, %edi 设置 exit 的参数为1,call exit@PLT 调用 exit 并退出程序
3.3.5循环
循环初始化、循环变量i
图 3.0.6初始化循环变量
.L2: 是循环的开始标签,movl $0, -4(%rbp) 初始化循环变量 i 为0,jmp .L3 跳转到循环条件检查。
循环体
图 4.0.7L4循环体
.L4: 是循环体的开始标签,将 argv 的基地址加载到 %rax,分别将argv[3][2][1]的地址加载到%rcx%rdx%rsi中,加载 .LC1 的地址到 %rax,设置 printf 的格式字符串参数,设置 printf 的 variadic 参数,调用printf;
图 3.0.8printf函数
图 3.0.9for循环
将 argv 的基地址加载到 %rax,将 argv[4] 的地址加载到 %rax,将 argv[4] 的内容加载到 %rdi,调用 atoi 将字符串转换为整数,
图 3.0.10sleep函数
将转换后的整数设置为 sleep 的参数,call sleep@PLT 调用 sleep,addl $1, -4(%rbp) 增加循环变量 i。
循环条件检查
图 3.0.11循环条件检查
.L3: 是循环条件检查标签,cmpl $9, -4(%rbp) 比较循环变量 i 是否小于等于9,jle .L4 如果是,跳转到循环体 .L4。
图 3.0.12输入
循环结束后,call getchar@PLT 调用 getchar,movl $0, %eax 设置返回值为0,leave 恢复旧的基址指针并调整栈指针,ret 返回。
3.4 本章小结
通过这些步骤,C代码被编译器转换为汇编代码,保留了原始逻辑并适应目标平台的指令集和调用约定。
数据类型处理:整型(如 int)变量 i 使用32位寄存器(movl 操作)。指针类型(如 char*)使用64位寄存器(movq 操作)。
算术和逻辑操作:如循环中的 i++ 被编译为 addl $1, -4(%rbp)。
函数调用:如 printf、sleep、atoi 等函数调用遵循函数调用约定,将参数放入适当的寄存器并使用 call 指令。
控制流:如 if 语句和 for 循环使用条件跳转指令(cmp、je、jmp、jle)实现。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
概念:汇编是将汇编语言代码转换为机器代码的过程,这一过程由汇编器完成。汇编语言是一种低级编程语言,它使用简洁的符号和助记符表示机器指令,能够直接操作硬件,且与特定的处理器架构紧密相关。
作用:汇编的作用是将汇编语言代码转化为计算机能够执行的机器语言指令,使程序可以在硬件上运行。通过汇编,程序员可以进行精细的硬件控制、优化程序性能,并在系统编程和嵌入式编程中实现对底层硬件的直接访问。
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令
图 4.0.1汇编
输入指令:gcc -c -m64 -no-pie -fno-PIC -c -o hello.o hello.s 生成hello.o文件
4.3 可重定位目标elf格式
图 4.0.2可重定位目标elf格式
输入指令:readelf -a hello.o > hello.elf 生成hello.elf
4.3.1ELF头
Magic: 7f 45 4c 46 是 ELF 文件的魔数,确认了这是一个 ELF 文件。
这是一个 64 位的 ELF 文件,数据编码方式为小端序。当前版本的 ELF 文件格式是1 (current),适用于 UNIX System V,ABI 版本号是0,这是一个可重定位文件,通常用于目标文件,目标系统架构是 x86-64,对于可重定位文件,入口点地址为 0,没有程序头。节头表的起始位置是1088 (bytes into file),没有特殊标志。ELF 文件头的大小是 64 (bytes),每个节头的大小 64 (bytes),总共有 14 个节头,节头字符串表在第 13 个节中。
图 4.0.3ELF头
4.3.2节头
包含
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
.text节:以编译的机器代码,类型为PROGBITS,意为程序数据,旗标为AX,即权限为分配内存、可执行。
.rela.text节:一个.text借中的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。
.data节:已初始化的静态和全局C变量。类型为PROGBITS,意为程序数据,旗标为WA,及权限为可分配可写。
.bss节:未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。类型为NOBITS,意为暂时没有存储空间,旗标为WA,即权限为可分配可写。
.rodata节:存放只读数据,例如printf中的格式串和开关语句中的跳转表。类型为PROGBITS,意为程序数据,旗标为A,即权限为可分配。
.comment节:包含版本控制信息。
.note.GNU_stack节:用来标记executable stack(可执行堆栈)。
.eh_frame节:处理异常。
.rela.eh_frame节:.eh_frame的重定位信息。
.shstrtab节:该区域包含节区名称。
.symtab节:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。
.strtab节:一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部的节名字。
图 4.0.4节头
4.3.3程序头
本文件中没有程序头。
4.3.4重定位节
.rela.text 和 .rela.eh_frame 包含重定位条目。
图 4.0.5重定位节
4.3.5符号表
.symtab 包含 11 个条目,包括文件名、段名和全局符号 main。
图 4.0.6符号表
4.4 Hello.o的结果解析
图 4.0.7hello.o反汇编
输入指令:objdump -d -r hello.o >hello.txt 生成helloo.txt文件
分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
机器语言是计算机可以直接执行的一种语言,它使用二进制代码表示指令和数据。机器语言中的指令由两部分组成:操作码:指定要执行的操作类型,例如加法、乘法、加载数据等。操作数:包含在指令中的数据或地址,用于指定操作码要操作的内容。汇编语言是一种更高级的语言,它使用助记符和符号来代替机器语言中的二进制指令码和内存地址。汇编语言与机器语言之间的映射关系由汇编器负责处理。汇编器将汇编语言源代码转换为对应的机器语言指令序列。
在汇编语言中会有特定的指令,它们直接对应于机器语言中的分支跳转和函数调用指令。汇编语言的这些指令会被汇编器转换成相应的机器语言指令,从而实现程序控制流的转移和函数调用功能。
图4.0.8调用函数
图 4.0.9无条件跳转到循环开始处
4.5 本章小结
由于直接生成的.o为二进制文件无法直接访问,我们通过查看ELF格式,以及用反汇编后得到的代码进行分析,发现各种操作中的地址被具体化,进一步了解了hello。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
概念:链接是将编译生成的目标文件(hello.o)和库文件结合起来,生成一个可执行文件(hello)的过程,这一过程由链接器完成。链接过程包括符号解析、地址重定位和合并多个目标文件及库文件,最终生成一个完整的、可以独立运行的可执行文件。
作用:链接的主要作用是将各个独立的代码模块(目标文件)和库函数结合起来,解决跨模块的符号引用,确保所有函数和变量都有定义。通过链接,程序得以整合成一个统一的可执行文件,从而可以在计算机上加载和运行,支持代码重用和模块化编程,提高开发效率和程序的可维护性。注意:这儿的链接是指从 hello.o 到hello生成过程。
5.2 在Ubuntu下链接的命令
图 5.0.1链接
输入如图指令,生成可执行文件hello
5.3 可执行目标文件hello的格式
图 5.0.2hello的ELF格式
输入指令:readelf -a hello >hello1.elf 生成hello1.elf
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
5.3.1ELF头
类型为:EXEC(可执行文件)入口点地址: 0x4010f0程序头起点: 64 bytes节头起点: 13560 bytes程序头大小: 56 bytes程序头数量: 12节头大小: 64 bytes节头数量: 27节头字符串表索引: 26
5.3.2节头
[1] .interp: 存放程序解释器路径 地址: 0x4002e0偏移: 0x2e0大小: 0x1c
[15] .text: 包含程序的代码段。地址: 0x4010f0偏移: 0x10f0大小: 0xd8
[19] .dynamic: 动态链接信息。地址: 0x403e50偏移: 0x2e50大小: 0x1a0
[21] .data: 初始化数据段。地址: 0x404048偏移: 0x3048大小: 0x4
5.3.3程序头
PHDR:偏移: 0x40VirtAddr: 0x400040FileSiz: 0x2aMemSiz: 0x2a0标志: R
INTERP:偏移: 0x2e0VirtAddr: 0x4002e0FileSiz: 0x1cMemSiz: 0x1c标志: R
解释器: /lib64/ld-linux-x86-64.so.2
5.3.4动态节
NEEDED: 共享库 libc.so.6
INIT: 初始化段地址 0x401000
5.3.5符号表
[9] puts@GLIBC_2.2.5
[12] printf@GLIBC_2.2.5
5.3.6重定位节 (Relocation Section)
重定位节包含了需要在加载时修正的地址:
.rela.dyn.rela.plt
5.4 hello的虚拟地址空间
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
图 5.0.3虚拟地址空间
Data Dump区域显示了虚拟地址空间的信息,可以看到hello程序从虚拟空间0x401000载入,查看Symbols发现是.init的地址,与5.3中节头部表中的地址一致。
图 5.0.4symbolviewer
打开edb,加载hello,打开symbolviewer查看虚拟地址各段信息。
5.5 链接的重定位过程分析
图 5.0.5hello反汇编
输入指令:objdump -d -r hello >hello1o.txt 生成hello1o.txt,分析hello与hello.o的不同,说明链接的过程。
图 5.0.6反汇编文件
与可重定位目标文件hello.o的反汇编的结果相比,主要有两个地方不同,一是扩充了很多函数代码,包括程序加载后执行main前的一些准备工作,以及hello需要用到的一些库函数的定义等;二是在main中,原本在hello.o中等待重定位而暂时置0的地址操作数被设置为了虚拟地址空间中真正的地址。
5.6 hello的执行流程
使用gdb/edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程(主要函数)。请列出其调用与跳转的各个子程序名或程序地址。
从加载hello到_start
程序名
ld -2.27.so!_dl_start
ld-2.27.so!_dl_init
hello!start
到call main
程序名
libc-2.27.so!_libc_start_main
libc-2.27.so!_al_fixup
libc-2.27.so!_libc_csu_init
libc-2.27.so!_setjmp
hello!main
到结束
程序名
hello!puts@plt
hello!exit@plt
ld-2.27.so!_dl_fixup
ld-2.27.so!exit
5.7 Hello的动态链接分析
分析hello程序的动态链接项目,通过edb/gdb调试,分析在动态链接前后,这些项目的内容变化。
在进行动态链接时,共享库中的代码和数据并未直接合并到可执行文件 hello 中。相反,动态链接器在加载 hello 文件时,对共享目标文件中相关模块内的代码和数据进行重定位。同时,动态链接器会加载共享库,并将其合并到 hello 文件中,生成完全链接的可执行目标文件。这一过程使得程序能够在运行时动态地获取共享库中的函数和数据,实现了更灵活、更高效的代码共享和重用。
图 5.0.700403000
dl_init前:404008和404010处为空
动态链接采用延迟加载的方式,只有在需要调用函数时才进行符号映射。系统利用偏移量表(GOT)和过程链接表(PLT)来实现动态链接。GOT 中存储函数的目标地址,并为每个全局函数创建副本函数,而这些副本函数位于 PLT 中。每次函数调用时,程序先跳转到 PLT 中的副本函数,然后再由副本函数跳转到实际的函数目标地址,实现了延迟加载和符号映射。
dl_init后:加载时动态连接器重定位这两个条目,写入了正确的地址。
图 5.0.800404000
5.8 本章小结
本章简要介绍了linux下链接的相关命令,介绍了生成的二进制可执行文件,对其可重定位目标elf格式进行了分析,并分析了其执行流程和动态连接过程。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
概念:进程是一个执行中程序的实例,系统的每个程序都运行在某个进程的上下文中,上下文是由程序正确运行所需的状态组成的。包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。
作用:提供给应用程序一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器;提供给应用程序一个私有的地址空间,它提供一个假象,好像我们的程序独占地使用内存系统。
6.2 简述壳Shell-bash的作用与处理流程
作用:
命令解释器:解释用户输入的命令,并将其传递给操作系统内核执行。
脚本执行:运行脚本文件,这些文件包含一系列命令和控制结构(如条件语句、循环等),用于自动化任务。
环境管理:管理用户的工作环境,包括设置环境变量、路径、别名等。
程序控制:提供工具来启动、停止和控制程序的执行。
输入输出重定向:允许用户重定向命令的输入输出。
处理流程:
读取用户输入的命令行或脚本文件中的命令。将输入的命令行分解为令牌,根据Shell的语法规则,将令牌解析成可以执行的命令结构。将命令中的变量替换为其实际值,别名替换为其对应的命令。执行命令替换操作。 根据重定向符号(如>、<、|),设置命令的输入输出流。重定向可以将命令的输出写入文件、从文件读取输入或将输出通过管道传递给另一个命令。调用操作系统内核,创建一个子进程来执行命令。如果是内建命令,Shell会直接在当前进程中执行。命令执行完毕后,接收并处理命令的返回状态,并将其存储在特殊变量中。对于交互式Shell,会返回到步骤1,等待用户输入新的命令;对于脚本文件,会继续读取并执行下一行命令,直到文件结束。
6.3 Hello的fork进程创建过程
图 6.0.1创建进程
6.4 Hello的execve过程
在shell新创建的子进程中,execve函数加载并运行可执行目标文件hello,且带参数列表argv和环境变量列表envp,只有出现错误是,execve才会返回到调用程序。
在execve加载hello之后,调用启动代码来执行hello,新的代码和数据段初始化为可执行文件的内容,跳转到_start调用libc_start_main设置栈,并将控制传递给新程序的主函数。
图 6.0.2新程序启动后的栈结构
6.5 Hello的进程执行
当一个新的进程被创建时,操作系统为其分配必要的资源和空间。这包括分配一个唯一的进程标识符(PID)、内存空间、寄存器状态等。hello程序的执行开始于进程创建和加载阶段。每个进程都有自己的上下文信息,包括寄存器内容、内存映像、打开的文件描述符、信号处理器状态等。这些信息定义了进程的当前状态和运行所需的所有数据。hello进程在执行过程中,其上下文信息将随着执行的不同阶段和操作系统调度的影响而变化。操作系统使用调度算法来决定何时以及如何分配CPU时间片给不同的进程。每个进程都被分配一个时间片,在该时间片结束之前,进程将继续执行。hello进程会根据调度算法的具体策略(如轮转调度、优先级调度等)获得CPU时间。进程执行时,操作系统会根据需要将进程从用户态切换到核心态(也称为内核态)。用户态下的进程只能访问受限资源,而核心态下的进程可以执行特权指令、访问所有资源并执行操作系统服务。例如,当hello进程执行系统调用(如printf)时,它会请求操作系统切换到核心态,以便内核执行必要的IO操作或其他服务。
加载和执行:hello进程被加载到内存并从其入口点(如0x4010f0)开始执行。
系统调用:当hello程序调用printf函数时,它将发起一个系统调用请求,这会导致进程从用户态切换到核心态,内核负责在终端上打印消息。
时间片耗尽:如果hello进程的时间片用尽,操作系统会暂停其执行,并将CPU分配给下一个等待执行的进程。
上下文切换:当操作系统决定重新调度hello进程时,它会保存当前进程的上下文信息,切换到下一个进程的上下文,并恢复其状态,使其可以继续执行。
进程结束:当hello进程执行完所有指令并返回时,或者通过调用exit系统调用显式终止时,操作系统会释放其分配的资源,包括内存空间、文件描述符等,并标记进程为已终止状态。
图 6.0.3上下文切换
6.6 hello的异常与信号处理
程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
6.6.1不停乱按
图 6.0.4乱按
图 6.0.5乱按结果
输入存储到缓冲区,程序正常运行。
6.6.2回车
图 6.0.6回车
无影响
6.6.3Ctrl C Ctrl Z
图 6.0.7CtrlC Z
Ctrl-C:程序收到SIGINT信号终止。
Ctrl-Z:程序收到SIGSTP信号暂停
6.6.4CtrlZ后PS
图 6.0.8PS
显示了暂停中的hello进程的信息。
6.6.5CtrlZ后jobs
图 6.0.9jobs
显示暂停的进程
6.6.6CtrlZ后pstree
图 6.0.10pstree
输入pstree, 显示进程树,显示所有进程的情况
6.6.7CtrlZ后fg
图 6.0.11fg
使第一个后台作业变成前台作业,这会让hello变为前台执行
6.6.8CtrlZ后kill
图 6.0.12kill
杀死hello进程
6.7本章小结
本章简要介绍了进程的概念和作用,fork()、execve()函数的使用等,展示了hello进程的创建,执行,终止以及各个命令,如ps,jobs等的作用。
(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
编译阶段:编译器将hello.c源代码编译为目标文件(如hello.o),生成与逻辑地址相关联的代码段和数据段。
链接阶段:链接器将目标文件链接为可执行文件hello,此时生成了与线性地址相关联的代码段和数据段。
运行阶段:当操作系统加载hello程序时,将程序的线性地址映射为虚拟地址,并使用分页机制将虚拟地址转换为物理地址,最终访问系统的实际内存位置。
7.1.1. 逻辑地址
逻辑地址是程序中使用的地址空间,通常由程序员定义,用于访问内存中的数据和指令。在编译和链接阶段,程序中的变量和代码被编译器和链接器处理,生成逻辑地址。
7.1.2. 线性地址
线性地址是逻辑地址经过操作系统的地址转换机制(如分段或分页机制)转换后得到的地址。在现代操作系统中,通常使用分页机制将逻辑地址转换为线性地址。
7.1.3. 虚拟地址
虚拟地址是进程所见到的地址空间,它包括程序的逻辑地址和由操作系统管理的内存地址。虚拟地址空间对每个进程都是独立的,进程可以在虚拟地址空间中自由访问,而不需要关心物理内存的实际位置。
7.1.4. 物理地址
物理地址是内存中存储单元的真实地址,它指的是存储器芯片上的特定位置。操作系统通过地址映射机制(如页表)将虚拟地址转换为物理地址,从而最终访问内存中的数据和指令。
7.2 Intel逻辑地址到线性地址的变换-段式管理
根据逻辑地址中的段选择子,确定要访问的段描述符。段选择子中的段索引指定了描述符表(GDT或LDT)中的一个条目。使用段选择子中的段索引,从GDT或LDT中获取对应的段描述符。段描述符中包含了段的基地址和段的大小等信息。对于32位系统,段的基地址由段描述符的基地址字段和偏移字段(offset)组成。
将逻辑地址中的偏移量与段描述符中的基地址进行加法运算,得到线性地址。线性地址即为CPU可直接访问的地址空间。在实际访问内存时,操作系统将线性地址转换为物理地址。这一步通常涉及到分页机制的介入,通过页表将线性地址映射到物理地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
CPU使用线性地址中的高10位作为页目录表索引,从页目录表中获取对应的页目录项(PDE)。从页目录项(PDE)中获取页表的基地址,并使用线性地址中的中间10位作为页表索引,从页表中获取对应的页表项(PTE)。从页表项(PTE)中获取物理页框号(PFN),将线性地址中的低12位(偏移量)与物理页框号进行组合,得到最终的物理地址。
图 7.0.1基于页表的地址翻译
7.4 TLB与四级页表支持下的VA到PA的变换
TLB和四级页表是协同工作的,TLB的作用是加速地址转换,减少对页表的访问次数,从而提高性能。四级页表的作用是提供了虚拟地址到物理地址的具体映射关系,是TLB未命中时的备用。
地址转换的完整过程为CPU生成一个虚拟地址。首先检查TLB中是否有该虚拟地址的映射关系。如果TLB命中,则直接从TLB中获取物理地址。如果TLB未命中,则通过多级页表(页目录表、页表)获取物理地址,并将新的映射关系加载到TLB中。通过TLB和多级页表的结合使用,现代操作系统可以有效地管理和保护内存,同时提供快速的地址转换功能,以满足不同程序对内存地址访问的需求。
图 7.0.2多级页表
7.5 三级Cache支持下的物理内存访问
在拥有三级缓存支持的系统中,物理内存访问经历了多个阶段。首先,当CPU执行指令或加载数据时,会先检查最快速的L1缓存,如果缓存未命中,则检查次级的L2缓存。如果L2缓存也未命中,则会查找更大、更慢速的L3缓存。如果L3缓存中找不到所需数据,则CPU将从主存(物理内存)中获取数据。整个过程中,缓存的层级逐渐增加,速度逐渐减慢,但通过层层缓存的分级访问,系统能够在保证快速响应的同时,有效地管理数据的访问与存储。
7.6 hello进程fork时的内存映射
当一个进程(例如名为hello的进程)调用fork()创建子进程时,操作系统会将父进程的地址空间完全复制一份给子进程,这包括代码段、数据段、堆、栈以及其他映射的内存区域。这种内存映射的复制是通过写时复制(copy-on-write)实现的,意味着父子进程共享相同的物理页面,直到其中一个进程试图修改这些页面的内容。这种机制保证了在fork()操作后,系统内存的有效利用,同时避免了不必要的内存复制开销。
7.7 hello进程execve时的内存映射
当hello进程调用execve()函数执行新程序时,操作系统会将当前进程的地址空间清除,然后重新建立新程序的地址空间映射。这个过程包括加载新程序的代码段、数据段、共享库以及其他必要的映射区域,以及设置新程序的堆栈和参数。execve()系统调用会替换当前进程的内存映射,将其转换为新程序的内存布局,从而开始执行新程序。
7.8 缺页故障与缺页中断处理
缺页故障指的是当程序访问的页面不在主存中,需要从磁盘中加载到主存时发生的情况。当CPU访问一个缺页时,会触发缺页中断。操作系统的缺页中断处理程序首先会检查访问的页面是否已经在主存中,如果不在,则根据页面表找到对应的页面位置,并将其从磁盘加载到主存中的空闲页面上。一旦页面加载完成,操作系统更新页表,使得程序可以重新执行被中断的指令,继续执行。
图 7.0.3缺页
图 7.0.4缺页中断处理
7.9动态存储分配管理
当使用`printf`函数输出格式化字符串时,如果字符串中包含可变的部分(例如变量值),`printf`需要在运行时动态地分配内存来存储这些可变部分的字符串表示。这通常涉及到调用`malloc`函数来分配足够大小的内存来存储格式化后的字符串。动态内存管理的基本方法包括使用`malloc`、`calloc`或`realloc`函数来分配内存,并使用`free`函数来释放已分配的内存。策略方面,常见的包括合理选择内存分配算法(如最先适应、最佳适应、最坏适应),管理内存碎片化问题,避免内存泄漏,并在需要时使用内存池等技术来优化内存管理效率。
7.10本章小结
本章简述了计算机中的各类地址及他们之间的相互转换,并探究了计算机的虚拟内存系统内部的工作原理,从内存角度重新审视了前面的若干知识,如fork、execve函数,最后解释了动态内存管理的基本方法与策略。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:所有的 I/O 设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行
设备管理:Linux内核有一个简单、低级的接口,称为Unix I/O,使得所有的输入和输出都能以一种统一且一致的方式来执行。
8.2 简述Unix IO接口及其函数
Unix IO接口提供了一套标准化的方法,使用户程序能够与操作系统内核进行文件和设备的输入输出交互。其主要函数包括`open`用于打开文件,`read`和`write`用于读写文件内容,`close`用于关闭文件,`lseek`用于定位文件指针,以及`ioctl`用于设备控制等。通过这些函数,程序可以管理文件描述符,进行文件和设备的操作,并进行错误处理和状态查询,提供了强大而灵活的IO操作能力,是Unix系统中核心的功能之一。
8.3 printf的实现分析
https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
在本章我们分析了Linux的IO设备管理方法,并对UnixIO接口及其函数进行了简单介绍;最后通过printf和getchar两个基础函数的实现,让我们看到了linux能够高效运行的优势所在:进行简单高效的IO管理。
(第8章1分)
结论
hello所经历的过程:
hello最初是一个C语言源程序hello.c,即program。
①预处理:对带#的指令解析,生成hello.i文件,解释预处理指令。
②编译:经历编译后,得到hello.s,即汇编语言级代码。
③汇编:把汇编语言转换成机器代码,生成重定位信息,生成hello.o。
④链接:与动态库链接,生成可执行文件hello。
⑤创建进程:在Shell利用./hello运行hello程序,父进程通过fork函数为hello创建进程。
⑥加载程序:通过加载器,调用execve函数,删除原来的进程内容,加载现在进程的代码、数据等到进程自己的虚拟内存空间。
运行hello时,内存管理单元MMU、翻译后备缓冲器TLB、多级页表机制、三级Cache同舟共济,完成对地址的请求。
异常处理机制保证了hello对异常信号的处理,使程序平稳运行。
当hello运行完毕,Shell父进程回收hello,内核删除为这个进程创建的所有数据结构。
hello.c程序从创建到结束的生命周期涉及了创建和编辑源代码、编译、链接、运行和结束的多个阶段。这个过程涉及了多个工具和组件的协作,包括编辑器、编译器、链接器以及操作系统。每个阶段都对应着特定的任务和输出,最终形成了一个可以执行并输出的程序。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.i 处理hello.c生成的文件
hello.s 对hello.i 进行编译生成的文件
hello.o 对hello.s 进行汇编生成的文件
hello.elf 对hello.o 进行readelf生成的文件
helloo 对hello 进行objdump反汇编生成的文件
hello 对hello.o进行链接生成的文件
hello.out.asm 对hello进行objdump反汇编生成的文件
hello1.elf 对hello 进行readelf生成的文件
(附件0分,缺失 -1分)
参考文献
为完成本次大作业你翻阅的书籍与网站等
[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分)