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



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

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

在退出我的进程"MonitorSipServer"之前,打开的套接字如下所示:

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"后,打开的套接字如下所示:

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)

我已经尝试过systempopenfork + execvexecve null环境。结果总是相同或更糟。我最后的希望是 Linux setsid 命令,但它也没有奏效。

任何帮助将不胜感激。问候一月

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

如果您使用的是某个更高级别的库,请检查是否有某种方法可以让它传递此标志,或者fcntl(sock_fd, F_SETFD, fcntl(sock_fd, F_GETFD) | FD_CLOEXEC)创建描述符后在描述符上设置 close-on-exec 标志(如果可以访问它(。(不过,fcntl(2)方法在多线程环境中可能很麻烦,因为某些线程可能会在创建套接字的点和在其上设置FD_CLOEXEC之间exec(3)程序。

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

Boost.Asio 支持fork()系统调用:

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

    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()。分叉支持文档指出:

    请注意,任何可通过 Boost.Asio 的公共 API 访问的文件描述符(例如底层的描述符basic_socket<>posix::stream_descriptor等(在分叉期间不会更改。计划有责任根据需要管理这些内容。

  • Boost.Asio的分叉支持对于多线程进程来说是不安全的。 对于多线程进程,fork() 声明子进程只能在fork()和其中一个exec()函数之间调用异步信号安全操作。 io_service::notify_fork()不做此保证,当前实现调用非异步信号安全操作。

话虽如此,我已经看到应用程序在多线程进程中使用 Boost.Asio 的 fork() 支持时不会观察到不良行为。 但是,如果希望同时满足fork()和Boost.Asio的要求,那么一种解决方案是在守护进程仍然是单线程时分叉它。 子进程将保持单线程,并在父进程通过进程间通信告知它这样做时执行fork()exec()。 此答案中建议并演示了此解决方案。

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

对于带有systemd的现代Linux,您可能希望研究套接字激活。也就是说,您的主服务守护程序只会将数据报发送到 unix 套接字(该数据报可能包含您希望显式传递给其他服务的文件描述符(。如果尚未启动,systemd 将为您启动其他服务。

使用 systemd 的好处是,您的服务不需要以 root 身份运行即可启动另一个服务,并且服务进程彼此完全隔离(不像 fork ing 时那样继承任何内容(。

最新更新