文章目录
进程间通信(IPC)篇
一、进程间通信概述
进程间通信是指在不同进程之间传播或交换信息,IPC的方式常有管道(包括无名管道和命名管道)消息队列,信号量,共享存储,Socket,Stream等。其中Socket和Stream支持不同主机上的两个进程IPC。
单机通信 | 多机通信 |
---|---|
半双工管道(FIFO) | 套接字 |
全双工管道 | STPEAMS |
消息队列 | |
信号量 | |
共享存储 |
二、管道通信原理
1. 管道(无名管道):
管道,通常指 无名管道 ,是UNIX系统IPC最古老的形式。只存在于内存中,当父子进程退出后,管道就消失了。
1.特点:
-
它是 半双工 的(即数据只能在一个方向上流动),具有固定的读端和写端。
-
它只是用于具有 亲缘关系 的进程之间 通信(也是父子进程或者兄弟进程之间)
-
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read,write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且 只存在于内存中 。当父子进程退出后,管道就消失了。
2.原型:
#include<unistd.h>
int pipe(int fd[2]); //返回值 :若成功返回0 失败返回-1
3.返回值: 若成功返回0 失败返回-1
当一个管道建立时候,它会创建两个文件描述符: fd[0] 为读而打开 ,fd[1] 为写而打开。
read(fd[0] , buff , 128);
write(fd[1] , “hello world \n” , 12);
要关闭管道,只需要将这两个文件描述符关闭即可。
close(fd[0]); //关闭读端
close(fd[1]); //关闭写端
例如:单个进程中的管道几乎没有任何用处。所以,通常调用pipe的进程接着调用fork,这样就创建了父进程与子进程之间的IPC通道,
2. 管道 编程的实战:
无名管道:父进程往管道里写,子进程从管道外读:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int fd [2]; //两个文件描述符
pid_t pid;
char buff [128];
if(pipe(fd) == -1){ //创建管道
printf("Create pipe error! \n"); //创建管道错误
}
pid = fork; //创建子进程
if(pid<0){
printf("Fork error \n"); //fork错误
}else if(pid > 0){ //父进程
wait();
printf("this is father\n");
close(fd[0]); //关闭读端
write(fd[1],"hello world \n",12);
}else{
printf("this is child\n");
close(fd[1]); //关闭写端
read(fd[0],buff,128);
printf("read from father:%s\n",buff);
exit(0);
}
return 0;
}
若read 没读到数据 ,read会阻塞
三、FIFO (命名管道)
FIFO,也称为命名管道,它是一种文件类型。
1.特点:
-
FIFO可以在无关的进程之间交换数据,与无名管道不同。
-
FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
2.原型:
#include<sys/stat.h>
#include<sys/types.h>
//返回值:成功返回0 ,失败返回-1
int mkfifo(char *pathname , mode_t mode);
返回值:成功返回0 ,失败返回-1
参数说明:
mode与open函数中的mode相同。一旦创建一个FIFO,就可以用一般的文件I/O函数操作它(如 open ,read ,write)。
1. 创建FIFO有名管道代码:
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
// int mkfifo(const char *pathname ,mode_t mode);
int main(){
if((mkfifo("./file",0600)== -1)&& errno!=EEXIST){
printf("mkfifo failuer\n");
perror("why");
}
return 0;
}
管道读取:
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>
// int mkfifo(const char *pathname ,mode_t mode);
int main(){
if((mkfifo("./file",0600)== -1)&& errno!=EEXIST){
printf("mkfifo failuer\n");
perror("why");
}
int fd = open("./file",O_RDONLY); //O_RDONLY读数据
printf("open success\n");
return 0;
}
结果显示:
创建一个管道后, 打开管道后printf(“open success\n”); 没有执行。
2. 当open一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别?
-
若没有指定O_NONBLOCK(默认),只读open要阻塞 到某个 其他进程为写而打开此FIFO ,类似的,只写open要阻塞到某个其他他进程为读而打开它。
-
若指定了O_NOBLOCK,则只读open立即返回。而只写open将出错误返回-1 如果没有进程已经为读而打开该FIFO,其errno置ENXIO 。
所以说上面代码是用写的方式打开管道,就不会堵塞了
3. 命名管道的数据通信编程实现:
写的当时打开管道:
./read
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>
// int mkfifo(const char *pathname ,mode_t mode);
int main(){
int buf[30]={0};
if((mkfifo("./file",0600)== -1)&& errno!=EEXIST){ //创建管道
printf("mkfifo failuer\n");
perror("why");
}
int fd = open("./file",O_RDONLY); //O_RDONLY 读数据
printf("write open success\n");
int nread = read(fd,buf,30);
printf("read%d byte from fifo context:%s\n",nread,buf);
return 0;
}
./write
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>
int main(){
int fd = open("./file",O_WRONLY); //O_WRONLY 写数据
printf("write open success\n");
return 0;
}
当用读的方式打开 会堵塞, 只写open要阻塞到某个其他他进程为读而打开它。
在./read堵塞的时候,在执行./write ,就可以获取数据
也可以实现两个进程间通信:多次写入:
./read
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>
int main(){
int buf[30]={0};
int nread=0;
int fd = open("./file",O_RDONLY);
printf("write open success\n");
while(1){
nread = read(fd,buf,30);
printf("read%d byte from fifo context:%s\n",nread,buf);
}
return 0;
}
./write
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<errno.h>
#include<fcntl.h>
#include<string.h>
int main(){
int cnt=0;
char *str="message from fifo";
int fd = open("./file",O_WRONLY);
printf("write open success\n");
while(1){
write(fd,str,strlen(str));
sleep(1);
if(cnt == 5){
break;
}
}
return 0;
}