2013-01-06 54 views
1

我正在使用popen()創建管道,並且該進程正在調用第三方工具,在某些罕見情況下,我需要終止該工具。如何強制文件描述符關閉以便pclose()不會被阻塞?

::popen(thirdPartyCommand.c_str(), "w"); 

如果我只是拋出一個異常,並展開堆棧,我的開卷試圖調用函數,pclose()在第三方進程,其結果我不再需要。然而,pclose函數()永遠不會返回它,有以下堆棧跟蹤在CentOS 4:

#0 0xffffe410 in __kernel_vsyscall() 
#1 0x00807dc3 in __waitpid_nocancel() from /lib/libc.so.6 
#2 0x007d0abe in [email protected]@GLIBC_2.1() from /lib/libc.so.6 
#3 0x007daf38 in _IO_new_file_close_it() from /lib/libc.so.6 
#4 0x007cec6e in [email protected]@GLIBC_2.1() from /lib/libc.so.6 
#5 0x007d6cfd in [email protected]@GLIBC_2.1() from /lib/libc.so.6 

有沒有什麼辦法來強制調用函數,pclose()是調用它之前的成功,所以我可以編程方式避免這種情況我的進程的情況越來越多,等待pclose()成功時,它永遠不會因爲我已經停止向popen()ed進程提供輸入並希望拋棄它的工作而成功。

在試圖關閉它之前,我應該以某種方式將文件末尾寫入popen()ed文件描述符?

請注意,第三方軟件本身是分叉的。在這裏pclose函數()已經掛點,有四道工序,其中之一是解散:

USER  PID %CPU %MEM VSZ RSS TTY  STAT START TIME COMMAND 
abc  6870 0.0 0.0 8696 972 ?  S 04:39 0:00 sh -c /usr/local/bin/third_party /home/arg1 /home/arg2 2>&1 
abc  6871 0.0 0.0 10172 4296 ?  S 04:39 0:00 /usr/local/bin/third_party /home/arg1 /home/arg2 
abc  6874 99.8 0.0 10180 1604 ?  R 04:39 141:44 /usr/local/bin/third_party /home/arg1 /home/arg2 
abc  6875 0.0 0.0  0  0 ?  Z 04:39 0:00 [third_party] <defunct> 

回答

2

我看到這裏有兩個解決方案:

  • 整潔的一個:你fork()pipe()execve() (或當然在exec家族中的任何東西......)「手動」,那麼它將取決於你決定是否要讓你的孩子成爲殭屍。 (即不適用於wait()
  • 醜陋的一個:如果您確定在任何給定時間只有這個子進程運行,您可以使用sysctl()來檢查是否有以該名稱運行的進程在您致電pclose() ... yuk之前。

我強烈建議在這裏整潔的方式,或者你可以問問誰負責修復你的第三方工具無限循環哈哈。

祝你好運!

編輯:

對於你的第一個問題:我不知道。做一些研究如何通過名稱使用sysctl()找到過程應該告訴你你需要知道什麼,我自己從來沒有把它推到這麼遠。

爲了您的第二個和第三個問題popen()基本上是一個包裝fork() + pipe() + dup2() + execl()

fork()複製的過程中,execl()用一個新的替換複製過程圖像,pipe()處理進程間通信和dup2()用於重定向輸出...然後pclose()wait()用於複製的過程死亡,這就是我們來這裏的原因。

如果你想知道更多,你應該檢查this answer,我最近解釋瞭如何用標準IPC執行簡單的分叉。在這種情況下,由於必須使用dup2()將標準輸出重定向到管道,因此情況稍微複雜一些。

您還應該看看popen()/pclose()源代碼,因爲它們當然是開源代碼。

最後,這裏有一個簡單的例子,我不能使它比更加清晰:

int pipefd[2]; 

pipe(pipefd); 
if (fork() == 0) // I'm the child 
{ 
    close(pipefd[0]); // I'm not going to read from this pipe 
    dup2(pipefd[1], 1); // redirect standard output to the pipe 
    close(pipefd[1]); // it has been duplicated, close it as we don't need it anymore 
    execve()/execl()/execsomething()... // execute the program you want 
} 
else // I'm the parent 
{ 
    close(pipefd[1]); // I'm not going to write to this pipe 
    while (read(pipefd[0], &buf, 1) > 0) // read while EOF 
     write(1, &buf, 1); 
    close(pipefd[1]); // cleaning 
} 

和往常一樣,記得閱讀手冊頁,並檢查所有的返回值。

再次祝你好運!

+0

不能保證我是唯一運行這個子進程的人,但是,給孩子的參數會提供一個唯一的簽名,那足夠了嗎? – WilliamKF

+0

對於整潔的人,你是否建議用fork(),pipe()和execve()替換popen()/ pclose()? – WilliamKF

+0

有沒有一個例子可以指出整潔的人會如何近乎完成? – WilliamKF

相關問題