精华内容
下载资源
问答
  • 2009年哈工大计算机科学与技术学院研究生复试内容
  • 哈工大计算机科学与技术学院2009年研究生复试内容
  • 课题试卷 计算机组成原理2002秋 试卷下载 计算机组成原理2002秋 试卷答案下载 计算机组成原理2003秋 试卷下载 计算机组成原理2003秋 试卷答案下载
  • 哈工大计算机系统大作业

    千次阅读 2020-01-05 23:26:42
    计算机系统 大作业 题 目 程序人生-Hello’s P2P 专 业 软件工程 学 号 1183000118 班 级 1837101 学 生 张智琦 指 导 教 师 史先俊 计算机科学与技术学院 2019年12月 摘 要 本文通过对于hello程序的...

    计算机系统

    大作业

    题 目 程序人生-Hello’s P2P
    专 业 软件工程
    学   号 1183000118
    班   级 1837101
    学 生 张智琦    
    指 导 教 师 史先俊

    计算机科学与技术学院
    2019年12月
    摘 要
    本文通过对于hello程序的分析,从hello.c直到hello可执行程序进行逐步分析,结合课本上的知识和一些资料,通过乌邦图虚拟机进行试验,试验gdb,edb以及gcc等工具进行试验,把这个学期csapp中各章节知识进行融合,形成自己的语言,展示自己的收获,体现自己对于对计算机系统这门课程的理解。

    关键词:计算机系统,hello程序的一生

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

    目 录

    第1章 概述 - 4 -
    1.1 HELLO简介 - 4 -
    1.2 环境与工具 - 4 -
    1.3 中间结果 - 4 -
    1.4 本章小结 - 5 -
    第2章 预处理 - 6 -
    2.1 预处理的概念与作用 - 6 -
    2.2在UBUNTU下预处理的命令 - 6 -
    2.3 HELLO的预处理结果解析 - 7 -
    2.4 本章小结 - 8 -
    第3章 编译 - 9 -
    3.1 编译的概念与作用 - 9 -
    3.2 在UBUNTU下编译的命令 - 9 -
    3.3 HELLO的编译结果解析 - 9 -
    3.4 本章小结 - 14 -
    第4章 汇编 - 15 -
    4.1 汇编的概念与作用 - 15 -
    4.2 在UBUNTU下汇编的命令 - 15 -
    4.3 可重定位目标ELF格式 - 15 -
    4.4 HELLO.O的结果解析 - 18 -
    4.5 本章小结 - 20 -
    第5章 链接 - 21 -
    5.1 链接的概念与作用 - 21 -
    5.2 在UBUNTU下链接的命令 - 21 -
    5.3 可执行目标文件HELLO的格式 - 21 -
    5.4 HELLO的虚拟地址空间 - 23 -
    5.5 链接的重定位过程分析 - 27 -
    5.6 HELLO的执行流程 - 28 -
    5.7 HELLO的动态链接分析 - 28 -
    5.8 本章小结 - 28 -
    第6章 HELLO进程管理 - 30 -
    6.1 进程的概念与作用 - 30 -
    6.2 简述壳SHELL-BASH的作用与处理流程 - 30 -
    6.3 HELLO的FORK进程创建过程 - 30 -
    6.4 HELLO的EXECVE过程 - 31 -
    6.5 HELLO的进程执行 - 32 -
    6.6 HELLO的异常与信号处理 - 33 -
    6.7本章小结 - 34 -
    第7章 HELLO的存储管理 - 36 -
    7.1 HELLO的存储器地址空间 - 36 -
    7.2 INTEL逻辑地址到线性地址的变换-段式管理 - 36 -
    7.3 HELLO的线性地址到物理地址的变换-页式管理 - 37 -
    7.4 TLB与四级页表支持下的VA到PA的变换 - 38 -
    7.5 三级CACHE支持下的物理内存访问 - 39 -
    7.6 HELLO进程FORK时的内存映射 - 39 -
    7.7 HELLO进程EXECVE时的内存映射 - 40 -
    7.8 缺页故障与缺页中断处理 - 41 -
    7.9动态存储分配管理 - 43 -
    7.10本章小结 - 44 -
    第8章 HELLO的IO管理 - 46 -
    8.1 LINUX的IO设备管理方法 - 46 -
    8.2 简述UNIX IO接口及其函数 - 46 -
    8.3 PRINTF的实现分析 - 46 -
    8.4 GETCHAR的实现分析 - 49 -
    8.5本章小结 - 49 -
    结论 - 50 -
    附件 - 51 -
    参考文献 - 52 -

    第1章 概述
    1.1 Hello简介
    Hello程序的生命周期是从一个源程序,或者说源文件的,即程序员通过编辑器创建并保存的文本文件,文件名是hello.c.
    hello.c分别经过预处理器cpp的预处理,、编译器ccl的编译,汇编器as的汇编依次生成生成hello.i文件,hello.s文件、,生成hello.o文件、最后使用链接器ld进行链接最终成为可执行目标程序hello.
    当我们在shell中输入字符串.\hello并使用回车代表输入结束后,shell通过一系列指令的调用将输入的字符读入到寄存器中,之后将Hello目标文件中的代码和数据从磁盘复制到主存。此时shell会调用fork函数创建一个新的进程,并通过加载报存上下文,将控制权交给这个新的进程,具体过程在后面会详细叙述。
    在hello加载完成后,处理器就开始执行这个程序,翻译成机器语言再翻译成指令编码,最后把程序的调用如:printf等进行链接。新的代码段和数据段被初始化为hello目标文件的内容.然后,加载器会从_start的地址开始,之后会来到 main 函数的地址,之后进入 main 函数执行目标代码,CPU 为运行的 hello 分配时间片执行逻辑 控制流。
    执行阶段把这个等执行的程序分解成几个阶段,分别执行对应的指令,最后输出字符串。之后输出的字符串从主存复制到寄存器文件,再从寄存器文件复制到显示设备,最终显示到屏幕上。
    这标志着进程的终止,shell的父进程回收这个进程操作系统恢复shell的上下文,控制权重回shell,由shell等待接受下一个指令的输入。
    1.2 环境与工具

    硬件环境:Intel Core i7 x64CPU 16G RAM 512G SSD
    软件环境:Ubuntu18.04.1 LTS
    开发与调试工具:visual studio edb,gcc,gdb,readelf,HexEdit,ld

    1.3 中间结果

    文件名 文件作用
    hello.i 预处理器修改了的源程序,分析预处理器行为
    hello.s 编译器生成的编译程序,分析编译器行为
    hello.o 可重定位目标程序,分析汇编器行为
    hello 可执行目标程序,分析链接器行为
    elf.txt hello.o的elf格式,分析汇编器和链接器行为
    objdump.txt hello.o的反汇编,主要是为了分析hello.o
    5.3.txt 可执行hello的elf格式,作用是重定位过程分析
    5.5.txt 可执行hello的反汇编,作用是重定位过程分析

    列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
    1.4 本章小结
    本章主要介绍了hello程序的P2P、020的过程并列出实验基本信息
    (第1章0.5分)

    第2章 预处理
    2.1 预处理的概念与作用
    预处理的基本概念:在源代码编译之前对源代码进行的错误处理。c语言的预错误处理主要包括有三个预处理方面的基本定义内容:1.宏的定义;2.文件的包含;3.条件的编译。c语言预处理的命令以原始的字符符号"#"开头。
    c语言的预处理器(cpp)根据以原始的c字符串中#开头的预处理命令,修改原始的c语言程序。在c语言和c/c++中常见的编译程序需要进行预处理的程序指令主要有:#define(宏定义),#include(源文件包含),#error(错误的指令)等。通过一个预处理命令使得一个源代码编译程序可以在不同的程序运行语言环境中被各种语言编译器方便的进行编译。
    预处理的作用效果:以#include为例,预处理器(cpp)把程序中声明的文件复制到这个程序中,具体到hello.c的就是#include <unistd.h> #include <stdlib.h>#include<stdio.h>。cpp把这些头文件的内容插入到程序文本中,方便编译器进行下一步的编译。结果就是得到了另一个c程序,通常得到的程序以.i作为文件扩展名。

    2.2在Ubuntu下预处理的命令
    命令:cpp hello.c > hello.i

    图2-2-1

    2.3 Hello的预处理结果解析
    使用文本编辑器打开输出的hello.i文件:

    图2-3-1
    我们可以发现,原本仅仅只有几行的头文件hello.c已经被程序扩展到了数千行,hello.i程序的依次开始是程序的头文件stdio.hunistd.hstdlib.h的依次展开。以#define<stdio.h>程序为例:
    在乌邦图中发现程序使用的头文件#define<stdio.h>后到一个乌邦图的一个环境变量下寻找一个stdio.h,把其中stdio.h的宏定义内容进行了复制,同样的把其中stdio.h其中的宏定义被复制进行了递归依次展开。

    图2-3-2
    而我们可以发现原始的hello.c的代码已经在程序的最后,前面都是预处理器复制的内容。
    2.4 本章小结
    本章首先介绍了预处理的定义与作用、之后结合预处理之后的hello.i程序对预处理的结果进行了简单分析。
    预处理过程为接下来对程序的操作打下了基础,是十分重要,不可或缺的。

    (第2章0.5分)

    第3章 编译
    3.1 编译的概念与作用
    编译要对程序进行以下的过程:首先进行的是语法分析,编译完成的程序使用语法分析器对编译程序输入的符号进行语法检查,检验其语法是否完全符合了语法的标准,如果不完全符合就是产生了错误。
    编译器使用ccl把一个简单文本翻译文件的其中的一个hello.i翻译成为一个新的hello.s,它的翻译目的主要是为了包含一个可以使用多种汇编语言的应用程序。
    在机器语言编译的整个过程中,编译器或程序都会产生一种新的中间代码,这种优化就是为了保证下一步的编译器为优化程序进行了充分的准备,大部分的编译器或程序都会针对编译器或程序的功能进行一些优化,目的之一就是在编译器可以很好地实现其原有机器语言功能的前提下提高编译器程序的性能和运行的效率。最后就是使用编译器把已经优化好的编译器和中间代码提取出来进行机器语言编译,调用机器语言汇编的程序,最后生成机器语言的代码。

    3.2 在Ubuntu下编译的命令
    gcc -S hello.i -o hello.s

    图3-2-1
    3.3 Hello的编译结果解析

    图3-3-1
    3.3.1 数据类型
    1.局部变量int i

            图3-3-2 
    

    局部数据空间的每个变量被认为储存在一个栈的寄存器或者栈中,在一个hello.s.的范例里面,编译器将i变量存储在栈上的局部空间-4(%rbp)中,i可以作为一个intt的类型大约占4个单字节。

    2.局部变量 argc
    作为第一个参数存在存在寄存器edi中

    图3-3-3
    3.数组argv[]
    main类型函数程序执行时需要输入的是命令行,char类型单个字符串元素的命令行大小为8为,这个数组文件中存放的指针是输入字符串的指针,程序中的字符串argv[1]和程序中的argv[2]的字符串地址分别被输入的rax分两次进行读取,argv[3]的秒数作为字符串的秒数被第三次读取。

          图3-3-4
    

    4.字符串

    字符以UTF-8编码的格式存在只读的数据段(rodata)。

    3.3.2 算数操作
    算数操作对应的机器语言实现如下

             图3-3-5
    

    i++被实现为:

    3.3.3 关系操作
    Argc!=3这里是使用je进行条件跳转。

       图3-3-6
    

    I<8 这里是使用jle进行条件跳转。

    图3-3-7
    3.3.4 控制转移

    1. if(argc!=4)这个条件判断被编译成

         图3-3-8
      

    :当argv不等于3的时候执行程序段l2中的第一行代码。cmpl比较执行完毕后,设置一个条件执行代码,je根据zf判断程序段是否完全符合条件,如果符合,就对程序段执行一次跳转,否则程序段会继续执行原来的语句。
    2.for(i=0;i<8;i++)这个循环语句被编译成

    图3-3-9
    I被存放在-4(%rbp)中,每次循环+1,直到符合比较进行跳转。

    3.3.5 函数操作
    一个非常典型的基于c编程语言的库应用程序为例,程序由唯一的一个主执行函数和若干个互相对应的库函数相互组合一起构成。在编译程序中自己自动调用的库函数或者自己在编译程序中自己定义的一个库主调用函数,在每次进行程序编译时都很有可能因为需要自动地对进行编译程序中所对应的一个库主调用函数进行处理。
    1.main函数
    函数调用:基于系统内部指令调用编译器系统可以通过使用系统相应的一个call内部指令调用语句对一个main调用函数内部指令进行函数调用.call被系统启动的一个函数会被__libc_start_main调用,并且将会对下一个系统调用函数指令的初始化和地址数据进行自动压缩并将其写入栈中,然后自动依次跳转这个调用指令语句到系统相应的一个main调用函数内部。
    参数传递:向main函数传递的参数argc和argv,分别使用%rdi和%rsi存储,在上面的叙述已经十分充分,在此就不过多重复了。

    图3-3-10
    内存管理:main函数调用结束时,也指的是在一个程序的运行结束时,调用一个leave指令,把栈的空间进行恢复,称之为针对栈的恢复现场,让栈的空间保持跟调用前一样的状态,然后调用一次ret进行返回。
    2.printf函数
    函数调用&参数传递:两次.

    图3-3-11
    第一次将Rdi设置为待传递的字符串"用法: Hello 学号 姓名 秒数!\n"的首地址。第二次将%rdi 为“Hello %s %s\n”的 起始的地址,使用对应的寄存器rsi 来分别进行针对 argv[1]的传递,,相应的,系统使用%rdx 传递 argv[2]。
    3.exit函数
    函数调用&参数传递:rdi设置为0,使用call(用法同上,不过多赘述)

    图3-3-12
    4.atoi 和sleep函数:
    函数调用&参数传递:atoi函数把需要传入的变量argv[3]放入rdi中,传给atoi函数。将转换完毕存在eax的秒数通过edi传给sleep函数,调用都是使用call函数进行。

    图3-3-13
    (5) getchar 函数:
    函数调用&参数传递:无需参数传递,直接使用call进行函数调用。

    3.4 本章小结
    本章简单的描述了编译器处理c语言程序的基本过程,根据hello程序中使用的各种数据类型,运算。循环操作和函数调用等操作对hello.s程序进行分析,查看编译器针对这些操作会采取什么样的策略。
    编译器将.i 的拓展程序编译为.s 的汇编代码。经过编译之后, hello 程序便从 C 语言被解读成为为更加低级的汇编语言。当然,根据不同类型的CPU可能会产生不同类型的汇编语言,在此仅做简要分析,以x86-64指令类型作为例子。

    (第3章2分)

    第4章 汇编
    4.1 汇编的概念与作用
    汇编器把这些hello.s翻译成机器语言的指令,并且把这些机器语言指令编码打包成一个可重定位目标程序的指令编码格式,结果指令编码保存在了hello.o中。
    hello.o文件是一个简单的二进制指令编码文件,它可以包含目标程序的所有指令进行编码。此外汇编器还可以通过翻译生成计算机能直接自动识别和控制指令执行的一种二进制语言,也即机器语言。

    4.2 在Ubuntu下汇编的命令
    as hello.s -o hello.o

                   图4-2-1
    

    4.3 可重定位目标elf格式
    指令:readelf -a hello.o > elf.txt 获得hello的elf文件并重定向到elf.txt

                         图4-2-1
    

    使用文本编辑器查看elf.txt

    4.3.1 elf头
    elf头以一个16字节的目标序列开始,这个字节的序列主要描述了一个生成该目标文件的操作系统的目标文件大小和生成目标字节的顺序。其他的描述还包括elf头的位置和大小,目标文件的位置和类型(例如是可重定位,可执行或者目标文件所共享的)机器文件类型,节头部表的目标文件位置和偏移,节头部表的偏移大小和目标文件数量。不同节的目标文件位置和偏移大小都是由一个节头部表条目来描述的,其中每个目标文件中每个目标字节都有一个固定大小的节头部表条目。

                                图4-3-2
    

    4.3.2节头部表
    描述目标文件的节,描述目标文件中不同节的位置和大小。

                                图4-3-3
    

    分析hello.o采用的ELF文件的格式,用诸如readelf等多种工具准确列出其各个数据节的基本工作项目定位信息,特别是需要突出注意的一点是针对重定位项目的分析。

    4.3.3重定位节

                          图4-3-4
    

    偏移量:通常对应于一些需要重定向的程序代码所在.text或.data节中的偏移位置.
    信息:重定位到的目标在符号表中的偏移量
    类型:代表重定位的类型,与信息相互对应。
    名称:重定向到的目标的名称
    加数:用来作为计算重复或定位文件位置的一个辅助运算信息,共计约占8个字节。

    4.3.4 符号表(Symbol Table)
    符号表:用来存放程序中定义和引用的函数和全局变量的信息。局部变量存在于栈中,并不存于这里。
    符号表索引:对此数组的索引。
    根据书本中重定位的算法:
    refaddr = ADDR(s) + r.offset 计算.text中对应节内部的位置的运行时地址+偏移量,下方的式子同理
    *refptr = (unsigned) (ADDR(r.symbol) + r.addend-refaddr)
    这就是计算重定位的方法之一。

                     图4-3-5
    

    对hello.o由于程序比较简单没有程序头部表等其他的节。

    4.4 Hello.o的结果解析
    命令:objdump -d -r hello.o > objdump.txt

    图4-4-1
    使用文本编辑器查看

                  图4-4-2
    

    4.4.1.函数调用
    在编译的过程中完成之后在生成的函数hello.s中,函数调用的后面接着就是下一个函数的目标地址名称,并没有直接确定下一条指令的相对地址位置,而在反汇编的过程中查看之后,可以很清楚的看到并且可以发现,call的后面跟着的函数就是调用目标函数相对地址时下一条的指令,相对的地址位置还是0。
    因为我们在hello.s在程序中需要调用的每个函数都是一个共享库程序当中的重定位函数,这就是需要通过等待调用动态的链接把重定位的函数目标地址链接到我们的共享库程序当中。在没有重定位的函数时候,调用的动态链接函数的地址和运行时目标地址也是不可以确定的,只有通过等待调用这些动态的链接器很好的确定了函数的地址和运行时目标地址,汇编的过程中就将这些不可以确定的运行时间地址的调用的函数目标的地址直接设置为下一条程序执行指令的目标地址,也就是运行时目标的地址设置为0.这时等待调用动态的链接器也做的就是下一步程序要进行的指令的确定。
    4.4.2.全局变量的访问
    在编译完成生成的hello.s中,访问全局变量.rodata段的方式是在汇编的过程中使用了段的名称+寄存器,而在反汇编输出的文本文件中进行查看操作之后,变成了0+寄存器,也就是把段名称对应的地址设置为0。这个的原因和上面的类似,也是因为全局变量数据的地址也是在运行时候确定的,对他的数据进行访问的时候也是需要进行重定位操作的。

    4.4.3 分支转移
    

    在编译完成生成的hello.s中,程序为了进行分支转移进行了分段,如.L1.L2等,但是在这里没有所谓的段标记名称,而是明确的地址,那个名称仅仅是机器在编译的时候方便标记的助记符。在汇编成为机器语言之后,程序为了寻找明确的下一条指令的位置当然不能使用如此笼统的标记,而是需要使用明确的地址。

    4.5 本章小结
    本章针对hello.s汇编到hello.o。在本章的分析中,我们查看了hello.o的可重定位目标文件的格式,使用反汇编查看hello.o经过反汇编过程生成的代码并且把它与hello.s进行比较,分析和阐述了从汇编语言进一步翻译成为机器语言的汇编过程。
    (第4章1分)

    第5章 链接
    5.1 链接的概念与作用
    链接操作是将各种机器代码和数据的片段进行收集并组合成一个单一的链接后的文件的编译过程,这个单一文件的链接可被应用程序加载(或者复制)到一个内存并加载和执行。
    链接是十分重要,不可或缺的,而且应用十分广泛。这是因为链接可以是执行于应用程序编译时,也就是在一个源代码被翻译成一个机器代码时;也就是可以直接执行于应用程序加载时,也就是在应用程序被一个加载器自动加载到一个内存并加载和执行时;甚至可以直接执行于应用程序运行时,也就是由一个大的应用程序连接器来加载和执行。在传统和现代的计算机操作系统中,链接的重要性是由应用程序连接器帮助应用程序自动加载和执行的。因为链接可以是帮助应用程序构造大型的程序,在小型的计算机中也可以用链接帮助应用程序加载和单独运行应用程序。
    链接重要性体现在它使得应用程序分离的编译过程成为了可能,程序员们可以把一个大的应用程序和连接器分开进行编译,独立进行管理和单独进行修改。极大的提升了进行大型文件编写的效率。

    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-2-1
    

    5.3 可执行目标文件hello的格式
    指令:readelf -a hello > 5.3.txt

                        图5-3-1
    

    5.3.1节头部表

                    图5-3-2
    
                    图5-3-3
    

    显然我们可以清楚地看出,节点的头部表包含了应用程序的每个节,图中清楚的则列出了它们的详细信息详细信息.其中头部表包括:名称,类型,地址,偏移量,大小,全体大小,链接,信息,对齐。我们可以用hexedit定位各个全体字节所占的大小区间。
    5.3.2.ELF头

                    图5-3-4
    

    elf头是由一个16字节的序列开始,这个序列描述了生成该文件的系统的大小和字节顺序。其他还包括elf头的大小,目标文件的类型(如可重定位,可执行或者共享的)机器类型,节头部表的文件偏移,节头部表的大小和数量。不同节的入口文件位置和偏移大小都是由一个节头部表描述的,其中每个目标入口文件中每个节都有一个固定文件大小的入口文件条目。除此之外,跟链接之前相比section header 和 program header 均增加,并获得了入口地址。
    5.3.3 程序头部表

                          图5-3-5
    

    程序头部表描述了文件整个的布局情况。
    5.3.4 符号表

                     图5-3-6
    

    存放程序中定义和引用的函数和全局变量的信息还有长度等等等。符号表的索引就是这个数组的下标。

    5.3.5 动态符号表

                   图5-3-7
    

    保存与用于动态数据链接导入模块内部相关的数据导入值和导出通用符号,这些内容里面并不包括模块内部的符号。

    5.3.6 重定位节

                  图5-3-8
    

    这是一个表示.text节中目标位置的链列表,包含了.text节中一些需要对目标进行重定位的函数信息,当我们的链接器把这个函数的目标位置文件和其他目标文件直接组合在一起来的时候,需要修改这些函数的位置。上述分别的列表描述了我们的应用程序中那些需要重定位的链接器函数的位置和声明。
    5.4 hello的虚拟地址空间
    使用edb打开hello程序,查看进程的地址信息。

    图5-4-1
    程序包含以下几个小段:1, phdr 保存了二进制的程序头表。 2,interp 指定在程序已经从可执行文件映射到具体的内存区域之后,必须进程调用的解释器(如动态链接器)。 3,load表示一个需要从二进制文件映射到虚拟地址空间的段。其中保存 了常量数据(如字符串)、程序的目标代码等,4,.dynamic 保存了由动态链接器使用的信息。5,note 保存辅助信息。 6,gnu_stack:权限标志,标志栈是否是可执行的。7,gnu_relro:这个节中指定了在重定位程序结束之后那些映射到内存的区域是需要重新设置为只读类型的。用户可以在程序中使用datadump查看虚拟地址段

                       图5-4-2
    

    以.interp和.dynamic为例,使用Data Dump 查看虚拟地址段:
    .interp

    .dynamic


    跟上面的节头部表是一致的,显然可以得到我们需要的结论。
    5.5 链接的重定位过程分析
    指令:objdump -d -r hello > 5.5.txt

                        图5-5-1
    
                   图5-5-2
    

    跟上一次查看反汇编时存在着几点区别:
    重定位:连接器通过把每个符合定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,从而重定位这些节。
    函数个数的区别:在使用ld命令链接的时候,指定了动态链接器为64的/lib64/ld-linux-x86-64.so.2。在动态共享库libc.so中,定义了hello需要的printf、sleep、getchar、exit 函数和_start 中调用的 __libc_csu_init,__libc_csu_fini,__libc_start_main。这些函数被链接器加入其中。
    根据具体的类型对外部内存调用函数的地址进行重定位,链接器根据已经确定的.text与对应的.plt节的相对距离调用地址计算相对偏移距离,修改下条指令的相对偏移地址,指向链接器对应的函数,hello.o的相对距离偏移调用地址本身就变成了链接器hello.o中的虚拟内存调用地址。
    节的改变:增加了.init和.plt节,和一些节定义中的函数。
    5.6 hello的执行流程
    函数名—地址
    ld-2.27.so!_dl_start—
    ld-2.27.so!_dl_init—
    hello!_start—0x400550

    hello!_init—0x4004c0

    hello!main—0x400582
    hello!puts@plt–0x4004f0
    hello!exit@plt–0x400530
    hello!printf@plt–0x400500
    hello!sleep@plt–0x400540
    hello!getchar@plt–0x400510
    sleep@plt—0x400540
    5.7 Hello的动态链接分析
    共享链接库代码是一个动态的目标模块,在程序开始运行或者调用程序加载时,可以自动加载该代码到任意的一个内存地址,并和一个在目标模块内存中的应用程序链接了起来,这个过程就是对动态链接的重定位过程。
    而在一个动态的共享链接库中仍然存在着一个可以调用程序加载而动态链接无需重定位的位置无关代码,编译器在程序中的函数开始运行时是不能自动预测各个函数的开始运行时间和地址的,这就可能需要系统添加重定位的记录,交给一个动态共享链接器或者采用它来进行重定位的动态共享链接,动态共享链接器本身就是负责执行对动态链接的重定位过程,这样做就有效地防止了程序运行时自动修改或者调用目标模块的位置无关代码段。
    动态的链接器在正常工作时链接器采取了延迟绑定的链接器策略,由于静态的编译器本身无法准确预测变量和函数的绝对运行时地址,动态的链接器需要等待编译器在程序开始加载时再对编译器进行延迟解析,这样的延迟绑定策略称之为动态延迟绑定。got链接器叫做全局变量过程偏移链接表,在plt和got中分别存放着链接器的目标变量和函数的运行时地址。一个动态的链接器通过静态的过程偏移链接表plt+got链接器实现了函数的一个动态过程链接,这样一来,它就已经包含了正确的绝对运行时地址。
    5.8 本章小结
    本章通过对hello可执行程序的分析,回顾了链接的基本概念,文件的重定位过程,动态链接过程,虚拟地址空间,可重定位目标文件ELF格式的各个节等与链接有关的内容。链接的过程在软件开发中扮演一个关键的角色,它们使得分离编译成为可能。
    (第5章1分)

    第6章 hello进程管理
    6.1 进程的概念与作用
    进程是一个针对执行中的应用程序的程序的实例。系统中的每个应用程序都可以运行在某个应用进程的可执行上下文中。每次程序用户可以通过向系统中的shell应用程序输入一个可执行程序的英文名字,运行这个应用程序时,shell就可能会自动创建一个新的应用进程,然后在这个新应用进程的可执行上下文中自动运行这个可执行文件,应用程序也同样可以自动创建新的可执行进程,并且在这个新进程的可执行上下文中用户可以运行他们自己的可执行代码或者其他的应用程序。
    我们应当感谢局部性的存在,进程所对应的处理功能部件会把它提供出来给所有的应用程序。它有两个关键抽象:一个独立的程序逻辑控制流:它可以提供一个独立的假象,好像我们的应用程序在一个独占的空间使用内存处理器。一个应用程序私有的地址处理器空间,它可以提供一个独立的假象,好像我们的应用程序独占的一个使用内存的系统。
    6.2 简述壳Shell-bash的作用与处理流程
    shell俗称壳,是一种指"为使用者提供操作界面"的嵌入式软件(也被称为命令解析器)。软件提供了一种允许用户与其他操作系统之间进行通讯的一种方式。这种简单的通讯方式可以以交互方式(从键盘输入,并且用户可以立即地得到命令响应),或者以交互方式shellscript(非交互)的方式允许用户执行。shell(即壳)它是一个简单的命令解释器,它允许系统接收到一个用户的命令,然后自动调用相应的命令执行应用程序。
    Shell 的处理流程:shell读取用户从终端使用外部设备输入(通常是键盘输入)的指令。解析所读取的指令,如果这个指令是一个内部指令则立即执行,否则,加载调用一个应用程序为申请的程序创建新的子进程,在子进程的上下文中运行。同时shell还允许接收从键盘读入的外部信号,(如:kill)并根据不同信号的功能进行对应的处理。

    (部分资料来源于百度百科)
    6.3 Hello的fork进程创建过程
    用户在终端输入对应的指令(./hello 1183000118 张智琦),这时shell就会读取输入的命令,并开始进行以下操作:
    第一步:判断hello不是一个内置的shell指令,所以调用应用程序,找到当前所在目录下的可执行文件hello,准备执行。
    Shell会自动的调用fork()函数为父进程创建一个新的子进程,子进程就会因此得到与父进程(即shell)虚拟地址空间相同的一段各种的数据结构的副本(包括代码和数据段,堆,共享库和用户栈)。父进程与子进程最大的不同在于他们分别拥有不同的PID,父进程与子进程分别是两个并发的进程,在子进程中程序运行的这个过程中,父进程在原位置等待着程序的运行完毕。

    图6-3-1
    6.4 Hello的execve过程
    Execve函数加载并运行可执行目标文件hello,且且包含相对应的一个带参数的列表argv和环境变量的列表exenvp,,只有当出现错误时,例如找不到hello文件时,execve才会返回-1到调用程序,execve调用成功则不会产生返回。
    在shell调用fork函数之后,execve调用驻留在内存中的被称为启动加载器的操作系统代码来执行程序,使用启动加载器,子进程调用execve函数,在当前进程即子进程的上下文中加载新程序hello,这个程序覆盖当前正在执行的进程的所有的地址空间,但是这样的操作并没有创建一个新的进程,新的进程有和原先进程相同的PID,并且它还继承了打开hello调用execve函数之前所有已经打开的文件描述符。新的栈和堆段都会被初始化为零,新的代码和数据段被初始化为可执行文件中的内容。只有一些调用程序头部的信息才可能会在加载的过程中被从可执行磁盘复制到对应的可执行区域的内存。

                        图6-4-1
    

    6.5 Hello的进程执行
    进程向每个程序人员提供一种假象,好像他们在一个独占的程序中使用了处理器,这种处理效果的具体实现效果本身就是一个逻辑控制流,它指的是一系列可执行程序的计数器pc的值,这些计数值唯一的定义对应于那些包含在程序的可执行文件目标对象中的可执行指令,或者说它指的是那些包含在程序运行时可以动态通过链接触到可执行程序的共享文件对象的可执行指令。时间片是指一个进程在执行控制流时候所处在的每一个时间段。
    处理器通过设置在某个控制寄存器中的一个模式位来限制一个程序可以可以执行的指令以及它可以访问的地址空间。没有设置模式位时,进程就运行在用户模式中。用户模式下不允许执行特权指令,不允许使用或者访问内核区的代码或者数据。设置模式位时,进程处于内核模式,该进程可以 访问系统中的任何内存位置,可以执行指令集中的任何命令。进程从用户模式变为内核模式的唯一方式是使用诸如中断,故障或陷入系统调用这样的异常。异常发生时,控制传递到异常处理程序,处理器从用户模式转到内核模式。
    上下文在运行时候的状态这也就是一个进程内核重新开始启动一个被其他进程或者对象库所抢占的网络服务器时该进程所可能需要的一个下文状态。它由通用寄存器、浮点数据寄存器、程序执行计数器、用户栈、状态数据寄存器、内部多核栈和各种应用内核数据结构等各种应用对象的最大值数据寄存器组合构成。
    在调用进程发送sleep之前,hello在当前的用户内核模式下进程继续运行,在内核中进程再次调用当前的sleep之后进程转入用户内核等待休眠模式,内核中所有正在处理等待休眠请求的应用程序主动请求释放当前正在发送处理sleep休眠请求的进程,将当前调用hello的进程自动加入正在执行等待的队列,移除或退出正在内核中执行的进程等待队列。
    之后设置定时器,休眠的时间等于自己设置的时间,当计时器时间到时候,发送一个中断信号。内核收到中断信号进行中断处理,hello被重新加入运行队列,等待执行,这时候hello就可以运行在自己的逻辑控制流里面了。

                              图6-5-1
    

    6.6 hello的异常与信号处理
    正常执行并结束:

                              图6-6-1
    

    当按下 ctrl-z 之后,hello被挂起,使用ps查看,发现有hello的PID

                           图6-6-2
    

    按下 ctrl-c : 父进程就会接收到一个 SIGINT 信号,它的作用是结束运行 hello,并调用诸如wait等的函数回收 hello 这个进程。在此时我们可以使用ps查看,发现没有hello。

                           图6-6-3
    

    乱按仅仅把输入的字符放到屏幕上输出:

    图6-6-4
    6.7本章小结
    本章系统的回顾了进程的基本定义以及其作用,对shell的工作方式和各种类型的信号处理的方法进行了简单的分析,学习了与进程相关的几个重要的函数,了解了如fork创建一个新进程,execve是怎么样加载的,还有这些函数的作用和原理,同时我们以hello这个应用程序为例针对异常控制和信号处理进行简单的分析。
    (第6章1分)

    第7章 hello的存储管理
    7.1 hello的存储器地址空间
    逻辑地址:逻辑地址用来指定一个操作数,它由选择符和偏移量组成。逻辑地址是程序代码在编译之后出现在汇编程序。
    线性地址:一个逻辑地址在经过段地址机制的转化之后变成一个线性分页地址(与虚拟地址同意义)。具体的格式可以表示为:虚拟地址描述符:偏移量。它作为一个逻辑分页地址被输入到一个物理地址变换之间的一个中间层,在分页地址变换机制中需要使用一个线性分页地址描述符作为输入,线性地址可以再经过物理地址变换以产生一个新的物理分页地址。在不同时启用一个分页地址机制的情况下,线性地址本身就是与虚拟地址同意义的。
    物理地址::一个计算机操作系统的物理主存被自动组织为一个由m个连续的字节相同大小的单元内存组成的计算机数据。单元内存没有唯一的字节都是因为有一个唯一的物理单元内存地址,中央处理器会通过地址总线的寻址,找到真实的物理单元内存对应的地址,这会使得具有相同单元内存地址的计算机物理数据通过存储器被自动进行读写。
    7.2 Intel逻辑地址到线性地址的变换-段式管理
    每个段的首地址就会被储存在各自的段描述符里面,所以的段描述符都将会位于段全局描述符表中(每个段的全局描述符表一个局部称为gdgdt和另一个局部的的段描述符表一个局部称为ldldt),通过段选择符我们可以快速寻找到某个段的段全局描述符。逻辑上段地址的偏移量结构就是段选择符+偏移量。
    段选择符的索引位组成和定义如下,分别指的是索引位(index),ti,rpl,当索引位ti=0时,段描述符表在rpgdt中,ti=1时,段描述符表在rpldt中。而索引位index就类似一个数组,每个元素内都存放一个段的描述符,索引位首地址就是我们在查找段描述符时再这个元素数组当中的索引。一个段描述符的首地址是指含有8个元素的字节,我们通常可以在查找到段描述符之后获取段的首地址,再把它与线性逻辑地址的偏移量进行相加就可以得到段所需要的一个线性逻辑地址。

    在分段保护模式下,分段有两种机制:段的选择符在段的描述符表->分段索引->目标段的段描述符条目->目标段的描述符基地址+偏移量=转换为线性段的基地址。由于现代的macosx86系统内核使用的描述符是基本扁平的逻辑模型,即目标段的逻辑地址=线性段的描述符=转换为线性段的基地址,等价于描述符转换为线性地址时关闭了偏移量和分段的功能。这样逻辑段的基地址与转换为线性段的基地址就合二为一了。
    7.3 Hello的线性地址到物理地址的变换-页式管理
    在分页的机制下地址转化管理机制主要实现了虚拟地址(它也即非非线性内存地址)向虚拟地址物理页或内存地址的非线性分页转化。vm内存系统将虚拟内存的块大小分割成作为一个被我们称为基于虚拟页的内存大小固定的块块用来进行处理这个固定大小的内存问题,每个称为虚拟页的内存大小可以固定为2p=2p个单位字节。类似的,物理块和虚拟内存被再一次细分为一个物理页(也被通常称为页帧),大小与每一个虚拟页的地址大小与其对应的值相等。例如:一个32位的虚拟机器,线性的地址可以最大达到4gb,用4kb为一个页来进行划分,也可以分为1m个页,通过页表处理和查找这些虚拟页的数据,方便对线性地址的大小进行管理。
    之后计算机会进行一个翻译操作,把一个n元素的虚拟地址空间中的虚拟元素与一个m元素的另一个物理虚拟地址空间相互进行映射,这个翻译操作被我们称为地址翻译。
    地址翻译示意图如下

    虚拟地址由对应的虚拟物理页号(vpn)和虚拟页偏移量(vpo)共同组成,类似的,物理地址由虚拟物理页偏移号(ppn)和对应的物理页偏移量(ppo)共同分配组成(这里没有特别考虑tlb快表的物理地址结构)。页表中物理地址存在三种常见的情况:未分配:没有在虚拟内存的空间中分配该条目的内存。未分配缓存:在虚拟内存的空间中已经分配了但是没有被直接缓存到对应物理地址的内存中。已分配已缓存:内存已经缓存在了对应物理地址的内存中。页表的基址寄存器paptbr+vpn在页表中可以获得条目pte,通过对比条目对应的有效位判断物理地址是上述哪一种的情况,如果有效则通过提取得出对应物理地址的页号寄存器ppn,与对应的虚拟页偏移量共同分配构成了物理地址寄存器pa。

    当页面命中时CPU硬件执行的步骤:
    第1步:处理器会产生一个虚拟地址,并且将它传送给地址管理单元MMU。
    第2步: MMU生成PTE地址,并从高速缓存/主存请求得到它。
    第3步:高速缓存或者主存向MMU返回PTE。
    第4步:MMU构造物理地址,并把它传送给高速缓存/主存。
    第5步:高速缓存或者主存会返回所请求的数据字给处理器。
    7.4 TLB与四级页表支持下的VA到PA的变换
    如果按照上述模式,每次CPU产生一个虚拟地址并且发送给地址管理单元,MMU就必须查找一个PTE行来用将虚拟地址翻译成物理地址。为了消除这种操作带来的大量时间开销,MMU中被设计了一个关于PTE的小的缓存,称为翻译后备缓冲器(TLB)也叫快表。例如当每次cpu发现需要重新翻译一个虚拟地址时,它就必须发送一个vpn得到虚拟地址mmu,发送一个vpo位得到一个l1高速缓存.例如当我们使用mmu向一个tlb的组请求一个页表中的条目时,l1高速缓存通过一个vpo位在页表中查找一个相应的数据标记组,并在页表中读出这个组里的个数据标记和相应的数据关键字.当mmu从一个tlb的组得到一个ppn时,代表缓存的工作在这个组的请求之前已经完全准备好,这个组的ppn与就已经可以与这些数据标记文件中的一个虚拟地址进行很好的匹配了。
    corei7采用四级页表层次结构,每个四级页表进程都有他自己私有的页表层次结构,这种设计方法从两个基本方面就是减少了对内存的需求,如果一级页表的pte全部为空,那么二级页表就不会继续存在,从而为进程节省了大量的内存,而且也只有一级页表才会有需要总是在一个内存中。四级页表的层次结构操作流程如下:36位虚拟地址被寄存器划分出来组成四个9位的片,每个片被寄存器用作到一个页表的偏移量。cr3寄存器内储存了一个l1页表的一个物理起始基地址,指向第一级页表的一个起始和最终位置,这个地址是页表上下文的一部分信息。vpn1提供了到一个l1pet的偏移量,这个pte寄存器包含一个l2页表的起始基地址.vpn2提供了到一个l2pte的偏移量,一共四级,逐级以此层次类推。

                             图7-4-1
    

    7.5 三级Cache支持下的物理内存访问
    三级cache的缓存层次结构如下:

                 图7-5-1
    

    物理地址的结构包括组索引位CI(倒数7-12位),使用它进行组索引,使用组索引位找到对应的组之后。假设我们的cache采用8路的块,匹配标记位CT(前40位)如果匹配成功且寻找到的块的有效位valid上的标志的值为1,则命中,根据数据偏移量CO(后6位)取出需要的数据然后进行返回。

    图7-5-2

    如果没有数据被匹配成功或者匹配成功但是标志位是 1,这些都是不命中的情况,向 下一级缓存中查询数据(L2 Cache->L3 Cache->主存),并且将查找到的数据加载到cache里面。这时候我们面临一个问题,替换谁。一般我们会选择使用一种常见的简单替换策略,查询得到的数据之后,如果我们映射得到的组内已经有很多个空闲块,则直接在组内放置;否则组内都已经是有效块,产生了冲突,则我们会采用最近最少最少使用(lfu)的策略,然后确定将哪一个块替换作为牺牲块。

    7.6 hello进程fork时的内存映射
    Fork函数在上面已经介绍过了,在此仅做简单的重复。
    shell通过一个调用用fork的函数可以让进程内核自动创建一个新的进程,这个新的进程拥有各自新的数据结构,并且被内核分配了一个唯一的pid。它有着自己独立的虚拟内存空间,并且还拥有自己独立的逻辑控制流,它同样可以拥有当前已经可以打开的各类文件信息和页表的原始数据和样本,为了有效保护进程的私有数据和信息,同时为了节省对内存的消耗,进程的每个数据区域都被内核标记起来作为写时复制。
    7.7 hello进程execve时的内存映射
    execve函数在上面已经介绍过了,在此仅做简单的重复。
    execve函数在当前代码共享进程的上下文中加载并自动运行一个新的代码共享程序,它可能会自动覆盖当前进程的所有虚拟地址和空间,删除当前进程虚拟地址的所有用户虚拟和部分空间中的已存在的代码共享区域和结构,但是它并没有自动创建一个新的代码共享进程。新的运行程序仍然在堆栈中拥有相同的区域pid。之后为新运行程序的用户共享代码、数据、bss和所有堆栈的区域结构创建新的共享区域和结构,这一步叫通过链接映射到新的私有代码共享区域,所有这些新的代码共享区域都可能是在运行时私有的、写时复制的。它首先映射到一个共享的区域,hello这个程序与当前共享的对象libc.so链接,它可能是首先动态通过链接映射到这个代码共享程序上下文中的,然后再通过映射链接到用户虚拟地址和部分空间区域中的另一个共享代码区域内。为了设置一个新的程序计数器,execve函数要做的最后一件要做的事情就是自动设置当前代码共享进程上下文的一个程序计数器,使之成为指向所有代码共享区域的一个入口点(即_start函数)。

    图7-7-1
    7.8 缺页故障与缺页中断处理
    在指令请求一个虚拟地址时,MMU中查找页表,如果对于的物理地址没有存在主存内部,以至于我们必须要从磁盘中读出数据,这就是缺页故障(中断)。

                      图7-8-1
    

    在发生缺页中断之后,系统会调用内核中的一个缺页处理程序,选择一个页面作为牺牲页面。具体的操作过程如下:
    第1步:CPU生成一个虚拟地址,并把它传送给MMU.
    第2步: 地址管理单元生成PTE地址,并从高速缓存/主存请求得到它.
    第3步:高速缓存/主存向MMU返回PTE.
    第4步:PTE中的有效位是零,所以MMU触发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序.
    第5步:缺页处理程序确定出物理内存中的牺牲页,如果这个页面已经被修改了,则把它换出到磁盘.
    第6步:缺页处理程序页面调人新的页面,并更新内存中的PTE.
    第7步: 缺页处理程序返回地址到原来的缺页处理进程,再次对主存执行一些可能导致缺页的处理指令,cpu,然后将返回地址重新再次发送给处理程序mmu.因为程序中虚拟的页面现在已经完全缓存在了物理的虚拟内存中,所以处理程序会再次命中,主存将所请求字符串的返回地址发送给虚拟内存的处理器。

                            图7-8-2
    

    7.9动态存储分配管理
    动态内存分配器维护着一个进程的虚拟内存区域,称为堆.系统之间细节不同,但是不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长(向更高的地址) .对于每个进程,内核维护着一个变量brk, 它指向堆的顶部。
    分配器将堆视为一组不同大小的块的集合来维护.每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的.已分配的块显式地保留为供应用程序使用.空闲块可用来分配.空闲块保持空闲,直到它显式地被应用所分配.一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。
    分配器有两种基本风格. 两种风格都要求应用显式地分配块.它们的不同之处在于由哪个实体来负责释放已分配的块
    显式分配器(explicit allocator):
    要求应用显式地释放任何已分配的块.例如,C标准库提供一种叫做malloc程序包的显式分配器.C程序通过调用malloc函数来分配一个块,并通过调用free函数来释放一个块.C++中的new和delete操作符与C中的malloc和free相当.
    隐式分配器(implicit allocator):
    要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块.隐式分配器也叫做垃圾收集器(garbage collector),而自动释放未使用的已分配的块的过程叫做垃圾收集( garbage collection).例如,诸如Lisp、ML以及Java之类的高级语言就依赖垃圾收集来释放已分配的块。
    一个块是由一个字的头部,有效载荷,以及可能的一些额外的填充组成的.头部编码了这个块的大小,以及这个块是已分配的还是空闲的.如果我们强加一个双字的对齐约束条件,那么块大小就总是8的倍数,且块大小的最低3位总是零.因此,我们只需要内存大小的29个高位,释放剩余的3位来编码其他信息.在这种情况中,我们用其中的最低位(已分配位)来指明这个块是已分配的还是空闲的。

    图7-9-1带边界标记
    显示空闲链表:
    一种更好的方法是将空闲块组织为某种形式的显式数据结构.因为根据定义,程序不需要一个空闲块的主体,所以实现这个数据结构的指针可以存放在这些空闲块的主体里面.例如,堆可以组织成一一个双向空闲链表,在每个空闲块中,都包含一个pred(前驱)和succ(后继)指针.
    (资料来源于课本)

    图7-9-2

    7.10本章小结
    本章主要通过对hello程序运行时虚拟地址的变化进行分析,解析了适用于hello应用程序的虚拟存储地址空间,分析了虚拟地址,线性地址和虚拟物理线性地址之间的互相转换,页表的命中与不页表的命中,使用动态快表缓存作为页表的高速缓存以及如何加速页表,动态内存管理的操作,fork时的动态内存中断与映射、execve时的动态内存中断与映射、缺页的中断与缺页映射和中断的处理。这些缓存方面的基础知识有助于我们能够编写出更加对高速缓存友好的程序代码以及一些对高速缓存的优化解决手段,加速我们的程序运行。

    (第7章 2分)

    第8章 hello的IO管理
    8.1 Linux的IO设备管理方法
    一个Linux文件就是一个m个字节的序列,:所有的 IO 设备都被模型化为文件,而所有的输入和输出都被 当做对相应文件的读和写来执行,这种将设备优雅地映射为文件的方式,允许 Linux 内核引出一个简单低级的应用接口,称为 Unix I/O。
    8.2 简述Unix IO接口及其函数
    Unix I/O接口:
    1.打开文件.一个应用程序通过要求内核打开相应的文件,来宣告它想要访间一个I/O 设备.内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件.内核记录有关这个打开文件的所有信息.应用程序只需记住这个描述符.
    2.Linux shell 创建的每个进程开始时都有三个打开的文件:标准输入(描述符为0) 、标准输出(描述符为1) 和标准错误(描述符为2) .头文件< unistd.h> 定义了常量STDIN_FILENO 、STOOUT_FILENO 和STDERR_FILENO, 它们可用来代替显式的描述符值.
    3.改变当前的文件位置.对于每个打开的文件,内核保持着一个文件位置k, 初始为0.这个文件位置是从文件开头起始的字节偏移量.应用程序能够通过执行seek 操作,显式地设置文件的当前位置为K .
    4.读写文件.一个读操作就是从文件复制n>0 个字节到内存,从当前文件位置k 开始,然后将k增加到k+n .给定一个大小为m 字节的文件,当k~m 时执行读操作会触发一个称为end-of-file(EOF) 的条件,应用程序能检测到这个条件.在文件结尾处并没有明确的“EOF 符号” .类似地,写操作就是从内存复制n>0 个字节到一个文件,从当前文件位置k开始,然后更新k .
    5.关闭文件.当应用完成了对文件的访问之后,它就通知内核关闭这个文件.作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中.无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源.

    Unix I/O 函数:

    1. int open(char* filename,int flags,mode_t mode) 。open 函数将 filename(文件名,含后缀)转换为一个文件描述符(C 中表现为指针),并且 返回描述符数字。
    2. int close(fd),fd 是需要关闭的文件的描述符(C 中表现为指针),close 返回操作结果。
    3. ssize_t read(int fd, void *buf, size_t n);read 函数从描述符为fd 的当前文件位置复制最多n 个字节到内存位置buf .返回值-1表示一个错误,而返回值0 表示EOF.否则,返回值表示的是实际传送的字节数量.
    4. ssize_t write(int fd, const void *buf, size_t n);
      write 函数从内存位置buf 复制至多n 个字节到描述符fd 的当前文件位置.图10-3 展示了一个程序使用read 和write 调用一次一个字节地从标准输入复制到标准输出.返回:若成功则为写的字节数,若出错则为-1.

    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;
    }
    va_list arg = (va_list)((char*)(&fmt) + 4);

    va_list的定义: 
    typedef char *va_list 
    这说明它是一个字符指针。 
    其中的: (char*)(&fmt) + 4) 表示的是...中的第一个参数。
    

    vsprintf返回的是要打印出来的字符串的长度,它的作用就是格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。
    下一步:调用write

    可以找到INT_VECTOR_SYS_CALL的实现:
    init_idt_desc(INT_VECTOR_SYS_CALL,DA_386IGate,sys_call,PRIVILEGE_USER);
    int INT_VECTOR_SYS_CALL表示要通过系统来调用sys_call这个函数。

    sys_call函数如下:
    sys_call:
    call save
    push dword [p_proc_ready]
    sti
    push ecx
    push ebx
    call [sys_call_table + eax * 4]
    add esp, 4 * 3
    mov [esi + EAXREG - P_STACKBASE], eax
    cli
    ret
    一个call save,是为了保存中断前进程的状态。
    syscall 将字符串中的字节从寄存器中通过总线复制到显卡的显存中,显存中存储的是字符的ASCII 码。

    8.4 getchar的实现分析
    异步异常-键盘中断的处理:在用户敲击或者按键盘上面的按钮的时候,键盘接口获得一个键盘扫描码,这样会在此时同时产生一个中断的请求,这会调用键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区的内部。getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回这个字符串。Getchar的大概思想是读取字符串的第一个字符之后再进行返回操作。

    8.5本章小结
    本章中简单的描述了linux的io的接口及其设备和管理模式,unixio的接口及其使用的函数,还有常见的printf函数和pritgetchar函数,当然,如果真的对此展开了分析,printf函数的设计和实现其实看起来还是相当困难的,所以仅仅是简单的了解,对此的深入了解还是有些过浅,需要学生通过不断的分析和学习实践来增进自己的认识和了解。
    (第8章1分)

    结论
    用计算机系统的语言,逐条总结hello所经历的过程。
    你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。
    Hello程序的一生是从文本编辑器开始的,是程序员亲手一个个打出来的字符,成为最初的hello.c文件,也是我们平时经常要交的程序。
    之后我们会把hello进行预处理操作,这一步进行之后,我们在hello.c内部进行的宏定义和为了省事用include调用先人大佬写好的库取出来合并到文件hello.i中。
    但是这一堆字符计算机是无论如何都认识不了的,所以就需要进行编译操作,hello.i被汇编成为汇编文件hello.s。
    编译进行完毕后,机器要让文件成为它能读懂的东西,hello.s经过汇编阶段变成了可重定位目标文件hello.o
    下一步是动态链接过程,hello.o和动态链接库共同链接成可执行目标程序hello。到这里,所谓的P2P: From Program to Process过程就完成了。
    然后hello就要开始发光发热了,我们要使用hello就在shell里面输入./hello 1183000118 张智琦 1 并按下回车键
    Shell读懂了我们的意思,它使用fork和execve函数加载映射虚拟内存,给我们的hello创建新的代码数据堆栈段,让我们的hello误以为自己在独占的使用这些资源。
    CPU为hello分配一个时间片,在这个时间片的时间里,hello获得对CPU的控制权力,在程序计数器中加入自己的代码,按顺序执行他们。
    之后程序或者从cache中很快的取得需要的数据,或者从内存中取出需要的数据,又或是从磁盘加载数据。
    如果我们想要在程序进行时休息一下,只要键入ctrl-z就可以让程序挂起,甚至还可以通过ctrl-c停止这个进程。这些都是信号实现的强大功能。
    最后,shell回收子进程,内核会删除这个进程使用所需要创建的一系列数据结构。至此,hello程序结束了自己短暂的一生。

    学习完了计算机系统这门课,我不禁为计算机内部如此精妙的设计感到震惊,一个个精细的设计无不体现前人的智慧,手中这台小小的机器,在软件硬件的双重加持之下,发挥出了如此强大的实力。
    当然,计算机的发展远远没有结束,人们还在追求更快的数据传输,更大的存储空间,这些都是我们以后要学习的。

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

    附件
    列出所有的中间产物的文件名,并予以说明起作用。
    文件名 文件作用
    hello.i 预处理器修改了的源程序,分析预处理器行为
    hello.s 编译器生成的编译程序,分析编译器行为
    hello.o 可重定位目标程序,分析汇编器行为
    hello 可执行目标程序,分析链接器行为
    elf.txt hello.o的elf格式,分析汇编器和链接器行为
    objdump.txt hello.o的反汇编,主要是为了分析hello.o
    5.3.txt 可执行hello的elf格式,作用是重定位过程分析
    5.5.txt 可执行hello的反汇编,作用是重定位过程分析

    (附件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分)

    展开全文
  • 哈尔滨工业大学计算机科学与技术学院组成原理课程设计: 从IN单元输入一个数,根据该数的低4位值X,计算1+2+…+X,并通过OUT单元输出。
  • 点击文末的阅读原文或者公众号界面左下角的保研夏令营或者公众号回复“夏令营”是计算机/软件等专业的所有保研夏令营信息集合,会一直更新的。哈工大计算机专业成立于1956年,是我国最早的计算机...

    点击文末的阅读原文或者公众号界面左下角的保研夏令营或者公众号回复“夏令营”是计算机/软件等专业的所有保研夏令营信息集合,会一直更新的。

    哈工大计算机专业成立于1956年,是我国最早的计算机专业,于1957年率先培养研究生;1958 年研制出第一台数字计算机;1981 年计算机系统结构获批我国首批博士点(2007 年成为国家重点学科);1986 年计算机应用技术获批我国首批重点学科;1998 年计算机科学与技术学科被评为国家一级重点学科。2019年计算机学科跻身USNews排行榜第28名、世界大学学术排行榜(ARWU)第40名、ESI排行榜全球第60名。2016年教育部计算机学科评估结果为A,2018年通过由7位国际知名专家组织的国际学科评估。

    为了促进高校优秀大学生之间的学术交流,加强青年学生对哈工大计算机学院的了解,选拔优秀学生到我院继续深造,并在招生政策方面为广大学子答疑解惑,我院将于2019年7月3日—5日举办暑期夏令营。

    一、申请资格

    1. 1.  本次夏令营主要招募能够在本科所在学校取得推荐免试研究生(简称推免生)资格,拟申请到我校攻读研究生(含直博生)的2020年优秀应届本科毕业生;

    2. 2.  前3学年或前5学期综合成绩排名在本专业前15%,来自一流高校或所在学科专业排名前20%的高校的学生优先考虑;

    3. 3.  有志攻读博士研究生的学生优先考虑;

    4. 4.  具有良好的英语水平(大学英语四级考试成绩达到425分及以上);

    5. 5.  品德良好,遵纪守法,身心健康。

    6. 二、申请流程

    7. 1.  申请方式:

    登陆哈尔滨工业大学研究生招生网官方网站夏令营报名系统(http://yzb.hit.edu.cn),直接报名,也可手机关注哈尔滨工业大学微信公众号报名报名起止日期:2019年5月30日—2019年6月24日。

    1. 2.  申请材料:

    (1)所在院(系)或学校教务处盖章的成绩单;

    (2)体现英语水平的证明材料(国家英语六级,TOEFL成绩,GRE/GMAT成绩等);

    (3)其他相关材料,包括但不限于:①本科期间各类获奖证书复印件;②学术论文清单及代表性论文复印件(期刊封面、目录、论文全文)。

    以上材料扫描件请发送至邮箱:2545956202@qq.com

    1. 3.  确定名单

    学院将根据申请者的网上报名信息,重点考察申请者学业水平、科研能力和综合素质,确定入选者名单。评审结果将于6月27日通过邮件的形式通知本人。

    三、活动安排

    1、报到时间:2019年7月3日8:00—19:00。

    2、报到地点:哈工大一校区(南岗区西大直街92号)计算机学院综合楼215室。

    报到时请携带个人有效身份证件、申请材料原件。

    3、活动内容:

    (1)7月4日上午:开营式,介绍学校与学院基本情况;

    (2)7月4日下午:学术报告;

    (3)7月5日上午:学术报告;

    (4)7月5日下午:师生交流、个人展示(展示方式会以邮件的方式通知入选者)。

    四、其他说明

    1优秀营员将在我校2020年推免生接收时享受一定优惠政策,具体政策以学校的有关规定为准。

    2计算机学院将负责夏令营期间的食宿、参观、学术讲座等费用,其他费用自理;

    3、营员报到后要求全程参加夏令营的活动。

    五、联系方式

    联系人:李老师   联系电话:0451-86413309

    信息来源网址:

    http://yzb.hit.edu.cn/2019/0524/c8822a224945/page.htm

    点击阅读原文,看看今年目前所有计算机/软件等专业的保研夏令营信息。

    公众号建立了 计算机保研夏令营交流 QQ群,最新的保研信息会在群里发布。

    群号 1023869036 欢迎加入。

    您还可以在以下平台找到我们

    展开全文
  • 哈工大计算机系统大作业 题 目 程序人生-Hello’s P2P 专 业 软件工程 学 号 1183710129 班 级 1837101 学 生 邓昆昆 指 导 教 师 史先俊 计算机科学与技术学院 2019年12月 摘 要 ...

    哈工大计算机系统大作业

    题 目 程序人生-Hello’s P2P
    专 业 软件工程
    学  号
    班  级
    学 生    
    指 导 教 师

    计算机科学与技术学院
    2019年12月
    摘 要
    本文主要讲述了hello.c程序在编写完成后运行在linux中的生命历程,记住相关工具分析预处理、编译、汇编、链接等各个过程在linux下实现的原理,分析了这些过程中产生的文件的相应信息和作用。并介绍了shell的内存管理、IO管理、进程管理等相关知识,了解了虚拟内存、异常信号等相关内容。
    关键词:预处理;编译;汇编;链接;shell;IO管理;进程管理;虚拟内存;异常信号

    目 录

    第1章 概述 - 4 -
    1.1 HELLO简介 - 4 -
    1.2 环境与工具 - 4 -
    1.3 中间结果 - 4 -
    1.4 本章小结 - 5 -
    第2章 预处理 - 6 -
    2.1 预处理的概念与作用 - 6 -
    2.2在UBUNTU下预处理的命令 - 6 -
    2.3 HELLO的预处理结果解析 - 7 -
    2.4 本章小结 - 7 -
    第3章 编译 - 8 -
    3.1 编译的概念与作用 - 8 -
    3.2 在UBUNTU下编译的命令 - 8 -
    3.3 HELLO的编译结果解析 - 9 -
    3.3.1处理变量 - 9 -
    3.3.2处理关系操作符与控制语句 - 11 -
    3.3.3处理四则运算与复合语句 - 13 -
    3.3.4处理数组、指针与结构体 - 14 -
    3.3.5处理函数 - 14 -
    3.4本章小结 - 15 -
    第4章 汇编 - 16 -
    4.1 汇编的概念与作用 - 16 -
    4.2 在UBUNTU下汇编的命令 - 16 -
    4.3 可重定位目标ELF格式 - 16 -
    4.4 HELLO.O的结果解析 - 20 -
    4.5 本章小结 - 22 -
    第5章 链接 - 23 -
    5.1 链接的概念与作用 - 23 -
    5.2 在UBUNTU下链接的命令 - 23 -
    5.3 可执行目标文件HELLO的格式 - 23 -
    5.4 HELLO的虚拟地址空间 - 25 -
    5.5 链接的重定位过程分析 - 27 -
    5.6 HELLO的执行流程 - 28 -
    5.7 HELLO的动态链接分析 - 29 -
    5.8 本章小结 - 29 -
    第6章 HELLO进程管理 - 30 -
    6.1 进程的概念与作用 - 30 -
    6.2 简述壳SHELL-BASH的作用与处理流程 - 30 -
    6.3 HELLO的FORK进程创建过程 - 30 -
    6.4 HELLO的EXECVE过程 - 30 -
    6.5 HELLO的进程执行 - 31 -
    6.6 HELLO的异常与信号处理 - 31 -
    6.7本章小结 - 32 -
    第7章 HELLO的存储管理 - 33 -
    7.1 HELLO的存储器地址空间 - 33 -
    7.2 INTEL逻辑地址到线性地址的变换-段式管理 - 33 -
    7.3 HELLO的线性地址到物理地址的变换-页式管理 - 33 -
    7.4 TLB与四级页表支持下的VA到PA的变换 - 34 -
    7.5 三级CACHE支持下的物理内存访问 - 35 -
    7.6 HELLO进程FORK时的内存映射 - 35 -
    7.7 HELLO进程EXECVE时的内存映射 - 35 -
    7.8 缺页故障与缺页中断处理 - 36 -
    7.9动态存储分配管理 - 37 -
    7.9.1带边界标记的隐式空闲链表 - 38 -
    7.9.2显示空间链表 - 38 -
    7.10本章小结 - 39 -
    第8章 HELLO的IO管理 - 40 -
    8.1 LINUX的IO设备管理方法 - 40 -
    8.2 简述UNIX IO接口及其函数 - 40 -
    8.3 PRINTF的实现分析 - 41 -
    8.4 GETCHAR的实现分析 - 43 -
    8.5本章小结 - 43 -
    结论 - 43 -
    附件 - 44 -
    参考文献 - 45 -

    第1章 概述

    1.1 Hello简介

    P2P:From Program to Process
    用高级语言编写得到.c文件,再经过编译器预处理得到.i文件,进而对其编译得到.s汇编语言文件。此后通过汇编器将.s文件翻译成机器语言,将指令打包成为可重定位的.o目标文件,再通过链接器与库函数链接得到可执行文件hello,执行此文件hello,操作系统会为其fork产生子进程,再调用execve函数加载进程。至此,P2P结束。
    020:From Zero-0 to Zero-0
    操作系统调用execve后映射虚拟内存,先删除当前虚拟地址的数据结构并为hello创建新的区域结构,进入程序入口后载入物理内存,再进入main函数执行代码。代码完成后,父进程回收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格式
    hello0.txt:hello.o反汇编代码
    hello1.txt:hello的反汇编代码

    1.4 本章小结

    本章主要介绍了hello的P2P,020过程,以及进行实验时的软硬件环境及开发与调试工具和在本论文中生成的中间结果文件。

    第2章 预处理

    2.1 预处理的概念与作用

    概念:
    程序设计领域中,预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。
    作用:
    最常见的预处理是C语言和C++语言。ISO C和ISO C++都规定程序由源代码被翻译分为若干有序的阶段(phase) ,通常前几个阶段由预处理器实现。预处理中会展开以#起始的行,试图解释为预处理指令 (preprocessing directive) ,其中ISO C/C++要求支持的包括#if/#ifdef/#ifndef/#else/#elif/#endif(条件编译)、#define(宏定义)、#include(源文件包含)、#line(行控制)、#error(错误指令)、#pragma(和实现相关的杂注)以及单独的#(空指令)。预处理指令一般被用来使源代码在不同的执行环境中被方便的修改或者编译。

    2.2在Ubuntu下预处理的命令

    对hello.c文件进行预处理的命令是:gcc -E -o hello.i hello.c
    在这里插入图片描述

    目录下会增加一个.i文件
    在这里插入图片描述

    hello.c截图:
    在这里插入图片描述

    hello.i部分截图:
    在这里插入图片描述

    2.3 Hello的预处理结果解析

    预处理得到.i文件打开后发现得到了扩展,到了3000多行。原文件中的宏进行了宏展开,增加的文本其实是三个头文件的源码。

    2.4 本章小结

    概括了预处理的概念和作用,详细说明了ubuntu下预处理命令,并分析了.i文件。

    第3章 编译

    3.1 编译的概念与作用

    概念:
    广义的编译是说将某一种程序设计语言写的程序翻译成等价的另一种语言。此处是指利用编译程序从预处理文本文件(.i)产生汇编程序(.s)的过程。
    作用:
    将输入的高级程序设计语言源程序翻译成以汇编语言或机器语言表示的目标程序作为输出。

    3.2 在Ubuntu下编译的命令

    对hello.i进行编译的命令是:gcc -S -o hello.s hello.i
    在这里插入图片描述
    目录下会增加一个.s文件
    在这里插入图片描述
    hello.s部分截图:
    在这里插入图片描述

    3.3 Hello的编译结果解析

    3.3.1处理变量

    源程序中只有局部变量,是int i;分析汇编代码并与源程序比较:

    在这里插入图片描述在这里插入图片描述
    可以看到局部变量放在了寄存器-4(%rbp)中。

    3.3.2处理关系操作符与控制语句

    本程序出现了if(argc!=4)的!=关系操作符,编译器转换成汇编语言后就成了:
    在这里插入图片描述
    在这里插入图片描述
    我们可以看到argc与4进行比较时,可以看到je指令,cmpl与je是放在一起的,如果两数相等je条件成立,跳转.L2也就是后面的循环否则跳过je继续向下执行。可以看到关系操作符与控制语句是借助jx指令实现的,对与其它的关系操作符有:
    在这里插入图片描述

    3.3.3处理四则运算与复合语句

    (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
    复合语句就是上面的组合,或者也有复合的汇编语句:z=x+Ay+B(A,B都是立即数)的汇编语言是leaq B(x,y,A) z
    本程序出现了addq:
    在这里插入图片描述

    3.3.4处理数组、指针与结构体

    (1)数组:取数组头指针加上第i位偏移量来处理。
    (2)指针与数组类似,如果rax表示指针所存的寄存器,访问x指向的值就是(%rax)
    (3)结构体:通过结构体内部的偏移量来访问。
    本程序中出现了数组,截图如下:
    在这里插入图片描述

    3.3.5处理函数

    (1)函数的调用与传参:给函数传参需要先设定寄存器,将参数传给所设的寄存器,再通过call来跳转到调用的函数开头的地址。在源代码中调用了printf、atoi、getchar、sleep和exit:
    

    在这里插入图片描述
    第一个printf转换成了puts,把.L0段的立即值传入%rdi,然后call跳转到puts。
    这里的exit是把立即数1传入到%edi中,然后call跳转到exit
    第二个printf有三个参数,第一个是.LC1中的格式化字符串%eax中,后面的两个依次是%rdi,%rsi,然后跳转到printf
    sleep有一个参数传到%edi中,之后call跳转到 sleep中
    getchar不需要参数,直接call跳转即可。
    (2)返回值:函数的返回值一般在寄存器%eax中,如果有返回值,则要先把返回值存到%eax中,再用ret返回。源程序中有主函数的return 0;就是先把返回值立即数0存到%eax中,再用ret返回。

    3.4本章小结

    概括了编译的概念和作用,重点分析了c程序的数据与操作翻译成汇编语言时的表示和处理方法。

    第4章 汇编

    4.1 汇编的概念与作用

    概念:
    汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程,将.s汇编程序翻译车工机器语言并将这些指令打包成可重定目标程序的格式存放在.o中。
    作用:
    将汇编代码转换为机器指令,使其在链接后能被机器识别并执行.

    4.2 在Ubuntu下汇编的命令

    命令为:gcc -c -o hello.o hello.s
    在这里插入图片描述
    目录下会增加一个.o文件:
    在这里插入图片描述

    4.3 可重定位目标elf格式

    在linux下生成hello.o文件elf格式的命令:readelf -a hello.o > hello.elf
    目录下会增加一个.elf文件:
    在这里插入图片描述
    在这里插入图片描述
    分析.elf文件中的内容:
    (1)ELF头:ELF头(ELF header)以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含了帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如x86-64)、节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量。不同节的位置和大小是有节头部表描述的,其中目标文件中每个节都有一个固定大小的条目(entry)。
    在这里插入图片描述
    (2)节头:记录各节名称、类型、地址、偏移量、大小、全体大小、旗标、链接、信息、对齐。
    在这里插入图片描述
    (3)重定位节:
    .rela.text,保存的是.text节中需要被修正的信息;任何调用外部函数或者引用全局变量的指令都需要被修正;调用外部函数的指令需要重定位;引用全局变量的指令需要重定位; 调用局部函数的指令不需要重定位;在可执行目标文件中不存在重定位信息。本程序需要被重定位的是printf、puts、exit、sleepsecs、getchar、sleep和.rodata中的.L0和.L1。
    .rela.eh_frame节是.eh_frame节重定位信息。
    在这里插入图片描述
    (4)符号表:.symtab,一个符号表,它存放在程序中定义和引用的函数和全局变量的信息,一些程序员错误地认为必须通过-g选项来编译一个程序,才能得到符号表信息。实际上每个可重定位目标文件在.symtab中都有一张符号表(除非程序员特意用STRIP命令去掉它)。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的条目。
    在这里插入图片描述

    4.4 Hello.o的结果解析

    命令:objdump -d -r hello.o > hello0.txt
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    与hello.s比较发现以下差别:
    (1)分支转移:在汇编代码中,分支跳转是直接以.L0等助记符表示,但在反汇编代码中,分支转移表示为主函数+段内偏移量。反汇编代码跳转指令的操作数使用的不是段名称,因为段名称知识在汇编语言中便于编写的助记符,所以在汇编成机器语言之后显然不存在,而是确定的地址。
    (2)函数调用:汇编代码中函数调用时直接个函数名称,而在反汇编的文件中call之后加main+偏移量(定位到call的下一条指令),即用具体的地址表示。在.rela.text节中为其添加重定位条目等待链接。
    (3)访问全局变量:汇编代码中使用.LC0(%rip),反汇编代码中为0x0(%rip),因为访问时需要重定位,所以初始化为0并添加重定位条目。

    4.5 本章小结

    概括了汇编的概念和作用,分析了ELF文件的内容,另外比较了重定位前汇编程序和重定位后反汇编的差别,了解从汇编语言翻译成机器语言的转换处理和机器语言和汇编语言的映射关系。

    第5章 链接

    5.1 链接的概念与作用

    概念:
    链接(linking)是将各种代码和数据片段收集并合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时(compile time),也就是在源代码被翻译成机器代码时;也可以执行于加载时(load time),也就是在程序被加载器(loader)加载到内存并执行时;甚至执行于运行时(run time),也就是在由应用程序来执行。
    作用:
    链接器在软件开发过程中扮演着一个关键的角色,因为它们使得分离编译(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的格式

    命令:readelf -a hello > hello1.elf
    在这里插入图片描述
    (1)ELF头:上次的节头数量为13个,这次变为25个。
    在这里插入图片描述
    (2)节头:
    在这里插入图片描述

    5.4 hello的虚拟地址空间

    在这里插入图片描述
    PHDR:保存程序头表
    INTERP:动态链接器的路径
    LOAD:可加载的程序段
    DYNAMIN:保存了由动态链接器使用的信息
    NOTE保存辅助信息
    GNU_STACK:标志栈是否可执行
    GNU_RELRO:指定重定位后需被设置成只读的内存区域
    使用edb打开hello从Data Dump窗口观察hello加载到虚拟地址的状况,并查看各段信息。
    在这里插入图片描述
    在0x400000~0x401000段中,程序被载入,自虚拟地址0x400000开始,到0x400fff结束,这之间每个节的地址同5.3中图(2)中的地址声明。

    5.5 链接的重定位过程分析

    命令: objdump -d -r hello > hello1.txt
    与hello.o生成的反汇编文件对比发现,hello1.txt中多了许多节。hello0.txt中只有一个.text节,而且只有一个main函数,函数地址也是默认的0x000000.hello1.txt中有.init,.plt,.text三个节,而且每个节中有许多的函数。库函数的代码都已经链接到了程序中,程序各个节变的更加完整,跳转的地址也具有参考性。
    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:初始化过的全局变量或者声明过的函数
    hello1.txt部分截图如下:
    在这里插入图片描述

    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
    (4)退出:exit
    程序名称 地址
    ld-2.27.so!_dl_start 0x7fb85a93aea0
    ld-2.27.so!_dl_init 0x7f9612138630
    hello!_start 0x400582
    lib-2.27.so!__libc_start_main 0x7f9611d58ab0
    hello!puts@plt 0x4004f0
    hello!exit@plt 0x400530

    5.7 Hello的动态链接分析

    在进行动态链接前,首先进行静态链接,生成部分链接的可执行目标文件hello。此时共享库中的代码和数据没有被合并到hello中。加载hello时,动态链接器对共享目标文件中的相应模块内的代码和数据进行重定位,加载共享库,生成完全链接的可执行目标文件。
    动态链接采用了延迟加载的策略,即在调用函数时才进行符号的映射。使用偏移量表GOT+过程链接表PLT实现函数的动态链接。GOT中存放函数目标地址,为每个全局函数创建一个副本函数,并将对函数的调用转换成对副本函数调用。
    在这里插入图片描述
    调用init之前的.got.plt
    在这里插入图片描述
    调用init之后的.got.plt
    从图中可以看到.got.plt的条目发生变化。

    5.8 本章小结

    概括了链接的概念和作用,重点分析了hello程序的虚拟地址空间、重定位和执行过程。简述了动态链接的原理

    第6章 hello进程管理

    6.1 进程的概念与作用

    概念:
    狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
    广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
    作用:
    进程作为一个执行中程序的实例,系统中每个程序都运行在某个进程的上下文中,上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。

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

    作用:
    是一种交互型的应用级程序,时Linux的外壳,提供了一个界面,用户可以通过这界面访问操作系统内核。
    处理流程:
    (1)从终端读入输入的命令。
    (2)将输入字符串切分获得所有的参数
    (3)如果是内置命令则立即执行
    (4)否则调用相应的程序为其分配子进程并运行
    (5)shell应该接受键盘输入信号,并对这些信号进行相应处理

    6.3 Hello的fork进程创建过程

    在终端中输入命令行./hello 1183710129 邓昆昆 1后,shell会处理该命令,如果判断出不是内置命令,则会调用fork函数创建一个新的子进程,子进程几乎但不完全与父进程相同。通过fork函数,子进程得到与父进程用户级虚拟地址空间相同的但是独立的一份副本,拥有不同的PID。

    6.4 Hello的execve过程

    execve的功能是在当前进程的上下文中加载并运行一个新程序。在执行fork得到子进程后随即使用解析后的命令行参数调用execve,execve调用启动加载器来执行hello程序。加载器执行的操作是,加删除子进程现有的虚拟内存段,并创建新的代码、数据、堆和栈段。代码和数据段被初始化为hello的代码和数据。堆和栈被置空。然后加载器将PC指向hello程序的起始位置,即从下条指令开始执行hello程序。

    6.5 Hello的进程执行

    逻辑控制流:如果想用调试器单步执行程序,我们会看到一系列的程序计数器(PC)的值,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或是包含在运行时动态链接到程序的共享对象中的指令。这个PC值的序列叫做逻辑控制流,或者简称为逻辑流。
    上下文切换:如果系统调用因为等待某个事件发生而阻塞,那么内核可以让当前进程休眠,切换到另一个进程,上下文就是内核重新启动一个被抢占的进程所需要的状态,是一种比较高层次的异常控制流。。
    时间片:一个进程执行它的控制流的一部分的每一时间段叫做时间片。
    用户模式和内核模式:shell使得用户可以有机会修改内核,所以需要设置一些防护措施来保护内核,如限制指令的类型和可以作用的范围。

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

    6.6 hello的异常与信号处理

    (以下格式自行编排,编辑时删除)
    hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
    程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps jobs pstree fg kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。
    执行过程可能出现的异常一共有四种:中断、陷阱、故障、终止。
    中断:来自I/O设备的信号,异步发生,总是返回到下一条指令。
    陷阱:有意的异常,同步发生,总是返回到下一条指令。
    故障:潜在可恢复的错误,同步发生,可能返回到当前指令或终止。
    终止:不可恢复的错误,同步发生,不会返回。
    (1)正常运行
    在这里插入图片描述
    (2)ctrl+c终止
    在这里插入图片描述
    (3)ctrl+z暂停,输入ps可以发现hello并未关闭
    在这里插入图片描述
    (4)运行过程中乱按,无关输入被缓存到stdin,并随着printf指令被输出到结果。
    在这里插入图片描述

    6.7本章小结

    概括了进程的概念和作用、shell-bash的处理过程与作用,介绍了fork和execve进程以及hello进程的执行过程中的信号异常处理过程。

    第7章 hello的存储管理

    7.1 hello的存储器地址空间

    (1)逻辑地址:格式为“段地址:偏移地址”,是CPU生成的地址,在内部和编程使用,并不唯一。
    (2)物理地址:加载到内存地址寄存器中的地址,内存单元的真正地址。CPU通过地址总线的寻址,找到真实的物理内存对应地址。在前端总线上传输的内存地址都是物理内存地址。
    (3)虚拟地址:保护模式下程序访问存储器所用的逻辑地址。
    (4)线性地址:逻辑地址向物理地址转化过程中的一步,逻辑地址经过段机制后转化为线性地址。

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

    8086共设计了20位宽的地址总线,通过将段寄存器左移4位加上偏移地址得到20位地址,即逻辑地址。将内存分为不同的段,每个段有段寄存器对应,段寄存器有一个栈、一个代码、两个数据寄存器。

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

    系统将虚拟页作为进行数据传输的单元。Linux下每个虚拟页大小为4KB。物理内存也被分割为物理页, MMU(内存管理单元)负责地址翻译,MMU使用页表将虚拟页到物理页的映射,即虚拟地址到物理地址的映射。
    在这里插入图片描述

    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支持下的物理内存访问

    使用7.4环境中获得的PA,首先取组索引对应位,向L1cache中寻找对应组。如果存在,则比较标志位,并检查对应行的有效位是否为1。如果上述条件均满足则命中。否则按顺序对L2cache、L3cache、内存进行相同操作,直到出现命中。然后向上级cache返回直到L1cache。如果有空闲块则将目标块放置到空闲块中,否则将缓存中的某个块驱逐,将目标块放到被驱逐块的原位置。

    7.6 hello进程fork时的内存映射

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

    7.7 hello进程execve时的内存映射

    execve函数调用驻留在内核区域的启动加载器代码,在当前进程中加载并运行包含在可执行目标文件hello中的程序,用hello程序有效地替代了当前程序。加载并运行hello需要以下几个步骤:
    (1)删除已存在的用户区域,删除当前进程虚拟地址的用户部分中的已存在的区域结构。
    (2)映射私有区域,为新程序的代码、数据、bss和栈区域创建新的区域结构,所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为hello文件中的.text和.data区,bss区域是请求二进制零的,映射到匿名文件,其大小包含在hello中,栈和堆地址也是请求二进制零的,初始长度为零。
    (3)映射共享区域, hello程序与共享对象libc.so链接,libc.so是动态链接到这个程序中的,然后再映射到用户虚拟地址空间中的共享区域内。
    (4)设置程序计数器(PC),execve做的最后一件事情就是设置当前进程上下文的程序计数器,使之指向代码区域的入口点。
    在这里插入图片描述

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

    如果程序执行过程中遇到了缺页故障,则内核调用缺页处理程序。处理程序会进行如下步骤:检查虚拟地址是否合法,如果不合法则触发一个段错误,程序终止。然后检查进程是否有读、写或执行该区域页面的权限,如果不具有则触发保护异常,程序终止。在两步检查都无误后,内核选择一个牺牲页面,如果该页面被修改过则将其交换出去,换入新的页面并更新页表。然后将控制转移给hello进程,再次执行触发缺页故障的指令。
    在这里插入图片描述

    7.9动态存储分配管理

    printf函数会调用malloc,下面简述动态内存管理的基本方法与策略:
    动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。
    分配器分为两种基本风格:显式分配器、隐式分配器。
    显式分配器:要求应用显式地释放任何已分配的块。
    隐式分配器:要求分配器检测一个已分配块何时不再使用,那么就释放这个块,自动释放未使用的已经分配的块的过程叫做垃圾收集。

    堆中的块主要组织为两种形式:
    1.隐式空闲链表(带边界标记)
    在块的首尾的四个字节分别添加header和footer,负责维护当前块的信息(大小和是否分配)。由于每个块是对齐的,所以每个块的地址低位总是0,可以用该位标注当前块是否已经分配。可以利用header和footer中存放的块大小寻找当前块两侧的邻接块,方便进行空闲块的合并操作。
    2.显式空闲链表
    在未分配的块中添加两个指针,分别指向前一个空闲块和后一个空闲块。采用该策略,使首次适配的分配时间从块总数的线性时间减少到了空闲块数量的线性时间。

    7.9.1带边界标记的隐式空闲链表

    (1)堆及堆中内存块的组织结构:
    在这里插入图片描述
    在内存块中增加4B的Header和4B的Footer,其中Header用于寻找下一个blcok,Footer用于寻找上一个block。Footer的设计是专门为了合并空闲块方便的。因为Header和Footer大小已知,所以我们利用Header和Footer中存放的块大小就可以寻找上下block。
    (2)隐式链表
    所谓隐式空闲链表,对比于显式空闲链表,代表并不直接对空闲块进行链接,而是将对内存空间中的所有块组织成一个大链表,其中Header和Footer中的block大小间接起到了前驱、后继指针的作用。
    (3)空闲块合并
    因为有了Footer,所以我们可以方便的对前面的空闲块进行合并。合并的情况一共分为四种:前空后不空,前不空后空,前后都空,前后都不空。对于四种情况分别进行空闲块合并,我们只需要通过改变Header和Footer中的值就可以完成这一操作。

    7.9.2显示空间链表

    将空闲块组织成链表形式的数据结构。堆可以组织成一个双向空闲链表,在每个空闲块中,都包含一个pred(前驱)和succ(后继)指针,如下图:
    在这里插入图片描述
    使用双向链表而不是隐式空闲链表,使首次适配的分配时间从块总数的线性时间减少到了空闲块数量的线性时间。维护链表的顺序有:后进先出(LIFO),将新释放的块放置在链表的开始处,使用LIFO的顺序和首次适配的放置策略,分配器会最先检查最近使用过的块,在这种情况下,释放一个块可以在线性的时间内完成,如果使用了边界标记,那么合并也可以在常数时间内完成。按照地址顺序来维护链表,其中链表中的每个块的地址都小于它的后继的地址,在这种情况下,释放一个块需要线性时间的搜索来定位合适的前驱。平衡点在于,按照地址排序首次适配比LIFO排序的首次适配有着更高的内存利用率,接近最佳适配的利用率。

    7.10本章小结

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

    第8章 hello的IO管理

    8.1 Linux的IO设备管理方法

    设备的模型化:所有IO设备都被模型化为文件,所有的输入和输出都能被当做相应文件的读和写来执行。
    设备管理:Linux内核有一个简单、低级的接口,成为Unix I/O,是的所有的输入和输出都能以一种统一且一致的方式来执行。

    8.2 简述Unix IO接口及其函数

    Unix I/O接口统一操作:
    (1)打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件,内核记录有关这个打开文件的所有信息。
    (2) Shell创建的每个进程都有三个打开的文件:标准输入,标准输出,标准错误。
    (3)改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0,这个文件位置是从文件开头起始的字节偏移量,应用程序能够通过执行seek,显式地将改变当前文件位置k。
    (4)读写文件:一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n,给定一个大小为m字节的而文件,当k>=m时,触发EOF。类似一个写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。
    (5)关闭文件,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中去。

    Unix I/O函数:
    (1) int open(char* filename,int flags,mode_t mode) ,进程通过调用open函数来打开一个存在的文件或是创建一个新文件的。open函数将filename转换为一个文件描述符,并且返回描述符数字,返回的描述符总是在进程中当前没有打开的最小描述符,flags参数指明了进程打算如何访问这个文件,mode参数指定了新文件的访问权限位。
    (2)int close(fd),fd是需要关闭的文件的描述符,close返回操作结果。
    (3)ssize_t read(int fd,void *buf,size_t n),read函数从描述符为fd的当前文件位置赋值最多n个字节到内存位置buf。返回值-1表示一个错误,0表示EOF,否则返回值表示的是实际传送的字节数量。
    (4)ssize_t wirte(int fd,const void *buf,size_t n),write函数从内存位置buf复制至多n个字节到描述符为fd的当前文件位置。

    8.3 printf的实现分析

    前提:printf和vsprintf代码是windows下的。
    查看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;
    }
    首先arg获得第二个不定长参数,即输出的时候格式化串对应的值。
    查看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’: //只处理%x一种情况
    itoa(tmp, ((int)p_next_arg)); //将输入参数值转化为字符串保存在tmp
    strcpy(p, tmp); //将tmp字符串复制到p处
    p_next_arg += 4; //下一个参数值地址
    p += strlen(tmp); //放下一个参数值的地址
    break;
    case ‘s’:
    break;
    default:
    break;
    }
    }
    return (p - buf); //返回最后生成的字符串的长度
    }
    则知道vsprintf程序按照格式fmt结合参数args生成格式化之后的字符串,并返回字串的长度。
    在printf中调用系统函数write(buf,i)将长度为i的buf输出。write函数如下:
    write:
    mov eax, _NR_write
    mov ebx, [esp + 4]
    mov ecx, [esp + 8]
    int INT_VECTOR_SYS_CALL
    在write函数中,将栈中参数放入寄存器,ecx是字符个数,ebx存放第一个字符地址,int INT_VECTOR_SYS_CALLA代表通过系统调用syscall,查看syscall的实现:
    sys_call:
    call save
    push dword [p_proc_ready]
    sti
    push ecx
    push ebx
    call [sys_call_table + eax * 4]
    add esp, 4 * 3
    mov [esi + EAXREG - P_STACKBASE], eax
    cli
    ret
    syscall将字符串中的字节“Hello 1183710129 邓昆昆”从寄存器中通过总线复制到显卡的显存中,显存中存储的是字符的ASCII码。
    字符显示驱动子程序将通过ASCII码在字模库中找到点阵信息将点阵信息存储到vram中。
    显示芯片会按照一定的刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
    于是我们的打印字符串“Hello 1183710129 邓昆昆”就显示在了屏幕上。

    8.4 getchar的实现分析

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

    8.5本章小结

    Linux提供了一种简单使用的抽象——将系统的IO设备抽象成文件,系统的输入和输出被抽象成文件的写和读操作。在此基础上,Linux对系统IO的操作可以以打开文件、改变文件位置、读写文件、关闭文件的操作进行。同时分析了printf和getchar的实现。
    结论
    空床坐听南窗雨,谁复挑灯夜补书。hello终于走完了它艰辛的路程:
    (一) 编写,将代码键入hello.c
    (二) 预处理,将hello.c调用的所有外部的库展开合并到一个hello.i文件中
    (三) 编译,将hello.i编译成为汇编文件hello.s
    (四) 汇编,将hello.s会变成为可重定位目标文件hello.o
    (五) 链接,将hello.o与可重定位目标文件和动态链接库链接成为可执行目标程序hello
    (六) 运行:在shell中输入./hello 1183710129 邓昆昆
    (七) 创建子进程:shell进程调用fork为其创建子进程
    (八) 运行程序:shell调用execve,execve调用启动加载器,加映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入 main函数。
    (九) 执行指令:CPU为其分配时间片,在一个时间片中,hello享有CPU资源,顺序执行自己的控制逻辑流
    (十) 访问内存:MMU将程序中使用的虚拟内存地址通过页表映射成物理地址。
    (十一) 动态申请内存:printf会调用malloc向动态内存分配器申请堆中的内存。
    (十二) 信号:如果运行途中键入ctr-c ctr-z则调用shell的信号处理函数分别停止、挂起。
    (十三) 结束:shell父进程回收子进程,内核删除为这个进程创建的所有数据结构。
    秋寒冬凛里带一点温热,为你解冻冰河。愿逐月华,照君夕永。
    附件
    hello.c:源代码
    hello.i:预处理后的文本文件
    hello.s:编译之后的汇编文件
    hello.o:汇编之后的可重定位目标执行文件
    hello:链接之后的可执行文件
    hello.elf:hello.o的ELF格式
    hello1.elf:hello的ELF格式
    hello0.txt:hello.o反汇编代码
    hello1.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.

    展开全文
  • 哈尔滨工业大学计算机科学与技术学院 实验报告 课程名称 数据结构与算法 课程类型必修 实验项目名称 线性表及其应用 实验题目 算术表达式求值 班级计算机科学与技术四班 学号 1110310422 姓名胡明 设计成绩 报告成绩...
  • 2021年3月31日下午,启智社区校园行活动在哈工大(深圳)教学楼举行,本次活动由计算机科学与技术学院承办,华为昇腾领域专家、启智社区工作人员参与交流分享。 第一部分由华为昇腾领域的专家何剑向同学们介绍华为...

    2021年3月31日下午,启智社区校园行活动在哈工大(深圳)教学楼举行本次活动由计算机科学与技术学院承办,华为昇腾领域专家、启智社区工作人员参与交流分享

    第一部分由华为昇腾领域的专家何剑向同学们介绍华为昇腾AI全栈创新的理念及探索,并基于此建设的业界最强AI算力平台,同时分享了华为昇腾赋能各行业的优秀实践与案例。
    在这里插入图片描述

    第二部分由华为昇腾产业部的哈工大(深圳)校友孔炽斌分享自己在昇腾产业部的工作生活经历,特别是从毕业入职华为的职场小白到逐渐接手部门重要工作的成长历程。校友分享后,由华为资深HR为大家介绍华为的招聘、实习相关情况,为同学们详细讲解了昇腾产品线各个职位的职能范畴,帮助同学们更加深入清晰地了解昇腾产品线的日常运作及工作详情。
    在这里插入图片描述

    最后一部分是现场自由交流环节,同学们向技术专家、资深HR积极提问,分享嘉宾也对各类问题进行耐心讲解,进一步加强了同学们有关开发实践、开源理念、职场发展等方面的理解。
    在这里插入图片描述
    作为启智社区“启梦行动”的重要组成部分,启智社区校园行系列活动旨在培育高校环境中的开源开放文化,为人工智能开源事业培养人才。未来,启智社区将定期在高校组织开展开源开放相关的各类活动,吸引学生了解并参与到开源开放项目建设中。参与本次活动的同学们纷纷表示收获颇丰,“启智社区,确实给力”名不虚传。

    展开全文
  • 哈工大 复习总结

    2012-03-21 20:04:31
    2012年,计算机科学与技术学院复试总结,包括4门课程,除了离散外,基本都包含。
  • 人工智能实验报告 人工智能导论实验报告 学院计算机科学与技术学院 专业计算机科学与技术 1 人工智能实验报告 2016.12.20 目录 人工智能导论实验报告 1 一 简介 (对该实验背景方法以及目的的理解 ) 4 1. 实验背景 4 ...
  • 第6章 内部排序 数据结构与算法 Data Structrues and Algorithms 张岩 海量数据计算研究中心 哈工大计算机科学与技术学院 2017/12/192017/12/192017/12/19 哈工大计算机科学与技术学院 张岩 Slide 6-1 第6章 内部...
  • 第4章图结构及其应用算法 数据结构与算法 数据结构与算法 Data Structures and Algorithms 张岩 哈工大计算机科学与技术学院 2010/12/16 哈工大计算机科学与技术学院 张岩 Slide 4-1 第4章 图结构及其应用算法 第4章...
  • 第5章查找结构 数据结构与算法 Data Structures and Algorithms 张岩 海量数据计算研究中心 哈工大计算机科学与技术学院 2017/12/52017/12/52017/12/5 哈工大计算机科学与技术学院 张岩 Slide 5-1 第5章查找结构 ...
  • 计算机科学与技术学院院长王亚东教授邀请,在国际项目管理中心的大力支持下,7位来自美国和欧洲的顶尖计算机学者将于2018年6月18日至24日访问我院,为师生带来为期一周的...
  • 计算机系统 大作业 题 目 程序人生-Hello’s P2P 专 业 计算机类 学 号 1180300508 ...计算机科学与技术学院 2019年12月 摘 要 本文通过对hello预处理、编译、汇编、链接的过程以及在此...
  • 本博客为哈工大计算机科学与技术学院大二软件构造课程的课件翻译。课程要求书写博客记录学习感悟。这一版本基本上为机器翻译,还存在很多错误,请谨慎参考
  • 来源:读特在日前召开的第十八届中国计算语言学大会(The Eighteenth China National Conference on Computational Linguistics,CCL 2019)上,哈工大(深圳)计算机科学与技术学院硕士生鲍建竹、蓝恭强、博士生巫继鹏...
  • 出席揭牌仪式的有哈工大(深圳)计算机科学与技术学院党委书记、联合实验室主任叶允明教授及其团队成员,爱知之星创始人&董事长李贵生、爱知之星副总&联合实验室副主任陈祖家,爱知之星中高管及研发团队。AI...
  • 2021年3月24日,哈工大(深圳)-南大通用云数据库研究中心签约暨揭牌仪式在哈工大深圳校区顺利举行,来自哈尔滨工业大学(深圳)的校长助理魏军教授、哈工大(深圳)计算机科学与技术学院院长王轩教授与南大通用高级...
  • 计算机科学与技术学院 实验报告 课程名称:机器学习 课程类型:选修 实验题目:k-means聚类和混合高斯模型 学号:1170300511 姓名:易亚玲 ...
  • 本博客为哈工大计算机科学与技术学院大二软件构造课程的Lab4实验报告。 该PDF28页左右,主题为异常与错误处理、防御式编程、测试和调试, 该博客不作为课程要求的博客记录数量,仅仅是个人的分享,为在Lab4已经结束...
  • 计算机系统 大作业 题 目 程序人生-Hello’s P2P 专 业 软件工程 ...计算机科学与技术学院 2019****年12月 摘 要 对于每个程序员来说,Hello World是一个开始,本论文目的在于利用gcc、edb等工具,...
  • 哈工大计算机科学与技术学院 软件基础教研室 操作系统 Operating Systems L32 目录解析代码实现 Directory Resolution lizhijun_os@ 综合楼 室 411 授课教师李治军 完成全部映射下的磁盘使用 用户 读test.c 202-212...
  • 哈工大计算机科学与技术学院 软件基础教研室 操作系统 Operating Systems L18 信号量的代码实现 Coding Semaphore lizhijun_os@ 授课教师李治军 综合楼 室 411 可以操刀了 从纸上到实际 Producer(item) { 用户态程序...
  • - 本博客为哈工大计算机科学与技术学院大二**软件构造**课程的**Lab2实验报告**。 - 该PDF28页左右,主题为**抽象数据类型ADT与面向对象编程OOP**, - 该博客**不作为课程要求的博客记录数量**,仅仅是个人的分享,...
  • 计算机科学与技术学院/国家示范性软件学院/威海/深圳分数线320 50/50/75/80081200计算机科学与技术【皮皮灰】【本部进复试41人,总分均分336】【政英数专平均分:76、73、95、109】【深圳进复试78人,总分均分353...
  • 哈工大(威海)计算机科学与技术学院 软件设计程序II的实验报告,基于QT,C++实现的简单饮食健康助手小程序,具有一定的数据结构知识的构建。原作者,可私聊源码。
  • 名 联系方式 哈尔滨工业大学威海计算机科学与技术学院 2014?年秋季学期软件工程 大作业一电子商务网站需求分析 聚美优品 哈工大威海计算机学院软件工程 大作业一电子商务网站需求分析 目 录 1?需求陈述1 1.1?业务...
  • 1997年至2008年就读于哈尔滨工业大学计算机科学与技术学院,获工学博士学位。主要研究方向为自然语言处理、机器翻译、机器学习等。参与过多项国家自然科学基金、国家“863”项目的研究工作,在国内外会议和期刊上...

空空如也

空空如也

1 2
收藏数 32
精华内容 12
关键字:

哈工大计算机科学与技术学院