第一章、计算机系统漫游

操作系统在计算机系统中处于计算机硬件和用户之间的位置

1.信息就是 (位+上下文)

#-------> hello.c
#include<stdio.h>
int mian(){
    printf("hello , world\n");
    return 0;
}
/*   这个源程序实际上由0和1组成的序列,每8位为一组,称为字节
 *   每个字节表示程序中的某个文本字符,现在用ASCII码来表示文本字符,就是用一个唯一的单字节大小整数值来表示1个字符
 *  有ASCII字符组成的字符称为文本字符,其他的文件称为二进制文件 ,注意每个文本行都是以一个看不见的换行符‘\n’来结束的
 */

hello.c说明了一个基本思想,系统中的所有信息(包括磁盘文件,程序,用户数据及网络上的数据)都是由一串位表示的。

重点:区分这些数字对象的唯一方法就是我们读到这些对象的上下文。在不同的上下文中,一个相同的字节可能表示一个整数或者浮点数或字符串等。

2.linux>gcc -o hello hello.c

为了在系统上运行hello.c程序,每条C语句都必须被其他程序转化为一系列的低级机器语言指令。

下图中从源文件到目标文件的转化是由编辑器驱动程序完成的

gcc

  • 预处理 预处理器cpp根据以字符#开头的命令,修改原始的c程序,得到了另一个源程序 *.c—->*.i
  • 编译 编译器ccl编译成汇编程序,汇编程序是通用的输出语言(fortra和c都生成相同的.s) *.i—>*.S
  • 汇编器 as汇编器生成可重定位二进制程序, *.s—->*.o
  • 链接生成可执行文件

3.处理器读取并解释存储在存储器中的指令

将可执行目标文件hello的文件名hello输入到称为shell的应用程序中,即可在unix系统上运行此文件

linux> ./hello

hello, world

linux>

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

1.1系统的硬件组成

总线:贯穿整个系统的一组电子管道,携带信息字节负责在各个部件之间传递。总线被设计为传送定长的字节块(字word),字中的字节数是一个基本的系统参数,现在的大多数机器字长要么是4个字节(32位),要么是8个字长(64位)。

IO设备(输入/输出):系统与外接设备联系的通道。用户输入的鼠标和键盘、显示屏、保存数据的磁盘、其他网络类设备。

主存:一个临时存储设备,在处理器执行程序是用来存放程序和程序处理的数据。从物理上讲主存一组动态随机存储存储器(DRAM)芯片组成,从逻辑上讲是一个线性的字节数组,每个字节都有唯一的地址(数组索引)。

处理器:包括运算器和控制器,解释执行保存在主存中指令的引擎。处理器的核心是一个大小为一个字的寄存器(PC),从系统通电开始,处理器一直在不断的读取PC寄存器的指令,在更新指令寄存器。

CPU在下列指令下可能会执行的操作

  1. 加载:从主存复制到寄存器
  2. 存储:从寄存器复制到主存的某个位置,更新原来的内容。
  3. 操作:把寄存器的内容复制到ALU(算数逻辑单元)进行算数运算。
  4. 跳转:执行指令更新指令寄存器的内容。

1.2运行hello.c程序

shell是一个命令行解释器,它等待你的输入,如果输入的第一个单词不是内置shell命令,shell会认为它是一个可执行文件。

当我们敲回车时,shell执行一系列命令加载可执行程序hello,把代码和数据从磁盘复制到主存。

执行hello程序的main函数里面的机器语言指令,把“hello world”从主存复制到寄存器,从寄存器复制到显示设备。

这个示例揭示了一个重要的问题,系统花费了大量的时间进行了信息的传递

 hello程序最初在磁盘上,当程序加载时复制到主存,程序执行时指令要复制到处理器。相似的字符串“hello world”开始在磁盘,然后被复制到主存,最后从主存复制到显示设备。从程序员的角度看,这些复制就是开销,系统设计这的主要目的就是使这些复制尽可能快的完成。针对这种差异,系统设计者采用了更小更快的存储设备,称为高速缓存存储器(简称为cache或者高速缓存)。

1.3操作系统

操作系统看成是应用程序与硬件之间插入的一层软件。

所有应用程序对硬件(处理器、主存、I/O设备)的操作都必须通过操作系统。

两大基本功能:

  1. 防止硬件被失控的应用程序滥用。
  2. 向应用程序提供简单一致的机制来控制复杂的低级硬件设备。
  3. 操作系统通过几个基本的抽象(进程、虚拟内存、文件)来实现这两个功能。

进程:对处理器、主存和IO设备的抽象表示。

虚拟内存:对主存和磁盘和IO设备的抽象。

文件:对IO设备的抽象表示。

1.3.1进程

进程是操作系统对一个正在运行的程序的一种抽象,在一个系统上可以同时运行多个进程,而每个进程都好像在独占的使用硬件。

并发执行:一个进程的指令和另一个进程的指令交替执行。

总结:
  并发是指多个线程轮流执行(单核CPU);
  并行是指多个线程同时执行(多核CPU),微观上是同时的;
  串行是指一个一个的执行,处理完一个才能处理下一个,不轮换;

一个CPU并发的地执行多个进程,这是通过处理器在进程间切换实现的。操作系统这种交错执行的机制称谓“上下文切换”

进程运行的所有状态信息称为“上下文”,单处理器只能处理一个进程,当操作系统进行进程转换时,它会保存当前进程的上下文,并恢复新进程的上下文,然后将控制权交给新进程。

两个并发进程:shell进程和hello进程 

最开始shell程序在执行,等待命令行输入。 当我们让他运行hello程序时,shell通过一个系统调用执行我们的请求,系统调用将控制权传递给操作系统。操作系统保存shell的上下文,创建一个新的hello进程及其上下文,将控制权传给hello进程,hello进程结束后,操作系统恢复shell进程的上下文,并将控制权传递给它。

进程之间的上下文切换是由操作系统的内核管理的,内核是常驻内存的操作系统代码部分。当应用程序需要操作系统的某些操作时,比如读写文件,他会执行特殊的系统调用命令,将控制权传递给内核,然后内核执行请求的操作并返回应用程序。内核不是一个独立的进程。它是系统管理全部进程所用代码和数据结构的集合。

1.3.2 虚拟内存 

虚拟内存是一个抽象概念,他为每一个进程提供一个假象,每个进程好像独占主存,每个进程看到的是一致的内存,称为虚拟地址空间。

  • 栈区—由编译器自动分配释放,存放函数形参,局部变量和自动变量
  • 堆区—用于分配由malloc、realloc、calloc分配的空间
  • 数据区—该区又分为bss段,rodata段和data段:
  • bss段—保存未初始化或者初始化为0的全局变量
  • rodata段—用于保存常量
  • data段(全局区、静态数据区)—保存初始化不为0的全局变量或者static修饰的变量
  • 代码区—存放函数体的二进制代码
1.3.4 文件

文件就是字节序列的容器,仅此而已。每一个IO设备,包括磁盘、键盘、显示器、甚至是网络,都可以看成是文件。系统中的所有输入输出都是通过使用一小组称为Unix I/O的系统函数调用读写文件来实现的


 线程

线程:是操作系统能够进行运算调度的最小单位。是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间等等。但同一进程中的多个线程有各自的调用栈,自己的寄存器环境,自己的线程本地存储。多线程之间较多进程之间更容易共享数据。

并发与并行

如果某个系统支持两个或者多个动作(Action)同时存在,那么这个系统就是一个并发系统。如果某个系统支持两个或者多个动作同时执行,那么这个系统就是一个并行系统。并发系统与并行系统这两个定义之间的关键差异在于“存在”这个词。

在并发程序中可以同时拥有两个或者多个线程。这意味着,如果程序在单核处理器上运行,那么这两个线程将交替地换入或者换出内存。这些线程是同时“存在”的——每个线程都处于执行过程中的某个状态。如果程序能够并行执行,那么就一定是运行在多核处理器上。此时,程序中的每个线程都将分配到一个独立的处理器核上,因此可以同时运行。

我相信你已经能够得出结论——“并行”概念是“并发”概念的一个子集。也就是说,你可以编写一个拥有多个线程或者进程的并发程序,但如果没有多核处理器来执行这个程序,那么就不能以并行方式来运行代码。因此,凡是在求解单个问题时涉及多个执行流程的编程模式或者执行行为,都属于并发编程的范畴。

摘自:《并发的艺术》 — 〔美〕布雷谢斯



猜你喜欢

转载自blog.csdn.net/weixin_41413441/article/details/79230145