1. 如何理解 Linux 中的线程?
线程:是进程内的一个执行分支。线程的执行粒度要比进程细。
2. 重新定义线程和进程
问题一:什么叫做线程?
我们认为,线程是操作系统调度的基本单位!重新理解进程:内核观点:进程是承担分配系统资源的基本实体。(进程 == 所有的内核数据结构 + 所有的代码和数据)
问题二:执行流是资源吗?
线程是进程内部的执行流资源!!!
问题三:如何理解我们以前说过的进程?
操作系统以进程为单位,给我们的进程分配资源,我们的进程内部,当前只有一个执行流,以前我们所提到的进程是一个特殊情况,只有一个执行流,现在说的一个进程有多个执行流才是正常!!!
其中既然线程是一种资源,那么线程就要被管理起来------先描述,再组织!!!Struct TCB(Thread Control Block)在大多数操作系统中,都有对应的 PCB 结构体(很复杂,但又不得不这样做,还不好维护)。而在 Linux 中,线程直接复用进程数据结构和管理算法,struct task_struct—模拟进程。Linux 没有真正意义上的线程,而是用进程的内核数据结构模拟的线程
CPU 无法识别 Linux 中的进程和线程。线程<=执行流<=进程。Linux 中的执行流:轻量型进程
3. 重谈地址空间—多线程
如图:
CPU 中有个 CR3 寄存器,其中存的就是页表的页目录地址,也有寄存器存储的是进程控制块的地址
问题一:虚拟地址是如何转换到物理地址的?(32 位虚拟地址为例)
其中 32 位虚拟地址并非一个整体,而是分为了三个部分 32=10+10+12。而页表也不是一张表那么简单!!!
32 = 10 + 10 + 12,通过前 20 位,就已经知道页框的起始位置了。
页框的起始位置 + 虚拟地址最后 12 位(你要访问的物理内存在页框中的偏移量) = 物理地址中的数据
所以创建一个进程依旧是一个很重的工作。
所以至此以后,我们知道了虚拟地址是如何映射物理地址的,那么访问任何变量都是起始地址 + 类型 == 页框的起始地址 + 偏移量。
问题一:如何理解资源分配给线程?
线程目前分配资源,本质就是分配地址空间的范围。
4. Linux 线程周边的概念
线程 vs 进程
问题一:为什么线程比进程要更加轻量化?
1. 创建和释放更加轻量化(生死) 2. 切换更加轻量化(运行) 所以线程的整个生命周期都比进程要更加轻量化
4.1 线程的优点
1. 创建一个新线程的代价要比创建一个新进程小得多。 2. 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。 3. 线程占用的资源要比进程少很多。 4. 能充分利用多处理器的可并行数量。 5. 在等待慢速 I/O 操作结束的同时,程序可执行其他的计算任务。 6. 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。 7. I/O 密集型应用,为了提高性能,将 I/O 操作重叠。线程可以同时等待不同的 I/O 操作。
4.2 线程的缺点
1. 性能损失
一个很少被外部事件阻塞的计算密集型线程往往无法与其它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。
2. 健壮性降低
编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
3. 缺乏访问控制
进程是访问控制的基本粒度,在一个线程中调用某些 OS 函数会对整个进程造成影响。
4. 编程难度提高
编写与调试一个多线程程序比单线程程序困难得多
4.3 线程异常
1. 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃 2. 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出
4.4 线程用途
1. 合理的使用多线程,能提高 CPU 密集型程序的执行效率 2. 合理的使用多线程,能提高 IO 密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)
线程共享进程数据,但也拥有自己的一部分数据:
1. 线程的上下文 2. 单独栈
4.5 线程库中的接口使用
问题一:内核中有没有很明确的线程的概念呢?
没有,只有一个轻量级进程的概念,所以操作系统是不会给我们提供线程的系统调用,只会给我们提供轻量级进程的系统调用!!!
pthread 线程库 — 应用层 —对轻量级进程接口进行封装,为用户提供直接线程的接口。
pthread:
1. 几乎所有的 Linux 平台,都是默认自带这个库的!!! 2. Linux 中编写多进程代码需要使用第三方 pthread 库!!!
pthread_create( ):
第一个参数:输出型参数,thread id 第二个参数:线程的属性,平时置 nullptr 就行。 第三个参数:是一个返回值为 void ,参数为 void的一个函数指针,其中 void*可以接收或返回任意类型。 第四个参数:创建线程成功,新线程回调线程函数的时候,需要的参数。
返回值,成功返回 0,失败则返回错误码,不会设置全局变量的错误码!!!
**要注意的是,pthread_create 不是系统调用。而线程库是一个动态库,所以要想使用该函数就要引入该动态库!!!```bash g++ -o $@ $^ -lpthread
> **ps -aL 选项就能看到轻量级进程的信息了,-a 选项是显示全部。**
> **其中 LWP 是轻量型进程(light weight process)**
> **LWP == PID 就是主线程,进程内的任意一个线程被杀掉,默认该进程也会被杀掉。**


