无名管道(PIPE)和有名管道(FIFO)
1、无名管道(PIPE)
无名管道(匿名管道),是一种具有两个端点的通信通道,管道的一端用于读取管道内数据,另一端用于将数据写入到管道内。创建一个管道后,会获取一对文件描述符,用于读取和写入。匿名管道通常是用在父子进程之间,由父进程创建匿名管道,子进程会继承管道的读端和写端,实现通讯。
管道是单向的、先进先出的、无结构的字节流。写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。
由于PIPE 存在于内存中,并且只有创建它的进程可以直接拿到读写两个文件描述符,其他的进程要想获取到这两个描述符相对来说比较困难,通常做法是父进程先创建无名管道,再创建子进程。由于子进程继承父进程打开的文件描述符,所以父子进程就可以通过无名管道进行通信。
无名管道是临时的,在通讯完成进程退出后,将自动消失,对管道的读写可以使用 read 和write函数。
无名管道默认情况下只有64KB
PIPE的创建
实例1
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(void)
{
pid_t pid=fork();
int fd[2];
if(pipe(fd) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
if(pid == 0)
{
char buff[64]={0};
char* str="son:i get!";
write(fd[1],str,strlen(str));
read(fd[0],buff,sizeof(buff));
printf("%s\n",buff);
}
if(pid > 0)
{
char buff[64]={0};
char* str="dad:hi! my son!";
write(fd[1],str,strlen(str));
read(fd[0],buff,sizeof(buff));
printf("%s\n",buff);
}
close(fd[0]);
close(fd[1]);
if(pid < 0)
{
perror("fork");
close(fd[0]);
close(fd[1]);
exit(EXIT_FAILURE);
}
return 0;
}
实例2
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(void)
{
pid_t pid=fork();
int fd[2];
if(pipe(fd) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
if(pid == 0)
{
int count=0;
char *str="a";
while(1)
{
write(fd[1],str,strlen(str));
count++;
printf("%d\n",count);
}
}
close(fd[0]);
close(fd[1]);
if(pid < 0)
{
perror("fork");
close(fd[0]);
close(fd[1]);
exit(EXIT_FAILURE);
}
return 0;
}
运行结果:
一个char型字符一个字节,64kb刚好就是65536个字节
2、有名管道(FIFO)
(1)概念:
有名管道(命名管道),不同与无名管道的是有名管道可以用于不相关的进程之间进行通讯,
创建有名管道的进程可以给管道取名字以及路径,该路径名是以特殊的文件存在放在文件系统中,因此两个进程可以通过访问该路径来建立联系,进行进程间的数据交换,有名管道和无名管道都遵循先进先出的原则。
有名管道是一个特殊的文件,它和普通的文件一样具有磁盘存放路径,文件权限和其他属性,但是两者又有区别,有名管道并不会在磁盘中存放真正的信息,信息存放在内存中。通讯的两个进程结束后,信息会自动消失,但管道文件路径依然存在。
(2)特点
两个进程已经完成打开有名管道操作,中途其中一个进程退出:
a)如果退出的是读操作,则写操作将返回 SIGPIPE 信号;
b)如果退出的是写操作,则读操作将不再阻塞,直接返回0
实例3
写
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FI_PATH "/home/yanlu/info"//管道路径
int main()
{
if(access(FI_PATH,F_OK) == -1)
{
if(mkfifo(FI_PATH,0666) < 0)
{
perror("mkfifo");
exit(EXIT_FAILURE);
}
}
int fd=open(FI_PATH,O_WRONLY);
if(fd == -1)
{
perror("open");
exit(EXIT_FAILURE);
}
char pp[64]={0};
sprintf(pp,"%d",getpid());
write(fd,pp,strlen(pp));
close(fd);
return 0;
}
读
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FI_PATH "/home/yanlu/info"
int main()
{
if(access(FI_PATH,F_OK) == -1)
{
if(mkfifo(FI_PATH,0666) < 0)
{
perror("mkfifo");
exit(EXIT_FAILURE);
}
}
int fd=open(FI_PATH,O_RDONLY);
if(fd == -1)
{
perror("open");
exit(EXIT_FAILURE);
}
char pp[64]={0};
read(fd,pp,sizeof(pp));
printf("%d\n",getpid());
close(fd);
return 0;
}
使用有名管道info将写进程的pid通过读进程读取