【问题标题】:Share socket between unrelated processes like systemd在 systemd 等不相关的进程之间共享套接字
【发布时间】:2017-06-14 16:05:26
【问题描述】:

有多个问题和答案如何做,但两个过程必须配合

等等

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

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

编辑:

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

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

【问题讨论】:

    标签: linux sockets share file-descriptor systemd


    【解决方案1】:

    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
    }
    

    【讨论】:

    • 这对我来说很清楚,但我感兴趣的是在exec()期间传递文件描述符的过程,而不需要正在执行的进程的合作。
    • 感谢您的扩展回答。我必须稍微调查一下。对我来说,流程仍然无关紧要,但对我来说有两个重要的技巧。默认情况下,文件描述符在execve() 中保持打开状态。这对我来说是新的。第二个技巧是 dup2() 函数,它执行 fd 的最后一步。
    • 注意在open_sockets()中设置了close-on-exec标志,但是在dup2()之后,两个文件描述符不共享文件描述符标志(close-on-exec标志),所以fd_accepted 的 close-on-exec 标志已关闭。
    猜你喜欢
    • 2017-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 2014-01-15
    • 1970-01-01
    • 2016-08-08
    • 2012-08-08
    相关资源
    最近更新 更多