[英]Threads and fork: fgetc() blocks when reading from popen()-pipe
在多線程程序(在ARM上運行)中,我有
一個主線程 ,除其他外,該線程定期使用popen( "pidof -s prog" )
是否正在運行另一個程序。 我使用O_CLOEXEC
標志作為文件描述符,並檢查fgetc()
是否從管道接收到任何東西。 將文件描述符設置為“非阻塞”將導致不讀取任何內容,也無濟於事 。 與shell命令相同的pidof
命令執行得很好。
在另一個線程中 ,每當發生特定事件時,使用子進程中具有直接execl()
fork()
即可啟動rsync
操作。 父母使用信號處理程序來觀察孩子的狀態,並可以選擇在另一個特定事件中殺死孩子。 我是否使用rsync
或sleep
調用exec()
都沒有關系-結果是相同的。
問題在於,主線程中的fgetc()
阻塞,直到子進程終止。
我將盡早通過fork()
解決這個問題(在我開始撰寫的另一篇文章中 ,應用程序是單線程的)。
但無論如何:
我想了解是什么導致fgetc()
從管道讀取時阻塞。
到目前為止,我已經嘗試了一些方法:
system()
使用相同的rsync
調用不會引起任何問題 我看過一個system()
實現 ,可以看到在fork()
之前對信號進行了處理:
我需要SIGCHLD的信號處理程序,但是出於好奇,我試圖與上面的代碼相同(我將sigprocmask()
替換為pthread_sigmask()
)-沒有成功,行為保持不變。
我的BSP隨附的源代碼中找不到system()
任何實現。
錯誤修正和意外行為的解釋
確實,我已經錯過了相關的要點。 在使示例程序更適合原始代碼示例之后,我看到了信號處理程序(在測試程序中工作)的問題。 摘抄:
void MyClass::sig_handler(int sig) {
if( m_pid < 1 ) // not the child we're waiting for
return;
pid_t pid;
int wstatus;
while ((pid = waitpid( -1, &wstatus, WNOHANG )) != -1 ) {
// error: this returns 0 as long as any children are alive
// -> check for "> 0" to ignore active child processes
if( pid != m_pid )
return;
// handle stuff here...
}
}
我不得不替換以下行
while ((pid = waitpid( -1, &wstatus, WNOHANG )) != -1 )
與
while ((pid = waitpid( -1, &wstatus, WNOHANG )) > 0 )
因為該程序的其他線程fork()
子級(例如,帶有popen()
)。 如果這些終止,則也會調用信號處理程序(靜態類函數)。
我認為:
在調用fork()
的線程中,我使用具有默認值和重置值-1
的成員m_pid
。 它從fork()
獲取pid。 如果m_pid
為-1,則sig處理程序立即返回。
擋在程序popen()
其中fork()
S(可以是任何其它調用fork()
多個)。 因此,當popen()
返回時,將輸入SIGCHLD的信號處理程序。 對m_pid
的檢查在調用m_pid = fork()
通過。 waitpid()
不會返回-1,而是popen()
子代的pid,然后繼續檢查返回值= 0,直到所有子代都終止為止-我正在等待的子代仍然存在! 只有waitpid()
返回-1
,主線程才能繼續使用fgetc()
讀取。
waitpid
頁:
如果指定了WNOHANG,並且存在由pid指定的一個或多個孩子,但尚未更改狀態,則返回0。 錯誤時,返回-1
因為sig處理程序檢查m_pid != -1
,所以僅當我在MyClass中使用fork()
設置m_pid
時才出現問題。
這就是為什么使用system()
不會引起問題的原因。 該m_pid
未設置為一個值!= -1,如果如一個孩子因此SIG處理程序后立即返回popen()
版在主線程。
模仿 system()
調用失敗,因為我將m_pid
設置為fork()
,因此sig處理程序沒有立即返回。
我猜因為sig處理程序是一個static member function
,因此該處理程序會阻塞fork()
創建子進程的那個線程。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.