一、什么是进程,什么是线程?
1.1 进程
- 进程中包括有多个线程,进程与进程之间是相对比较独立的。
- 进程中有一个逻辑内存,每个进程都会有分配到一个独立的内存空间,还分配了一个文件/网络句柄,
- 句柄类似一个标识符,所有的进程所或多或少都有一定的句柄数的引用,句柄实际上是一个指针,指向一块包含具体信息数据的内存,所以句柄是当你要访问该进程时取得的,使用完必须释放。(打个比方,我要使用redis需要打开6379端口,这个时候需要访问句柄,获取到内存地址去访问获取对应的资源来执行该进程)
- 一个进程可以拥有多个句柄数目,但是只有一个进程ID
-
进程不同时刻所引用的句柄数目是不一定的
1.2 线程
- 线程中有一个栈内存(很多人把它称为堆栈),主要用来存储局部变量资源或者内存地址
- 还有一个PC块,主要是存储下一条执行指令的地址,PC是指向一块共同的内存(进程里面的被系统分配到的逻辑内存)。
- TCS :ThreadLocalStorage 主要存储线程自己定义的变量,不想和其他线程进行共享,理解为线程的私有变量。
- 线程共享进程的资源主要有:进程代码块、进程的全局和静态变量、进程打开的文件描述符、信号的处理器、进程当前的目录、进程ID、进程组ID。
二、进程和线程的区别
- 进程是程序资源分配的最小单位,线程是程序执行的最小单位。
- 进程有自己的内存地址空间,线程包含在进程的地址空间中。
- 相对于进程与进程之间线程之间通信方式比较方便,线程能共享进程分配到的逻辑内存的资源。也就是说,同一进程下的线程共享全局变量、静态变量等数据,具体共享的内容1.2中介绍线程已经说明。
- 进程的分配开销比线程大,但是进程的健壮性比线程高,因为进程间不会互相影响,线程一个挂掉了可能会造成进程崩溃。
- 也可以这么说,线程是程序执行的单位,进程只是存放线程的容器,容器会提供线程执行的资源。
三、进程和进程之间的通信方式
因为进程与进程之间是相互独立的,所以关于进程之间的通信数据共享复杂,需要用IPC;数据是分开的,同步简单,但是占用内存多,切换复杂,CPU利用率低。
IPC的方式通常有:管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。
3.1 管道
管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。
3.1.1 管道的特点
- 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
- 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
- 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
3.1.2 原型结构
当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。如下图:要关闭管道只需将这两个文件描述符关闭即可。
3.13 示例
单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:
若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。
3.2 有名管道FIFO
FIFO,也称为命名管道,它是一种文件类型。
3.2.1 特点
- 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
- FIFO可以在无关的进程之间交换数据,与无名管道不同。
- FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
3.2.2 介绍
FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。
3.3 消息队列
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。
3.3.1 特点
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
- 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除,所以另一个进程读写的时候要判断前进程是否已经读完数据。
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的指定类型读取。
3.4 信号量
信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
3.4.1 特点
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
- 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
- 支持信号量组。
3.5 共享内存
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
3.5.1 特点
- 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
- 因为多个进程可以同时操作,所以需要进行同步。
- 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
3.6 五种通信方式的对比
1.管道:速度慢,容量有限,只有父子进程能通讯
2.FIFO:任何进程间都能通讯,但速度慢
3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
4.信号量:不能传递复杂消息,只能用来同步
5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存。
四、线程与线程之间的通信(多线程)
4.1 同步
4.2 wait和notify/notifyAll机制
4.3 while轮询的方式
4.4 管道通信
4.5 Semaphore 信号量