【发布时间】:2014-04-06 14:07:15
【问题描述】:
在 exec() 之后,子进程挂在套接字上时遇到问题。该进程 1) 读取 udp 数据包,2) 杀死/启动其他进程。此进程通过它们发送的 udp 数据包监视其他进程。
这在 Windows、Linux 和 AIX 上运行。我在 AIX 上没有遇到任何问题,只有在 Linux 上。 (Windows 代码有很大不同,所以我不会详细说明。)
我在通过 fcntl() 创建返回的描述符后立即在返回的描述符上设置 FD_CLOEXEC 标志。这必须在 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 地址(可能没有必要,但它使用底层实用函数来简化对绑定的调用。)
fork 之后子进程中的关闭逻辑(由于 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检查套接字状态。 -
@thouvila netstat -anp 确认它是一个子进程,与 /proc/
/fd/ inode 交叉引用端口的 /proc/net/udp inode 一样(尽管无论如何,这可能是 netstat 所做的。)杀死子进程也可以解决问题。 -
s和socketno是同一个套接字吗?您检查fcntl()调用是否成功?您是否显式添加了 FD_CLOEXEC 标志?代码是什么样的?这里真的没什么可做的。我没有遇到过这样的问题,所以我无法仅从您的描述中猜测解决方案。您的应用程序代码可能很复杂,因为您提到了底层实用功能等。也许某些实用功能做了一些不可移植的事情。您真的应该尝试编写一段展示这种行为的代码并将其展示给我们。