加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

使用命名管道完成非亲缘关系进程间的通信(转)

(2013-03-19 09:25:10)
一、命名管道的概念
    管道的一个不足之处是没有名字,因此,只能用于具有亲缘关系的进程间通信,在命名管道(named pipe或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。值得注意的是,FIFO(first input first output)总是按照先进先出的原则工作,第一个被写入的数据将首先从管道中读出。
    二、命名管道的创建与读写
    Linux下有两种方式创建命名管道。一是在Shell下交互地建立一个命名管道,二是在程序中使用系统函数建立命名管道。Shell方式下可使用mknod或mkfifo命令,下面命令使用mknod创建了一个命名管道:
mknod namedpipe
    创建命名管道的系统函数有两个:mknod和mkfifo。两个函数均定义在头文件sys/stat.h,函数原型如下:
#include
#include
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);
    函数mknod参数中path为创建的命名管道的全路径名:mod为创建的命名管道的模式,指明其存取权限;dev为设备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到。这两个函数调用成功都返回0,失败都返回-1。下面使用mknod函数创建了一个命名管道:
umask(0);
if (mknod("/tmp/fifo",S_IFIFO | 0666) == -1)
{
perror("mkfifo error");
exit(1);
}
    函数mkfifo前两个参数的含义和mknod相同。下面是使用mkfifo的示例代码:
umask(0);
if (mkfifo("/tmp/fifo",S_IFIFO|0666) == -1)
{
perror("mkfifo error!");
exit(1);
}
    “S_IFIFO|0666”指明创建一个命名管道且存取权限为0666,即创建者、与创建者同组的用户、其他用户对该命名管道的访问权限都是可读可写。
    命名管道创建后就可以使用了,命名管道和管道的使用方法基本是相同的。只是使用命名管道时,必须先调用open()将其打开。因为命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。
    需要注意的是,调用open()打开命名管道的进程可能会被阻塞。但如果同时用读写方式(O_RDWR)打开,则一定不会导致阻塞;如果以只读方式(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写方打开管道;同样以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。
    下面通过例10-5和例10-6演示使用命名管道在无亲缘关系的进程间如何进行通信。这个实例包含两个程序。
    例10-5
#include
#include
#include
#include
#include
#include

#define FIFO_NAME "myfifo"
#define BUF_SIZE 1024

int main(void)
{
int fd;
char buf[BUF_SIZE];

umask(0);
fd = open(FIFO_NAME,O_RDONLY);
read(fd,buf,BUF_SIZE);
printf("Read content: %s\n",buf);
close(fd);
exit(0);
}
     例10-6
#include
#include
#include
#include
#include
#include

#define FIFO_NAME "myfifo"
#define BUF_SIZE 1024

int main(void)
{
int fd;
char buf[BUF_SIZE] = "Hello 10-6,I come from process named 10-5!";

umask(0);

if (mkfifo(FIFO_NAME,S_IFIFO|0666) == -1)
    {
      perror("mkfifo error!");
      exit(1);
    }

if ((fd = open(FIFO_NAME,O_WRONLY)) == -1)
    {
      perror("fopen error!");
      exit(1);
    }

write(fd,buf,strlen(buf)+1);

close(fd);
exit(0);

}
    程序说明:
    编译后首先运行10-6(运行后牌阻塞状态),打开另一个终端运行程序10-5。运行结果如下:
$ ./10-5
Read content: Hello 10-6,I come from process named 10-5!
    三、命名管道的应用实例/_
    通过创建两个管道可以实现进程间的全双工通信,同样也可以通过创建两人个FIFO来实现不同进程间的全双工通信。下面通过例10-7和例10-8演示一个程序中两个进程间的聊天程序(一个为server端,另一个为client端)来说明使用命名管道进行全双工通信。
    例10-7
#include
#include
#include
#include
#include
#include
#include

#define FIFO_READ "readfifo"
#define FIFO_WRITE "writefifo"
#define BUF_SIZE   1024

int main(void)
{
int wfd,rfd;
char buf[BUF_SIZE];
int len;

umask(0);
if (mkfifo(FIFO_WRITE,S_IFIFO|0666))
    {
      printf("Can't create FIFO %s because %s",FIFO_WRITE,strerror(errno));
      exit(1);
    }
umask(0);
wfd = open(FIFO_WRITE,O_WRONLY);
if (wfd == -1)
    {
      printf("open FIFO %s error:%s",FIFO_WRITE,strerror(errno));
      exit(1);
    }

while((rfd = open(FIFO_READ,O_RDONLY)) == -1)
    {
      sleep(1);
    }

while(1)
    {
      printf("Server:");
      fgets(buf,BUF_SIZE,stdin);
      if (strncmp(buf,"quit",4) == 0)
    {
      close(wfd);
      unlink(FIFO_WRITE);
      close(rfd);
      exit(0);
    }
      write(wfd,buf,strlen(buf));

      len = read(rfd,buf,BUF_SIZE);
      if (len > 0)
    {
      buf[len] = '\0';
      printf("Client: %s\n",buf);
    }
    }
}
    例10-8
#include
#include
#include
#include
#include
#include

#define FIFO_READ "writefifo"
#define FIFO_WRITE "readfifo"
#define BUF_SIZE   1024

int main(void)
{
int wfd,rfd;
char buf[BUF_SIZE];
int len;

umask(0);
if (mkfifo(FIFO_WRITE,S_IFIFO|0666))
    {
      printf("Can't create FIFO %s because %s",FIFO_WRITE,strerror(errno));
      exit(1);
    }

if ((rfd = open(FIFO_READ,O_RDONLY)) == -1)
    {
      sleep(1);
    }

wfd = open(FIFO_WRITE,O_WRONLY);
if (wfd == -1)
    {
      printf("Fail to open FIFO %S:%S", FIFO_WRITE,strerror(errno));
      exit(-1);
    }

while(1)
    {
      len = read(rfd ,buf,BUF_SIZE);
      if (len > 0)
    {
      buf[len] = '\0';
      printf("Server: %s\n",buf);
    }
    
      printf("Client:");
      fgets(buf,BUF_SIZE,stdin);
      buf[strlen(buf) -1] = '\0';
      if (strncmp(buf,"quit",4) == 0)
    {
      close(wfd);
      unlink(FIFO_WRITE);
      close(rfd);
      exit(0);
    }
      write(wfd,buf,strlen(buf));
    }
}
    程序说明:
    观察10-7和10-8两个程序,可以看出两者的实现基本是一样的,只不过对FIFO文件的读写顺序颠倒了一下,两个程序中只要定义FIFO文件名的宏的值对换一下就可以了。分别在两个终端上运行这两个程序,并在10-7端和10-8端输入数据观察它们的运行结果:
    10-7端输入输出如下:
$ ./10-7
Server:hello  
Client: world
    10-8端输入输出如下:
$ ./10-8
Server: hello

Client:world  
    从运行结果可以看出,通过两个命名管道也可以实现进程间的双向通信。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有