admin 管理员组文章数量: 1184232
文件=内容+属性
1. 内容(Content)
文件的内容是文件的主体部分,即存储在文件中的实际数据。这些数据可以是文本、图像、视频、程序代码或其他任何形式的信息。对于用户和应用程序来说,内容是他们直接与之交互的部分,
例如:
在文本文件中,内容就是文件中的文字。在程序文件中,内容是编译后的代码。在媒体文件中,内容是音频或视频数据。
2. 属性(Attributes)
文件的属性是指与文件内容相关的元数据(数据的数据),它提供了关于文件的额外信息,如文件的权限、所有者、创建时间和修改时间等。这些属性对于文件系统的管理非常重要,因为它们帮助操作系统执行安全控制、存储管理、以及其他一些必要的维护任务。常见的文件属性包括:
文件大小:文件占用的存储空间量。
创建日期和时间:文件被创建的日期和时间。
修改日期和时间:文件最后被修改的日期和时间。
访问控制列表(ACL):指定哪些用户或系统进程可以访问文件以及他们可以执行哪些操作(如读取、写入、执行)。
文件类型:文件是文本文件、二进制文件、可执行文件等。
所有者和用户组:文件的所有者以及与文件相关联的用户组。
被打开的文件:
- c语言打开文件:
FILE * fp = fopen ( " 333.txt " , " w " );
const char * a = " hello linux \n " ;
fwrite ( a , strlen ( a ), 1 , fp ); //中间那个是度的数据块的长度,后面那个是读几个数据块
fprintf ( fp , " %s \n " , " hello honey " );
C程序在打开时,默认打开三个文件,stdin,stdout,srderr
- 系统级别
umask(0);
int fd=open("log.txt",O_WRONLY|O_CREAT,0666);
//返回值,文件描述符
write(fd,a,strlen(a));
struct file 的作用与内容:
每当进程打开一个文件,操作系统内核中会创建一个 struct file 结构体。这个结构体用来描述并管理被打开的文件。
struct file 包含了多种属性,比如文件在磁盘上的位置、文件的基本属性(如权限、大小)、当前的读写位置、打开文件的用户等信息。
此外,struct file 还可能包含文件的缓存信息,以提高文件操作的效率。
全局文件表 (File Table):
系统内所有被打开的文件都通过一个全局的文件表进行管理。这个文件表实际上是一个链表,链表中的每一个节点都是一个 struct file 结构体。
进程的文件描述符表 (File Descriptor Table):
在每个进程中,有一个文件描述符表,这是一个包含 struct file_struct* file 指针数组的数据结构。这个指针数组的地址保存在进程的进程控制块(PCB)中。
当进程需要打开或关闭文件时,会在文件描述符表中进行相应的插入或删除操作。
文件描述符实际上是文件描述符表中的索引(即数组下标)。常见的文件描述符有 stdin (标准输入,值为0),stdout (标准输出,值为1),stderr (标准错误,值为2)。
文件的共享:
当文件被多个进程共享时(如通过 fork() 系统调用创建的子进程),不同进程的文件描述符表中的条目可能会指向文件表中的同一个 struct file。这意味着多个进程可以共享对同一个文件的访问。
总得来说,就是,每一个进程中有一个管理全部打开文件的struct file_struct的实例,它里面有一张struct file的指针的数组struct file*fd_array[],数组的每一项都是一个指针,指向对象的struct file,而每一个struct file就代表着一个被打开的文件。如果一个文件被打开,就好有一个struct file的指针加入struct file_struct实例中的struct file指针数组中。
新成为老的拷贝,就是新是变换的,新是被赋值的那个,老的是赋值的那个,最后只剩下老的
dup2(fd,1);//将输入到1的,改到fd
dup2(1,fd);//将输入到fd,改到1
缓冲区刷新策略(应用层,非操作系统层):
- 无缓冲-------直接刷新
- 行缓冲-------遇到\n再刷新-----显示器常用
- 全缓冲-------缓冲区满了再刷新------文件写入常用
进程退出也会刷新
为什么需要缓冲区
- 效率
- 配合格式化(比如,将int(123),在打印时,需要弄成字符输出到屏幕)
inode
目录也是文件,文件的内容就是该目录下的文件名与inode的映射关系,一个文件的名字与inode的映射,保存在其所在它的上级文件夹的data blocks中。
在Linux系统中,目录本身也被视为一种特殊类型的文件。目录文件的数据块不包含文件的实际数据,而是包含了一系列的目录项(directory entries),每个目录项至少包含以下两部分信息:
1.文件名:用户可识别的文件标识。
2.inode编号:一个指向inode表中特定条目的索引,该inode条目包含了文件的元数据(如文件权限、所有者信息、文件大小、指向文件数据块的指针等)。
打开一个文件时,操作系统做的事:
1. 路径解析
分析文件路径:操作系统首先需要解析文件路径(绝对路径或相对路径)。如果是相对路径,系统会将其与当前工作目录结合,以生成完整的文件路径。
遍历目录:从根目录开始,操作系统逐层遍历路径中的目录。对每个目录,系统查找目录项以中对应当前路径文件名的inode号,并通过inode号找到对应目录的数据块中存储的子目录的目录项,也就是文件名与inode的对应关系,得到子目录的inode号,再持续进行上面步骤,直到找到文件。
2. 访问权限检查
检查用户权限:在访问每个目录和最终的文件时,系统会检查当前用户是否有足够的权限(读、写或执行)来访问路径中的每个组成部分。这通常涉及到比较用户ID和组ID与文件和目录的权限设置。(对于每个目录,没有X就无法进入目录,没有r就无法查看文件,没有W就不能创建文件)
3. 读取inode
加载inode信息:一旦找到文件的inode号,文件系统会读取存储在磁盘上的inode。inode包含了文件的元数据,如文件类型、大小、权限、所有者、时间戳以及指向文件实际数据的指针。
4. 处理间接指针
数据块地址解析:如果文件较大,可能需要处理间接指针(一级、二级、甚至三级间接指针)来获取所有数据块的地址。这一步骤在打开文件时可能不会立即完成,而是在实际读取文件内容时按需进行。
5. 文件句柄分配
分配文件描述符:操作系统为打开的文件分配一个文件描述符(在用户空间)或文件句柄(在内核空间),这是一个引用打开文件的索引或标识符(fd)。应用程序通过文件描述符进行后续的读写操作。
6. 缓存和优化
数据预取和缓存:为了提高性能,操作系统可能会预取文件内容并将其缓存。这样,当实际读取文件时,可以直接从内存中获取数据,而不是每次都访问磁盘。
7. 返回控制权
准备使用:一旦文件被成功打开,并且文件描述符被分配给调用程序,操作系统就返回控制权到应用程序,应用程序可以开始进行实际的读写操作。
访问文件:
假如在没有内存缓存的情况下,linux下,通过./来访问当前路径的一个文件,./对应的inode是存储在../的数据块中,那么要想访问../的数据块,你就会需要访问../../的数据块,这样依次往上,最终必须从root文件开始,依次往下,才能读到./中的文件的inode,才能访问对应文件的数据块
而在有数据缓存的情况下:
1.访问当前目录(.) 在命令行中用.来表示当前目录时,例如通过命令./filename来访问某个文件,操作系统会直接使用当前工作目录的inode。当前工作目录的inode已经在你进入该目录时被加载到内存中,所以无需重新查找。
2.访问上级目录(..): 当使用..来引用上级目录时,例如在路径../filename中,操作系统同样会查看当前目录的inode中的目录列表,其中包括一个指向上级目录的特殊入口(..)。这个入口已经包含了上级目录的inode号,所以系统可以直接加载上级目录的inode而无需逐级回溯到根目录。
3.绝对路径访问(/file1/file2/file3/myfile):从根目录开始,首先访问根目录的inode(根目录inode通常是预先定义的,如在UNIX系统中常常是inode号2)。接着,操作系统查看根目录下的目录项,找到file1对应的inode号。然后加载file1的inode,再查看file1目录下的目录项,找到file2对应的inode号。重复此过程直到file3,并最终查找到myfile的inode。一旦找到myfile的inode,系统就可以加载它的inode信息,获取文件的存储位置、权限、大小等元数据,并执行后续操作,如打开文件
硬链接:取别名,拥有相同的inode
软连接:创建快捷方式,即创建一个文件,那个文件的内容是链接的文件的地址
Tips:缺页中断
就是有一个映射页表,左边是虚拟内存地址,右边是物理内存地址,如果有一个进程,有一些数据不经常用,他在把这个数据对应的虚拟内存填入页表左边的时候,右边不会填入物理内存地址或者填入一个什么值都没有的物理地址。
在虚拟地址的页表条目中,如果对应的物理页面当前不在内存中,该条目可能标记为“未映射”或包含一个特殊的标记(如置位的有效(valid)位为无效)。
当进程尝试访问其虚拟内存空间中的一个地址时,如果该地址的页表条目未映射到任何物理内存或标记为无效,就会触发缺页中断。缺页中断是由内存管理单元(MMU)检测并触发的,当它发现没有有效的物理地址与请求的虚拟地址关联时。
缺页中断触发后,会进行一系列检查,检查全部通过后,就会将数据从外设硬盘中读入对应的数据,将其加入物理内存,然后将物理内存地址填入页表的右边,然后更新页表,将虚拟地址对应的条目设置为指向新的物理地址,并重新标记为有效。
动静态链接:
静态链接
在静态链接中,应用程序在编译时会将所有需要的库函数直接复制到最终的可执行文件中。这意味着可执行文件包含了所有它需要的代码,包括任何库中的代码。
优点:
自足:静态链接的程序包含了所有必需的代码,不需要任何外部库就可以运行。这简化了部署和分发,因为你只需要处理一个文件。
性能:启动时间通常比动态链接的应用程序更快,因为加载程序时不需要额外的链接步骤或搜索和加载库的开销。
缺点:
文件大小:静态链接的可执行文件通常大小较大,因为它包含了所有使用到的库的代码。
更新与维护:如果库中的代码需要更新(例如,修复安全漏洞),整个应用程序需要重新编译和重新部署。无法仅更新库文件。
动态链接
在动态链接中,应用程序使用的库在编译时并未被复制到可执行文件中。相反,库保留为单独的文件(通常是 DLL 或 SO 文件),在程序启动或运行时加载。
优点:
减少资源使用:多个程序可以共享同一份物理内存中的库代码,减少了总体内存占用。
更新简便:更新库文件不需要重新编译依赖它的应用程序。只需替换库文件,所有依赖这个库的应用程序在下次启动时可以直接使用更新过的库。
缺点:
依赖问题:如果所需的库不在系统上,或者库的版本不兼容,程序可能无法运行。这种情况通常被称为“地狱依赖”。
性能开销:动态链接的应用程序在启动时可能需要更多的时间来加载所需的库。此外,调用动态库中的函数比调用静态链接的库函数略慢,因为涉及到额外的间接层。
动静态库:
静态库:libXXX.a
动态库:libYYY.so
静态库编译命令
gcc main.c -o 111 -I ./lib/include -L./lib/mymathlib -lmymath
注:-I指定头文件所在文件夹,不用指定文件
-L指定库文件.a所在文件夹,虽不用指定文件,但要紧跟-l +库名字,库名字:去掉lib前缀与.a后缀
.a的意义,不想给人家看实现的源码
生成文件
export LD_LIBRARY_PATH=/XXX/XXX/lib:$LD_LIBRARY_PATH
上诉命令可以将lib位置加入系统默认搜索的动态库地址(只在运行这个命令的终端有用)
在默认搜索路径里搞一个同名的软连接也可以,甚至直接把库文件拷进去也可以
版权声明:本文标题:从头开始:了解操作系统文件系统的基本属性 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1773203352a3559355.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论