" /> xuyifeng: March 2006 归档

主页面

March 15, 2006

FreeBSD rfork和文件操作导致系统崩溃

很显然,有一个错误在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).

64位Athlon的神坛

前些日子,64位的PC被媒体炒翻天,把64位的PC说的神乎其神,首先支持64位的Athlon 64是很让人期待。本人有辛,也买了一个Athlon 64, 装上FreeBSD 64位版本,说实在的,我以前的机器比较慢,这次完全是为了买一台快的机器,那台PIII已经服务了4年了,太老了。装上系统后,make buildworld, 觉的挺快的,系统跑的很光滑,一直用着64位。但是那台PIII是双CPU的,很多东西的BUG,一个CPU的系统是很难测试出来的,但是那台机器编译实在太慢了,一个make buildworld, 一个半小时的时间,受不了。于是,就打算用那台Athlon 64装个32位版本,make buildworld, 然后双PIII的机器,就可以用NFS做系统升级。亲而易举,就搞定了。 偶尔有机会,听到国外的FreeBSD的用户说64位比32位慢,我想这是有可能的。我这一说,很可能会找来很多非议,但我不怕,我们这些人最讲真话就是技术方面的事情。从微观上来说,32位的程序,以前一个指针是32位的,4个字节,但是现在变成64位了,8个字节,而CPU cache一出厂就是固定的,不会因为你跑在64位的long模式,就会凭空多出一倍的cache,相反,同样的C程序编译成为32位和64位,对cache使用有了不同,原来跑的很好的32位程序完全有可能因为指针的大小大了一倍,许多数据结构的大小也可观地增加了。 如果任务繁重,有可能cache变的不够用,产生cache颠簸。64位的系统,CPU的cache要大才好,至少比32位的CPU要大些才好。