仿京东网站后台外贸seo营销ppt
web/
2025/9/28 7:17:43/
文章来源:
仿京东网站后台,外贸seo营销ppt,三亚兼职网站,基于jsp的精品课程网站建设个人主页#xff1a;Lei宝啊
愿所有美好如期而遇 前言
本篇文章将会介绍#xff0c;磁盘中是如何存储一个文件#xff0c;磁盘中的文件是如何从磁盘中加载进内存#xff0c;与进程又有怎样的关系#xff0c;我们写的代码变成可执行程序执行起来时如何完成对文件的一系列…个人主页Lei宝啊
愿所有美好如期而遇 前言
本篇文章将会介绍磁盘中是如何存储一个文件磁盘中的文件是如何从磁盘中加载进内存与进程又有怎样的关系我们写的代码变成可执行程序执行起来时如何完成对文件的一系列操作。
那么接下来我们将会是一块整体长篇大论去提及。
详谈
我们这里提到的磁盘是机械磁盘他大概长这样 我们可以看到几个盘片这些盘片就是磁盘用来保存数据在微观上他是一块块的小磁块分为南北极也对应着我们常说的比特位的0和1对每一块盘片我们又给他分了磁道和扇区就像这样 这样一个扇区能够存储数据的大小为512字节当然也可以更多我们这里提及的是一般情况下。
那么磁盘是如何定位数据的呢我们上面的图中有一个磁头通过磁头定位盘面再确定磁道最后是扇区(磁盘IO的基本单位是扇区)。
上述就是磁盘的物理层面那么我们是如何管理这些数据的这就要提到操作系统由操作系统对他们进行管理怎么管理呢一个磁盘能够保存的数据并不小几百GB到几个T都有。
于是有了逻辑层面上将磁盘的物理存储抽象为逻辑存储操作系统对磁盘的管理也就变成了对一个个结构体的管理先描述再组织而这种管理方式就需要加载文件系统来进行管理。
我们说磁盘很大我们不好管理于是就有了分区管理将磁盘进行分区每个分区的管理可以借由不同或相同的文件系统进行管理而每个分区又有分组借由这种方式来管理磁盘。
分组中的管理方式不同的文件系统有不同我们这里介绍ext2文件系统中分组的管理方式 super Block超级块存储着文件系统的信息这个块如果损坏这个区的信息就损坏了但是并不是每个分组都会有这个超级块由操作系统决定部分分组有这个超级块并且他们的信息是同步的一个损坏了去其他分组再同步过来。
Group Descriptor Table(GDT)块组描述符保存着这个组的一系列信息块损坏这个组信息损坏。
Block BitmapData BlocksData Blocks就是操作系统分配的只存储文件内容的数据块里面有一个个的基本数据块对应着逻辑抽象的8块扇区能够存储数据的大小为4KB。Bitmap的作用就是标记使用和未使用的数据块在保存文件内容时直接遍历位图查询bit位为0的位置并记录下这个位置在位图中相对于起始位置的偏移量在Data BLocks中直接根据这个偏移量索引找到空闲数据块。
inode Bitmapinode Tableinode是一个结构体保存着文件的各种信息同时里面保存着一个inode编号以及int block[15]。
我们可以根据这个block[15]数组来将文件的内容和属性对应起来但是block看起来只能存储60KB的内容文件不是可以很大吗这个数组有15个下标从0~14其中0~11存储的LBA地址对应的数据块里存放的就是文件内容而下标为12,13的LBA地址对应的数据块里存放的不是文件内容他存放的仍然是LBA地址而这个地址再去索引找到的数据块里存放着的才是文件内容我们也将这个过程叫做二级索引但是即使这样文件的内容依然不是很大也就扩大了1024倍也就是60KB*1024那么最后一个下标14他是三级索引这样就再次扩大了文件大小我们可以依据这样去存放更大的文件。
inode编号在一个分区内唯一标识一个文件关于inode编号我们需要到后面串起来才能理解这里暂时不做过多解释。
inode Bitmap这个位图标识着哪个inode是空闲的未存储文件信息。每一个分组中都有一个inode起始编号同时GDT中会保存着这个分组中可以保存多少文件信息我们在创建一个文件时操作系统会扫描一个分组的inode Bitmap找到在位图中为0的位置并记录下偏移量那么这个文件的inode编号就是当前分组的起始inode编号加上这个偏移量。
在我们查询一个文件时也是先由文件的inode编号确定分组再由inode编号减去分组起始inode编号剩下的值就是在位图中的偏移量根据这个值我们可以在inode Block中找到文件的信息并由int block[15]找到文件的内容。
其实这里有一个小点就是我们怎么知道文件的inode编号inode里虽然保存着inode编号但是我要找的就是inode我怎么知道inode编号这个问题我们后面解释。
现在我们知道了文件在磁盘中的存储现在我们要打开一个文件了怎么打开写一个代码编译链接成可执行跑起来就打开了表面上看起来的确是这样现在我们深入来探究一下。
好我们先来写一个代码 我们以只读方式打开一个文件并且读取他的数据可是这些代码在整个流程中扮演了怎样的角色底层到底是怎样完成这一切的
头文件里面保存着一系列库函数的声明这个我们是知道的。库函数的实现自然就在库中保存着在我们编译这个代码后和库文件进行链接形成可执行程序。
接下来我们介绍库库分为动态库和静态库在Linux中库默认是动态库静态库需要我们自己下载在Linux中动态库的后缀是.so静态库的后缀是.a。
库也是文件是由一些.o目标文件打包形成的那么为什么要打包? 我们假设今天老师布置了一个任务写一个程序只要可执行程序和main.c就行这在你看来非常轻松你分分钟在一个项目工程中写了7个源文件6个头文件编译运行通过了非常轻松但是你的同学他不会啊就问你要源代码但是呢你辛苦写的不想给他源文件可是又不好意思不给于是你转念一想反正老师只要可执行和main.c那好说我把头文件和除main.o的其他.o文件发给他他自己写一个main.c编译链接一下就行了于是你发给了他这些东西于是你的同学拿到了这些头文件和.o文件结合他自己的main.cgcc main.c xxx.o xxx.o ...... -o xxx写了一长串可算弄完了。
但是如果是很大的一个工程项目几百上千的文件还这么编译写都写烦了于是你又转念一想那怎么办那好把.o文件打包成库吧于是这样你就自己搞了一个库通过某种方式你分别弄出了动静态库。
将来你的同学再次使用时就很方便了。
我们写的代码也是如此在下载好一个编译器时他把一系列库文件也下载了下来当我们写代码时只需要包含头文件头文件中函数的实现我们链接时找库要就行。
接下来到了FILE* fp fopen...fopen就是一个库函数返回值是FILE*我们直接看来是这行代码他打开了文件实际上不是的而是可执行程序跑起来变成进程进程执行到这行代码时才打开了文件。
这里我们需要说到事实上fopen这种函数是为了更好的给初学者使用所以封装了这么一层对于访问硬件这种事情操作系统是不允许普通用户进行访问的他会提供一个系统调用也是函数由C/C编写大部分是C只有使用这个系统调用才能够进行访问操作系统认为这才是安全的因为这个函数是操作系统提供的所以我们要想访问一个文件就需要在fopen中封装访问硬件中文件的系统调用而这个系统调用就是open()。
open这个系统调用返回值是int类型的fd叫做文件描述符参数是文件路径和打开方式以及一个模式 文件路径似乎我们挺熟啊是的其实我们不熟什么是文件路径? /dir/dir1/dir2是这样吗这么说是没错但是我们这里要谈到的路径在磁盘中是没有这个概念的也没有目录这样的概念只有文件只有操作系统将他们的信息加载进内存中才形成了目录以及路径这个概念那么现在我们来解释inode编号的问题。
目录是不是文件是的那么目录也有内容他的内容保存什么呢文件的inode编号和文件名的映射关系。
我们假设有这样一个路径/dir/test/haha.c那么haha.c的inode编号和他的文件名就保存在test目内容中可是test目录也是文件啊所以test的inode编号和文件名保存在dir内容中dir也是文件dir的inode编号和文件名也保存在根目录下/那么根目录呢他的inode编号是确定的在操作系统加载后他就确定了我们之前提到过分区操作系统管理文件不是通过分区吗怎么会根据根目录呢是的根目录是挂载到分区上的分区的访问都是通过挂载到一个目录下进行访问的
于是我们就可以根据文件路径逆向递归般的可以找到文件inode编号。
可是我怎么找到文件的路径呢通过进程进程的路径是确定的在运行起来时就已经确定了所以在我们查找一个文件时如果我们给出文件的绝对路径那么我们就使用这个绝对路径如果没有给出那么就使用这个进程的路径文件名去查找这个文件。
于是到这里我们就清楚了路径的问题。
flag又是什么鬼他是宏通过一系列宏来决定打开文件的方式只写只读读写清空追加等等mode就是创建的文件的权限决定了拥有者所属组其他人对文件的读写执行权限。
于是通过这个系统调用我们就打开了文件但是怎么打开的呢这里就提到文件描述符我们说open这个系统调用返回了文件描述符文件描述符又是什么
这里提到stdin(标准输入)stdout(标准输出)stderr(标准错误)三者都是FILE*类型这三个文件在进程启动时会自动打开并默认分配0,1,2这三个文件描述符保存在他们指向的FILE中的fileno这个值中。 流程是这样的open打开一个文件这个文件被从磁盘加载进内存中创建struct file结构体里面保存着这个文件的信息和内容接着这个文件的地址被填入一个数组中填入位置的下标就是文件描述符接着这个下标被返回给openopen被封装在FILE中于是这个fd给到了FILE中的fileno最后由fopen返回这个FILE的地址最终我们就打开了这个文件。
fwritefread等等函数与文件相关的函数都需要这个fd所以这也就是为什么我们打开文件后返回的fp要传递给他们因为它里面封装了fd。
当我们向文件中写入内容的时候也并不是直接就写进了struc file结构体里而是写进了一个缓冲区这个缓冲区就是FILE里的一个buffer[]然后由write系统调用将这个缓冲区里的内容写进struct file里的缓冲区再由操作系统刷新到磁盘。
fclose就是通过fd清空fd_arrry对应位置的内容这个文件也就被关闭了。
现在我们解释了代码后代码就要开始编译链接成可执行程序了那么可执行程序内部是什么样子呢我们对他进行反汇编可以看到这样样一张图 我们可以看到一些汇编语言以及一些地址事实上这些地址就是我们在调试程序时所看到的地址都是已经提前编译好的这些地址也被叫做虚拟地址注意不是物理地址。
当程序要执行时操作系统会为他创建一个进程控制块task_struct然后将程序的代码和数据加载进内存这两者结合起来就叫做进程。
当我们的可执行程序的代码和数据加载进内存时也就有了物理地址此时操作系统会将他的物理地址和虚拟地址在页表中建立映射关系并使用表头初始化进程地址空间。
如果程序链接使用的是静态库那么在可执行程序在链接时就已经将所需代码从库中拷贝进来在执行时就已经不需要这个库了如果使用的是动态库那么在执行时仍然需要将动态库加载进内存下面这张图就是链接静态库的程序。 那么现在我们来整体理一理这个过程
首先我们写好一份代码代码里使用了操作文件的函数(如果没写那其实解释起来更简单点)于是我们开始编译链接文件假设我们链接静态库这个说起来省事在形成可执行程序文件后这个文件里就有了一系列信息被分成了几个段代码段数据段以及表头等等同时里面的代码和数据都会有相应的地址在磁盘中我们称逻辑地址加载进内存后我们称虚拟地址其实二者是等价的接着要开始执行这个程序创建进程控制块加载程序代码和数据进程形成页表构建完成进程地址空间以及CPU内的寄存器初始化完成开始执行我们的代码 由于main函数地址在初始化时就将地址交给了cpu中的程序计数器所以操作系统他是可以找到主函数入口的当执行到打开文件的指令时如果是读文件首先根据我们提供的路径(绝对路径就使用不是就拼接当前进程路径文件名)逆向递归找到这个文件的根目录也就找到了这个文件所在的分区因为分区是挂载在这个根目录下的而根目录的inode编号是确定的于是我们可以从磁盘中读到根目录的内容里面存放着他下面一系列文件的inode编号和文件名的映射关系这样一路解析下去我们就找到了我们要打开的文件如果是写文件并且文件不存在那么操作系统就先创建文件在一个分区的一个分组下去创建一个文件并返回inode编号将这个编号和我们输入的文件名建立映射关系填到父目录内容中一个文件也就创建好了。找到要打开的文件后将要打开的文件读进内存中建立struct_file结构体里面填充这个文件的信息和内容然后将这个结构体的地址填入到进程控制块中的struct files_struct *file指向的struct files_struct中的fd_arrry中并返回这个填入位置的下标于是后面对于文件的操作也就可以完成了。当代码全部执行完成后释放文件释放程序的代码和数据接着释放进程控制块程序执行完成。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/83199.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!