前言
第一章 计算机系统漫游
计算机系统由硬件和系统软件组成。例如执行一个简单的hello.c程序如下所示,均需要硬件和软件的协同工作:第一阶段:hello.c经过预处理、编译、汇编、链接为可执行的hello程序(包含了CPU的操作指令)被存放在磁盘上;第二阶段:终端输入./hello程序->shell识别该程序并请求操作系统加载->操作系统在磁盘中找到hello文件并在内存中分配虚拟内存空间->创建一个新进程->hello 可执行文件(包括解析可执行文件格式、设置代码段、数据段等)从磁盘加载到内存->CPU开始执行hello程序中的相关指令;第三阶段:CPU执行链接的标准库代码->执行printf库函数准备将字符串 "Hello, World!" 写入到标准输出->printf库函数内部通过syscall指令触发系统调用->CPU从用户态切换到内核态->操作系统内核控制->操作系统内核调用设备驱动程序->将字符转换成对应的像素矩阵,并写入显存中(写入显卡的控制寄存器,通知显卡开始新一帧的渲染)->显卡定时地从显存中读取像素数据,并通过数字信号接口(如HDMI, DisplayPort)发送给显示器。->显卡接收信号,控制屏幕上数百万个液晶单元的开合和背光->"hello,world"在屏幕上显示
#include<stdio.h>
int main()
{
printf("hello,world\n");
return 0;
}
1.1信息就是位+上下文
程序员通过编辑器编辑hello.c的文本文件即源代码,源代码由一个个的文本字符组成,一个文本字符在计算机内部存储和处理的形态是一个字节的整数值(编码),一个字节在磁盘(一般情况下)或内存上占8个位,由0或1的二进制数组成。对于hello.c这样的纯英文源代码,其字符编码与 ASCII标准完全兼容。但现代计算机系统更普遍地使用 Unicode标准(如UTF-8编码),它与ASCII在英文字符集上保持兼容。(程序员看的):单个文本字符底层为单个字节大小的整数值(固定8位的二级制整数值),如下图1所示,源代码由文本字符序列组成,由若干单个字节整数值序列的方式存储的。一串字符编码(如ASCII或UTF-8)的文件,通常被称为文本文件,其他的均为二进制文件。

hello.c的表示方法是ACSII标准文本文件,但其在系统中存储的所有信息,如:磁盘文件、内存中的程序、内存中存放的用户数据以及网络传送的数据,均为二进制的文件。区分不同数据的对象的方法为上下文信息,单独提取出来的相同二进制序列,在不同的上下文中,可能被解释为具有完全不同含义的数据。
1.2程序被其他程序翻译成不同的不同的格式
hello.c源文件并不能直接在系统上运行,编译器驱动程序(GCC编译器(Linux系统上))读取源文件经过预处理、编译、汇编、链接个阶段生成可执行文件hello(二进制磁盘文件)储存在磁盘上。执行这四个阶段的编译器分别为:预处理器、编译器、汇编器、链接器共同构成编译系统,如下图所示。

1.3了解编译系统如何工作是大有益处的
- 优化程序性能:见3、5、6章
- 理解链接时出现的错误:见第7章
- 避免安全漏洞:见第3章
1.4处理器读并解释储存在内存中的指令
shell是一个命令行解释器,将 ./hello 键入到LInux系统的shell运行程序中,并按enter,等待在屏幕上输出 hello,world。等待下一个命令的输入。
1.4.1系统的硬件组成
为深入探讨hello程序运行时发生了什么,需了解系统硬件的组成,其组成如下图所示:

- 总线
携带信息字节并负责在各部件间传递,且贯穿整个系统的电子管道称为总线。总线被设计为传递定长的字节块,即字(word),在不同系统中不尽相同,一般为4字节或8字节。
- I/O设备
I/O设备被分为四大类型功能类型:输入设备、输出设备、存储设备、通信设备。输入设备:键盘、鼠标等;输出设备:显示器;存储设备:硬盘、磁盘、U盘等;网络设备:网卡、WiFi模块、蓝牙适配器等。可执行程序hello存放在磁盘设备上。每个I/O设备都通过一个控制器或者适配器与I/O总线相连,二者封装不同。控制器是I/O设备本身或者系统主板上的芯片组;适配器是系统主板插在插槽上的卡(芯片)。总之二者都是I/O设备与I/O总线之间传递信息字节。
- 主存
主存 ⊂ 内存,是内存最主要,最核心的部分,是一个临时存储器(物理上:由一组动态随机存取存储器(DRAM)芯片组成)。逻辑上,存储器为一个连续的线性字节数组,每一个字节都有唯一的地址,地址是从0开始的,C程序的变量占存储器的大小随变量的大小而变化。
- 处理器
中央处理单元(CPU),简称处理器,是解释(执行)存储在主存上的指令的引擎。处理器的核心是一个大小为一个字节的存储器(或寄存器),称为程序计数器(PC)。在任何情况下PC都指向主存中的某条机器语言指令的地址。PC指向内存中按顺序执行的指令的地址,随指令修改指向的位置。PC围绕CPU(主存、寄存器文件、算术/逻辑单元(ALU))进行,一般寄存器文件称为寄存器,寄存器由单个字节的寄存器构成,相当于轻量级的内存,ALU负责计算新的数据和地址值。
CPU一般进行的操作如下:
-
- 加载:从主存复制一个字或一个字节到寄存器,覆盖寄存器原来的内容。
- 存储:从寄存器复制一个字或一个字节主存的某个位置,覆盖主存原来的内容。
- 操作:把两个寄存器的内容复制到ALU,ALU对其进行算术运算,计算结果保存在一个寄存器中,覆盖该寄存器中原来的内容。
- 跳转:从本身指令中抽取的一个字,将改字复制到PC中,覆盖原来的值。
1.4.2运行hello程序
- 在键盘上输人字符串“./hello”后, shell 程序将字符串逐一读入寄存器, 最终将其存放到内存中,如下图1所示。
- 按下回车键后,shell执行一系列指令,利用直接存储器存取(DMA) 技术将可执行 hello 文件直接从磁盘复制到内存。数据包括最终会被输出的字符串“hello, world“,如下图2所示。
- 可执行文件hello一旦被加载到内存中,处理器就开始执行 hello 程序的 main 程序中的机器语言指令。这些指令将“hello, world’ 字符串中的字节从主存复制到寄存器文件, 再从寄存器文件中复制到显示设备,最终显示在屏幕上,如下图3所示。

图1

图2

图3
1.5高速缓存至关重要
高速缓存里存放可能经常访问的数据,大部分的内存操作都能在快速的高速缓存中完成。

1.6储存设备形成层次结构
存储器层次结构的主要思想是上一层的存储器作为低一层存储器的高速缓存。因此,寄存器文件就是 L1 的高速缓存, L1 是 L2 的高速缓存, L2 是 L3 的高速缓存, L3 是主存的高速缓存,而主存又是磁盘的高速缓存。 在某些具有分布式文件系统的网络系统中,本地磁盘就是存储在其他系统中磁盘上的数据的高速缓存。

1.7操作系统管理硬件
hello加载到内存及hello程序从磁盘拷贝到内存、hello程序的字符最终打印在屏幕上的整个程序的运行到结束的过程中,没有再直接访问过处理器、主存、I/O设备、主存这四部分的硬件,而是由操作系统代劳。因此操作系统被看作是应用程序和硬件之间的中间软件层。

操作系统有 两个基本功能:(1)防止硬件被失控的应用程序滥用;(2)向应用程序提供简单一致的机制来控制复杂而又通常大不相同的低级硬件设备。
操作系统通过几个基本的抽象概念(进程、虚拟内存和文件) 来实现这两个功能。如图所示,文件是对 I/O 设备的抽象, 虚拟内存是对程序存储器的抽象,而进程是对一个正在运行的程序的抽象。

1.7.1进程
进程是程序执行的动态过程,通过操作系统为进程提供操作硬件的资源。像 hello 这样的程序在现代系统上运行时,操作系统会提供一种假象,就好像系统上只有这个程序在运行。程序看上去是独占地使用处理器、主存和 I/O 设备。处理器看上去就像在不间断地一条接一条地执行程序中的指令,即该程序的代码和数据是系统内存中唯一的对象。进程是操作系统对一个正在运行的程序的一种抽象。在一个系统上可以同时运行多个进程,而每个进程都好像在独占地使用硬件。而并发运行,则是说一个进程的指令和另一个进程的指令是交错执行的过程。在大多数系统中, 需要运行的进程数是多于可以运行它们的CPU 个数的。传统系统在一个时刻只能执行一个程序,而先进的多核处理器同时能够执行多个程序。无论是在单核还是多核系统中,一个 CPU 看上去都像是在并发地执行多个进程,这是通过处理器在进程间切换来实现的。操作系统实现这种交错执行的机制称为上下 文切换。为了简化讨论, 我们只考虑包含一个 CPU 的单处理 器系统的情况。
如下图所示,如图 1-12 所示,从一个进程到另一个进程的转换是由操作系统内核(kernel)管理的。文件,它就执行一条特殊的系统调用(system call)指令, 将控制权传递给内核。然后内核执行被请求的操作并返回应用程序。注意,内核不是一个独立的进程。相反,它是系统管理全部进程所用代码和数据结构的集合。

1.7.2线程
尽管通常我们认为一个进程只有单一的控制流,但是在现代系统中,一个进程实际上可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中, 并共享同样的代码和全局数据。由于网络服务器中对并行处理的需求,线程成为越来越重要的编程模型,因为多线程之间比多进程之间更容易共享数据,也因为线程一般来说都比进程更高效。
1.7.3虚拟内存
在 Linux 中,地址空间最上面的区域是保留给操作系统中的代码和数据的,这对所有进程来说都是一样。地址空间的底部区域存放用户进程定义的代码和数据。请注意,图中的地址是从下往上增大的。
* 程序代码和数据。对所有的进程来说,代码是从同一固定地址开始, 紧接着的是和. C 全局变量相对应的数据位置。代码和数据区是直接按照可执行目标文件的内容初始化的。
* 堆。代码和数据区后紧随着的是运行时堆。代码和数据区在进程一开始运行时就被指定了大小,与此不同,当调用像 malloc 和 free 这样的 C 标准库函数时,堆可以在运行时动态地扩展和收缩。
* 共享库。大约在地址空间的中间部分是一块用来存放像 C 标准库和数学库这样的共享库的代码和数据的区域。共享库的概念非常强大,也相当难懂。
* 栈。位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。 和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。特别地, 每次我们调用一个函数时, 栈就会增长; 从一个函数返回时,栈就会收缩。
* 内核虚拟内存。地址空间顶部的区域是为内核保留的。 不允许应用程序读写这个区域的内容或者直接调用内核代码定义的函数。相反,它们必须调用内核来执行这些操作。

1.7.4文件
文件就是字节序列,仅此而已。每个 I/O 设备,包括磁盘、键盘、显示器, 甚至网络,都可以看成是文件。系统中的所有输人输出都是通过使用一小组称为 Unix I/O 的系统函数调用读写文件来实现的。
1.8系统之间利用网络通信
网络可视为一个I/O设备。系统从主存复制一串字节到网络适配器,经由网络传递到另一台机器。同理,系统也可以接受来自其他机器传来的数据,并复制到主存。


1.9重要主题
1.9.1并发和并行
系统同时进行多个活动称为并发,系统使用并发让程序运行的更快称为并行。
- 线程级并发
单处理器:计算机执行多任务(多进程活动)时,单核处理器只能通过在多进程间不断切换来完成多任务。
多核处理器:将多个CPU集成在一块电路芯片上,如图所示,每个核都有L1和L2高速缓存,其中L1一个存放最近取到的指令,另一个存放最近读取的数据。
超线程:又称同时多线程,允许CPU执行多个控制流的技术。举例来说,Intel Core i7 处理器可以让每个核执行两个线程,所以一个 4 核的系统实际上可以并行地执行 8 个线程。


- 指令级并行
在较低的抽象级,处理器同时执行多条指令的属性,称为指令级并行。
- 单指令、多数据并行
一条指令产生多个可以并行执行的操作,这种方式称为单指令、多数据,即 SIMD 并行。例如,较新几代的 Intel 和AMD 处理器都具有并行地对 8 对单精度浮点数(C 数据类型 float)做加法的指令。
1.9.2计算机系统中抽象的重要性
抽象为一组函数规定一个简单的应用程序接口(AH)就是一个很好的编程习惯, 程序员无须了解它内部的工作便可以使用这些代码。在处理器里,指令集架构提供了对实际处理器硬件的抽象。 使用这个抽象,机器代码程序表现得就好像运行在一个一次只执行一条指令的处理器上。虚拟机,它提供对整个计算机的抽象,包括操作系统、 处理器、主存、I/O设备。

1.10小结
计算机系统是由硬件和系统软件组成的,它们共同协作以运行应用程序。计算机内部的信息被表示为一组组的位,它们依据上下文有不同的解释方式。 程序被其他程序翻译成不同的形式,开始时是ASCII文本,然后被编译器和链接器翻译成二进制可执行文件。
处理器读取并解释存放在主存里的二进制指令。因为计算机花费了大量的时间在内存、I/O设备和CPU 寄存器之间复制数据 ,所以将系统中的存储设备划分成层次结构层的硬件高速缓存存储器、 DRAM 主存和磁盘存储器。 在层次模型中,位于更高层的存储设备比低层的存储设备要更快,单位比特造价也更高。 层次结构中较高层次的存储设备可以作为较低层次设备的高速缓存。 通过理解和运用这种存储层次结构的知识,程序员可以优化 C 程序的性能。
操作系统内核是应用程序和硬件之间的媒介。它提供三个基本的抽象: 1)文件是对 I/O 设备的抽攀;2)虚拟内存是对主存和磁盘的抽象; 3)进程是处理器、主存和 I/O 设备的抽象。最后,网络提供了计算机系统之间通信的手段。从特殊系统的角度来看, 网络就是一种 I/O 设备。
注:显卡,显存,显示器的分工合作
1.【CPU -> 显卡】:CPU(在操作系统和驱动程序的协助下)向显卡发出指令:“需要在屏幕上显示字符串 'Hello, World!'”。
2.【显卡 -> 显存】:GPU 开始工作,将字符串的字体、颜色、背景等渲染成最终的像素阵列。GPU 将这个完整的、包含 "Hello, World!" 的一整帧图像,写入显存的帧缓冲区。
3.【显存 -> 显示器】:显卡上的显示控制器开始工作。它以一个恒定的速率(例如每秒60次)扫描显存中的帧缓冲区。它从左到右、从上到下,依次读取每个像素的颜色数据。每读一个像素,就通过视频线(如HDMI)将其发送给显示器。
4.【显示器呈现】:显示器接收到的是一连串的像素数据流。它按照同样的顺序,用接收到的数据控制屏幕上的每一个物理像素点发光。当一帧的所有像素都显示完毕,它就等待下一个垂直同步信号,然后开始显示下一帧(即使画面没变,也是在重复显示)。于是,"Hello, World!" 就稳定地显示在了屏幕上。
---------------------------------------------------------------------------------------------------------------------------------转载于作者本人:https://www.cnblogs.com/bluewhale20251019/articles/19151590
1224

被折叠的 条评论
为什么被折叠?



