目录
一、什么是线程分离
1.1pthread_detach
1.2pthread线程库存在的意义
1.3__thread线程的局部存储
1.4系统调用clone
一、什么是线程分离
1.1pthread_detach
int pthread_detach(pthread_t thread);
pthread_detach(pthread_self());//对自己进行分离
而为什么要有线程分离或者是为什么要将线程进行分离呢?我们在运行或者打开一个软件或应用时,往往我们不去主动关闭,该线程一般是不会主动进行退出的,所以一般情况下主线程都是常驻的而在我们需要去进行其他操作时再由主线程去创建其他新线程,所以主线程一般都是死循环运行的。而一般情况下我们都希望主线程是最后一个退出的。
所以线程分离底层依旧属于同一个进程,只是不需要进行等待了。
1.2pthread线程库存在的意义
Linux中不存在真正的线程,线程底层都是用进程来进行模拟的,而之所以有线程是针对面向用户进行使用的,而Linux中的线程也被叫做用户级线程,所以在Linux中存在着一个线程库,库中则封装着由OS将线程转换成轻量级进程的具体方法。所以就要由OS提供一个库将线程进行封装,然后转换成用户习惯的使用方式为用户提供用户习惯的上层接口。
所以我们之前使用的pthread_t类型所表示的数字是什么呢?
我们可以在编写代码时,让代码拿到当前线程tid然后转换成16进制。
也可以通过指令去获取指定进程的LWP。
但是通过观察可以发现,我们获取的tid和LWP并不一样。
而OS在代码刚开始运行时,首先创建的是一个进程,包括task_struct 、mm_struct 、页表。然后将磁盘中存储的代码加载到内存中然后进行运行。而我们在代码中创建线程,但系统中并没有线程的概念,只有轻量级进程的概念,但是又需要让用户看到并对线程进行操作,所以线程的创建管理等工作是在库中进行实现的,库中对各种操作进行了封装处理。而库也是一个文件存在于OS中
而这个库就是一个动态库,而想要执行该库就需要先将其加载到内存中,然后将其映射到该进程的地址空间的共享区中,所以创建线程、等待线程、终止线程等等工作都是在库中进行实现的,所以库就要对用户创建的线程进行管理,而管理的方式一如既往是先描述,再组织。
而动态库就会如下图的方式一样被加载到进程的地址空间中,而线程也有自己的线程控制块TCB,而一般操作系统是不直接提供TCB数据结构的。
所以创建一个线程就创建一个tcb,而每个tcb的起始地址就叫做线程的tid,所以系统提供给用户的线程的地址就是tcb的起始地址,通过该地址就能找到线程的所有属性信息。而tcb中就封装了该线程的LWP等信息。而库中不仅维护该线程所独立拥有的栈等,还会维护相关的数据结构。而线程的栈结构和上下文数据都是自己私有的。主线程的栈就在如图所示的地方,一旦开辟新的线程就会创建和分配新的栈结构,所以每个线程都有独立的栈结构。虽然线程间的栈结构是独立的,但其指向的都是同一进程的同一地址空间,所以彼此之间还是能够相互访问的。
这个库是一个动态库,也被叫做共享库,共享库可以通过页表被映射到每一个进程地址空间当中,所以不仅仅是一个进程,整个系统的所有进程中的线程,都由该库进行统一管理。
1.3__thread线程的局部存储
假设定义了一个全局变量int gvl;
__thread int gvl;
则可以对gvl的全局属性做修改,让它变成每个线程所私有的
全局变量一般存在于进程地址空间的初始化数据区,是所有线程所共享的,也就是说一个线程对其进行修改,所有的线程都能看到。如果这时在定义全局变量之前加上__thread就可以让该全局变量变成每个线程所私有的,从而不会受其他线程的影响。而这就叫做线程的局部存储。在代码编译时OS会将该变量单独拆解出来,存放到每一个线程的局部存储当中。注意,局部存储只可以存储内置类型,不能存储自定义类型。
1.4系统调用clone
在代码执行过程中,pcb在执行线程相关的代码时,也需要找到线程所对应的栈的位置,clone就可以解决这个问题,而clone就是pthread_create的底层实现。