计算机系统中的所有信息——包括磁盘文件、内存中的程序、内存中存放的用户数据以及网络上传送的数据,都是由一串比特序列(也可以说是字节序列,一个字节八个位,即八比特)表示的。区分不同数据对象的唯一方法是我们读到这些数据对象时的上下文。比如,在不同的上下文中,一个同样的字节序列可能表示一个整数、浮点数、字符串或者机器指令。
// 文件名:hello.c
#include
int main() {
printf("hello,world!\n");
return 0;
}
上面源程序hello.c
,以文本文件的形式被我们所看到,但是计算机中无法存放字符,只能存放0和1,而0和1只表示数字,所以大佬们就制定了一种全世界统一的标准,比如ASCII编码(或者其他编码),用一个数字代表、且只能代表一个字符。所以文本文件实际上在磁盘和内存中是以二进制的形式存放的,根据ASCII编码标准,每个字符都有特定且唯一的数值来表示。hello.c
对应ASCII编码如下:
i
字符对应的十进制数值是105
,则在计算机中,i
字符是以105
的二进制数1101001
存放的。根据区分计算机中不同数据对象的唯一方法——读到这些数据对象时的上下文可明白,当计算机中的比特序列1101001
表示字符时,它就是字符i
,当1101001
表示无符号整数时,他就是105
。
hello.c
程序的生命周期是从一个高级C语言程序开始的,因为这种形式能够被人读懂。然而为了在系统上运行hello.c
程序,每条C语句都必须被其他程序转化为一系列的低级机器语言指令。然后这些指令按照一种称为可执行目标程序的格式打包好,并以二进制磁盘文件的形式存放起来。目标程序也称为可执行目标文件。
上面编译系统的整个过程,在编译器结构中详细介绍过。
总线:贯穿整个系统的一组电子管道,称做总线,他携带信息字节并负责在各个部件间传递。总线被设计成传送定长的字节块,即字(word),字的字长由机器的位数决定,4字节(32位)还是8字节(64位)。
I/O设备:键盘,鼠标,显示器,磁盘等。
主存:是一个临时存储设备,在处理器执行时,存放程序和程序处理的数据。
处理器:解释(或执行)存储在主存中指令的引擎。处理器的核心是一个字长的存储设备或寄存器,称为程序计数器(PC,就是CS:IP)。在任何时刻,程序计数器都指向主存中的某条机器语言指令(也就是PC中存放着该条指令的地址)。
从通电到断电,处理器一直在不断地从PC指向的内存中读取指令,解释指令中的位,执行该指令提示的简单操作(加载、存储、ALU运算、跳转),然后更新PC,使其指向下一条指令,而这条指令并不一定和在内存中刚执行完的指令相邻。
直接存储器存取(DMA)技术:数据可以不通过处理器而直接从磁盘到达主存。
hello
程序的机器指令最初放在磁盘上,程序加载时,被复制到主存,处理器运行程序时,指令又从主存复制到处理器。
程序运行时,系统需花费大量的时间把信息从一个地方挪到另一个地方,但是主存和处理器之间的读取速率相差太大,主存拖后腿,增加额外的时间开销。所以系统设计者为了尽可能快的完成这一操作,搞了高速缓存这么个玩意,作为暂时的集结区域,用来存放处理器近期可能用到的信息。
存储器层次结构的主要思想是:上一层的存储器作为低一层存储器的高速缓存。
操作系统是介于应用程序和硬件之间的一个软件,它有两个基本功能:
操作系统通过几个基本的抽象概念(进程, 虚拟内存, 文件 )来实现这两个功能。注意这只是抽象出来的概念,并非真实存在的设备,文件是对IO设备的抽象,虚拟内存是对主存和文件的抽象,进程是对处理器、内存和文件的抽象。
进程是操作系统对一个正在运行的程序的一种抽象。当一个程序被加载到内存中并成为一个进程时,它可以分为四个部分——堆栈、堆、文本和数据。进程数都是多于可以运行他们的CPU个数的。传统系统在一个时刻只能执行一个程序,而先进的多核处理器能够执行多个程序,无论实在单核还是多核系统,一个CPU看上去都像是在并发地执行多个进程。这是通过处理器在进程间切换来实现的,操作系统实现这种交错执行的机制称为上下文切换。
上下文:即是进程运行所需的所有状态信息,比如PC和寄存器文件的当前值,以及主存内容。
上下文切换:进程的上下文切换是指 cpu 从一个进程切换到另一个进程。即保存当前进程的上下文,恢复将要运行的新进程的上下文,然后将控制权传递给新进程,新进程就会在上一次停止的地方开始。
进程上下文切换主要包含两个主要过程:进程地址空间切换和处理器状态切换
进程地址空间切换
处理器状态切换
一个进程实际可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。多线程比多进程更容易共享数据,有多处理器的时候,多线程也是一种使程序更快运行的方法。
虚拟存储器是一个抽象的概念,它为每个进程提供了一个假象,即每个进程独占地使用所有主存。每个进程看到的内存都是一致的,称为虚拟地址空间。
地址空间最上面的区域为操作系统的代码和数据保留的,这对所有进程都是一样的。地址空间的底部区域存放用户进程定义的代码和数据。
每个进程的虚拟地址空间由大量准确定义的区构成,每个区都有专门的功能。从最低地址开始,逐步向上介绍:
程序代码和数据:对于所有进程来说,代码从一固定地址开始,紧接和C全局变量相对应的数据位置。代码和数据区是直接按照可执行目标文件的内容初始化的。
堆 :代码和数据区紧随着运行时堆。代码和数据区是在进程一开始就被规定了大小,与此不同,调用malloc和free时堆动态的扩展和收缩。
共享库:地址空间的中间部分存放C标准库和数学库这样共享库代码和数据的区域。共享库概念非常强大,相当难懂。
栈: 用户虚拟地址顶部的是用户栈,编译器用它来实现函数调用。和堆一样,在执行时动态的扩展和收缩。
内核虚拟存储器: 内核总是驻留在内存中,是操作系统一部分。
文件 就是 字节序列,仅此而已。所有I/O设备 都可视为文件,包括磁盘、键盘、显示器、甚至是网络。系统中的所有输入输出都是通过使用Unix I/O 的系统函数调用读写文件来实现。
从一个单独的系统来看,网络可视为一个I/O设备。如系统从主存中赋值一串字节经过网络适配器,再到另一台机器。
我们平时用XShell操作服务器时,程序运行的基本步骤与下面一样。
并发:是一个通用概念,指一个同时具有多个活动的系统。
并行:指的是用并发使一个系统运行的更快。并行可以在计算机多个抽象层次运用。