Linux 线程的前世今生
最近在重新翻阅《Unix环境高级编程》的时候,被书上的一段例程所困扰,那段代码是分别在主线程和子线程中使用 getpid() 函数打印进程标识符PID,书上告诉我们是不同的值,但是测试结果是主线程和子线程中打印出了相同的值。
在我的印象中《Linux内核设计与实现》这本书曾经谈到线程时如是说:从内核的角度来说,它并没有线程这个概念。Linux内核把所有的线程都当成进程来实现……在内核中,线程看起来就像是一个普通的进程(只是线程和其他一些进程共享某些资源,比如地址空间)。
《Unix环境高级编程》第二版著书时的测试内核是2.4.22,而《Linux内核设计与实现》这本书是针对2.6.34内核而言的(兼顾2.6.32),而我的内核是3.9.11,难道是内核发展过程中线程的实现发生了较大的变化?百度一番之后发现资料乱七八糟不成系统,索性翻阅诸多文档和网页,整理如下。如有偏差,烦请大家指正。
在 Linux 创建的初期,内核一直就没有实现“线程”这个东西。后来因为实际的需求,便逐步产生了LinuxThreads 这个项目,其主要的贡献者是Xavier Leroy。LinuxThreads项目使用了 clone() 这个系统调用对线程进行了模拟,按照《Linux内核设计与实现》的说法,调用 clone() 函数参数是clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0)
,即创建一个新的进程,同时让父子进程共享地址空间、文件系统资源、文件描述符、信号处理程序以及被阻断的信号等内容。也就是说,此时的所谓“线程”模型符合以上两本经典巨著的描述,即在内核看来,没有所谓的“线程”,我们所谓的“线程”其实在内核看来不过是和其他进程共享了一些资源的进程罢了。
通过以上的描述,我们可以得到以下结论:
- 此时的内核确实不区分进程与线程,内核没有“线程”这个意识。
- 在不同的“线程”内调用 getpid() 函数,打印的肯定是不同的值,因为它们在内核的进程链表中有不同的 task_struct 结构体来表示,有各自不同的进程标识符PID。