我可以将文件描述符共享给Linux上的另一个进程,还是它们是进程的本地进程?

liv*_*m95 50 c linux

假设我有2个进程,ProcessA和ProcessB.如果我int fd=open(somefile)在ProcessA中执行,那么我可以将文件描述符的值fd通过IPC传递给ProcessB并让它操作同一个文件吗?

nos*_*nos 54

您可以通过unix域套接字将文件描述符传递给另一个进程.这是传递这样一个文件描述符的代码,取自Unix网络编程

ssize_t
write_fd(int fd, void *ptr, size_t nbytes, int sendfd)
{
    struct msghdr   msg;
    struct iovec    iov[1];

#ifdef  HAVE_MSGHDR_MSG_CONTROL
    union {
      struct cmsghdr    cm;
      char              control[CMSG_SPACE(sizeof(int))];
    } control_un;
    struct cmsghdr  *cmptr;

    msg.msg_control = control_un.control;
    msg.msg_controllen = sizeof(control_un.control);

    cmptr = CMSG_FIRSTHDR(&msg);
    cmptr->cmsg_len = CMSG_LEN(sizeof(int));
    cmptr->cmsg_level = SOL_SOCKET;
    cmptr->cmsg_type = SCM_RIGHTS;
    *((int *) CMSG_DATA(cmptr)) = sendfd;
#else
    msg.msg_accrights = (caddr_t) &sendfd;
    msg.msg_accrightslen = sizeof(int);
#endif

    msg.msg_name = NULL;
    msg.msg_namelen = 0;

    iov[0].iov_base = ptr;
    iov[0].iov_len = nbytes;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;

    return(sendmsg(fd, &msg, 0));
}
/* end write_fd */
Run Code Online (Sandbox Code Playgroud)

这是接收文件描述符的代码

ssize_t
read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
{
    struct msghdr   msg;
    struct iovec    iov[1];
    ssize_t         n;
    int             newfd;

#ifdef  HAVE_MSGHDR_MSG_CONTROL
    union {
      struct cmsghdr    cm;
      char              control[CMSG_SPACE(sizeof(int))];
    } control_un;
    struct cmsghdr  *cmptr;

    msg.msg_control = control_un.control;
    msg.msg_controllen = sizeof(control_un.control);
#else
    msg.msg_accrights = (caddr_t) &newfd;
    msg.msg_accrightslen = sizeof(int);
#endif

    msg.msg_name = NULL;
    msg.msg_namelen = 0;

    iov[0].iov_base = ptr;
    iov[0].iov_len = nbytes;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;

    if ( (n = recvmsg(fd, &msg, 0)) <= 0)
        return(n);

#ifdef  HAVE_MSGHDR_MSG_CONTROL
    if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
        cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
        if (cmptr->cmsg_level != SOL_SOCKET)
            err_quit("control level != SOL_SOCKET");
        if (cmptr->cmsg_type != SCM_RIGHTS)
            err_quit("control type != SCM_RIGHTS");
        *recvfd = *((int *) CMSG_DATA(cmptr));
    } else
        *recvfd = -1;       /* descriptor was not passed */
#else
/* *INDENT-OFF* */
    if (msg.msg_accrightslen == sizeof(int))
        *recvfd = newfd;
    else
        *recvfd = -1;       /* descriptor was not passed */
/* *INDENT-ON* */
#endif

    return(n);
}
/* end read_fd */
Run Code Online (Sandbox Code Playgroud)

  • 请注意,文件描述符的实际数值通常在两个进程中是不同的. (15认同)
  • @EJP SCM_RIGHTS的想法就是它.虽然没有人想到,但我确信有一些警告.(即,这个概念与dup()非常相似,但在不相关的进程之间) (7认同)
  • 您可以通过这种方式传递*号码*。这并不神奇地意味着它将作为两端同一文件的文件描述符。 (3认同)

kol*_*pto 8

2020 年,在 Linux 5.6 及更高版本上,Linux 中添加了一个新的系统调用,\xe2\x80\x99 将使进程能够通过系统调用获取 pidfd 引用的另一个进程的文件描述符的副本pidfd_getfd()

\n


kay*_*kay 5

如果两个进程都属于同一用户,那么您可以简单地使用procfs。

char fd_path[64];  // actual maximal length: 37 for 64bit systems
snprintf(fd_path, sizeof(fd_path), "/proc/%d/fd/%d", SOURCE_PID, SOURCE_FD);
int new_fd = open(fd_path, O_RDWR);
Run Code Online (Sandbox Code Playgroud)

当然,您将需要某种IPC机制来共享的价值SOURCE_FD。参见例如“ Linux C:在接收到信号后,是否可以知道发送者的PID?”。

  • 对于未来的读者:这适用于真实文件,但绝对不适用于诸如未命名域套接字(`socketpair()`)之类的东西,可能不适用于命名域套接字,而且我不确定管道。对于域套接字,如果您在文件系统路径上调用“open()”,您将得到“ENXIO”;`connect()` 会失败,因为它只在未连接的域套接字上下文中有意义;`bind()` 会说地址已经在使用中。 (4认同)