管道通信
1.无名管道
无名管道,是 UNIX 系统IPC最古老的形式
(1)特点
1.它是半双工的(即数据只能在一个方向上流动),具有固定读端和写端。
2.它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
3.它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。(其字节大小为0)
(2)无名管道的创建与关闭
无名管道是基于文件描述符的通信方式。当一个管道创建时,它会创建两个文件描述符:fd[0] 、fd[1] 。其中 fd[0] 固定用于读管道,而 fd[1] 固定用于写管道,如下图,这样就构成了一个单向的数据通道:
管道关闭时只需要用 close() 函数将这两个文件描述符关闭即可。
(3)函数原型
#include <unistd.h>//所需头文件
int pipe(int fd[2]);
-
参数:fd ,包含两个元素的整型数组,存放管道对应的文件描述符
-
函数返回值:成功:0,出错:-1
(4)用法说明
用pipe() 函数创建的管道两端处于一个进程中。由于管道主要是用于不同进程间的通信,通常是先创建一个管道,再调用 fork () 函数创建一个子进程,该子进程会继承父进程所创建的管道。需要注意的是,无名管道是单工的工作方式,即进程要么只能读管道,要么只能写管道。父子进程虽然都拥有管道的读端和写端,但是只能使用其中一个,若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。
示例:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
int fd[2];//存放文件描符的数组
char readBuf[128];
pid_t pid;
int readSize;
if(pipe(fd) == -1){
printf("creat pipe faild!");
exit(0);
}
pid = fork();
if(pid > 0){//父进程
printf("This is father progress the pid is %d \n",getpid());
close(fd[0]);//关闭父进程的读
write(fd[1],"hello this is father",strlen("hello this is father"));//利用管道向子进程发送信息
wait(NULL);//等待子进程退出
}
if(pid == 0){//子进程
printf("This is child progress the pid is %d \n",getpid());
close(fd[1]);//关闭子进程的写
read(fd[0],readBuf,128);
printf("the message from farherr is %s \n",readBuf);
exit(1);
}
return 0;
}
2.有名管道
有名管道(FIFO)是对无名管道的一种改进
(1)特点
1.它可以使互不相关的两个进程实现彼此通信;
2.该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当做普通文件一样进行读写操作,使用非常方便;
3.FIFO严格地遵循先进先出规则,对管道及 FIFO 的读总是从开始处返回数据,对它们的写则把数据添加到末尾。有名管道不支持如lseek()等文件定位操作;
(2)函数原型
#include <sys/stat.h>//需要引入的头文件
int mkfifo(const char *pathname, mode_t mode);
- 参数 :
pathname参数:为所创建管道的路径名称
mode 参数:与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。 - 返回值:成功返回0,出错返回-1
(3)有名管道用法
有名管道(FIFO)的创建可以使用 mkfifo() 函数,该函数类似文件中的open() 操作,可以指定管道的路径和访问权限 (用户也可以在命令行使用 “mknod <管道名>”来创建有名管道)。
在创建管道成功以后,就可以使用open()、read() 和 write() 这些函数了。与普通文件一样,对于为读而打开的管道可在 open() 中设置 O_RDONLY,对于为写而打开的管道可在 open() 中设置O_WRONLY。
-
对于读进程
缺省情况下,如果当前FIFO内没有数据,读进程将一直阻塞到有数据写入或是FIFO写端都被关闭。
-
对于写进程
只要FIFO有空间,数据就可以被写入。若空间不足,写进程会阻塞,知道数据都写入为止;
-
关于是否设置阻塞标志:
当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO -
示例
writefifo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
int fd;
int n;
char buf[1024] = {0};
if(argc < 2){
printf("输入需要创建或打开的文件名!\n");
exit(1);
}
fd = open(argv[1],O_WRONLY);//以只写的方式打开fifo
if(fd < 0){
printf("opem fail\n");
perror("why");
exit(1);
}
while(1){
printf("输入要发送的内容:");
scanf("%s",buf);
n= write(fd,buf,strlen(buf));//写入fifo的内容
if(n == -1){
perror("write fails");
}
}
return 0;
}
openfifo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
int fd;
int nread;
char buf[128] = {0};
if(argc < 2){
printf("输入需要创建或打开的文件名!\n");
exit(1);
}
if((mkfifo(argv[1], 0600)<0)&& errno != EEXIST){//其中 errno 表示错类型,这种写法可以在文件已经存在时不报错
printf("creat fifo fail\n");
perror("why");
exit(1);
}
fd = open(argv[1],O_RDONLY);
if(fd < 0){
printf("open fail\n");
perror("why");
exit(1);
}
while(1){
nread = read(fd,buf,128);//读取fifo的内容
if(nread == -1){
perror("read fails");
exit(1);
}else if(nread ==0){
printf("none from fifowrite\n");
exit(1);
}else
{
buf[nread] = '\0';//将读出的最后一个字节添加一个结束符
printf("read %d bytes from fifo:%s\n",nread,buf);
}
}
return 0;
}
- 执行结果
写端:
读端: