Linux2.6以前的线程
在Linux内核2.6出现之前进程是(最小)可调度的对象,当时的Linux不真正支持线程。Linux 2.4内核中不知道什么是“线程”,只有一个“task_struct”的数据结构,就是进程。
Linux内核有一个系统调用指令clone(),这个指令产生一个调用调用的进程的复件,而且这个复件与原进程使用同一地址空间。LinuxThreads计划使用这个系统调用来提供一个内核级的线程支持。但是这个解决方法与真正的POSIX标准有一些不兼容的地方,尤其是在信号处理、进程调度和进程间同步原语方面。
Native POSIX Thread Library
一个操作系统比较全面的支持线程是需要改内核的,怎么干改内核这个艰苦卓越的工作?Linux是开源、免费的,谁愿意来干这个活?
有两家公司参与了对LinuxThreads的改进(向他们致敬):IBM启动的NGTP(Next Generation POSIX Threads)项目,以及红帽Redhat公司的NPTL(Native POSIX Thread Library),IBM那个项目,在2003年因为种种原因放弃了,大家都转到NPTL这个项目来了。
因为Linux一开始就决定在进程的结构上支持线程,线程和进程都共用task_struct这个结构,所以这个设计一直延续了下来。所以说Linux下通过NPTL创建的线程是内核线程,他会在内核创建一个线程结构供处理器调度,也就是所谓的1:1模型。
用户级线程
举个例子,在收发网络包的时候,recv可以是阻塞的,这个时候如果没有网络回包,正在运行的进程会阻塞,然后这个正在运行的pthread 会从CPU上下来,调度其他的线程。
由于上面的问题,我们希望我们的程序不因为IO而终止运行,于是产生了用户级线程,我们在用户程序部分定义自己的线程(执行流),在线程阻塞之前主动切换。
对Linux来说,用户级的线程其实还是跑在pthread上面的,其中有两种模型比较出名:
M:1 : M个用户级线程跑在1个内核线程上,俗称 协程
M:N : M个用户级线程跑在N个内核级线程上,golang和baidu-rpc的做法
当然,这部分不是Linux做的,用户程序可以在内核线程的支持下实现