Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5218727
  • 博文数量: 553
  • 博客积分: 13864
  • 博客等级: 上将
  • 技术积分: 11041
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-28 21:25
个人简介

个人Blog: hhktony.com

文章分类

全部博文(553)

文章存档

2015年(1)

2014年(2)

2013年(12)

2012年(384)

2011年(154)

分类: LINUX

2012-04-02 21:01:58

管道通信

普通的Linux shell都允许重定向,而重定向使用的就是管道。例如:
ps | grep vsftpd .管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进 程在管道的道端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管 道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。管道主要用于不同进程间通信。

管道创建与关闭
创建一个简单的管道,可以使用系统调用pipe()。它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。创建一个管道之后,一般情况下进程将产生一个新的进程
============================================================
系统调用:pipe();
原型:int pipe(int fd[2]);
返回值:如果系统调用成功,返回0。如果系统调用失败返回-1:
errno=EMFILE(没有空清的文件描述符)
EMFILE(系统文件表已满)
EFAULT(fd数组无效)
注意:fd[0]用于读取管道,fd[1]用于写入管道。
============================================================
管道的创建

点击(此处)折叠或打开

  1. #include<unistd.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>

  4. int main(int argc, char *argv[])
  5. {
  6.     int pipe_fd[2];
  7.     if (pipe(pipe_fd) < 0) {
  8.         printf("pipe create error\n");
  9.         return -1;
  10.     } else
  11.         printf("pipe create success\n");
  12.     close(pipe_fd[0]);
  13.     close(pipe_fd[1]);
  14. }
管道的读写
管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过fork函数创建一个子进程。
子进程写入和父进程读
管道读写注意事项:
可以通过打开两个管道来创建一个双向的管道。但需要在子理程中正确地设置文件描述符。必须在系统调用fork()中调用pipe(),否则子进程将不会继承文件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中 的进程都将无法寻址它。而在命名管道中却不是这样。
管道实例:pipe_rw.c

点击(此处)折叠或打开

  1. #include<unistd.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. int main(int argc char *argv[])
  5. {
  6.     int pipe_fd[2];
  7.     pid_t pid;
  8.     char buf_r[100];
  9.     char *p_wbuf;
  10.     int r_num;
  11.     memset(buf_r, 0, sizeof(buf_r));/*数组中的数据清0*/

  12.     if (pipe(pipe_fd) < 0) {
  13.         printf("pipe create error\n");
  14.         exit(1);
  15.     }
  16.     if ((pid = fork()) == 0) {
  17.         printf("\n");
  18.         close(pipe_fd[1]);
  19.         sleep(2);
  20.         if ((r_num = read(pipe_fd[0], buf_r, 100)) > 0) {
  21.             printf("%d numbers read from be pipe is %s\n",
  22.              r_num, buf_r);
  23.         }
  24.         close(pipe_fd[0]);
  25.         exit(0);
  26.     } else if (pid > 0) {
  27.         close(pipe_fd[0]);
  28.         if (write(pipe_fd[1], "Hello", 5) != -1)
  29.             printf("parent write success!\n");
  30.         if (write(pipe_fd[1], " Pipe", 5) != -1)
  31.             printf("parent wirte2 succes!\n");
  32.         close(pipe_fd[1]);
  33.         sleep(3);
  34.         waitpid(pid, NULL, 0);
  35.         exit(0);
  36.     }

  37.     exit(0);
  38. }
标准流管道
与linux中文件操作有文件流的标准I/O一样,管道的操作也支持基于文件流的模式。接口函数如下:
================================================================================
库函数:popen();
原型:FILE *open (char *command, char *type);
返回值:如果成功,返回一个新的文件流。如果无法创建进程或者管道,返回NULL。管道中数据流的方向是由第二个参数type控制的。此参数可以是r或者w,分别代表读或写。但不能同时为读和写。在Linux 系统下,管道将会以参数type中第一个字符代表的方式打开。所以,如果你在参数type中写入rw,管道将会以读的方式打开。
================================================================================
使用popen()创建的管道必须使用pclose()关闭。其实,popen/pclose和标准文件输入/输出流中的fopen()/fclose()十分相似。
================================================================================
库函数:pclose();
原型:int pclose(FILE *stream);
返回值:返回系统调用wait4()的状态
如果stream无效,或者系统调用wait4()失败,则返回-1。注意此库函数等待管道进程运行结束,然后关闭文件流。库函数pclose()在使用popen()创建的进程上执行wait4()函数,它将破坏管道和文件系统、。
================================================================================
流管道的例子

点击(此处)折叠或打开

  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<fcntl.h>
  5. #define BUFSIZE 1024

  6. int main(int argc, char *argv[])
  7. {
  8.     FILE *fp;
  9.     char *cmd = "ps -ef";
  10.     char buf[BUFSIZE];
  11.     buf[BUFSIZE] = '\0';
  12.     if ((fp = popen(cmd, "r")) == NULL)
  13.         perror("popen");
  14.     while ((fgets(buf, BUFSIZE, fp)) != NULL)
  15.         printf("%s", buf);
  16.     pclose(fp);
  17.     exit(0);
  18. }
命名管道(FIFO)
基本概念
命名管道和一般的管道基本相同,但也有一些显著的不同:
A、命名管道是在文件系统中作为一个特殊的设备文件而存在的
B、不同祖先的进程之间可以通过管道共享数据
C、当共享管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中以便以后使用

管道只能由相关进程使用,它们共同的祖先进程创建了管道。但是,通过FIFO,不相关的进程也能交换数据。

命名管道创建
=============================================================================
#include
#include
int mkfifo(const char *pathname,mode_t mode);

返回:若成功则为0,若出错返回-1
一旦已经用mkfifo创建了一个FIFO,就可用open打开它。确实,一般的文件I/O函数(close,read,write,unlink等)都可用于FIFO。当打开一个FIFO时,非阻塞标(O_NONBLOCK)产生下列影响:
(1)在一般情况中(没有说明O_NONBLOCK),只读打开要阻塞到某个其他进程为写打开此FIFO。类似,为写而打开一个FIFO要阻塞到某个其他进程为读而打开它。
(2)如果指一了O_NONBLOCK,则只读打开立即返回。 但是,如果没有进程已经为读而打开一个FIFO,那么只写打开将出错返回,其errno是ENXIO。类似于管道,若写一个尚无进程为读而打开的 FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。
FIFO相关出错信息:
EACCES(无存取权限)
EEXIST(指定文件不存在)
ENAMETOOLONG(路径名太长)
ENOENT(包含的目录不存在)
ENOSPC(文件系统余空间不足)
ENOTDIR(文件路径无效)
EROFS(指定的文件存在于只读文件系统中)
=============================================================================
命名管道实例:fifo_write.c

阅读(3549) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~