(个人理解,有误导欢迎指正)
道家说,形而上为道,形而下为器。人类为器,由道而来,人类可能永远无法通彻宇宙之道,但计算机由人类所创,对计算机而言,人为道而计算器为器。我这里只是在人类的角度上,形而上的认识计算机科学~
讲解进程间通信前,先了解一下计算机到底是什么?
计算机本质上是为了方便解决人类问题而被人类创造而来的,说白了,是由可以理解和执行人类逻辑运算的CPU作为核心,外加一个总线,总线上挂载了一堆的设备,内存、显卡、磁盘、网卡、定时器、中断控制器等,这些设备各有各的功能,比如存储数据、产生电信号、转换数据为其他形式等,根源上讲,这些设备本质都是在处理、存储、转换电信号,另外不同的设备处理电信号的速度不同,在各种外设中,要数内存RAM存储电信号速度最快,所以被设计来在程序执行过程中存储CPU指令和数据的核心----任何存储在较慢设备上的CPU指令和数据在执行前都会被转存到内存上进行执行。在所有的外设中,中断控制器比较特殊,它可以产生电信号来中断CPU当前正在运行的指令,转而去执行另外的指令,其他的好多外设也都可以产生中断信号,所有其他外设的中断信号都统一交给中断控制器,由中断控制器来中断CPU,这样设计很有扩展性~
操作系统是什么?进程是什么?
操作系统其实可以分为静态部分和动态部分:
静态部分是指经过编译后的CPU指令,是一个在系统启动的时候就被加载到内存中的大的函数库,函数其实就是按功能划分的一块块的CPU指令集合,函数库里包含了一堆API函数等待被调用,这些被动的被调用的函数中,按功能可以分为很多种,比如,可以和各种外设打交道的函数称为设备驱动程序、可以操作内存的函数有内存管理函数、进程管理函数、可以处理外设中断信号的中断处理函数等。
动态部分其实就是指操作系统运行起来后的内存布局了,用程序执行上下文来描述应该比较合理,我们来分析下程序执行过程中都涉及到了哪些上下文:首先,指令执行过程会有CPU中的一系列寄存器参与来参与完成指令,这组寄存器俗称硬件上下文,其次,由于CPU寄存器数量有限,程序执行还必须要有内存的辅助,一般来说,函数的执行需要有内存栈和堆,堆和栈俗称软件上下文,我们可以这么描述进程的定义:静态的指令代码和静态数据+程序执行上下文(堆栈和寄存器)组成了一个进程~
进程在内存中的布局是什么样子的呢?
磁盘中的可执行程序被加载到内存中后,一般会有这么几部分来存储:
代码段:存放只读的CPU指令集合
只读数据段:存放从可执行文件加载到内存中就不会变化的只读数据
已初始化数据段:存放从可执行文件加载到内存中的已初始化的变量数据
未初始化数据段: 存放未初始化的变量
堆区:程序运行过程中动态生成的数据
栈区:函数执行上下文,函数运行时压入新栈帧,函数执行后销毁,栈帧中存放函数的参数、局部变量等数据
简单分析下操作系统启动过程:
任何系统都会有个初始化的过程,操作系统也不例外。计算机启动,然后操作系统被从磁盘加载后,最先会启动的第一个进程是idle进程,它要做如下一些事情:
1 初始化虚拟内存,内核页表等
2 初始化VFS-虚拟文件系统,VFS是内核中的一种文件系统,所有外部设备、外设上的其他文件系统都会挂载到该文件系统上,linux把计算机所有的外部设备都抽象为文件,俗称设备文件,磁盘上的多个分区的文件系统也会挂载到VFS上,该文件系统是应用进程访问外部设备的和外部文件系统的桥梁,说白了,VFS就是操作系统初始化后在内存中建立的文件系统数据结构,文件系统一般分为inode索引节点和其关联的数据节点,数据节点可以对应为普通的二进制数据,也可以和内核在内存中建立的其他数据结构相关联,比如设备文件inode节点就会关联一个结构体,结构体中包含了对指定设备进行访问的函数,比如open close read write等
3 孵化出init进程,通常init进程会装入shell程序,shell程序可以是命令行的shell,比如bash,也可以是图形界面的shell,比如Xserver,shell程序的主要功能是监听终端设备的命令,执行用户程序,如bash监听tty设备,执行用户程序,XServer配合窗口管理器提供桌面UI环境,监听用户的鼠标和键盘事件,启动GUI用户进程等
4 初始化内核中断向量表,允许硬件中断,然后进入idle进程的事件循环。这时候正式开启了进程调度,硬件定时器会定时产生中断,该中断会调用进程调度服务程序,来完成进程执行上下文件的保存和切换,实现进程调度。idle事件循环一般是执行计算机长时间无操作休眠以及调用进程调度服务程序来软调度进程。后面就可以通过shell来执行更多的用户进程了~
进程间如何通信?
孤立的进程作用有限,大部分情况都会有多个进程间相互通信的需求,我所说的进程间通信是广义的,不一定是在同一台计算中中的进程,也可能是跨网络的多个计算机中的进程通信。操作系统中的每个进程的地址空间都是相互隔离的,每个进程都有自己的页表,指令、数据、堆栈都是相互隔离的,我们把两个进程想象成两个水槽,两个水槽如何才能互相连通呢? 管道! 通过一根管道可以控制水流单向流动,一对管道就可以实现两个水槽的水流互相流动。操作系统目前实现的所有进程间通信方式都可以抽象成一个或者一对管道,来实现进程间的单向或者双向通信。
单机下的进程通讯方式有:
1 共享内存,一个进程通过系统调用在内核中开辟一块共享的内存,然后和自己进程的页表相关联,然后开启另一个子进程按同样的方式将共享的那块内存和自己的页表相关联来实现通讯,这里共享内存充当了“管道”
2 管道文件通讯,和共享内存的方式基本一致,通过系统调用在内核中打开一个管道文件,两个进程一个读管道、一个写管道
3 消息队列,操作系统内核中提供一个消息队列服务,一个或多个进程向队列生产数据,另一个进程或者多个进程消费同一个队列的数据,这里的队列充当了我上面抽象的“管道”,而且可以多进程相互通讯,实现了解耦
4 socket文件方式,两个进程打开一对socket文件进行通讯,socket数据传输是双向的,相当于我上面抽象的“双向管道”,在单机的情况下,通过socket发送的数据包不会经过网卡设备发送出去再接受回来,而是直接在内存协议栈中来回传递
跨机器的进程通讯方式:
由于跨了机器,本质上就是两个进程是分别在各自机器的内存中执行的,由于是在两个内存中,这种方式下的两个进程要想通信,只有一种方式来充当通讯“管道”了--socket,本质上,跨机器的socket通讯相当于一个机器的网卡外设和另一个机器的网卡外设之间通讯,然后分别在两个机器上的进程分别读取自己的socket文件来把网卡外设的数据转移到自己的内存进程中去,和单机间进程的通讯其实原理是一致的。
让我们脑洞一下,多个机器间通过网络相连接,相当于是网线把每个机器的内部总线互相连接在一起了,假如科技的发展导致网线传输数据的速率达到了机器内部总线一样的速度,那么多个机器间就不存在本质的隔阂了,地球上只有一台“超级计算器”!
我们再脑洞一下,单个计算机比作我们体内的神经元细胞,网卡和网线分别是树突和轴突,多个计算机通过网线相连就相当于我们体内的神经网络,轴突和树突依次相接组成庞大的网络,计算机之于人类会不会就像人类之于宇宙,细思恐极也~