很显然,有一个错误在kernel的fork代码中,在线程引入到FreeBSD以前,程序一直以单线程的方式运行,但是现在情况变了,以前rfork有个标志参数,可以把当前进程的文件列表给清空,在多线程下是不安全的,如果不采取锁保护机制,很可能导致系统崩溃,下面的代码可以演示这个过程:
#include 
#include 
#include 
void *thr(void *arg)
{
        for (;;)
                close(open("./test.dat", O_RDWR|O_CREAT, 0666));
        return (NULL);
}
int main()
{
        pthread_t td;
        pthread_create(&td, NULL, thr, NULL);
        for (;;)
                rfork(RFCFDG);
        return (0);
}
主要原因是那里没有锁来保护这个指向文件列表的指针,而这个指针可以被rfork替换,内存被释放,如果其他的线程正在操作文件列表,可以导致崩溃。
一个quick fix就是以下的补丁:
Index: kern_fork.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/kern_fork.c,v
retrieving revision 1.257
diff -u -r1.257 kern_fork.c
--- kern_fork.c 8 Feb 2006 08:09:16 -0000   1.257
+++ kern_fork.c 15 Mar 2006 07:19:27 -0000
@@ -220,6 +220,16 @@
     * certain parts of a process from itself.
     */
    if ((flags & RFPROC) == 0) {
+       if ((p1->p_flag & P_HADTHREADS) &&
+           (flags & (RFCFDG | RFFDG))) {
+           PROC_LOCK(p1);
+           if (thread_single(SINGLE_BOUNDARY)) {
+               PROC_UNLOCK(p1);
+               return (ERESTART);
+           }
+           PROC_UNLOCK(p1);
+       }
+
        vm_forkproc(td, NULL, NULL, flags);
        /*
@@ -237,6 +247,13 @@
         */
        if (flags & RFFDG) 
            fdunshare(p1, td);
+
+       if ((p1->p_flag & P_HADTHREADS) &&
+           (flags & (RFCFDG | RFFDG))) {
+           PROC_LOCK(p1);
+           thread_single_end();
+           PROC_UNLOCK(p1);
+       }
        *procp = NULL;
        return (0);
    }
原理就是让所有别的线程停靠到用户-系统边界上,那里是个安全点,线程应当没有在那里做文件操作的,当前调用 rfork的线程可以安全的做文件列表操作,可以是彻底删除和重新创建。
有点降低性能,幸运的是,这条执行路径不太用,不是hot path, 慢点也无所谓。几乎只有非常古老的程序才用这个rfork(RFCFDG).