一 有名管道的概述
- 主要用于没有血缘关系的进程间通信;
- 有名管道实际上是一块物理内存;无名管道是逻辑内存;
二 有名管道的特点
1. 半双工,数据只能在一个方向上流动;
2. 写入FIFO中的数据遵循先入先出的规则;
3. FIFO所传输的数据是无格式的,这要求FIFO的读出方与写入方必须事先约定好数据格式,如多少字节算一个消息等;
4. FIFO在文件系统中作为一个特殊文件存在,但FIFO中的内容是存放在内存中的;
5. 管道在内存中对应一个缓冲区,不同的系统其大小不一定相同;
6. 从FIFO读数据是一次性操作,读完就从FIFO中被抛弃;
7. 当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用;
8. FIFO有名字,不相关的进程可以通过打开命名管道进行通信
三 有名管道的API
3.1 创建有名管道
- 需要的头文件和函数原型
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
- 参数
pathname:FIFO的路径名+文件名
mode:权限描述符
- 返回值
成功:返回0
失败:如果文件已经存在,则会出错并返回-1
四 有名管道案例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int arg, char *argv[])
{
//创建有名管道
mkfifo("/root/myfifo1",0666);
#ifdef READ
int fd = open("/root/myfifo1",O_RDONLY);
#endif
#ifdef WRITE
int fd = open("/root/myfifo1",O_WRONLY);
#endif
if(fd<0)
{
perror("open");
return 0;
}
#ifdef READ
printf("读端open成功了\n");
while(1)
{
char buf[1024] = "";
read(fd,buf,sizeof(buf));
printf("收到数据:%s\n",buf);
if(strcmp(buf,"bye") == 0)
break;
}
#endif
#ifdef WRITE
printf("写端open成功了\n");
while(1)
{
printf("请输入:");
char buf[1024] = "";
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = 0;
write(fd,buf,strlen(buf));
if(strcmp(buf,"bye") == 0)
break;
}
#endif
close(fd);
return 0;
}
五 有名管道读写特点
以阻塞方式打开管道:
1. open函数以只读方式打开有名管道时,要阻塞直到有其他进程以写方式打开这个管道;
2. open函数以只写方式打开有名管道时,要阻塞直到有其他进程以读方式打开这个管道;
3. 调用read函数从有名管道读数据时,read会阻塞;
4. 通信过程中如果写进程先退出,则调用read函数从有名管道读数据时不阻塞;如果写进程又重新运行,则调用read函数又恢复阻塞;
5. 读进程退出后,写进程向管道写时会收到SIGPIPE信号而退出;
6. 调用write函数向管道写数据时,如果缓冲区满了,write会阻塞
以非阻塞方式打开管道:
7. 先以只读方式打开:如果没有进程已经为写而打开一个FIFO,只读open成功,而且open不阻塞;
8. 先以只写方式打开:如果没有进程已经为读而打开一个FIFO,只写open将出错返回-1;
9. read,write读写命名管道中数据时不堵塞;
4.通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会收到SIGPIPE信号而退出;
注意: open函数以可读可写方式打开FIFO文件时的特点:
1. open不阻塞;
2.调用read函数从FIFO里读取数据时read会阻塞;
3. 调用write函数向FIFO里写数据,当缓冲区已满时write也会阻塞
六 命名管道实例-单机qq聊天
实现单机QQ聊天。提示:父进程创建子进程,实现多任务。父进程负责发消息(向FIFO里写数据),子进程负责接收信息(从FIFO里读数据)。打开命名管道用阻塞方式打开。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int arg, char *argv[])
{
//创建有名管道
mkfifo("./andytolinda",0666);
mkfifo("./lindatoandy",0666);
//创建两个子进程用管道通信
int i = 0;
for(i=0;i<2;i++)
{
pid_t pid = fork();
if(pid == 0)
break;
}
if(i==0) //子进程1 负责写
{
#ifdef andy
int fd = open("./andytolinda",O_WRONLY);
#endif
#ifdef linda
int fd = open("./lindatoandy",O_WRONLY);
#endif
if(fd==-1)
{
perror("open");
return 1;
}
while(1)
{
#ifdef andy
printf("\rAndy: ");
#endif
#ifdef linda
printf("\rLinda: ");
#endif
char buf[128]="";
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
write(fd,buf,strlen(buf));
if(strcmp(buf,"bye")==0)
break;
}
close(fd);
_exit(0);
}
else if(i == 1) //子进程2 负责读
{
#ifdef andy
int fd = open("./lindatoandy",O_RDONLY);
#endif
#ifdef linda
int fd = open("./andytolinda",O_RDONLY);
#endif
if(fd==-1)
{
perror("open");
return 1;
}
while(1)
{
char buf[128]="";
read(fd,buf,sizeof(buf));
#ifdef andy
printf("\rLinda said: %s\n\rAndy:",buf);
#endif
#ifdef linda
printf("\rAndy said: %s\n\rLinda:",buf);
#endif
if(strcmp(buf,"bye")==0)
break;
}
close(fd);
_exit(0);
}
else if(i == 2) //父进程
{
while(1)
{
pid_t pid = wait(NULL);
if(pid > 0)
printf("主进程回收了子进程:%d\n",pid);
else if(pid < 0)
break;
}
}
return 0;
}