【Linux基础IO】文件IO、文件描述符、重定向本质

目录

一、前言

二、C语言文件IO

三、Linux文件IO

1.open

2.close

3.write

4.read

四、文件描述符

五、一切皆文件

六、输入重定向

七、重定向的系统调用


一、前言

本文先回顾了C语言文件围绕FILE*的IO的操作,随后介绍系统调用的open,close,write,read四个函数,接着了解Linux中围绕文件操作符fd的IO操作,探究文件描述符的本质是进程中文件描述符表的下标,最后理解重定向的本质底层原理。


二、C语言文件IO

在正式讲解Linux中如何对文件进行IO前,我们先简单回顾下C语言中的文件IO操作。

fopen用于打开文件:FILE* fopen(const char *pathname, const char *mode);

其中pathname是打开文件的路径,可以是绝对路径或相对路径,mode则是打开文件的模式,有以下常见的模式:

  • "r":只读,若文件不存在则报错
  • "w":只写,若文件不存在则创建同名文件,打开文件时清空文件原有内容
  • "a":只写,若文件不存在则创建同名文件,打开文件时不会清空,在末尾追加内容

该函数会返回一个FILE*的指针,C语言中通过操作这个FILE*的指针来控制文件的IO。当我们打开文件时,每个被打开的文件都会在内存中开辟一个相应的文件信息区,用来存放文件相关信息,而它们都被保存在名为FILE的结构体中,我们通过FILE*这个指针来操纵文件。

C语言程序在启动的时候,就默认打开了三个流:stdin,stdout,stderr ,之前我们说过Linux中一切皆文件,这三个流其实就是向显示器和键盘对应的文件进行读取、写入而已。


三、Linux文件IO

每个语言都有一套自己的文件操作函数,但这都是上层语言的变化,其底层都是封装的系统调用,因此接下来就学习文件的系统调用:open,close,write,read

在C语言中文件通过FILE*来操纵,而在Linux中所有IO操作都围绕着文件描述符fd

1.open

这个flag虽然是int类型,但实际上是当做位图来使用的,通过判断某位是否为1来查看对应哪个选项,可以使用以下这些宏定义,若要使用多个,用 |(或)连接

例如O_WRONLY | O_CREAT 的意思就是以只读形式打开文件,若不存在则创建

至于open的第三个参数mode,那是用来控制文件的初始权限的,若没有传参,生成的文件的权限值是随机的。但mode控制也仅仅是初始权限,要经过umask过滤后得到的才是实际权限,因此要先通过umask(0000)将权限掩码置为0,在传参mode(例如0666),那么最后该文件的权限就是0666,也就是rw-rw-rw-


2.close

 

close就很简单了,传入对应文件的文件描述符fd来关闭文件,成功则返回0。


3.write

write是向指定文件描述符fd的文件中写入数据,数据来源于buf,写入的字节数是count,调用成功后返回写入到文件的字节数。

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

4.read

read是从文件描述符fd对应的文件中读取数据到buf中,count是最多读取的字节数,读取成功返回读取到的字符个数,读取到文件的末尾则返回0,读取发生错误返回<0的数。


四、文件描述符

文件描述符fd是int类型的,那么它是否有什么规律呢?

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

结果是3,4,5,6,可以发现新文件描述符是依次递增的,那么0,1,2号到哪去了?实际上0,1,2号正是我们之前提到的stdin,stdout,stderr这三个标准文件。后续如果把3号文件关闭了,那么新创建的文件fd就是3而不是7了。

在操作系统内很可能有多个打开的文件,那么OS就必须要来维护这些资源,和之前的进程管理一样,先描述再组织,用struct file来描述文件属性,再用链表将其连接起来:

这是文件在OS内部的管理体系,而每个进程也都知道自己打开了哪些文件,因此进程的PCB中会保存一张文件描述符表(本质是结构体指针),这个表中存放了这个进程打开的所有文件:

从这张图中我们不难看出,其实文件描述符的本质就是数组的下标,每次打开文件都会去数组中扫描没有被使用的下标,来充当新打开文件的fd


五、一切皆文件

之前说过,底层不同的各种硬件,如磁盘、显卡、网卡等都对应了不同的操作方法,但这些设备的核心功能就是读写,也就是IO

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


六、输入重定向

根据上述的结论,我们知道0,1,2对应的是stdin,stdout和stderr,如果此时我们将1号进程关闭,再打开一个新文件,这个文件的文件描述符是否就是1,那么此时再运行会发生什么?

我们可以发现新打开文件的fd确实是1,但此时printf和fprintf都不会输出到屏幕了,而是输出到test.txt中,说明stdout是只认1号文件标识符的,不管此时1还是不是标准输出。

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


七、重定向的系统调用

如果每次重定向都要先关闭一个文件再来操作就十分麻烦,可以直接使用系统调用dup或dup2来完成重定向操作

一般都使用dup2,它的意思是将oldfd拷贝给newfd,也就是说newfd对应文件的fd变成了oldfd,例如我们要将打印在显示器(1号)的信息打印在test.txt中就这样使用:

open("test.txt",O_WRONLY | O_CREAT);
dup2(3,1);//oldfd - newfd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值