我可以与 Linux 上的另一个进程共享文件描述符吗?或者它们是该进程本地的?
- 2024-10-09 09:10:00
- admin 原创
- 78
问题描述:
假设我有 2 个进程,ProcessA 和 ProcessB。如果我int fd=open(somefile)
在 ProcessA 中执行,那么我可以通过 IPC 将文件描述符的值传递fd
给 ProcessB 并让其操作同一个文件吗?
解决方案 1:
您可以通过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 */
以下是接收文件描述符的代码
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:
如果两个进程属于同一个用户,那么您可以简单地使用 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? ”。
解决方案 3:
2020 年,在 Linux 5.6 及更高版本中,添加了一个新的系统调用,使进程能够通过系统调用获取 pidfd 引用的另一个进程的文件描述符的副本pidfd_getfd()
。
解决方案 4:
简短回答:
尝试pidfd_getfd
长答案
pidfd_getfd ()系统调用在调用进程(进程 B)中分配一个新的文件描述符。这个新的文件描述符是PID 文件描述符pidfd引用的进程(进程 A)中现有文件描述符targetfd的副本。当然,您需要一种从进程 A 获取 targetfd 的机制。
newfd = syscall(SYS_pidfd_getfd, int pidfd, int targetfd, 0);
我们从pidfd_open()获取 PID 文件描述符 pidfd 。
pidfd = syscall(SYS_pidfd_open, pid_t pid, 0);
pidfd_getfd()的效果类似于使用SCM_RIGHTS消息,但为了使用SCM_RIGHTS消息传递文件描述符,两个进程必须首先建立UNIX 域套接字连接,因此需要被复制文件描述符的进程进行合作。相比之下,使用pidfd_getfd()时则不需要这样的合作。
一个虚拟示例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <string.h>
#define MMAP_NAME "/tmp/mmap"
struct shared_mem{
int targetfd;
};
int main(void){
int fd;
struct shared_mem *shmp;
unlink(MMAP_NAME);
fd = open(MMAP_NAME, O_CREAT | O_RDWR, 00600);
ftruncate(fd, sizeof(struct shared_mem));
shmp = (struct shared_mem *)mmap(NULL,
sizeof(struct shared_mem),
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0);
if (fork() == 0){
sleep(5);
write(syscall(SYS_pidfd_getfd,
syscall(SYS_pidfd_open, getppid(), 0),
shmp->targetfd,
0),
"Messege from Child
",
strlen("Messege from Child
"));
close(shmp->targetfd);
exit(EXIT_SUCCESS);
}else{
shmp->targetfd = open("foo.txt", O_RDWR | O_CREAT);
write(shmp->targetfd, "Messege from Parent
", strlen("Messege from Parent
"));
wait(NULL);
}
munmap(shmp, sizeof(struct shared_mem));
return EXIT_SUCCESS;
}
..
# cat foo.txt
Messege from Parent
Messege from Child
解决方案 5:
您可以使用此线程中描述的方法 nos,或者使用(更传统的)方式,通过在相关进程(通常是父子进程或兄弟进程)之间共享它,通过创建它,分叉的进程会自动收到一份副本。
确实,分叉的进程会获取所有 FD 并且可以使用它们,除非它们关闭它们(这通常是一个好主意)。
因此,如果父进程派生两个子进程,如果它们都有一个未关闭的文件描述符,则现在共享该文件描述符(即使父进程随后关闭了它)。例如,这可能是一个从一个子进程到另一个子进程的管道。这就是 shell 重定向的方式,例如
ls -l | more
工作。
解决方案 6:
注意上面例子中接收时变量的设置,如:
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;
不是必需的。带有标头的消息结构的整体思想是接收站点不必知道它读取了什么,并且可以通过检查(第一个)标头来了解它是什么类型的消息以及期望什么。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件