内核线程和用户线程
对于线程的实现机制来说,通常可以选择在内核内或者内核外实现,这两种方式的区别在于线程是在核内还是核外调度。核内调度更利于并发使用多处理器的资源,内核可以将同一个进程的不同线程调度到不同处理器上执行,当某个线程阻塞时,内核可以将处理器调度到同一个进程的另一个线程。而核外调度的上下文切换开销更低,因为线程的切换不用陷入内核态。
进程-线程模型
当内核既支持进程也支持线程时,就可以实现线程-进程的"多对多"模型,即一个进程的某个线程由核内调度,而同时它也可以作为用户级线程池的调度者,选择合适的用户级线程在其空间中运行。这样既可满足多处理机系统的需要,也可以最大限度的减小调度开销。
在内核外实现的线程又可以分为"一对一"、"多对一"两种模型,前者用一个内核进程对应一个线程,将线程调度等同于进程调度,交给内核完成,而后者则完全在核外实现多线程,调度也在用户态完成。后者就是前面提到的单纯的用户级线程模型的实现方式,显然,这种核外的线程调度器实际上只需要完成线程运行栈的切换,调度开销非常小,但同时因为内核信号都是以进程为单位的,因而无法定位到线程,所以这种实现方式不能用于多处理器系统。
linux的轻量级进程
linux内核只提供了轻量进程的支持,限制了更高效的线程模型的实现,但linux着重优化了进程的调度开销,一定程度上也弥补了这一缺陷。目前linux的线程机制都采用的线程-进程"一对一"模型,调度交给内核,而在用户级实现一个包括信号处理在内的线程管理机制。
linux内核在2.0.x版本就已经实现了轻量进程,应用程序可以通过一个统一的clone
系统调用接口,用不同的参数指定创建轻量进程还是普通进程。在内核中,clone
调用经过参数传递和解释后会调用do_fork
,这个核内函数同时也是fork
、vfork
系统调用的最终实现:
intdo_fork(unsignedlongclone_flags,unsignedlongstack_start,structpt_regs*regs,unsignedlongstack_size);
在do_fork
中,不同的clone_flags将导致不同的行为(共享不同的资源),下面列举几个flag的作用。
CLONE_VM 如果do_fork
时指定了CLONE_VM
开关,创建的轻量级进程的内存空间将会和父进程指向同一个地址,即创建的轻量级进程将与父进程共享内存地址空间。
CLONE_FS
如果do_fork
时