个人主页:仍有未知等待探索-CSDN博客
专题分栏: Linux
目录
一、文件
1、文件的理解(浅层)
1.文件是什么?
2.文件操作的前提
3.文件的存储
4.一个进程可以打开多个文件吗?如果可以怎么管理的?
5.打开文件(fopen用法)
编辑
选项‘w‘:
选项’a‘:
6、输出重定向
重定向:
追加重定向:
2、文件的理解(深层)
1.文件操作
本质:
底层逻辑:
2.认识系统调用和文件操作
1、文件描述符fd
标准输出和标准错误都是向显示器中打印数据,他们俩有什么区别?
打开多个文件的时候,怎么存储在struct file_struct* fd_array[N]数组里面?
文件操作符表的分配规则?
2、打开(并创建)文件open
参数:
编辑
返回值:
底层逻辑:
3、关闭文件close
参数:
返回值:
4、写入文件write
5、读文件read
返回值:
3.理解Linux一切皆文件
4.C语言中文件类型FILE
二、重定向和缓冲区
1、重定向
1.本质
2.重定向的理解
3.重定义符号‘>’
2、缓冲区
1.stat:系统调用
参数:
2.缓冲区
为什么打印的时候不进行刷新就没有显示?
什么是缓冲区?
为什么要有缓冲区?
缓冲区是怎么刷新的?
a、提高刷新策略。
b、特殊情况。
C语言为什么要在FILE中提供用户级缓冲区?
为什么需要fd == 1、2?
perror和printf的区别?
文件和缓冲区的关系
一、文件
1、文件的理解(浅层)
1.文件是什么?
文件 = 属性 + 内容。
2.文件操作的前提
程序运行是文件操作的前提。
文件操作就是cpu在执行代码,本质就是进程在操作文件。
3.文件的存储
文件在没有打开的时候:文件的属性和内容存储在磁盘上。
文件打开的时候:文件的属性存储在内存里,文件的内容按需加载到内存中。
4.一个进程可以打开多个文件吗?如果可以怎么管理的?
一个进程可以打开多个文件。
管理的本质就是先描述,在组织。
管理一个文件:为了能更好的描述文件,os内部会用一个结构体来记录文件的各种属性,结构体内部会有一个指针指向一个文件缓冲区,里面存储着文件的内容。
管理一堆文件:将每个文件所对应的结构体用一个数组进行存储。
5.打开文件(fopen用法)
选项‘w‘:
以 ’写‘ 的方式打开文件。
1、如果该文件不存在,就在当前路径下,新建一个文件。
2、默认打开的时候,会把目标文件清空。
选项’a‘:
以 ’追加‘ 的方式打开文件。
1、 默认打开的时候,不会把目标文件清空,而是在文件内容的后面进行追加。
6、输出重定向
重定向:
echo “aaa” > log.txt
- 重定向一定是文件操作。
- 以 ’w‘ 的方式打开文件。
追加重定向:
echo “aaa” >> log.txt
- 追加重定向一定是文件操作。
- 以 'a' 的方式打开文件。
2、文件的理解(深层)
1.文件操作
本质:
进程和文件的交互关系。
底层逻辑:
---> 想要对文件进行操作。
---> 去磁盘中寻找文件(找到以指定方式打开,找不到则新建文件)。
---> 对其进行打开操作,然后需要外设对其进行输入或者输出操作。
---> 向文件进行写入,本质上是向硬件中写入。
---> os是硬件的管理者,用户没有权限对其进行修改。
---> 所以os提供了一系列的系统调用。
---> fopen/fwrite/fread/fpintf/scanf/printf/cin/cout都是对系统调用的封装。
2.认识系统调用和文件操作
1、文件描述符fd
文件描述符fd的本质就是存储文件信息的数组下标。
下面这个3个文件都是默认打开的。
fd == 0:标准输入,键盘 --- stdin。
fd == 1:标准输出,显示器 --- stdout。
fd == 2:标准错误,显示器 --- stdout。
标准输出和标准错误都是向显示器中打印数据,他们俩有什么区别?
在打印信息的时候,可以将标准输出和标准错误进行分离。
打开多个文件的时候,怎么存储在struct file_struct* fd_array[N]数组里面?
找到没有存储文件且下标最小的位置。
文件操作符表的分配规则?
查自己(进程)的文件操作符表,分配最小的没有被使用文件操作符fd。
由图可知:无论是读文件还是写文件,都将文件内容从磁盘中读取到文件级缓冲区中。
2、打开(并创建)文件open
参数:
pathname:
a.如果传的是路径,则在指定路径下打开文件。若没有该文件,则以指定方式,新建文件。
b.如果传的是文件名,则在当前路径下打开文件。若没有,则以指定方式,新建文件。
flags:
32位比特位,是指定打开文件的方式(有很多的系统的选项)。
O_WRONLY --- 只写
O_CREAT --- 创建
O_TRUNC --- 清空
O_APPEND --- 追加
O_RDONLY --- 只读
// 以只写、追加的方式打开文件,当当前路径没有文件时,创建文件;若有文件,则打开文件 int fd1 = open("test.txt", O_WRONLY | O_CREAT |O_APPEND, 0666); // 以只写、追加的方式打开文件,当当前路径没有文件时,不处理;若有文件,则打开文件 int fd2 = open("test.txt1", O_WRONLY | O_APPEND, 0666);
// 原理flags // 这样就完成了各种选项的筛选 #include <stdio.h> #define O_WRONLY (1) #define O_CREAT (1 << 1) #define O_APPEND (1 << 2) #define O_RDONLY (1 << 3) #define O_TRUNC (1 << 4) void func(int flags) { if (flags & O_WRONLY) printf("O_WRONLY\n"); if (flags & O_CREAT) printf("O_CREAT\n"); if (flags & O_APPEND) printf("O_APPEND\n"); if (flags & O_RDONLY) printf("O_RDONLY\n"); if (flags & O_TRUNC) printf("O_TRUNC\n"); printf("\n"); } int main() { func(O_WRONLY); func(O_WRONLY | O_CREAT); func(O_WRONLY | O_CREAT | O_APPEND); func(O_WRONLY | O_CREAT | O_APPEND | O_RDONLY); func(O_WRONLY | O_CREAT | O_APPEND | O_RDONLY | O_TRUNC); return 0; }
mode:
如果有O_CREAT选项,并且在指定路径下,文件不存在,则新建文件的权限就是mode。
最终的文件权限 = ~unmark & mode
返回值:
返回的是打开文件或者新建文件的文件描述符fd。
底层逻辑:
- 创建file。
- 开辟文件缓冲区的空间,加载文件数据(延后加载、按需加载)。
- 查进程中的文件描述符表。
- 将 file 的struct_file的地址存入文件描述符表中。
- 返回下标。
3、关闭文件close
参数:
fd 就是你要关闭的文件的fd。
返回值:
如果关闭成功,则返回 0 ;
如果关闭失败,则返回-1,并且设置了错误码errno。
4、写入文件write
将大小为 count 的 buf 里面的内容,写入到 fd 文件里面。
5、读文件read
从 fd 中 读数据,存储到buf中,count为能读取的最大字节数,即buf所指向的空间大小。
返回值:
返回值大于零,则代表实际读了多少字节。
3.理解Linux一切皆文件
对于fd == 0、1、2分别对应着不同的硬件,但是硬件是怎么和文件相关联上的呢?
4.C语言中文件类型FILE
建议使用的时候用语言层面的函数,即标准库函数。因为在不同的平台下面,系统调用的用法不一定相同。
FILE是一个C语言提供的结构体,里面一定封装了 fd 文件描述符。
FILE* fp = fopen("log.txt", "w"); // fp 里面封装了 fd:fp->fileno // "w" == O_WRONLY | O_CREAT | O_TRUNC
二、重定向和缓冲区
1、重定向
1.本质
是在内核中改变文件描述符表特定下标的内容 和上层无关。
2.重定向的理解
dup2的本质就是文件描述符下标对应的内容拷贝。
---> 将newfd文件关闭,把oldfd文件描述符放入newfd中,具体的效果就是下列的图。
对应printf函数,在底层就已经默认是对fd==1的位置的文件中进行打印。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
// 把显示器关闭
int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC);
dup2(fd, 1);
printf("I am test.c");
return 0;
}
3.重定义符号‘>’
格式:newfd > oldfd。
2、缓冲区
1.stat:系统调用
stat系统调用的作用是:获取文件或者目录的状态信息(文件的各种属性)
参数:
struct stat* buf:是一种输出型参数。指向的是存储文件属性的结构体。
off_t st_size:文件的大小(字节)
2.缓冲区
为什么打印的时候不进行刷新就没有显示?
struct FILE* 这个结构体里有语言级别的文件缓冲区。
fflush()的作用是:将语言级别的缓冲区刷新到操作系统里面的内核文件缓冲区。
什么是缓冲区?
缓冲区就是一段内存空间。
为什么要有缓冲区?
给上层空间提供高效的IO体验,间接提高整体的效率。
缓冲区是怎么刷新的?
a、提高刷新策略。
立即刷新。fflush(stdout):用户级到内核级缓冲 区、fsync(int fd):内核级缓冲区到外设
行刷新。是为了照顾用户的习惯 --- 显示器
全缓冲。缓冲区写满才刷新。--- 普通文件
b、特殊情况。
进程退出,系统会自动刷新。
强制刷新。
C语言为什么要在FILE中提供用户级缓冲区?
为了减少底层调用系统调用的次数,让使用C语言的IO函数效率更高。
为什么需要fd == 1、2?
我们写的程序,本质都是对数据在进行处理(计算、存储),所以我们需要知道处理时候发生异常的原因。
打印到显示器的里面的信息,可能是正确的,可能是错误的。1、2都是往显示器中打印,这样的话,在文件层面,正确信息和错误信息就分开了,./a.out 1> log.txt 2> err.txt。
perror和printf的区别?
perror本质上是向2打印,而printf本质上是向1打印。
文件和缓冲区的关系
每一个文件都有自己的缓冲区和文件操作符表。
每个进程在操作系统中通过文件描述符来访问文件,并且这些访问可能涉及到由操作系统或应用程序库管理的文件缓冲区。
谢谢大家!!!