在多线程程序中fork出一个新进程,发现新的进程没法正常工做。由于:在使用fork时会将原来进程中的全部内存数据复制一份保存在子进程中。可是在拷贝的时候,可是线程是没法被拷贝的。若是在原来线程中加了锁,在使用的时候会形成死锁。能够将开线程的代码放在fork之后。也就是放在新的子进程中进行建立。promise
在多线程程序里,在”自身之外的线程存在的状态”下一使用fork的话,就可能引发各类各样的问题.比较典型的例子就是,fork出来的子进程可能会死锁.请不要,在不能把握问题的原委的状况下就在多线程程序里fork子进程.
在子进程的执行开始处调用doit()时,发生死锁的机率会很高.安全
void* doit(void*) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&mutex); struct timespec ts = {10, 0}; nanosleep(&ts, 0); // // 睡10秒 pthread_mutex_unlock(&mutex); return 0; } int main(void) { pthread_t t; pthread_create(&t, 0, doit, 0); // 作成并启动子线程 if (fork() == 0) { //子进程 //在子进程被建立的瞬间,父的子进程在执行nanosleep的场合比较多 doit(0); return 0; } pthread_join(t, 0); // 等待子线程结束 }
如下是说明死锁的理由:
通常的,fork作以下事情数据结构
在内存区域里,静态变量mutex的内存会被拷贝到子进程里.并且,父进程里即便存在多个线程,但它们也不会被继承到子进程里. fork的这两个特征就是形成死锁的缘由.多线程
以上程序执行流程异步
像这里的doit函数那样的,在多线程里由于fork而引发问题的函数,咱们把它叫 作”fork-unsafe函数”.反之,不能引发问题的函数叫作”fork-safe函数”.虽然在一些商用的UNIX里,源于OS提供的函数(系统调 用),在文档里有fork-safety的记载,可是在 Linux(glibc)里固然!不会被记载.即便在POSIX里也没有特别的规定,因此那些函数是fork-safe的,几乎不能判别.不明白的话,做 为unsafe考虑的话会比较好一点吧.(2004/9/12追记)Wolfram Gloger说过,调用异步信号安全函数是规格标准,因此试着调查了一下,在pthread_atforkの这个地方里有” In the meantime*5, only a short list of async-signal-safe library routines are promised to be available.”这样的话.好像就是这样.async
malloc函数就是一个维持自身固有mutex的典型例子,一般状况下它是fork-unsafe的.依赖于malloc函数的函数有不少,例如printf函数等,也是变成fork-unsafe的.函数
直目前为止,已经写上了thread+fork是危险的,可是有一个特例须要告诉你们.”fork后立刻调用exec的场合,是做为一个特列不会产生问题的”. 什么缘由呢..?exec函数*6一被调用,进程的”内存数据”就被临时重置成很是漂亮的状态.所以,即便在多线程状态的进程里,fork后不立刻调用一切危险的函数,只是调用exec函数的话,子进程将不会产生任何的误动做.可是,请注意这里使用的”立刻”这个词.即便exec前仅仅只是调用一回printf(“I’m child process”),也会有死锁的危险.
译者注:exec函数里指明的命令一被执行,该命令的内存映像就会覆盖父进程的内存空间.因此,父进程里的任何数据将不复存在.spa
本blog的理解:查看前面进程建立中,子进程在建立后,是写时复制的,也就是子进程刚建立时,与父进程同样的副本,当exce后,那么老的地址空间被丢弃,而被新的exec的命令的内存的印像覆盖了进程的内存空间,因此锁的状态可有可无了。
规避方法1:作fork的时候,在它以前让其余的线程彻底终止.
在fork以前,让其余的线程彻底终止的话,则不会引发问题.但这仅仅是可能的状况.还有,由于一些缘由而其余线程不能结束就执行了fork的时候,就会是产生出一些解析困难的不具合的问题.
规避方法2:fork后在子进程中立刻调用exec函数
不用使用规避方法1的时候,在fork后不调用任何函数(printf等)就立刻调用execl等,exec系列的函数.若是在程序里不使用”没有exec就fork”的话,这应该就是实际的规避方法吧.把本来子进程应该作的事情写成一个单独的程序,编译成可执行程序后由exec函数来调用.线程
规避方法3:”其余线程”中,不作fork-unsafe的处理
除了调用fork的线程,其余的全部线程不要作 fork-unsafe的处理.为了提升数值计算的速度而使用线程的场合*7,这多是fork- safe的处理,可是在通常的应用程序里则不是这样的.即便仅仅是把握了那些函数是fork-safe的,作起来还不是很容易的.fork-safe函 数,必须是异步信号安全函数,而他们都是能数的过来的.所以,malloc/new,printf这些函数是不能使用的.调试
规避方法4:使用pthread_atfork函数,在即将fork以前调用事先准备的回调函数.apue中详细介绍了它
使用pthread_atfork函数,在即将 fork以前调用事先准备的回调函数,在这个回调函数内,协商清除进程的内存数据.可是关于OS提供的函数 (例:malloc),在回调函数里没有清除它的方法.由于malloc里使用的数据结构在外部是看不见的.所以,pthread_atfork函数几乎 是没有什么实用价值的.
规避方法5:在多线程程序里,不使用fork
就是不使用fork的方法.即用pthread_create来代替fork.这跟规避策2同样都是比较实际的方法,值得推荐.
总结:在程序正常运行时出现不能正常工做,而在调试时又能正常工做。则能够考虑死锁的状况!