0.参考文章
- 深入理解线程和进程
- UNP — <UNIX网络编程卷1:套接字联网API>
1.定义
1.1 进程定义
进程是操作系统资源分配的最小单位,线程是任务调度和执行的最小单元。
进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,表示一个逻辑控制流,拥有一个独立的虚拟内存地址空间。如下是进程的虚拟空间分配模型图。
关于进程的虚拟空间进一步探讨可以浏览linux 进程地址空间的一步步探究一文。
1.2 线程定义
线程是独立调度和分派的基本单位,还能够并发的执行,共享进程的资源,是一个轻型的实体。
多线程的进程地址空间如下:
2.开销
进程通过fork进行创建,线程通过clone进行创建。
2.1 进程切换开销
fork的代价是昂贵的,创建子进程时会将父进程的内存映像复制到子进程,fork不只是复制了页表结构(虚拟地址对应物理地址),还复制了父进程的文件描述符表(linux下一切皆文件),信号控制表,进程信息,寄存器资源等等。它是一个较为深入的复制。
额外了解:尽管有着写时复制技术(fork刚创建时采用了共享的技术,只用指针的方式指向了父进程的物理资源,当子进程需要这些资源进行写操作时,才会真正的复制一块物理资源供子进程使用).
一个进程的运行依赖于这些资源,当进行进程上下文切换时,需要对这些资源进行保存。这也是多进程切换的缺陷。
2.2 线程开销
我们知道,线程是共享进程的资源的,当线程进行上下文切换时,只需要保存线程运行的一些简单信息,所以线程的切换时轻量级的。
具体来说,同一进程内的所有线程除了共享全局变量外还共享:
- 大多数数据
- 文件描述符
- 信号处理函数和信号处置
- 当前工作目录
- 用户id和组id
- 进程指令
线程有各自的 - 线程id
- 寄存器集合(程序计数器和栈指针)
- 栈
- errno
- 信号掩码
- 优先级
3.通信方式
3.1 进程间通信
不同进程间有着不同的虚拟地址空间,所以进程间的通信不能简单的直接共享内存。常用的进程方式有
- 管道 : 半双工的通信方式,只能单向流动,适合在父子进程间通信
- 有名管道:类似管道,允许无亲缘关系的进程通信。
- 共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,配合信号量用来实现进程间的同步和通信
- 消息队列:消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识
- 信号量:它是一个计数器,用于实现进程间的互斥和同步,并不存储消息。
- 套接字
具体解释和demo实例可参考文章进程间通信(IPC)
3.2 线程间通信
线程是共享进程的内存空间,所以考虑好互斥和同步即可.常用的措施有:
- 锁(互斥):互斥锁、自旋锁、读写锁。访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量,适用于防止同时访问某个共享变量.
- 条件变量(同步):适用于一个线程完成了某一个动作就通过条件变量发送信号告诉别的线程,别的线程再进行某些动作。在等待某种条件时进入睡眠。
- 信号量(同步):使用线程的信号量可以高效地完成基于线程的资源计数
简单介绍和函数使用参考unp第二十六章或者[linux基础——linux线程间通信及同步机制总结(https://blog.csdn.net/a987073381/article/details/52029070)。