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

2024-10-09 09:10:00
admin
原创
226
摘要:问题描述:假设我有 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;

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

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用