类似于fork()和vfork(),Linux特有的系统调用clone()也能创建一个新线程。与前两者不同的是,后者在进程创建期间对步骤的控制更为准确。其主要用于线程库的实现。其函数原型如下:
#define _GNU_SOURCE
#include <sched.h>
int clone(int (*func)(void*),void *child_stack,int flags,void *func_arg,....
/*pid_t *ptid,struct user_desc *tls,pid_t *ctid*/);
Return process ID of child on success,or -1 on error
如同fork(),由clone()创建的新进程几近于父进程的翻版。
但是与fork()不同的是,克隆生成的子进程继续运行时不以调用处为起点,转而去调用以参数func所指定的函数,func又称为子函数。调用子函数时的参数由func_arg指定。经过转换,子函数可对改参数的含义自由解读,例如可以作为整型值(int),也可以视为指向结构的指针。
当函数func返回或者是调用exit()(或者_exit())之后,克隆产生的子进程就会终止。照例,父进程可以通过wait()一类函数来等待克隆子进程。
因为克隆产生的子进程可能共享父进程内存,所以它不能使用父进程的栈。相反,调用者必须分配一块大小适中的内存空间供子进程的栈使用,同时将这块内存的指针置于参数child_stack中。
参数flags服务于双重目的。首先,其低字节中存放着子进程的终止信号,子进程退出时其父进程将收到这一信号。(如果克隆产生的子进程因信号而终止,父进程依然会收到SIGCHLD信号)该字节也可能为0,这时将不会产生任何信号。
clone()函数中的flags参数是各位掩码的组合。其参数如下:
- 共享文件描述符:CLONE_FILES
如果指定了该标志,父子进程会共享同一个打开文件描述符表。也就是说,无论哪个进程对文件描述符的分配与释放都会影响宁一个进程。
- 共享与文件系统相关的信息:CLONE_FS
如果指定了该标志,那么父子进程将共享与文件系统相关的信息:权限掩码、根目录以及当前工作目录。也就是说无论在哪个进程中调用umask()、chdir()或者chroot(),都将影响到另一个进程。
- 共享对信号的处置设置:CLONE_SIGHAND
如果设置了该标志,那么父子进程将共享同一信号处置表。无论在哪个进程中调用sigaction()或者signal()来改变对信号处置的设置,都会影响其他进程对信号的处置。
- 共享父进程的虚拟内存:CLONE_VM
如果设置了该标志,父子进程将会共享同一份虚拟内存页。无论哪一个进程更新了内存,或是调用了mmap()、munmap(),另一进程同样会观察到变化。
- 线程组:CLONE_THREAD
若设置了该标志,则会将子进程置于父进程的线程组中。如果未设置该标志,那么会将子进程置于新的线程组中。
- 线程库支持:CLONE_PARENT_SETTID、CLONE_CHILD_SETTID和CLONE_CHILD_CLEARTID
为实现POSIX线程,Linux2.6提供了对CLONE_PARENT_SETTID、CLONE_CHILD_SETTID和CLONE_CHILD_CLEARTID的支持。这些标志将会影响clone()对参数ptid、ctid的处理。如果设置了CLONE_PARENT_SETTID,内核会将子进程的线程ID写入ptid所指向的位置。如果设置了CLONE_CHILD_SETTID,那么clone()会将子线程的线程ID写入指针ctid所指向的位置。如果设置了CLONE_CHILD_CLEARTID,则会在子进程终止时将ctid所指向的内存清零。
- 线程本地存储:CLONE_SETTLS
如果设置了该标志,那么参数tls所指向的user_desc结构会对线程所使用的线程本地存储缓冲区加以描述。
- 共享systemV信号量的撤销值:CLONE_SYSVSEM
如果设置了该标志,父子进程会将共享同一个SystemV信号量撤销值列表。
- 每进程挂载命名空间:CLONE_NEWNS
- 将子进程的父进程置为调用者的父进程:CLONE_PARENT
默认情况下,当调用clone()创建新进程时,新进程的父进程就用clone()进程。如果设置该标志,那么调用者的父进程就成为子进程的父进程。
- 进程跟踪:CLONE_PTRACE和CLONE_UNTRACED
如果设置了CLONE_PTRACE且正在跟踪子进程,那么也会对子进程进行跟踪。从Linux2.6起,即可设置CLONE_UNTRACED标志,这也意味着跟踪进程不能强制其子进程设置为CLONE_PTRACE
- 挂起父进程直至子进程退出或者调用exec():CLONE_VFORK
如果设置了该标识,父进程将一直挂起,直至子进程调用exec()或者_exit()来释放虚拟内存资源为止。