在 Linux 中使用 fork() + execlp 和 boost::asio 时解决重用错误

Posted

技术标签:

【中文标题】在 Linux 中使用 fork() + execlp 和 boost::asio 时解决重用错误【英文标题】:address reuse error when using fork() + execlp with boost::asio in Linux 【发布时间】:2013-07-28 12:40:15 【问题描述】:

我有一个程序在 TCP 端口上侦听特定字符串并使用 execlp 调用启动应用程序。我正在做一个fork() 来在这个execlp 调用之前启动一个子进程。在此启动后,父进程再次开始在同一端口上侦听。我正在关闭子进程中的套接字。

我在boost::asio::tcp_socket 上编写了一个包装器,我在绑定套接字之前将addr_reuse 选项设置为true

现在我的问题是在 Linux 中启动应用程序几次后出现地址重用错误。在我的程序中,它不断尝试接受连接(或更准确地说,尝试安排接受boost::asio::io_service)直到绑定然后接受成功。所以我在这个循环中收到错误。

奇怪的是,如果我关闭(或杀死)已启动的可执行文件,此错误将停止出现,这意味着 bind 成功。我确信在启动的应用程序中没有在任何地方使用相同的端口。 我正在使用异步套接字操作。知道为什么我会收到此错误吗?

这是我在套接字上接受的方式:(在开始新的接受之前,我还在 boost::asio::tcp_socket(_tcpSocket) 共享指针上调用了 reset。)

boost::asio::ip::tcp::endpoint endPoint(boost::asio::ip::tcp::v4(), port);
_acceptor.reset ( new boost::asio::ip::tcp::acceptor( *_ioservice.get() ) );
_acceptor->open( endPoint.protocol() );
_acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));

boost::system::error_code ec;
_acceptor->bind(endPoint, ec);
if ( ec.value() != boost::system::errc::success )

    ec.clear();
    _acceptor->close(ec);
    close();
    return false;


ec.clear();
_acceptor->listen(boost::asio::socket_base::max_connections, ec);
if ( ec.value() != boost::system::errc::success )

    return false;

_acceptor->async_accept(*_tcpSocket,
                        boost::bind(&TCPSocket::_handleAsyncAccept,
                                    this,
                                    boost::asio::placeholders::error) );

这是我的分叉方式:

pid_t pid = fork();
switch (pid)

    case 0:
    
        /// close all sockets for child process. as it might cause addr reuse error in parent process
        _asyncNO->closeAll();
        std::string binary = "<binaryName>";
        std::string path = "<binaryPath>";
        if ( execlp( path.c_str(), binary.c_str(), controllerIP.c_str(), (char *)0 ) == -1 )
        
            LOG_ERROR("System call failed !!")
        
    
    break;

    default:

为简单起见,我删除了日志记录。

【问题讨论】:

Boost.Asio 需要收到fork() 的通知;否则,可能会发生未定义的行为。可能值得实现docuemtnation 中提到的fork() 通知,看看这是否能解决问题。 【参考方案1】:

正如@TannerSansbury 在 cmets 中所说,这很可能是因为 Boost.Asio 需要收到fork() 的通知:

Newer version of the documentation on forking with Boost.Asio.

相关部分转载于此:

Boost.Asio 支持使用fork() 系统调用的程序。如果程序在适当的时候调用io_service.notify_fork(),Boost.Asio 将重新创建任何内部文件描述符(例如用于唤醒反应器的“自管道技巧”描述符)。通知通常如下执行:

io_service_.notify_fork(boost::asio::io_service::fork_prepare);
if (fork() == 0)

  io_service_.notify_fork(boost::asio::io_service::fork_child);
  // ...

else

  io_service_.notify_fork(boost::asio::io_service::fork_parent);
  // ...

用户定义的服务也可以通过覆盖io_service::service::fork_service() 虚函数来实现分叉感知。

请注意,任何可通过 Boost.Asio 的公共 API 访问的文件描述符(例如,basic_socket&lt;&gt;posix::stream_descriptor 的底层描述符等)在分叉期间都不会更改。根据需要管理这些是程序的责任。

【讨论】:

以上是关于在 Linux 中使用 fork() + execlp 和 boost::asio 时解决重用错误的主要内容,如果未能解决你的问题,请参考以下文章

在传统的 Linux fork-exec 中使用 _exit() 和 exit() 有啥区别?

linux中fork, source和exec的区别

Linux下Fork与Exec使用

Linux下Fork与Exec使用

Linux fork/exec 到同一目录中的应用程序

为啥在 Linux 上 fork 或 exec 后 RLIMIT_STACK 丢失?