【Linux】文件

其实我们在C语言就学过文件操作,但是从语言的角度,我们只是说会用了关于文件的一些操作和函数,但其实它究竟是怎么回事我们其实并不明白,那么当我们学习到Linux操作系统的时候,我们才能更加深入的去了解这文件究竟是怎么回事
那么我们需要首先明确一些概念:文件=内容+属性 ,不能说我这个文件是空,那么就不占空间;访问文件都得先打开,然后通过执行代码的方式去修改文件内容,也就是文件要被加载到内存中;是进程打开的文件并且一个进程可以打开多个文件;一个时间段内,可能有多个进程,操作系统要管理这些进程,同时也可能有多个被打开的文件,操作系统也要管理这些文件,那么如何管理呢?先描述,再组织,就是说,操作系统要给每个文件创建一个结构体对象,对文件的管理就变成了对于结构体的管理。

C语言关于文件操作的函数

下面只是介绍了一小部分,如果想详细了解,可以去我的另一篇博客:

C语言文件操作详谈

那么下面先回忆一下之前C语言用的一些函数:

首先就是fopen ,并且我记得当时选项w最为奇怪,因为每一个用,那么之前的数据都会不见了,这不是跟我们的**输出重定向(>)**很像吗,因为它们都是每次使用之前的内容就会被清空

这段话的意思是:截断文件到长度0或者如果没有那么创建新文件

我们原来都是这么用的:

我们说如果w选项,当前路径下没有这个文件,那么就会在当前路径下去创建,那么进程怎么知道当前在那个路径下呢?我们说过进程启动时,会记录下自己的路径

所以进程就会在这个路径下创建新文件

并且我们还有一个选项a,叫做append(附加) ,这不是跟**追加重定向(>>)**很像吗

下面我们再看一下fwrite这个函数

基本的使用就是这样

我们可以确定的是,输入或输出一些东西必须要指定文件,就连键盘和显示器也不例外,因为Linux下一切皆文件 ,那么平时用printf/scanf 的时候也没打开键盘和显示器文件并且也没有指定文件啊(我们这么想是因为把键盘和显示器看成和普通文件一样了),那是因为首先stdout,stdin和stderror是进程默认打开的,不用手动打开,可以直接使用

其次为什么printf不用指定文件呢?因为它为了方便使用,是不用指定文件的,但是底层实现是封装了fprintf

fprintf是要加stdout的,所以printf就是这么实现的

Linux关于文件操作的系统调用

我们知道,对硬件进行修改只能通过操作系统 ,所以操作系统就必须提供系统调用接口 ,像fopen这样的库函数是语言层面的概念,为了实现语言的跨平台性和可移植性,所以它要封装系统调用 ,并且在不同的操作系统要封装各自的系统调用。所以我们下面就介绍一下Linux操作系统关于文件操作的系统调用open和close

open的第一个参数就是文件所处的路径,第二个参数就是之前说的类似于"w"、"a"的一些选项,常见的打开标志有:

O_RDONLY:以只读方式打开文件

O_WRONLY:以只写方式打开文件

O_CREAT:没有这个文件就创建

O_TRUNC:打开文件前会清空文件

O_APPEND:在文件尾追加数据

第三个参数就是我刚创建好一个文件,此时要给文件设置的权限,返回值就是文件描述符,其实就是一个整数,通过这个整数,就可以确定这个文件,具体是怎么确定的,这就跟底层实现有关了,我们后面会介绍一下

当我们用第一个open时,并且路径中没有,它需要创建,这时我们可以看到文件的权限是乱码

所以我们一般使用第二个,权限计算还是给定的权限减去权限掩码,我们把它们当成八进制数,比如:

666-002=664

并且通过umask()函数我们还可以在当前程序中设置权限掩码,并且这个权限掩码只在当前程序下生效

666-666=000

我们上面介绍了第二个参数,第二个参数要给一个整数 ,其实下面的一些"选项"就是一些 ,这些宏可以通过位运算决定整数的比特位,其实就是位图,从而达到不同的下面的选项,知道了这些宏的意义,我们就可以和之前fopen的不同选项 的功能对应上了,其实"w"选项不就是这三个选项的叠加吗

其实如果只有前两个的话原始文件中的内容并不会清空,而新内容就从头开始进行覆盖,就是这样一种现象

选项"a"不就是这三个选项的叠加吗

并且追加内容时会在下一行追加

所以我们说fopen底层就是封装了open ,并且不同的选项底层就对应了不同的宏,我们上面说stdin、stdout、stderr每个进程都会默认打开,并且它们的类型是FILE* ,这是C语言层面的类型,本质就是一个结构体,既然Linux要通过文件描述符来确定一个文件,C语言要通过FILE*的对象,所以FILE*结构体中肯定有文件描述符

为了探究文件描述符到底是什么,我们可以连续创建一些文件看看它们的文件描述符之间有什么规律

文件描述符是从3开始依次增长,那0,1,2呢?其实0,1,2就分别是程序默认打开的stdin,stdout和stderr,并且这个数字其实就是**数组下标,**什么意思呢?

我们知道,操作系统不仅要进行进程管理,还要进行文件管理,对于进程的管理我们知道是通过PCB,就是一个结构体对象;同样,对于打开的文件(不管以什么样的方式打开),操作系统也是要创建一个结构体对象(比如我们叫做struct file)进行管理的,我们可以把它们之间的关系大致理解为这样:

就是说:一个进程的PCB中存着此进程打开的文件列表的指针,通过这个指针可以找到打开的文件列表,而这个文件列表中也是存着每个文件管理的指针,通过这个指针就可以找到操作系统对文件进行管理的结构体了

这就是为什么我们可以通过文件描述符(一个整数),来确定唯一一个文件了

并且要注意,我们图中的缓冲区是操作系统给每个打开的文件提供的,和下面说的C语言库函数提供的缓冲区不是一个概念
既然stdout等是自动打开的,我们可以关闭stdout 试一试,还不能关这个,因为一旦关了就打印不出东西来了,我们可以关掉stdin,于是新打开的文件的文件描述符就会从最小的没用到的0开始

这个_fileno就是FILE*结构体中文件描述符变量的名字
所以我们就可以利用上面的规则实现重定向,比如printf默认向stdout中打印,其实它认识的是文件描述符为1,所以我们就可以这样,将想打印的内容打印到一个文件中而不是显示器


这样也确实比较麻烦,并且还要了解文件描述符的分配规则,其实也不知可以这么做,系统还提供了系统调用 来供我们使用,这个就是通过拷贝指针来实现目的,就是上面图中中间那个图里边的指针

于是我们就可以这么用:

就是让数组下标为1的里边的内容变成 数组下标为fd的里边的内容,这样printf打印肯定还是向数组下标为1的里边的指针指向的文件打印,这时文件就变成log.txt

完善myshell

既然已经明白了重定向的原理:就是通过文件描述符实现向任意一个文件中写入,我们就可以完善一下我们之前写的myshell

C语言缓冲区

相关推荐
深耕AI1 小时前
【完整教程】宝塔面板FTP配置与FileZilla连接服务器
运维·服务器
serve the people1 小时前
Prompts for Chat Models in LangChain
java·linux·langchain
李昊哲小课2 小时前
Ubuntu 24.04 MariaDB 完整安装与配置文档
linux·ubuntu·mariadb
无聊的小坏坏2 小时前
从单 Reactor 线程池到 OneThreadOneLoop:高性能网络模型的演进
服务器·网络·一个线程一个事件循环
AI智域边界 - Alvin Cho3 小时前
Bloomberg、LSEG 与 MCP 缺口:为什么尚未发布完整的 MCP 服务器,以及多智能体系统如何解決这问题
运维·服务器
人间打气筒(Ada)3 小时前
zerotier内网穿透部署(rockylinux部署本地服务器)超详细~~~
linux·内网穿透·内网·公网·zerotier·穿透
Elias不吃糖4 小时前
Git常用指令合集
linux·git
_OP_CHEN4 小时前
Linux网络编程:(七)Vim 编辑器完全指南:从入门到精通的全方位实战教程
linux·运维·服务器·编辑器·vim·linux生态·linux软件
Maple_land4 小时前
第1篇:Linux工具复盘上篇:yum与vim
linux·运维·服务器·c++·centos
shizhan_cloud4 小时前
Linux 硬盘分区管理
linux·运维