Linux进程之间的通信机制(IPC)概述
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方式,因为访问用户空间的内存的操作是非常快的。但是,需要谨慎地处理对共享内存的访问,以避免出现竞态条件和死锁等问题。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!