我有一个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'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 时那样继承任何内容(。