系统编程二(管道)

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文件描述符
cmdF_GETFL:获取文件描述符状态;F_SETFL:设置文件描述符状态;
argO_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()操作,读写操作需要同时执行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.4文件描述符复制

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值