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

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

  

目录

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接口。

🍙open

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类型的数,参数之间进行|运算,就能实现传入多个参数的效果。

源码定义

🌰O_WRONLY | O_CREAT 

模拟结果

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

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

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

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

🌰简单使用open

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<assert.h>

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

权限解释

🍙write

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

🌰测试write

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<assert.h>
#include<string.h>
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;    
}

🍙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;    
}

🍙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开始? 

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

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

🍙内核中文件描述符的探究

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

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

🍙分配规则

在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;    
}

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

答:文件描述符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;    
}

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

🍙重定向的本质

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

🍙dup2系统调用

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

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

🌰dup2系统调用测试

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<assert.h>
#include<unistd.h>

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;    
}
dup2实现重定向

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

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 35
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值