从C ++应用程序启动Linux服务时避免套接字继承

时间:2015-03-06 13:49:11

标签: c++ linux sockets boost boost-asio

我有一个Linux服务(守护程序),它有多个线程并使用boost io_service监听TCP套接字。当我在该套接字上收到某个消息时,我想要启动另一个服务,例如/etc/init.d/corosync start

问题是,在启动服务后,当我退出自己的服务时,其他服务从我自己的服务继承了套接字,并且它仍处于一种奇怪的状态,我无法以通常的方式停止它。

退出我的流程之前" MonitorSipServer" open socket显示如下:

netstat -anop |grep 144
tcp        0      0 0.0.0.0:20144           0.0.0.0:*               LISTEN          4480/MonitorSipServ off (0.00/0/0)
tcp        0      0 140.0.24.181:20144      140.0.101.75:47036      ESTABLISHED 4480/MonitorSipServ off (0.00/0/0)

退出我的流程" MonitorSipServer" open socket显示如下:

netstat -anop |grep 144
tcp        0      0 0.0.0.0:20144           0.0.0.0:*               LISTEN      4502/corosync    off (0.00/0/0)
tcp        0      0 140.0.24.181:20144      140.0.101.75:47036      ESTABLISHED 4502/corosync    off (0.00/0/0)

我已尝试使用systempopen以及fork + execvexecvenull环境。它总是相同的结果或更糟。 我最后的希望是Linux setsid命令,但它也没有用。

任何帮助将不胜感激。 问候, 扬

3 个答案:

答案 0 :(得分:5)

如果你指的是套接字描述符本身被exec的子进程继承,这是不可取的,那么你可以在用SOCK_CLOEXEC创建套接字时传递socket(2)确保在执行其他程序时关闭它们。 (顺便说一句,这不会关闭连接,因为你的程序仍然有对套接字的引用。)

如果您正在使用某个更高级别的库,那么检查是否有某种方法可以让它传递此标志,或者执行fcntl(sock_fd, F_SETFD, fcntl(sock_fd, F_GETFD) | FD_CLOEXEC)在描述符之后设置close-on-exec标志。创建,如果你能够达到它。 (fcntl(2)方法在多线程环境中可能很活泼,因为某些线程可以exec(3)在创建套接字的点和设置FD_CLOEXEC之间的程序。 )

如果以上方法不起作用,那么您可以在执行服务之前手动fork(2)然后close(2)套接字描述符。 SOCK_CLOEXEC的优点是只有在exec*()实际成功时才会关闭套接字,这有时可以更容易地从错误中恢复。此外,SOCK_CLOEXEC可以避免某些比赛,并且更难忘记关闭描述符。

答案 1 :(得分:3)

Boost.Asio supports the fork() system call

  • 它要求程序准备并通过io_service::notify_fork()通知叉子的io_service

    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);
      ...
    }
    

    未能使用此模式会导致未指定的行为。对于某些配置,父级将无法接收事件通知,因为它们由孩子使用。

  • 程序负责处理可通过Boost.Asio的公共API访问的任何文件描述符。例如,如果父级具有打开的acceptor,则子级需要在生成其自己的子进程或替换进程映像之前显式调用acceptor::close()。 fork支持文档说明:

      

    请注意,通过Boost.Asio的公共API(例如basic_socket<>posix::stream_descriptor下面的描述符等)可访问的任何文件描述符都不会在fork中更改。该计划有责任根据需要进行管理。

  • Boost.Asio的fork支持对于多线程进程并不安全。对于多线程进程,fork()指出子进程可能仅在fork()exec()函数之一中调用异步信号安全操作。 io_service::notify_fork()不提供此保证,当前implementation会调用非异步信号安全操作。

话虽如此,我看到在多线程进程中使用Boost.Asio的fork()支持时,应用程序没有观察到不良行为。但是,如果一个人希望满足fork()和Boost.Asio的要求,那么一个解决方案是在守护进程仍处于单线程时进行派生。子进程将保持单线程,并在父进程通过进程间通信告知它时执行fork()exec()。在this回答中建议并演示了此解决方案。

答案 2 :(得分:0)

  

我有一个Linux服务(守护程序),它有多个线程并使用boost io_service监听TCP套接字。当我在该套接字上收到某个消息时,我想要启动另一个服务,例如/etc/init.d/corosync start

对于使用systemd的现代Linux,您可能希望查看socket activation。也就是说,您的主服务守护程序只会将数据报发送到unix套接字(该数据报可能包含您希望显式传递给其他服务的文件描述符)。如果还没有开始,systemd会为你启动其他服务。

使用systemd的好处是您的服务不需要以root身份运行,以便能够启动另一个服务,并且服务进程彼此完全隔离(没有像fork那样的任何继承荷兰国际集团)。