计算机科学与技术学院
2022年5月
本文通过运用所学的csapp知识分析hello程序从源代码到程序,从程序到进程的一生, 加深巩固对csapp课程和计算机系统的认知,并熟练掌握相应技能.
关键词 计算机系统;hello程序;
(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)
目 录
2.2在Ubuntu下预处理的命令............................................................................. - 5 -
5.3 可执行目标文件hello的格式....................................................................... - 8 -
6.2 简述壳Shell-bash的作用与处理流程........................................................ - 10 -
6.3 Hello的fork进程创建过程........................................................................ - 10 -
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 -
8.2 简述Unix IO接口及其函数.......................................................................... - 13 -
第1章 概述
1.1 Hello简介
P2P: from program to process
Program(程序):通过编辑器写成hello.c,并通过预处理、编译、汇编、链接从而生成hello.o
Process(进程):通过shell为程序fork,产生子进程.
020: from zero to zero
Zero(first): Hello程序执行前,不占用内存空间
Zero(second):P2P后,hello.o被shell fork出子进程,并execve,并通过mmap进行内存映射,随后分时间片从而在硬件上能够执行程序代码.最后程序结束,shell回收子进程,内存被回收.
1.2 环境与工具
CPU: Intel® Core™ i7-8750H CPU @ 2.20GHz
RAM: 16.00GB
软件
Windows10 64位
Ubuntu 20.04.1
工具
Visual Studio Code,cpp,gcc,as,ld,GNU readelf,GNU gdb,EDB
1.3 中间结果
1.4 本章小结
本章对hello进行了一个总体的概括,首先介绍了P2P、020的意义和过程,介绍了作业中的硬件环境、软件环境和开发工具,最后简述了从.c文件到可执行文件中间经历的过程。
(第1章0.5分)
第2章 预处理
2.1 预处理的概念与作用
概念:
预处理指的是编译前扫描源程序,检查预处理指令语句和宏定义,对其进行替换,同时删除程序中的注释和多余空白字符,最终产生调整后的源代码.
作用:
- 将#include形式声明的文件内容复制到新程序中去,并删除原命令,从而连接成一个新的源文件.
- 用实际值替换用#define 定义的字符串
- 条件编译处理,跟据#if等条件确定要编译的代码
2.2在Ubuntu下预处理的命令
指令:gcc -E hello.c -o hello.i
2.3 Hello的预处理结果解析
(1)源代码文件等相关的信息
(2)文件包含信息
(3)类型信息
(4)函数信息
(5)源文件代码
预处理:
源程序进行代码展开.首先cpp到环境变量下寻找标准库文件,若标准库文件中仍然包含#include或#define,则对其再进行寻找,并递归展开代码.对于#if#ifndef条件,cpp对条件值判断是否执行包含.
2.4 本章小结
本章主要介绍了预处理(包括头文件的展开、宏替换、去掉注释、条件编译)的概念和应用功能,以及Ubuntu下预处理的两个指令,同时具体到我们的hello.c文件的预处理结果hello.i文本文件解析,详细了解了预处理的内涵。
(第2章0.5分)
第3章 编译
3.1 编译的概念与作用
概念:
编译是指对经过预处理之后的源程序代码进行分析检查,确认所有语句均符合语法规则后将其翻译成等价的中间代码或汇编代码(assembly code)的过程。
作用:
(1)词法分析:对由字符组成的单词进行处理,从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。
(2)语法分析:编译程序的语法分析器以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,如表达式、赋值、循环等,最后看是否构成一个符合要求的程序。
(3)中间代码:使程序的结构在逻辑上更为简单明确。
(4)代码优化:对程序进行多种等价变换,便于生成更有效的目标代码。
(5)目标代码:目标代码生成器把语法分析后或优化后的中间代码变换成目标代码,此处指目标代码为汇编代码。
3.2 在Ubuntu下编译的命令
应截图,展示编译过程!
3.3 Hello的编译结果解析
此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析。
3.3.1 数据与赋值
(1)常量数据:
格式字符串和输出字符串:
源程序:
汇编代码:
条件判断值:
源程序:
汇编代码:
(2)变量数据:
局部变量:
int i, int argc即条件判断值:
源程序:
汇编代码:
i:
argc:
全局变量:
初始化的全局变量储存在.data节,它的初始化不需要汇编语句,而是直接完成的。
3.3.2 算术操作
Int i的自增操作:
源程序:
汇编代码:
3.3.3 数组指针结构操作:
argv数组:
argv[0]为输入程序的路径和名称字符串起始位置.
argv[1]为学号
argv[2]为姓名
argv[3]为秒数
源程序:
汇编代码:
按照基址-变址寻址法访问argv[1]和argv[2],argv[3](由于指针char*大小为8字节,分别偏移8、16, 24字节来访问)
3. 3. 4 关系操作及控制转移
if判断:
源代码:
汇编代码:
for判断:
源代码:
汇编代码:
3. 3 .5 函数操作
main()函数:
参数传递:int argc,char *argv[]
汇编代码:
函数调用:被启动函数调用
汇编代码:
函数返回:返回1或返回0
汇编代码:
printf()函数:
参数传递: 字符串参数首地址或argv[1],argv[2]地址
汇编代码:
函数调用: if判断满足条件后调用,与for循环中被调用
汇编代码:略(见上图)
函数返回:返回值被忽略
汇编代码:略
exit()函数:
参数传递: int型值,退出的状态.
汇编代码:
函数调用:main函数末尾调用或者if满足条件调用
汇编代码:略(见上图)
函数返回:无返回值,直接退出程序
汇编代码:略
sleep()函数:
参数传递: sleep时间(输入后转为int类型)
汇编代码:
函数调用: 主函数通过call指令调用
汇编代码: 略(见上图)
函数返回: 返回值略
汇编代码: 略
getchar()函数:
参数传递: 无参数
汇编代码: 略
函数调用: 主函数通过call指令调用
汇编代码:
函数返回: 忽略返回值
汇编代码: 略
3.4 本章小结
本章主要介绍了编译的概念以及过程。同时通过示例函数表现了c语言如何转换成为汇编代码。介绍了汇编代码如何实现变量、常量、传递参数以及分支和循环。编译程序所做的工作,就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码表示。
(第3章2分)
第4章 汇编
4.1 汇编的概念与作用
驱动程序运行汇编器,将汇编语言程序翻译成机器语言指令,并将这些指令打包成可重定位目标文件的过程称为汇编,hello.o是二进制编码文件,包含程序的机器指令编码。
汇编的作用
汇编过程将汇编程序转化为相应机器可直接识别执行的机器语言程序
4.2 在Ubuntu下汇编的命令
gcc -c hello.s -o hello.o
4.3 可重定位目标elf格式
(1) readelf -a hello.o > elf.txt命令将hello.o ELF信息导入到elf.txt中.
(2)ELF头
包含了系统信息,魔数,ELF头大小,节的大小和数量等等信息:
(3)节头目表
各个节的类型,位置,所占空间
- 可重定位节
在链接时,通过可重定位节对这些位置地址进行修改,连接器通过重定位的类型,偏移量来计算出正确地址.
- 符号表
存放定义的函数和全局变量信息
4.4 Hello.o的结果解析
命令:objdump -d -r hello.o>hellodis.txt
比较分析:
- 分支跳转:hello.s跳转位置为.xx代码块的形式,而反汇编代码跳转位置是偏移位置
- 函数名替换:hello.s中的函数名在反汇编后改为偏移位置
- 数的进制从hello.s的十进制换为十六进制.
4.5 本章小结
经过汇编器的操作,汇编语言转化为机器语言,hello.o可重定位目标文件的生成为后面的链接做了准备。通过对比hello.s和hello.o反汇编代码的区别,令人更深刻地理解了汇编语言到机器语言实现地转变,和这过程中为链接做出的准备,对可重定位目标elf格式进行了详细的分析,侧重点在重定位项目上。
(第4章1分)
第5章 链接
5.1 链接的概念与作用
链接概念:
链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时、执行时甚至运行时。在现代系统中,链接是由叫做链接器的程序自动执行的
链接作用:
链接使得可以用更小的模块进行独立地修改和编译,从而组成更大的应用程序.当需要修改时,只需要更改其中模块并重新链接即可.
注意:这儿的链接是指从 hello.o 到hello生成过程。
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的格式
各段的基本信息(起始地址、大小等)记录在节头部表中:
使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。
虚拟空间:
符号表对照:
.rodata:
.main:
.data:
通过以上我们可以清楚地知道各段的虚拟地址与节头部表的对应关系.
5.5 链接的重定位过程分析
(以下格式自行编排,编辑时删除)
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
对比:
(1)多了.init,.plt,.plt.sec节
(2)增加了函数:
(3)重定位条目消失,跳转和函数调用地址都变成了虚拟内存地址.
5.6 hello的执行流程
(以下格式自行编排,编辑时删除)
顺序记录:
子程序名 | 地址(16进制) |
ld-2.31.so!_dl_start | |
ld-2.31.so!_dl_init | |
hello!_start | |
libc-2.31.so!__libc_start_main | |
hello!printf@plt(调用8次) | |
hello!atoi @plt(调用8次) | |
hello!sleep@plt(调用8次) | |
hello!getchar@plt | |
libc-2.31.so!exit |
使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
5.7 Hello的动态链接分析
(以下格式自行编排,编辑时删除)
通过全局偏移量表(GOT)和过程链接表(PLT)的协同工作实现函数的动态链接,其中GOT中存放函数目标地址,PLT使用GOT中地址跳转到目标函数.
在此之后,程序调用共享库函数时,会首先跳转到PLT执行指令,第一次跳转时,GOT条目为PLT下一条指令,将函数ID压栈,然后跳转到PLT[0],在PLT[0]再将重定位表地址压栈,然后转进动态链接器,在动态链接器中使用两个栈条目确定函数运行时地址,重写GOT,再将控制传递给目标函数。以后如果再次调用同一函数,则通过间接跳转将控制直接转移至目标函数
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
5.8 本章小结
本章主要了解温习了在linux中链接的过程。通过查看hello的虚拟地址空间,并且对比hello与hello.o的反汇编代码,更好地掌握了链接与之中重定位的过程。不过,链接远不止本章所涉及的这么简单,就像是hello会在它运行时要求动态链接器加载和链接某个共享库,而无需在编译时将那些库链接到应用中。
(第5章1分)
第6章 hello进程管理
6.1 进程的概念与作用
进程是执行中程序的映像.
在运行程序时,shell创建该程序的映像,并且在进程的上下文中运行该可执行目标文件.同时程序也可以创建进程,并且可以在上下文中执行自己的代码.
进程可以解决在一个系统可以并发执行多个任务,且拥有自己独立的地址空间.
6.2 简述壳Shell-bash的作用与处理流程
作用:
解释命令,使得用户能够对操作系统或内核等进行操作.
流程:
- 终端读取命令行(由用户输入)
- 一个一个分析命令行字符串,对字符串进行特殊字符串处理,分块处理等等.
- 获取命令行参数,并放入argv中.
- 检测argv[0]是否为内置shell命令,不是则创建该进程.
- 用execve执行指定程序.
- 若后面带&,则后端执行.
- 若不带,则前端执行.
6.3 Hello的fork进程创建过程
在6.2的4中,父进程通过fork函数创建子进程.子进程与父进程拥有独立但是相同的数据段,代码,共享库,用户栈都相同.但fork函数调用一次,返回两次,父进程中fork返回子进程的pid,在子进程中,fork返回0.而且两者的pid不同.
6.4 Hello的execve过程
在6.2的5中,execve运行hello程序,且附带参数argv,环境变量envp等.该函数在当前进程(shell)的上下文加载并运行新程序.execve会为hello设置栈,并将进程控制交给main主函数.
具体流程如下:
- 读取可执行文件在磁盘的数据.
- 准备运行的环境,需要修改ldt局部描述符表, 把当前进程的页表映射全部切断.
- 系统预留了32个页(4KB*32)的空间用于存放参数及环境变量, 将参数及环境变量如同堆栈一般,从上往下压入32页空间
- 参数占用多少物理页,就进行多少页的映射.
- 制作环境变量、参数指针表.
- 修改跳转地址及用户堆栈,使系统调用返回地址改为可执行文件的进入地址.
6.5 Hello的进程执行
(以下格式自行编排,编辑时删除)
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。
上下文信息:
指的是进程恢复到原来的寄存器,栈,PC等原本的值.
进程时间片:
当进行IO或者sleep函数调用时.从用户模式转为内核模式,此时时间片停止,后面内核模式转为用户模式,继续执行指令.
调度:
内核选择先前被抢占进程替代当前进程,成为调度.利用上下文信息和上下文切换机制将控制转移到新的进程中去.
用户态和核心态转换:
用户态就是指程序在规定的空间内访问,不超过该空间执行程序.而内核态为程序提供了访问所有空间的权限.只有在必要的情况下(比如故障,中断等情况下才能进入内核态)
6.6 hello的异常与信号处理
异常类型:
类别 | 原因 | 异步/同步 | 返回行为 |
中断 | 来自I/O设备的信号 | 异步 | 总是返回到下一条指令 |
陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 |
故障 | 潜在可恢复的错误 | 同步 | 可能返回到当前指令 |
终止 | 不可恢复的错误 | 同步 | 不会返回 |
正常运行:
异常操作:
CTRL-Z:
发出SIGSTP信号,停止进程.
CTRL-C:
发出SIGINT信号,结束进程.
Kill:
杀死进程
随便乱按:
Sa,das读入缓存区,当前台进程结束后,解析命令.
6.7本章小结
本章了解了hello进程的执行过程。主要讲hello的创建、加载和终止,通过键盘输入。程序是指令、数据及其组织形式的描述,进程是程序的实体。可以说,进程是运行的程序。在hello运行过程中,内核有选择对其进行管理,决定何时进行上下文切换。也同样是在hello的运行过程中,当接受到不同的异常信号时,异常处理程序将对异常信号做出相应,执行相应的代码,每种信号都有不同的处理机制,对不同的异常信号,hello也有不同的处理结果。(第6章1分)
第7章 hello的存储管理
7.1 hello的存储器地址空间
(以下格式自行编排,编辑时删除)
结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。
- 逻辑地址:
是指由hello程序产生的与段相关的偏移地址部分。又称绝对地址
- 线性地址:
逻辑地址加上相应段的基地址就生成了一个线性地址.
- 虚址地址:
对于hello进程来说,使用的都是虚拟地址。每个进程维护一个单独的页表。
- 物理地址:
如果启用了分页机制,那么hello的线性地址会使用页目录和页表中的项变换成hello的物理地址;如果没有启用分页机制,那么hello的线性地址就直接成为物理地址了。
分段:
进程的地址空间:按照程序的逻辑划分成若干段,每个段可以有一个段名,每段从0开始. 分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。
段表:
程序分多个段,各段离散地转入内存,而需要为每个进程建立一张段映射网,简称”段表”.
地址变换:
根据逻辑地址得到段号,段内地址.判断段号是否越界,在查询段表找到对应的段表项,段表项存放地址为F+S*段表项长度.检查段内地址是否超过段长.若W>=C,则产生越界中断,否则继续执行.最后计算出物理地址.
对于lntel来说:
段号又称段选择符,可以通过这个去找到一个具体的段描述符,这个描述符就描述了一个短. 全局的段描述符,放在“全局段描述符表(GDT)”中,一些局部的段描述符,放在“局部段描述符表(LDT)”中。
GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。
页式管理是一种内存空间存储管理的技术,页式管理分为静态页式管理和动态页式管理。将各进程的虚拟空间划分成若干个长度相等的页(page),页式管理把内存空间按页的大小划分成片或者页面(page frame),然后把页式虚拟地址与内存地址建立一一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题。页式管理采用请求调页或预调页技术实现了内外存存储器的统一管理。
虚拟地址:
线性地址(虚拟地址)由虚拟页号VPN和虚拟页偏移VPO组成。首先,MMU从线性地址中抽取出VPN,并且检查TLB,看他是否因为前面某个内存引用缓存了PTE的一个副本。TLB从VPN中抽取出TLB索引和TLB标记,查找对应组中是否有匹配的条目。若命中,将缓存的PPN返回给MMU。若不命中,MMU需从页表中的PTE中取出PPN,若得到的PTE无效或标记不匹配,就产生缺页,内核需调入所需页面,重新运行加载指令,若有效,则取出PPN。最后将线性地址中的VPO与PPN连接起来就得到了对应的物理地址。
四级页表:
将虚拟地址的VPN划分为四个相等大小的不同的部分,每个部分用于寻找由上一级确定的页表基址对应的页表条目。
过程:
解析VA, 利用前m位vpn1寻找一级页表位置,接着一次重复k次,在第k级页表获得了页表条目,将PPN与VPO组合获得PA
MMU将物理地址发给L1缓存,缓存从物理地址中取出缓存偏移CO、缓存组索引CI以及缓存标记CT。若缓存中CI所指示的组有标记与CT匹配的条目且有效位为1,则检测到一个命中条目,读出在偏移量CO处的数据字节,并把它返回给MMU,随后MMU将它传递给CPU。若不命中,则在下一级cache或是主存中寻找需要的内容,储存到上一级cache后再一次请求读取。
7.6 hello进程fork时的内存映射
当fork时,内核创建数据段,代码,共享库,用户栈。同时保持只读。当fork完成时,新进程和旧进程的虚拟内存是相同的,当后面再进行不同操作时,由于写时复制机制,两者仍然保存了自己的私有空间。
7.7 hello进程execve时的内存映射
当execve hello程序时,控制权从发出execve进程转移到hello程序中。同时保存旧进程的上下文,然后删除已存在的用户区域,映射私有和共享区域,设置程序计数器(PC)。
7.8 缺页故障与缺页中断处理
缺页故障:
若页表条目中有效位为0,则引发缺页故障。
缺页中断处理,假设发生一个缺页故障:
处理器生成一个虚拟地址,并将它传送给MMU生成PTE地址,并访问多级页表等缓存,高速缓存/主存向MMU返回PTE,PTE中的有效位是0,所以MMU出发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序。缺页处理程序取出牺牲页,把它换到磁盘,再调入新的页面,并更新内存中的PTE,最后返回到原来的进程,再次执行导致缺页的命令。CPU将引起缺页的虚拟地址重新发送给MMU。因为虚拟页面已经换存在物理内存中,所以就会命中。
7.9动态存储分配管理
基本方法和策略:
带边界标签的隐式空闲链表分配器管理
带边界标记的隐式空闲链表的每个块是由一个字的头部、有效载荷、可能的额外 填充以及一个字的尾部组成的。
当一个应用请求一个k字节的块时,分配器搜索空闲链表,查找一个符合大小的 空闲块来放置这个请求块。分配器有三种放置策略:首次适配、下一次适配合最 佳适配。在释放一个已分配块的时候需要考虑是否能与前后空闲块合并,减少系 统中碎片的出现。
显示空间链表管理:
显式空闲链表是将空闲块组织为某种形式的显式数据结构。因为根据定义,程序 不需要一个空闲块的主体,所以实现这个数据结构的指针可以存放在这些空闲块 的主体里面。如,堆可以组织成一个双向链表,在每个空闲块中,都包含一个前 驱与一个后继指针。放置策略与上述放置策略一致
7.10本章小结
本章主要介绍了 hello 的存储器地址空间、 intel 的段式管理、 hello 的页式管理,在指定环境下介绍了 VA 到 PA 的变换、物理内存访问,还介绍 hello 进程 fork 时的内存映射、 execve 时的内存映射、缺页故障与缺页中断处理、动态存储分配管理。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
在linux中所有的IO设备都被模型化为文件。
设备管理:unix io接口
这样的操作,Linux则可以引出一个简单,低级的应用接口。
操作:open/close,read/write;
8.2 简述Unix IO接口及其函数
Unix IO接口:
利用内核返回的文件描述符对文件进行操作。
Linux shell创建的每个进程开始时都有三个打开的文件:标准输入(文件描述符0)、标准输出(描述符为1),标准出错(描述符为2)。头文件<unistd.h>定义了常量STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,他们可用来代替显式的描述符值。
Unix IO函数:
1. open()函数
功能描述:用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。
函数原型:int open(const char *pathname,int flags,int perms)
参数:pathname:被打开的文件名(可包括路径名如"dev/ttyS0")flags:文件打开方式,
返回值:成功:返回文件描述符;失败:返回-1
2. close()函数
功能描述:用于关闭一个被打开的的文件
所需头文件: #include <unistd.h>
函数原型:int close(int fd)
参数:fd文件描述符
函数返回值:0成功,-1出错
3. read()函数
功能描述: 从文件读取数据。
所需头文件: #include <unistd.h>
函数原型:ssize_t read(int fd, void *buf, size_t count);
参数:fd:将要读取数据的文件描述词。buf:指缓冲区,即读取的数据会被放到这个缓冲区中去。count: 表示调用一次read操作,应该读多少数量的字符。
返回值:返回所读取的字节数;0(读到EOF);-1(出错)。
4. write()函数
功能描述: 向文件写入数据。
所需头文件: #include <unistd.h>
函数原型:ssize_t write(int fd, void *buf, size_t count);
返回值:写入文件的字节数(成功);-1(出错)
5. lseek()函数
功能描述: 用于在指定的文件描述符中将将文件指针定位到相应位置。
所需头文件:#include <unistd.h>,#include <sys/types.h>
函数原型:off_t lseek(int fd, off_t offset,int whence);
参数:fd;文件描述符。offset:偏移量,每一个读写操作所需要移动的距离,单位是字节,可正可负(向前移,向后移)
返回值:成功:返回当前位移;失败:返回-1
8.3 printf的实现分析
vsprintf函数将所有的参数内容格式化之后存入buf,然后返回格式化数组的长度。write函数将buf中的i个元素写到终端。从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
getchar是读入函数的一种。它从标准输入里读取下一个字符,相当于getc(stdin)。返回类型为int型,为用户输入的ASCII码或EOF。getchar可用宏实现:#define getchar() getc(stdin)。getchar有一个int型的返回值。当程序调用getchar时.程序就等着用户按键。用户输入的字符被存放在键盘缓冲区中。直到用户按回车为止(回车字符也放在缓冲区中)。当用户键入回车之后,getchar才开始从stdin流中每次读入一个字符。getchar函数的返回值是用户输入的字符的ASCII码,若文件结尾(End-Of-File)则返回-1(EOF),且将用户输入的字符回显到屏幕。 [2]
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
本章介绍了 Linux 的 I/O 设备的基本概念和管理方法
(第8章1分)
结论
hello.c经过预编译,拓展得到hello.i文本文件,hello.i经过编译,得到汇编代码hello.s汇编文件,hello.s经过汇编,得到二进制可重定位目标文件hello.o,hello.o经过链接,生成了可执行文件hello.
bash进程调用fork函数,生成子进程;并由execve函数加载运行当前进程的上下文中加载并运行新程序hello,在hello的变化过程中,会有各种地址,hello再运行时会调用一些函数,比如printf函数,这些函数与linux I/O的设备模拟化密切相关,最终被shell父进程回收,内核会收回为其创建的所有信息.
学习csapp让我深切明白了规格严格,功夫到家的校训,想要学习知识,就必须要深入原理去得到真理.
附件
列出所有的中间产物的文件名,并予以说明起作用。
(附件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分)