繁体   English   中英

linux:fork / socketpair / close和多个子进程

[英]linux: fork/socketpair/close and multiple child process

现在,我尝试理解子进程的stdin / out / err的派生/重新绑定,并正确地管理资源( 文件句柄,套接字 ),而不会泄漏任何资源。

剩下一些问题:创建套接字对和派生后,在父5个文件描述符和子( stdin / out / err / socket1 / socket2 )中。 在子进程中,我需要关闭套接字对的“父”侧。 在fork和dup()套接字的“客户端”之后,我关闭() stdin / out / err三次。 dup()之后 ,是否需要关闭dup的“源”? 我想是的...但是我是对的吗?

当我以这种方式创建(参见下文)第二个孩子时,资源处理正确吗? 我试图严重依赖RAII来不泄漏任何fds,但这是对的吗? 我想念一件大事吗?

再见,谢谢!

格奥尔格

编辑:我修复了rebind_and_exec_child中的错误。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <memory>
#include <cassert>

// this handle takes a fd, behaves like an int and makes sure the fd is closed again
class fdhandle {
public:
    explicit fdhandle(int fd) {
        mp_fd = std::shared_ptr<int>(new int, [=](int* pfd) {
            close(*pfd);
            delete pfd;
        });
        assert(mp_fd);
        *mp_fd = fd;
    }
    operator int() {
        assert(mp_fd);
        return *mp_fd;
    }
private:
    std::shared_ptr<int>    mp_fd;
};

void rebind_and_exec_child(fdhandle fd, std::string exe) {
    // now close the std fds and connect them to the given fd
    close(0);   close(1);   close(2);

    // dup the fd three times and recreate stdin/stdout/stderr with fd as the target
    if (dup(fd) != 0 || dup(fd) != 1 || dup(fd) != 2) {
        perror("error duplicating socket for stdin/stdout/stderr");
        exit(EXIT_FAILURE);
    }

    // now we can exec the new sub process and talk to it through 
    // stdin/stdout/stderr
    char *arguments[4] = { exe.c_str(), exe.c_str(), "/usr/bin", NULL };
    execv(exe.c_str(), arguments);

    // this could should never be reached
    perror("error: executing the binary");
    exit(EXIT_FAILURE);
}

fdhandle fork_connected_child(std::string exe) {
    // create the socketpair
    int fd[2];
    if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) {
        perror("error, could not create socket pair");
        exit(EXIT_FAILURE);
    }
    fdhandle fdparent(fd[0]);   fdhandle fdchild(fd[1]);

    // now create the child
    pid_t pid = fork();
    switch (pid) {
    case -1:    // could not fork
        perror("error forking the child");
        exit(EXIT_FAILURE);
        break;

    case 0: // child
        rebind_and_exec_child(fdchild);
        break;

    default:    // parent
        return fdparent;
        break;
    }       
}

int main(int argc, const char** argv) {
    // create 2 childs
    fdhandle fdparent1 = fork_connected_child("/bin/ls");
    fdhandle fdparent2 = fork_connected_child("/bin/ls");   
}

我想,我找到了解决方案。 对于在socketpair()调用中创建的每个套接字,我都设置FD_CLOEXEC 这样,我可以确定内核会关闭所有文件描述符。 我的代码处理的所有其他套接字将通过fdhandle类对close()调用来close() 重新绑定stdin / stdout / stderr后,我将dup()替换为dup2()因为它确实关闭并自动完成了dup。

提示是此页面: http : //pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html

在调用过程映像中打开的文件描述符应在新的过程映像中保持打开状态,除非已设置执行关闭标志FD_CLOEXEC 对于那些保持打开状态的文件描述符,打开文件描述的所有属性均保持不变。 对于任何由于这个原因而关闭的文件描述符,如close()所述,由于关闭而删除文件锁。 通过关闭文件描述符未删除的锁保持不变。

现在是我调整后的代码:

编辑:调整后的结构

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <memory>
#include <cassert>
#include <iostream>

// this handle takes a fd, behaves like an int and makes sure the fd is closed again
class fdhandle {
public:
    fdhandle()  {}
    explicit fdhandle(int fd) {
        mp_fd = std::shared_ptr<int>(new int, [=](int* pfd) {
            close(*pfd);
            delete pfd;
        });
        assert(mp_fd);
        *mp_fd = fd;

        // set FD_CLOEXEC on fd
        int flags;
        flags = fcntl(fd, F_GETFD);
        if (-1 == flags) {
            perror("error, could not get flags from filedescriptor");
            exit(EXIT_FAILURE);
        }
        flags |= FD_CLOEXEC;
        if (fcntl(fd, F_SETFD, flags) == -1) {
            perror("error, could not set FD_CLOEXEC");
            exit(EXIT_FAILURE);
        }
    }
    operator int() {
        assert(mp_fd);
        return *mp_fd;
    }

    void show_fd_status() {
        if (!mp_fd)
            return;

        int fd = *mp_fd;

        using namespace std;
        char buf[256];
        int fd_flags = fcntl(fd, F_GETFD);
        if (fd_flags == -1)
            return;
        int fl_flags = fcntl(fd, F_GETFL);
        if (fl_flags == -1)
            return;
        char path[256];
        sprintf(path, "/proc/self/fd/%d", fd);
        memset(&buf[0], 0, 256);
        ssize_t s = readlink(path, &buf[0], 256);
        if (s == -1) {
            cerr << " (" << path << "): " << "not available";
            return;
        }
        cerr << fd << " (" << buf << "): ";
        // file status
        if (fd_flags & FD_CLOEXEC)  cerr << "cloexec ";
        if (fl_flags & O_APPEND)  cerr << "append ";
        if (fl_flags & O_NONBLOCK)  cerr << "nonblock ";

        // acc mode   
        if (fl_flags & O_RDONLY)  cerr << "read-only ";
        if (fl_flags & O_RDWR)  cerr << "read-write ";
        if (fl_flags & O_WRONLY)  cerr << "write-only ";
        if (fl_flags & O_DSYNC)  cerr << "dsync ";
        if (fl_flags & O_RSYNC)  cerr << "rsync ";
        if (fl_flags & O_SYNC)  cerr << "sync ";

        struct flock fl;
        fl.l_type = F_WRLCK;
        fl.l_whence = 0;
        fl.l_start = 0;
        fl.l_len = 0;
        fcntl(fd, F_GETLK, &fl);
        if (fl.l_type != F_UNLCK)
        {
            if (fl.l_type == F_WRLCK)
                cerr << "write-locked";
            else
                cerr << "read-locked";
            cerr << "(pid:" << fl.l_pid << ") ";
        }
    }
private:
    std::shared_ptr<int>    mp_fd;
};

struct child
{
    pid_t       pid;
    fdhandle    fd;
};

void rebind_and_exec_child(fdhandle fd, std::string exe) {
    // unset FD_CLOEXEC 
    int flags, oflags;
    flags = oflags = fcntl(fd, F_GETFD);
    if (-1 == flags) {
        perror("error, could not get flags from filedescriptor");
        exit(EXIT_FAILURE);
    }
    flags &= ~FD_CLOEXEC;
    if (fcntl(fd, F_SETFD, flags) == -1) {
        perror("error, could not unset FD_CLOEXEC");
        exit(EXIT_FAILURE);
    }

    // close and rebind the stdin/stdout/stderr
    // dup the fd three times and recreate stdin/stdout/stderr with fd as the target
    if (dup2(fd, STDIN_FILENO) != 0 || dup2(fd, STDOUT_FILENO) != 1 || dup2(fd, STDERR_FILENO) != 2) {
        perror("error duplicating socket for stdin/stdout/stderr");
        exit(EXIT_FAILURE);
    }

    // restore the old flags
    if (fcntl(fd, F_SETFD, oflags) == -1) {
        perror("error, could not set FD_CLOEXEC");
        exit(EXIT_FAILURE);
    }

    // now we can exec the new sub process and talk to it through 
    // stdin/stdout/stderr
    char path[256];
    char argv[256];
    sprintf(path,"%s",exe.c_str());
    sprintf(argv,"%d",30);
    execlp(path, path, argv, 0);

    // this should never be reached
    perror("error: executing the binary");
    exit(EXIT_FAILURE);
}

child fork_connected_child(std::string exe) {
    // create the socketpair
    int fd[2];
    if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) {
        perror("error, could not create socket pair");
        exit(EXIT_FAILURE);
    }
    fdhandle fdparent(fd[0]);   fdhandle fdchild(fd[1]);

    // now create the child
    pid_t pid = fork();
    switch (pid) {
    case -1:    // could not fork
        perror("error forking the child");
        exit(EXIT_FAILURE);
        break;

    case 0: // child
        rebind_and_exec_child(fdchild, exe);
        break;

    default:    // parent
        std::cout << "forked " << exe << std::endl;
        return child { pid, fdparent };
        break;
    }       
}

int main(int argc, const char** argv) {
    // setup the signal handler prior to forking
    sleep(20);

    // create 2 childs
    {
        child child1 = fork_connected_child("/usr/bin/sleep");
        child child2 = fork_connected_child("/usr/bin/sleep");

        int status;
        waitpid(child1.pid, &status, 0);
        waitpid(child2.pid, &status, 0);
    }

    sleep(20);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM