linux - 在不相关的进程之间共享套接字,例如 systemd

标签 linux sockets share file-descriptor systemd

如何做,有多个问答,但两个进程必须配合

等等

systemd 中,有一个特性 socket activation ,您只是在没有任何合作的情况下在您的过程中打开并准备了文件描述符。您可以只使用文件描述符 3 (SD_LISTEN_FDS_START) 并且它已被 systemd 激活套接字。

systemd 是如何做到这一点的?我找不到任何相关的源代码。

编辑:

我知道如何编写 systemd 套接字激活服务,但我对从 systemd 的角度将文件描述符传递到我的服务的过程很感兴趣。

例如如果我想编写自己的套接字激活器,它的行为与 systemd 完全相同。

最佳答案

systemd 与共享套接字的进程并非无关systemd 启动并监督整个系统,因此它可以在 exec() 期间轻松传递套接字文件描述符。 systemd 代表服务进行监听,只要有连接进来,就会生成相应服务的实例。 Here是实现:

int main(int argc, char **argv, char **envp) {
        int r, n;
        int epoll_fd = -1; 

        log_parse_environment();
        log_open();

        r = parse_argv(argc, argv);
        if (r <= 0)
                return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;

        r = install_chld_handler();
        if (r < 0)
                return EXIT_FAILURE;

        n = open_sockets(&epoll_fd, arg_accept);
        if (n < 0)
                return EXIT_FAILURE;
        if (n == 0) {
                log_error("No sockets to listen on specified or passed in.");
                return EXIT_FAILURE;
        }

        for (;;) {
                struct epoll_event event;

                r = epoll_wait(epoll_fd, &event, 1, -1);
                if (r < 0) {
                        if (errno == EINTR)
                                continue;

                        log_error_errno(errno, "epoll_wait() failed: %m");
                        return EXIT_FAILURE;
                }

                log_info("Communication attempt on fd %i.", event.data.fd);
                if (arg_accept) {
                        r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
                        if (r < 0)
                                return EXIT_FAILURE;
                } else
                        break;
        }
        ...
}

一旦连接进来,它会调用do_accept():

static int do_accept(const char* name, char **argv, char **envp, int fd) {
        _cleanup_free_ char *local = NULL, *peer = NULL;
        _cleanup_close_ int fd_accepted = -1; 

        fd_accepted = accept4(fd, NULL, NULL, 0); 
        if (fd_accepted < 0)
                return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);

        getsockname_pretty(fd_accepted, &local);
        getpeername_pretty(fd_accepted, true, &peer);
        log_info("Connection from %s to %s", strna(peer), strna(local));

        return fork_and_exec_process(name, argv, envp, fd_accepted);
}

最后,it调用 execvpe(name, argv, envp); 并将 fd 包装在 envp 中。其中有一个技巧,如果fd_accepted不等于SD_LISTEN_FDS_START,它会调用dup2()。使 SD_LISTEN_FDS_START 成为 fd_accepted 的副本:

    if (start_fd != SD_LISTEN_FDS_START) {
            assert(n_fds == 1);

            r = dup2(start_fd, SD_LISTEN_FDS_START);
            if (r < 0)
                    return log_error_errno(errno, "Failed to dup connection: %m");

            safe_close(start_fd);
            start_fd = SD_LISTEN_FDS_START;
    }

因此您可以在您的应用程序中像这样使用文件描述符 3,sd_listen_fds 将解析从 envp 传递的环境变量 LISTEN_FDS:

int listen_sock;
int fd_count = sd_listen_fds(0);
if (fd_count == 1) { // assume one socket only
  listen_sock = SD_LISTEN_FDS_START; // SD_LISTEN_FDS_START is a macro defined to 3
} else {
  // error
}
struct sockaddr addr;
socklen_t addrlen;
while (int client_sock = accept(listen_sock, &addr, &addrlen)) {
  // do something
}

关于linux - 在不相关的进程之间共享套接字,例如 systemd,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41919984/

相关文章:

python - ThreadingTCPServer 与 ThreadingMixIn HTTPServer

android - 如何将我的 Android 应用程序添加到共享对话框?

workflow - 如何在 Alfresco Share 中自定义工作流程详细信息页面?

facebook-graph-api - 获取哪些用户分享了帖子

linux - 在 linux 中获取 % 之前的数字的命令

linux - Linux内核如何确定ld.so的加载地址?

c - 如何在程序运行时更改 GTK 中的标签;从一个单独的线程

linux - 如何打印特定行和列中的数字

java - 套接字编程、多线程编程。错误 : IOException: java.net.ConnectException:连接超时:连接

c++ - 如何正确关闭用 fdopen 打开的套接字?