进程间通信
1. 进程间通信
- 操作系统为进程间能够通信所提供的几种方式,每个进程都有自己的虚拟地址空间,访问的都是虚拟地址,因此进程之间具有独立性,是无法直接通信的。
- 原理:系统为进程间提供公共传输媒介实现公共访问进而实现通信
- 根据通信需求不同提供了不同方式:管道(先进先出),共享内存,消息队列,信息量
2. 管道
2.1 管道的基本概念
- 特性:半双工通信(可以选择方向的单工通信),管道提供字节流传输服务:有序的、基于连接的、可靠的传输方式
- 本质:操作系统内核中的一块缓冲区(内核空间中开辟的一块内存)
- 通信原理:多个进程只要能够访问同一块内核中的缓冲区(管道)就能实现通信。
- 管道:| 链接两个命令,将前面命令进程要输出到标准输出的结果,不再输出到标准输出,而将这个数据传输到后面的命令进程
· ps -ef | grep pipe
· ps -ef:将所有进程信息写入到标准输出
· grep pipe :不断循环从标准输入读取数据进行字符串匹配过滤 - 分类:匿名管道、命名管道
· 匿名管道:只能用于具有亲缘关系的进程间通信(无标识符,不能被找到)
· 命名管道:可以用于同一主机上任意进程间通信(有标识符,可以被找到) - 管道自带同步与互斥:
· 互斥:通过保证同一时间对临界资源的唯一访问保证操作安全性
· 互斥的具体体现:对管道进行写操作时,写入数据大小不超过PIPE_BUF默认4096字节的大小,则保证操作原子性
· 同步:通过某种条件判断,实现对资源访问获取的有序合理性
· 同步的具体体现:若管道中没有数据,则read阻塞,若管道中数据满了,则write阻塞 - 管道生命周期随进程
2.2 匿名管道
- 管道缓冲区,没有标识符,无法被其他进程找到,只能通过子进程复制父进程的方式获取到管道的操作句柄,进行通信
2.2.1 代码操作
int pipe(int pipefd(2));
- pipefd[0] : 用于从管道读数据;
- pipefd[1] : 用于向管道写数据;
- 返回值:成功返回0;失败返回-1;
- 通过IO操作完成对管道的操作
2.2.2 读写特性
- 若管道中没有数据,则read会阻塞;若管道中数据满了,则write会阻塞
- 若管道的所有读端被关闭,则继续write就会触发异常,导致进程退出
- 若管道的所有写端被关闭,read读完所有数据后,则不再阻塞,返回0
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[])
{
int pipefd[2];
int ret = pipe(pipefd);
if (ret < 0) {
perror("pipe error");
return -1;
}
pid_t pid = fork();
if (pid < 0) {
perror("fork error");
exit(-1);
}else if (pid == 0) {
//child
close(pipefd[1]);
sleep(1);
char buf[1024] = {0};
int ret = read(pipefd[0], buf, 1023);
if (ret < 0) {
perror("read error");
return -1;
} else if (ret == 0) {
printf("all write closed\n");
}
printf("buf:%s\n", buf);
}else {
//parent
close(pipefd[1]);
sleep(100);
int total = 0;
char *ptr = "中午吃什么呢??\n";
while(1) {
int ret = write(pipefd[1], ptr, strlen(ptr));
if (ret < 0) {
perror("write error");
return -1;
}
total += ret;
printf("写入成功:%d\n", total);
}
}
return 0;
}
2.2.3 shell进程中创建两个进程
- 一个进程运行ps,一个进程运行grep
- 要将ps进程的数据,交给grep进程只能通过进程间通信完成–匿名管道
· 1.创建管道
· 2.创建两个进程,在各自进程中进行程序替换
· 3.在程序替换之前,ps进程应该标准输出重定向到管道写入端,grep进程应该标准输入重定向到管道读取端
· 4.父进程等待子进程退出
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main (int argc, char *argv[])
{
int pipefd[2];
int ret = pipe(pipefd);
if (ret < 0) {
perror("pipe error");
return -1;
}
if (fork() == 0) {
dup2(pipefd[1], 1);//标准输出重定向,将数据写入管道
execlp("ps", "ps", "-ef", NULL);
exit(-1);
}
if (fork() == 0) {
close(pipefd[1]);
dup2(pipefd[0], 0);//标准输入重定向,从管道读取数据
///execlp(新的程序文件名称,运行参数, NULL)
execlp("grep", "grep", "pipe", NULL);
exit(-1);
}
close(pipefd[0]);
close(pipefd[1]);
wait(NULL);
wait(NULL);
return 0;
}
2.3 命名管道
- 可以用于同一主机上任意进程间通信-- 管道缓冲区具有标识符
2.3.1 命名操作
- mkfifo filename 创建一个管道文件
- 本质:内核中的一块缓冲区,但是命名管道有名字具有标识符,而这个标识符就是一个可见于文件系统的管道类型文件,多个进程可以通过打开同一个管道文件访问同一块内核中的缓冲区实现通信
2.3.2 代码操作
int mkfifo(char *path, int mode)
- 对管道的读写操作,依然是I/O操作
2.3.3 打开特性
- 若以只读方式打开命名管道文件,则会阻塞,直到这个管道文件被以写的方式打开
- 若以只写方式打开命名管道文件,则会阻塞,直到这个管道文件被以读的方式打开
- 写操作
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
int main (int argc, char *argv[])
{
umask(0);
char *fifo_name = "./test.fifo";
int ret = mkfifo(fifo_name, 0664);
if (ret < 0 && errno != EEXIST) {
perror("mkfifo error");
return -1;
}
int fd = open(fifo_name, O_WRONLY);
if (fd < 0) {
perror("open error");
return -1;
}
while(1) {
char buf[1024] = {0};
scanf("%s", buf);
int ret = write(fd, buf, strlen(buf));
if (ret < 0) {
perror("write error");
return -1;
}
}
close(fd);
return 0;
}
- 读操作
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
int main (int argc, char *argv[])
{
umask(0);
char *fifo_name = "./test.fifo";
int ret = mkfifo(fifo_name, 0664);
if (ret < 0 && errno != EEXIST) {
//不是因为文件已经存在而报错
perror("mkfifo error");
return -1;
}
//open(文件名,打开方式,权限)
int fd = open(fifo_name, O_RDONLY);
if (fd < 0) {
perror("open error");
return -1;
}
printf("open fifo success\n");
while(1) {
char buf[1024] = {0};
int ret = read(fd, buf, 1023);
if (ret < 0) {
perror("read error");
return -1;
}else if (ret == 0) {
printf("all write closed\n");
return -1;
}
printf("buf:%s\n");
}
close(fd);
return 0;
}