程序人生-Hello’s P2P

程序人生-Hello’s P2P

摘要

一个hello程序的生成,要经历预处理,编译,汇编和链接,从而得到可执行目标文件;而hello程序的运行,则需要进程,信号和I/O的共同实现。通过一个简简单单的hello文件,可以大致窥探到计算机运行的整个面貌。

关键词:编译;汇编;链接;虚拟内存;进程;I/O。

目录

第1章 概述

1.1 Hello简介

1.2 环境与工具

1.3 中间结果

1.4 本章小结

第2章 预处理

2.1 预处理的概念与作用

2.2 在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

2.4 本章小结

第3章 编译

3.1 编译的概念与作用

3.2 Ubuntu下编译命令

3.3 Hello的编译结果解析

3.4 本章小结

第4章 汇编

4.1 汇编的概念与作用

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标ELF格式

4.4 Hello.o的结果解析

4.5 本章小结

第5章 链接

5.1 链接的概念与作用

5.2 在Ubuntu下链接的命令

5.3 可执行目标文件HELLO的格式

5.4 HELLO的虚拟地址空间

5.5 链接的重定位过程分析

5.6 HELLO的执行流程

5.7 HELLO的动态链接分析

5.8 本章小结

第6章 HELLO的进程管理

6.1 进程的概念与作用

6.2 简述壳SHELL-BASH的作用与处理流程

6.3 的FORK进程创建过程

6.4 HELLO的EXECVE过程

6.5 HELLO的进程执行

6.6 HELLO的异常与信号

6.7 本章小结

第7章 HELLO的存储管理

7.1 HELLO的存储器地址空间

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

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

7.4 TLB与四级页表支持下的VA到PA的变换

7.5 三级CACHE支持下的物理内存访问

7.6 HELLO进程FORK时的内存映射

7.7 HELLO进程EXECVE时的内存映射

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

7.9 动态存储分配管理

7.10 本章小结

第8章 HELLO的IO管理

8.1 LINUX的IO设备管理方法

8.2 简述UNIX IO接口及其函数

8.3 PRINTF的实现分析

8.4 GETCHAR实现分析

8.5 本章小结

结论

附件

参考文献

第1章 概述

1.1 Hello简介

P2P(from program to process),将源代码经过预处理转化为预处理输出文件,将预处理输出文件经过编译转化为汇编代码文件,将汇编代码文件经过汇编转化为可重定位目标文件,将可重定位目标文件经过链接转化为可执行目标文件。
020(from zero-0 to zero-0),hello程序从主存到开始,通过虚拟地址寻址并由IO执行,在执行完之后释放内存存储空间,一切就像没有发生过的一样。

1.2 环境与工具

电脑型号:荣耀MagicBook2019
CPU:AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx 2.10 GHz
物理机系统:Windows10 版本号:19042.1052
虚拟机:VMware 16.1.2 build-17966106
虚拟机系统:Ubuntu64位20.04
gcc: C语言编译器
objdump,edb,gdb

1.3 中间结果

hello.c: 源文件
hello.i: 预处理输出文件
hello.s: 编译代码文件
hello.o:可重定位目标文件
hello:可执行目标文件

本章小结

本章从整体角度大致地介绍了程序(program)的生成和执行过程,以及下文内容的实现所使用的软硬件环境。

第2章 预处理

2.1 预处理的概念与作用

预处理是将源程序转化为可执行程序的第一个转化步骤,主要用于C语言编译器对各种预处理命令进行处理,包括对头文件的包含,宏定义的扩展,条件编译的选择等。

2.2 在Ubuntu下预处理的命令

将.c文件转化为预处理后的.i可见文本文件
hello.c -> hello.i: linux>gcc -E hello.c -o hello.i
图2.2.1预处理指令
图2.2.2 预处理输出文件

2.3 Hello的预处理结果解析

在这里插入图片描述
预处理后可以发现,原先源代码里的头文件和宏都被程序替换为对应路径里面的文本,将所有调用全部展开,为后面的编译做准备。

本章小结

本章介绍了预处理的指令与过程,从代码展开的层面解释了预处理的原理与意义,为后面介绍编译做准备。

第3章 编译

3.1 编译的概念与作用

C编译器在进行具体的程序翻译之前,会先对源程序进行词法分析,语义分析和语法分析,然后根据分析的结果进行代码优化和存储分配,最终把C语言源程序翻译成为汇编语言程序。编译器通常采用对源程序进行多次扫描的方式进行处理,每次集中完成一项或几项任务,也可以将一项任务分散到几次扫描进行完成。

3.2 在Ubuntu下编译的命令

hello.i -> hello.s: linux>gcc -S hello.i -o hello.s

3.3 Hello的编译结果解析

3.3.1 全局变量sleepsecs

sleepsces作为全局变量被放在.data节,并通过globl声明为全局变量。

3.3.2 局部变量i

局部变量i为int型数据,在这里大小为4字节。局部变量一般都是存储在寄存器或者栈空间,在这里%rbp栈堆开出4字节空间存储i

3.3.3 if 的控制转移

If循环使用cmpl来进行数值比较和条件码的设置,若不等于3,通过无条件跳转jmp跳转到L3来实现if内的语句。

3.3.4 for 循环的实现

For循环也是通过条件码来实现跳转,在这里L3的条件跳转和L4的自然结束后过渡到L3共同构成了一个循环,当i>=10时跳出循环。

3.3.5 函数调用

Printf函数,getchar函数,sleep函数,exit函数,通过call来调用这些函数

3.3.6 数组

编译后数组通过索引值和基值实现对数组内容的寻址,char*为地址数组类型,每一个大小8字节,则将寄存器%rax中的值增加16恰好到达argv[2]处。

3.3.7 强制转换

Sleepsecs被隐式强制转换,实际上被赋值为2(向0舍弃)。

3.3.8 算数操作

通过add来实现加法运算

本章小结

本章通过hello的实例将C语言操作和数据类型与汇编语言对应起来,从寄存器和堆的角度来看待程序的运行。

第4章 汇编

4.1 汇编的概念与作用

汇编的功能是将编译生成的汇编语言代码转换为机器语言代码。因为通常最终的可执行目标文件由多个不同模块对应的机器语言目标代码组合而形成,所以,在生成单个模块的机器语言目标代码时,不能确定每条指令或每个数据最终的地址,也即,单个模块的机器语言目标代码文件需要重新定位,因此,通常把汇编生成的机器语言目标代码文件称为可重定位目标文件。

4.2 在Ubuntu下汇编的命令

hello.s -> hello.o: linux>gcc -c hello.s -o hello.o

可重定位目标elf格式

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

ELF头:
开头Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00,描述了生成该文件的系统的字的大小和字节顺序,紧接着便是ELF头的大小、目标文件的类型、机器类型、节头部表的文件偏移、节头部表中的条目的大小和数量
节头部表:
节头部表显示不同节的位置与大小信息,目标文件中每个节都有一个固定大小的条目。
重定位节:
显示偏移量,信息,类型,符号值,符号名称和加数等信息。
符号表:
符号表由汇编器构造,表中包含一个数组。

4.4 hello.o的结果解析

objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
两者的代码实现功能相同,但是相比之下反汇编的代码更加“奇妙”,这种奇妙是机器运行计算和人脑思维运算的不同。由.i文件得到的.s文件像是一种翻译,将人类的高级编程语言翻译成机器语言,但是思维逻辑还是人的思维;而.o文件的反汇编更像是对程序在机器层面运行的记录,它的每一步都通过机器语言从硬件层面记录下来,同时也可以优化人类所翻译的机器语言,提高了逻辑性,降低了人类的阅读性和理解性。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

4.5 本章小结

通过汇编与反汇编的对比,用ELF表头显式地表现程序结构,从机器层面去了解和解释程序的运行变得更加容易。

第5章 链接

5.1 链接的概念与作用

链接的功能是将所有关联的可重定位目标文件组合起来,以生成一个可执行文件。可重定位目标文件和可执行目标文件都是机器语言目标文件,所不同的是前者是单个模块生成的,而后者是多个模块组合而成的。

5.2 在Ubuntu下链接的命令

ld -o hello.ld -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的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。

5.4 hello的虚拟地址空间

edb显示的与5.3中内容对应。

5.5 链接的重定位过程分析

在获得的反汇编文件中,对于一些过程要比汇编生成的内容要详细,如生成了链接过程中才能指定的地址。在重定位中要对绝对地址进行引用,需要进行PC相对寻址;同时也将一些头文件的函数通过引用里libc.so加入到.text中。

5.6 hello的执行流程

_init 0x1000
.plt 0x1020
_cxa_finalize@plt 0x1080
puts@plt 0x1090
printf@plt 0x10a0
getchar@plt 0x10b0
exit@plt 0x10c0
sleep@plt 0x10d0
_start 0x10e0
_libc_csu_fini 0x10fa
_libc_csu_init 0x1101
deregister_tm_clones 0x1110
_fini 0x12c8

5.7 Hello的动态链接分析

PIC函数调用了由共享库定义的函数,编译器无法预测函数的运行时的地址。GNU编译系统把函数地址的解析推迟到它实际被调用的地方。延迟绑定通过动态链接器使用过程链接表(PLT)和全局偏移量表(GOT) 两个数据结构实现。GOT 存放函数目标地址,PLT通过 GOT 中存储的地址间接跳转至目标函数。

当调用库内函数时,控制流会跳转到相应函数的PLT表中,PLT会通过GOT把将要调用的函数序号压入栈中,然后调用动态链接器;动态链接器会进行重定位,用栈中的地址重写入GOT,替代GOT原先用来跳转到PLT的地址变为实际的函数地址,再把控制传递给调用函数和PLTPLT,再次通过GOT间接跳转。

本章小结

本章讲述链接的过程,最终生成可执行文件,并且对执行过程进行对比,使我对链接的理解更加深刻。

第6章 hello进程管理

6.1 进程的概念与作用

进程就是程序的一次运行过程。更确切地说,进程是一个具有一定的独立功能的程序关于某个数据集合的一次运行活动,因而进程具有动态意义。计算机处理的所有任务实际上都是由进程来完成的。

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

Shell接收用户命令,调用相关程序:读取命令并处理得到的参数;判断命令的类型,对不同类型命令分情况执行;处理信号,更新进程状态;判断输入发出信号。

6.3 Hello的fork进程创建过程

子进程通过fork()函数来创建。子进程相当于当前进程的一个复制本,但是其发展走向与父进程并不一定完全相同;此外,由fork函数创建的子进程其pid一定不为0,故fork函数返回值为0,这就意味着pid为0时为子进程,这为判断进程提供了方法。子进程继续运行函数的剩余部分,子进程也可以有自己的子进程,父进程和子进程的运行顺序按照拓扑排序执行,所有进程一直运行直到进程终止。

6.4 Hello的execve过程

excve函数在上下文中加载运行一个新程序,在这里它加载hello可执行文件,之后调用启动代码,并且传递控制。

6.5 Hello的进程执行

系统执行进程时,内核可以暂停当前进程,同时启用其他进程,这个过程就是调度,而这些进程以及它们的PC值所构成的序列就是逻辑控制流。当进程被执行时,内核代码不断地根据上下文信息,时间片等进行判断,并根据其结果转移控制权并完成调度。

6.6 hello的异常与信号处理

正常情况没有干扰下,内容打印10遍
乱按情况下没有影响
ctrl+c直接结束程序
Ctrl+z程序挂起
ps查看程序运行状态
jobs查看作业
pstree查看进程关系
fg程序继续运行
kill杀死进程

6.7 本章小结

本章介绍fork和execve函数,通过shell介绍命令和进程以及信号处理过程。

第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:由程序产生,其内容是与段相关的偏移地址,由段标识符和段内偏移量组成。
线性地址:当一个地址空间的地址是连续的非负整数时,该地址空间中的地址被称为线性地址。
虚拟地址:CPU通过虚拟地址寻址,然后通过MMU(内存管理单元)将虚拟地址转换为物理地址。
物理地址:计算机主存被划分成连续字节大小的内存组成的数组,每个字节都有唯一一个地址,这个地址就是物理地址。

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

全局描述符表GDT:通常来说系统只定义了一个GDT,用来存放系统内每个任务都可能访问的描述符。
局部描述符表LDT:存放进程专用的描述符
首先,判断段选择描述符中T1字段的数字,可知当前将要转换的是GDT中的段,还是LDT中的段;再根据指定的相应寄存器,得到其地址和大小,得到一个数组。抽取段选择符中的前13位,在这个数组中查找到对应的段描述符,这样就有了Base。把基地址Base+Offset,得到要转换的下一个阶段地址。

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

虚拟缓存系统必须有某种方法来判定一个虚拟页是否缓存在DRAM中的某个地方。如果是,系统还必须判定这个虚拟页存放在哪个物理页中。如果不命中,系统必须判定这个虚拟页存放在磁盘的哪个位置,在物理内存中选择一个牺牲页,并将虚拟页从磁盘复制到DRAM中,替换掉牺牲页。这些功能是由软硬件联合提供的,包括操作系统软件,MMU(内存管理单元)中的地址翻译硬件和一个存放在物理内存中的骄傲做页表的数据结构,页表会将虚拟页映射到物理页。
首先,根据控制寄存器CR3给出的页目录表首地址找出页目录表,由DIR字段提供的10位页目录索引找到对应的页目录项,每个页目录项大小为4B;然后,根据页目录项中20 位基地址指出的页表首地址找到对应页表,再根据线性地址中间的页表索引(PAGE字段)找到页表中的页表项;最后,将页表项中的20位及地址和线性地址中的12 位页内偏移量组合成32位物理地址。

7.4 TLB与四级页表支持下的VA到PA的变换

为了减小缓存次数,往往把页表中最活跃的几个页表项复制到高速缓存中,这种在高速缓存中的页表项组成的页表称为后被转换缓冲器(TLB)。在VA到PA的变换过程中,虚拟地址被分割成4个9位的片。在这里,CR3寄存器包含L1页表的物理地址。VPN1有一个到L1 PTE的偏移量,找到这个PTE以后又会包含到L2页表的基础地址;VPN2包含一个到L2PTE的偏移量,找到这个PTE以后又会包含到L3页表的基础地址;VPN3包含一个到L3PTE的偏移量,找到这个PTE以后又会包含到L4页表的基础地址;VPN4包含一个到L4PTE的偏移量,找到这个PTE以后就是相应的PPN。
7.5 三级Cache支持下的物理内存访问
组选择:组索引位标识组,如图7.5.1所示。

行匹配和字选择:行匹配检查多个行的标记位和有效位,以确定所请求的字是否在集合中。传统的内存是一个值的数组以地址作为输人,并返回存储在那个地址的值。另一方面,相联存储器是一个( key , value )对的数组,以 key 为输人,返回与输人的 key 相匹配的( key , value )对中的 value 值,因此我们可以把组相联高速缓存中的每个组都看成一个小的相联存储器,key是标记和有效位,而value就是块的内容。

图7.5.3展示了相联高速缓存中行匹配的基本思想。这里的一个重要思想就是组中的任何一行都可以包含任何映射到这个组的内存块。所以高速缓存必须搜索组中的每一行寻找一个有效的行,其标记与地址中的标记相匹配,如果高速缓存找到了这样一行,那么我们就命中,块偏移从这个块中选择一个字。

7.6 hello进程fork时的内存映射

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

7.7 hello进程execve时的内存映射

execve 函数在当前进程中加载并运行包含在可执行目标文件 hello的程序,用 hello程序有效地替代了当前程序。加载并运行hello需要以下儿个步骤:

  1. 删除已存在的用户区城。删除当前进程虚拟地址的用户部分中的已存在的区域结构。
  2. 映射私有区城。为新程序的代码、数据、 bss 和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为 a . out 文件中的, text 和,data区。 bss 区域是请求二进制零的,映射到匿名文件,其大小包含在 hello 中,栈和堆区域也是请求二进制零的,初始长度为零,图7.7.1概括了私有区域的不同映射。
  3. 映射共享区域。hello 程序与共享对象(或目标)链接,这些对象都是动态链接到这个程序的,然后再映射到用户虚拟地址空间中的共享区域内。
  4. 设置程序计数器( PC )。 execve 做的最后一件事情就是设置当前进程上下文中的粘序计数器,使指向代码区域的入口点。
    下一次调度这个进程时,它将从这个入口开始执行。 Linux 将根据需要换入代码和数据页面。

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

假设 MMU 在试图翻译某个虚报地址 A 时,触发了一个缺页。这个异常导致控制转移到内核的缺页处理程序,处理程序随后就执行下面的步骤:

  1. 虚报地址 A 是合法的吗?换句话说,A 在某个区域结构定义的区域内吗?为了回答这个向题,缺页处理程序搜索区域结构的链表,把 A 和每个区域结构中的 vm_start 和vm_end 做比较。如果这个指令是不合法的,那么缺页处理程序就触发一个段错误,从而终止这个进程。这个情况在图7.8.1中标识为”1”。因为一个进程可以创建任意数量的新虚拟内存区域,所以顺序搜索区域结构的链表花销可能会很大,因此在实际中,Linux使用某些没有显示出来的字段,Linux在链表中构建了一棵树,并在这棵树上进行查找。
  2. 试图进行的内存访问是否合法?换句话说,进程是否有读、写或者执行这个区域内页面的权限?例如,这个页面是不是由一条试图对这个代码段里的只读页面进行写操作的存储指令造成的?如果试图进行的访问是不合法的,那么缺页处理程序会触发一个保护异常,从而终止这个进程,这种情况在图7.8.1中标为”2”。
  3. 此刻,内核知道了这个缺页是由于对合法的虚拟地址进行合法的操作造成的。它是这样来处理这个缺页的:选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它交换出去,换入新的页面并更新页表,当缺页处理程序返回时, CPU 重新启动引起缺页的指令,这条指令将再次发送A到MMU 。这次,MMU就能正常地翻译A,而不会再产生缺页中断了。

7.9动态存储分配管理

动态内存分配器维护者一个进程的虚拟内存区域,称为堆。系统之间的细节不同,但是不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长。对于每个进程,内核维护者一个变量brk,它指向堆的顶部。
分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可以用来分配。空闲块可以保持空闲,直到它显式地被应用所分配。一个已分配的块保持分配状态,直到它被释放,这种释放要么是应用程序显式执行,要么是内存分配器自身隐式执行。
分配器有两种基本风格。两种风格都要求应用显式地分配块。它们的不同之处在于由哪个实体来负责释放已分配的块。

  1. 显式分配器( explicit alloeator ),要求应用显式地释放任何已分配的块。例如,C标准库提供一种叫做 malloc 程序包的显式分配器,C 程序通调洲用mal1oc函数来分配一个块,并通过调用 Free 函数来释放一个块,C中的new和delete操作符与C中的mal1oc和Free相当。
  2. 隐式分配器( implicit alocator ),另一方面,要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块。隐式分配器也叫做垃圾收集器( garbage collector),而自动释放未使用的块的过程叫做垃圾收集。

7.10本章小结

深入了解了hello程序运行时在内存中的映像,展示虚拟内存和物理内存之间的关系。

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件
设备管理:unix io接口
所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入输出都被当作相对应文件的读和写。这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O。

8.2 简述Unix IO接口及其函数

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 。
关闭文件。当应用完成了对文件的访问之后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源。

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

8.3 printf的实现分析

https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

本章了解了系统级I/O下的文件操作和管理方法,以printf和getchar两个函数为例分析了库函数的实现。
(第8章1分)
结论
hello.c通过预处理生成hello.i
hello.i通过编译生成hello.s
hello.s通过汇编生成hello.o
hello.o通过链接生成hello
hello被IO执行,分配内存空间。开始运行后,系统不断接收和发出信号调节和维持进程状态。运行结束后,系统释放缓存,清除数据,还原状态。

附件

列出所有的中间产物的文件名,并予以说明起作用。
Hello.i:预处理生成文件
Hello.s:汇编语言文件
Hello.o:可重定位文件
Hello:可执行生成文件
Hello.elf:elf表头文件,是txt的可读取文件
Hello.txt:反汇编文件,用来和hello.s对比

参考文献

为完成本次大作业你翻阅的书籍与网站等
[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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值