[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)

        hello,大家好,这里是bang___bang_ ,今天和大家谈谈Linux中的基础IO,包含内容有对应的系统文件I/O接口,文件描述符,理解重定向。

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第1张图片

  

目录

1️⃣初识文件

2️⃣ 系统文件I/O接口

open

write

read

close

3️⃣文件描述符

0&1&2

内核中文件描述符的探究

分配规则

4️⃣重定向

重定向现象 

重定向的本质

dup2系统调用


1️⃣初识文件

        文件=内容+属性(属性也是数据)

        文件的所有操作:a.对内容    b.对属性

        文件在磁盘(硬件)上放着,我们访问文件,先写代码->编译->exe->运行->访问文件。

        本质上是 进程 在访问文件。

Linux下一切皆文件。曾经我们理解的文件就是磁盘上的那些普通文件能read,write。

        在C语言中,我们对显示器有方法:printf->这是write(output);键盘:scanf->这是read(input)。也就是说我们的显示器和键盘也是文件。

        站在系统的角度来说:我们的程序要加载到内存,键盘相当于把我们的数据输交给内存(input),而内存把读取到的数据刷新到文件或者显示器当中(output)。

系统角度:能够被input读取,或者能够被output写出的设备就叫做文件!

2️⃣ 系统文件I/O接口

        在C语言中,文件操作接口有fopen,fwrite,fprintf,fclose等等,实际上这些接口都是封装了系统I/O接口。[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第2张图片

open

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第3张图片

pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项
参数 :
         O_RDONLY: 只读打开
         O_WRONLY: 只写打开
         O_RDWR : 读,写打开
        O_RDONLY  O_WRONLY  O_RDWR 这三个常量,必须指定一个且只能指定一个
         O_CREAT : 若文件不存在,则创建它。需要使用 mode 选项,来指明新文件的访问权限
         O_APPEND: 追加写
        O_TRUNC: 清空文件
mode: 为新建文件设置权限

问题:如何实现flags传入多个参数选项呢?

答:采用位图的思想,flags的参数实际上是一个int类型的数,参数之间进行|运算,就能实现传入多个参数的效果。

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第4张图片 源码定义

O_WRONLY | O_CREAT 

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第5张图片 模拟结果

检测是否有当前状态,只需要使用flag&状态的值。

0110 0101 & 0000 0001 = 0000 0001 有O_WRONLY状态

0110 0101 & 0110 0100 = 0110 0100 有O_CREAT状态

open函数图中我框选了返回值为一个文件描述符,在下面讲解。

简单使用open

#include
#include
#include
#include
#include

int main()
{
    int fd=open("log.txt",O_WRONLY | O_CREAT,0666);
    assert(fd!=-1);
    printf("open sucess! fd:%d\n",fd);
    return 0;    
}

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第6张图片 权限解释

write

 功能:把缓冲区buf的前count字节写入与文件描述符fd关联的文件中。它返回实际写入的字节数。

测试write

#include
#include
#include
#include
#include
#include
int main()
{
    int fd=open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
    assert(fd!=-1);
    printf("open sucess! fd:%d\n",fd);
    const char* buf="This is a test\n";
    write(fd,buf,strlen(buf));
    return 0;    
}

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第7张图片

read

功能:从与文件描述符fd相关联的文件中读取count个字节的数据。并把它放入到数据区buf中。

测试read,读取上面write写的内容

int main()
{
    int fd=open("log.txt",O_RDONLY);
    assert(fd!=-1);
    printf("open sucess! fd:%d\n",fd);

    char res[64];
    read(fd,res,sizeof(res));
    printf("res:%s",res);
    return 0;    
}

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第8张图片

close

 

功能:将文件描述符fd相关联的文件关闭。

close操作演示

int main()
{
    int fd=open("log.txt",O_RDONLY);
    assert(fd!=-1);
    printf("open sucess! fd:%d\n",fd);
    close(fd);
    return 0;    
}

3️⃣文件描述符

0&1&2

fd的显示问题:

int main()
{
    int fd1=open("log1.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd2=open("log2.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd3=open("log3.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd4=open("log4.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
    assert(fd1!=-1);
    assert(fd2!=-1);
    assert(fd3!=-1);
    assert(fd4!=-1);
    printf("open sucess! fd1:%d\n",fd1);
    printf("open sucess! fd2:%d\n",fd2);
    printf("open sucess! fd3:%d\n",fd3);
    printf("open sucess! fd4:%d\n",fd4);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    return 0;    
}

问题:为什么fd是从3开始? 

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第9张图片

答:实际上C/C++程序默认会打开三个文件流:标准输入stdin(0),标准输出stdout(1),标准错误stderr(2)

0,1,2一般对应的物理设备是:键盘,显示器,显示器。

内核中文件描述符的探究

在上面我们可以发现文件描述符fd就是一个小整数,那么在内核中文件描述符究竟是什么呢?

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第10张图片 内核结构
        当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行 open 系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表 files_struct, 该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上, 文件描述符就是该数组的下标 。所以,只要拿着文件描述符,就可以找到对应的文件。
[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第11张图片 抽象结构图

分配规则

在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

验证分配规则,关闭文件0,进行分配。

int main()
{
    close(0);
    int fd=open("log1.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
    assert(fd!=-1);
    printf("open sucess! fd:%d\n",fd);
    close(fd);

    return 0;    
}

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第12张图片

问题:如果关闭文件描述符1,为什么没有输出显示。

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第13张图片

答:文件描述符1对应显示器,关闭后自然就不会有内容显示到显示器了,但是也没有显示到文件log.txt中。

4️⃣重定向

重定向现象 

问题:上面例子中内容没有显示到显示器和文件log.txt中,那么内容实际上在哪个地方?

答:在文件的缓冲区中

那是不是意味着我们刷新缓冲区就可以将内容显示出来?

int main()
{
    close(1);
    int fd=open("log.txt",O_WRONLY | O_CREAT| O_TRUNC,0666);
    assert(fd!=-1);
    printf("open sucess! fd:%d\n",fd);
    fflush(stdout);//刷新缓冲区
    close(fd);
    return 0;    
}

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第14张图片

刷新后成功输出,内容本来应该输出到显示屏,但是现在输出到了log.txt这实际上就是输出重定向!!!(O_CREAT创建文件,O_TRUNC刷新文件,实现C语言中的w模式;O_APPEND追加内容,实现C语言中的a模式)

重定向的本质

重定向的本质,其实是在OS内部,更改fd对应的内容的指向!!

[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第15张图片

dup2系统调用

在重定向中,我们通常不使用close关闭fd,再重定向,而是直接使用系统调用dup2来重定向。 

将oldfd的内容给newfd,即newfd对应内容指向oldfd

dup2系统调用测试

#include
#include
#include
#include
#include
#include

int main()
{
    int fd=open("log.txt",O_WRONLY | O_CREAT| O_TRUNC,0666);
    assert(fd!=-1);
    dup2(fd,1);
    printf("open sucess! fd:%d\n",fd);
    close(fd);
    return 0;    
}
[Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)_第16张图片 dup2实现重定向

文末结语,本文讲解Linux的基础IO,包含内容:初识文件,系统文件I/O接口open、read、write、close,文件描述符的详细讲解以及文件描述符的分配规则,重定向现象的演示及重定向的本质讲解,实现重定向的系统调用dup2。如有需要,希望能有所帮助!!!

你可能感兴趣的:(我在地球学Linux,linux,运维,I/O接口,文件描述符,重定向,文件)