匿名管道
管道是一种简单的进程通信(IPC)机制。管道实质上就是pipe函数在内核中开辟了一段缓冲区,有一个读端和一个写端。两个进程之间能够通信的本质:通过fork函数传递文件描述符(子进程是父进程的副本,父进程所有打开的文件描述符都被复制到子进程中,父子进程的每个相同的打开描述符共享一个文件表项)使得两个进程可以访问同一个管道,从而实现通信。
特点:
- 单向通信(若实现双向通信,必须建立两个管道);
- 适用于有血缘关系的进程之间,尤其指父子进程;
- 面向数据流的服务;
- 生命周期随进程;
使用管道(阻塞I/O操作)的特殊情况:
- 写端都关闭,读端读取完后返回零;
- 写端未关闭但不进行写入,读端读取数据后陷入阻塞,直至有数据写入需要读取;
- 读端都关闭,在写端进行写入数据的程序会收到SIGPIPE信号,使得程序异常退出;
- 读端未关闭但不进行读取,写端将缓冲区写满后陷入阻塞。
注:设置O_NONBLOCK标志,可设置为非阻塞。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int pipefd[2] = {0,0};
if(pipe(pipefd)<0)
{
perror("pipe");
exit(1);
}
pid_t id = fork();
if(id < 0)
{
perror("fork");
exit(2);
}
else if(id == 0)
{
//child
close(pipefd[0]);
char* const msg = "hello bit";
int count = 5;
while(count)
{
write(pipefd[1],msg,strlen(msg));
count--;
}
close(pipefd[1]);
exit(3);
}
else
{
//father
close(pipefd[1]);
int count = 5;
char buf[1024];
while(count)
{
ssize_t s = read(pipefd[0],buf,sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
printf("%s\n",buf);
}
else
{}
count--;
}
close(pipefd[0]);
exit(5);
}
return 0;
}
命名管道
命名管道是为了解决匿名管道只适用于有血缘关系的进程之间通信的缺陷而设计的。不同于匿名管道的是它提供了一个路径名与其相连,以FIFO的文件形式存在于文件系统中。这样即使两进程不存在血缘关系,但也可以通过访问该路径,实现彼此相互通信。
命名管道的创建
- 在shell下交互地建立一个命名管道;
命令:mknod namedpipe - 使用系统函数mkfifo/mknod;
int mkfifo(const char* path,mode_t mod,dev_t dev);
其中path是创建的命名管道的全路径名;mod为创建的命名管道的模式,指明其权限;dev为设备值,该值取决于文件创建的种类。
mkfifo(“/code/fifo”,S_IFIFO|0666) ; //第二个参数指明创建一个命名管道且存取权限为0666。(注意umask对生成的管道文件权限的影响)
命名管道使用前,必须先调用open()将其打开,需要注意的是该进程可能被阻塞,但是如果使用读写方式打开,则一定不会导致阻塞。
我们也可以使用下面函数创建命名管道:
mkfifo(const char* filename,mode_t mode); //创建一个真实存在于文件系统中的文件。
两种管道的比较:
- 命名管道是一个存在于硬盘上的FIFO文件,而匿名管道是存在于内存的特殊文件;
- 命名管道是双向通信,匿名管道是单向通信;
- 命名管道可用于网络通信;
- 命名管道有两种模式(字节流/字节),匿名管道是面向数据流的;
- 两者的生命周期都是随进程(缺陷);
- 两者的每条消息的最大长度都是有上限的(缺陷);
//client
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
umask(0);
if(mkfifo("./fifo",0666|S_IFIFO) < 0)
{
perror("mkfifo");
exit(1);
}
int fd = open("./fifo",O_RDONLY);
if(fd < 0)
{
perror("open");
exit(2);
}
char buf[128];
while(1)
{
ssize_t s = read(fd,buf,sizeof(buf)-1);
if(s < 0)
{
perror("read");
exit(3);
}
else if(s == 0)
{
printf("client quie,I should quit\n");
break;
}
else
{
buf[s] = 0;
printf("%s",buf);
}
}
return 0;
}
//server
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
int fd = open("./fifo",O_WRONLY);
if(fd < 0)
{
perror("open");
exit(1);
}
while(1)
{
printf("please enter:");
fflush(stdout);
char buf[128];
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s < 0)
{
perror("read");
exit(2);
}
buf[s] = 0;
write(fd,buf,sizeof(buf)-1);
}
close(fd);
return 0;
}