在六种进程间通信方式中,管道是最简单、效率最差的一种,管道属于半双工通信,即可以实现双向通信,但不能在两个方向上同时进行,必须交替进行
管道类型分为两种:匿名管道和命名管道
匿名管道:指不带名字标识的管道,用于父进程与其子进程之间的通信(即具有亲缘关系),常直接称匿名管道为管道
命名管道:指带有名字标识符的管道,支持任意两个进程之间的通信
一、管道(pipe)
1.基本介绍
管道的实质是内核缓冲区,进程以先进先出(FIFO,First In First Out)的方式从缓冲区存取数据:管道的一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的。当缓冲区读空或者写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或满的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。
局限性:
①半双工,不支持同时双向
②只支持具有亲缘关系的进程之间
③一个进程不能同时操作读、写两端
④数据为一次性可读,读后便不存在管道中,不能重复读取
2.函数介绍
(1)pipe()
#include <unistd.h>
int pipe(int fd[2]);
①函数功能:创建管道
②函数参数:fd[0]为读而打开,fd[1]为写而打开,fd[1]的输出就是fd[0]的输入
③函数返回值:成功,返回0;失败,返回-1;
3.管道编程
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MSG_STR "This message is from parent:Hello,child process!"
int main(int argc,char **argv)
{
int pipe_fd[2]; //定义读写文件描述符
int rv;
int wstatus; //保存wait()函数返回的子进程状态
char buf[1024];
pid_t pid; //定义进程号
if(pipe(pipe_fd) < 0) //先创建管道在创建进程
{
printf("create pipe failure:%s\n",strerror(errno));
return -1;
}
printf("create pipe successsfully!\n");
if((pid = fork()) < 0) //创建进程
{
printf("create child process failure:%s\n",strerror(errno));
return -1;
}
else if (pid == 0) //子进程运行
{
close(pipe_fd[1]); //子进程关闭写通道
memset(buf,0,sizeof(buf));
rv = read(pipe_fd[0],buf,sizeof(buf)); //从读端读取管道内容
if( rv < 0 )
{
printf("child process read data from pipe failure:%s\n",strerror(errno));
return -1;
}
printf("child process read %d bytes from pipe:%s\n",rv,buf);
return 0;
}
close(pipe_fd[0]); //父进程关闭读通道
if(write(pipe_fd[1],MSG_STR,sizeof(MSG_STR)) < 0) //从写端往管道写内容
{
printf("parent process write data to pipe failure:%s\n",strerror(errno));
return -1;
}
printf("parent process start waiting child process exit...\n");
wait(&wstatus);//等待子进程终止并回收
return 0;
}
二、命名管道(fifo)
1.基本介绍
匿名管道仅仅在具有亲缘关系的进程中使用,而命名管道可以在任意进程中使用,使用范围更为广。命名管道是借助文件系统实现的,文件系统中的路径名是全局的,各进程都可以访问。
2.函数介绍
(1)access()
#include <unistd.h>
int access(const char*pathname,int mode);
①函数功能:检查是否可以对某文件进行某种操作
②函数参数:pathname指文件路径名+文件名,mode在指定access的作用下,有如下值
F_OK 值为0,判断文件是否存在
X_OK 值为1,判断对文件是可执行权限
W_OK 值为2,判断对文件是否有写权限
R_OK值为4,判断对文件是否有读权限
注:后三种可以使用或“|”的方式,一起使用,如W_OK|R_OK
③函数返回值:成功,返回0 ;失败则返回-1;
(2)mkfifo()
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
①函数功能:使用指定的文件名创建FIFO(命名管道)
②函数参数:pathname指文件名,mode为该文件的权限;
③函数返回值:成功,返回0;失败,返回-1;
(3)fgets()
#include <stdio.h>
char *fgets(char *buf,int size,FILE *stream);
①函数功能:从标准流中获取字符串,当遇到‘\0’或读取字符等于(size-1)就会停止;
②函数参数:buf为存储字符串的位置;size为存储字符串的最大个数(size-1);stream指向读取的流,使用中一般写为STDIN,意为标准输入流;
③函数返回值:成功,返回字符串;错误或文件结束条件返回NULL;可以使用feof或ferror来确定是否发生错误
3.fifo编程
创建两个掩藏的命名管道文件在不同的进程间进行双向通信,最后形成两个类似聊天窗口
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <libgen.h>
#include <stdlib.h>
#define FIFO_FILE1 ".fifo_chat1"
#define FIFO_FILE2 ".fifo_chat2"
int g_stop = 0;
void sig_pipe(int signum)
{
if(SIGPIPE == signum)
{
printf("get pipe broken signal and let programe exit\n");
g_stop = 1;
}
}
int main(int argc,char **argv)
{
int fdr_fifo;
int fdw_fifo;
int rv;
int mode = 0;
char buf[1024];
fd_set rdset;
if(argc != 2) //通道选择
{
printf("please select a fifo to chat(fifo_chat 0 or 1)\n");
printf("This chat program need run twice,1st time run with [0] and 2nd run with [1]\n");
return -1;
}
mode = atoi(argv[1]); //将管道选择从字符转换为整型
if( access(FIFO_FILE1,F_OK) != 0) //判断文件是否存在(F_OK)
{
printf("%s not exist and create it now\n ",FIFO_FILE1);
mkfifo(FIFO_FILE1,0666); //创建FIFO_FILE1,打开权限
}
if( access(FIFO_FILE2,F_OK) != 0) //判断文件是否存在(F_OK)
{
printf("%s not exist and create it now\n ",FIFO_FILE2);
mkfifo(FIFO_FILE2,0666); //创建FIFO_FILE2,打开权限
}
signal(SIGPIPE,sig_pipe);
if( 0 == mode)
{
printf("start open '%s' for read and it will blocked untill write endpoint opened...\n",FIFO_FILE1);
if( (fdr_fifo=open(FIFO_FILE1, O_RDONLY)) < 0 ) //判断FIFO_FILE1是否可读
{
printf("Open fifo[%s] for chat read endpoint failure: %s\n", FIFO_FILE1, strerror(errno));
return -1 ;
}
printf("start open '%s' for write...\n", FIFO_FILE2); //FIFO_FILE2打开写通道开始写内容
if( (fdw_fifo=open(FIFO_FILE2, O_WRONLY)) < 0 ) //判断FIFO_FILE1是否可写
{
printf("Open fifo[%s] for chat write endpoint failure: %s\n", FIFO_FILE2, strerror(errno));
return -1;
}
}
else
{
printf("start open '%s' for read and it will blocked untill write endpoint opened...\n",FIFO_FILE1);
if( (fdw_fifo=open(FIFO_FILE1, O_WRONLY)) < 0 )
{
printf("Open fifo[%s] for chat write endpoint failure: %s\n", FIFO_FILE1, strerror(errno));
return -1 ;
}
printf("start open '%s' for read...\n", FIFO_FILE2); //FIFO_FILE1打开读通道开始读内容
if( (fdr_fifo=open(FIFO_FILE2, O_RDONLY)) < 0 ) //判断FIFO_FILE2是否可读
{
printf("Open fifo[%s] for chat read endpoint failure: %s\n", FIFO_FILE2, strerror(errno));
return -1;
}
}
printf("start chating with another program now,please input message now:\n");
while(!g_stop)
{
FD_ZERO(&rdset); //清空集合
FD_SET(STDIN_FILENO,&rdset); //将给定的描述符(标准输入)加入集合
FD_SET(fdr_fifo,&rdset);
rv = select(fdr_fifo+1,&rdset,NULL,NULL,NULL);
if( rv <= 0)
{
printf("select get timeout or error:%s\n",strerror(errno));
continue;
}
if(FD_ISSET(fdr_fifo,&rdset)) //判断指定描述符是否在集合中
{
memset(buf,0,sizeof(buf));
rv = read(fdr_fifo,buf,sizeof(buf));
if(rv < 0)
{
printf("read data from FIFO failure:%s\n",strerror(errno));
break;
}
else if(0 == rv)
{
printf("another side of FIFO get closed and progarm will exit now\n");
break;
}
printf("--%s",buf);
}
if(FD_ISSET(STDIN_FILENO,&rdset))
{
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin); //从标准输入读取整行输入
write(fdw_fifo,buf,strlen(buf));
}
}
}
最终实现效果:
./fifo_chat 0
start open '.fifo_chat1' for read and it will blocked untill write endpoint opened...
start open '.fifo_chat2' for write...
start chating with another program now,please input message now:
read 6 word--hello
how are you
read 19 word--i'm fine,thank you
./fifo_chat 1
start open '.fifo_chat1' for write and it will blocked untill read endpoint opened...
start open '.fifo_chat2' for read...
start chating with another program now,please input message now:
hello
read 12 word--how are you
i'm fine,thank you
another side of FIFO get closed and progarm will exit now