CSapp 大作业

计算机系统

大作业

题 目 程序人生-hello’s p2p
专 业 计算机科学与技术
学   号 1182910105
班   级 1836101
学 生 马容与    
指 导 教 师 刘宏伟

计算机科学与技术学院
2019年12月
摘 要
本文通过对hello程序p2p的整体介绍,阐述了hello经过多种处理后是如何在电脑在运行实现从.c文件成为process的,并且hello的进程从创建到结束回收等的全过程。通过这些展示与分析,我们更好地说明了计算机的底层实现,并且更深地了解了整个程序的生命周期。

关键词:p2p;020;进程;计算机系统; …

(摘要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简介

  1. p2p: 从程序到进程 。hello.c经过cpp的预处理、ccl的编译、as的汇编、ld的链接最终成为二进制的可执行目标程序hello,并在shell中新建进程来执行
  2. 020: shell通过execve加载并执行hello,为其映射虚拟内存,开始运行程序后将其分配并载入物理内存;执行目标代码,将其输出至屏幕;程序运行结束,shell回收hello进程与内存空间
    1.2 环境与工具
  3. 硬件环境:x64 cpu;2.11 ghz;8g ram;256g ssd;
  4. 软件环境:win10 64位;vmware 15;ubuntu 16.04 64位
  5. 开发工具:codeblocks
    1.3 中间结果
    hello.c:源代码
    hello.i:预处理后的文本文件
    hello.s编译后的汇编文件
    hello.o:汇编后的可重定位目标文件
    hello:链接后的可执行文件
    1.4 本章小结
    本章对hello的p2p、020过程进行了简单介绍,并列举出了实验的环境与中间结果,对hello进行了概述。
    (第1章0.5分)

第2章 预处理
2.1 预处理的概念与作用

  1. 概念:预处理器(cpp)根据以#开头的命令,修改原始的c程序
  2. 作用:让编译器在随后对文本进行编译的过程中,更加方便,因为访问库函数这类操作在预处理阶段已经完成,减少了编译器的工作
    2.2在ubuntu下预处理的命令
    linux>gcc –e –o hello.i hello.c

2.3 hello的预处理结果解析
预处理之前,程序包含注释内容、头文件、全局变量和main函数;预处理之后,hello.i字节与行数比hello.c大幅增长,main函数在文件最后部分,没有了注释部分,并将需要用到的库的地址与库中的函数添加进来了。

2.4 本章小结
本章介绍了由hello.c到hello.i的预处理阶段,通过预处理器对hello.c文件的头文件、宏定义与注释进行操作,将涉及的系统头文件的代码补充进入程序。
(第2章0.5分)

第3章 编译
3.1 编译的概念与作用
1. 概念:指编译器(ccl)对hello.i文件进行处理的过程,使得编译器完成对代码规范性的检查与对语法语义的分析,生成汇编代码并将其保存在hello.s文件之中。
2. 作用:排查语法错误并反馈,同时生成汇编代码以便于汇编器继续进行操作
3.2 在ubuntu下编译的命令
linux>gcc –s hello.i –o hello.s

3.3 hello的编译结果解析
1. 开头部分:

.file:源文件名;.globl:全局变量;.data:数据段;.align:对齐方式;.type:指定对象类型或函数类型;.size:大小;.long:长整型;.string:字符串;.text:代码段
2. 数据:可分为全局变量、局部变量、指针数组
a). int sleepsecs = 2.5:其中sleepsecs为已初始化的全局变量,存放在.data段,由上图可知为全局变量globl,对齐方式为4,类型为object,大小为4字节。
b). int argc;argc为函数传入的int型参数,存储在%edi之中。
c). int i(movl $0, -4(%rbp));局部变量,保存至寄存器或栈之中,占用了4字节栈空间。
3. 赋值:
a). 对全局变量sleepsecs:赋初值为2并存储在.rodata段

b). 对局部变量i:使用movl语句,使其赋值为0

  1. 类型转换:sleepsecs为整型数int,而当给其赋值浮点数2.5时,自动舍入为2而隐式赋值

  2. 算术操作:将i++编译为如下,进行寻址并将地址中的值+1

  3. 关系操作:设置条件码cc并进行判断跳转
    a). i < 10编译为如下

    b). 将argc != 3编译为如下

  4. 控制转移:if的判断操作可等效于由上面所说的关系操作的判断后来跳转;如for(i = 0; i < 10; i++)即在循环里执行与转移跳出循环。

  5. 数组/指针/结构操作:指针数组char argv[]:argv[0]指向程序的路径与名称,argv[1]和argv[2]保存了两个字符串,char占8个字节。对于数组中的其他元素,由于数组申请时是连续地址,所以用起始地址加偏移量表示,如(%rax)和%rax+8表示argv[1]和argv[2]两个字符串

  6. 函数操作:%rax寄存器保存函数返回值,函数内参数可保存至如%rdi、%rsi、%rdx、%rcx等,更多参数可在栈中保存。

3.4 本章小结
本章介绍了编译器将文本编译成汇编代码的过程。通过上述步骤操作,我们可以发现编译过程会对各种操作进行解析并且优化后进行翻译而非逐字逐句编译下来。最终,生成hello.s文件
(第3章2分)

第4章 汇编
4.1 汇编的概念与作用
1. 概念:汇编器(as)将hello.s翻译为机器语言指令,把这些指令打包成可重定位目标程序的格式,并将结果保存至二进制目标文件hello.o文件中,其中包含程序的指令编码。
2. 作用:将汇编代码(人可以读懂)翻译成二进制代码(机器可以读懂并执行)
4.2 在ubuntu下汇编的命令
linux>gcc –c hello.s –o hello.o

4.3 可重定位目标ELF格式

  1. ELF头:描述生成该文件的系统的字的大小和字节顺序,并包含了帮助链接器语法分析和解释目标文件的信息

  2. 节头部表:描述了不同节的位置和大小,包括节的名称、类型、地址和偏移量等。

  3. 可重定位节:告诉链接器在将目标文件合并为可执行文件时如何修改引用。

  4. .symtab节:存放程序中定义和引用的函数和全局变量信息的符号表

4.4 hello.o的结果解析
linux> objdump -d -r hello.o

对比hello.s(可见之前章节)与反汇编代码,差别有如下四点:

  1. 操作数:hello.s为十进制,反汇编代码为十六进制
  2. 分支转移:hello.s为段名称,反汇编代码为相对偏移地址
  3. 函数调用:hello.s为call+函数名称,反汇编代码为call+函数相对偏移地址
  4. 全局变量访问:对于.rodata、sleepsecs的访问,hello.s为$.LC0、sleepsecs(%rip),反汇编为$0x0、0(%rip)
    4.5 本章小结
    本章主要介绍了通过汇编操作将汇编语言转化为机器语言的过程,并通过对比hello.s与反汇编代码,了解了汇编语言到机器语言转变的实现细节。
    (第4章1分)

第5章 链接
5.1 链接的概念与作用

  1. 概念:通过链接器(ld)将各种代码和数据片段收集并组合成一个可被加载(复制)到内存并执行的单一文件的过程。
  2. 作用:使得代码方便简洁,可移植性强,模块化程度高。
    5.2 在ubuntu下链接的命令
    linux>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的格式
保存了hello中各个节的信息,包括了名称、大小、地址、偏移量,以及程序的入口点。可以发现hello比hello.o节的数目对了近一倍,是因为链接之后添加了其他文件。

5.4 hello的虚拟地址空间

查看只读内存段与读写内存段信息。可看出.text节开始于00000000004004d0,此外还有目标文件的偏移、内存地址、段大小、内存中的段大小等内容。
5.5 链接的重定位过程分析
linux> objdump -d -r hello

对比hello和hello.o的反汇编代码,有四点不同:

  1. 在hello中链接加入了新的函数
  2. hello增加了.init和.plt节和一些节中定义的函数
  3. hello中无hello.o中的重定位条目,而是虚拟内存地址
  4. hello.o中的相对偏移地址变为了hello中的虚拟内存地址
    5.6 hello的执行流程
    0x400430 init;
    0x400460 puts@plt;
    0x400470 printf@plt
    0x400480 __libc_start_main@plt;
    0x400490 getchar@plt;
    0x4004a0 exit@plt;
    0x4004b0 sleep@plt;
    0x4004d0 _start;
    0x4004fe main
    0x400580 __libc_csu_init;
    0x4005f0 __libc_csu_fini;
    0x4005f4 _fini;
    5.7 hello的动态链接分析
    global_offset表dl_init前:

dl_init后:

说明在执行过_dl_init后global_offset被赋值了相应的偏移量,即该操作为给程序赋值当前执行的内存地址偏移量
5.8 本章小结
本章主要介绍了链接的过程,将各种代码和数据片段收集并组成一个单一文件。通过比较反汇编代码,更高的理解了链接与重定位的过程。
(第5章1分)

第6章 hello进程管理
6.1 进程的概念与作用

  1. 概念:一个执行中的程序的实例

  2. 作用:通过进程,似乎程序独占得使用处理器和内存,无间断地一条条执行程序中的指令,程序中的代码和数据似乎是系统内存中唯一的对象
    6.2 简述壳shell-bash的作用与处理流程

  3. 作用:在操作系统中提供交互界面,可访问操作系统的内核服务,将用户输入的命令解释并送到内核。

  4. 处理流程:读取用户输入、将输入内容解释转换、执行内核命令或分配新的子进程运行、监控键盘输入命令
    6.3 hello的fork进程创建过程
    父进程通过调用fork函数创建一个新的子进程。子进程复制但独立使用父进程的代码、数据段、堆、共享库等。fork函数调用一次,返回两次,其中父进程返回子进程PID,子进程返回0。
    6.4 hello的execve过程
    execvc函数使得当前进程的上下文中加载并运行一个新的程序。execvc不是创建新进程而是直接在当前进程中删除已存在的用户区域、映射私有区域、映射共享区域、设置PC,从而加载一组新的代码、数据、段、用户栈的段。
    6.5 hello的进程执行

  5. 上下文信息:内核重新启动一个被抢占的进程所需要的状态,由通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成

  6. 进程时间片:一个进程执行它的控制流的一部分的每一时间段

  7. 用户模式与内核模式:内核模式(上帝模式)有最高的访问权限,甚至可以停止处理器、改变模式位,或者发起一个I/O操作,处理器使用一个寄存器当作模式位,描述当前进程的特权。当进程中断、故障或者陷入系统调用时,才会将模式位设置成上帝模式,得到内核访问权限,其他情况下都始终在用户权限中,就能够保证系统的绝对安全

  8. 进程执行:在Hello执行的某些时刻,比如sleep函数,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程,这种决策就叫做调度。当内核调度了一个新的进程运行后,它就抢占Hello进程,并且使用上下文切换机制来将控制转移到新的进程。Hello进程初始运行在用户模式中,直到Hello进程中的sleep系统调用,它显式地请求让Hello进程休眠,内核可以决定执行上下文切换,进入到内核模式。当定时器2.5后中断时,内核就能判断当前Hello休眠运行了足够长的时间,切换回用户模式。
    6.6 hello的异常与信号处理

  9. 会出现的异常:中断——信号SIGTSTP,默认行为是停止直到下一个SIGCONT;终止——信号SIGINT,默认行为是终止

  10. 各命令情况:
    a). 正常运行:运行时什么都不按,程序执行完进程回收,再按enter结束。

    b). 执行ctrl+z: shell父进程会收到一个SIGSTP信号,这个信号的功能是将程序挂起并且放到后台。通过ps命令我们可以看到,hello命令并没有结束。接下来我们用fg命令将JID最大的放到前台,也就是刚刚挂起的hello进程,可以看到进程又继续执行完了剩下循环。

    c). 执行ctrl+c:父进程收到sigint信号,终止并回收hello进程。通过ps可看到没有hello进程了。

    d). jobs命令:列出当前shell中已启动的任务状态

    e). pstree命令:将所有进程按照树状结构print。(图为部分截图)

6.7本章小结
本章介绍了进程的概念和具体使用,并且阐述了利用shell对进程进行监控、发送信号等一系列操作方法。
(第6章1分)

第7章 hello的存储管理
7.1 hello的存储器地址空间

  1. 逻辑地址:包含在机器语言中用来指定一个操作数或一条指令的地址
  2. 线性地址:逻辑地址到物理地址变换之间的中间层
  3. 虚拟地址:一个带虚拟内存的系统中,CPU从一个有N=2^n个地址空间中生成虚拟地址
  4. 物理地址:用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应
    7.2 intel逻辑地址到线性地址的变换-段式管理
    逻辑地址由段标识符、段内偏移量两部分组成,其中段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号,后面3位包含一些硬件细节。索引号是“段描述符(segment descriptor)”的索引,很多个段描述符,就组了一个数组,叫“段描述符表”,这样,可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段,每一个段描述符由8个字节组成。其中Base字段,它描述了一个段的开始位置的线性地址,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中,由段选择符中的T1字段表示选择使用哪个,=0,表示用GDT=1表示用LDT。GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。
    转换步骤:
  5. 给定一个完整的逻辑地址
  6. 看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。可以得到一个数组。
  7. 取出段选择符中前13位,在数组中查找到对应的段描述符,得到Base,也就是基地址。
  8. 线性地址 = Base + offset。
    7.3 hello的线性地址到物理地址的变换-页式管理
    线性地址到物理地址的转换是通过页的这个概念完成的。线性地址被分为以固定长度为单位的组,称为页。目录中的每一个目录项,就是一个地址——对应的页的地址。 另一类“页”,我们称之为物理页,或者是页框(frame)、页桢的。是分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。
    下图是二级管理模式图:

分页单元中,页目录是唯一的,它的地址放在CPU的cr3寄存器中,是进行地址转换的开始点。每一个活动的进程,因为都有其独立的对应的虚似内存(页目录也是唯一的),那么它也对应了一个独立的页目录地址。——运行一个进程,需要将它的页目录地址放到cr3寄存器中,将别个的保存下来。
CPU芯片上有一个专门的硬件叫做内存管理单元(MMU),这个硬件的功能就是动态的将虚拟地址翻译成物理地址的。这个表示如何工作的呢,如图7-5所示。N为的虚拟地址包含两个部分,一个p位的虚拟页面偏移(VPO)和一个(n-p)位的虚拟页号(VPN)。MMU利用VPN来选择适当的PTE(页表条目)。接下来在对应的PTE中获得PPN(物理页号),将PPN与VPO串联起来,就得到了相应的物理地址。
7.4 tlb与四级页表支持下的va到pa的变换
36位的虚拟地址被分割成4个9位的片。CR3寄存器包含L1页表的物理地址。VPN1有一个到L1 PTE的偏移量,找到这个PTE以后又会包含到L2页表的基础地址;VPN2包含一个到L2PTE的偏移量,找到这个PTE以后又会包含到L3页表的基础地址;VPN3包含一个到L3PTE的偏移量,找到这个PTE以后又会包含到L4页表的基础地址;VPN4包含一个到L4PTE的偏移量,找到这个PTE以后就是相应的PPN(物理页号)。

7.5 三级cache支持下的物理内存访问
使用CI(后六位再后六位)进行组索引,每组8路,对8路的块分别匹配 CT(前40位)如果匹配成功且块的valid标志位为1,则命中(hit),根据数据偏移量CO(后六位)取出数据返回。 如果没有匹配成功或者匹配成功但是标志位是 1,则不命中(miss),向下一级缓存中查询数据(L2 Cache->L3 Cache->主存)。查询到数据之后,一种简单的放置策略如下:如果映射到的组内有空闲块,则直接放置,否则组内都是有效块,产生冲突(evict),则采用最近最少使用策略 LFU 进行替换。
7.6 hello进程fork时的内存映射

  1. mm_struct(内存描述符):描述了一个进程的整个虚拟内存空间。
  2. vm_area_struct(区域结构描述符):描述了进程的虚拟内存空间的一个区间。
  3. 步骤:创建当前进程的mm_struct,vm_area_struct和页表的原样副本。两个进程的每个页面都标记为只读页面。两个进程的每个vm_area_struct都标记为私有,这样就只能在写入时复制。
    7.7 hello进程execve时的内存映射
    execve函数在shell中加载并运行包含在可执行目标文件hello中的程序,用hello程序有效的替代了当前程序。加载并运行hello需要以下几个步骤:
    删除已存在的用户区域。删除shell虚拟地址的用户部分中的已存在的区域结构。
    映射私有区域。为hello的代码、数据、bss 和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为hello 文件中的.text和.data 区。bss 区域是请求二进制零的,映射到匿名文件,其大小包含在hello 中。栈和堆区域也是请求二进制零的,初始长度为零。图7.7 概括了私有区域的不同映射。
    映射共享区域。如果hello程序与共享对象(或目标)链接,比如标准C 库libc. so, 那么这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域内。
    设置程序计数器(PC) 。execve 做的最后一件事情就是设置当前进程上下文中的程序计数器,使之指向代码区域的入口点。
    7.8 缺页故障与缺页中断处理
    缺页现象的发生是由于页表只相当于磁盘的一个缓存,所以不可能保存磁盘中全部的信息,对于有些信息的查询就会出现查询失败的情况,也就是缺页。
    对于一个访问虚拟内存的指令来说,如果发生了缺页现象,CPU就会触发一个缺页异常。缺页异常会调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,例如图7-7中存放在PP3中的VP4,如果VP4已经被更改,那就先将他存回到磁盘中。
    找到了要存储的页后,内核会从磁盘中将需要访问的内存,例如图7-7所示的VP3放入到之前已经操作过的PP3中,并且将PTE中的信息更新,这样就成功的将一个物理地址缓存在了页表中。当异常处理返回的时候,CPU会重新执行访问虚拟内存的操作,这个时候就可以正常的访问,不会发生缺页现象了。
    7.9动态存储分配管理
    动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块的集合,来维护,每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。
    放置策略:首次适配:从头搜索,遇到第一个合适的块就停止;下次适配:从头搜索,遇到下一个合适的块停止;最佳适配:全部搜索,选择合适的块停止。
    7.10本章小结
    本站通过对虚拟内存的介绍了解,学习了TLB和四级页表支持下VA到PA的转换,以及得到了PA后,三级cache下的物理内存的访问过程,掌握了各种函数与虚拟内存的关系
    (第7章 2分)

第8章 hello的io管理
8.1 linux的io设备管理方法
设备的模型化:文件
设备管理:unix io接口
所有的I/ O 设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,允许Linux 内核引出一个简单、低级的应用接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行。
8.2 简述unix io接口及其函数

  1. 接口:
    a). 打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件,内核记录有关这个打开文件的所有信息。
    b). Shell创建的每个进程都有三个打开的文件:标准输入,标准输出,标准错误。
    c). 改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0,这个文件位置是从文件开头起始的字节偏移量,应用程序能够通过执行seek,显式地将改变当前文件位置k。
    d). 读写文件:一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n,给定一个大小为m字节的而文件,当k>=m时,触发EOF。类似一个写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。
    e). 关闭文件,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中去。
  2. 函数:
    a). int open(char* filename,int flags,mode_t mode) 打开一个已经存在的文件或者创建一个新的文件。
    b). int close(fd) 关闭一个打开的文件。
    c). ssize_t read(int fd,void *buf,size_t n) 从当前文件位置复制字节到内存位置。
    d). ssize_t wirte(int fd,const void *buf,size_t n) 从内存复制字节到当前文件位置。
    8.3 printf的实现分析
    int printf(const char fmt, …)
    {
    int I;
    va_list arg = (va_list)((char
    )(&fmt) + 4);
    i = vsprintf(buf, fmt, arg);
    write(buf, i);
    return i;
    }
    printf函数主要调用了vsprintf和write函数。调用printf函数的时候,先是最右边的参数入栈。fmt是一个指针,这个指针指向第一个const参数(const char *fmt)中的第一个元素。fmt也是个变量,它的位置,是在栈上分配的,它也有地址。
    vsprintf函数作用是将所有的参数内容格式化之后存入buf,然后返回格式化数组的长度。write函数作用是将buf中的i个元素写到终端的函数。
    因此,执行过程为:从vsprintf生成显示信息,显示信息传送到write系统函数,write函数再陷阱-系统调用 int 0x80或syscall.字符显示驱动子程序。从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
    8.4 getchar的实现分析

getchar调用了一个read函数,这个read数是将整个缓冲区都读到了buf里面,然后将返回值是缓冲区的长度。如果buf长度为0,getchar才会调用read函数,否则是直接将保存的buf中的最前面的元素返回。
异步异常-键盘中断的处理:当用户按键时,键盘接口会得到一个代表该按键 的键盘扫描码,同时产生一个中断请求,中断请求抢占当前进程运行键盘中断子 程序,键盘中断子程序先从键盘接口取得该按键的扫描码,然后将该按键扫描码 转换成ASCII码,保存到系统的键盘缓冲区之中。
8.5本章小结
本章介绍了linux下的I/O设备管理机制,了解了开关读写与转移文件的接口和函数,以及printf、getchar基本函数的实现与操作
(第8章1分)

结论

  1. 通过文本编辑器,将用高级语言编写的程序存入hello.c文件
  2. 预处理器将hello.c文件经过初步修改变成了hello.i文件
  3. 编译器将hello.i文件处理成为了汇编代码并保存在hello.s文件
  4. 汇编器将hello.s文件处理成了可重定位的目标程序hello.o文件
  5. 链接器将hello.o与外部文件进行链接,得到hello文件
  6. shell中输入运行hello文件的命令,内核分配好运行程序所需要的堆、用户栈、虚拟内存等一系列信息
  7. 从外部对hello程序进行操控时,从键盘键入相应信号来执行
  8. hello需要访问磁盘中的信息时,利用MMU翻译地址
  9. hello完成执行,由shell回收

上述,便是hello的一生。
通过这次大作业,深入了对计算机系统的理解,使我在今后编写代码时可以更好地优化程序提高性能。
(结论0分,缺失 -1分,根据内容酌情加分)

附件
hello.c :hello源代码

hello.i :预处理后的文本文件

hello.s :hello.i编译后的汇编文件

hello.o :hello.s汇编后的可重定位目标文件

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分)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值