两个程序之间传递数据的一种简单方法是使用popen和pclose。

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
    popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串是要运行的程序名和相应的参数。type必须是"r"或"w"。如果type是"r",被调程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE *文件流指针,可以读取被调程序的输出;如果type是"w",调用程序就可以向被调程序发送数据,而被调程序可以在自己的标准输入上读取这些数据。pclose函数只在popen启动的进程结束后才返回。如果调用pclose时它仍在运行,pclose将等待该进程的结束。
#include <stdio.h>#define SIZE 1024*100int main()
{FILE *fp = popen("ps -ef", "r");if (fp == NULL){perror ("popen");return -1;}char buf[SIZE] = {0};int ret = fread(buf, sizeof(char), SIZE-1, fp);// printf ("读到的数据:\n %s\n", buf);FILE *fp2 = popen("grep a.out", "w");if (fp2 == NULL){perror ("popen");return -1;}fwrite (buf, sizeof(char), ret, fp2);printf ("写入完成\n");pclose (fp);pclose (fp2);return 0;
}

管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道尾部写入数据,另一个进程(读进程)从管道的头部读出数据。管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。
无名管道由pipe( )函数创建:

int pipe(int filedis[2]);

当一个管道建立时,它会创建两个文件描述符:filedis[0]fi用于读管道,ledis[1]
用于写管道。

管道用于不同进程间通信。通常先创建一个管道,在通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道描述符。必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符。

1、单个进程中的管道

#include <stdio.h>
#include <unistd.h>#define SIZE 1024*100int main()
{int fd[2];int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}ret = write (fd[1], "hello", 5);printf ("写入 %d 个字节\n", ret);char ch;while (1){// 如果管道里面没有数据可读,read会阻塞ret = read (fd[0], &ch, 1);if (ret == -1){perror ("read");break;}printf ("读到 %d 字节: %c\n", ret, ch);}close (fd[0]);close (fd[1]);return 0;
}

2、父子进程通过管道进行通信

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>#define SIZE 1024// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{// 将管道的写端关闭close (fd[1]);char buf [SIZE];while (1){// 从父进程读取数据int ret = read (fd[0], buf, SIZE-1);if (ret == -1){perror ("read");break;}buf[ret] = '\0';printf ("子进程读到 %d 字节数据: %s\n", ret, buf);}// 关闭读端close (fd[0]);
}// 父进程通过管道向子进程发送数据
void father_do(int *fd)
{// 将管道读端关闭close (fd[0]);char buf[SIZE];while (1){fgets (buf, SIZE, stdin);// 向子进程发送数据int ret = write (fd[1], buf, strlen(buf));printf ("父进程发送了 %d 字节数据\n", ret);}// 关闭写端close (fd[1]);
}int main()
{int fd[2];// 创建管道int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}// 创建子进程pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0:   // 子进程child_do(fd);break;default:father_do(fd);break;}return 0;
}

3、父子进程通过管道实现文件复制

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>#define SIZE 1024// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{// 将管道的写端关闭close (fd[1]);int fd_write = open ("2.mmap", O_WRONLY|O_CREAT, 0777);if (fd_write == -1){perror ("open");return;}int ret;char buf [SIZE];// read 从管道读数据,如果管道没有数据可读,read 会阻塞// 如果 管道的写端 被关闭, read 返回 0while (ret = read (fd[0], buf, SIZE)){if (ret == -1){perror ("read");break;}// 把从父进程接收的数据写入到新文件中write (fd_write, buf, ret);}printf ("文件复制完成\n");// 关闭读端close (fd[0]);close (fd_write);
}// 父进程通过管道向子进程发送数据
void father_do(int *fd)
{// 将管道读端关闭close (fd[0]);int fd_read = open ("1.mmap", O_RDONLY);if (fd_read == -1){perror ("open");return;}int ret;char buf[SIZE];while (ret = read (fd_read, buf, SIZE)){if (ret == -1){perror ("read");break;}// 把读到的内容发送给子进程write (fd[1], buf, ret);}// 关闭写端close (fd[1]);close (fd_read);
}int main()
{int fd[2];// 创建管道int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}// 创建子进程pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0:   // 子进程child_do(fd);break;default:father_do(fd);break;}return 0;
}

4、管道读端关闭,写端继续写数据

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>#define SIZE 1024// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{close (fd[1]);close (fd[0]);
}void father_do(int *fd)
{// 将管道读端关闭close (fd[0]);printf ("等待子进程关闭读端\n");sleep(2);// 所有读端都关闭了,写端继续往管道写入数据// 如果管道所有的读端都被关闭,继续写数据系统默认的操作是使程序退出write (fd[1], "hello", 5);printf ("11111111111111111111111111111111\n");// 关闭写端close (fd[1]);
}int main()
{int fd[2];// 创建管道int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}// 创建子进程pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0:   // 子进程child_do(fd);break;default:father_do(fd);break;}return 0;
}

以上是无名管道常用的一些操作。

命名管道(FIFO)和无名管道基本相同,但也有不同点:无名管道只能由父子进程使用;但是通过命名管道,不相关的进程也能交换数据。

命名管道具有很好的使用灵活性,表现在:

1) 既可用于本地,又可用于网络。

2) 可以通过它的名称而被引用。

3) 支持多客户机连接。

4) 支持双向通信。

5) 支持异步重叠I/O操作。

1、创建命名管道

#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);

pathname: FIFO文件名
mode:属性(同文件操作)

一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>int main()
{int ret = mkfifo("/home/mkfifo", 0777);if (ret == -1){perror ("mkfifo");return -1;}return 0;
}

2、命名管道的传输
当打开FIFO时,非阻塞标识(O_NONBLOCK)将对以后的读写产生影响:
1、没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如果试图读取空的FIFO,将导致进程阻塞。
2、使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回。errno是ENXIO。

写入:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>#define SIZE 1024int main()
{int fd = open("/home/mkfifo", O_WRONLY);if (fd== -1){perror ("mkfifo");return -1;}char buf[SIZE];while (1){fgets (buf, SIZE, stdin);write (fd, buf, strlen(buf));}return 0;
}

读取:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#define SIZE 1024int main()
{int fd = open("/home/mkfifo", O_RDWR);if (fd == -1){perror ("mkfifo");return -1;}char buf[SIZE];while (1){int ret = read (fd, buf, SIZE);buf[ret] = '\0';printf ("读到 %d 字节: %s\n", ret, buf);}return 0;
}

管道作为进程间通信的4种方式之一,他并不会保存数据,区别与共享内存。

进程间通信之管道通信相关推荐

  1. Linux系统无名管道通信实验,Linux进程间通信(二)---管道通信之无名管道及其基础实验...

    管道简介 管道是Linux中进程间通信的一种方式,它把一个程序的输出直接连接到另一个程序的输入(其实我更愿意将管道比喻为农村浇地的管子).Linux的管道主要包括两种:无名管道和有名管道.这一节主要讲 ...

  2. Linux进程间通信之管道通信

    一.管道通信 管道的通信方式分为无名管道和有名管道,无名管道可用于具有亲缘关系的进程间的通信,有名管道克服了管道没有名字的限制. 管道是Linux支持的最初UNIX IPC形式之一,具有以下特点. 管 ...

  3. c语言程序实现进程的管道通信,C 进程间通信--命名管道通信代码实现及其原理图示...

    在将这个题目之前大家需要了解几个概念: 进程: 我们可以先看进程的定义:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础: ...

  4. linux程序间管道通信,linux进程间通信——管道 详解

    管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入.常说的管道多是指无名管道, 无名管道只能用于具有亲缘关系的进程之间,这是它与有名管道的最大区别. 有名管道叫nam ...

  5. linux下进程间管道通信,Linux下进程间通信方式-管道

    本文关键字: linux 管道通信,linux 进程通信方式,无名管道,有名管道 管道是Linux中进程间通信的一种方式,它把一个程序的输出直接连接到另一个程序的输入.Linux的管道主要包括两种:无 ...

  6. linux内核剖析(八)进程间通信之-管道

    管道 管道是一种两个进程间进行单向通信的机制. 因为管道传递数据的单向性,管道又称为半双工管道. 管道的这一特点决定了器使用的局限性.管道是Linux支持的最初Unix IPC形式之一,具有以下特点: ...

  7. CreatePipe匿名管道通信

    管道(Pipe)实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机.一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来.匿名管道(An ...

  8. 进程间通信线程间通信

    一.多进程: 首先,先来讲一下fork之后,发生了什么事情. 由fork创建的新进程被称为子进程(child process).该函数被调用一次,但返回两次.两次返回的区别是子进程的返回值是0,而父进 ...

  9. IPC 进程间通信方式——管道

    进程间通信概述 数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到. 通知时间: ...

最新文章

  1. 如何让图片开口说话 3DMeNow教程
  2. Qt安装后配置环境变量(Mac)
  3. swoole process进程 多分发
  4. c#用webkit内核支持html5
  5. java 图片组合 分解_切分和组合图片(二)
  6. 买得起修不起?华为Mate X 5G维修价格公布:被吓到了
  7. 微信小程序头部导航栏自定义
  8. 通过asp.net 短信猫发短信
  9. ExtAspNet v2.0.6发布 - AJAX性能提升
  10. HDU2206 IP的计算【文本处理】
  11. qt label显示图片_Qt官方示例-QML Axes
  12. Oracle删除重复记录三种方法
  13. 2021-09-09394. 字符串解码 栈
  14. 自动控制原理复习第七章——非线性系统分析
  15. linux中oracle数据乱码,Linux环境解决Oracle 中文乱码
  16. 数据结构 查找 的思维导图
  17. 2019-2020记罗振宇“时间的朋友”跨年演讲(二)
  18. 解决“连接U8数据库服务器失败”的方法尝试
  19. c# WPF中System.Windows.Interactivity的使用
  20. JAVA 网络聊天程序设计与实现(附关键代码) 可群聊私聊发送图片

热门文章

  1. mysql居左查询abcd_数据库--查询语句
  2. C# linq Expression left join如何使用
  3. log4j 标准配置模板:
  4. leetcode191. 位1的个数
  5. 顺序结构实现【数据结构】
  6. Date类的构造方法以及成员方法220
  7. Redis:15---键迁移(move、dump、restore、migrate)
  8. 算法入门篇八 贪心算法
  9. 十大教养,让你气度非凡!
  10. 10岁起编程,并不认为自己是“黑客”