目录
管道(fifo)
命名管道的定义和特点
管道与命名管道的区别
说明
代码示例
运行结果
管道是一种两个进程间进行单向通信的机制。因为管道传递数据的单向性,管道又称为半双工管道。管道的这一特点决定了器使用的局限性。管道是Linux支持的最初Unix IPC形式之一,具有以下特点:
POSIX标准中的FIFO又名有名管道或命名管道。我们知道前面讲述的POSIX标准中管道是没有名称的,所以它的最大劣势是只能用于具有亲缘关系的进程间的通信。FIFO最大的特性就是每个FIFO都有一个路径名与之相关联,从而允许无亲缘关系的任意两个进程间通过FIFO进行通信。所以,FIFO的两个特性:
前面讲到的未命名的管道只能在两个具有亲缘关系的进程之间通信,通过命名管道(Named PiPe)FIFO,不相关的进程也能 交换数据。FIFO不同于管道之处在于它提供一个路径与之关联,以FIFO的文件形式存在于系统中。它在磁盘上有对应的节点,但 没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建 立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被 进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。
下面这个例程创建了两个掩藏的命名管道文件(.fifo1和.fifo2)在不同的进程间进行双向通信。该程序需要运行两 次(即两个进程),其中进程0(flag=0)从标准输入里读入数据后通过命名管道2(.fifo2)写入数据给进程 1(flag=1);而进程1(flag=1)则从标准输入里读入数据后通过命名管道1(.fifo1)写给进程0,这样使用命名管 道实现了一个进程间聊天的程序。
flag =0
falg = 1
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO_FILE1 ".fifo1"
#define FIFO_FILE2 ".fifo2"
#define BUF_SIZE 1024
int g_stop = 0;
void signal_pipe(int signum) //捕捉SIGPIPE信号
{
if (SIGPIPE == signum)
{
printf("Get pipe broken signal and let porgrame exit...\n");
g_stop = 1;
}
}
int main(int argc, char *argv[])
{
int fdr_fifo = -1;
int fdw_fifo = -1;
int rv = -1;
fd_set rdset;
char buf[BUF_SIZE];
int flag = -1;
if (argc != 2)
{
printf("Usage: %s[0/1]\n", basename(argv[0]));
printf("This program need run twice, first time is running with[0] and the second time is runing with[1]...\n");
return -1;
}
flag = atoi(argv[1]);
if (access(FIFO_FILE1, F_OK)) //判断FIFO_FILE1 是否存在
{
printf("Fifo file \"%s\" not exist and will create it now.\n", FIFO_FILE1);
mkfifo(FIFO_FILE1, 06666);
}
if (access(FIFO_FILE2, F_OK))
{
printf("Fifo file \"%s\" not exist and will create it now.\n", FIFO_FILE2);
mkfifo(FIFO_FILE2, 06666);
}
signal(SIGPIPE, signal_pipe); ///注册信号函数
if (0 == flag)
{
/*
* 在这里打开的命名管道FIFO_FILE1默认是阻塞模式,
* 如果命名管道的写端不打开open会一直阻塞在这里;
*
* 如果在这里打开FIFO_FILE1的读端,则另一个程序必须打开FIFO_FILE1的写端,
* 否则会出现死锁,反之亦然
*
* */
printf("Start open '%s'for read and it will bolcked here untill write pipe opened...\n", FIFO_FILE1);
if ( (fdr_fifo = open(FIFO_FILE1, O_RDONLY)) < 0) //以只读的形式打开FIFO_FILE1的读端
{
printf("Open fifo[%s] for read error: %s\n", FIFO_FILE1, strerror(errno));
return -1;
}
printf("Start open '%s' for write ...\n", FIFO_FILE2);
if ( (fdw_fifo = open(FIFO_FILE2, O_WRONLY)) < 0)//以只写的形式打开FIFO_FILE2的写端
{
printf("Open fifo[%s] for write error: %s\n", FIFO_FILE2, strerror(errno));
return -1;
}
}
else
{
printf("Start open '%s'for write and it will bolcked here untill write pipe opened...\n", FIFO_FILE1);
if ( (fdw_fifo = open(FIFO_FILE1, O_WRONLY)) < 0) //以只写的形式打开FIFO_FILE1的写端
{
printf("Open fifo[%s] for write error: %s\n", FIFO_FILE1, strerror(errno));
return -1;
}
printf("Start open '%s' for read ...\n", FIFO_FILE2);
if ( (fdr_fifo = open(FIFO_FILE2, O_RDONLY)) < 0)//以只读的形式打开FIFO_FILE2的读端
{
printf("Open fifo[%s] for read error: %s\n", FIFO_FILE2, strerror(errno));
return -1;
}
}
printf("Start chating with another program now, enter the messsage you want to send:\n");
while(!g_stop)
{
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO, &rdset); //标准输入加入道select的监听列表
FD_SET(fdr_fifo, &rdset); //命名管道的读端加入道select的监听列表
rv = select(fdr_fifo + 1, &rdset, NULL, NULL, NULL);//使用select的多路复用分别监听标准输入和命名管道的读端
if (rv <= 0)
{
printf("Select get timeout or error: %s\n", strerror(errno));
continue;
}
if (FD_ISSET(fdr_fifo, &rdset)) //如果是fdr_fifo事件到来,则进行命名管道的读操作
{
memset(buf, 0, BUF_SIZE);
rv = read(fdr_fifo, buf,sizeof(buf));
if ( rv< 0)
{
printf("Read form fifo get error: %s\n", strerror(errno));
break;
}
else if (0 == rv)
{
printf("Another side of fifo get closed and program will exit now.\n");
break;
}
printf("<---------%s\n", buf);
}
if (FD_ISSET(STDIN_FILENO, &rdset)) //如果是标准输入的事件发生,则进行写操作
{
memset(buf, 0, BUF_SIZE);
fgets(buf, BUF_SIZE, stdin);
write(fdw_fifo, buf, strlen(buf));
}
}
printf("The fifo file will delete\n");
system("rm -r .fifo*");//删除产生的fifo文件
return 0;
}
注:本代码涉及到select的多路复用的使用,大家有不懂的可以参考博客:https://blog.csdn.net/qq_44045338/article/details/104879888