【linux基础I/O(一)】文件描述符的本质&重定向的本质

简介: 【linux基础I/O(一)】文件描述符的本质&重定向的本质

1. 前言

“在Linux系统下,一切皆文件”,相信你也

听过这句话, 那么怎样理解这句话呢?

学会这篇文字,你就能理解了

本章重点:

本篇文章着重讲解I/O的四个系统调用
接口, 以及文件描述符fd的认识与fd的
本质, 最后讲解应该怎样理解Linux下
一切皆文件这一说法.在此之前,会先复习
一下C语言的文件相关的库函数


2. 理解C语言的文件接口

首先C\C++程序会默认打开stdin,

stdout和stderr三个标准文件方便

程序员直接进行读写.

但是显然有点不对劲, 我们平时使用
printf和scanf时是从显示器中显示和
从键盘输入, 是不是代表显示器和键盘
在OS内部其实也可以看作文件?是的,
向显示器打印和向磁盘写入无本质区别!

C语言打开文件的方式: fopen

C语言的读取: fread, fscanf, fgets

C语言的写入: fwrite, fprintf, fputs

fopen的返回值和三个标准文件类型都是FILE*


2. 操作文件的系统调用接口

每个语言都有一套自己的文件操作函数

但不管上层语言怎样变化,它都是封装了

系统调用,所以文件的系统调用很重要!

一共四个函数:

  1. open: 打开文件
  2. close: 关闭文件
  3. write: 向文件写入
  4. read: 从文件中读取

2.1 open函数详解

open函数的解释如下:

这个flag比较特殊,虽然它是整型,但是
内部却当作了位图在使用,即传递过来
的选项会被当作位图中的不同位,通过
判断某位是否为1来查看是否有这个选项

open的选项(实际上是宏定义的整数)

open的用法: 多个选项用或|分割

int fd = open("/home/kwy/test.txt",O_WRONLY | O_CREAT);

2.2 close函数详解

close函数很简单,意思就是关闭文件

描述符fd对应的文件, 调用成功返回0


2.3 write函数详解

write是向文件描述符fd对应的文件中

写入数据, 数据的来源是buf, 要写入的

字节数是count, 调用成功返回写入到

文件中的字节数

write的一般用法:

char* buffer = "abcdef";
int fd = open("/home/kwy/text.txt",O_WRONLY);
write(fd,buffer,sizeof(buffer));

2.4 read函数详解

read是从文件描述符fd对应的文件

中读取数据, 将数据读取到buf中,要

读取的长度是count, 调用成功返回

读取到的字节数

read的一般用法:

char buffer[1024];
int fd = open("/home/kwy/text.txt",O_WRONLY);
ssize_t n = read(fd,buffer,sizeof(buffer));
if(n > 0)
  buffer[n] = '\0';//将字符串变成C语言风格,以\0结尾

3. 文件描述符详解

我们知道文件描述符是一个整数,那么

它是否有什么规律呢?请看下面的代码:

int fd1 = open("/home/kwy/text1.txt",O_WRONLY | O_CREAT);
int fd2 = open("/home/kwy/text2.txt",O_WRONLY | O_CREAT);
int fd3 = open("/home/kwy/text3.txt",O_WRONLY | O_CREAT);
int fd4 = open("/home/kwy/text4.txt",O_WRONLY | O_CREAT);
printf("%d, %d, %d, %d",fd1,fd2,fd3,fd4);

会发现fa1,2,3,4的整数值分别是:

3,4,5,6,这是为什么?需要回答两个问题:

  1. 0号1号和2号描述符去哪儿了?
  2. 文件描述符的增长规律是什么?

首先, 在最开始说过C/C++程序会默认
打开stdin, stdout, stderr三个标准文件
所以其实0,1,2号文件描述符就是这三个
标准文件
.

其次, 0,1,2被使用后, 后面的文件描述符
会从3开始, 依次+1, 一共创建到6号描述符,
若此时将3号文件描述符关闭,下次打开文件
对应的描述符就是3,而不是7!

可以使用下面的代码来验证第一个猜想:

//向屏幕打印信息
const char* str = "abcdef";
write(1, str, strlen(str));
//从屏幕读取信息
char buffer[1024];
int n = read(0, buffer, sizeof(buffer));
if(n > 0)
  buffer[n] = '\0';

4. 文件描述符的内核本质

进程想要访问某个文件的前提是打开文件
在操作系统内可能会有很多个打开的文件
OS为了维护这些资源,需要对它进行管理
OS会为每个打开的文件创建struct file
结构体, 再用链表将这些结构体连接起来.

这是文件在OS内部的管理体系,

而每个进程都要知道自己打开了

哪些文件, 所以进程PCB中会保存

一张文件描述符表(本质是结构体指针)

这个表中存放了这个进程打开的所有文件

从这个图中可以看见, 文件描述符的本质
其实就是数组的下标,每次打开文件会去数
组中扫描,找到最近的没有被使用的下标


5. 怎样理解Linux下一切皆文件?

首先,底层不同的硬件如磁盘,显卡,键盘等

一定对应了不同的操作方法,但这些设备的

核心功能就是读写,也就是I/O

操作系统会为每一个底层硬件创建
struct file结构体,此结构体中一定包含
了两个函数指针,分别指向这个硬件
对应的读方法和写方法,于是和硬件
的交互实际上就变成了访问struct file
结构体中的指针,达到所以硬件一视同仁


6. 理解输出输入重定向

根据上面的推论,如果我先把1号描述符

关闭了,再打开一个文件,它的描述符就

应该是1,此时再进行输出会发现什么?

int main()
{
    close(1);
    int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
    printf("fd: %d\n",fd);
    fprintf(stdout,"hello fprintf,我是一号文件描述符\n");
    return 0;
}

两个现象,第一个确实如刚刚所说的
新打开的文件的描述符就是1,并且
此时使用printf输出也不会输出到屏幕
使用fprintf向stdout也不会输出到屏幕
而是输出到文件log.txt中.说明stdout
只认文件描述符1,不管1此时还是不是
标准输出,printf函数也是如此

结论:重定向本质就是在OS内部修改fd对应的内容指向


7. 重定向的系统调用

如果每次写重定向都要先关闭一个文件

再来操作未免有些麻烦了,可以直接使

用系统调用dup或dup2函数

我们一般都使用dup2,它的意思是将
oldfd拷贝给newfd,最终和oldfd描述符
是一样的,比如现在想把本来应该打印
在显示器(1号描述符)的信息打印在
log.txt中(3号描述符),应该这样使用:

open("log.txt",O_WRONLY | O_CREAT);
dup2(3,1);

1本来指向显示器的,现在将它指向

log.txt,所以要将3号描述符的内容

拷贝到1号描述符,最后1号描述符和3号一样


8. 总结

文件描述符是学习Linux下I/O的关键

而基础IO的知识将会一直陪伴我们到

学习Linux网络和高级IO,掌握文件描述

符fd的本质对后续的学习至关重要!


🔎 下期预告:缓冲区与文件系统 🔍


相关文章
|
3月前
|
图形学 开发者 存储
超越基础教程:深度拆解Unity地形编辑器的每一个隐藏角落,让你的游戏世界既浩瀚无垠又细节满满——从新手到高手的全面技巧升级秘籍
【8月更文挑战第31天】Unity地形编辑器是游戏开发中的重要工具,可快速创建复杂多变的游戏环境。本文通过比较不同地形编辑技术,详细介绍如何利用其功能构建广阔且精细的游戏世界,并提供具体示例代码,展示从基础地形绘制到植被与纹理添加的全过程。通过学习这些技巧,开发者能显著提升游戏画面质量和玩家体验。
127 3
|
3月前
|
存储 Unix Linux
Linux I/O 重定向与管道
【8月更文挑战第17天】重定向在Linux中改变命令I/O流向,默认有">"覆盖输出至文件及">>"追加输出至文件末尾,便于保存结果;使用"<"从文件读取输入而非键盘,高效处理数据。文件描述符如0(stdin)、1(stdout)、2(stderr)标识I/O资源,支持读写操作。管道以"|"连接命令,使前一命令输出成为后一命令输入,如排序用户或找出CPU占用最高的进程,构建复杂数据处理流程。
47 9
|
3月前
|
监控 Linux
在Linux中,如何监控磁盘I/O性能?
在Linux中,如何监控磁盘I/O性能?
|
3月前
|
Linux
Linux的I/O操作
Linux的I/O操作
|
3月前
|
存储 Unix Linux
Linux I/O 重定向与管道
【8月更文挑战第14天】输出重定向可将命令结果存入文件,如`>`覆盖写入或`>>`追加写入。输入重定向从文件读取数据,如`<`代替键盘输入。这些操作利用文件描述符(如0:stdin, 1:stdout, 2:stderr)管理I/O。管道`|`连接命令,使前一命令输出作为后一命令输入,便于数据处理,如排序用户`sort -t: -k3 -n /etc/passwd | head -3`或查找CPU占用高的进程`ps aux --sort=-%cpu | head -6`。
39 4
|
3月前
|
Unix Linux Shell
Linux I/O 重定向简介
Linux I/O 重定向简介
36 2
|
2月前
|
Unix Linux
linux中在进程之间传递文件描述符的实现方式
linux中在进程之间传递文件描述符的实现方式
|
3月前
|
运维 Rust 监控
Linux高效运维必备:fd命令深度解析,文件描述符管理从此得心应手!
【8月更文挑战第23天】本文介绍了一款名为fd的命令行工具,该工具基于Rust语言开发,旨在以更直观的语法和更快的速度替代传统的`find`命令。通过本文,您可以了解到如何安装fd以及一些基本用法示例,比如使用正则表达式匹配文件名、排除特定目录等。此外,文章还展示了如何结合`ps`和`lsof`命令来查找特定文件并显示其文件描述符,从而帮助您更好地管理和监控Linux系统中的文件与进程。
125 0
|
3月前
|
存储 Linux 数据处理
在Linux中,管道(pipe)和重定向(redirection)的是什么?
在Linux中,管道(pipe)和重定向(redirection)的是什么?
|
存储 Linux 文件存储
6.6 Linux重定向(输入输出重定向)
我们知道,Linux 中标准的输入设备默认指的是键盘,标准的输出设备默认指的是显示器。而本节所要介绍的输入、输出重定向,完全可以从字面意思去理解,也就是:
313 0
6.6 Linux重定向(输入输出重定向)