从 C++ 应用程序启动 Linux 服务时避免套接字继承
Avoid socket inheritance when starting Linux service from C++ application
我有一个 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)
我已经尝试过 system
、popen
和 fork
+ execv
或 execve
以及 null
环境。结果总是相同或更糟。
我最后的希望是 Linux setsid
命令,但它也没有用。
如有任何帮助,我们将不胜感激。
问候,
一月
如果您指的是套接字描述符本身被 exec 的子进程继承,并且这是不可取的,那么您可以在使用 socket(2)
创建套接字时传递 SOCK_CLOEXEC
以使确保在执行其他程序时关闭它们。 (顺便说一句,这不会关闭连接,因为您的程序仍然有对套接字的引用。)
如果你正在使用一些更高级别的库,那么检查是否有一些方法让它通过这个标志,或者做 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
可以避免一些竞争,并且更难忘记关闭描述符。
I have a Linux service (daemon) that has multiple-threads and uses boost io_service listening on a TCP socket. When I receive a certain message on that socket I want to start another service with e.g. /etc/init.d/corosync start
对于带有 systemd 的现代 Linux,您可能希望查看 socket activation。也就是说,您的主服务守护进程只会将数据报发送到 unix 套接字(该数据报可能包含您希望显式传递给其他服务的文件描述符)。如果尚未启动,systemd 将为您启动其他服务。
使用 systemd 的好处是您的服务不需要 运行 作为 root 来启动另一个服务,并且服务进程彼此完全隔离(没有任何继承当 fork
ing).
Boost.Asio supports the fork()
system call:
需要程序准备并通知分叉的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 的 public API 访问的任何文件描述符。例如,如果父进程有一个打开的 acceptor
,那么子进程需要在生成自己的子进程或替换进程映像之前显式调用 acceptor::close()
。分叉支持文档指出:
Note that any file descriptors accessible via Boost.Asio's public API (e.g. the descriptors underlying basic_socket<>
, posix::stream_descriptor
, etc.) are not altered during a fork. It is the program's responsibility to manage these as required.
- Boost.Asio 的 fork 支持对于多线程进程是不安全的。对于多线程进程,
fork()
states that the child may only invoke async-signal-safe operations between fork()
and one of the exec()
functions. io_service::notify_fork()
does not make this guarantee, and the current implementation 调用非异步信号安全操作。
尽管如此,我已经看到应用程序在多线程进程中使用 Boost.Asio 的 fork()
支持时没有观察到不良行为。但是,如果希望同时满足 fork()
和 Boost.Asio 的要求,那么一种解决方案是在守护进程仍然是单线程时 fork 守护进程。当父进程通过进程间通信告知子进程执行 fork()
和 exec()
时,子进程将保持单线程并执行 fork()
和 exec()
。 this 答案中建议并演示了此解决方案。
我有一个 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)
我已经尝试过 system
、popen
和 fork
+ execv
或 execve
以及 null
环境。结果总是相同或更糟。
我最后的希望是 Linux setsid
命令,但它也没有用。
如有任何帮助,我们将不胜感激。 问候, 一月
如果您指的是套接字描述符本身被 exec 的子进程继承,并且这是不可取的,那么您可以在使用 socket(2)
创建套接字时传递 SOCK_CLOEXEC
以使确保在执行其他程序时关闭它们。 (顺便说一句,这不会关闭连接,因为您的程序仍然有对套接字的引用。)
如果你正在使用一些更高级别的库,那么检查是否有一些方法让它通过这个标志,或者做 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
可以避免一些竞争,并且更难忘记关闭描述符。
I have a Linux service (daemon) that has multiple-threads and uses boost io_service listening on a TCP socket. When I receive a certain message on that socket I want to start another service with e.g. /etc/init.d/corosync start
对于带有 systemd 的现代 Linux,您可能希望查看 socket activation。也就是说,您的主服务守护进程只会将数据报发送到 unix 套接字(该数据报可能包含您希望显式传递给其他服务的文件描述符)。如果尚未启动,systemd 将为您启动其他服务。
使用 systemd 的好处是您的服务不需要 运行 作为 root 来启动另一个服务,并且服务进程彼此完全隔离(没有任何继承当 fork
ing).
Boost.Asio supports the fork()
system call:
需要程序准备并通知分叉的
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 的 public API 访问的任何文件描述符。例如,如果父进程有一个打开的
acceptor
,那么子进程需要在生成自己的子进程或替换进程映像之前显式调用acceptor::close()
。分叉支持文档指出:Note that any file descriptors accessible via Boost.Asio's public API (e.g. the descriptors underlying
basic_socket<>
,posix::stream_descriptor
, etc.) are not altered during a fork. It is the program's responsibility to manage these as required.- Boost.Asio 的 fork 支持对于多线程进程是不安全的。对于多线程进程,
fork()
states that the child may only invoke async-signal-safe operations betweenfork()
and one of theexec()
functions.io_service::notify_fork()
does not make this guarantee, and the current implementation 调用非异步信号安全操作。
尽管如此,我已经看到应用程序在多线程进程中使用 Boost.Asio 的 fork()
支持时没有观察到不良行为。但是,如果希望同时满足 fork()
和 Boost.Asio 的要求,那么一种解决方案是在守护进程仍然是单线程时 fork 守护进程。当父进程通过进程间通信告知子进程执行 fork()
和 exec()
时,子进程将保持单线程并执行 fork()
和 exec()
。 this 答案中建议并演示了此解决方案。