windows下的文件构成
.内容
.属性
所以, 文件的构成为内容和属性。
文件 = 内容 + 属性
推此即彼, linux下的文件构成也是如此。
liunx下,文件 = 文件的内核数据结构(属性)+内容
深入理解c语言中的文件操作
在c语言中如果想要操作一个文件必须打开,这是为什么?
文件没有被打开前文件在哪里?-> 在磁盘里,被打开后被加载进内存。
还有,文件是由谁打开的? -> 进程。
所以,之所以文件被操作需要被打开,是因为,根据冯诺依曼体系结构,cpu只与内存直接打交道,不与磁盘直接打交道(间接打交道),所以cpu想要修改文件,就需要将文件打开加载到内存。
Linux OS,是如何管理文件?
顶层想法是,先描述,再组织。
liunx下,文件 = 文件的内核数据结构(属性)+内容
如果想解决以上的问题,需要了解一下进程与文件的关系。
系统调用接口
1.open
pathname: 填写想要打开文件的路径(绝对路径);也可以直接写想要创建的文件名,但该文件就是在进程所在的路径下创建的,这是为什么呢?
每个进程都有cwd(current work direcotry)指向当前的路径,所以编译器在编译时会自动补齐对应的路径信息。
该文件在 /proc/该进程的pid 下
flags: 标志位,标志位间用 |连接,标志位的类似于位图
常见的标志位
O_CREAT:创建
O_WRONLY:只写
O_RDONLY:只读
O_TRUNC:将文件清零
O_APPEND:追加
使用
int fd = open("log.txt",O_CREAT|O_WRONLY|O_TRUNC);//相当于fopen("log.txt","w");
int fd = open("log.txt",O_CREAT|O_WRONLY|O_APPEND); //fopen("log.txt","a");
int fd = opne("log.txt",O_RDONLY);//fopen("log.txt","r");
mode:就是创建文件时的umask权限,每个进程都有一个自己的umask
返回值是int:是文件描述符fd(>=0),如果open打开失败那么就返回-1。
2.write
3.close
进程与文件
文件描述符的分配规则
在c语言中,我们知道一个进程被启动会默认打开三个标准输入输出流
文件流 文件描述符
stdin 0 (标准输入)
stdout 1 (标准输出)
stderr 2 (标准错误)
#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("log.txt",O_CREAT|O_WRONLY|O_TRUNC,0666);
int fd1 = open("log1.txt",O_CREAT|O_WRONLY|O_TRUNC,0666);
int fd2 = open("log2.txt",O_CREAT|O_WRONLY|O_TRUNC,0666);
int fd3 = open("log3.txt",O_CREAT|O_WRONLY|O_TRUNC,0666);
cout<<fd<<endl;
cout<<fd1<<endl;
cout<<fd2<<endl;
cout<<fd3<<endl;
return 0;
}
从结果可以看出,一个进程运行起来后,打开的文件是从三开是往后分配的,并且是连续分配的,原因是0 ,1,2被默认打开了。
分配规则:分配最小的,没有被使用的
为什么文件描述符是一个整数呢?
LInux2.60的内核源代码
每个进程里都有维护的files_struct指针,files_struct中又维护这一个struc file的数组,struct file就描述了了一个文件,所以每个进程就可以通过open返回的fd(int)就可以标识一个文件。
理解linux os下一切皆文件
说直白一点就是linux os就是通过操作文件的方式来对各种外设来进行读写操作的。
来探究一下怎么办?
struct file中维护了操作表,在操作表中有相应功能的函数指针来完成读写,因为不同的外设的I/O方式不同,所以初始化函数指针不同,在struct file看来调用向应的write和read函数就可以从相应的设备中读取数据。
这种调用相同的函数但函数执行的结果会根据对象不同而不同的特性在c++中叫什么来着?
多态,以上的方式就类似于c++中的多态。
举个例子:
linux在打开一个外设时,会描述这个外设(struct device),并通过特定的数据结构组织并管理,创建相应的struct file,struct file中相应的外设的特定的读写方法,这样系统就可以以文件的方式,读写硬件的数据了。
总结,通过struct file就可已屏蔽底层的不同,用统一的方法进行操作,而这个操作就是文件操作,所以在linux下一切皆文件。
vfs(virtual file system)虚拟文件系统
struct FILE
就linux而言
通过以上的了解可以知道c语言struct FILE就是封装的系统的struct file
fopen对应的就是open
fclose 则是close
为什么语言喜欢封装? c/c++
1.方便用户的使用
封装的函数,更容易让用户去理解和使用,不用过多考虑底层,因为封装都已经解决了底层的问题。
2.提过语言的可移植性
每个系统的调用接口不同,比如fopen底层使用的是类似于open的系统调用接口,在linux系统中调用的是linux的open,而在windows下调用的是windows下的open,这就导致同一份代码,用linux系统调用接口,而代码如果在windows就跑步了。
而封装解决了这个问题,使语言具有可移植性。
总结,一句就是封装屏蔽了系统接口的差异,使在不同的系统中调用该系统的接口。
内核级文件缓冲区
是什么?
内核缓存区 当一个用户进程要从磁盘读取数据时,内核一般不直接读磁盘,而是将内核缓冲区中的数据复制到进程缓冲区中。 但若是内核缓冲区中没有数据,内核会把对数据块的请求,加入到请求队列,然后把进程挂起,为其它进程提供服务。
为什么?
内存的读写快,而磁盘的读写慢,为了提高内存与磁盘建的I/O效率,通过将想要的数据先写入缓冲区,在刷新进磁盘的策略来减少I/O次数。
怎么样(如何刷新)?
注意:内核级文件缓冲区,不受用户操作,什么时候刷新,都由操作系统自主决定。
那么可能就有人要问了,fflush不是刷新缓冲区吗?
fflush是刷新缓冲区,但fflush()函数刷新的是用户级缓冲区