前面介绍了线程的基本概念,下面介绍一下线程在内核中的存在方式。
在OS中,存在着大量的线程,为了管理这些线程,我们必然需要用结构体对其进行描述,然后再组织起来管理。但是在linux中,实际上是不存在线程这个概念的。那么这个结构体是在哪里被管理的呢?
在linux中,实际上不存在线程,OS对底层的系统调用做了封装,打包成.so的动态库。每当我们需要执行可执行程序时,OS会将原生的线程库加载到物理内存当中,然后将物理内存中的数据经页表映射到共享区当中。这样,当我们在调用线程的相关接口时,就直接在地址空间内跳转即可。
实际上,这个结构体就在我们映射的动态库当中。而我们创建线程时的pthread_t其实就是动态库中struct_pthread的地址,如果我们将tid转成16进制,就可以发现。
每当我们创建一个线程时,线程库中就会增加一个类似的结构,所有的线程也就是被这个库所管理起来了。由于库会映射到不同的进程地址空间当中,即使我们在不同的进程中创建线程,线程库也能对这些线程进行管理。
其中struct pthread 中包含了线程的一些属性;线程的栈结构是独立的,哪怕不同线程访问同一个函数中的变量,这些变量也是被不同线程私有的。(如下图)
下面通过一个例子来说明线程的局部存储。
在不同的线程访问全局变量时,所有的线程都可以对该全局变量做修改,且访问的一定是同一全局变量,因为全局变量属于已初始化数据,被所有线程共享。这里我们对全局变量做一定修改,然后让不同线程对其进行访问。
在全局变量前加上一个__thread,然后我们的线程访问全局变量,就会发现这两个函数中的全局变量地址已经不同了,不同的线程中已经私有了一份变量。这种技术,就叫做线程的局部存储,这里__thread是一种g++编译选项。在编译时,全局变量的值会被拷贝到线程的局部存储区域并创建一个变量,然后当线程需要对这个全局变量进行操作时,只会对各自的局部存储区域内对各自的变量进行操作。需要注意的是,局部存储只能存储内置类型数据,其他的不行。