管道和FIFO

1.管道(pipe)

  管道在Unix及Linux进程间通信是最基础的,很容易理解。管道就像一个自来水管,一端注入水,一端放出水,水只能在一个方向上流动,而不能双向流动。管道是典型的单向通信,即计算机网络所说的“半双工”。管道又名匿名管道,所以只能用在具有公共祖先的进程之间使用,通常使用在父子进程之间通信。通常是父进程创建一个管道,然后fork一个子进程,此后父子进程共享这个管道进行通信。

管道又pipe函数创建,函数原型如下:

#include <unistd.h>

Int pipd(int fd[2]);成功返回0,否则返回-1;参数fd返回两个文件描述符,fd[0]为读,fd[1]为写,fd[1]的输入是fd[0]的输出。即fd[0]对应读端,fd[1]对应写端。

举例说明一下管道的用法:模拟client-server通信过程,父进程模拟client,子进程模拟server。Server向client发送一个字符串,client接收到输出到屏幕。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
int main()
{
	int fd[2];
	pid_t childpid;
	char buf[100];
    memset(buf, 0, 100); //进行内存初始化
	//创建一个管道
	if (pipe(fd) == -1)
	{
		perror("pipe() error");
		exit(-1);
	}
	//创建一个子进程
	childpid = fork();
	if (childpid == 0)//创建子进程成功
	{
		printf ("server input a message: ");
		scanf("%s", buf);
		//关闭读端
		close(fd[0]);
		write(fd[1], buf, strlen(buf));
		exit(0);
	}
    if (childpid == -1)
	{
		perror("fork() error");
		exit(-1);
	}
	//父进程关闭写端
	close(fd[1]);
	read(fd[0], buf, 100);
	printf("client read a message: %s\n", buf);
	waitpid(childpid, NULL, 0);
	return 0;
}

程序执行结果如下:


上面程序的细节问题在于子程序需要关闭读端,父进程需要关闭写端。因为管道最早提出时候是单向,虽然现在有些系统提供全双工管道。那么如何采用管道实现双向通信呢?很显然我们需要两个管道,控制两个不同的数据流向。现在又模拟一个Client和Server双向通信的过程,Client与Server之间可以相互发送和接受信息。此时需要两个管道进行模拟,管道1模拟Server写Client读数据流向,管道2模拟Client写Server读数据流向。代码如下所示:

#include <unistd.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <errno.h>
  #include <string.h>
  
  int main()
  {
     int fd1[2],fd2[2];
     pid_t childpid;
     char buf[100];
 
     memset(buf,0,100);
     
     if(pipe(fd1) == -1)
     {
         perror("pipe() error");
         exit(-1);
     }
     if(pipe(fd2) == -1)
     {
         perror("pipe() error");
         exit(-1);
     }
     childpid = fork();
     if(childpid == 0)
     {
         printf("Server input a message : ");
         gets(buf);
         close(fd1[0]);
         close(fd2[1]);
         write(fd1[1],buf,strlen(buf));
         read(fd2[0],buf,100);
         printf("Server received message from client:%s\n",buf);
         exit(0);
     }
     if(childpid == -1)
     {
         perror("fork() error");
         exit(-1);
     }
     close(fd1[1]);
     close(fd2[0]);
     read(fd1[0],buf,100);
     printf("Client receive a message from server: %s\n",buf);
     printf("Client input a message : ");
     gets(buf);
     write(fd2[1],buf,strlen(buf));
     waitpid(childpid,NULL,0);
     return 0;
}

程序执行结果如下:


2. FIFO(first in firstout)

  FIFO又名有名管道,相对于上述管道而言。管道没有名字,因此只能在具有共同祖先进程的各个进程之间通信,无法在无亲缘关系的两个进程之间创建一个管道进行通信。为此有了FIFO,类似管道,也是一个单向(半双工)数据流,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO。FIFO由mkfifo函数创建

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *pathname,mode_t mode); 成功返回0,出错返回-1。pathname是一个普通的路径名,是FIFO的名字,mode指定文件的权位。

在创建FIFO后,必须打开来读或者打开来写,不能打开来既读既写(因为FIFO是半双工)。现在采用FIFO实现上面的第二个例子,代码实现如下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_MODE(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"
int main()
{
   int readfd, writefd;
   pid_t childpid;
   char buf[100];
   memset(buf, 0, 100);
   //创建FIFO
   if ((mkfifo(FIFO1, FILE_MODE) <0) && (errno != EEXIST))
   {
	   perror("mkfifo() error");
	   exit(-1);
   }
    if ((mkfifo(FIFO2, FILE_MODE) <0) && (errno != EEXIST))
   {
	   unlink(FIFO1);
	   perror("mkfifo() error");
	   exit(-1);
   }
	//创建子进程
	childpid = fork();
	if (childpid == 0)
	{
		readfd = open(FIFO1,O_RDONLY,0 );
        readfd = open(FIFO2,O_WRONLY,0 );
		printf("Server input a message: ");
		gets(buf);
		write(writefd,buf,strlen(buf));
		read(readfd, buf, 100);
		printf("Server received a message from Client: %s\n", buf);
		exit(0);
	}
	if (childpid = -1)
	{
		perror("fork() error");
		exit(-1);
	}
	//防止死锁,注意顺序
    writefd = open(FIFO1,O_WRONLY,0);
     readfd = open(FIFO2,O_RDONLY,0);
     read(readfd,buf,100);
     printf("Client received a message form Server: %s\n",buf);
     printf("Client input a mesage: ");
     gets(buf);
     write(writefd,buf,strlen(buf));
     waitpid(childpid,NULL,0);
     close(readfd);
     close(writefd);
     unlink(FIFO1);
     unlink(FIFO2);
     return 0;
     
}
运行结果如下:


上面的程序当中父进程打开FIFO的顺序不能颠倒,否则会造成死锁。因为在当前没有任何进程打开某个FIFO来写的时候,打开该FIFO来读的进程将会阻塞。交换父进程中两个open的调用顺序后,父子进程都将打开同一个FIFO进行读,而当前没有任何进程来打开该文件进行写,于是父子进程都阻塞,造成死锁。

下面采用FIFO实现无亲缘关系的两个进程之间的通信。Client与Server是两个独立的进程,程序代码如下:

//公共同文件fifo.h
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
#include <sys/types.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <string.h>
 #include <fcntl.h>

#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

#define FIFO1   "/tmp/fifo.1"
#define FIFO2   "/tmp/fifo.2"



 //server进程 server.c 
 #include "fifo.h"
 
 int main()
 {
      int readfd,writefd;
 pid_t   childpid;
 char buf[100];
 memset(buf,0,100);
//创建FIFO
if((mkfifo(FIFO1,FILE_MODE) < 0) && (errno != EEXIST))
{
perror("mkfifo() error");
exit(-1);
}
if((mkfifo(FIFO2,FILE_MODE) < 0) && (errno != EEXIST))
{
unlink(FIFO1);
perror("mkfifo() error");
exit(-1);
}
readfd = open(FIFO1,O_RDONLY,0);
writefd = open(FIFO2,O_WRONLY,0);
printf("Server input a message: ");
gets(buf);
write(writefd,buf,strlen(buf));
read(readfd,buf,100);
printf("Server received a message from Client: %s\n",buf);
return 0;
}
复制代码

复制代码
 //client进程 client。c
 #include "fifo.h"
 
 int main()
 {
 int readfd,writefd;
 pid_t   childpid;
 char buf[100];
 memset(buf,0,100);
//创建FIFO
if((mkfifo(FIFO1,FILE_MODE) < 0) && (errno != EEXIST))
{
perror("mkfifo() error");
exit(-1);
}
if((mkfifo(FIFO2,FILE_MODE) < 0) && (errno != EEXIST))
{
unlink(FIFO1);
perror("mkfifo() error");
exit(-1);
}

//防止死锁,注意顺序
writefd = open(FIFO1,O_WRONLY,0);
readfd = open(FIFO2,O_RDONLY,0);
read(readfd,buf,100);
printf("Client received a message form Server: %s\n",buf);
printf("Client input a mesage: ");
gets(buf);
write(writefd,buf,strlen(buf));
close(readfd);
close(writefd);
unlink(FIFO1);
unlink(FIFO2);
return 0;
}

先执行server进程,然后执行client进程:结果如下



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值