Скажем, у меня есть 2 процесса, ProcessA и ProcessB. Если я выполняю int fd=open(somefile)
в ProcessA, могу ли я передать значение дескриптора файла fd
через IPC в ProcessB и заставить его управлять одним и тем же файлом?
Могу ли я поделиться файловым дескриптором с другим процессом в Linux или быть локальным для этого процесса?
Ответ 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 отправителя?".