2.进程通信

1.进程通信的概念

进程间通信的目的:

  • 数据传输:一个进程需要将它的数据发送给另一个进程。

  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

  • 资源共享:多个进程之间共享同样的资源。为了做到这一点,需要内核提供互斥和同步机制。

  • 进程控制:有些进程希望完全控制另一个进程的执行(如 Debug 进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

  1. 管道 (使用最简单)

  2. 信号 (开销最小)

  3. 共享映射区 (无血缘关系)

  4. 本地套接字 (最稳定)

2.管道

2.1无名管道-pipe

2.1.1管道特点
  • 半双工,数据在同一时刻只能在一个方向上流动。

  • 管道不是普通的文件,不属于某个文件系统,其只存在于内存中。

  • 管道没有名字,只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。

    #include <unistd.h>

    int pipe(int pipefd[2]);
    功能:创建无名管道。

    参数:
    pipefd : 为 int 型数组的首地址,其存放了管道的文件描述符 pipefd[0]、pipefd[1]。

    复制代码
      当一个管道建立时,它会创建两个文件描述符 fd[0] 和 fd[1]。其中 fd[0] 固定用于读管道,而 fd[1] 固定用于写管道。一般文件 I/O的函数都可以用来操作管道(lseek() 除外)。

    返回值:
    成功:0
    失败:-1

    #include<sys/types.h>
    #include<unistd.h>
    #include<stdio.h>

    int main()
    {
    int fd_pipe[2] = { 0 };
    pid_t pid;
    if (pipe(fd_pipe) < 0)
    {// 创建管道
    perror("pipe");
    }
    pid = fork(); // 创建进程
    if (pid == 0)
    { // 子进程
    char buf[] = "I am mike";
    // 往管道写端写数据
    write(fd_pipe[1], buf, strlen(buf));
    _exit(0);
    }
    else if (pid > 0)
    {// 父进程
    wait(NULL); // 等待子进程结束,回收其资源
    char str[50] = { 0 };
    // 从管道里读数据
    read(fd_pipe[0], str, sizeof(str));
    printf("str=[%s]\n", str); // 打印数据
    }
    return 0;
    }

2.1.3读写特点

读管道:

  • 管道中有数据,read返回实际读到的字节数。

  • 管道中无数据:

    • 管道写端被全部关闭,read返回0

    • 写端没有全部被关闭,read阻塞等待

写管道:

  • 管道读端全部被关闭, 进程异常终止(也可使用捕捉SIGPIPE信号,使进程终止)

  • 管道读端没有全部关闭:

    • 管道已满,write阻塞。

    • 管道未满,write将数据写入,并返回实际写入的字节数。

      设置非阻塞方法
      //获取原来的flags
      int flags = fcntl(fd[0], F_GETFL);
      // 设置新的flags
      flag |= O_NONBLOCK;
      // flags = flags | O_NONBLOCK;
      fcntl(fd[0], F_SETFL, flags);

      查看管道缓冲区函数
      #include <unistd.h>

      long fpathconf(int fd, int name);
      功能:该函数可以通过name参数查看不同的属性值
      参数:
      fd:文件描述符
      name:
      _PC_PIPE_BUF,查看管道缓冲区大小
      _PC_NAME_MAX,文件名字字节数的上限
      返回值:
      成功:根据name返回的值的意义也不同。
      失败: -1

2.2有名管道-fifo

2.2.1特点

命名管道(FIFO)不同于无名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据。

命名管道(FIFO)和无名管道(pipe)有一些特点是相同的,不一样的地方在于:

  1. FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在内存中。

  2. 当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。

  3. FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。

    #include <sys/types.h>
    #include <sys/stat.h>

    int mkfifo(const char *pathname, mode_t mode);
    功能:
    命名管道的创建。
    参数:
    pathname : 普通的路径名,也就是创建后 FIFO 的名字。
    mode : 文件的权限,与打开普通文件的 open() 函数中的 mode 参数相同。(0666)
    返回值:
    成功:0 状态码
    失败:如果文件已经存在,则会出错且返回 -1。

3.共享存储映射-mmap

3.1概念

存储映射I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。

共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式, 因为进程可以直接读写内存,而不需要任何数据的拷贝。

复制代码
#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
功能:
    一个文件或者其它对象映射进内存
参数:
    addr :  指定映射的起始地址, 通常设为NULL, 由系统指定
    length:映射到内存的文件长度
    prot:  映射区的保护方式, 最常用的 :
        a) 读:PROT_READ
        b) 写:PROT_WRITE
        c) 读写:PROT_READ | PROT_WRITE
    flags:  映射区的特性, 可以是
        a) MAP_SHARED : 写入映射区的数据会复制回文件, 且允许其他映射该文件的进程共享。
        b) MAP_PRIVATE : 对映射区的写入操作会产生一个映射区的复制(copy - on - write), 对此区域所做的修改不会写回原文件。
    fd:由open返回的文件描述符, 代表要映射的文件。
    offset:以文件开始处的偏移量, 必须是4k的整数倍, 通常为0, 表示从文件头开始映射
返回值:
    成功:返回创建的映射区首地址
    失败:MAP_FAILED宏

#include <sys/mman.h>

int munmap(void *addr, size_t length);
功能:
    释放内存映射区
参数:
    addr:使用mmap函数创建的映射区的首地址
    length:映射区的大小
返回值:
    成功:0
    失败:-1
相关推荐
雯宝4 小时前
mnt_init 函数
linux
律宏阔10 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔10 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴10 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千11 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei3711 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei3711 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei3711 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW11 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW11 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux