Подтвердить что ты не робот

Могу ли я поделиться файловым дескриптором с другим процессом в Linux или быть локальным для этого процесса?

Скажем, у меня есть 2 процесса, ProcessA и ProcessB. Если я выполняю int fd=open(somefile) в ProcessA, могу ли я передать значение дескриптора файла fd через IPC в ProcessB и заставить его управлять одним и тем же файлом?

4b9b3361

Ответ 1

Вы можете передать файловый дескриптор другому процессу через 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 */

Ответ 2

Вы можете использовать метод nos, описанный в этом потоке, или (более обычный) способ, путем совместного использования его между связанными процессами (обычно родительским или дочерним), создав его, разветвленные процессы автоматически получают копию.

Действительно, forked процессы получают все ваши FD и могут использовать их, если они не закрывают их (что обычно является хорошей идеей).

Поэтому, если родительский файл разворачивает двух дочерних элементов, если оба они имеют файловый дескриптор, они не закрываются, теперь он разделяется (даже если потом его потом закрывает). Это может быть, например, труба от одного ребенка к другому. Это как перенаправление оболочки, например

ls -l | more

Работа.

Ответ 3

Обратите внимание, что в приведенном выше примере установка переменных при приеме, например:

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;

не требуется. Вся идея структуры сообщений с заголовками заключается в том, что принимающий сайт не должен знать, что он читает, и может, проверив заголовок (первый), какое сообщение оно и что ожидать.

Ответ 4

Если оба процесса принадлежат одному и тому же пользователю, вы можете просто использовать 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: после получения сигнала можно узнать PID отправителя?".