线程和进程
基本概念
- 进程 对运行时程序的封装,是系统进行资源调度和分配的基本单位,实现了操作系统的并发
- 线程
进程的子任务,是 CPU 调度和分派的基本单位,用于保证程序的实时性,实现线程内部的并发
线程是操作系统可识别的最小执行和调度单位
每个线程都独自占用一个虚拟处理器:独立的寄存器组、指令计数器和处理器状态
每个线程完成不同的任务,但是共享同一地址空间(即同样的动态内存、映射文件、目标代码等等)、打开的文件队列以及其他内核资源
区别
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程,线程是依赖于进程存在的
- 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。
资源分配给进程,同一进程的所有线程恭喜那个该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量)、数据段(全局变量和静态变量)、扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用于存储所有局部变量和临时变量
- 进程是资源分配的最小单位,线程是 CPU 调度的最小单位
- 系统开销
由于在创建或销毁进程时,系统都要为之分配或回收资源(内存空间、I/O 设备等),因此操作系统所付出的开销将显著地大于在创建或销毁线程时的开销
类似的,在进行进程切换时,涉及到整个当前进程 CPU 环境的保存以及新被调度运行的进程的 CPU 环境的设置,而线程切换只需要保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作
可见进程切换的开销远大于线程切换的开销 - 通信
由于同一进程中的多个线程具有相同的地址空间,致使他们之间的同步和通信的实现也比较容易
通过进程间通信 IPC,线程间可以直接读写进程数据段(比如全局变量)来进行通信--需要进程同步和互斥手段的辅助以保证数据的一致性
在有的系统中,线程的切换、同步、通信都无需操作系统内核的干预 - 进程编程调试简单、可靠性高,但是创建销毁开销大
线程正好相反,开销小、切换速度快,但是编程调试相对复杂 - 进程间不会互相影响;一个线程挂掉可能导致整个进程挂掉
- 进程适用于多核、多机分布;线程适用于多核
进程间的通信方式
- 进程间通信包括管道、系统 IPC(消息队列、信号量、信号、共享内存等)、套接字 socket
1. 管道
- 管道主要包括匿名管道和命名管道
匿名管道可用于具有亲缘关系的父子进程间的通信,命名管道处理具有匿名管道所具有的功能之外,还允许无亲缘关系进程间的通信 - 匿名管道 PIPE
- 半双工(数据只能在一个方向上流动),具有固定的读端和写端
- 只能用于具有亲缘关系的进程之间的通信(父子进程或兄弟进程之间)
- 可以看成是一种特殊的文件,对于它的读写也可以使用普通的 read、write 等函数,但它不是普通的问题间,并不属于其他任何文件系统,并且只存在内存中
- 命名管道
- FIFO 可以在无关的进程之间交换数据
- FIFO 有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中
2. 系统 IPC
- 消息队列
消息队列是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列 ID)来标记,具有写权限的进程可以按照一定规则向消息队列中添加新信息,具有读权限的进程则可以从消息队列中读取信息
消息队列克服了信号传递信息少、管道只能承载无格式数据以及缓冲区大小受限等特点
特点:- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
- 消息队列独立于发送和接受进程,进程终止时,消息队列及其内容不会被删除
- 消息队列可以实现消息的随机查询,消息并不一定要以先进先出的次序读取,也可以按照消息类型读取
- 信号量 semaphore
信号量(semaphore)和前面已经介绍过的 IPC 结构不同,它是一个计数器,可以用来控制多个进程对共享资源的访问,信号量用于实现进程间的互斥和同步,而不是用于存储进程间通信数据
特点- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作
- 每次对信号量的 PV 操作不仅限于对信号量值 +1 或者 -1,可以任意加减任何正整数
- 支持信号量组
- 信号 signal
一种比较复杂的通信方式,用于通知接受进程某个事件已经发生 - 共享内存 (Shared Memory)
它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新,这种方式需要依赖某种同步操作,如互斥锁和信号量等
特点- 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取
- 因为多个进程可以同时操作,所以需要进行同步
信号量 + 共享内存
通常结合在一起使用,信号量用来同步对共享内存的访问
3. 套接字 socket
- socket 也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机之间的进程通信
线程间通信的方式
- 临界区:通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问
- 互斥量 Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问
- 信号量 Semaphore:为控制具有有限数量的用户资源而设计,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目
- 事件(信号),Wait/Notify:通过通知的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作