一、无名管道
1.进程间通信介绍
无名管道(pipe)、有名管道 (fifo)、信号(signal)、共享内存(mmap)、套接字(socket)
无名管道具有如下特点:
1.只能用于具有亲缘关系的进程之间的通信(父子、兄弟进程)
2.单工的通信模式,具有固定的读端和写端(单向通信)
3.无名管道创建时会返回两个文件描述符,分别用于读写管道
4.无名管道在内存文件中不可见
2.无名管道创建
pipe
#include <unistd.h>
int pipe(int pfd[2]);
成功时返回0,失败时返回EOF
pfd 包含两个元素的整形数组,用来保存文件描述符
pfd[0]用于读管道;pfd[1]用于写管道
代码实现
示例1:子进程1和子进程2分别往管道中写入字符串,父进程读管道内容并打印
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(void){
pid_t pid1, pid2; //定义pid1、2保存返回值
char buf[32]; //缓冲区
int pfd[2]; //数组,用于创建无名管道
if(pipe(pfd)<0){ //判断
perror("pipe");
exit(-1);
}
//注:先创建无名管道,再创建子进程!
if((pid1 = fork())<0){
perror("fork");
exit(-1);
}
else if(pid1 == 0){ //子进程1
strcpy(buf,"I'm process1"); //将字符串拷贝到缓冲区中
write(pfd[1],buf,32); //将缓冲区中的内容写入到管道中,pfd[1]
exit(0);
}
else { //父进程
if((pid2 = fork()) < 0){
perror("fork");
exit(-1);
}
else if(pid2 == 0){ //子进程2
sleep(1); //延时,使子进程1有足够的时间写入到管道
strcpy(buf,"I'm process 2");
write(pfd[1],buf,32);
}
else{ //父进程
wait(NULL); //等待回收子进程1
read(pfd[0],buf,32); //读取管道中的内容到缓冲区
printf("%s\n",buf); //打印缓冲区的内容
wait(NULL); //等待回收子进程1
read(pfd[0],buf,32); //读取管道中的内容到缓冲区
prif("%s\n",buf); //打印缓冲区的内容
}
}
return 0;
}
注:
管道内的内容读走了则不存在了
示例2:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
int pfd[2];
int re;
char buf[20]={0};
pid_t pid;
re = pipe(pfd);
if(re<0){
perror("pipe");
return 0;
}
printf("%d,%d\n",pfd[0],pfd[1]);
pid = fork();
if(pid<0){
perror("fork");
return 0;
}else if(pid>0){
//close(pfd[0]);
while(1){
strcpy(buf,"hhahahahah");
write(pfd[1],buf,strlen(buf));
sleep(1);
}
}else{
close(pfd[1]);
while(1){
re=read(pfd[0],buf,20);
if(re>0){
printf("read pipe=%s\n",buf);
}
}
}
}
二、无名管道读写特性
1.读管道
1)写端存在
管道中有数据:
read返回实际读取的字节数
管道中无数据:
①管道写端被全部关闭,read返回0 (好像读到文件结尾)
②写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)
2)写端不存在
管道中有数据 :
read返回实际读取的字节数
管道中无数据:
read返回0
2.写管道
1)读端存在
管道中有空间:
write将数据写入,并返回实际写入的字节数
管道中无空间:空间不足:
先写入剩下空间能写入的数据,剩余的数据等待有空间再写,进程阻塞
2)读端不存在
管道断裂!
验证管道断裂:
代码:
#incLude <stdio.h>
#include <stdLib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
pid_t pid;
int pfd[2];
char buf[32];
if(pipe(pfd) < 0){
perror("pipe");
exit(-1);
}
close(pfd[0]);
if((pid = fork()) < 0){
perror("fork");
exit(-1);
}
else if(pid == 0){
write(pfd[1],buf,32); //子进程写入管道
exit(0);
}
else{
wait(&status); //父进程回收子进程
printf("status = %x\n",status);
}
return 0;
}
执行结果:
3.无名管道的大小
循环写入管道,直到阻塞
统计循环次数
代码演示:
#include<stdio.h>
#include<stdlib.h>
int main(){
int count = 0;fid[2];
char buf[1024];
if(pipe(pfd) < 0){
perror("pipe");
exit(-1);
}
while(1){
write(pfd[1],buff,1024);
printf("wrote %dk bytes\n",++count);
}
return 0;
}
三、有名管道
1.有名管道
创建有名管道后有实际的文件
特点:
1 有名管道可以使非亲缘的两个进程互相通信
2 通过路径名来操作,在文件系统中可见,但内容存放在内存中
3 打开管道时可指定读写方式
4 文件IO来操作有名管道
5 遵循先进先出规则
6 不支持leek操作
7 单工读写
2.有名管道创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
成功时返回0,失败时返回EOF
path创建的管道文件路径
mode管道文件的权限,如0666
open(const char *path, O_RDONLY);//1
open(const char *path, O_RDONLY | O_NONBLOCK);//2
open(const char *path, O_WRONLY);//3
open(const char *path, O_WRONLY | O_NONBLOCK);//4
代码演示:
//创建管道
//省略头文件
int main(void){
if(mkfifo("myfifo", 0666) < 0){
perror(" mkfifo");
exit(-1);
}
return 0;
}
//写
//省略头文件
#define N 32
int main(void) {
char buf[N]; //缓冲区
int pfd;
if ((pfd = open("myfifo", O_WRONLY))<0){ //只写方式打开管道
perror("open");
exit(-1);
}
while(1){
fgets(buf, N, stdin); //fgets,从标准输入读取字符串到buf中
if (strcmp(buf,"quit\n") == 0) //quit
break;
write(pfd, buf, N); //缓冲区中内容写入到管道中
}
close(pfd); //关闭管道
return 0;
}
//读
//省略头文件
#define N 32
int main(void){
char buf[N];
int pfd;,
if((pfd = open("myfifo", O_RDONLY))<0){ //只读方式打开管道
perror("open");
exit(-1);
}
//从管道中读取内容到缓冲区中,若管道中没有数据,read自动返回0,退出循环
while (read(pfd, buf, N)>0){
printf("the length of string is %d\n",strlen(buf));
}
close(pfd);
return 0;
}
注:
单运行读文件会阻塞,只有读端;须同时运行写文件(写端)
四、作业
无名管道的读写特性有哪些?
1.单工,数据在同一时刻只能在一个方向上流懂
2.数据只能从管道的一段写入,另一端读出
3.写入管道中的数据遵循先进先出的规则
4.管道所传送的数据是无格式的,要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等
5.管道不是普通的文件,不属于某个文件系统,其只存在于内存中
6.管道在内存中对应一个缓冲区。不同的系统其大小不一定相同
7.从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据
8.管道没有名字,只能在亲缘关系的进程(父子进程,或者兄弟进程)之间使用