Linux基础IO——文件系统(上)
Posted 风起、风落
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux基础IO——文件系统(上)相关的知识,希望对你有一定的参考价值。
文章目录
1.了解磁盘的物理结构
磁盘计算机上唯一的一个机械设备,同时它还是外设
机械磁盘很便宜,虽然效率会慢一些,所以企业一般使用机械磁盘,因为便宜
磁盘不仅仅外设,还是一个机械设备(盘片、磁头),所以磁盘一定非常慢
![](https://image.cha138.com/20230408/8b0901b1f0084d0bbf5aaaa2254b2caa.jpg)
![](https://image.cha138.com/20230408/9adb9d06892449afaae793e9c452ecc0.jpg)
盘片:一片两面,有一摞盘片
![](https://image.cha138.com/20230408/5bfbe30d5f724455ba5d839ce52603ea.jpg)
磁头:一面一个磁头
一个磁头负责一面的读取
![](https://image.cha138.com/20230408/932d4d0264194d7e883100e24aaea717.jpg)
马达比如说剃须刀,或者手机的震动等
所以盘片就可以 以顺时针的方式高速旋转
同时在磁头位置也存在一个马达,控制磁头左右来回摆动
磁头是共进退的,要不一块过去,要不就一块不去
磁盘盘片上有无数个基本单元,每一个基本单元按照特定的空间排布好的,每一个单元就是磁铁
南极表示1,北极表示0,
向磁盘写入:把北极改成南极 (N->S)对内容做磁化
删除磁盘数据:把数据从南极设置为北极 (S->N)
这样就可以完成微观上 一个比特位的读和写
磁头本质上 是对数据做写入和读取,更改基本元素的南北极,读取南北极
磁盘的具体物理存储结构
![](https://image.cha138.com/20230408/95915f875dba481d8809a210d2c326fc.jpg)
整体结构
![](https://image.cha138.com/20230408/f8503772c8a443e6a54a72a0e4bfd6b8.jpg)
抽象的一面结构
![](https://image.cha138.com/20230408/6e3963add16f4839b12aa57985088ed8.jpg)
磁盘中存储的基本单元是扇区,一般扇区的大小为512字节或者4KB字节
一般磁盘所有的扇区都是512字节大小
同半径的所有的扇区即为磁道
在一面上,如何在硬件上定位一个扇区?
1.先定位在哪一个磁道—由半径决定
![](https://image.cha138.com/20230408/5621c9f388b343f0b798d25e9f8068e3.jpg)
2.再确定在该磁道,在哪一个扇区,根据扇区的编号,来定位一个扇区
所以首先要定位哪一个面
![](https://image.cha138.com/20230408/9ab5dc26e9f54e378d04f46cb2fa96ba.jpg)
磁头是共进退的,半径相同的每一个面上的磁道共同在抽象上就会形成一个柱面
只需要确定用哪一个磁头读取,磁头的编号就表示哪一个面
所以定位任意一个扇区,需要确定 磁头head 、柱面 cylinder、扇区 sector 即CHS定位法
普通文件中包含属性和数据,都可以看做数据(0,1),占用一个或者多个扇区,来进行自己的数据存储
既然能用CHS定位为任意一个扇区,就能定位任意多个扇区,从而将文件从硬件角度进行读取或者写入
2.逻辑抽象
OS内部是不是直接使用CHS地址?不是
第一点:因为OS是软件,磁盘是硬件,硬件定位一个地址,用的是CHS,但是如果OS直接用了这个地址,万一硬件改变,OS也要发生变化,所以OS要和硬件做好解耦工作
第二点:即便是扇区512字节,单位IO的基本数据量也是很小的,所以硬件是按照512字节处理,
操作系统实际进行IO,基本单位是4KB
操作系统和磁盘进行交互时,基本以4KB为单位,
基本大小:进行磁盘读取和磁盘写入时,必须以基本单位为基本大小,来与外设进行交互
哪怕只修改一个比特位,也要把一个比特位所在的4KB全部读到内存,
把一个比特位改完,再把4KB内容全部写到目标文件中,要以块的方式整体与外设进行交互
所以一般把磁盘称为 块设备
所以OS需要有一套新的地址,来进行块级别的访问
![](https://image.cha138.com/20230408/399ae9fc9d1a465eb205251964cbc1da.jpg)
把盘面抽象成一种线性结构
以一个盘面举例
相同磁道的东西一定放在一起的
整体可以看作是数组,设置数组名为 sector_disk1
初步完成了从物理逻辑到线性逻辑抽象的过程
因为看作是一个数组,而数组都是有下标的
假设数组下标为n,定位一个扇区,只需要数组下标就可以定位一个扇区了
![](https://image.cha138.com/20230408/85cdbceb62614821aff2b2ff7d768b48.jpg)
OS是以4KB为单位进行IO的,一个操作系统对应的文件块要包括8个扇区
计算机常规访问方式:采用起始地址+偏移量的方式
只需要知道数据块起始位置的地址,即第一个扇区的下标地址 + 4KB(块的类型)
可以把数据块看作一种类型
所以块的地址本质就是数组的一个下标N
N的地址在OS中被叫做LBA,即逻辑块地址
磁盘只认CHS,LBA如何跟磁盘地址互相转化?
假设LBA地址为6500 ,单片大小为5000
首先确定在那一面,也就相当于在哪一个磁头上
H(磁头): int n=6500/5000=1 说明H的地址在第2面上
C(柱面):6500/1000=6 1000为磁道的大小,对应的H就在6号磁道上
S(扇区): 6500 %1000=500
所求扇区 就在 2号面中的6号磁道的第500个扇区
连续读取8个扇区,就能得到块
3.文件系统
![](https://image.cha138.com/20230408/5b95a89134dc40289c572bb47b12590c.jpg)
为了方便管理,就把数组拆分为一个个的区域
若感觉管理500GB太难了,就减少管理,从而管理150GB,将管理小的经验复制,从而使每一个小的都管理好
从而使500GB整体管理好 ,这种思想就叫做分区
![](https://image.cha138.com/20230408/44693b624e30423395b11ae0d5d995ab.jpg)
虽然分完区小了很多,但依旧很大,所以操作系统对整个分区还会在做分组
假设每个区为150GB,就把15GB为一组,把其中一个组管理好了,按照一个组的管理方式就把所有组管理好了
由于每个区都可以分组,就有了 Block group
![](https://image.cha138.com/20230408/2f8f11be642d4715880549d8b6db2467.jpg)
一个分区当中最开始有一个Boot Block
会保存与操作系统启动相关的内容,如分区表和操作系统镜像地址
一个组的结构
一个组中分为 Super Block(超级块) 、Group Descriptor Table(组描述符)、Block Bitmap、inode Bitmap(位图)、inode Table (inode表)、Data blocks(数据块)
Super Block 保存的是文件系统的所有属性信息 如文件系统的类型、整个分组的情况
Super Block在各个分组里面可能都会存在,而且是统一更新的
为了防止Super Block区域坏掉,如果出现故障,整个分区不可以被使用,所以要做好备份
Group Descriptor Table
GDT:组描述符 – 改组内的详细统计等属性信息,用来描述整个
一般而言,一个文件内部所有属性的集合,被称为inode节点 ,大小一般为128字节
一个文件,一个inode,一个分区内部也会存在大量的文件即会存在大量的inode节点,一个group,需要有一个区域专门保存该group的所有文件的inode节点 即 inode table -----inode表
文件的内容是变化的,用数据块对文件内容保存的,所以一个有效文件要保存内容就需要1/n数据块
若有多个文件就需要更多的数据块,数据块称为 Data blocks
linux查找一个文件,是要根据inode编号,来进行文件查找到,包括读取内容
一个inode对应一个文件,而该文件inode属性和该文件的数据块是由映射关系的
inode Bitmap
共有4096*8个比特位,按照从低向高扫描位图时,比特位的位置对应inode表中的inode
为1表示inode正常工作,为0表示inode不正常工作
每一个比特位表示 一个inode是否空闲可用
Block Bitmap
每一个bit位表示data block是否空闲可用
细节问题
1.inode与文件名
Linux系统只认inode编号,文件的inode属性中,并不存在文件名
文件名是给用户用的
2.目录是文件么?
是的,目录有inode和内容
3.任何一个文件,一定在目录内部,所以目录的内容是什么?
目录要有内容就需要数据块,目录的数据块里面保存的是该目录下 文件名和inode编号对应的映射关系
在目录内,文件名和inode编号互为key值
4.当我们访问一个文件的时候,是在特定目录下访问的 cat log.txt
1.先要在当前目录下,找到log.txt 的 inode编号
2.一个目录也是一个文件,也一定属于 一个分区,在该分区中找到分组,在该分组中对应的inode table中,找到文件的inode
3. 通过inode与 对应的data block的映射关系,找到该文件的数据块 ,并加载到OS,并完成到显示器
[Linux] 基础 IO
文件想大家都不陌生吧,计算机中所有的数据都存在文件中,但是前面是一个普通人对文件的理解,那么现在我就带你看看程序员眼中的文件
文章目录
1 语言层面上的文件(C语言)
1.2 C语言接口介绍
在C语言中想要对文件进行操作那么需要用到一下的接口(fopen,fclose,fwrite,fred)对文件进行读写操作
如图所示 : >
代码实现
写形式打开文件
int main()
FILE * fd=fopen("log.txt","w");//打开文件
const char *str="hello word";
fwrite(str,1,strlen(str),fd);//写文件
fclose(fd);//关闭文件
return 0;
结果:
写/读形式读打开文件
代码:
int main()
FILE * fd=fopen("log.txt","w");//W打开文件
FILE * fd=fopen("log.txt","r");//打开文件
char str[1024];
size_t ret=fread((char*)str,1,1024,fd);
str[ret]='\\0';//c语言中字符串以\\0结尾
printf("%s\\n",str);
fclose(fd);//关闭文件
return 0;
读结果
写结果
追加追加形式写文件
代码
int main()
FILE * fd=fopen("log.txt","a");//打开文件
const char *str="hahahaha~";
fwrite(str,1,strlen(str),fd);
fclose(fd);//关闭文件
return 0;
追加结果:
1.2 系统接口介绍
不知大家是否记得,就是用户是无法访问硬件的(要访问则一定要贯穿整个计算机体系结构),一般系统会暴露接口,则库为了方便使用会进行封装,上述介绍的接口,但其实我们也可以直接调用这些系统接口
如图所示 : >
写的形式打开,不存在则创建文件
int mian()
int fd = open("log.txt",O_CREAT|O_WRONLY,0x644);//打开文件没有则创建权限为644
char * str="hello word";
write(fd,str,strlen(str));
close(fd);
结果
读形式打开,不存在则创建(读一般情况都在吧)
int main()
int fd = open("log.txt",O_CREAT|O_RDONLY,0x644);//打开文件没有则创建权限为644
char str[1024];
ssize_t ret=read(fd,str,1024);
str[ret]='\\0';
printf("%s \\n",str);
close(fd);
结果
追加形式打开
int main()
int fd = open("log.txt",O_WRONLY|O_APPEND);
char * str="hahaha~~~";
write(fd,str,strlen(str));
结果
仔细观察系统接口与C语言的接口大致相同,那以后使用C的接口时你就会想到他的底层是如何运作的。但是他们还是有所区别,系统调用返回的是
一个整形(文件描述符)
而C语言是一个FILE * 的指针,后文介绍文件描述符
1.3 三个默认打开输入输出流
不知道大家还记不记得三个标准输入输出流,分别是标准输入,标准输出,标准错误
(stdin,stdout,stderr)所对应的硬件是键盘,显示器,显示器
问
那么为啥要默认打开这三个输入输出流?
答
都有输入输出的要求,且方便操作(如人生第一个程序hello world和你说输入要打开标准输入,打印时要打开标准输出,从入门到入土)
c语言就有对应的接口fprintf,fsanf他们就需自己指定的流输入输出:
如图所示 : >
计算体系结构图 : >
2 系统层面上理解文件
一个文件其实可以划分成俩个板块
- 属性
- 内容(数据)
问
创建一个文件不写入任何数据,会占用内存吗?
答
占用,上述说过一个文件分为俩部分
属性与内容
,而创建文件时显然属性也一定创建了
2.1如何理解linux一切皆文件
大家或多或少动听过linux下一切皆文件,那么这个要如何理解呢?其实只需要理解这六个字先描述在组织
问
当一个进程执行时,是否会打开各种文件,硬件(驱动)?
答
当然会啊,那么那么如果文件和硬件(驱动)打开的多,操作系统(作用: 文件、驱动、进程、内存管理)则需要讲他们管理起来,则管理的本质就是
先描述在组织
问
对文件的操作是??
答
emmm对文件的操作其实就是
读写
问
各种硬件不是有不同的读写方式吗?如键盘只有读,我从来没看到过写数据到键盘上的,那么如何组织,如何如题所说一切皆文件呢?
答
确实各种硬件都有不同的读写方式,但是我们可以
先描述在组织用
,那么操作系统管理硬件,那么就可以用相同的方式操作
如图所示 : >
stdin对应的是键盘,stdout,stderr对应显示器
2.2 文件描述符 与 FILE
用系统的接口调用返回的一个整数也就是文件描述符,那是如何根据文件描述符来找对对应的文件呢?
代码
int main()
int fd1=open("log1.txt",O_CREAT|O_WRONLY,0x666);
int fd2=open("log2.txt",O_CREAT|O_WRONLY,0x666);
int fd3=open("log3.txt",O_CREAT|O_WRONLY,0x666);
int fd4=open("log4.txt",O_CREAT|O_WRONLY,0x666);
int fd5=open("log5.txt",O_CREAT|O_WRONLY,0x666);
int fd6=open("log6.txt",O_CREAT|O_WRONLY,0x666);
int fd7=open("log7.txt",O_CREAT|O_WRONLY,0x666);
int fd8=open("log8.txt",O_CREAT|O_WRONLY,0x666);
printf("fd1 %d\\n",fd1);
printf("fd2 %d\\n",fd2);
printf("fd3 %d\\n",fd3);
printf("fd4 %d\\n",fd4);
printf("fd5 %d\\n",fd5);
printf("fd6 %d\\n",fd6);
printf("fd7 %d\\n",fd7);
printf("fd8 %d\\n",fd8);
return 0;
结果:
问
嗯居然是连续的,他们不会放在一起吧?为啥第一个文件描述符是从3开始的?
答
连续下标想到了啥?数组呀,没错这些文件底层其实是放在数组中(客观表示),文件描述符为啥从三开始,上述说过编译器会默认打开三个流(输入、输出、错误),自然后面创建的文件描述符只能从3开始
上述说用统一的方式看待硬件实现一切皆文件,但是文件一多就需要管理
,并用一种数据结构组织起来
,这里用的数据结构就是数组
如图所示 : >
问
那么C是如何操作的呢?他的返回值不是一个FILE*吗?
答
其实FILE底层是一个结构体并且里面有俩个重要的部分
1:C语言缓冲区 2:file_on(fd)
如图所示 : >
验证_filen 是否是fd
int main()
printf("%d\\n",stdin->_fileno);
printf("%d\\n",stdout->_fileno);
printf("%d\\n",stderr->_fileno);
int fd=open("log.txt",O_CREAT|O_WRONLY,0x666);//系统接口
printf("%d\\n",fd->_fileno);
结果
那么C语言中底层调用文件我们大致就可以猜想出来了(fopen调用open,open返回fd给 fopen ,fopen再把fd写入到 FILE*对象中的_fileon,后面通过接口对文件读写
)
如图所示 : >
2.3 文件描述符的分配规则
文件描述符的分配规则其实很简单,就是数组中最小空余的下标
,上述例子已经可以看出(标准输入、输出、错误占用下标0,1,2,新开文件就在3下标处)
问
标准输入输出错误流可以关闭吗?
答
既然他是默认打开的,注意默认打开,那么也就是说是被打开的,那么就可以被关闭
代码:
int main()
close(0);
int fd=open("log.txt",O_CREAT|O_WRONLY,0x666);
printf("%d\\n",fd);
结果
问
如果问吧标准输给关闭了,是否会打印呢?应该会吧,标准错误也是显示器
答
其实是不会打印的,虽然stderr也是显示器,但是
底层要把数据输显示器上,还是通过stdout去输出
,但是stdout中的fileon还是2
,但files_strcuct中的fd 数组下标为2的数据已经被 “抹除”
,那么就找不到显示器文件如图所示 : >
2.3.2 重定向
重定向:把本该输出到固定位置的数据输出到别出简称重定向
上述说,可以把stdout关闭,但stdout的_fileson还是2
,且文件描述符分配规则是没使用且下标最小的位置
,那么我新打开一个文件
,在输出数据不就是到文件中
了吗,这不就是重定向吗??
验证代码:
int main()
close(1);//关闭显示器文件
int fd = open("log.txt",O_WRONLY);//已经存在
printf("hello world");//输出到stdout中
return 0;
结果:
正常情况下我们不会像上面的操作来完成重定向,但是我们确实需要重定向的需求,C语言中有个接口可以帮助我们实现重定向dup2
接口介绍
接口使用
int main()
int fd = open("log.txt",O_WRONLY);//已经存在
dup2(fd,1);
printf("hello world");
return 0;
结果
2.4 创建一个进程的新理解
其实上述中所画的图是有一点错误的,其实FILE*其实是在代码也就是数据,他是在内存中的,根本不可能直接访问files_struct,他们其实用是PCB进行互相访问
如图所示 : >
2.5 缓冲区的理解
上述在介绍文件描述符与FILE*时提到过FILE 是一个结构体,它里面包含了俩个重要的部分 1. 缓冲区 2. files_on, 缓冲区(对IO数据的临时存储的区域
)想必大家已经耳熟能详了吧!
怪异现象(程序结束后调用fork)
int main()
const char *msg0="hello printf\\n";
const char *msg1="hello fwrite\\n";
const char *msg2="hello write\\n";
printf("%s",msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1,msg2,strlen(msg2));
fork();
输出结果 : >
那么把输出的结果重定向到文件中的时候 : >
问
这个应该就是所谓的怪异现象吧,C语言的接口所输出的数据重定向后输出了俩份,这个是为啥呢?
答
确实这就是所谓的怪异现象,其实这都是看似没有用处的fork 与 C语言缓冲区导致的
- 都知道C语言的缓冲区是遇到
\\n、\\r、fflush、程序结束、缓冲区满了
才会看时机吧数据刷新给内核中的缓冲区,且C缓冲区对显示器是行刷新,对普通文件是全刷新
fork后父子数据是共享
的,那么fork前父进程输出的数据没有刷新,那么就留在C缓冲区
(FILE 结构体,也就是数据),当父亲将数据刷新时,那么就会写实拷贝
这就是为啥输出到屏幕上是正常的,但是重定向后却变得怪异起来
解决怪异现象(提前全刷新,防止写实拷贝)
int main()
const char *msg0="hello printf\\n";
const char *msg1="hello fwrite\\n";
const char *msg2="hello write\\n";
printf("%s",msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1,msg2,strlen(msg2));
fflush(stdout);//提前进行全刷新
fork();
结果
问
为啥要存在C语言缓冲区 ? 不是有内核系统自带的缓冲区吗?直接刷新的系统的不香吗?
答
我觉得是提高效率,听个故事,现在是早上,你爹现在每5分钟给你2元,并会给你5次,2元只可以买一个包子,那么你是会拿到钱直接去买包子再回来等,还是等钱全部到手再去统一一次性买呢?
刷新策略如图所示 : >
2.6 文件系统
上述介绍的都是打开的文件,那么下面来介绍一下没有打开呆在磁盘上的文件吧!!!
这里有一个问题就是打开的文件要管理,那么没打开的文件需要进行管理吗?
那一定要管理呀,不然你如果需要打开文件去哪里找?搜索整个磁盘吗?
硬件部分:
机械硬盘,固态硬盘(ssd),光盘,磁带。现在磁带光盘应该已经退出时代的舞台了,现在基本用的都是机械硬盘或者是固态硬盘。由于ssd的工艺太高,那么我就介绍一下机械硬盘吧!
如图所示 : >
问
那如何吧数据写入固态硬盘中?open(“盘片/磁道/扇区/文件名”,……)?
答
你不觉得这个很麻烦吗?且不通用吧,假如你有钱了换个存储器呢?ssd那不是又要改。所以一切都需要抽象一下,
直接把底层看成一个线性结构,像磁带一样只是卷在一起了
(数组等),一个盘对应一个数组,可以把他抽象为多维数组(in t arr[盘面][磁道][扇区]
,如何存储器都是如此),底层维护这种关系的是对应存储器的驱动
补充1 :
都知道,需要执行一个程序一定要把程序的加载到内存中,那么程序底层不过就是代码数据,那这些都是存在磁盘中的。
内存IO大小是4字节,且他也是有自己基础单位是1字节
补充2 :
当存储器用统一的方式看待后,管理又是一个难题,假设存储器的大小为2个T,这时就是一个巨大的数组。分区不知道大家是否听过 (
分区:把大空间的盘分为多个小空间的盘,方便操作系统使用和管理
),没错这里就要进行分区。
//stata 文件 的截图
2. 6.2 文件系统
LINUX文件系统
- EXT2
- EXT3
- fs
- usb-fs
- sysfs、proc
- ……
每一个分区所使用的管理系统不一定是一样
的这个要看OS
EXT2文件系统介绍
inode
每一个文件,目录,都有一个Inode,且Inode中存放的是文件的属性信息,但是不包括文件名,
查看inode 在ls后面+i选项
问
通过上面的EXT2文件系统现在可以知道一个文件在磁盘上是如何存放的
(inode table 存放文件的属性,Data blokcks 存放数据
),但是他们是如何联系的呢?
答
创建一个文件那么就会申请Inode存文件的属性信息,在申请blocks存放数据内容,他们如何管理,
每个inode里面有一个数组存的是blocks的编号
(存放内容的block)
如图所示 : >
问
我好奇Block bitmap 和 Inode bitmap 是计数让我们知道Inode和blocks的使用情况?用一个整形来计数吗?
答
其实计数也不是不行但是,这里用了却更加妙,这里
使用的位图
(用一个bit位来标识状态),相比计数他可以很快的找到那个Inode和blocks使用或者没有使用
如图所示 : >
那么现在要操作一个文件我是如何找他的呢?
不知道大家是否还记得,目录结构其实是一个
树状结构
的,且上面也说过目录也是有自己的Inode(目录也是文件,也有自己的属性),那么他的内容是啥
呢?当然是里面文件的文件喽(与文件对应的Inode)
,毕竟一个当父亲(目录)不可能不记得自己儿子(文件)的名字(文件名)和生日(inode)树状结构如图所示 :>
目录Inode与blocks与文件的关系 如图所示 :>
问
目录也是文件,那我要找当前目录不是要找上级目录………
答
没错正是如此,他会一直一直的往上找知道根目录,执行过一次后就会缓存路径,现在可以解释一个现象了,就是当你第一次开机执行命令的时候是不是都比平时慢一点(自己验证下吧)?
问
文件名是否是一个文件的标识?理解ls后就可以看到存在磁盘上文件的属性信息?
答
当然不是表示一个文件的是Inode。
ls后,直接区当前目录找文件,并通inode返回文件的属性
现在理解一下创建一个文件需要的操作
1.inode bitmap 和 blocks bitmap申请所需大小个数,2. 存储到inode中的block组中当前文件内容所需block的编号 3. 把文件名和inode的映射关系存到目录的block中
如图所示 :>
2.6.3 软硬链接
想要进行软硬链接就需要用到命令 ln
软硬链接的理解
如果用C++来理解
软连接是指针而硬连接是引用
,也就是说软连接有自己的空间,而硬链接就是本体
如图so是软ha是硬 : >你会发现硬的inode和文件是一样的,而软的inode是不同的,那么就说inode也有对应的block,他的block中存的就是文件的路径(类似windows中的快捷方式)
怪异现象(创建文件硬链接是1,创建目录确实2)
是否还记得ls的一个选项就是-a显示全部包括隐藏的文件?你会发现有一个 .的文件
他就是代表当前文件夹,..的文件
代表上级目录。当查看 .文件的inode
时发现居然是一样的
完结🎉🎉🎉🎉
上面就是博主总结的IO博客,如果对你有所帮助,请给我点个赞吧,点赞,点赞,点赞,给大家跳个舞!!!
以上是关于Linux基础IO——文件系统(上)的主要内容,如果未能解决你的问题,请参考以下文章
Linux系统编程:基础IO 上简单复习C语言文件接口 | 学习系统文件接口 | 认识文件描述符 | Linux下,一切皆文件 | 重定向原理
Linux系统编程:基础IO 上简单复习C语言文件接口 | 学习系统文件接口 | 认识文件描述符 | Linux下,一切皆文件 | 重定向原理
Linux系统编程:基础IO 上简单复习C语言文件接口 | 学习系统文件接口 | 认识文件描述符 | Linux下,一切皆文件 | 重定向原理