http://shaohui.me/archives/256
N久之前, 某两人为了linux的调度基本单元是进程还是线程争得面红耳赤, 当然, 且不论为了一个技术问题不顾体面其用心何在, 单纯从技术角度讲, 这是个挺好的问题.
单纯的下一个结论几乎总是错误的, 所谓的对与错也只不过是在特定的时段和特定的场景下符合规律的一种状态, 一旦时过境迁, 对的东西往往变成了错误的, 而错误的东西又会变成对的.所以我建议在这里不要使用"对"或是"错"来判定事务, 只能说是在当前的情况下, 从UUU角度看XXX更显得更"符合实际".
我们都知道linux是不断发展的.在早期版本中, linux的基本调度单元是Task, 只到现在, 依然是它.
在早期版本中, 一个Task对应着一个进程, 完全没有线程这个概念.
随着时间的发展, 线程的概念出现的, 但是linux并没有马上接受这一概念, 要知道, 线程是现代操作系统的特征, 向一个现有的操作系统内核引入线程是一件伤筋动骨的事情, 更何况在线程概念的早期, 受历史原因(UNIX)和硬件的限制(多核尚不是主流), 线程的地位尚不确定. 所以, linux并没有在内核中引入线程的概念.
但是, linux提供了一个新的系统调用clone, 通过该系统调用, 内核中的多个进程可以共享一些信息, 比如进程空间等. 注意, 此时linux内核的基础调度单元依然是Task, 而且在内核看来, 一个进程依然对应着一个Task.
有了clone系统调用, 人们在用户态就可以模拟出线程的行为. 但是, 内核并没有线程的概念, 他依然将所有的进程看着是独立的. 这个用户态模拟的线程现实是linux pthread lib.
虽然可以在一定程度上模仿出线程的行为, 但是那些只有在内核中才能实现的线程的行为, 在用户态是模拟不了的. 而且, 随着时间的发展, 内核技术的进步, 已经多核的普及, 促生了linux 2.6内核版本.
注意, linux kernel 2.6是一个飞跃性的版本, 大家都认同, 本次版本将linux从传统操作系统晋升为现代操作系统. 其中最重要的改进就是, 内核中引入了线程组的概念.
这里要分析一下, clone调用的引入, 使得两个进程可以共享一些资源, 但是内核依然将两个进程看做是独立的, 互不相关的. 它们拥有自己的独立进程id, 信号处理等等.
2.6 内核中引入了线程组的概念, 也就是说在clone调用时, 如果设置了适当的标志位(libc 的NPTL 库正确的实现了这一特性), 内核将把新创建的进程和原进程视为一个组, 这个组称之为"线程组", 组中的进程称之为"线程". 注意, 到了这一步, 内核的调度单元依然是Task, 在内核中, 每个Task对应着一个进程, 只不过, 这个进程有可能是一个单独的进程, 也有可能是一个"线程组"中的一个线程.
线程组中的所有线程(进程)共享同一个进程pid, 给线程组中的任何一个线程发送信号, 整个线程组中的线程都能收到信号.
综上所述, 可以得出以下一些结论:
1. 内核的调度单元是Task
2. 2.6以前的内核中没有线程的概念. 每个Task对应一个进程.
3. 2.6以后的内核中增加了线程组的概念, 每个Task对应一个进程, 或者是一个线程组中的一个线程. 所谓的线程, 只是几个相关进程中的一个.
进一步, 2.6的这种改法, 符合现代操作系统对于进程和线程的内涵. 因为在现代操作系统中:
进程: 提供计算资源.
线程: 进行计算.
从这个角度出发, 在2.6以后的linux中, 对于多线程程序, 我们可以看做是一个进程 + 多个线程. 对于单线程程序, 我们也可以看做是一个进程 + 一个线程. 在这样统一个看法中可以说一个Task对应一个线程, 进而得出"内核的调度单元是线程"的结论.
当然, 我们也可以不使用现代操作系统的观点, 这样的话, 对多线程程序, 我们可以看做是内核将多个进程组织起来形成的一个进程, 对于单线程程序, 我们就把它看做是一个进程, 进而也可以得出"内核的调度单元是进程"的结论.
其实真正重要的是, 系统是否给我们系统了符合线程行为定义的机制, 至于下面的实现是进程还是线程, 都不是我们关心的, 因为正是有了进程, 线程这样的抽象的概念, 我们才能够在更高的层面上认识问题, 更好的使用它们.
所以, 争论内核的调度单元是Task, 进程, 线程, 其实是混淆了看问题的角度, 时空, 以及无法区分抽象和现实而产生的, 当然, 单纯的技术问题也可以用来当着"指鹿为马", 但别人看来只是一场笑话.
附一篇很好的文章, 比较详细的描述介绍了linux下的线程机制.