进程间的通信(IPC)指的是两个任意的进程之间的通信。
同一进程在一个地址空间中,所以同一进程的不同模块,(不同函数,不同文件)之间都是很简单的。(很多时候都是全局变量、也可以通过函数形参来传递)
两个不同的进程处于不同的地址空间,因此想要相互通信很难,进程地址空间相互独立,每个进程各自有不同的用户地址空间,因此,进程间通信的成本是比较高的。若没有进程间通信,那么也就无法使用并发能力,无法实现进程间协同、传输数据、消息通知等。
绝大多数的程序是不需要考虑进程间的通信的。因为大多数的程序都是单进程的(可以多线程),复杂的大型的程序,因为设计的需要就必须设计成多进程程序(我们整个程序就设计成了多个进程同时工作来完成的模式。)比如说GUI 服务器。IPC技术在一般的中小型程序中用不到,在大型程序中才会用到
无名管道和有名管道
无名管道,又称为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的数据就会被删除,即写一次 读一次 清空