前言
以<深入理解计算机系统>(以下称"本书")内容为基础,对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定
引入
并发是一种非常重要的机制,用于处理多个指令流.特别是在网络通信中,并发非常适合处理多个连接中的数据请求.
并发基础
如果逻辑控制流在时间上重叠,那么他们就是并发的(concurrent).
我们主要将并发看做是一种操作系统内核用来运行多个应用程序的机制 ---本书原话
举个例子:有个厨师,他要做炒菜,也要做汤菜.他会在时间上这样分配:
1>给做汤菜的锅掺水打开炉灶开关开始烧水 2>切菜
3>汤菜下锅煮 4>炒菜.
4个动作在2个时间段内完成了汤菜和炒菜的制作.当把厨师换成cpu,把做的菜换成程序,这就是并发的含义.此外,如果有多个厨师,相当于多个CPU做更多的事,就是并行.
设计并发的原因
每个程序都会被编译成机器指令,CPU执行的是类似这样的指令的集合,他们像一个流:
MOVEQ 0x12345678 %rax;
有多个程序运行在操作系统上,即多个等待被执行的指令流.所以,逻辑控制流=指令流(笔者个人感觉指令流的叫法更形象,所以之后沿袭这个叫法).
指令流有哪些?除了程序员编写的程序(标志是main()函数)外,还有硬件中断,信号,内核函数等.
指令流在执行过程中,不时会读取文件数据 ,类似上面的厨师烧水,这时候CPU处于等待 状态,如果CPU不干点别的,等待这段时间相当于浪费了.所以设计了并发这种机制.++并发的目的是为了提高CPU的效率++ ,并发的实质是指令流之间的切换.
指令流切换的原因除了程序等待数据外,还可能是硬件中断 (例如时钟中断,硬件故障等),信号(例如程序执行后出现某种情况需要跳转)等.这部分内容(包括进程)笔者之前没有提及,一是因为比较抽象,而是没找到具体应用场景,在并发的时候边学边补.
进程模型
有必要了解进程.并发有多种形式,但都基于进程.下面是笔者对进程模型的简单理解:
指令流---被编译的程序(或者系统内核函数,信号),他们是躺在硬盘里"睡觉"的二进制文件.当程序被执行时,文件里的数据被调入内存,再经过高速缓存,寄存器被CPU执行.被执行的程序称为"进程".
文件内容:被编译成指令流前的程序文件---也就是程序员很熟悉的自己写的程序文件,大概包含了这些数据:自定义类型,全局变量,函数,根据自定义类型产生的对象.++为了程序的执行++ ,内存给他们每个程序分配了空间,包括堆区,栈区,临时存储区,文件指针等(见本书P510)
当发生指令流切换时,被切换进程的当前状态 需要被记录下来,包括栈指针,文件指针,程序计数器等数据,当进程切换回来时,数据要返回到被切换时的状态.切换时被记录的状态称为进程上下文.
从物理的角度来讲,进程=进程上下文+进程空间.就是说进程在内存中包括了这两部分.为什么要从物理角度来讲呢?如同理解数据结构,物理层的数据结构只有数组和链表两种,比较容易理解.逻辑层比较抽象,当逻辑层理解不了时,站在物理层还能稍微站得住脚.
fork子进程
进程提供了子进程机制,来提供并发的基本模型.当调用fork后生成一个子进程.父子进程之间的关系是:独立地址空间+共享文件.独立地址空间是指内存复制进程空间给子进程,子进程拥有和父进程相同的地址空间.共享文件是指父子进程使用的文件指针相同(访问文件时需要特别注意).
并发应用场景
本书提到了几种应用级并发的场景:
1.访问慢速I/O设备.
和前面提到的程序等待数据是一个意思,"慢速"是相对的,即CPU速度相比较访问文件快.
2.与人交互
例如打印文档时,人还可以缩放窗口,点鼠标等操作.本书提到了现代视窗系统 (Windows操作系统)利用并发来提供这种能力.意思是Windows系统支持并发,似乎不需要程序员显式提供.
3.通过推迟工作以降低延迟.
4.服务多个网络客户端
本章重点,通篇都是在讲服务器的并发问题,如何建立并发服务器,允许服务器同时为多个客户端服务.---黑体字是本书原话,也是本章核心内容.
5.多核机器进行并行计算
并发程序
概念:使用应用级并发的应用程序称为并发程序.
**---**解读:程序员可以编写的用于并发的程序.
操作系统提供了三种基本的构造并发程序的方法:进程;I/O多路复用;线程
基于进程的并发编程
构建并发程序最简单的方法就是用进程.例如,一个构造并发服务器的自然方法:在父进程中接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务.
本书P682的几张图把基于进程的并发编程描述得很清楚.
要点
本书P683,有两个要点,一是回收僵死子进程;二是关闭父进程描述符.分别对应P683中echoserverp.c代码第4~9行,以及第30行,第33行.
代码不做过多解读,主要是和信号相关.
分析
本书P684对进程做了优劣描述.优点是不会覆盖另一个进程的虚拟内存(即上面提到的"进程空间 "),缺点是进程间通信困难,必须使用IPC通信机制.而因为进程控制和IPC的开销高 ,所以速度慢
另外书上没提但容易想到的一点:进程需要的物理空间大---每一个连接请求,fork一个子进程就需要一个新的进程空间,以及对应的上下文空间.
所以,++基于进程的并发,没有多少应用场景++.也不是应关注的重点.
但可以试想一下:访问量小,客户端连接少;服务器内存大,不在乎进程带来的内存负担.满足这两点,可以做基于进程的服务器.
小结
并发编程基础,和基于进程的并发服务器