模拟实现文件库
创建文件以便理解
自己想实现的文件接口,进行模拟实现
模拟的头文件要准备的头文件
open接口的实现
write接口的实现
fflush接口的实现flose接口的实现
文件实现
stdio.h
stdio.c
test.c
makefile
创建makefile
编译运行
执行后输出log.txt,看到动态的输出那么如果此时把sprintf的\n去掉,发现在写的时候没有找到\n就一直不刷新,最后close的时候强制刷新 发现在执行中找不到log.txt的数据了而当close的时候才找到被强制刷新出去
这说明写入的时候我们对应的数据刚开始就没有在os内部文件内部,而是缓存在了自己定义的缓冲区里,说白了我们写了一大推的数据,但是所有数据都在你的file对象的缓冲区里,后面自动的帮我们做了一次结束时刷新一次,我们写了10次但是最后刷新了一次即只调用了一次系统调用,原本带\n是刷新10次。缓冲区暂时把数据缓冲起来,最后缓存数据量足够多经过一次IO就从内存里搬到os或文件里,大大减少IO次数提高效率;
C语言为什么要在FILE中提供用户级缓冲区?为了减少底层系统调用的次数,让使用C IO 函数(printf,sprintf等)效率提高(只是把数据放到缓冲区,我这个函数就返回了,至于这个数据怎么刷新由库决定)
0 1 2
我们写的程序,本质:都是对数据在进行处理(计算,存储...);
只要对数据处理就决定了--数据从哪里来(0),数据哪里去(1),用户看不看到这个过程;
理解2
创建一个c语言文件,并向stdout和stderr写入
可以看到1和2都指向同一个显示器文件,要不然不可能向同一个显示屏打
那么这时候重定向到log.txt可以看到差别为什么向显示器打的stderr不向log.txt打,只有stdout打入了,这又是为什么呢?
首先我们要理解'>'是标准输出重定向,也就是说只会更改1号fd里面的内容
为什么需要2呢?
因为在正常的情况下,我们平时在写程序的时候我们输出的消息有两类信息:正确的 错误的;
不管是正确的还是错误的都是向1号去打,但是实际上呢,我们如果写代码,正确的常见的信息往1里打,而一旦出错了往2里打就行,这样的话未来我们只需要做一次重定向就可以将正确的信息和错误的信息分开了;
1> ok.txt:将标准输出(文件描述符 1)重定向到文件 ok.txt。所有本来会输出到终端的内容,现在将会写入到 ok.txt 文件中。2>err.txt:将标准错误输出(文件描述符 2)重定向到文件 err.txt。所有错误信息或警告,本来会输出到终端的内容,现在将会写入到 err.txt 文件中。
如果想将所有信息都放到一个里面:
记住重定向会清空文件内容的 !每打开一个文件就在内核中创建一个struct file!
那么要区分一下perror:
此时重定向:发现没有成功
所以c语言当中的perror,例如perror(''open'')打开失败了本质是向2打印,为什么不用printf呢?因为printf是向1打印的;所以未来一旦有报错全部向2打入。
上面的都是在文件被打开的前提下;
未被打开的文件
但是linux中有很多文件,但是被进程访问打开的文件只是少数的;那么没有被打开的文件在哪里存放着呢? 就比如我写了一大推.c /.h 文件最后我只用了可执行文件,那么.c /.h都没被打开,那么他们在哪里呢?在磁盘上存放(即磁盘文件);
我们每次打开一个文件都是先找到这个文件,就需要在磁盘中找到,通过文件路径+文件名然后在磁盘中打开;
物理磁盘
了解磁盘的存储结构
我们现在已经知道磁盘是一个机械设备,而且他是一个外设,注定了磁盘比较慢因为根据冯诺依曼体系得知外设IO慢,要减少与外设的IO次数;
文件=内容+属性===都是数据,都是二进制数据。所谓把文件存储在磁盘其实就是文件在磁盘中占有几个扇区!
对磁盘的存储进行逻辑抽象
我们走一遍os对磁盘这样的设备进行管理和抽象;为什么要抽象?如果os使用CHS的话,硬件改了软件也要改,耦合度太高。不想让软件直接访问硬件,也是为了方便实现内核进行磁盘管理 ;