【深入理解计算机操作系统】ch1 计算机系统漫游

计算机系统是由硬件和系统软件组成的,他们共同工作来运行应用程序。
大家都熟知hello world程序,但是为了让它实现运行,系统的每个主要组成部分都需要协调工作。

#include <stdio.h>
int main()
{
    printf("hello,world\n");
    return 0;
}

从某种意义上来说,本书的目的就是为了帮助你了解当你在系统上执行hello程序时,系统发生了什么以及为什么会这样。

1.1 信息就是位+上下文

  • hello程序的生命周期是从一个源程序(源文件)开始的,即程序员通过编辑器创建并保存的文本文件,文件名为hello.c

  • 源程序实际上就是一个由值0和1组成的位(又称为比特)序列,而8个位又被组成为一组,这一组序列被称为字节。而每个字节表示程序中的某些文本字符。

  • 大部分现代计算机系统都使用ASCII标准来表示文本字符,这种方式实际上就是用一个唯一的单字节大小的整数值来表示每个字符
    hello.c的ASCII文本表示

  • hello.c的表示方法说明了一个基本思想:系统中所有信息——包括磁盘文件、内存中的程序、内存中存放的用户数据以及网络上传送的数据,都是由一串比特表示的。

  • 区分不同数据对象的唯一方法:我们读取这些对象时的上下文。

  • C语言成功的原因:

    • C语言与Unix操作系统关系密切
    • C语言小而简单
    • C语言是为了实践目的设计的

1.2 程序被其他程序翻译成不同的格式

为了让机器能够读懂高级C语言程序,则需要将每条C语言程序转化为机器能读懂的语言,即低级的机器语言指令。然而将这些指令按照可执行目标程序的格式打包好,并以二进制磁盘文件的形式存放起来。而此时的目标程序也可以成为可执行目标文件

在Unix系统上,为了实现这个转化(从源文件到目标文件),可由编译器驱动程序完成:

gcc -o hello hello.c

编译系统
GCC编译器驱动程序读取了源程序文件hello.c,并把它翻译成一个可执行目标文件hello。而这个翻译过程可分为4个阶段。其中使用预处理器,编译器,汇编器和链接器一起构成了该编译系统。

  • 预处理阶段:预处理器(cpp)会根据以字符#开头的命令,修改原始的C程序。而修改后的文件,通常以.i为文件扩展名,即生成hello.i
  • 编译阶段:编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包含了一个汇编语言程序。即将高级语言翻译成相对应的汇编语句。
  • 汇编阶段:汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫可重定位目标程序的格式,并将结果保存在目标文件hello.o中。而如果你打开hello.o文件,你会发现是一堆乱码,那是因为hello.o文件是一个二进制文件,它包含的字节是函数main的指令编码。
  • 链接阶段。链接器(ld)负责嘴和将多个.o文件(hello.c中还有函数printf,即需要生成的printf.o)合并在一起,最后得到一个可执行目标文件(简称可执行文件),而该文件能够加载到内存中,由系统执行。

以hello.c为例,以Linux系统讲解

  1. 先进行预处理:cpp hello.c > hello.i
    预处理

  2. 编译:cc -S hello.i
    编译

  3. 汇编:as -o hello.o hello.s
    汇编

  4. 链接:ld -o hello printf.o hello.o -lc


当然,这上面的printf.o的情况是理想状况下C语言编译器提供的printf函数生成的printf.o,然后通过链接器合并在一起,然而由于寻找printf.o却成为了难题。然而gcc为什么能够成功呢,于是我们通过-v来查看gcc调用ld时的参数。我们发现不仅仅是printf.o,在生成文件的时候还将一些必要的.o文件也合并在文件中
使用命令:gcc -v hello.o -o hello
链接
我们发现gcc现在使用的是collect2进行合并,因为collect2可以看做ld功能相同的程序,所以也可使用collect2进行合并
5. 运行:./hello
运行

1.3 了解编译系统如何工作是大有益处的

  1. 优化程序性能
  2. 理解链接时出现的错误
  3. 避免安全漏洞

1.4 处理器读并解释存储在内存中的指令

好了,我们已经把hello.c翻译成了可执行目标文件hello,并存放在磁盘上,但是要如何运行呢,对于Unix系统上运行该文件,直接将文件名输入到shell的应用程序中即可。

shell是一个命令行解释器,它输出一个提示符,等待一个命令行,然后执行这个命令。

1.4.1系统的硬件组成

硬件组成

  • 总线:贯穿整个系统的一组电子管道,它携带信息字节并负责在各个部件间传递。
    • 通常总线被设计成传送定长的字节块(也就是字)。
    • 字中的字节数(即字长)是一个基本的系统参数。要么是4个字节(32位),要么是8个字节(64位)
  • I/O设备:I/O(输入/输出)设备是系统与外部世界的连系统通道。
    • 即我们通过该设备与计算机进行交互,例如作为我们输入的键盘和鼠标,作为计算机输出的显示器,以及长期存储数据和程序的磁盘驱动器(简单说就是磁盘)。
    • 每个I/O设备都通过一个控制器或适配器与I/O总线相连。其功能都是I/O总线和I/O设备之间传递信息。其中控制器是I/O设备本身或者系统的主印制电路板(通常称为主板)上的芯片组。而适配器则是一块插在主板插槽上的卡。
  • 主存:临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据。
    • 从物理上看,主存是由一组动态随机存取存储器(DRAM)芯片组成
    • 从逻辑上看,存储器是一个线性的字节数组,每个字节都有唯一的地址,且从零开始
    • 组成程序的每条机器指令都由不同数量的字节构成
  • 处理器:中央处理单元(CPU),是解释(或执行)存储在主存中指令的引擎。
    • 处理器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(PC),而任何时刻,PC都指向主存中的某条机器语言指令(即含有该指令的地址)。
    • 寄存器文件时一个小的存储设备,由一些单个字长的寄存器组成,每个寄存器都有唯一的名字。
    • 算术/逻辑单元(ALU)计算新的数据和地址值

1.4.2 运行hello程序

一开始,当我们开始从键盘中输入“./hello”后,shell程序将字符逐一读入到寄存器中,再把它放到内存中
从键盘上读取hello命令

当我们敲下回车后,shell认为我们结束了输入。然后开始执行一系列指令来加载hello文件,而这些指令将hello文件中的代码和数据从磁盘复制到主存中。包括最终会输出的字符串"hello, world\n"
从磁盘加载可执行文件到主存

当hello的代码和数据加载到主存后,处理器就开始执行指令。即将字符串"hello, world\n"的字节从主存复制到寄存器文件中,再从寄存器文件汇总复制到显示设备上。
将字符串从存储器写到显示器

1.5 高速缓存至关重要

看到上文,可能会发现,处理数据有很大一部分时间在于把信息从一个地方转换到另有一个地方。随着数据越来越大,人们对主存的容量需求也越来越大,再加上半导体技术的进步,使得处理器与主存之间的差距越来越大。

为了解决处理器和主存之间的差异,系统设计者采用更小更快的存储设备,称为高速缓存存储器(简称cache或高速缓存),作为暂时的集结区域。而这种高速缓存是用一种静态随机访问存储器(SRAM)的硬件技术实现的。即利用高速缓存的局部性原理,使得系统的访问速度加快。
告诉缓存存储器

意识到高速缓存存储器存在的应用程序员能够利用高速缓存将程序的性能提高一个数量级

1.6 存储设备形成层次结构

实际上,每个计算机系统中的存储设备都被组成了一个存储器层次结构,上至下,设备的访问速度越来越慢,但是容量也越来越大,并且每字节造价也越来越便宜。
存储器的层次结构

而存储器层次结构的主要思想是上一层的存储器作为第一层存储器的高速缓存

1.7 操作系统管理硬件

抽象表示

回到hello程序的例子发现,我们处理数据并不是直接访问键盘,显示器,磁盘或者主存的。而它们都是依靠操作系统提供服务的。我们可以把操作系统看做是应用程序和硬件之间的一层软件。所有应用程序对硬件的操作尝试都必须通过操作系统。

操作系统有两个基本功能:

  1. 防止硬件被失控的应用程序滥用
  2. 向应用程序提供简单一致的机制来控制复杂而又通常大不相同的低级硬件设备。

操作系统通过几个基本的抽象概念来实现这两个功能。

  • 文件是对I/O设备的抽象表示
  • 虚拟内存是对主存和I/O设备的抽象表示
  • 进程是对处理器、主存和I/O设备的抽象表示

1.7.1 进程

  • 进程是操作系统对一个正在运行的程序的一种抽象。
  • 并发运行,则是说在一个进程的指令和另一个进程的指令是交错执行的。
    • 操作系统保持跟踪进程运行所需要的所有状态信息。而这种转态,就是上下文。
  • 针对单CPU来说,一个时刻只能执行一个程序,而CPU看上去能并发执行多个,实际上是通过处理器在进程间切换来实现的。而操作系统实现这种交错执行的机制就称为上下文切换。即保存当前进程的上下文,恢复新进程的上下文,然将控制权传递到新进程。

上下文切换

1.7.2 线程

一个进程实际上可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。

1.7.3 虚拟内存

虚拟内存是一个抽象概念,它为每个进程提供一个假象,即每个进程都在独占地使用主存。每个进程看到的内存都是一致的,称为虚拟地址空间
进程的虚拟地址空间

每个进程看到的虚拟空间由大量的准确定义的区构成,每个区都有专门的功能

  • 程序代码和数据:对所有进程来说,代码是从同一固定地址开始,紧接着是和C全局变量相对应的数据位置。该区是直接按照可执行目标文件的内容初始化的。即一开始运行时就被值定了大小。
  • 堆:可以在运行时动态地扩展和收缩,即调用malloc和free等函数,就是在该区创建空间存储数据的
  • 共享库:用来存放共享库的代码和数据的区域
  • 栈:编译器用该区来实现函数调用,例如递归函数等
  • 内核虚拟内存:该区域为内核保留。不允许应用程序读写区域的内容或直接调用内核代码定义的函数。

虚拟内存的运作需要硬件和操作系统软件之间精密复杂的交互。基本思想是把一个进程虚拟内存的内容存储在磁盘上,然后用主存作为磁盘的高速缓存。

1.7.4 文件

文件就是字节序列。系统中的所有输入输出都是通过使用一小组称为Unix I/O的系统函数调用读写文件来实现的。

1.8 系统之间利用网络通信

当系统从主存复制一串字节到网络适配器时,数据流经网络到达另一台机器。相似地,系统可以读取从其他机器发来的数据,并把数据复制到自己的主存。
网络也是一种I/O设备

以hello示例,当telnet客户端键入“hello”字符串后,客户端软件将字符串发送到telnet的服务器。telnet服务器通过从网络上接收到这个字符串后,会把它传递给远端shell程序。
接下来,远端shell运行hello程序,并将输出行返回给telnet服务器。
最后,telnet服务器通过网络把字符串转发给telnet客户端,客户端就将字符串输出到我们的本地终端上。
利用telnet通过网络运行hello

1.9 重要主题

系统是硬件和系统软件互相交织的集合体,它们必须共同协作以达到运行应用程序的最终目的

1.9.1 Amdahl定理

主要思想,当我们对系统的某个部分加速时,其对系统整体性能的影响取决于该部分的重要性和加速程度。

若系统执行某应用程序需要时间为t,假设系统某部分所需要执行时间和该时间的比例为a,而该部分性能提升比例为k。即该部分初始所需要时间为at,现在所需要时间为(a*t)/k,因此总的执行时间为:
T = ( 1 − a ) t + a ∗ t k = t [ ( 1 − a ) + a k ] T = (1-a)t + \frac{a*t}{k} = t[(1-a)+\frac{a}{k}] T=(1a)t+kat=t[(1a)+ka]
由此,计算加速比S=t/T为:

S = 1 ( 1 − a ) + a k S=\frac{1}{( 1-a ) +\frac {a}{k} } S=(1a)+ka1

Amdahl定理的主要观点:想要显著加速整个系统,必须提升全系统中相当大的部分的速度
Amdahl定理描述了改善任何过程的一般原则。


练习题

1.1
由题知,a=1500/2500=0.6
A.
k=150/100=1.5,代入定理S=1.25X
B.
由题知S=1.67X,a=0.6,代入定理得 k=3
速度v=3*100=300
1.2
S=2X,a=0.8
代入得k=2.67


1.9.2 并发和并行

  • 并发:指一个同时具有多个活动的系统
  • 并行:指用并发来使一个系统运行更快
  1. 线程级并发
  2. 指令级并行
  3. 单指令,多数据并行

1.9.3 计算机系统中抽象的重要性

在处理器里,指令集架构提供了对实际处理器硬件的抽象。
虚拟机,它提供了对整个计算机的抽象
抽象表示

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值