程序本身是静态的代码和数据,当程序被载入内存开始运行就成为进程。但进程与程序并非一对一的关系,多进程的程序很常见。
比如 Chrome 的进程就分为:
- 一个浏览器进程:负责浏览器层面如 UI、IO;
- 多个渲染进程:每个 tab 一个,负责呈现页面;
- 多个插件进程:每个运行中的插件一个,实现插件功能
进程的创建主要有两种方式,一种是在 Unix 系统下调用 fork()
复制当前的进程(往往接下来会再调用 exec()
抛弃原来进程的内存映射);一种是 Win32 API中的 CreateProcess()
函数,它跟当前已有的进程没什么关系,需要传入参数创建全新的进程。
进程在创建时,操作系统会建立一个 PCB(Process Control Block) 存储一个进程相关的信息,包括其状态、内存信息、打开的文件列表、CPU 调度用的信息…… 等等。一个 CPU 同时只能执行一个进程,所以它需要在不同进程间切换,每次切换时都需要将整个 PCB 都移进移出 (context switch)。
进程从创建到结束可能的状态如图:
其中比较有意思的是 ready(等待被 CPU 调度)和 waiting(暂停,等待某个事件)。
进程要结束的时候一般是通过 system call exit()
,这个函数会返回进程的退出状态给其父进程,然后在等待父进程响应期间被称为僵尸进程(ps status: Z
= zombie)。父进程会通过 wait()
函数获得自己子进程的退出状态,到这一步子进程才能顺利退出。而如果一个进程未执行 wait()
就结束了,那么它的子进程会托管给一切用户进程的父进程:init
(pid = 1)。
进程之间的通讯主要通过两种方式:共享内存 及 信息传递。
共享内存指进程共享一些变量,通过这些变量来传递信息。操作系统本身只需要提供共享内存给进程即可。
消息传递则主要由操作系统完成。消息传递比内存共享更容易实现,但是由于要靠 system call 来实现,所以效率比共享内存低。
Client - Server 模式中的进程通信常用方式为: 1.Socket 相对简单低级的通信方式。 2.RPC(Remote Procedure Calls) 并非简单的数据包,传递的消息有函数与参数。接收方根据请求执行,然后把结果作为另一条消息发回。 3.Pipes