目录

线程概念

线程控制


线程概念

1、进程的重新理解

进程:分配资源的基本单位,一提到进程,一定是给其分配资源,比如内存,比如CPU

之前说的进程是一个struct和代码和数据,这个是特殊情况;更加普适的情况是一个进程其实包含多个线程,每个线程去处理一部分代码。此时一个进程内部,多个线程指向同一块虚拟地址空间。

所有线程共享地址空间。

2、硬件:物理内存->磁盘的映射

通过page去管理物理内存的每个4KB大小的页框

3、页表肯定不是单张,不然就很大

4、虚拟地址:10+10+12

第一个10:找到对应的页表

第二个10:找到该页表内对应的4KB的页框

第三个10:找到该页框内对应的8字节的具体数据,也就是用12位记录页内偏移的地址

进程能拿到的虚拟地址空间越多,其得到的资源也越多

为什么需要虚拟地址,直接用物理地址不好吗?

答:我们希望操作系统提供给⽤⼾的空间必须是连续的,但是物理内存最好不要连续

5、线程深层次的理解

创建一个线程比创建一个进程代价小的多,因为创建线程的时候资源已经划分好了,只需要创建对应的pcb就行了。

线程更轻量化。

一般有几个CPU就创建几个线程。

6、线程周边

为啥线程切换更简单?只是因为不用保存CR3寄存器以及不用切换页表吗?这是一个原因,但更重要的是缓存命中,一旦切换进程,那么此时上下文也被切换,缓存中的内容全部要被替换,那么此时访问内存效率就会很低。但要是线程切换,不会全部替换缓存的内容,所以访存的开销不大。

这里的上下文意思是,当把内存一条数据拿到CPU,OS就会把这一条数据上下相关的数据都拿到cache缓存中,以便后续访问。所以一旦进程切换后,这个缓存中所有的上下文数据也都会被替换掉,导致不命中。

线程控制

1、一个进程,两个线程,其中一个崩了,那么整个进程就被终止了,所以线程健壮性不好

2、用户只知道线程,但是Linux只有创建微进程的接口,因此二者不一,那么直接在用户与系统之间实现一个软件层,也就是pthread库

C++11的多线程,本质也是封装了pthread库

3、pthread_create创建线程以及pthread_join等待线程

lwp不是线程id,线程有自己的线程id的,可用pthread_self方法获取当前线程的id

4、pthread_join

在main里等待的目标线程,如果异常了,包括main线程,所以,join异常,没有意义,看也看不到!join都是基于:线程健康跑完的情况,不需要处理异常信号,异常信号,是进程要处理的话题!

pthread_join:拿到的返回值,就是线程退出设定的返回值

5、给线程传递的参数和返回值,可以是任意类型(包括对象)

6、线程终止的问题:

(1)新线程里,进行return就是线程终止。(用的最多)

(2)线程不能exit()终止,因为exit是终止进程的!

(3)pthread_exit()

(4)pthread_cancel();在main里取消新线程,那么目标线程的退出码就是-1

7、注意:本质线程是进程的一部分

(1)不能再线程里出现异常

(2)不能再线程里用exit

8、如果主线程不想再关心新线程,而是当新线程结束时,让它自己释放?设置新线程为分离状态。

类似于进程里面,父进程提前把SIGCHLD信号的处理方式设为SIG_IGN(忽略)。内核会知道:“这个父进程不想管子进程的退出,我来自动回收它的资源”。此时子进程退出后,内核会直接清理它的资源,不会变成僵尸进程,父进程也就不用调用wait了。

技术层面:线程默认是需要被等待的,joinable,如果不想让主进程等待新线程,想让新线程结束后,自己退出,设置为分离状态(!joinable or detach)。

理解层面:线程分离,主分离新,新把自己分离。分离线程,依旧在进程的地址空间中,进程的所有资源,被分离的线程,依旧可以访问,可以操作。主不等待新线程。

主线程分离新线程,pthread_detach,分离后,不需要join,主线程不能再pthread_join新线程了,会出错,新线程会自动释放资源。

9、pthread_create

①在库里创建一个线程tcb

②内核底层也会用clone方法真正创建一个线程,具体执行的方法会映射到库里的方法

10、mmap

①把这段内存映射到进程的虚拟地址空间

②两个进程的虚拟地址空间都同时映射到同一个内存空间,也就是共享内存

③磁盘空间映射到物理内存,对于进程来说,就不用文件描述符访问文件了,直接用虚拟地址就可以访问物理内存里面的文件了

11、线程的局部存储,只有当前线程自己能访问

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐