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

时间:2010-03-01 20:05:32

标签: c linux

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

4 个答案:

答案 0 :(得分:51)

您可以通过unix domain套接字将文件描述符传递给另一个进程。 以下是传递此类文件描述符的代码,取自Unix Network Programming

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 */

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

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 */

答案 1 :(得分:2)

您可以使用此线程中描述的方法,或者(更常规)方式,通过创建相关进程(通常是父子或兄弟)之间共享它,分叉进程自动接收副本。 / p>

实际上,分叉进程会获取所有FD并且可以使用它们,除非它们关闭它们(这通常是一个好主意)。

因此,如果父亲分叉两个孩子,如果他们都有一个他们没有关闭的文件描述符,它现在被共享(即使父母随后关闭它)。例如,这可能是从一个孩子到另一个孩子的管道。这就是shell重定向的方式,如

ls -l | more

工作。

答案 2 :(得分:2)

请注意,在上面的示例中,接收时设置变量,如:

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;

不是必需的。带有标题的消息结构的整个概念是接收站点不必知道它读取的内容,并且可以通过检查(第一个)标题,它是什么类型的消息以及期望什么。

答案 3 :(得分:2)

如果两个进程都属于同一个用户,那么您只需使用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);

当然,您需要一些IPC机制来共享SOURCE_FD的值。参见例如“Linux C: upon receiving a signal, is it possible to know the PID of the sender?”。