ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

7.文件重定向和管道

2022-02-02 11:36:19  阅读:140  来源: 互联网

标签:文件 重定向 int pipefd 管道 描述符 close include


标准文件描述符

文件描述符(file descriptor)的概念:文件描述符是一个大于等于0的整数,它的含义是进程的文件打开表中项目的序号(从0开始)。对于一个进程而言,文件描述符是操作一个打开文件(或是一个设备文件,或是一个socket连接)的句柄。该条项目会存储指向系统级文件打开表相应条目的指针。系统级文件打开表会存储打开文件所对应的文件控制块(FCB)。

Unix(包括Linux)的特色之一是提供了大量的小的软件工具。这些软件工具都是基于标准I/O,从而可以使用重定向和管道更加灵活地使用这些工具。标准文件描述符包括标准输入(stdin)标准输出(stdout)标准错误输出(stderr),分别对应于文件描述符0、1、2。任何软件工具,都是从标准输入读取输入,把输出结果写入标准输出,将错误提示(如perror())写入标准错误输出。

很显然,我们编写程序时并没有建立0、1、2这三个文件描述符。这三个文件描述符是从父进程(shell)继承得来的,全部都连接于终端设备(即键盘和显示器)。

最低可用文件描述符(lowest-available file descriptor)原则

既然文件描述符本质上为进程文件打开表的条目序号,当我们关闭某个文件,也就释放了文件打开表中对应的条目,即该文件描述符可用。下次我们再打开一个文件时,就会使用最小数字的文件描述符,以确保文件打开表尽量的小。

我们通过以下程序可以看到进程始终使用最低可用的文件描述符。

#include <fcntl.h>
#include <stdio.h>

int main() {
    int fd1,fd2;
    fd1 = open("/usr/include/stdio.h", O_RDONLY);
    printf("%d\n", fd1);

    fd2 = open("/usr/include/stdio.h", O_RDONLY);
    printf("%d\n", fd2);
    
    close(0);
    printf("close stdin\n");

    close(fd1);
    close(fd2);

    fd1 = open("/usr/include/stdio.h", O_RDONLY);
    printf("%d\n", fd1);

    fd2 = open("/usr/include/stdio.h", O_RDONLY);
    printf("%d\n", fd2);

    close(fd1);
    close(fd2);

    return 0;
}

运行结果:

image-20211207190121374

I/O重定向

例:在shell中使用I/O重定向

在本例中,who的输出本来是终端,使who输出重定向到文件后,文件是who输出的内容

image-20211207190338186

既然任何的软件工具都是从“标准输入”读取输入,“标准输出”输出结果。则如果我们能够提前将标准输入连接至某个文件,则可使进程以为自己从终端读用户的输入,而实则从文件中读取数据。对于标准输出和标准错误输出亦是同理。

I/O重定向方法1:close … open

close(0)关闭标准输入,文件描述符0可用。这时再打开文件,该文件连接到标准输入。此时再从标准输入读取,将读得文件内容。

close(1)关闭标准输出,文件描述符1可用。这时再打开文件,该文件连接到标准输出。此时再向标准输出,如printf,将不会在终端显示该内容,而是写入到文件。

例:printf的内容重定向到文件

#include <fcntl.h>
#include <stdio.h>

int main() {
    int fd;
    close(1);
    fd = open("haha", O_WRONLY | O_CREAT, 0644);

    printf("%d\n", fd);

    return 0;
}

I/O重定向方法2:open… close… dup… close

当某些情况下,一个文件描述符已经打开,比如一个socket连接。这时已经来不及使用方法1。这种情况下,可以先关闭标准文件描述符,获得低可用文件描述符;再对希望重定向的文件描述符进行复制(dup)。此时标准文件描述符已连接至我们希望连接的文件上。将原有文件的文件描述符关闭。完成重定向。

#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>

main(){
    int fd;
    fd=open("haha",O_WRONLY|O_CREAT,0644);
    
    close(1);
    dup(fd);
    close(fd);
    
    execlp("who","who",NULL);
}

I/O重定向方法3:open… dup2… closed

方法3是方法2的简化。用dup2(fd, 0)替换close(0)dup(fd)

I/O重定向方法1和2的示意图如下:

image-20211229191741306

管道

我们平时所谓的管道(pipe)一般特指无名管道:直观上是将一个进程运行的结果作为另一个进程的输入。也可以将多于两个的进程用管道依次连接起来。

例如,在本例中,sort将ls的输出内容排序了

image-20211229192049169

管道将一个进程的标准输出与另外一个进程得标准输入相连。因为每一个软件工具都是从“标准输入”读,往“标准输出”写。这样就可以实现一个数据流的串联处理。

注意:

  1. 管道的下部是在内核里的,即管道要受内核的管理和控制;
  2. 管道是有方向的,如同家里的下水管——不是双向的。
管道的创建

通过pipe()系统调用可以在单个进程上创建一个管道。

查看man手册pipe(2)。pipe的调用格式为ini pipe(int pipfd[2]); 其中pipefd[0]是读数据端文件描述符,pipefd[1]是写数据端文件描述符。

image-20211229204222479

再通过fork()系统调用复制该进程。由于子进程继承父进程的一切,包括文件描述符,所以能获得如下图的状态。

image-20211229204243644

此时父子进程间就建立了管道。关闭不需要的文件描述符,再将父子进程分别运行我们希望运行的程序。无名管道就实现了。

例:该程序演示管道两端的方向性。

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

main(){
    int pipefd[2];
    char buf[1024];
    int n;
    pipe(pipefd);

    write(pipefd[1], "haha", 4);
    n=read(pipefd[0],buf,sizeof(buf));

    printf("%.*s\n", n, buf); 
}

例:实现 cat /usr/include/stdio.h|sort

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

main(){
    int pipefd[2];
    int rv;
    pipe(pipefd);

    rv=fork();
    if(0==rv){
        close(pipefd[1]);
        close(0);
        dup(pipefd[0]);
        close(pipefd[0]);
        execlp("sort", "sort", NULL);
    }else{
        close(pipefd[0]);
        close(1);
        dup(pipefd[1]);
        close(pipefd[1]);
        execlp("cat", "cat", "/usr/include/stdio.h", NULL);
    }
}

例:实现 cat /usr/include/stdio.h|sort

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

main(){
    int pipefd[2];
    int rv;
    pipe(pipefd);

    rv=fork();
    if(0==rv){
        close(pipefd[1]);
        close(0);
        dup(pipefd[0]);
        close(pipefd[0]);
        execlp("sort", "sort", NULL);
    }else{
        close(pipefd[0]);
        close(1);
        dup(pipefd[1]);
        close(pipefd[1]);
        execlp("cat", "cat", "/usr/include/stdio.h", NULL);
    }
}

标签:文件,重定向,int,pipefd,管道,描述符,close,include
来源: https://blog.csdn.net/AIbeichen/article/details/122749655

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有