文件
文件 = 内容 + 属性
访问文件之前,必须要打开它
文件在未打开之前,是在磁盘中的
当访问一个文件时,是进程在访问文件
进程在内存里,cpu只可以读取内存
但是文件存在磁盘中
所以可以看出,文件也会加载到内存中
否则cpu无法访问文件
一个进程可以打开多个文件,多个进程则会打开更多的文件
os也需要对文件进行管理
在内核中,文件 = 文件的内核数据结构(属性) + 文件的内容
研究打开的文件,就是在研究进程和文件的关系

fputs(),fwrite(),fprintf()均可向显示器写入
我们所使用的c文件接口,底层都一定要封装对应的文件类系统调用



open可以创建文件
如果没有创建则无法打开,需要加O_WRONLY | O_CREAT
如果已经存在文件,但是需要清空,则需要加O_TRUNC
想要新增新的内容,则是O_APPEND
但是由于我们创建的文件没有设置权限,所以不能正常使用
所以需要采用三参数的open
在第三个参数写上0666,设置权限
open的第三个参数是专门用来设置创建文件的权限的
close则可以通过传入fd来关闭文件
read和write也可以通过传入fd来进行读写
在语言上则可以通过调用fclose/fopen/fread/fwritr来进行文件操作

文件描述符

文件描述符 fd 本质是数组下标
是一个让进程和文件产生关联的文件描述符表的下标

进程启动,默认启动了三个标准的输入输出流
stdin,stdout,stderr
所以后续打开文件后,fd初始值为3,后续递加
文件加载到内存中存在一个struct file
里面存放着文件的属性
每一个文件都对应了一个struct file
一切皆文件



对于不同的设备,他们的属性类别是一样的,但是值可以不同
他们的IO方法一定是不同的
eg
keyboard的write()为空
struct file 相当于做了一层软件的封装,一切皆文件
这种封装被成为vfs(虚拟文件系统)
站在进程角度,一切皆文件
只需要通过文件描述符,看到struct file就可以了
在底层存在各种不同的设备
但是访问时,存在统一的struct file
这种技术就是C++中的多态
文本写入与二进制写入
当我们写入12345时,本质是写入"1""2""3""4""5"
当我们输入时,本质是将写入的内容转换成二进制存储
再将二进制根据要求转换成对应的数据
系统为了方便用户,会将read和从buffer中取数据并转换
直接封装成了scanf
本质是为了方便用户操作
同时也为了适应不同平台,提高语言的可移植性
如果没有封装
则只能使用系统调用,那么在linux中的代码,在windows中就不能用了

在系统中,没有所谓的文本,只有二进制
IO的基本过程和文件的内核级缓冲区
写入时,上层会通过操作表来调用write()
write则会将文件的内核缓冲区中的内容刷新入myfile
这个刷新的时机由os自主确定

read则是从当前文件的数据拷贝入内核缓冲区
如果再调用read时,还没有加载入内核缓冲区,进程则会阻塞
不管是read还是write,本质都是拷贝
修改的本质也是先读取,再写入
不管是写入,读取,还是修改
都需要将数据写入内核缓冲区
操作系统对每一个文件都有其对应的内核级缓冲区
当我们写的内容没有保存程序就突然退出时
就是因为文件还没有来得及将缓冲区中的内容写入磁盘中
数据才会因为进程关闭而丢失
重定向
进程打开文件,需要给文件分配新的fd
fd的分配规则,是给最小的,没有被使用的fd

正常来说,关闭1后,应该将1给到fd1
但是实际上,什么都没有打印出来
因为printf是向stdout输出
而stdout对应的就是文件描述符1
而1的内容已经不再是标准输出了,而是log1.txt
因此printf应该直接打印到 log1.txt 的文件中
这个操作就是重定向


这里必须加fflush才能够刷新出来
调用系统调用也是需要成本的(时间或空间)
调用将数据拷贝至文件的内核缓冲区的函数也需要消耗
因此为了减小成本,系统会选择在用户级缓冲区收集一定的数据后,再统一进行拷贝(write进行拷贝)
此时还没有进行拷贝
而之后又直接调用close将fd1关闭了,导致无法后续进行刷新
因此这里需要fflush(手动刷新)后才能打印出来
是因为系统还没有进行刷新,需要手动刷新
去掉close代码,或使用fclose都可以让fd1被正常写入

当一个进程在退出的时候,会自动刷新自己的缓冲区(所有的file对象内部,包括stdin,stdout,stderr)
fclose是c语言的函数,关闭进程时,也会自动刷新
普通文件,缓冲区写满再刷新
显示器文件,行刷新
操作系统中可以使用fsync()函数进行强制刷新
即将设备上的数据和内存数据同步

重定向就是更改文件描述符表内特定下标里的内容,就是文件对象的地址
更改后就能自动完成重定向
这个过程上层是不知道的
在Linux系统中,允许直接进行重定向
dup2
输出重定向,只需要将地址拷贝到要重定向的目标位置中即可

newfd是指需要修改的地方
oldfd是指不需要修改的地方
即将oldfd拷贝到newfd
eg.
输出重定向


追加重定向


他们没有什么区别
只是打开文件的方式不同
输入重定向
将地址覆盖0


就能将从键盘读数据,改成从文件中读取数据