进程间通信之管道通信
来源:互联网 发布:单片机是什么专业 编辑:程序博客网 时间:2024/05/21 08:59
两个程序之间传递数据的一种简单方法是使用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 返回 0 while (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种方式之一,他并不会保存数据,区别与共享内存。
- [进程通信] 进程间通信 之 管道
- 进程间通信之管道通信
- Linux进程间通信之管道通信
- Linux进程间通信之管道通信
- 进程间通信之匿名管道通信
- 进程间通信之管道通信
- 进程间通信之管道
- 进程间通信之 管道
- 进程间通信 之 管道
- 进程间通信之管道
- 进程间通信之管道
- 进程间通信之 管道
- 进程间通信之管道
- 进程间通信之管道
- 进程间通信之管道
- 进程间通信之管道
- 进程间通信之管道
- 进程间通信之管道
- 自定义View实现视差特效
- matplotlib 详解3 面向对象
- JAVA企业面试题精选 Spring 11-14
- Linux学习笔记_系统输入输出标准(重定向,>,>>,<,2>&1,xargs)
- Android 设置app字体不受Android系统控制
- 进程间通信之管道通信
- C++ MFC 简单 串口通信
- PyOpenPose编译与使用
- 树莓派的PWM调控知识
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">用处
- php微信消息单人推送
- Oracle sql语句教学
- 【Spring】DispatcherServlet的启动和初始化
- MindManager,818点燃一夏