我可以与 Linux 上的另一个进程共享文件描述符吗?或者它们是该进程本地的?

2024-10-09 09:10:00
admin
原创
78
摘要:问题描述:假设我有 2 个进程,ProcessA 和 ProcessB。如果我int fd=open(somefile)在 ProcessA 中执行,那么我可以通过 IPC 将文件描述符的值传递fd给 ProcessB 并让其操作同一个文件吗?解决方案 1:您可以通过unix 域套接字将文件描述符传递给另一个...

问题描述:

假设我有 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;

不是必需的。带有标头的消息结构的整体思想是接收站点不必知道它读取了什么,并且可以通过检查(第一个)标头来了解它是什么类型的消息以及期望什么。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用