一给 发表于 2024-7-12 12:00:35

程序人生-Hello‘s P2P-ICS大作业

https://img-blog.csdnimg.cn/direct/f2cec5bcdd96427dafe51342d5633568.jpeg
https://img-blog.csdnimg.cn/direct/c035777ba4bf47b085bc3aafd61e36e6.jpeg
程序人生-Hello's P2P-ICS

摘  要
本报告深入剖析了hello程序在计算机系统中的实行流程,以P2P和020的视角展开,对从源代码到可实行程序的每一个转变阶段均进行了详细解读。报告首先介绍了项目标开发环境与工具,包罗软硬件配置及调试手段,并概述了项目中心结果,如天生的各个编译阶段文件及其功能。通过逐步分析预处置惩罚、编译、汇编、链接等关键步调,揭示了从源代码到可实行文件的转化机制。阐释了计算机系统计划与实现的深刻洞察和创新理念,如动态链接、存储管理、进程控制以及I/O操作的内部原理,展示了当代操作系统如何管理资源,支持程序的高效运行。

关键词:计算机系统;进程;预处置惩罚;编译;汇编;存储管理;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.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(From Program to Process)过程:


[*]程序编写:Hello的路程始于程序员通过编辑器(如Vim或Emacs)键入源代码并保存为hello.c,这是Hello作为程序(Program)的起点。
[*]预处置惩罚:通过编译器的预处置惩罚器,hello.c经历头文件包罗、宏展开等操作,转化为hello.i,这是程序编译前的初步加工。
[*]编译、汇编、链接:hello.i颠末编译器转换成汇编代码hello.s,再由汇编器转化为目标代码hello.o。链接器将hello.o与其他须要的库文件链接,天生可实行文件hello,此时Hello完成了从源代码到可实行进程(Process)的转变。
[*]进程管理:在操作系统(OS)的Bash shell中,通过fork创建了Hello的新进程,execve加载并运行Hello的代码。OS为Hello分配时间片,通过进程管理确保其在CPU、RAM和I/O资源上运行。
[*]存储管理:通过虚拟所在到物理所在的转换(VA到PA),以及TLB、页表和Cache的支持,Hello的内存访问得以加快。
[*]I/O与信号处置惩罚:Hello与硬件如键盘、屏幕的交互,以及对信号的相应,展现了操作系统如何协调软硬件资源,确保程序的顺畅运行。
020(From Zero-0 to Zero-0)过程:


[*]零起点:Hello从零开始,作为一个简单的源代码文件,没有任何运行时的状态。
[*]生命周期:随着编译、链接、实行的过程,Hello从无到有,活跃在计算机系统中,经历从创建到实行的完整生命周期。
[*]最终消逝:Hello实行完毕后,其进程被操作系统回收,资源被开释,仿佛回归到最初的“零”,虽已不在,但其生命周期的痕迹留在了计算机系统的汗青中,证明白它的存在。
1.2 环境与工具

硬件环境:X64 CPU;4GHz;64G RAM;2TB SSD Disk
软件环境:Ubuntu 20.04
开发工具:VScode;gcc;vim;edb;readelf等
1.3 中心结果

文件的作用
文件名
预处置惩罚后的文件
hello.i
编译之后的汇编文件
hello.s
汇编之后的可重定位目标文件
hello.o
链接之后的可实行目标文件
hello
Hello.o 的 ELF 格式
elf.txt
Hello.o 的反汇编代码
Disas_hello.s
hello的ELF 格式
hello1.elf
hello 的反汇编代码
hello_objdump.s
1.4 本章小结

本章扼要介绍了程序"Hello"从源代码到可实行进程的完整生命周期,即P2P过程,以及它从无到有再到消逝的020过程。这一过程涵盖程序编写、预处置惩罚、编译、汇编、链接直到在操作系统中运行的各个阶段,同时说明白其间涉及的进程管理、存储管理、I/O与信号处置惩罚等计算机系统的核心概念。
别的,本章还概述了完成此过程所使用的环境与工具,列出了在完成报告过程中天生的一系列中心结果文件及其作用。
(第1章0.5分)



第2章 预处置惩罚

2.1 预处置惩罚的概念与作用

概念:
C预处置惩罚器是C语言、C++语言的预处置惩罚器。用于在编译器处置惩罚程序之前预扫描源代码,完成头文件的包罗,宏扩展,条件编译,行控制(line control)等操作。
C语言尺度规定,预处置惩罚是指前4个编译阶段(phases of translation)。
1.三字符组与双字符组的更换
2.行拼接(Line splicing): 把物理源码行(Physical source line)中的换行符转义字符处置惩罚为普通的换行符,从而把源程序处置惩罚为逻辑行的顺序聚集。
3.单词化(Tokenization): 处置惩罚每行的空缺、注释等,使每行成为token的顺序集。
4.扩展宏与预处置惩罚指令(directive)处置惩罚。

作用:
1.用于包罗另一个文件:比如 hello.c 的第一行的#include<stdio.h>命令告诉预处置惩罚器读取系统有文件 stdio.h 的内容,并把它直接插入程序文本中
2.条件编译:根据可能存在的#ifdef 来确定程序需要实行的代码段
3.宏定义与扩展:对于#define 指令,进行宏更换,对于代码中所有使用宏定义的地方使用符号表现的实际值更换定义的符号
4.特殊宏与指令
5.Token字符串化
6.Token连接
2.2在Ubuntu下预处置惩罚的命令

https://img-blog.csdnimg.cn/direct/cb65c1543f4b49f88f5e3610e6158f66.jpeg
图2-1 预处置惩罚
2.3 Hello的预处置惩罚结果剖析

源代码文件经预处置惩罚后从25行拓展成了3062行,对比hello.i文件和stdio.h文件,可见#include<stdio.h>命令告诉预处置惩罚器读取系统当前环境变量下文件 stdio.h 的内容,并把它直接插入程序文本中;stdio.h文件中同样使用了#include命令来引入其他库文件。因此最终所有被引用的头文件都被插入到了hello.i文件中,使源代码拓展成了3062行,而且源代码中的main函数处于文件最末尾。
https://img-blog.csdnimg.cn/direct/d34c69c84fd64bd7b27f198a32bf4e8e.jpeghttps://img-blog.csdnimg.cn/direct/29d850d702fe4e29afdce5664d99d8f4.jpeg
图2-2 hello.i文件                                    图2-3 stdio.h文件
https://img-blog.csdnimg.cn/direct/55b21dbc567b4af9992abab95fec3ea6.jpeg
图2-4 hello.i文件main函数部分
源代码开头的注释在预处置惩罚之后被删除。
2.4 本章小结

第2章重要阐述了C/C++编程中预处置惩罚的机制、作用及实践应用。预处置惩罚是编译前的准备阶段,通过预处置惩罚器扫描源代码,实行头文件包罗、宏更换、条件编译和行控制等操作,为后续的编译打底子。具体包罗四个关键阶段:字符更换、行拼接、单词化和宏指令处置惩罚。预处置惩罚具有重要意义,如引入外部库文件、实当代码条件编译、简化代码编写(通过宏定义)、以及字符串化和连接Token等。
在Ubuntu环境下,通过特定命令实行预处置惩罚操作,将原始源代码文件(如hello.c)转换为颠末预处置惩罚的文件(如hello.i)。以"hello"程序为例,预处置惩罚将一个简单的源文件扩展为包罗所有依赖头文件内容的更大型文件,源代码的#included内容被实际库文件stdio.h的代码更换,源文件的注释也被移除。这一过程中,stdio.h内部的其他库文件引用同样被递归包罗进来,最终形成了一个包罗全部依赖项的、大幅扩展的源文件版本,清晰展示了项目中所有直接和间接包罗的代码布局,以及主函数在团体预处置惩罚后代码中的位置。
(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

概念:
编译器(ccl)将文本文件 hello.i 翻译成文本文件 hello.i,它包罗一个汇编语言程序。
作用:编译将差别高级语言翻译成通用的输出语言。
编译过程可分为6步:扫描(词法分析)、语法分析、语义分析、源代码优化、代码天生、目标代码优化。
1.词法分析:扫描器(Scanner)将源代的字符序列分割成一系列的暗号(Token)。lex工具可实现词法扫描。
2.语法分析:语法分析器将暗号(Token)产生语法树。yacc工具可实现语法分析(yacc: Yet Another Compiler Compiler)。
3.语义分析:静态语义(在编译器可以确定的语义)、动态语义(只能在运行期才气确定的语义)。
4.源代码优化:源代码优化器(Source Code Optimizer),将整个语法书转化为中心代码(Intermediate Code)(中心代码是与目标机器和运行环境无关的)。中心代码使得编译器被分为前端和后端。编译器前端负责产气愤器无关的中心代码;编译器后端将中心代码转化为目标机器代码。
5.目标代码天生:代码天生器(Code Generator).
6.目标代码优化:目标代码优化器(Target Code Optimizer)。
3.2 在Ubuntu下编译的命令

https://img-blog.csdnimg.cn/direct/71c9f7a738bb47f38e93e03f0cd59d3e.jpeg
图3-1 Ubuntu系统下代码编译
3.3 Hello的编译结果剖析

3.3.1数据
       ①常量:if语句if (argc != 4)中的常量4保存在.text中,作为汇编指令的常量。
    movl    %edi, -20(%rbp)
    movq    %rsi, -32(%rbp)
    cmpl    $4, -20(%rbp)
    je  .L2
同理下列for循环语句中出现的常量0、8、1、2、3也被存储在.text节中。
for (i = 0; i < 8; i++)
    {
        printf("Hello %s %s\n", argv, argv);
        sleep(atoi(argv));
}
printf()、scanf()函数中的字符串被存储在.rodata节中,
printf("用法: Hello 2022112720 xhw 18307050214 4!\n");
printf("Hello %s %s %s\n",argv,argv,argv);

.LC0:
.string  "\347\224\250\346\263\225: Hello 2022112720 xhw 18307050214 4\357\274\201"
.LC1:
   .string  "Hello %s %s %s\n"
       ②变量
       Ⅰ.全局变量
初始化的全局变量储存在.data节,它的初始化不需要汇编语句,而是直接完成的。
       Ⅱ.局部变量
       局部变量存储在寄存器或栈中。程序中的局部变量i定义如下:
      int i;
i在汇编代码中的初始语句如下:
.L2:
       movl    $0, -4(%rbp)
      jmp .L3
其中i被赋值0,并保存在栈%rsp-4的位置上。

3.3.2算术操作
在for循环中,对变量i使用了自加++操作符:
for (i = 0; i < 8; i++)
因此在每次循环结束后,使用addl指令对变量i进行一次自加操作,栈上存储变量i的值加1,即
   addl    $1, -4(%rbp)

3.3.3关系操作和控制转移
程序中的if语句判断传入参数argc是否等于4:
    if (argc != 4)
    {
        printf("用法: Hello 2022112720 xhw 18307050214 4!\n ");
        exit(1);
    }
因此在汇编代码使用指令cmpl判断操作数是否相等,当两个操作数不相等时跳转指令je将程序跳转到指定所在;
    cmpl    $4, -20(%rbp)
    je  .L2

程序中的for循环中的判断i是否小于8来控制循环的停止:
   for (i = 0; i < 8; i++)
因此在汇编代码中使用指令cmpl判断操作数大小关系,当后一个操作数的值小于等于前一个跳转指令jle将程序跳转到指定所在;
.L3:
       cmpl    $7, -4(%rbp)
      jle .L4

3.3.4数组、指针和布局操作
指针数组char *argv[]作为参数传入main函数:
int main(int argc, char *argv[]) {…}
在argv数组中,argv指向输入程序的路径和名称,argv和argv分别表现两个字符串。
char* 数据类型占8个字节,故argv数组中相邻两项的所在相差8;在汇编语言中,程序通过所在%rsi-8和%rax-16,分别调用argv和argv。
.LFB6:
      movq    %rsp, %rbp
      .cfi_def_cfa_register 6
      subq    $32, %rsp
      movl    %edi, -20(%rbp) //argc存储在%edi
movq    %rsi, -32(%rbp) //argv存储在%rsi
.L4:
      leaq    .LC1(%rip), %rdi
      movl    $0, %eax
      call    printf@PLT

3.3.5函数操作
①main函数
源代码:
int main(int argc, char *argv[])
汇编代码:
main:
.LFB6:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $32, %rsp
    movl    %edi, -20(%rbp)
movq    %rsi, -32(%rbp)

参数传递:传入参数argc和argv[],分别用寄存器%rdi和%rsi存储
函数调用:由系统启动函数调用
函数返回:将调用寄存器%eax置0并返回

②printf函数:
源代码:
printf("用法: Hello 2022112720 xhw 18307050214 4!\n ");
printf("Hello %s %s\n", argv, argv);
汇编代码:
.LFB6:
    cmpl    $4, -20(%rbp)
    je  .L2
    leaq    .LC0(%rip), %rdi
    call    puts@PLT

.L4:
    movq    -32(%rbp), %rax
    addq    $16, %rax
    movq    (%rax), %rdx
    movq    -32(%rbp), %rax
    addq    $8, %rax
    movq    (%rax), %rax
    movq    %rax, %rsi
    leaq    .LC1(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT

参数传递:call puts@PLT时传入参数字符串所在;call  printf时传入参数 argv和argc的所在
函数调用:分别在if循环条件和for循环条件下由call指令调用函数

③exit函数:
源代码:
exit(1);
汇编代码:
.LFB6:
    movl    $1, %edi
    call    exit@PLT
参数传递:将寄存器%edi置1,作为参数传入exit函数
函数调用:在if条件下,由call指令调用函数

④sleep函数:
源代码:
    sleep(atoi(argv));
汇编代码:
.L4:
    movq    -32(%rbp), %rax
    addq    $24, %rax
    movq    (%rax), %rax
    movq    %rax, %rdi
    call    atoi@PLT
    movl    %eax, %edi
call    sleep@PLT

参数传递:通过寄存器%edi传入参数atoi(argv),
函数调用:在for循环条件下,由call指令调用函数

⑤getchar函数:
源代码:
    getchar();
汇编代码:
.L3:
call    getchar@PLT

函数调用:由call指令调用函数
3.4 本章小结

第3章深入探讨了编译过程,从概念、作用到具体步调,并以Ubuntu系统下的实践操作及“hello”程序的编译结果分析为例加以说明。
编译是将高级语言程序转换为低级机器语言的关键步调,通过编译器(如ccl)实现。其核心作用在于语言间的翻译,将便于人类明白的高级语言代码转换为计算机可实行的机器代码。该过程细化为六个阶段:词法分析、语法分析、语义分析、源代码优化、代码天生、目标代码优化,涉及工具如lex和yacc等。这一系列步调不仅剖析代码布局,还进行性能优化,确保天生的代码高效且目标平台兼容。
“hello”程序编译后包罗以下各类数据:


[*]数据:常量存储在.text节,字符串常量位于.rodata节,全局变量初始化数据位于.data,而局部变量存储于栈中。
[*]算术与逻辑操作:展示了循环变量的初始化与迭代过程中的自增操作如何映射到汇编代码中的addl指令。
[*]控制流:表明白条件语句和循环控制如何通过比较指令(如cmpl)和跳转指令(如je, jle)实现。
[*]内存与指针操作:分析了如何通过指针访问数组元素,如argv数组的使用及其在汇编层面的所在计算。
[*]函数操作:详述了重要函数如main、printf、exit、sleep及getchar的调用机制,包罗参数传递、调用约定及返回处置惩罚,展现了从高级语言函数到汇编调用的映射细节。
综上,本章理论性地介绍了编译原理及步调,分析了编译过程对源代码的具体转换效果。
(第3章2分)

第4章 汇编

4.1 汇编的概念与作用

概念:
汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中,hello.o是一个二进制文件。

作用:
       将汇编代码转换成二进制机器代码。
4.2 在Ubuntu下汇编的命令

https://img-blog.csdnimg.cn/direct/9c5cf81116f74c508a01fb5f7bea3496.jpeg
图4-1 Ubuntu系统下汇编命令
4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。

4.3.1 ELF头

ELF 头以一个 16 字节的序列开始,这个序列描述了天生该文件系统下的字的
大小以及一些其他信息。ELF 头剩下的部分包罗帮助链接器语法分析和表明目标
文件的信息:包罗 ELF 头的大小、目标文件的类型、机器类型、节头部表的文件
偏移,以及节头部表中条目标大小和数量。ELF 头具体内容如下:
https://img-blog.csdnimg.cn/direct/5faca4a8bba7433c84391ae4f5eb18aa.jpeg
图4-2 ELF 头
4.3.2 节头表
节头表描述了.o 文件中每一个节出现的位置,大小,目标文件中的每一个节都有一个固定大小的条目。具体内容如下图所示:
https://img-blog.csdnimg.cn/direct/3638337fb00e463e841a8af2f44f32e8.jpeg
图4-3 节头表
4.3.3 重定位节
重定位节中包罗了在代码中使用的一些外部变量等信息,在链接的时候需要根据重定位节的信息对这些变量符号进行修改。链接的时候链接器会根据重定位节的信息对外部变量符号决定选择何种方法计算精确的所在,通过偏移量等信息计算出精确的所在。本程序需要重定位的信息有:.rodata 中的模式串,puts,exit,printf,slepsecs,sleep,getchar 这些符号同样需要与相应的所在进行重定位。重定位节具体内容如下图所示:
https://img-blog.csdnimg.cn/direct/a7e675b550e94e3f803e4ba2033a45aa.jpeg
图4-4 重定位节
4.3.4 符号表
.symtab 是一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。例如本程序中的 getchar、puts、exit 等函数名都需要在这一部分表现,具体内容如下图所示:
https://img-blog.csdnimg.cn/direct/9edb00bd915b4697a475801e18467892.jpeg
图4-5 符号表
4.4 Hello.o的结果剖析

由objdump命令得到反汇编代码如下:
https://img-blog.csdnimg.cn/direct/b75628729ec54ed0a97e8d18664dfc69.png
图4-6 反汇编代码
分析 hello.o 的反汇编代码,并与hello.s 进行对照分析:
可以发现有如下几点差别:
1. 进制差别:hello.s 文件颠末反汇编之后对于数字的表现是十进制的,而 hello.o 文件颠末反汇编之后数字的表现是十六进制的。
2. 分支转移:对于条件跳转,hello.s 反汇编中给出的是段的名字,例如.L2 等来表现跳转的所在,而 hello.o 由于已经是可重定位文件,对于每一行都已经分配了相应的所在,因此跳转命令后跟着的是需要跳转部分的目标所在。
3. 函数调用:hello.s 中,call 指令后跟的是需要调用的函数的名称,而 hello.o 中,汇编代码中 call 指令使用的是 main 函数的相对偏移所在。同时可以发现在hello.o 反汇编代码中调用函数的操作数都为 0,即函数的相对所在为 0,因为再链接天生可实行文件后才会天生其确定的所在,所以这里的相对所在都用 0取代。
4.5 本章小结

颠末编译器的.s文件颠末汇编器之后,天生了一个可重定位目标 elf 格式,为下一步链接做好了准备。
汇编语言与机器语言之间的映射通常由汇编器(Assembler)完成。汇编器读取汇编语言编写的源代码,将其转换为机器语言指令,这个过程称为汇编。天生的机器语言指令可以直接被计算机硬件实行。
汇编语言固然靠近机器语言,但仍旧提供了肯定程度的抽象,使得程序员不必直接处置惩罚二进制代码。这种抽象使得汇编语言比机器语言更易于编写和维护,但仍旧需要对底层硬件有较深的明白。
(第4章1分)

第5章 链接

5.1 链接的概念与作用

概念:
链接(linking)是将各种代码和数据片断网络并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并实行。链接可以实行于编译时(compile time),也就是在源代码被翻译成机器代码时;也可以实行于加载时(load time),也就是在程序被加载器(loader)加载到内存并实行时;甚至实行于运行时(run time),也就是由应用程序来实行。链接是由叫链接器(linker)的程序自动实行的。
作用:
链接使得分离编译(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的格式

5.3.1 ELF头

ELF 头以一个 16 字节的序列开始,这个序列描述了天生该文件系统下的字的
大小以及一些其他信息。ELF 头剩下的部分包罗帮助链接器语法分析和表明目标
文件的信息:包罗 ELF 头的大小、目标文件的类型、机器类型、节头部表的文件
偏移,以及节头部表中条目标大小和数量。ELF 头具体内容如下:

https://img-blog.csdnimg.cn/direct/f59f376362684508af7b88e54e5c4452.png
图5-1 ELF头
5.3.2 节头
描述了各个节的大小、偏移量和其他属性。链接器链接时,会将各个文件的雷同段合并成一个大段,而且根据这个大段的大小以及偏移量重新设置各个符号的所在。详细内容如下:

https://img-blog.csdnimg.cn/direct/09a66df127374f889abdb765364ac1a7.png
图5-2 节头
5.4 hello的虚拟所在空间

使用edb加载hello,查看本进程的虚拟所在空间各段信息,并与5.3对照分析说明。  
使用 edb 打开 hello 可实行文件,可以在 edb 的 Data Dump 窗口看到 hello 的虚拟所在空间分配的环境,具体内容截图如下:
https://img-blog.csdnimg.cn/direct/8371eb15593a4a8789659687956b62e3.png
图5-3 数据转储视图
该段程序的起始所在为 0x401000 ,具有 ELF 的标识,可以判断从可实行文件时加载的信息。其中 PHDR 保存的是程序头表;INTERP 保存了程序实行前需要调用的表明器;LOAD纪录程序目标代码和常量信息;DYNAMIC 储存了动态链接器所使用的信息;NOTE 纪录的是一些辅助信息;GNU_EH_FRAME 保存异常信息;GNU_STACK 使用系统栈所需要的权限信息;GNU_RELRO 保存在重定位之后只读信息的位置。
5.5 链接的重定位过程分析

https://img-blog.csdnimg.cn/direct/e8c92815a3d4463bb721bb42a49e5677.png
图5-4 反汇编代码
hello 与 hello.o 的差别:

[*]在链接过程中,hello 中加入了代码中调用的一些库函数,例如 getchar,puts,printf,等,同时每一个函数都有了相应的虚拟所在。例如 exit 函数的虚拟所在如下图:
https://img-blog.csdnimg.cn/direct/c3d65f8370ae4e72b9ee0097c4e0e06c.png

[*]对于全局变量的引用,由于 hello.o 中还未对全局变量进行定位,因此 hello.o 中用 0 加上%rip 的值来表现全局变量的位置,而在 hello 中,由于已经进行了定位,因此全局变量的的值使用一个确切的值加上%rip 表现全局变量的位置。
https://img-blog.csdnimg.cn/direct/2159f8899ee94af5af30af151cf22105.png

[*]hello 中增加了.init 和.plt 节,和一些节中定义的函数
[*]hello 中无 hello.o 中的重定位条目,而且跳转和函数调用的所在在 hello 中都变成了虚拟内存所在。这是由于 hello.o 中对于函数还未进行定位,只是在.rel.text 中添加了重定位条目,而 hello 进行定位之后自然不需要重定位条目。
[*]所在访问:在链接完成之后,hello 中的所有对于所在的访问或是引用都调用的是虚拟所在所在。例如下图中条件跳转代码所示:
https://img-blog.csdnimg.cn/direct/9fa9eb68faf94b06b84a50309f323128.png

链接的过程:
链接重要分为两个过程:符号剖析和重定位。
符号剖析:目标文件定义和引用符号,符号剖析将每个符号引用和一个符号定义关联起来。
重定位:编译器和汇编器天生从 0 开始的代码和数据节。链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得它们指向这个内存位置。链接器使用汇编器产生的重定位条目标详细指令,不加甄别地实行这样的重定位。

5.6 hello的实行流程

hello程序运行流程:

[*]动态链接准备:   

[*].dynsym 和 .dynstr 节:动态链接器使用这些节来剖析外部符号(如库函数)的名称和所在。例如,hello!.dynsym+exd80x00000000004004cc 指向动态符号表中的某个条目,.dynstr+0x5c 提供了符号名称。
[*].gnu.version 和 .rela.dyn 节:用于版本控制和动态重定位,确保精确版本的库函数被调用,而且在程序加载时修正代码和数据的所在。

[*]程序初始化与清理:   

[*].init 和 .fini 节:包罗程序启动前需要实行的初始化代码和程序退出前的清理代码。例如,hello!.init 和 hello!fini 分别代表这些阶段的入口。

[*]程序入口点与动态链接:   

[*].plt(Procedure Linkage Table):包罗对外部函数(如 puts, printf, getchar, atoi, exit, sleep)的跳转桩代码,用于动态链接。例如,hello!putsoplt 指向 puts 函数的PLT条目。
[*].got(Global Offset Table)和 .got.plt:用于存储动态链接符号的所在,以及动态链接过程中的所在剖析。

[*]数据与只读数据段:   

[*].data, .bss, .rodata:分别存储初始化的全局变量、未初始化的全局变量和只读数据(如字符串常量)。hello!.rodata 存放程序中的只读数据。

[*]运行时支持:   

[*]libc csu init 和 libc csu fini:C运行时库的初始化和清理函数,负责设置C运行环境和清理工作。
[*]do global dtors aux:辅助函数,用于实行全局析构函数(destructor),在程序退出时清理全局对象。

[*]主程序实行:   

[*]hello!main:程序的主函数入口点,从这里开始实行程序的重要逻辑。

[*]动态库处置惩罚:   

[*]deregister_tm_clones 和 register_tm_clones:与线程局部存储(TLS, Thread Local Storage)和析构函数注册有关,用于线程安全的全局变量管理。
[*]do global dtors aux fini:在程序结束时实行全局析构函数的辅助函数。

[*]程序结束与资源开释:   

[*].fini_array:包罗程序退出前需要实行的清理函数指针数组。

https://img-blog.csdnimg.cn/direct/da6e7c0fd07b412b9bba06b517802e65.png
图5-5 hello文件运行流程
子函数名和所在:
401000 <_init>
401020 <.plt>
401030 <puts@plt>
401040 <printf@plt>
401050 <getchar@plt>
401060 <atoi@plt>
401070 <exit@plt>
401080 <sleep@plt>
401090 <_start>
4010c0 <_dl_relocate_static_pie>
4010c1 <main>
401150 <__libc_csu_init>
4011b0 <__libc_csu_fini>
4011b4 <_fini>
5.7 Hello的动态链接分析

当程序调用一个由共享库定义的函数时,由于编译器无法预测这时候函数的所在是什么,因此这时,编译系统提供了延迟绑定的方法,将过程所在的绑定推迟到第一次调用该过程时。通过 GOT 和过程链接表 PLT 的协作来剖析函数的所在。在加载时,动态链接器会重定位 GOT 中的每个条目,使它包罗精确的绝对所在,而PLT 中的每个函数负责调用差别函数。
在elf文件中可以找到:
   .got              PROGBITS         0000000000403ff0  00002ff0
       0000000000000010  0000000000000008  WA       0     0     8
   .got.plt          PROGBITS         0000000000404000  00003000
       0000000000000048  0000000000000008  WA       0     0     8
 0x0000000000000003 (PLTGOT)             0x404000
进入edb查看:
https://img-blog.csdnimg.cn/direct/46e275cf514340c88920fba3aeb7b70e.png
图5-6 初始化之前数据转储
https://img-blog.csdnimg.cn/direct/62297976ddc94e72881a9fc84ec36c11.png
图5-7 hello初始化之后数据转储
运行之后发现,0x403ff0之后的部分都发生了较大变化,几处为0的位置被添补上了相应的所在。这是程序开始运行,调用动态链接器的结果。说明此时got表已经通过动态链接发挥了作用。
5.8 本章小结

第5章重要探讨了程序链接的概念、过程及其在Ubuntu系统下的实践。链接是将单独编译的代码和数据片断合并成一个可实行文件的过程,由链接器实行,支持分离编译和模块化编程。在Ubuntu下,使用ld命令可以完成链接操作。链接产生的可实行文件,如hello,遵循ELF格式,含有描述文件布局的头部和节信息,以及虚拟所在空间布局,用于指导程序的加载与实行。
链接的核心机制包罗符号剖析和重定位,前者将符号引用绑定到其定义,后者调整代码和数据的所在引用。动态链接进一步允许程序在运行时剖析和绑定外部库函数,通过GOT和PLT实现。hello程序从初始化、主函数实行到动态库调用、最终资源开释的整个生命周期,展示了链接在程序实行流程中的具体作用,特别是动态链接在所在剖析和库函数调用中的灵活性和效率。
(第5章1分)

第6章 hello进程管理

6.1 进程的概念与作用

进程是操作系统中的一个核心概念,代表着正在实行中的程序的一个实例,是程序实行的动态实体。它是对运行时程序的一个抽象,不仅包罗了程序的指令聚集,还涉及到了程序实行时所需的资源,如内存、打开的文件以及处置惩罚器状态等。
进程的重要作用表现在以下几个方面:

[*]程序实行的载体:每当用户通过shell命令启动一个程序时,操作系统就会创建一个新的进程,为该程序分配须要的资源,并在其上下文中实行这个程序。这意味着,纵然同时运行多个程序,每个程序都会在一个独立的进程中运行,保证了程序间的隔离性。
[*]资源管理和隔离:每个进程都拥有自己独立的所在空间,这就为应用程序营造了一个假象,仿佛它独自占据整个内存系统,这种私有所在空间的设定,确保了差别进程之间的数据不会相互干扰,增强了系统的稳定性和安全性。
[*]逻辑控制流的独立性:进程为应用程序提供了一个逻辑上的控制流,使其看起来好像独占处置惩罚器一样实行,纵然在多任务环境中,每个进程都能顺序地实行自己的指令序列,操作系统通过时间片轮转、优先级调理等方式在多个进程间快速切换,给予用户并行实行的错觉。
[*]促进多任务处置惩罚:通过创建和管理多个进程,操作系统可以或许实现多任务处置惩罚,即同时运行多个程序或同一程序的多个实例。这对于进步系统效率、相应用户哀求以及实现复杂的应用场景至关重要。
[*]应用程序扩展功能:应用程序自身也可以使用进程机制来创建子进程,实行额外的任务或运行其他程序。这种本事使得软件计划更加灵活,可以或许实现更复杂的功能,比如配景处置惩罚、并发实行任务或实现服务器的并发处置惩罚本事。
总之,进程是操作系统中程序实行的基本单元,它通过提供独立的实行环境、资源管理和控制流抽象,支撑了当代计算中多任务并行、资源共享和程序隔离的需求,是明白和计划复杂软件系统不可或缺的一部分。
6.2 简述壳Shell-bash的作用与处置惩罚流程

作用



[*]表明命令:将用户输入的文本命令剖析为操作系统可以实行的形式。
[*]连接用户与操作系统:作为用户界面,吸收用户输入,并调用操作系统服务来实行命令。
[*]管理进程和环境:负责创建、监控和销毁进程,同时管理环境变量,控制作业的前台和配景实行。
处置惩罚流程


[*]分词与元字符处置惩罚:Bash首先分析输入命令,识别出如空格、制表符、分号等元字符,将命令分割成一个个token(单词)。
[*]关键字识别:检查这些token是否为Bash的保留关键字,如if、for等,用于构建控制布局。
[*]别名更换:根据用户自定义的别名表(aliases),将命令首单词更换为别名所指的命令,之后重新进行分词。
[*]路径展开与变量更换:将波浪线~展开为用户的家目录路径,更换所有以$开头的变量为它们的值。
[*]命令更换:识别并实行$(command)形式的命令更换,将命令的输出插入到当前位置。
[*]算术表达式计算:计算$(expression)形式的算术表达式,并更换为计算结果。
[*]IFS分割:使用内部字段分隔符(IFS,默以为空格、制表符和换行符)进一步分割命令行,准备后续处置惩罚。
[*]通配符展开:处置惩罚星号(*)、问号(?)和方括号([])等通配符,匹配文件名。
[*]去除注释与命令查找:删除处置惩罚过程中的注释,然后按照内置命令、用户定义的函数、脚本文件的顺序查找命令。
[*]输入输出重定向初始化:根据命令中的<、>、|等符号设置输入输出重定向规则。
[*]命令实行:完成所有预处置惩罚后,最终实行命令。假如是外部命令或脚本,Bash会根据PATH环境变量查找可实行文件,并通过fork-exec机制创建新的进程来实行。
Bash的这一系列处置惩罚流程确保了命令的灵活剖析和高效实行,同时也为用户提供了一个强大的交互环境,支持复杂的脚本编程和系统管理任务。
6.3 Hello的fork进程创建过程


[*]用户命令输入与剖析:当用户在shell中输入命令./hello实行hello程序时,shell首先会对这条命令进行剖析,检查是否为内部命令或别名。由于hello通常是一个外部命令(即可实行文件),shell接下来会准备创建一个新的进程来运行这个程序。
[*]fork系统调用:为了创建新进程,shell会调用操作系统提供的fork系统调用。fork是Unix/Linux系统中最常用的进程创建函数,其重要作用是从现有进程中创建一个险些完全雷同的副本,即子进程。这个过程是通过复制父进程的内存空间(包罗代码段、数据段、堆、栈等)、打开的文件描述符、信号处置惩罚方式等资源来实现的。值得注意的是,只管资源副本是共享的,但父子进程各自拥有独立的视图,任何一方的修改都不会直接影响另一方,除非涉及共享内存或文件等特殊环境。
[*]资源复制与独立性:通过fork产生的子进程,除了PID(进程ID)之外,与父进程险些完全雷同,拥有自己独立的所在空间(固然初始时内容雷同)。这意味着子进程可以自由地修改其内存空间而不影响父进程,包罗数据段的修改、堆的扩展或栈的增长等。同时,子进程继续了父进程的打开文件描述符,意味着它可以读写父进程中打开的文件,但各自的操作是独立的,不会相互干扰。
[*]fork的返回值:fork调用在父进程中返回新创建子进程的PID,而在子进程中返回0。这是区分父子进程的一个关键标志,父进程可以依据返回的PID来管理子进程,如通过wait或waitpid系统调用来等待子进程结束。子进程通过返回值0知晓自己是新创建的进程,并据此实行特定的初始化逻辑或直接实行下一步操作,如实行hello程序。
[*]实行hello程序:在子进程中,接下来可能紧接着实行execve系统调用来覆盖当前进程的内存空间,加载并运行hello程序。这一步调会更换掉子进程当前的所在空间内容,包罗代码、数据等,用hello程序的内容取而代之,从而开始实行hello程序的代码。
综上所述,通过fork系统调用,hello程序从一个父进程创建出了一个与之相似但独立的子进程,这个子进程随后通过实行execve等操作,加载并实行实际的hello程序代码,实现了程序的启动和运行。
6.4 Hello的execve过程

exceve 函数在当前进程的上下文中加载并运行一个新程序。exceve 函数加载
并运行可实行目标文件,并带参数列表和环境变量列表。只有当出现错误时,exceve才会返回到调用程序。所以,与 fork 一次调用返回两次差别,在 exceve 调用一次并从不返回。当加载可实行目标文件后,exceve 调用启动代码,启动代码设置栈,将可实行目标文件中的代码和数据从磁盘复制到内存中,然后通过跳转到程序的第一条指令或入口点来运行该程序,由此将控制传递给新程序的主函数。
6.5 Hello的进程实行

逻辑控制流与时间片

在多任务操作系统中,每个进程都有自己的逻辑控制流,即一系列程序计数器(PC)值的序列,指示着程序的实行顺序。由于处置惩罚器资源有限,多个进程通过时间片轮转的方式共享CPU,即每个进程实行一小段时间后被停息,让给下一个进程实行。因此,hello程序的实行不是连续的,而是在其分配的时间片内运行一部分,然后可能被其他进程打断。
用户态与核心态的转换



[*]用户态:在用户态下,hello程序实行通例的指令,不能直接访问系统资源或实行特权指令。当程序需要系统服务(如I/O操作、内存分配或进程间通讯)时,会通过系统调用进入核心态。
[*]核心态:当hello程序通过调用sleep函数哀求休眠时,它通过系统调用从用户态切换到核心态。在核心态下,程序可以实行任何指令,包罗访问内核代码和数据。sleep函数的实行会导致当前进程被挂起,并设置一个定时器,然后内核会调理另一个进程运行。
上下文切换

在hello程序实行sleep函数并被挂起时,内核会保存当前进程的上下文,包罗程序计数器、寄存器、栈信息等,这是为了未来可以或许规复实行该进程时,可以或许从之前停止的地方继续实行。上下文切换过程如下:

[*]保存当前进程上下文:内核保存hello进程的实行状态,包罗用户栈、内核栈、寄存器状态等。
[*]选择下一个进程:调理器根据肯定的计谋选择下一个要运行的进程。
[*]规复新进程上下文:加载新选中进程的上下文,包罗设置程序计数器到新进程的实行点,规复寄存器状态等。
[*]实行新进程:处置惩罚器开始实行新进程的指令。
https://img-blog.csdnimg.cn/direct/d8682188db9b4fad8ba76ab64805e32f.png
图6-1 进程上下文切换
hello的实行过程概览


[*]初始实行:hello程序开始在用户态下实行,控制流按照程序的逻辑进行。
[*]调用sleep:当实行到sleep函数时,通过系统调用进入核心态,哀求操作系统挂起当前进程一段时间。
[*]上下文切换:内核保存hello进程的上下文,根据调理计谋选择另一个进程实行,并加载其上下文。
[*]等待与唤醒:在hello指定的休眠时间结束后,系统通过停止或其他机制唤醒hello进程,并进行上下文切换,规复hello的实行。
[*]继续实行:hello程序在用户态下规复实行,继续实行sleep调用之后的代码。
通过这一系列过程,hello程序展现了当代操作系统中进程如何通过系统调用、上下文切换和调理机制,与内核协同工作,实现时间分享和资源的有效管理。
6.6 hello的异常与信号处置惩罚

正常运行状态:
https://img-blog.csdnimg.cn/direct/d87a731a7b6341939c89e1aee409015c.jpeg
图6-2 正常运行状态
异常类型:

类别
原因
异步/同步
返回举动
停止
来自I/O设备的信号
异步
总是返回到下一条指令
陷阱
故意的异常
同步
总是返回到下一条指令
故障
潜伏可规复的错误
同步
可能返回到当前指令
停止
不可规复的错误
同步
不会返回
处置惩罚方式:
https://img-blog.csdnimg.cn/direct/0b5cb2d20ca449e5a7d7cc29ded60dfa.pnghttps://img-blog.csdnimg.cn/direct/eaf9421dd1a040af97fbddb49a863ae6.png
图6-3 停止处置惩罚方式                          图6-4 陷阱处置惩罚方式
https://img-blog.csdnimg.cn/direct/4dd0d1ee198e4a6db0f604cafa7d4ab1.png https://img-blog.csdnimg.cn/direct/6d1a3e608e6d43c2942ea971f4d8aa1c.png
图6-5 故障处置惩罚方式                          图6-6 停止处置惩罚方式
不停乱按:将屏幕的输入缓存到缓冲区。乱码被以为是命令,不影响当前进程的实行。
https://img-blog.csdnimg.cn/direct/ebe24a443bad45d39c0277b8993ddaa2.jpeg
图6-7 程序运行时不停乱按输出结果
按下 Ctrl-Z:程序运行时按 Ctrl-Z,这时,产生停止异常,它的父进程会吸收
到信号 SIGSTP 并运行信号处置惩罚程序,然后便发现程序在这时被挂起了,并打印了相干挂起信息。
https://img-blog.csdnimg.cn/direct/944cdda92d6045528d60842912d45716.jpeg
图6-8 程序挂起信息
Ctrl-Z 后运行 ps:打印出了各进程的 pid,可以看到之前挂起的进程 hello。
https://img-blog.csdnimg.cn/direct/368c408f44324b26b2cd8e908644a5ba.jpeg
图6-9 ps命令
Ctrl-Z 后运行 jobs:打印出了被挂起进程组的 jid,可以看到之前被挂起的 hello,
以被挂起的标识 Stopped。
https://img-blog.csdnimg.cn/direct/83a28ff6d4e64a7ea6f94ed33eec552f.jpeg
图6-10 jobs命令
Ctrl-Z 后运行 pstree:可看到打印出的信息,
https://img-blog.csdnimg.cn/direct/0bf02da5da5a467bbfb570f3d3baf5c7.jpeg
图6-10 pstree命令
Ctrl-Z 后运行 fg:因为之前运行 jobs 是得知 hello 的 jid 为 1,那么运行 fg 1 可
以把之前挂起在配景的 hello 重新调到前台来实行,打印出剩余部分,然后输入 hello回车,程序运行结束,进程被回收。
https://img-blog.csdnimg.cn/direct/c67e585bdec845d88e8060644aff685b.jpeg
图6-11 fg命令
Ctrl-Z 后运行 Kill:重新实行进程,可以发现 hello 的进程号为 34230,那么便
可通过 kill -9 34230 发送信号 SIGKILL 给进程 34230,它会导致该进程被杀死。然后再运行 ps,可发现已被杀死的进程 hello。
https://img-blog.csdnimg.cn/direct/825fce8623cf444483a6fe8815fb2da6.jpeg
图6-12 kill进程
6.7本章小结

第6章重要探讨了操作系统中的进程管理,以hello程序为例,深入剖析了进程的概念、创建、实行及管理的全过程。
进程底子:进程是程序实行的动态实体,负责管理资源,提供独立实行环境和控制流,实现多任务并行,增强系统稳定性和灵活性。每个进程拥有独立所在空间,确保隔离性。
Shell-Bash的作用与处置惩罚:Bash作为用户与操作系统间的桥梁,负责剖析命令、管理进程和环境。其处置惩罚流程包罗分词、关键字识别、别名更换、路径与变量展开、命令更换、算术运算、通配符处置惩罚、重定向设置等,确保命令高效实行。
进程创建(fork):通过fork系统调用,操作系统为"hello"程序创建新进程,复制父进程资源如内存空间、文件描述符等,但拥有独立PID。fork后,父子进程根据返回值(PID或0)实行差别逻辑,父进程可能通过wait监控子进程,而子进程常用execve加载并实行新程序代码。
进程实行与管理:"hello"进程在用户态实行,遇系统调用(如sleep)切换至核心态。操作系统采用时间片轮转进行多进程调理,通过上下文切换保存和规复进程状态,保证程序实行的连续性。异常与信号处置惩罚展示了进程如何相应停止、陷阱、故障和停止,以及用户通过Ctrl+Z挂起进程、使用jobs、fg、bg命令管理配景进程,以及kill命令停止进程的机制。
综上,该章全面剖析了从进程概念到其生命周期的各个环节,夸大了进程在当代操作系统中的核心作用,以及hello程序如何融入这一复杂而精妙的进程管理体系中。
(第6章1分)

第7章 hello的存储管理


[*]
[*]hello的存储器所在空间

逻辑所在

逻辑所在是程序内部使用的所在,对于"hello"程序来说,当它被编译天生目标文件(如hello.o)时,代码和数据会被分配到差别的段中,每个段内都有一个从0开始的偏移量。例如,函数调用指令中的所在就是逻辑所在,它只包罗段内的偏移量。逻辑所在不直接对应于物理内存的实际位置,需要通过所在转换才气访问实际的物理内存。
线性所在

线性所在是逻辑所在到物理所在转换过程中的一个中心层,在32位系统中,线性所在通常是一个32位的无符号整数,可以或许访问4GB的所在空间。对于"hello"程序,当它运行时,其逻辑所在会加上相应的段基所在(在段式内存管理中),天生线性所在。假如操作系统使用了分页机制,这个线性所在随后会被转换为物理所在;假如没有分页,线性所在就直接对应物理所在。
虚拟所在

虚拟所在是程序看到的所在空间,它是逻辑所在或线性所在在虚拟内存系统中的表现。虚拟所在空间允许每个进程拥有独立的所在空间,从而实现所在隔离和保护。在运行"Hello, World!"程序时,程序代码、变量、堆栈等都分配在各自的虚拟所在上,操作系统通过页表映射这些虚拟所在到实际的物理所在。
物理所在

物理所在是计算机硬件真正用来访问内存的实际所在,它出现在CPU的所在总线上并直接指向内存中的某个物理位置。在"hello"程序实行过程中,颠末段基址加逻辑所在天生线性所在后(假如有分页机制还需颠末页表转换),最终得到的就是物理所在。这个所在直接控制内存芯片上的读写操作,不受程序的逻辑布局影响。
总结来说,"hello"程序在内存中的所在空间经历了从逻辑所在到线性所在,再到最终物理所在的转换过程,这一系列转换由CPU和操作系统协作完成,确保程序可以或许在不知道实际物理内存布局的环境下精确实行。

7.2 Intel逻辑所在到线性所在的变换-段式管理

一个逻辑所在由两部分组成,段标识符,段内偏移量。段标识符是一个16位长的字段组成,称为段选择符,其中前13位是一个索引号。背面三位包罗一些硬件细节。
索引号,可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。
这内里,我们只用关心Base字段,它描述了一个段的开始位置的线性所在
全局的段描述符,放在“全局段描述符表(GDT)”中,一些局部的段描述符,放在“局部段描述符表(LDT)”中。
GDT在内存中的所在和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。给定一个完整的逻辑所在段选择符+段内偏移所在,看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其所在和大小。我们就有了一个数组了。拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,这样,它了Base,即基所在就知道了。
把Base + offset,就是要转换的线性所在了
在Intel x86架构中,逻辑所在由两部分构成:段选择符(Segment Selector)和段内偏移量(Offset)。段选择符是一个16位的字段,用于指定一个特定的段,它由三部分组成:前13位是索引号,接下来的1位是Table Indicator(T1),末了2位是哀求特权级(RPL)。
GDT和LDT是存储段描述符的数据布局。每个段描述符包罗了关于一个段的所有重要信息,包罗基所在(Base)、段限长(Limit)、访问权限等。GDT的位置和大小存储在CPU的GDTR寄存器中,而LDT的位置存储在LDTR寄存器中。
使用段选择符中的索引号,CPU在相应表(GDT或LDT)中找到对应的段描述符。这个索引号作为数组下标,帮助定位到精确的段描述符。
从找到的段描述符中取出基所在(Base)字段,它是段的起始线性所在。将段内偏移量(Offset)与基所在相加即可得到最终的线性所在。假如段限长检查通过,且访问权限验证乐成,这个计算结果就是有效的线性所在。
综上所述,Intel处置惩罚器通过段选择符在段描述符表中定位段描述符,进而获取段的基所在,并将此基所在与段内偏移量相加以产生线性所在,这是从逻辑所在到线性所在转换的核心过程。这一机制允许系统为差别的程序或进程创建独立的所在空间,增强内存管理和保护。
7.3 Hello的线性所在到物理所在的变换-页式管理

在页式管理机制下,Hello程序的线性所在转换到物理所在重要包罗以下几个步调:

[*]线性所在分解:首先,线性所在会被分成两部分:页号(Page Number, PN)和偏移量(Page Offset)。页的大小是固定的(例如4KB),页号用来索引页表中的条目,偏移量则表现在该页内的具体所在。
[*]查询页表:根据页号在页表中查找对应的条目。页表是一个数据布局,存储了页号与物理内存中页框号(Page Frame Number, PFN)的映射关系。每个页表条目还包罗了诸如访问权限、存在位等额外信息。
[*]所在合成:假如页表条目中的存在位表明该页已经在物理内存中(即未发生缺页),则将页表条目中的页框号与线性所在中的偏移量结合,形成物理所在。物理所在 = 页框号 × 页大小 + 偏移量。
[*]缺页处置惩罚:假如页表条目中的存在位为0,表现该页不在物理内存中,此时会触发缺页停止。操作系统会根据自己的页面更换算法(如LRU、FIFO等)选择一个牺牲页,将其内容写回磁盘(假如已修改),然后将所需页从磁盘读入内存,并更新页表。之后,重新实行导致缺页的指令。
https://img-blog.csdnimg.cn/direct/037c590b5db047feba7ffa1973487898.png
图7-1 页式管理流程图
页式管理的优点


[*]办理了内存碎片问题:通过非连续分配,使得程序可以在不连续的物理内存中运行,减少了内部碎片(固定分区)和外部碎片(可变分区)的问题。
[*]虚拟内存的实现:通过哀求调页或预调页技能,实现了逻辑所在空间大于物理所在空间的虚拟内存,极大地扩展了可用内存。
[*]利于多道程序计划:动态分配物理页面给需要的进程,进步了内存使用率和系统的多任务处置惩罚本事。
页式管理的缺点


[*]硬件资本增加:页式管理需要硬件支持,如所在变换机构(TLB)、缺页停止处置惩罚等,增加了系统的硬件资本。
[*]系统开销:缺页停止的处置惩罚、页表的维护等增加了系统的运行开销。
[*]可能的抖动现象:假如页面置换算法选择不妥,频繁的页面换入换出可能导致系统性能下降,表现为“抖动”现象。
[*]页面内部碎片:只管页式管理减少了外部碎片,但每个页面的末了一部分可能未被充分使用,尤其是当页面较大时,这部分浪费可能仍旧显著。
综上所述,页式管理通过动态映射虚拟所在到物理所在,有效进步了内存的使用效率和程序的运行环境,但也陪同着肯定的硬件资本和系统开销,需要通过合理的算法计划和系统优化来平衡这些利弊。对于Hello程序而言,其线性所在到物理所在的转换正是在这个框架下完成的,确保了程序指令和数据的精确加载与实行。
7.4 TLB与四级页表支持下的VA到PA的变换

       TLB是一种高速缓存,用于存储迩来访问过的页表条目(PTE, Page Table Entry),目标是加快虚拟所在到物理所在的转换过程。当CPU天生一个虚拟所在时,MMU首先会在TLB中查找是否有对应的PTE。假如找到了(称为TLB掷中),MMU可以直接使用TLB中的物理页号(PPN, Physical Page Number)与虚拟所在中的页内偏移(VPO, Virtual Page Offset)组合,敏捷计算出物理所在,这个过程只需几个时钟周期。假如未掷中(TLB Miss),则需要按照多级页表的布局去逐步查找,这个过程耗时较长,可能需要几十到几百个时钟周期。
https://img-blog.csdnimg.cn/direct/db8525102cc14f38b4b5bda0c4c16850.png
图7-2 使用k级页表进行翻译
在四级页表的计划中,虚拟所在被划分为多部分,通常包罗几级的虚拟页号(VPN, Virtual Page Number)和页内偏移。以一个简化的四级页表为例,假设虚拟所在布局如下:
VPN = {vpn1, vpn2, vpn3, vpn4}
VPO = 低位部分
转换过程如下:
MMU首先使用最高位的部分vpn1作为索引,在一级页表中查找对应的PTE。这个PTE包罗了一级页表的下一级(二级页表)的基所在。使用vpn2在从第一级得到的二级页表中查找,获取到三级页表的基所在。同样的,使用vpn3在三级页表中查找,得到四级页表的基所在。然后,使用vpn4在四级页表中找到最终的PTE,这个PTE包罗目标物理页的基所在(PPN)。末了将从四级页表中获得的PPN与虚拟所在中的页内偏移VPO相结合,形成最终的物理所在。
7.5 三级Cache支持下的物理内存访问

CPU发送一条虚拟所在,随后MMU按照上述操作获得了物理所在PA。根据cache大小组数的要求,将PA分为CT(标志位)CS(组号),CO(偏移量)。根据CS探求到精确的组,比较每一个cacheline是否标志位有效以及CT是否相等。假如掷中就直接返追念要的数据,假如不掷中,就依次去L2,L3,主存判断是否掷中,当掷中时,将数据传给CPU同时更新各级cache的cacheline(假如cache已满则要采用换入换出计谋)。
https://img-blog.csdnimg.cn/direct/eace2475181f40519e3f8cac7a9dbe70.pnghttps://img-blog.csdnimg.cn/direct/f2eba2baaefb4723a7c2c11a58bdbfd1.png
图7-3 存储器层次布局                    图7-4 使用k级页表进行翻译
7.6 hello进程fork时的内存映射

当fork函数被当前进程调用时,内核为新进程创建各种数据布局,并分配给它一个唯一的PID,同时为这个新进程创建虚拟内存。
它创建了当前进程的mm_struct、区域布局和页表的原样副本。它将两个进程中的每个页面都标志位只读,并将两个进程中的每个区域布局都标志为私有的写时复制。
当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存雷同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面。因此,也就为每个进程保持了私有空间所在的抽象概念。
7.7 hello进程execve时的内存映射

execve函数在当前进程中加载并运行包罗在可实行目标文件 hello 中的程序,用hello程序有效地替换了当前程序。加载并运行hello需要以下几个步调:
1.execve加载hello程序后,设置栈,将控制传递给hello程序的主函数。
2.删除已存在的用户区域。
3.映射私有区域:为新程序的代码、数据、bss和栈区域创建新的私有的、写时复制的区域布局。代码和数据区域被映射为hello文件中的.text和.data区。bss区域是哀求二进制零的,映射到匿名文件,其大小包罗在hello中。栈和堆区域也是哀求二进制零的,初始长度为零。
4.映射共享区域:假如 hello 程序与共享对象(或目标)链接,那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟所在空间中的共享区域内;
5.设置程序计数器(PC):execve做的末了一件事情就是设置当前进程上下文中按需换入页面: Linux采用的是按需分页(Demand Paging)计谋,意味着只有当程序真正访问到某一页数据或代码时,才会将其从磁盘上的可实行文件或页面缓存中加载到物理内存中。这样可以减少启动时的内存占用,加快程序的加载速度,尤其是在程序体积较大而实际运行时并不需要立即加载所有数据到内存的环境。
综上所述,execve系统调用通过一系列经心计划的步调,实现了新程序hello在当前进程上下文中的无缝更换和启动,确保了内存空间的合理分配、资源共享以及程序实行的顺利进行。
7.8 缺页故障与缺页停止处置惩罚

页面掷中完全是由硬件完成的,而处置惩罚缺页是由硬件和操作系统内核协作完成的:
https://img-blog.csdnimg.cn/direct/ab52483ffe6449e6883db045fb0054fb.png
图7-5 缺页停止处置惩罚
团体的处置惩罚流程:

[*]处置惩罚器天生一个虚拟所在,并将它传送给MMU
[*]MMU天生PTE所在,并从高速缓存/主存哀求得到它
[*]高速缓存/主存向MMU返回PTE
[*]PTE中的有效位是0,所以MMU出发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处置惩罚程序。
[*]缺页处置惩罚程序确认出物理内存中的牺牲页,假如这个页已经被修改了,则把它换到磁盘。
[*]缺页处置惩罚程序页面调入新的页面,并更新内存中的PTE
[*]缺页处置惩罚程序返回到原来的进程,再次实行导致缺页的命令。CPU将引起缺页的虚拟所在重新发送给MMU。因为虚拟页面已经换存在物理内存中,所以就会掷中。
7.9动态存储分配管理

动态储存分配管理使用动态内存分配器来进行。动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组差别大小的块的聚集来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可以用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配的状态,直到它被开释,这种开释要么是应用程序显式实行的,要么是内存分配器自身隐式实行的。动态内存分配重要有两种基本方法与计谋:
带边界标签的隐式空闲链表分配器

1.数据布局:块布局,每个内存块由头部、有效载荷(即用户可使用区域)、可能的添补区域(用于对齐)和尾部组成。头部和尾部包罗元数据,如块的大小和状态(已分配或空闲)。
2.隐式链表:通过头部中的信息,分配器能遍历所有块,识别哪些是空闲的,形成一个隐式的空闲链表。这种链表不额外消耗内存来维护链表指针,而是使用块本身的信息。
3.分配计谋:包罗首次适配(简单快速,但可能造成内部碎片)、下一次适配(实验均匀分配,减少碎片)和最佳适配(只管减少浪费的空间,但搜索资本较高)。
4.分裂与合并:为优化内存使用,分配器在分配时可能分割大块为小块满足需求,开释时则实验合并相邻的空闲块,减少碎片。
显式空闲链表管理

1.数据布局:与隐式链表差别,显式链表维护一个明白的数据布局(如双向链表)来纪录空闲块。每个空闲块中包罗指向前后相邻空闲块的指针。
2.上风:允许更灵活的管理计谋,如LIFO(后进先出)计谋,新开释的块直接添加到链表头部,适合短期重用模式。或者按所在排序,便于快速定位相邻块进行合并。
3.缺点:每个空闲块需额外存储链表指针,增加了内存开销。按所在排序的链表在插入新节点时可能需要较高的时间复杂度(线性时间搜索)。
7.10本章小结

本章节深入探讨了"Hello, World!"程序在存储管理方面的多个关键方面,涵盖了从逻辑所在到物理所在的转换过程、存储管理机制及其对程序实行的影响。
逻辑与线性所在介绍了程序内部使用的逻辑所在如何通过段式管理转换为线性所在,这一过程涉及到段选择符、段描述符表(GDT和LDT),以及基所在与偏移量的结合,以实现所在空间的划分和管理。
线性到物理所在的转换详细说明白页式管理机制,包罗线性所在的分解、页表查询、所在合成及缺页处置惩罚。这一过程支持虚拟内存,允许程序所在空间大于物理内存,同时有效管理内存碎片和提拔多任务处置惩罚本事。
TLB与多级页表夸大了转换后备缓冲区(TLB)在加快所在转换中的作用,以及多级页表如何支持大所在空间的高效映射,只管这以增加硬件复杂性和系统开销为代价。
物理内存访问中的Cache支持表明白CPU如何使用多级缓存体系布局加快对物理内存的访问,进一步收缩了数据访问时间。
进程的内存映射阐述了fork()和execve()系统调用如何影响进程的内存空间,包罗写时复制机制、内存区域的映射与解除映射,以及如何通过这些机制实现资源的有效共享和隔离。
缺页故障处置惩罚概述了当程序访问尚未载入内存的页面时,操作系统如何通过复杂的硬件-软件交互机制相应缺页停止,确保内存访问的连续性和程序实行的精确性。
动态存储分配管理讨论了程序运行时动态内存管理的重要性,对比了带边界标签的隐式空闲链表与显式空闲链表两种基本计谋,分析了它们在分配效率、内存碎片控制以及灵活性方面的优劣。
综上,本章通过“Hello, World!”程序为载体,全面剖析了当代操作系统中内存管理的核心原理和技能,揭示了从程序代码到硬件实行背后复杂的所在转换与资源调配机制。
(第7章 2分)

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化
在Linux中,每个I/O设备都被表现为一个文件,通常位于/dev目录下。这些特殊文件允许使用尺度的文件操作接口(如read(), write())进行设备操作。有两类重要的设备文件:块设备(如硬盘)和字符设备(如键盘)。

设备管理
UNIX I/O接口在设备管理中扮演着至关重要的脚色,它通过一套尺度化的API(应用程序编程接口)来简化和统一设备访问方式,使得用户和程序员可以或许以雷同的方式来操作差别的I/O设备。
1.抽象化:UNIX系统将所有设备视为文件,这意味着无论是物理磁盘、网络套接字还是终端设备,都可以通过文件描述符(file descriptor)来访问。这种抽象化极大地简化了编程模型,程序员可以使用尺度的open()、read()、write()、close()等函数来操作任何设备,而无需关心底层硬件的具体细节。
2.统一接口:通过提供一组通用的I/O函数,UNIX确保了对差别设备的一致性访问。这不仅低落了学习资本,也进步了代码的可移植性和可维护性。无论设备的物理特性如何,应用程序都能以雷同的方式与之交互。
3.缓冲计谋:UNIX I/O系统自动管理缓冲,如行缓冲、全缓冲或无缓冲,这取决于设备类型。这种计谋优化了读写效率,特别是在涉及慢速设备如磁盘时,可以或许减少实际I/O操作的次数,进步团体性能。
4.I/O多路复用:通过系统调用如select()、poll()和epoll(),UNIX允许一个进程监视多个文件描述符上的I/O事件,有效支持了高并发的网络服务和高效的事件驱动编程模型,这对于设备管理而言是一个强大的工具,尤其是在处置惩罚大量并发I/O哀求时。
5.异步I/O支持:固然传统UNIX I/O多为同步操作,当代UNIX变体(如Linux)也支持异步I/O(AIO),允许程序发起I/O操作后立即继续实行其他任务,而无需等待操作完成,进步了程序的相应速度和吞吐量。
UNIX I/O接口通过上述方法,为设备管理提供了一个强大且灵活的底子,使得系统可以或许高效、可靠地处置惩罚各类I/O操作,同时也让开发者可以或许以更加高效和一致的方式编写涉及设备交互的应用程序。
8.2 简述Unix IO接口及其函数

Unix IO接口:
Unix I/O接口是操作系统与外部设备间交换数据的核心机制,它通过文件描述符这一简洁而强大的抽象概念,实现了对多样化的输入输出设备的统一管理。具体而言,该接口的工作流程如下:
1.打开文件与获取文件描述符:当应用程序需要访问一个文件或设备时,通过调用如open()函数,并指定文件路径及相干标志,内核会检查权限并初始化相干数据布局,随后返回一个非负整数作为文件描述符。这个描述符成为了后续对该文件所有操作的唯一标识。
2.尺度文件描述符:在Linux shell环境中,每个新创建的进程自动关联三个尺度文件描述符,它们分别是尺度输入(文件描述符0,用于吸收输入数据)、尺度输出(文件描述符1,用于输出数据)和尺度错误(文件描述符2,用于输堕落误信息)。为了增强代码的可读性,程序员可以使用<unistd.h>头文件中定义的常量STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO来取代直接使用数字描述符。
3.文件定位:使用lseek()函数,应用程序可以或许改变当前文件的读写位置。通过设定文件偏移量至特定位置k,程序可以或许精确控制从那里开始读取或写入数据,为随机访问文件内容提供了便利。
4.读写操作:Unix I/O接口提供了基本的读写功能。读取操作(如通过read()函数)会将文件当前位置开始的n个字节数据复制到内存中,并自动更新文件位置指针到k+n。相反,写入操作(如通过write()函数)则是将内存中的n个字节数据写入到文件的当前位置k处,并同样更新文件位置指针。这样的计划既保证了操作的连续性,又便于控制数据活动的方向和范围。
5.关闭文件:完成文件操作后,应用程序必须通过调用close()函数关照内核关闭文件。这一步调不仅是资源清理的过程,内核会开释为打开文件分配的所有资源,包罗数据布局等,并将该文件描述符归还到描述符池中,以便未来重用,从而确保了系统资源的有效管理和回收。
通过上述机制,Unix I/O接口有效地封装了复杂的硬件操作细节,为应用程序提供了一套高效、灵活且统一的文件和设备访问方法,促进了跨平台的兼容性和程序的可移植性。


Unix IO函数:
Unix I/O函数构成了与文件和设备交互的底子,以下是这些核心函数的详细说明和其在设备管理中的应用:
1.open()函数(int open(const char *pathname,int flags,int perms)):此函数用于打开现有文件或创建新文件,并可设置文件的访问模式及权限。通过传递文件路径名、文件打开标志(如读、写、追加等)和权限模式(如文件创建时的权限设置)作为参数,open()为乐成操作返回一个非负的文件描述符,这是后续读写操作的关键标识。若操作失败,则返回-1。
2.close()函数(int close(int fd)):负责关闭之前由open()打开的文件描述符,开释与之相干的系统资源。调用时需提供文件描述符作为参数。乐成实行时返回0,否则返回-1,指示发生了错误。
3.read()函数(ssize_t read(int fd, void *buf, size_t count)):用于从已打开的文件或设备中读取数据到内存缓冲区。该函数需要文件描述符、指向缓冲区的指针以及要读取的字节数作为参数。返回值为实际读取的字节数,若到达文件末尾则返回0,碰到错误则返回-1。
4.write()函数(ssize_t write(int fd, void *buf, size_t count)):与read()相对应,write()负责将内存缓冲区中的数据写入到指定的文件或设备中。它同样需要文件描述符、包罗待写数据的缓冲区指针和要写入的字节数。乐成实行时返回实际写入的字节数,堕落时返回-1。
5.lseek()函数(off_t lseek(int fd, off_t offset,int whence)):提供了在文件内部定位读写位置的本事,这对于随机访问文件内容至关重要。通过指定文件描述符、偏移量和起始基准(如SEEK_SET、SEEK_CUR、SEEK_END),lseek()可以将文件指针移动到文件内的任意位置。操作乐成时,它返回新的文件偏移量;若失败,则返回-1。
这些函数共同构成了Unix I/O系统的底子框架,它们通过尺度化的接口简化了对差别类型I/O设备的操作。
8.3 printf的实现分析

vsprintf的格式化过程:
vsprintf首先吸收一个格式化字符串(如"Hello, %s, you are %d years old.")和一个va_list类型的参数列表,它指向实际的参数(如"Alice"和30)。随后vsprintf遍历格式化字符串,对于每个格式化占位符(如%s、%d),使用va_arg从va_list中取出相应类型的参数,并根据占位符类型进行格式化转换(例如,将整数转换成字符串表现)。转换后的字符直接写入缓冲区。完成所有格式化后,vsprintf返回写入缓冲区的字符总数,这可以用于后续的输出操作。

write系统调用:
1.参数准备:write函数通常需要三个参数:文件描述符(通常是尺度输出1,代表屏幕)、缓冲区的所在、以及要写的字节数(即上述vsprintf返回的长度)。这些信息被构造好,准备传递给操作系统。
2.系统调用触发:在x86架构上,传统方式是通过int 0x80指令进入内核模式,而在较新的体系布局如x86_64或ARM上,可能使用syscall指令。这一步是用户态到内核态的转变,需要保存用户态上下文并切换到内核态实行环境。
3.内核处置惩罚:操作系统内核吸收到write哀求后,会检查权限,确保调用者有权向指定的文件描述符写入。然后,内核将缓冲区的内容复制到内核空间或直接操作硬件输出。

字符表现驱动子程序:
1.ASCII到字模库转换:在字符表现的上下文中,缓冲区中的字符(ASCII码)需要转换成对应的字模(即字符的像素表现)。字模库中存储了每个字符的点阵信息,根据字体和大小差别,字模也会差别。
2.写入VRAM:转换后的字模数据,按照字符的坐标位置,逐字节或逐字写入视频RAM(VRAM)。VRAM专门用于存储图形表现的数据,包罗字符的像素信息和颜色信息。
3.RGB颜色信息处置惩罚:对于彩色表现,每个字符或像素点的颜色由红绿蓝(RGB)三原色的组合决定。VRAM中会存储每个点的RGB值,以实现彩色表现。

表现芯片与LCD表现过程:
1.刷新频率控制:表现芯片根据预设的刷新率(如60Hz),定期从VRAM中读取数据。
2.逐行扫描:表现芯片按照行列顺序读取VRAM中的数据,逐行发送到液晶表现器的各个像素点。每一行数据发送完毕后,光束会回扫到下一行的起始位置,继续这个过程。
3.信号传输:通过LVDS(低电压差分信号)或eDP(嵌入式表现端口)等接口,表现芯片将包罗RGB分量的视频信号传输到液晶面板。液晶分子根据信号调整透光程度,配合背光源,最终在屏幕上形成图像。
8.4 getchar的实现分析

异步异常 - 键盘停止的处置惩罚


[*]停止发生:当用户按下键盘上的某个键时,键盘控制器会产生一个硬件停止信号,这个信号会被CPU捕捉。在x86架构中,这通常是一个不可屏蔽停止(NMI)或可编程停止控制器(PIC)管理的停止。
[*]停止处置惩罚程序:CPU停息当前实行的任务,保存其上下文(寄存器状态等),然后跳转到停止处置惩罚程序的入口所在实行。键盘停止处置惩罚程序是操作系统的一部分,负责直接与键盘硬件交互。
[*]扫描码转换:停止处置惩罚程序读取键盘控制器提供的按键扫描码。扫描码是键盘上每个键唯一的标识,不肯定直接对应ASCII码。处置惩罚程序根据预定义的映射表将扫描码转换为相应的ASCII码或别的编码(如对于特殊键)。
[*]保存至缓冲区:转换后的ASCII码或字符被放入系统的键盘缓冲区。键盘缓冲区是一种环形缓冲区,用于暂存用户输入,直到应用程序通过读操作取走这些字符。这样计划允许用户连续输入而不必担心数据丢失,同时为应用程序提供了非阻塞的输入方式。
getchar等调用read系统函数


[*]系统调用触发:在用户空间,当程序调用如getchar()这样的函数时,实际上是发起一个系统调用,通常是read()。这个调用哀求从一个文件描述符(在本例中是尺度输入,文件描述符0)读取数据。系统调用会通过陷阱指令(如Intel架构的syscall)进入内核模式。
[*]权限检查与读取操作:内核首先验证调用者是否有权限从尺度输入读取数据。假如权限检查通过,内核检查键盘缓冲区是否为空。假如缓冲区中有数据(即用户已经按下了键),内核将数据从缓冲区中移除,并复制到用户空间提供的缓冲区中。
[*]阻塞与非阻塞举动:假如缓冲区为空(没有输入等待处置惩罚),默认环境下read调用会阻塞当前进程,直到有数据可读。但在某些配置下,getchar可以通过设置非阻塞模式避免阻塞,立即返回一个指示状态的值(如EWOULDBLOCK错误)。
[*]回车键的特别处置惩罚:通常,getchar函数会不停等待直到读取到一个完整的行,这意味着它可能会持续调用read直到碰到换行符(即回车键对应的ASCII码\n)。这种举动依赖于具体的实现和上下文,但常见于需要行输入的场景。
综上,getchar的实现涉及到操作系统对硬件停止的相应、字符编码的转换、数据的缓存管理,以及通过系统调用接口在用户空间与内核空间之间的协调,确保了用户输入可以或许高效、准确地被程序所处置惩罚。
8.5本章小结

Linux的I/O设备管理以高度抽象化和尺度化接口为核心,将所有设备视作文件处置惩罚。在/dev目录下,差别设备通过特定文件表现,如块设备(硬盘)和字符设备(键盘)。UNIX I/O接口通过文件描述符简化设备访问,提供统一API如open(), read(), write(), lseek(), 和close(),实现设备操作的抽象化、统一化访问。该接口支持缓冲计谋优化读写效率,I/O多路复用处置惩罚并发哀求,并引入异步I/O提拔相应速度。核心函数如open()开启文件访问,close()关闭文件开释资源,read()和write()分别实行读写操作,lseek()定位文件读写位置,共同构成强大的I/O系统底子。
printf的实现中,vsprintf()负责格式化字符串与参数,将格式化结果写入缓冲区。之后,write()系统调用将缓冲区内容输出,经历从用户态到内核态的转换,内核处置惩罚权限检查并实行实际的输出操作。字符表现流程涉及ASCII到字模的转换、数据写入VRAM,并通过表现芯片按刷新频率逐行读取VRAM数据,经LVDS/eDP等接口传输至LCD,展现为可见图像。
getchar功能实现中,键盘停止触发硬件与操作系统的交互,停止处置惩罚程序将按键扫描码转换成ASCII码存入键盘缓冲区。getchar通过read系统调用读取缓冲区内容,此过程可能涉及进程阻塞直至有数据可读,特别地,getchar通常等待读取到回车符作为输入结束标志。整个过程整合了硬件停止相应、数据处置惩罚、缓冲管理及用户空间与内核空间的协作,保障了输入数据的高效处置惩罚与程序的平稳运行。
(第8章1分)


结论

Hello程序经历的计算机系统过程总结如下:

[*]源代码编写:开发者使用文本编辑器(如Vim或Emacs)创建并编辑hello.c文件,这是程序的源代码形式,包罗了用C语言编写的指令序列。
[*]预处置惩罚阶段:通过调用编译器(如GCC)的预处置惩罚器,hello.c中的源代码会被处置惩罚,包罗展开宏定义、处置惩罚条件编译指令、包罗头文件等,天生扩展名为.i的纯C源代码文件(例如hello.i)。
[*]编译阶段:预处置惩罚后的文件颠末编译器进一步转换为汇编语言,这个过程将高级语言指令转换为机器可以明白的低级指令集,产生hello.s汇编代码文件。
[*]汇编阶段:汇编器将汇编代码翻译为目标代码(机器语言),天生可重定位的目标文件hello.o。每个目标文件都包罗了程序的一部分,而且包罗的是相对于其他代码段的所在引用。
[*]链接阶段:链接器负责将hello.o与须要的库文件合并,剖析所有外部符号引用,调整代码和数据段的所在,最终天生一个可实行文件hello。这个过程确保了程序可以在内存中精确地定位和实行。
[*]进程创建与管理:操作系统通过fork()系统调用为hello程序创建一个新的进程。接着,execve()系统调用加载该可实行文件到内存,并开始实行。操作系统负责调理进程,为其分配CPU时间片、内存空间(通过虚拟内存管理机制如页表、TLB和Cache),以及管理其I/O操作。
[*]存储管理:在程序实行期间,虚拟所在通过页表转换为物理所在,使用高速缓存和TLB(Translation Lookaside Buffer)加快访问。这种机制允许程序拥有独立的所在空间,同时也支持内存保护和共享。
[*]I/O与信号处置惩罚:Hello程序与用户(通过键盘、屏幕等)进行交互,操作系统管理这些硬件资源的访问,并处置惩罚任何发送给程序的信号,确保了程序对外部事件的相应。
关于计算机系统计划与实现的感悟及创新理念:


[*]模块化与抽象层次:计算机系统的计划夸大清晰的模块划分和层次抽象,这不仅进步了系统的可维护性和扩展性,也为创新提供了底子框架。例如,通过将编译过程分解为预处置惩罚、编译、汇编、链接等多个独立步调,每一步都可以独立优化或更换,促进了编译技能的发展。
[*]性能与效率的平衡:在计划实现中,追求高性能的同时也要思量资源效率。比如,当代编译器的优化技能和内存管理计谋旨在减少实行时间和内存占用,同时保持精良的可读性和可维护性。
[*]安全性和可靠性:随着软件复杂度增加,安全性成为系统计划的重要考量。引入更严酷的类型检查、自动化的代码审查工具、内存安全语言特性,以及在操作系统层面实行更细粒度的权限控制,都是提拔系统团体安全性的创新方向。
[*]跨平台与可移植性:面对多样化硬件架构和操作系统环境,计划具有高度可移植性的软件变得至关重要。通过遵循开放尺度、使用跨平台开发框架和容器化技能,可以简化程序在差别环境下的部署和运行。
综上所述,计算机系统的计划与实现不断向着更高性能、更强安全、更广泛兼容和更高效开发的方向发展,持续推动技能创新和应用实践的进步。

(结论0分,缺失 -1分,根据内容酌情加分)

附件

列出所有的中心产物的文件名,并予以说明起作用。
文件的作用
文件名
预处置惩罚后的文件
hello.i
编译之后的汇编文件
hello.s
汇编之后的可重定位目标文件
hello.o
链接之后的可实行目标文件
hello
Hello.o 的 ELF 格式
elf.txt
Hello.o 的反汇编代码
Disas_hello.s
hello的ELF 格式
hello1.elf
hello 的反汇编代码
hello_objdump.s

(附件0分,缺失 -1分)

参考文献

为完资本次大作业你翻阅的书籍与网站等
  《深入明白计算机系统》 Randal E.Bryant  David R.O’Hallaron 机械工业出版社
Hello World – Programming Fundamentals
操作系统 动态内存管理 OS | Echo Blog
https://en.wikipedia.org/wiki/C_dynamic_memory_allocation
https://lisha.ufsc.br/teaching/os/exercise/hello.html
  博客园 printf函数实现的深入剖析
  CSDN博客 Ubuntu系统预处置惩罚、编译、汇编、链接指令
  博客园 从汇编层面看函数调用的实现原理
 CSDN博客 ELF可重定位目标文件格式
  博客园 shell命令实行过程
  《步步惊芯——软核处置惩罚器内部计划分析》 TLB的作用及工作过程
博客园 [转]printf 函数实现的深入剖析 
(参考文献0分,缺失 -1分)




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 程序人生-Hello‘s P2P-ICS大作业