共识原理:
1.文件 = 内容 + 属性
2.文件分为打开的文件 和 没打开的文件
3.打开的文件: 谁打开? 进程! ----本质是研究进程和文件的关系!
根据冯诺依曼原理,文件被打开,必须先被加载到内存!不然CPU怎么访问它
那么是文件内容被加载?还是属性被加载?还是都被加载?
一定要先把属性加载到内存,内容要不要被加载取决于要不要对文件做修改,理想情况是都被加载
一个进程可以打开多个文件,则进程与打开文件数量关系为
进程:打开的文件 = 1 :n
则操作系统内部,一定存在大量的被打开的文件!
------0S要不要管理这些被打开的文件呢? ----怎么管理???—先描述,在组织 — 在内核中,一个被打开的文件都必须有自己的文件打开对象,包含文件的很多属性。struct XXX{文件属性; struct XXX*next}; 用双链表组织起来
4.没打开的文件:∶在哪里放着呢? 在磁盘上。我们最关注什么问题?没有被打开的文件非常多。文件如何被分门别类的放置好…我们要快速的进行增删查改…快速找到文件
如何存储?
本文目标主要先研究被打开的文件
1、以C语言为主,先回忆一下C文件接口
打开文件的路径和文件名,默认在当前路径下新建一个文件,fopen中当前路径是什么?
当前路径,进程的当前路径cwd —如果我更改了当前进程的cwd(chdir),就可以把文件新建到其他目录
ls /proc/进程ID 可以查看进程task_stuct的绝大多数属性,cwp就是进程环境变量pwd改了个名字来的
下面用fwrite进行写入一段字符串
更改字符串由hello linux -> abcd 发现并不是abcdo linux 而是只有abcd
w方式打开的特性:
如果以w方式打开,如果文件不存在会创建一个
w : 写入之前,会对文件进行清空处理,并且从文件开始写入
观察输出重定向,盲猜一波它本质一定是打开这个文件并且是w模式打开的,因为他会先清空
问题:对文件进行写入字符串时,要不要把\0也加上?
答:字符串以\0结尾,是你C语言的规定,和我文件有什么关系? ??
a 方式打开 追加写
则 > 和 >> 一定是打开方式的区别 w / a
C程序默认再启动的时候帮我们打开三个流,标准输入stdin,标准输出stdout,标准错误stderr
这三个输入输出流就是文件
在C语言看来 向显示器写入和向文件写入没有区别
因为Linux下一切皆文件,所以我们可以不用printf,直接用文件fprintf等接口直接向显示器写入
那么问题来了
1、如何理解 Linux一切皆文件?
2、三个标准流为什么所有语言都要支持,它到底是什么?怎么做到的?
2、过渡到系统,认识文件系统调用
文件在磁盘上,磁盘是外部设备,访问文件其实是在访问硬件!
根据计算机体系层状结构和操作系统不相信任何用户,则C语言访问文件的库函数一定封装了文件系统调用接口!利用系统调用贯穿操作系统访问硬件
认识几个文件系统调用
1、open
对于flags是标志位,代表只写/追加/没有创建新文件/清空…
我们要是自己写可能在形参直接干上2~3以上标志位,但其实一个整形本来就有32位,可以利用一个Bit位代表一个状态
我们可以用一种优雅的方式来完成这个工作。做到只传一个int flags来完成标记
手册中看到O_RDONLY,O_WRONLY,这一看就是个宏,只有一个bit位是1,代表不同的状态!
比特位级别的标志位传递方式:
falgs提供了很多宏,只有一个Bit位为1,他可以传递多种标志位做按位或的组合,在函数内部再一与,就可以调用不同的功能函数
再回到open上来
1、O_WRONLY不会创建文件,如果文件不存在就打开失败,open返回值为-1,则需要或上O_CREAT
2、即使标志位为O_WRONLY|O_CREAT 新建文件权限为随机值,所以创建文件必须告诉Open第三个形参权限
0666为八进制,rw- rw- rw- ,可结果是664 那么是因为掩码的存在
umask默认002 也是八进制 可以通过更改umask的方式来就让文件权限是666
系统调用umask
open周边问题
返回值fd是啥?
file descriptor : 文件描述符, 是一个整数
系统调用write
通过上面代码进行测试后,我们发现库函数fopen以w / a 方式必定封装了这两种系统调用
在接口层面fopen封装了open,那么在返回值数据类型FILE* fp和 int fd什么关系呢?
3、访问文件的本质
左边是进程与文件有关的,右边是文件管理,他们通过数组下标产生关联
操作系统对多个被打开的文件进行管理,先描述再组织,进程打开open某个文件就去文件描述符表中找一个空的位置把文件struct file的地址填进去,最后再把数组下标返回给上层用户,这样当你write时,通过fd就能从PCB找到文件描述符表在找到下标存着的文件对象指针找到你要写入的文件了
结论:int fd : 本质是数组的下标
如果在进程中open打开多个文件,发现确实是数组下标,而是都是从3开始,那么0,1,2呢?对于失败打开返回-1
细心的就会发现C语言默认打开的三个流正好对应了3个位置,但是操作系统不认识什么stdin,stdout,它只认识fd,即为0,1,2
为什么所有语言都支持这三个流,哪有这么巧,那是因为操作系统默认打开的,
别和我说你打开了三个流,而是操作系统打开了你直接用的,那么OS为什么要默认打开呢?
因为计算机开机时你的显示器和键盘已经被OS打开了,编程时必须键盘输入和显示器查看结果,所以语言默认都给我们打开了。开机识别后默认就打开显示器和键盘了,所以启动一个进程时只需要把你打开的显示器文件和键盘文件地址填入文件描述符表中就可以了,程序员天然需要这两个键盘输入和显示器看结果
验证:我们代码中直接向0,1 输入和输出,证实了上面的结论
回答上面遗留的问题
关于FILE是什么呢?
FILE是C库自己封装的结构体,这里面必须封装文件描述符
FILE结构体中封装了fd,fwrite调用系统调用write时才能通过指针指向的fd才能成功调用
验证:
结构体中可以随意访问成员,又不像C++还要权限
这三个家伙不就是FILE结构体指针吗,那我当然可以访问它的成员fd
问题
为什么我把显示器1关了,2还能打印呢?
1号关闭了,但是2 号还是只想显示器的,他们两个是分开的,所以往2号打印能显示
close与引用计数
一个文件是可以被多个文件打开的,比如1,2号都指向显示器
struct file中包含了引用计数count字段,包含了引用计数,几个文件描述符指向我,Count就是几,
所以close时,将count–,并且将文件描述符下标数组内容制空,之后判断引用计数count是不是为0,不为0文件还有其他人再引用,为0 了那么系统会回收这个对象
4、重定向 && 缓冲区