资讯详情

哈工大计算机系统大作业

题 目 程序人生-Hello’s P2P

专 业 计算机学部

学 号 120L040211

班 级 2003001

学 生 曹毓超

指 导 教 师 史先俊

hello每个人都会写程序,但只有真正系统的人才知道它的生活。从刚写到预处理、汇编、编译、链接和过程管理…;这个大作业,就是要更清楚地看到它的一生。

过程管理;编译;OS;存储管理,IO,虚拟内存到物理内存;

1.1 Hello简介... - 4 -

1.2 环境与工具... - 4 -

1.3 中间结果... - 4 -

1.4 本章小结... - 4 -

2.1 预处理的概念和作用... - 5 -

2.2在Ubuntu下一个预处理命令... - 5 -

2.3 Hello预处理结果分析... - 5 -

2.4 本章小结............................................................................................................ - 5 -

3.1 编译的概念与作用............................................................................................ - 6 -

3.2 在Ubuntu下编译的命令................................................................................ - 6 -

3.3 Hello的编译结果解析..................................................................................... - 6 -

3.4 本章小结............................................................................................................ - 6 -

4.1 汇编的概念与作用............................................................................................ - 7 -

4.2 在Ubuntu下汇编的命令................................................................................ - 7 -

4.3 可重定位目标elf格式.................................................................................... - 7 -

4.4 Hello.o的结果解析.......................................................................................... - 7 -

4.5 本章小结............................................................................................................ - 7 -

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.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.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.1 Linux的IO设备管理方法............................................................................. - 13 -

8.2 简述Unix IO接口及其函数.......................................................................... - 13 -

8.3 printf的实现分析........................................................................................... - 13 -

8.4 getchar的实现分析....................................................................................... - 13 -

8.5本章小结.......................................................................................................... - 13 -

1.1 Hello简介

              首先编成源程序,然后存储在内存中,随后预处理器对它进行预处理,包括  ●将源文件中以”include”格式包含的文件复制到编译的源文件中。        ●用实际值替换用“#define”定义的字符串。        ●根据“#if”后面的条件决定需要编译的代码。

然后,编译器将它

链接:

第一步 空间与地址分配 扫描所有的输入目标文件,并且获得他们的各个段的长度、属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表中。这一步中,链接器将能够获得所有输入到目标文件的段长度,并且将它们合并,计算出输出文件中各个段合并后的长度与位置,并建立映射关系。

第二步 符号解析与重定位 使用上面第一步中收集到的所有信息,读取输入文件中段的数据、重定位信息,并且进行符号解析与重定位、调整代码中的地址等。事实上第二步是链接过程的核心,特别是重定位过程。

随后,shell为其fork一个子进程,然后分配cpu资源和虚拟内存,采用execve加载进程实现了P2P的转变。在CPU工作时,通过取指、译码、执行等微程序,逐步执行目标文件中的程序。同时,CPU使用流水线、进程切换等工作方式实现多进程作业。

在程序的执行过程中会使用到内存中的数据。这些数据通过各级存储,包括磁盘、主存、Cache等,并使用页表等辅助存储,实现访存的加速。在这个过程中还涉及操作系统的信号处理,控制进程,使得系统资源得到充分利用。而IO管理与信号处理通过软硬结合,完成程序从键盘、主板、显卡,再到屏幕的工作。当进程执行结束后,操作系统进行进程回收,实现所谓的O2O:From Zero-0 to Zero-0。

1.2 环境与工具

硬件环境:处理器:Intel® Core™ i7-9750H CPU @ 2.60GHz 2.59GHz

RAM:32.00GB

软件环境:Windows10 64位;Ubuntu 16.04

开发调试工具:gcc/as/ld/vim/edb/readelf/gedit

1.3 中间结果

Hello.i :预处理之后的结果

Hello.s:汇编后的汇编文件

Heloo.c:源程序

Hello.o:可重定位目标文件

Hello:可执行程序

Hello.aout:反汇编后的文件

1.4 本章小结

本章是介绍了总体的过程,做到心中有数,然后简单介绍了一下环境。

2.1 预处理的概念与作用

预处理(或称预编译)是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理指令指示在程序正式编译前就由编译器进行的操作,可放在程序中任何位置。

     预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。

             

●将源文件中以”include”格式包含的文件复制到编译的源文件中。        ●用实际值替换用“#define”定义的字符串。        ●根据“#if”后面的条件决定需要编译的代码。

2.2在Ubuntu下预处理的命令

 

2.3 Hello的预处理结果解析

 

变为了3060行,仍为C语言,相关头文件全部展开。

2.4 本章小结

预处理将头文件展开,宏替换,处理条件编译,将无用代码块去除。

3.1 编译的概念与作用

编译程序也称为编译器,是指把用高级程序设计语言书写的源程序,翻译成等价的汇编语言格式目标程序的翻译程序。编译程序属于采用生成性实现途径实现的翻译程序。它以高级程序设计语言书写的源程序作为输入,而以汇编语言表示的目标程序作为输出。

编译程序的基本功能是把源程序(高级语言)翻译成目标程序。除了基本功能之外,编译程序还具备语法检查、调试措施、修改手段、覆盖处理、目标程序优化、不同语言合用以及人机联系等重要功能。

       

3.2 在Ubuntu下编译的命令

 

3.3 Hello的编译结果解析

 

 

3.3.1

局部变量 int i

可以看出rbp-4地址处为i

3.3.2

立即数4和立即数7

 

可以看出常量是以立即数的形式出现的

3.3.3

这个是控制转移对应的if语句

 

这一个对应的是for循环

3.3.4

 

可以看出,赋值一般是用mov来的

3.3.5

 

大小比较是用cmpl和jmp条件转移来实现。

3.3.6

函数调用

3.3.7

类型转换,将字符转换成int型

3.3.8

涉及到的算术操作i++

3.3.9

 

Leap代表着printf引号里的内容,rbp-8代表着arv[3],rbp-16->arv[2],rbp-24->[1]

3.4 本章小结

这里主要是理解hello.s的内容

4.1 汇编的概念与作用

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

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

   

ELF Header:以16字节 的序列 Magic 开始,Magic 描述了生成该文件的系统 的字的大小和字节顺序,ELF 头剩下的部分包含帮助链接器语法分析和解释目标文件的信息,其中包括 ELF 头的大小、目标文件的类型、机器类型、 字节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量等信息。

根据头文件的信息,可以知道该文件是可重定位目标文件,有14个节。

Section Headers:节头部表,包含了文件中出现的各个节的语义,包括节 的类型、位置和大小等信息。 由于是可重定位目标文件,所以每个节都从0开始,用于重定位。在文件头中得到节头表的信息,然后再使用节头表中的字节偏移信息得到各节在文件中的起始位置,以及各节所占空间的大小,同时可以观察到,代码是可执行的,但是不能写;数据段和只读数据段都不可执行,而且只读数据段也不可写。

 

.symtab: 存放程序中定义和引用的函数和全局变量的信息。name是符号名称,对于可冲定位目标模块,value是符号相对于目标节的起始位置偏移,对于可执行目标文件,该值是一个绝对运行的地址。size是目标的大小,type要么是数据要么是函数。Bind字段表明符号是本地的还是全局的。

重定位节:一个.text 节中位置的列表,包含.text 节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。

重定位节.rela.text中各项符号的信息:

Offset:需要被修改的引用节的偏移Info:包括symbol和type两个部分,symbol在前面四个字节,type在后面四个字节.

symbol:标识被修改引用应该指向的符号,

type:重定位的类型

Type:告知链接器应该如何修改新的应用

Attend:一个有符号常数,一些重定位要使用它对被修改引用的值做偏移调整Name:重定向到的目标的名称。

4.4 Hello.o的结果解析

 

 

 

(1)分支转移:反汇编的跳转指令用的不是段名称比如.L3,二是用的确定的地址,因为段名称只是在汇编语言中便于编写的助记符,所以在汇编成机器语言之后显然不存在,而是确定的地址。

(2)函数调用:在.s 文件中,函数调用之后直接跟着函数名称,而在反汇编程序中,call的目标地址是当前下一条指令。这是因为 hello.c 中调用的函数 都是共享库中的函数,最终需要通过动态链接器才能确定函数的运行时执 行地址,在汇编成为机器语言的时候,对于这些不确定地址的函数调用,将其call指令后的相对地址设置为全0(目标地址正是下一条指令),然后在.rela.text 节中为其添加重定位条目,等待静态链接的进一步确定。

(3)立即数在反汇编中被自动转换为16进制。

(4)反汇编中汇编指令被详细转换为机器代码。

4.5 本章小结

本章对hello.s进行了汇编,生成了hello.o可重定位目标文件,并且分析了可重定位文件的ELF头、节头部表、符号表和可重定位节,比较了hello.s和hello.o反汇编代码的不同之处,分析了从汇编语言到机器语言的一一映射关系。

5.1 链接的概念与作用

链接是将各种代码和数据片段收集并组合成一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时,也就是在源代码被编译成机器代 码时;也可以执行于加载时,也就是在程序被加载器加载到内存并执行时;甚至于运行时,也就是由应用程序来执行。链接是由叫做链接器的程序执行的。链接器使得分离编译成为可能。

5.2 在Ubuntu下链接的命令

 

           

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

 

ELF格式

节数目27

 

Section Headers

大小 Size 以及在程序中的偏移量 Offset,因此根据 Section Headers 中的信息我们就可以用 HexEdit 定位各个节所占的区间(起始位置,大小)。其中 Address 是程序被载入到虚拟地址的起始地址。

 

.symbol

 

Rela.txt

5.4 hello的虚拟地址空间

 

正在上传…重新上传取消

代码段开始于0x4010f4处,大小为0x01f2。

       

5.5 链接的重定位过程分析

 

不同:与hello.o的反汇编文件对比发现,hello的反汇编中多了许多节。前者中只有一个.text节,而且只有一个main函数,函数地址也是默认的0x000000。后者中有.init,.plt,.text三个节,而且每个节中有很多函数。库函数的代码都已经链接到了程序中,程序各个节变的更加完整,跳转的地址也具有参考性。

重定位过程:

(1)重定位节和符号定义链接器将所有类型相同的节合并在一起后,这个节就作为可执行目标文件的节。然后链接器把运行时的内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号,当这一步完成时,程序中每条指令和全局变量都有唯一运行时的地址。

(2)重定位节中的符号引用这一步中,连接器修改代码节和数据节中对每个符号的引用,使他们指向正确的运行时地址。

(3)重定位条目当编译器遇到对最终位置未知的目标引用时,它会生成一个重定位条目。代码的重定位条目放在.rel.txt中。

5.6 hello的执行流程

401000<_init>

401020<.plt>

401030<puts@plt>

401040<printf@plt>

401050<getchar@plt>

401060<atoi@plt>

401070<exit@plt>

401080<sleep@plt>

401090<_start>

4010c0<_dl_relocate_static_pie>

4010c1<main>

401150<__libc_csu_init>

4011b0<__libc_csu_fini>

4011b4<_fini>

5.7 Hello的动态链接分析

  

 

 

 

动态链接采用了延迟加载的策略,即在调用函数时才进行符号的映射。使用偏移量表GOT+过程链接表PLT实现函数的动态链接。GOT中存放函数目标地址,为每个全局函数创建一个副本函数,并将对函数的调用转换成对副本函数调用。

5.8 本章小结

本章主要介绍了链接的概念和作用,以及生成链接的命令,分析了hello的elf格式文件,同时也分析了hello的虚拟地址空间以及重定位过程,遍历了整个hello的执行过程,并且比较了hello.o的反汇编和hello的反汇编。

6.1 进程的概念与作用

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。

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

Shell是一个交互性命令解释器,把用户输入的命令翻译给操作系统。提

按照以下处理流程:

(1)从终端读取用户输入的命令行,并把命令行划分为一个或多个参数;

(2)分析命令行字符串,若是内置命令,则立即执行;

 (3)如果不是内置命令,则调用fork()创建新子进程,再调用execve()执行指定程序。

6.3 Hello的fork进程创建过程

对于实验中的hello.c程序,当我们输入 ./hello 120L040211 曹毓超 1 的时候,首先shell对我们输入的命令进行解析,由于我们输入的命令不是一个内置的shell命令,shell会认为是要执行当前目录下的可执行目标文件hello,因此shell会调用fork()创建一个子进程,得到与父进程完全相同的数据空间,栈,堆等资源,程序开始执行。

6.4 Hello的execve过程

  exceve函数加载并运行可执行目标文件,当出现错误时,exceve会返回到调用程序,否则,exceve调用一次且不返回。在exceve调用加载器加载可执行目标文件,加载器删除子进程现有的虚拟内存段,并创建新的代码、数据、堆和栈段调用启动代码,启动代码设置栈,将可执行目标文件中的代码和数据从磁盘复制到内存中,然后通过跳转到hello程序的第一条指令或入口点来运行该程序,由此将控制传递给新程序的主函数。

6.5 Hello的进程执行

进程上下文信息:操作系统内核采用一种成为上下文切换的较高层形式的异常控制刘来实现多任务,内核为每一个进程维持一个上下文。上下文切换:(1)保存当前进程的上下文;(2)恢复某个先前被抢占的进程被保存的上下文;(3)将控制传递给这个新恢复分进程。

进程时间片:一个进程执行它的控制流的一部分的每一时间段叫做时间片。

进程调度过程:在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程,这种决策就是调度。

用户态与核心态转换:处理器通常使用一个寄存器提供两种模式的区分,该寄存器描述了进程当前享有的特权,当没有设置模式位时,进程就处于用户模式中, 用户模式的进程不允许执行特权指令,也不允许直接引用地址空间中内核区内的代码和数据;设置模式位时,进程处于内核模式,该进程可以执行指令集中的任何命令,并且可以访问系统中的任何内存位置

过程:键盘输入./hello 7203610228 cyc 1,首先shell通过加载器加载可执行目标文件hello,操作系统进行上下文切换,切换到hello的进程中,此时为用户态,执行完相应函数后,调用sleep函数,进入内核态,当sleep的时间完成后时定时器发送一个中断信号,此时进入内核状态执行中断处理,将hello进程从等待队列中移出重新加入到运行队列,上下文切换再进入hello进程,回到用户态。

 

6.6 hello的异常与信号处理

异常和信号异常可以分为四类:中断、陷阱、故障、终止

中断:处理器外部I/O设备引起,异步异常,例如:时钟中断,键盘上敲击Ctrl-C.

陷阱:有意的异常,执行指令产生的结果,发生时间可预知,同步异常,例如:系统调用。

故障:不是有意的,但可能被修复,同步异常,例如:缺页故障,保护故障。

终止:非故意,不可恢复非致命错误造成,例如:非法指令,奇偶校验错误。

1.ctrl+c 程序终止

 

2.胡乱按

3.ctrl+z

hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。

 程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

6.7本章小结

本章介绍了进程的概念和作用,以及壳Shell-bash的作用与处理流程,调用 fork 创建新进程,调用 execve函数执行 hello,最后介绍了执行过程中的异常与信号处理。

7.1 hello的存储器地址空间

物理地址是用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。现代操作系统都提供了一种内存管理的抽像,即虚拟内存。

进程使用虚拟内存中的地址,即虚拟地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址。hello.s中使用的就是虚拟空间的虚拟地址。

线性地址指虚拟地址到物理地址变换的中间层,是处理器可寻址的内存空间(称为线性地址空间)中的地址。程序代码会产生逻辑地址,或者说段中的偏移地址,加上相应段基址就成了一个线性地址。如果启用了分页机制,那么线性地址可以再经过变换产生物理地址。若是没有采用分页机制,那么线性地址就是物理地址。而逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址。它是Intel为了兼容,而将段式内存管理方式保留下来的产物。

逻辑(虚拟)地址经过分段(查询段表)转化为线性地址。线性地址经过分页(查询页表)转为物理地址。

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

一个逻辑地址由两部分组成,段标识符和段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节。可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。

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

CPU的页式内存管理单元,任务就是把虚拟地址空间翻译为物理地址空间。线性地址被分为以固定长度为单位的组,称为页(page),例如一个64位的机器,线性地址最大可为16E-1,可以用8KB为一个页来划分,这时候,就出现了一个大的页数组,共有2的51个次方个页。这就是页目录。目录中的每一个目录项对应页的地址。另一类“页”,我们称之为物理页,或者是页框、页桢的。是分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。

 

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

四级的页表将虚拟地址翻译成物理地址。36位VPN 被划分成四个9 位VPN,分别用于一个页表的偏移量。如下图所示(图表借鉴的网络)

 

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

 

获得物理地址之后,先取出组索引对应位,在L1中寻找对应组。如果存在,则比较标志位,相等后检查有效位是否为1.如果都满足则命中取出值传给CPU,否则按顺序对L2cache、L3cache、内存进行相同操作,直到出现命中。然后再一级一级向上传,如果有空闲块则将目标块放置到空闲块中,否则将缓存中的某个块驱逐,将目标块放到被驱逐块的位置。

7.6 hello进程fork时的内存映射

在shell输入命令行后,内核调用fork创建子进程,为hello程序的运行创建上下文,并分配一个与父进程不同的PID。同时为这个新进程创建虚拟内存,创建当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记位只读,并将两个进程中的每个区域结构都标记为私有的写时复制。

当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。

7.7 hello进程execve时的内存映射

Execve执行后,会删除当前进程的程序的数据和代码,并让新程序覆盖当前的虚拟内存和空间,并删除当前程序的代码共享区结构。

加载并运行 hello 需要以下几个步骤:

(1)删除当前进程中用户的虚拟内存数据

(2)映射私有区域,为新程序开辟出新的区域。

(3)映射共享区域,链接hello与libc.so,再映射到共享区域。

(4)设置当前进程上下文程序计数器(PC)。

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

DRMA缓存不命中称为缺页,如果程序执行过程中遇到了缺页故障,触发缺页异常,则内核调用缺页处理程序。

处理程序会进行如下步骤:

(1).检查虚拟地址是否合法,如果不合法则触发一个段错误,程序终止。

(2).检查进程是否有读、写或执行该区域页面的权限,如果不具有则触发保护异常,程序终止。

(3).内核选择一个牺牲页面,如果该页面被修改过则内核会将它复制回磁盘,换入新的页面并更新页表。

(4).将控制转移给hello进程,再次执行触发缺页故障的指令。

7.9动态存储分配管理

动态内存管理的基本方法与策略:

动态内存分配器维护着一个进程的虚拟内存区域,称为堆,系统之间细节不同,但是不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长(向更高地址)。对于每个进程,内核维护着一个变量brk,它指向对的顶部。

分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显示地保留为供应用程序使用。空闲块可用来分配。空闲块保持空闲,直到它显示地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显示执行的。要么是内存分配器自身隐式执行的。分配器有两种基本风格。两种风格都要求应用显示地分配块。他们的不同之处在于由哪个实体来负责释放已分配的块。

显式分配器:要求应用显式地释放任何已分配的块。例如malloc。

隐式分配器:也叫做垃圾收集器,要求分配器检测一个已分配块何时不再使用,那么就释放这个块,自动释放未使用的已经分配的块的过程叫做垃圾收集。

堆中的块主要组织为两种形式:

1.隐式空闲链表(带边界标记)

2.显式空闲链表

7.10本章小结

本章介绍了在hello的内存管理,虚拟地址、物理地址、线性地址、逻辑地址的区别以及它们之间的变换模式,以及段式、页式的管理模式,在内存映射的角度上重新认识 fork和execve,同时介绍了动态存储分配的方法与原理。

8.1 Linux的IO设备管理方法

设备的模型化:IO设备被看出文件和内核,输入和输出也当作文件来写。

设备管理:Linux的一个低级,简单的接口,把所有输入输出标准化。

8.2 简述Unix IO接口及其函数

(1)打开文件:程序想要打开文件,内核返回一个描述符标识这个文件,描述符程序要求内核打开相应文件,表示想要访问I/O文件,内核返回一个小的非负整数(描述符),用于标识这个文件。返回的描述符总是在当前没有打开的最小描述符。内核记录这个描述符,程序在只要记录这个描述符便能记录打开文件的所有信息。

(2)shell在进程的开始为其打开三个文件:标准输入、标准输出和标准错误。

(3)改变当前文件的位置:对于每个打开的文件,内核保存着一个文件位置k,初始为0。这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek操作显式地设置文件的当前位置为k。

(4)读写文件:一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n。给定一个大小为m字节的文件,当k>=m时执行读操作会出发一个称为EOF的条件,应用程序能检测到这个条件,在文件结尾处并没有明确的EOF符号。类似一个写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。

(5)关闭文件:内核释放打开文件时创建的数据结构以及占用的内存资源,并将描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源。

8.3 printf的实现分析

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++) {

标签: arv连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台