您的位置:首页 > 其它

fork产生僵死子进程的问题

2008-03-15 14:41 330 查看
之前写的一个程序,调用fork产生子进程,子进程进行实际工作,父进程则使用信号处理函数来接收子进程结束信号,然后调用waitpid,处理子进程的僵死进程。
按理说这样做,应该不会出现僵死进程了,但是,经过大量实际数据测试后,还是会出现僵死进程。
一直都找不到问题原因,最后使用ps命令得到父进程的pid,并用gdb调试器attach父进程,执行bt命令,发现堆栈情况如下
(gdb) bt
#0 0x000000355b30aecb in __lll_mutex_lock_wait () from /lib64/tls/libpthread.so.0
#1 0x0000000000483c26 in baselib::Simple_Log::print (this=0x2a9633fce8, format=0x2a96332c00 "") at simple_log.cpp:414
#2 0x0000000000484d82 in baselib::Simple_Log::LOCK (this=0x1ffc790) at ../inc/base/simple_log.h:231
#3 0x0000000000483c4e in baselib::Simple_Log::print (this=0x1ffc790, format=0x4925b0 "[%s](%d) %s FILE:[%s],LINE:[%d]%s")
at simple_log.cpp:418
#4 0x00000000004071c0 in baselib::LogInstance_Ex::operator() (this=0x7fbfffc520,
fmt=0x492550 "[sig_child] child process pid=%d exited") at ../inc/base/baselog.h:213
#5 0x0000000000405c79 in sig_child () at Worker.cpp:23
#6 <signal handler called>
#7 0x000000355a880546 in __offtime () from /lib64/tls/libc.so.6
#8 0x000000355a88275d in __tz_convert () from /lib64/tls/libc.so.6
#9 0x000000000048e2cd in _localtime64_r (now=0x0, _t=0x7fbfffcb48, p=0x7fbfffcb90) at date2038.cpp:279
#10 0x000000000048e3f5 in base_localtime64_r (_t=0x7fbfffcd68, p=0x7fbfffcb90) at date2038.cpp:311
#11 0x0000000000474485 in baselib::Date::GetLocalTm (this=0x7fbfffcd60, ptm=0x7fbfffcb90) at date.cpp:297
#12 0x0000000000475f2b in baselib::Date::GetMonth (this=0x7fbfffcd60) at date.cpp:822
#13 0x000000000048324d in baselib::Simple_Log_Policy::RollOver (this=0x1ffcbe8) at simple_log.cpp:179
#14 0x0000000000484bbb in baselib::Simple_Log::RollOver (this=0x1ffc790) at simple_log.cpp:790
#15 0x0000000000483c79 in baselib::Simple_Log::print (this=0x1ffc790, format=0x4925b0 "[%s](%d) %s FILE:[%s],LINE:[%d]%s")
at simple_log.cpp:427
#16 0x00000000004071c0 in baselib::LogInstance_Ex::operator() (this=0x7fbfffe100,
fmt=0x492808 "[CWorker::MainProcess] child process pid=%d exited") at ../inc/base/baselog.h:213
#17 0x0000000000406c25 in CWorker::MainProcess (this=0x5d1620) at Worker.cpp:189
#18 0x000000000042744e in CBaseApp::Run (this=0x5d1620) at ../../frame/baseframe/BaseApp.cpp:231
#19 0x000000000042a7eb in main (argc=5, argv=0x7fbffff7b8) at ../../frame/baseframe/BaseFrameMain.cpp:29

首先发现,顶层堆栈是__lll_mutex_lock_wait(),说明发生了死锁,而程序中只有在写日志时用到了锁。接下来的几层堆栈说明确实是在写日志时发生了死锁。
再往下看,发现有如下一层
#6 <signal handler called>
这说明有信号发生了,即收到了子进程结束的信号。再看该层下面的堆栈,这下找到原因了,原来是父进程需要维护子进程的数量,在信号处理函数中将子进程的数量减一,并且写一个日志,记录某某子进程已经退出。
这样,在父进程写日志时,使用Simple_Log::LOCK()对日志文件上锁完成,但还没有解锁之前,此时可能有子进程结束信号发生了,这时父进程中断正在写日志的操作,而转去执行信号处理函数,在信号处理函数中,再次调用Simple_Log::LOCK()对同一个日志文件锁上锁,由于该锁之前已经上锁,还未解锁,再次上锁会造成死锁。当其它子进程结束后,向父进程发送的子进程结束信号得不到处理,所以就会处在僵死状态。
找到原因后,修改父进程的信号处理函数,将写日志的操作去掉,再次执行,已没有出现僵死进程的现象,问题解决。

总结,当初在写该信号处理函数之前,本不想写日志操作的,因为觉得信号处理函数是中断正在执行的业务操作而去执行其它操作,如果太过复杂,执行时间较长的话,会影响正在执行的业务处理。但是,为了提高日志信息的可维护性,最后还是写了日志,没有考虑到这种错误情况会发生。现在想来,中断处理函数还是应该遵循以下几个原则为好
1、越简单越好
2、能越早返回越好
3、如果在中断处理函数中需要上锁,这种情况要特别注意
4、不要调用太多的其它函数,有可能这些函数也会阻塞
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: