Linux进程之间的通信机制(IPC)概述

发布时间:2024年01月07日

Linux进程之间通信

为什么需要进程间通信?

进程间的通信(IPC)指的是两个任意的进程之间的通信。
同一进程在一个地址空间中,所以同一进程的不同模块,(不同函数,不同文件)之间都是很简单的。(很多时候都是全局变量、也可以通过函数形参来传递)
两个不同的进程处于不同的地址空间,因此想要相互通信很难,进程地址空间相互独立,每个进程各自有不同的用户地址空间,因此,进程间通信的成本是比较高的。若没有进程间通信,那么也就无法使用并发能力,无法实现进程间协同、传输数据、消息通知等。

什么样的程序设计需要进程间的通信

绝大多数的程序是不需要考虑进程间的通信的。因为大多数的程序都是单进程的(可以多线程),复杂的大型的程序,因为设计的需要就必须设计成多进程程序(我们整个程序就设计成了多个进程同时工作来完成的模式。)比如说GUI 服务器。IPC技术在一般的中小型程序中用不到,在大型程序中才会用到

Linux 内核提供多种进程间的通信

  • 无名管道和有名管道

    无名管道,又称为pipe,只适用于父子进程之间的通信。它是一个存在于内存中的文件,对它的读写操作需要通过两个已经打开的文件进行,分别代表管道的两端。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

    有名管道,又称为FIFO(First In First Out),是一种在文件系统中可见的管道,可以在任意两个进程之间进行通信。它就如同一个独立的文件系统,有其自己的数据结构。有名管道通过mkfifo命令在文件系统中创建,以文件的形式存在,可以通过路径名来访问。
    无论是无名管道还是命名管道,它们都采用半双工通信方式,即只能以只读或者只写的方式打开,不能同时进行读写操作。因此,对管道进行操作时需要同时打开两个进程,一个负责读,一个负责写。
    无名管道Demo

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int main() {
    // 创建无名管道
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe 创建失败");
        exit(EXIT_FAILURE);
    }

    // 在子进程中写入数据到管道
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork 创建失败");
        exit(EXIT_FAILURE);
    } else if (pid == 0) { // 子进程
        close(pipefd[0]); // 关闭读端
        const char *message = "Hello, world!";
        write(pipefd[1], message, strlen(message)); // 写入数据到管道
        close(pipefd[1]); // 关闭写端
        exit(EXIT_SUCCESS);
    } else { // 父进程
        close(pipefd[1]); // 关闭写端
        char buffer[256];
        read(pipefd[0], buffer, sizeof(buffer)); // 从管道中读取数据
        printf("Received from child process: %s\n", buffer);
        close(pipefd[0]); // 关闭读端
        wait(NULL); // 等待子进程结束
    }

    return 0;
}

有名管道的使用方法:固定一个文件名,两个进程分别使用mkfifo 创建fifo文件,然后分别open打开获取到fd,然后一个读一个写

有名管道Demo
一个进程向管道写入数据

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <unistd.h>  
  
#define FIFO_FILE_PATH "my_fifo"  
#define BUFFER_SIZE 1024  
  
int main() {  
    int fd;  
    char buffer[BUFFER_SIZE];  
    // 创建有名管道  
    mkfifo(FIFO_FILE_PATH, 0666);  
    // 打开管道为写模式  
    fd = open(FIFO_FILE_PATH, O_WRONLY);  
    if (fd == -1) {  
        perror("open");  
        exit(1);  
    }  
    // 向管道写入数据  
    const char *msg = "Hello, FIFO!";  
    write(fd, msg, strlen(msg));  
    close(fd);  
    printf("进程A已经向有名管道中写入了数据\n");
    return 0;  
}

一个进程向管道读取数据

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <unistd.h>  
  
#define FIFO_FILE_PATH "my_fifo"  
#define BUFFER_SIZE 1024  
  
int main() {  
    int fd;  
    char buffer[BUFFER_SIZE];  
      
   
    // 打开管道为读模式  
    fd = open(FIFO_FILE_PATH, O_RDONLY);  
    if (fd == -1) {  
        perror("open");  
        exit(1);  
    }  
      
    // 从管道读取数据并打印到控制台  
    while (1) {  
        ssize_t bytesRead = read(fd, buffer, BUFFER_SIZE);  
        if (bytesRead <= 0) {  
            break;  // 如果没有数据可读,则退出循环  
        }  
        buffer[bytesRead] = '\0';  // 添加字符串终止符  
        printf("%s", buffer);  // 打印读取的数据到控制台  
    }  
    close(fd);  
    return 0;  
}

在这里插入图片描述
注意事项: 进程A向管道写入数据之后,并不会立即结束,而是在等被读取,才进行下一步,即return结束。
进程B读取之后,pipe的数据就会被删除,即写一次 读一次 清空

  • System VIPC :信号量 消息队列 共享内存
    1.信号量(Semaphore):信号量是一种用于控制多个进程对共享资源的访问次数的计数器。它是一个整数值,通常用于保护某些临界资源,以确保一次只有一个进程能够访问这些资源。信号量的值可以增加或减少,通过信号量的值,进程可以判断是否可以访问共享资源。如果信号量的值为0,则表示当前没有进程可以访问共享资源;如果信号量的值大于0,则表示当前有进程可以访问共享资源。
    2.消息队列(Message Queue):消息队列是一种用于进程间通信的机制,类似于一个队列,其中可以存储不同类型的数据。一个进程可以将消息添加到队列中,而另一个进程可以从队列中读取消息。每个消息都有一个类型和一个数据部分,进程可以根据消息的类型来决定如何处理消息。消息队列提供了一种可靠的、有序的通信方式,并且可以在不同的进程之间传递数据。
    3.共享内存(Shared Memory):共享内存是一种使多个进程可以访问同一块物理内存的机制。通过共享内存,多个进程可以读取和修改同一块内存区域,从而实现数据共享和通信。共享内存的访问权限可以通过一些机制来控制,例如访问权限位图或访问控制表。共享内存是一种非常快速的IPC方式,因为访问用户空间的内存的操作是非常快的。但是,需要谨慎地处理对共享内存的访问,以避免出现竞态条件和死锁等问题。
文章来源:https://blog.csdn.net/weixin_46999174/article/details/135445469
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。