这种实现方式不能用于多处理器系统,而这个需求正变得越来越大,因此,在现实中,纯用户级线程
的实现,除算法研究目的以外,几乎已经消失了。
Linux 内核只提供了轻量进程的支持,限制了更高效的线程模型的实现,但 Linux 着重优化了进程
的调度开销,一定程度上也弥补了这一缺陷。目前最流行的线程机制 LinuxThreads 所采用的就是
线程-进程"一对一"模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。
Linux-LinuxThreads 的运行机制正是本文的描述重点。
?
二.Linux 2.4 内核中的轻量进程实现
最初的进程定义都包含程序、资源及其执行三部分,其中程序通常指代码,资源在操作系统层面上通
常包括内存资源、IO 资源、信号处理等部分,而程序的执行通常理解为执行上下文,包括对 cpu 的
占用,后来发展为线程。在线程概念出现以前,为了减小进程切换的开销,操作系统设计者逐渐修正
进程的概念,逐渐允许将进程所占有的资源从其主体剥离出来,允许某些进程共享一部分资源,例如
文件、信号,数据内存,甚至代码,这就发展出轻量进程的概念。Linux 内核在 2.0.x 版本就已经
实现了轻量进程,应用程序可以通过一个统一的 clone()系统调用接口,用不同的参数指定创建轻量
进程还是普通进程。在内核中,clone()调用经过参数传递和解释后会调用 do_fork(),这个核内
函数同时也是 fork()、vfork()系统调用的最终实现:
;
int do_fork(unsigned long clone_flags, unsigned long stack_start,
struct pt_regs *regs, unsigned long stack_size)
其中的 clone_flags 取自以下宏的"或"值:
;
#define CSIGNAL0x000000ff/* signal mask to be sent at exit */
#define CLONE_VM0x00000100/* set if VM shared between processes */
#define CLONE_FS0x00000200/* set if fs info shared between processes */
#define CLONE_FILES 0x00000400/* set if open files shared between
processes */
#define CLONE_SIGHAND0x00000800/* set if signal handlers and blocked
signals shared */
#define CLONE_PID0x00001000/* set if pid shared */
#define CLONE_PTRACE0x00002000/* set if we want to let tracing continue on
the child too */
#define CLONE_VFORK0x00004000/* set if the parent wants the child to wake
it up on mm_release */
#define CLONE_PARENT0x00008000/* set if we want to have the same parent as
the cloner */
#define CLONE_THREAD0x00010000/* Same thread group? */
#define CLONE_NEWNS0x00020000/* New namespace group? */
#define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD)
在 do_fork()中,不同的 clone_flags 将导致不同的行为,对于 LinuxThreads,它使用(
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)参数来调用 clone()创建"线程
",表示共享内存、共享文件系统访问计数、共享文件描述符表,以及共享信号处理方式。本节就针
对这几个参数,看看 Linux 内核是如何实现这些资源的共享的。
1. CLONE_VM