管道
匿名管道
简介
管道是UNIX系统中最古老的IPC形式,简单来说,管道是进程之间最简单的通信方式.
局限性
- 历史上,管道是半双工的,也就是说数据只能在一个方向上流动.比如我们指定了管道x用于a和b之间的通信,a为写入进程,b为读取进程.那么此时b是无法写入信息传送给a的(下文会有说明).现在某些系统提供了全双工管道,但是考虑到可移植性的问题,我们不应假设系统支持.
- 使用场景有限.因为管道是进程创建并存在于内核中的,所以管道并不能跨越两个没有亲缘关系的进程,即它们不是同属相同的祖先进程.
例子
要求:父进程读取路径并传送给子进程,子进程读出文件内容并传送给父进程.
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <limits.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<sys/types.h>
#define bufSize 65535
void client(int,int);
void server(int,int);
int main(int argc, char const *argv[]) {
//fd[0]为读入端,fd[1]为写入端
int pipeForPath[2];
int pipeForContent[2];
pid_t childpid;
pipe(pipeForPath);
pipe(pipeForContent);
if((childpid = fork()) == 0) {
//关闭Content的读以及Path的写
close(pipeForContent[0]);
close(pipeForPath[1]);
client(pipeForPath[0], pipeForContent[1]);
exit(0);
}
//关闭Content的写以及Path的读
close(pipeForContent[1]);
close(pipeForPath[0]);
server(pipeForContent[0], pipeForPath[1]);
//等待子进程退出
waitpid(childpid, NULL, 0);
exit(0);
}
void client(int readfd, int writefd) {
int fd;
size_t n;
char readBuf[bufSize];
char path[PATH_MAX + 1];
//从管道中读取路径,出错则退出
if((n = read(readfd, path, PATH_MAX + 1)) == 0){
printf("Read Error\n");
exit(0);
}
path[n] = '\0';
//打开指定文件,出错则写入错误信息并退出
if((fd = open(path, O_RDONLY)) < 0){
char error[] = "Open File Error\n";
write(writefd, error, strlen(error));
exit(0);
}
//将文件内容读出并写入管道
while((n = read(fd, readBuf, bufSize-1)) != 0) {
write(writefd, readBuf ,n);
}
close(fd);
}
void server(int readfd, int writefd) {
size_t length,n;
char path[PATH_MAX+1];
char readBuf[bufSize];
printf("Please enter the file path: ");
scanf("%s", path);
length = strlen(path);
//向管道中写入路径
write(writefd, path, length);
//读取管道的返回值并输出到控制台.
while((n = read(readfd, readBuf, bufSize)) > 0)
printf("%s",readBuf);
}
全双工管道
全双工管道在某些操作系统中已经实现,其原理其实很简单,就是在全双工管道中"藏着"两条半双工管道.
假设我们创建了一条全双工管道A,A中包含着半双工管道a和b.此时fd[0]的read是从管道a中读取,而fd[0]的write是写入管道b.fd[1]则与fd[0]相反.
通过这种方式我们就实现了全双工管道.
popen和pclose
首先来看一下两个函数的函数原型.
#include <stdio.h>
FILE *popen(const char *command, const char *type)
//成功则返回描述符,失败则返回NULL
int pclose(FILE *stream)
//成功则为Shell的终止状态,出错则为-1
- popen:
- popen打开一个进程,通过shell执行我们传递的command命令.
- type参数指明了我们对于进程输入/输出的操作方式
- type参数:
- 'r' : 调用进程读出command的标准输出
- 'w' : 调用进程写入command的标准输入