« 64位Athlon的神坛 | 主页面

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).

引用通告

此日记的引用通告 URL:
https://blog.codingnow.com/cgi-bin/mt/mt-tb.cgi/88

发表一个评论

(如果你此前从未在此 Blog 上发表过评论,则你的评论必须在 Blog 主人验证后才能显示,请你耐心等候。)