我在執行exec()後掛起到子套接字上的子進程存在問題。這個過程1)讀取udp數據包,2)殺死/啓動其他進程。該進程通過它們發送的udp數據包監視其他進程。文件描述符未在exec上關閉
這適用於Windows,Linux和AIX。我沒有在AIX上遇到任何問題,只在Linux上。 (在Windows代碼是顯著不同的,所以我不會去到有關細節。)
我設置對返回的描述符中的FD_CLOEXEC標誌通過的fcntl在創建後立即()。這必須在Red Hat EL 4-6上運行,因此在創建時使用O_CLOEXEC不是一種選擇(RHEL4/5中的內核不具備該選項)。
對於維護,監視過程可能需要重新啓動,當我嘗試重新啓動它時,我發現偶爾會有一個子進程仍然綁定到套接字,從而阻止監視進程這樣做。 [通常這不是問題(因爲用戶會看到重啓失敗並採取適當的行動),但是監視器本身是通過不同的機制進行監視的(以避免SPOF),並且監視過程的自動重啓可能會如果其子進程之一掛在套接字上,則會失敗。這可能會導致下游發生更多壞事。 ]
我已經添加了fork()和exec()調用之間的代碼,以顯式關閉子進程中的套接字(與關聯關閉),並將fork()和read )通過pthread_mutex,以便在發生分支時不從套接字讀取數據。
插座與
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
,並沒有其他的選項創建的。在創建之後,我立即調用fcntl來設置FD_CLOEXEC。該過程在這一點上仍然是單線程的,所以在設置標誌之前沒有競爭條件(理論上)。
接下來完成綁定,但仍然是單線程的。它綁定到與getaddrinfo返回的匹配「localhost」的第一個IPV4地址(可能不必要,但它使用底層實用函數來簡化綁定調用)。這應該是因爲FD_CLOEXEC必要的)是:
char retryClose = 1;
int eno = 0;
int retries = 20;
if (shutdown(s, SHUT_RDWR)) {
/* Failed to shutdown. Wait and try again */
my_sleep(3000); /* sleep using select(0,NULL,NULL,NULL, timeval) */
shutdown(socketno, SHUT_RDWR);
/* not much else can be done... */
}
while (retryClose && (close(s) == -1))
{
/* save error number */
eno = errno;
/* check specific error */
switch (eno) {
case (EIO) :
/* terminate loop if retries have expired; otherwise sleep for a while and try again */
if (--retries <= 0) {
retryClose = 0;
}
else {
my_sleep(50);
}
case (EINTR) :
break;
case (EBADF) :
default:
retryClose = FALSE;
break;
} /* switch (eno) */
}
所以,我設置FD_CLOEXEC標誌和EXEC()調用之前顯式關閉FD。
我錯過了什麼?有什麼我可以做,以確保兒童進程真的不掛在插座上?
你確定這是一個徘徊在插座上的子進程嗎?看到一些pre-fork代碼,特別是bind()會很有趣,但最好是一個完整的可編譯示例,表現出這種行爲。您也可以嘗試在重啓之前和期間使用'lsof -i udp'或'ss -apeu'檢查套接字狀態。 – thuovila
@thouvila netstat -anp確認它是一個子進程,與/ proc//fd/inodes引用/ proc/net/udp inode交叉引用端口(儘管這可能是netstat的任何操作)。殺死孩子的過程消除了這個問題。 –
's'和'socketno'是同一個插座嗎?你檢查'fcntl()'調用成功嗎?你是否明確地添加了FD_CLOEXEC標誌?代碼是什麼樣的?這裏真的沒有太多的東西可以繼續下去。我沒有遇到過這樣的問題,所以我不能完全從你的描述中猜出一個解決方案。你的應用程序代碼可能很複雜,因爲你提到了底層的效用函數等等。你真的應該試着把一段展現出這種行爲的代碼放到一起並展示給我們。 – thuovila