1.简介
查看管道命令:
man 7 pipe
前一个的输出作为,后一个的输入,用做进程之间的通讯工具。
分类:
匿名管道
FIFO管道/命名管道
2.匿名管道
2.1 单工管道(四个操作)
程序进程与Shell命令行进程单项通信。
一.打开管道:
FILE* popen (const char *command, const char *open_mode)
参数说明:
参数 | 含义 |
---|---|
command | 命令行字符串 |
open_mode | "r"只读"w"只写 |
返回值说明:
返回值 | 含义 |
---|---|
NULL | 文件描述符 |
非NULL | 打开失败 |
二.读取:
size_t fread ( void *buffer, size_t size, size_t count, FILE *stream)
参数说明:
参数 | 含义 |
---|---|
buffer | 用于接收数据的内存地址 |
size | 读取每个数据项的字节数 |
count | 数据项个数 |
stream | 输入流 |
返回值说明:
返回值 | 含义 |
---|---|
>count | 出错 |
正数 | 真实读取的数据项个数 |
三.写入:
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream)
参数说明:
参数 | 含义 |
---|---|
buffer | 写入数据的内存地址 |
size | 写入数据项的字节数 |
count | 写入数据项的个数 |
stream | 目标文件指针 |
返回值说明:
返回值 | 含义 |
---|---|
>count | 出错 |
正数 | 真实写入的数据项个数 |
四.关闭管道:
int pclose(FILE *stream);
参数说明:
参数 | 含义 |
---|---|
stream | 文件描述符 |
返回值说明:
返回值 | 含义 |
---|---|
-1 | 失败 |
0 | 成功 |
示例一(写):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
FILE* fd = popen("wc","w");
//FILE* fd = popen("ls -l","r");
//char str[] = "123 456";
char str[] = "123 456\n";
size_t n = fwrite(str,sizeof(char),sizeof(str),fd);
if(n > sizeof(str)){
fprintf(stderr,"FILE:%d,LINE:%d-fwrite error",__FILE__,__LINE__);
exit(EXIT_FAILURE);
}
pclose(fd);
}
示例二(读):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
FILE* fd = popen("ps -ef","r");
//FILE* fd = popen("ls -l","r");
char buf[BUFSIZ];
size_t count = 0;
printf("read data:\n");
do{
memset(buf,'\0',BUFSIZ);
size_t n = fread(buf,sizeof(char),BUFSIZ-1,fd);
if( n > BUFSIZ - 1 ){
perror("fread error");
exit(EXIT_FAILURE);
}
count += n;
printf("\n%d:\n%s",n,buf);
}while(!feof(fd));
printf("total size:%ld\n",count);
pclose(fd);
}
半双工通道
一.创建管道:
int pipe(int filedes[2])
参数说明:
参数 | 含义 |
---|---|
filedes[0] | 读 |
filedes[1] | 写 |
返回值说明:
参数 | 含义 |
---|---|
-1 | 失败 |
0 | 成功 |
二.读取:
ssize_t write(int fd, const void *buf, size_t nbyte)
参数说明:
参数 | 含义 |
---|---|
fd | 文件描述符 |
buf | 写入数据的内存单元 |
nbyte | 写入文件指定的字节数 |
返回值说明:
参数 | 含义 |
---|---|
-1 | 出错 |
正数 | 写入的字节数 |
三.写入
ssize_t read(int fd, void *buf, size_t count)
参数说明:
参数 | 含义 |
---|---|
fd | 文件描述符 |
buf | 读取数据的内存单元 |
返回值说明:
参数 | 含义 |
---|---|
-1 | 出错 |
0 | 无数据 |
正数 | 读取的字节数 |
四.控制
int fcntl(int fd, int cmd, long arg)
如果管道是空的,read()默认是阻塞
参数说明:
参数 | 含义 |
---|---|
fd | 文件描述符 |
cmd | F_GETFL:获取文件描述符状态;F_SETFL:设置文件描述符状态; |
arg | O_NONBLOCK:非阻塞;O_BLOCK:阻塞 |
五.关闭管道
close(filedes)
示例
单进程读写管道:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
int fd[2];
pipe(fd);
char in[] = "Hello pipe";
write(fd[1],in,sizeof(in));
printf("write:%s\n",in);
char out[sizeof(in)]={0};
ssize_t n = read(fd[0],out,sizeof(out));
if(-1 == n){
perror("read error");
return -1;
}
printf("read:%s\n",out);
close(fd[0]);
close(fd[1]);
}
两进程读写:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
int fd[2];
pipe(fd);
if(!fork()){// child
char in[] = "Hello pipe";
write(fd[1],in,sizeof(in));
printf("child %d write:%s\n",getpid(),in);
}else{// parent
char out[BUFSIZ]={0};
ssize_t n = read(fd[0],out,sizeof(out));
if(-1 == n){
perror("read error");
return -1;
}
printf("parent %d read:%s\n",getpid(),out);
}
close(fd[0]);
close(fd[1]);
}
两进程先读后写:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
int fd[2];
pipe(fd);
if(!fork()){// child
char in[] = "Hello pipe";
sleep(3);
write(fd[1],in,sizeof(in));
printf("child %d write:%s\n",getpid(),in);
}else{// parent
char out[BUFSIZ]={0};
ssize_t n = read(fd[0],out,sizeof(out));
if(-1 == n){
perror("read error");
return -1;
}
printf("parent %d read:%s\n",getpid(),out);
}
close(fd[0]);
close(fd[1]);
}
两进程先读后写(非阻塞):
include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int main(){
int fd[2];
pipe(fd);
if(!fork()){// child
char in[] = "Hello pipe";
sleep(3);
write(fd[1],in,sizeof(in));
printf("child %d write:%s\n",getpid(),in);
}else{// parent
fcntl(fd[0],F_SETFL,O_NONBLOCK);
char out[BUFSIZ]={0};
ssize_t n = read(fd[0],out,sizeof(out));
if(-1 == n){
perror("read error");
return -1;
}
printf("parent %d read:%s\n",getpid(),out);
}
close(fd[0]);
close(fd[1]);
}
更加规范的写法(关闭不需要的管道):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int main(){
int fd[2];
pipe(fd);
if(!fork()){// child
close(fd[0]);
char in[] = "Hello pipe";
sleep(3);
write(fd[1],in,sizeof(in));
printf("child %d write:%s\n",getpid(),in);
close(fd[1]);
}else{// parent
close(fd[1]);
fcntl(fd[0],F_SETFL,O_NONBLOCK);
char out[BUFSIZ]={0};
ssize_t n = read(fd[0],out,sizeof(out));
if(-1 == n){
perror("read error");
return -1;
}
printf("parent %d read:%s\n",getpid(),out);
close(fd[0]);
}
}
FIFO管道/命名管道
一.创建命名管道
int mkfifo(pathname,mode);
参数说明:
参数 | 含义 |
---|---|
pathname | 文件路径,文件必须不存在 |
mode | 模式 |
返回值说明:
返回值 | 含义 |
---|---|
0 | 成功 |
非0 | 失败 |
示例:
#include <stdio.h>
#include <unistd.h>
int main(){
if(-1 == mkfifo("/tmp/test",0644)){
perror("mkfifo error");
return 1;
}
}
注意:
管道文件通常在/tmp目录下创建。
管道文件大小通常是0
二.打开FIFO文件
int open(const char *path, int mode);
参数说明:
参数 | 含义 |
---|---|
pathname | 文件路径 |
mode | 模式 |
参数模式说明:
模式 | 含义 |
---|---|
O_RDONLY | 阻塞只读 |
O_RDONLY/O_NONBLOCK | 非阻塞只读 |
O_WRONLY | 阻塞只写 |
O_WRONLY/O_NONBLOCK | 非阻塞只写 |
返回值说明:
返回值 | 含义 |
---|---|
-1 | 失败 |
其他 | 文件描述符 |
示例:
阻塞读:
#include <iostream>
#include <unistd.h>
#include <fcntl.h> // open() O_RDONLY
using namespace std;
#define BUFSIZE 258
int main(){
// 打开命名管道
int fd = open("/tmp/fifo",O_RDONLY);
if(-1 == fd){
perror("open error");
return EXIT_FAILURE;
}else{
cout << "filo open ok" << endl;
}
// 读取字符串
char buffer[BUFSIZE] = {0};
read(fd,buffer,BUFSIZE);
// 打印读取的字符串
cout << buffer << endl;
// 关闭管道
close(fd);
return EXIT_SUCCESS;
}
阻塞写:
#include <iostream>
#include <fcntl.h> // open() O_WRONLY
#include <unistd.h>
using namespace std;
int main(){
// 打开命名管道
int fd = open("/tmp/fifo",O_WRONLY);
if(-1 == fd){
perror("open error");
return EXIT_FAILURE;
}else{
cout << "filo open ok" << endl;
}
// 读取字符串
string str;
getline(cin,str);
// 写入管道
write(fd,str.c_str(),str.size()+1);
// 关闭管道
close(fd);
return EXIT_SUCCESS;
}
写非阻塞:
说明:
只需要在open()添加O_NONBLOCK。
写open()非阻塞,读open()阻塞的情况。读open()需要先执行,否则,写open()会出现No such device or address。
#include <iostream>
#include <fcntl.h> // open() O_WRONLY O_NONBLOCK
#include <unistd.h>
using namespace std;
int main(){
// 打开命名管道
int fd = open("/tmp/fifo",O_WRONLY|O_NONBLOCK);
if(-1 == fd){
perror("open error");
return EXIT_FAILURE;
}else{
cout << "filo open ok" << endl;
}
// 读取字符串
string str;
getline(cin,str);
// 写入管道
write(fd,str.c_str(),str.size()+1);
// 关闭管道
close(fd);
return EXIT_SUCCESS;
}
读非阻塞
说明:
只需要在open()添加O_NONBLOCK。
写open()阻塞,读open()非阻塞的情况。读read()需要处理写open()未执行(read()返回0)和读不到数据(写open()打开但是没有写数据,read()返回-1)的情况。
#include <iostream>
#include <unistd.h>
#include <fcntl.h> // open() O_RDONLY O_NONBLOCK
using namespace std;
#define BUFSIZE 258
int main(){
// 打开命名管道
int fd = open("/tmp/fifo",O_RDONLY|O_NONBLOCK);
if(-1 == fd){
perror("open error");
return EXIT_FAILURE;
}else{
cout << "filo open ok" << endl;
}
// 读取字符串
char buffer[BUFSIZE] = {0};
int n = -1;
while(n<=0){
n=read(fd,buffer,BUFSIZE);
}
// 打印读取的字符串
cout << buffer << endl;
// 关闭管道
close(fd);
return EXIT_SUCCESS;
}
特点:
可以是非亲缘进程之间
FIFO首先会阻塞在open(),等待读写文件的文件描述符都打开。接着阻塞在read()/write()操作,读写操作需要同时执行。