Linux系统编程:管道

公众号:CppCoding

匿名管道
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)
  • 返回值
返回值含义
-1失败
0成功
  • write
#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);
}
  • read
#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:阻塞

把文件描述符设置为非阻塞的
fcntl(filedes,F_SETFL,O_NONBLOCK)

  • 关闭管道
close(fd)
  • 单进程读写管道
#include<stdio.h>
#include<stdlib.h>
#include<stdring.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>
#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]);
    }
}

在这里插入图片描述

管道复制
分类文件描述符文件号
标准输入STDIN_FILENO0
标准输出STDOUT_FILENO1
标准出错信息STDERR_FILENO2

内核为每个进程创建文件描述符,每复制一次,文件号一次递加

  • 函数
int dup(int oldfd)
  • 参数
参数含义
oldfd旧文件描述符
  • 返回值
返回值含义
-1失败
其他新文件描述符
  • 函数
int dup2(int oldfd,int newfd)
参数含义
oldfd旧文件描述符
newfd新文件描述符
  • 返回值
返回值含义
-1失败
其他最小及尚未使用的文件描述符
  • 复制文件描述符

新文件描述符与旧文件描述符不同,但是具备旧文件描述符功能

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
 
int main(){
    int fd = open("./test",O_CREAT|O_RDWR,FILE_MODE); 
    char str[]="Hello dup\n";
    write(fd,str,sizeof(str));
    int cp_fd = dup(fd);
    printf("copy %d to %d",fd,cp_fd);
    write(cp_fd,str,sizeof(str));
    //fprintf(fdopen(fd,"w"),"%d printf:Hello dup\n",fd);
    close(fd);
}
  • 把文件描述符重定向(复制)到标准输出,并且输出后还原
    注意把标准输出流从文件重定向(复制)回终端,需要清除缓冲区。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
int main(){
    int save_fd = dup(STDOUT_FILENO);
    int fd = open("./test",O_CREAT|O_RDWR,FILE_MODE); 
    if(-1 == dup2(fd,STDOUT_FILENO)){
        perror("dup2 error0");
        return 1;
    }
    close(fd);
    printf("%d printf:Hello dup\n",fd);
    fflush(stdout);// 一定要清除缓冲区,否则会输出到终端
    if(-1 == dup2(save_fd,STDOUT_FILENO)){
        perror("dup2 error");
        return 1;
    }
    close(save_fd);
    printf("%d printf:this is save\n",save_fd);
}
FIFO管道/命名管道

FIFO和管道的区别:

管道只能在亲缘进程之间,FIFO可以在无亲缘关系的进程之间进行IPC
FIFO用mkfifo创建,之后用open打开。

  • 创建命名管道
int mkfifo(pathname,mode)
参数含义
pathname文件路径,文件必须不存在
mode模式
  • 返回值
返回值含义
0成功
非0失败
  • 打开FIFO文件
int open(const char *path,int mode)
参数含义
pathname文件路径
mode模式
  • 模式
模式含义
O_RDONLY阻塞只读
O_RDONLY | O_NONBLOCK非阻塞只读
O_WRONLY阻塞只写
O_WRONLY\NONBLOCK非阻塞只写
  • 返回值
返回值含义
-1失败
其他文件描述符

特点

  • 可以是非亲缘进程之间
  • 读写必须同时执行,否则阻塞

在进行全双工的时候,在父进程首先打开(open)1管道的RDONLY时,然后打开2管道的WRONLY,在子进程打开(open)1管道的WRONLY和打开2管道的RDONLY,在此过程中,父进程和子进程的RD不能同时打开,否则该程序将一直阻塞。原因是目前没有任何进程打开FIFO来写,即打开该FIFO来读的进程将阻塞。这种现象称为死锁。

设置描述符的两种方式:

open(FIFO,O_RDONLY|O_NONBLOCK,0);
//FIFO的设置

fcntl(fd,F_SETFL,O_NONBLOCK);
//

在这里插入图片描述

  • 对管道或者FIFO进行close时,该管道或者FIFO的任何数据都会被丢弃
迭代服务器和并发服务器

迭代服务器是完全处理每个客户的请求后,在接待下一个客户。
并发服务器是每当有一个客户请求到达时,服务器就让主进程fork一个新的子进程。

拒绝服务型攻击

客户发送一个请求后,但是从来不打开自己的FIFO来读,这是拒绝服务型攻击。
解决办法:将服务器程序编写为并发型,这样上述的问题就只影响一个子进程,而不会影响主服务器。

OPEN_MAX:一个进程打开的最大文件描述符
PIPE_BUF:往管道或者FIFO写的最大数据量
可以使用ulimit查看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值