Linux文件系统相关数据结构及相互间的关系案例分析
Linux系统关系族谱图:应用程序、内核、驱动程序、硬件详解
Linux系统关系族谱图:应用程序、内核、驱动程序、硬件详解目前,Linux软件工程师大致可分为两个层次:01Linux应用软件工程师(ApplicaTIon Software Engineer):主要利用C库函数和Linux API 进行应用软件的编写;从事这方面的开发工作,主要需要学习:符合linux posix标准的API函数及系统调用,linux 的多任务编程技巧:多进程、多线程、进程间通信、多任务之间的同步互斥等,嵌入式数据库的学习,UI编程:QT、miniGUI等。
02Linux固件工程师(Firmware Engineer):主要进行Bootloader、Linux的移植及Linux设备驱动程序的设计工作。
一般而言,固件工程师的要求要高于应用软件工程师的层次,而其中的Linux设备驱动编程又是Linux程序设计中比较复杂的部分,究其原因,主要包括如下几个方面:1 )设备驱动属于Linux内核的部分,编写Linux设备驱动需要有一定的Linux操作系统内核基础;需要了解部分linux内核的工作机制与系统组成2)编写Linux设备驱动需要对硬件的原理有相当的了解,大多数情况下我们是针对一个特定的嵌入式硬件平台编写驱动的,例如:针对特定的主机平台:可能是三星的2410、2440,也可能是atmel的,或者飞思卡尔的等等3 )Linux设备驱动中广泛涉及到多进程并发的同步、互斥等控制,容易出现bug;因为linux本身是一个多任务的工作环境,不可避免的会出现在同一时刻对同一设备发生并发操作4 )由于属于内核的一部分,Linux设备驱动的调试也相当复杂。
linux设备驱动没有一个很好的IDE环境进行单步、变量查看等调试辅助工具;linux驱动跟linux内核工作在同一层次,一旦发生问题,很容易造成内核的整体崩溃。
在任何一个计算机系统中,大至服务器、PC机、小至手机、mp3/mp4播放器,无论是复杂的大型服务器系统还是一个简单的流水灯单片机系统,都离不开驱动程序的身影,没有硬件的软件是空中楼阁,没有软件的硬件只是一堆废铁,硬件是底层的基础,是所有软件。
数据结构与操作系统内存管理和文件系统的关系
数据结构与操作系统内存管理和文件系统的关系数据结构是计算机科学的重要基础学科,与操作系统的内存管理和文件系统密切相关。
本文将探讨数据结构在操作系统内存管理和文件系统中的应用和作用。
一、数据结构在操作系统内存管理中的应用操作系统负责管理计算机的内存资源,其中涉及到内存的分配、使用和回收等操作。
数据结构在这一过程中起到了关键的作用。
1. 内存分配在操作系统中,内存被分为不同的区域,如操作系统内核区、用户程序区等,每个区域有不同的内存需求和特性。
数据结构中的链表、栈和队列等数据结构被广泛应用于内存分配算法中,以实现高效的内存分配。
以链表为例,操作系统可以利用链表数据结构来维护内存块的分配情况。
通过链表节点的链接关系,可以记录每个内存块的起始地址、大小以及是否被分配等信息。
在分配内存时,操作系统可以根据链表的状态来查找合适的空闲内存块,以提高内存的利用率。
2. 内存管理操作系统需要对内存资源进行管理,包括内存的分配、使用和回收等。
数据结构在内存管理中起到了辅助和支持的作用。
例如,操作系统可以利用树状结构来管理内存中的页表,以实现虚拟地址到物理地址的映射。
通过树的层级结构,操作系统可以快速查找并定位到对应的物理地址,实现高效的内存访问。
3. 内存回收在操作系统中,当某个进程结束或者释放了一部分内存时,操作系统需要回收相应的内存资源,以供其他进程使用。
数据结构在内存回收过程中起到了辅助和优化的作用。
例如,操作系统可以利用链表、栈和队列等数据结构来管理已分配内存块的释放情况。
通过合理的数据结构选择和算法设计,操作系统可以高效地回收内存,并避免内存碎片的产生,从而提高内存的利用率。
二、数据结构在操作系统文件系统中的应用文件系统是操作系统中用于管理和操作文件的一种机制。
数据结构在文件系统中扮演了重要的角色,用于组织和管理文件的存储和访问。
1. 目录结构文件系统中的目录结构是对文件的组织和管理方式的抽象表示。
数据结构如树状结构、图等被广泛应用于目录结构的设计和实现中。
请简述 linux 中的文件系统层次结构
请简述 linux 中的文件系统层次结构
Linux系统的文件系统层次结构是非常复杂的,通常可以分成如下几个层次:
1. 根目录:根目录是 Linux 文件系统中最顶层的根目录,它可以看做是 Linux 系统整个文件系统的根,其它所有的子目录都在它之下,一般用 '/' 表示。
2. 二级目录:包括 '/etc'、'/usr'、'/bin'、'/sbin'、'/lib' 等,这些目录又可以看做是四级目录的父目录,下面可以放置用户自定义的文件和文件夹。
3. 四级目录:这里可以放置用户自定义的应用程序,一般都是以某个子目录名开头,比如 '/usr/local','/usr/bin'、'/usr/sbin'等。
4. 程序文件:可以放置各种程序文件,包括可执行文件、库文件、配置文件等。
5. 日志文件:记录系统的运行日志,以及用户行为日志,用于排查故障。
6. 数据文件:用户可以将各种数据文件存放在用户指定的目录下。
总之,Linux系统的文件系统层次结构比较复杂,它们可以根据用户需求和功能进行多层次的划分,以满足用户的不同需求。
Linux操作系统实验总结分析报告
Linux操作系统实验总结分析报告从系统的⾓度分析影响程序执⾏性能的因素1.Linux系统概念模型从全局的⾓度来看,Linux系统分为内核空间和⽤户空间,但毫⽆疑问内核空间是Linux系统的核⼼,因为内核负责管理整个系统的进程、内存、设备驱动程序、⽂件,决定着系统的性能和稳定性。
于是从这个⾓度我构建的Linux系统的概念模型如下图所⽰:此模型将Linux系统主要划分为四个模块:内存管理、进程管理、设备驱动程序、⽂件系统。
这四个部分也是⼀个操作系统最基本也是最重要的功能。
2.概念模型解析2.1 内存管理Linux系统采⽤虚拟内存管理技术,使得每个进程都有各⾃互不⼲涉的进程地址空间。
该空间是块⼤⼩为4G的线性虚拟空间,⽤户所看到和接触到的都是该虚拟地址,⽆法看到实际的物理内存地址。
利⽤这种虚拟地址不但能起到保护操作系统的效果(⽤户不能直接访问物理内存),⽽且更重要的是,⽤户程序可使⽤⽐实际物理内存更⼤的地址空间。
内存管理主要有分为如下⼏个功能:地址映射、虚拟地址管理、物理内存管理、内核空间管理、页⾯换⼊换出策略和⽤户空间内存管理,这些模块的架构图如下所⽰:2.2 进程管理进程管理是Linux系统⾮常重要的⼀部分,进程管理虽然不像内存管理、⽂件系统等模块那样复杂,但是它与其他⼏个模块的联系是⾮常紧密的。
进程管理主要包括进程的创建、切换、撤销和进程调度。
2.2.1 进程的创建、切换、撤销进程的创建:在Linux编程中,⼀般采⽤fork()函数来创建新的进程,当然,那是在⽤户空间的函数,它会调⽤内核中的clone()系统调⽤,由clone()函数继续调⽤do_fork()完成进程的创建。
整个进程创建过程可能涉及到如下函数:fork()/vfork()/_clone----------->clone()--------->do_fork()---------->copy_process()进程的切换:进程切换⼜称为任务切换、上下⽂切换。
linux分层设计体系结构
linux分层设计体系结构Linux是一种开源的操作系统,其设计采用了分层的体系结构。
这种设计使得Linux具有高度的灵活性和可扩展性,同时也方便了系统的维护和管理。
本文将详细介绍Linux的分层设计体系结构。
在Linux的分层设计中,最底层是硬件层。
硬件层包括计算机的各种硬件设备,如处理器、内存、硬盘、网络接口等。
Linux通过设备驱动程序来管理和控制这些硬件设备,使其能够与操作系统进行交互。
在硬件层之上是内核层。
内核是操作系统的核心,负责管理系统的资源和提供各种系统服务。
Linux的内核是一个单独的模块,可以独立于其他软件进行开发和维护。
内核提供了各种系统调用接口,以及对进程、文件系统、网络和设备的管理和控制功能。
在内核层之上是库层。
库是一组共享的代码和函数,可以为应用程序提供常用的功能和服务。
Linux提供了许多不同的库,如C库、数学库、网络库等。
这些库可以被开发人员用来开发应用程序,提高开发效率和代码复用性。
在库层之上是应用层。
应用层包括各种应用程序和工具,如文本编辑器、图形界面、网络浏览器等。
这些应用程序可以通过系统调用接口与内核进行交互,并利用库提供的功能来实现各种任务和操作。
除了以上四个层次外,Linux还有其他一些重要的组件和模块。
例如,系统初始化和启动过程中,会加载引导程序和初始化程序;文件系统是用来组织和管理文件和目录的;网络协议栈是用来实现网络通信的;系统服务是用来提供各种系统功能和服务的。
这些组件和模块与其他层次之间相互关联,共同构成了Linux的完整体系结构。
Linux的分层设计体系结构具有许多优点。
首先,分层设计使得系统的各个组件和模块之间相互独立,可以分别进行开发、测试和维护,提高了开发和维护效率。
其次,分层设计使得系统的各个层次之间的接口清晰明确,方便了系统的扩展和升级。
此外,分层设计还提高了系统的稳定性和可靠性,一旦某个层次出现问题,不会对其他层次造成影响。
Linux的分层设计体系结构是一种高效、灵活和可扩展的设计方式。
操作系统第二次实验报告——Linux创建进程及可执行文件结构分析
操作系统第⼆次实验报告——Linux创建进程及可执⾏⽂件结构分析0 个⼈信息张樱姿201821121038计算18121 实验⽬的熟练Linux创建进程fork操作。
2 实验内容在服务器上⽤VIM编写⼀个程序:⼀个进程创建两个⼦进程。
查看进程树查看进程相关信息3 实验报告 3.1编写程序创建两个⼦进程1 #include<sys/types.h>2 #include<stdio.h>3 #include<unistd.h>45int main(){6 pid_t cpid1 = fork(); //创建⼦进程178if(cpid1<0){9 printf("fork cd1 failed\n");10 }11else if(cpid1==0){12 printf("Child1:pid: %d, ppid: %d\n",getpid(),getppid());13 }14else{15 pid_t cpid2 = fork(); //创建⼦进程216if(cpid2<0){17 printf("fork cd2 failed\n");18 }19else if(cpid2==0){20 printf("Child2:pid: %d, ppid: %d\n",getpid(),getppid());21 }22else{23 printf("Parent: pid :%d\n",getpid());24 }25 }26 }编译运⾏后的结果:3.2打印进程树 添加sleep函数以挂起进程,⽅便打印进程树:1 #include<sys/types.h>2 #include<stdio.h>3 #include<unistd.h>45int main(){6 pid_t cpid1 = fork();78if(cpid1<0){9 printf("fork cd1 failed\n");10 }11else if(cpid1==0){12 printf("Child1:pid: %d, ppid: %d\n",getpid(),getppid());13 sleep(30); //挂起30秒14 }15else{16 pid_t cpid2 = fork();17if(cpid2<0){18 printf("fork cd2 failed\n");19 }20else if(cpid2==0){21 printf("Child2:pid: %d, ppid: %d\n",getpid(),getppid());22 sleep(30); //挂起30秒23 }24else{25 printf("Parent: pid :%d\n",getpid());26 sleep(60); //挂起60秒27 }28 }29 }pstree -p pid #打印进程树 3.3 解读进程相关信息 3.3.1 解释执⾏ps -ef后返回结果中每个字段的含义 ps -ef输出格式 :UID PID PPID C STIME TTY TIME CMDUID: User ID,⽤户ID。
linu中简述文件、文件夹、文件系统三者的关系
linu中简述文件、文件夹、文件系统三者的关系文件、文件夹、文件系统是计算机存储和管理数据的重要组成部分,它们之间密切关联并相互依存。
首先,我们来了解一下文件和文件夹的概念。
文件是计算机中存储数据的基本单元,可以是文本文件、图像文件、音频文件、视频文件等。
每个文件都有一个唯一的文件名和文件扩展名来标识,文件名用于区分不同文件,而文件扩展名则用于指示文件的类型。
文件夹(也被称为目录)是用于组织和存储文件的容器,可以将文件分门别类地归纳到不同的文件夹中。
文件夹可以嵌套,形成层次结构。
文件夹可以包含文件和其他文件夹,通过这种层次结构可以方便地管理和查找文件。
文件系统是计算机操作系统用于管理文件和文件夹的一种机制。
文件系统提供了一组规则和结构,用于组织和存储文件和文件夹,并提供了一组操作方法来管理这些文件和文件夹。
计算机系统中常见的文件系统有FAT、NTFS、EXT系列等。
接下来,我们来探讨一下文件、文件夹和文件系统之间的关系。
首先,文件和文件夹是文件系统中的基本组成单位。
文件系统通过文件和文件夹的组织和管理,实现了对存储在计算机中的数据的存取和整理。
在一个文件系统中,文件和文件夹都有一个唯一的路径来标识。
路径是由文件夹的层次结构和文件(或文件夹)的名称组成的。
通过路径,我们可以准确定位和访问文件和文件夹。
例如,路径“C:\Users\John\Documents\file.txt”表示在C盘的Users文件夹下的John文件夹下的Documents文件夹下的file.txt文件。
文件和文件夹之间通过父子关系来建立联系。
每个文件夹都可以包含多个文件或其他文件夹,形成一个层次结构。
父文件夹与其子文件夹或文件之间形成了一种组织关系。
例如,根文件夹是文件系统中的最顶层文件夹,它可以包含其他文件夹和文件,而这些文件夹和文件又可以进一步包含其他文件夹和文件,形成了一个树状的层次结构。
文件系统还提供了一组操作方法,用于管理文件和文件夹。
Linux文件系统之文件的读写
Linux文件系统之文件的读写展开全文------------------------------------------本文系本站原创,欢迎转载!转载请注明出处:/------------------------------------------一:前言文件的读写是文件系统中最核心也是最复杂的一部份,它牵涉到了很多的概念.之前分析文件系统其它操作的时候,遇到与文件系统相关的读写部份都忽略过去了.在这一节里,来讨论一下文件的读写是怎样实现的.二:I/O请求的概述如之前所提到的,为了提高文件的操作效率,文件系统中的内容都是缓存在内存里的.每当发起一个Rear/Write请求的时候,都会到页面高速缓存中寻找具体的页面.如果页面不存在,则在页面高速缓存中建立相关页面的缓存.如果当前的页面不是最新的.那就必须要到具体的文件系统中读取数据了.一般来说,内核提供了这样的界面:它产生一个I/O请求.这个界面为上层隐藏了下层的不同实现.在这个界面中,将产生的I/O 请求提交给I/O调度.再与I/O调度调用具体的块设备驱动程序.整个过程如下图所示:上图中的Generic Block Layer就是上面描述中所说的I/O的界面.接下来我们以上图从下到上的层次进行讨论.三:块设备驱动块设备与字符设备的区别在于:块设备可以随机的访问,例如磁盘.正是因为它可以随机访问,内核才需要一个高效的手段去管理每一个块设备.例如对磁盘的操作,每次移动磁针都需要花不少的时候,所以尽量让其处理完相同磁道内的请求再将磁针移动到另外的磁道.而对于字符设备来说,不存在这样的顾虑,只需按顺序从里面读/写就可以了.先来看一下块设备驱动所涉及到的数据结构.3.1: block_device结构:struct block_device {//主次驱备号dev_t bd_dev; /* not a kdev_t - it's a search key */ //指向bdev文件系统中块设备对应的文件索引号struct inode * bd_inode; /* will die *///计数器,统计块驱备被打开了多少次int bd_openers;// 块设备打开和关闭的信号量struct semaphore bd_sem; /* open/close mutex *///禁止在块设备上建行新安装的信号量struct semaphore bd_mount_sem; /* mount mutex *///已打开的块设备文件inode链表struct list_head bd_inodes;//块设备描述符的当前拥有者void * bd_holder;//统计字段,统计对bd_holder进行更改的次数int bd_holders;//如果当前块设备是一个分区,此成员指向它所属的磁盘的设备//否则指向该描述符的本身struct block_device * bd_contains;//块大小unsigned bd_block_size;//指向分区描述符的指针struct hd_struct * bd_part;/* number of times partitions within this device have been opened. *///统计字段,统计块设备分区被打开的次数unsigned bd_part_count;//读取块设备分区表时设置的标志int bd_invalidated;//指向块设备所属磁盘的gendiskstruct gendisk * bd_disk;//指向块设备描述符链表的指针struct list_head bd_list;//指向块设备的专门描述符backing_dev_infostruct backing_dev_info *bd_inode_backing_dev_info;/** Private data. You must have bd_claim'ed the block_device* to use this. NOTE: bd_claim allows an owner to claim* the same device multiple times, the owner must take special* care to not mess up bd_private for that case.*///块设备的私有区unsigned long bd_private;}通常,对于块设备来说还涉及到一个分区问题.分区在内核中是用hd_struct来表示的.3.2: hd_struct结构:struct hd_struct {//磁盘分区的起始扇区sector_t start_sect;//分区的长度,即扇区的数目sector_t nr_sects;//内嵌的kobjectstruct kobject kobj;//分区的读操作次数,读取扇区数,写操作次数,写扇区数unsigned reads, read_sectors, writes, write_sectors;//policy:如果分区是只读的,置为1.否则为0//partno:磁盘中分区的相对索引int policy, partno;}每个具体的块设备都会都应一个磁盘,在内核中磁盘用gendisk表示.3.3: gendisk结构:struct gendisk {//磁盘的主驱备号int major; /* major number of driver *///与磁盘关联的第一个设备号int first_minor;//与磁盘关联的设备号范围int minors; /* maximum number of minors, =1 for* disks that can't be partitioned. *///磁盘的名字char disk_name[32]; /* name of major driver *///磁盘的分区描述符数组struct hd_struct **part; /* [indexed by minor] *///块设备的操作指针struct block_device_operations *fops;//指向磁盘请求队列指针struct request_queue *queue;//块设备的私有区void *private_data;//磁盘内存区大小(扇区数目)sector_t capacity;//描述磁盘类型的标志int flags;//devfs 文件系统中的名字char devfs_name[64]; /* devfs crap *///不再使用int number; /* more of the same *///指向磁盘中硬件设备的device指针struct device *driverfs_dev;//内嵌kobject指针struct kobject kobj;//记录磁盘中断定时器struct timer_rand_state *random;//如果只读,此值为1.否则为0int policy;//写入磁盘的扇区数计数器atomic_t sync_io; /* RAID *///统计磁盘队列使用情况的时间戳unsigned long stamp, stamp_idle;//正在进行的I/O操作数int in_flight;//统计每个CPU使用磁盘的情况#ifdef CONFIG_SMPstruct disk_stats *dkstats;#elsestruct disk_stats dkstats;#endif}以上三个数据结构的关系,如下图所示:如上图所示:每个块设备分区的bd_contains会指它的总块设备节点,它的bd_part会指向它的分区表.bd_disk会指向它所属的磁盘.从上图中也可以看出:每个磁盘都会对应一个request_queue.对于上层的I/O请求就是通过它来完成的了.它的结构如下:3.4:request_queue结构:struct request_queue{/** Together with queue_head for cacheline sharing*///待处理请求的链表struct list_head queue_head;//指向队列中首先可能合并的请求描述符struct request *last_merge;//指向I/O调度算法指针elevator_t elevator;/** the queue request freelist, one for reads and one for writes *///为分配请请求描述符所使用的数据结构struct request_list rq;//驱动程序策略例程入口点的方法request_fn_proc *request_fn;//检查是否可能将bio合并到请求队列的最后一个请求的方法merge_request_fn *back_merge_fn;//检查是否可能将bio合并到请求队列的第一个请求中的方法merge_request_fn *front_merge_fn;//试图合并两个相邻请求的方法merge_requests_fn *merge_requests_fn;//将一个新请求插入请求队列时所调用的方法make_request_fn *make_request_fn;//该方法反这个处理请求的命令发送给硬件设备prep_rq_fn *prep_rq_fn;//去掉块设备方法unplug_fn *unplug_fn;//当增加一个新段时,该方法驼回可插入到某个已存在的bio 结构中的字节数merge_bvec_fn *merge_bvec_fn;//将某个请求加入到请求队列时,会调用此方法activity_fn *activity_fn;//刷新请求队列时所调用的方法issue_flush_fn *issue_flush_fn;/** Auto-unplugging state*///插入设备时所用到的定时器struct timer_list unplug_timer;//如果请求队列中待处理请求数大于该值,将立即去掉请求设备int unplug_thresh; /* After this many requests *///去掉设备之间的延迟unsigned long unplug_delay; /* After this many jiffies */ //去掉设备时使用的操作队列struct work_struct unplug_work;//struct backing_dev_info backing_dev_info;/** The queue owner gets to use this for whatever they like.* ll_rw_blk doesn't touch it.*///指向块设备驱动程序中的私有数据void *queuedata;//activity_fn()所用的参数void *activity_data;/** queue needs bounce pages for pages above this limit *///如果页框号大于该值,将使用回弹缓存冲unsigned long bounce_pfn;//回弹缓存区页面的分配标志int bounce_gfp;/** various queue flags, see QUEUE_* below*///描述请求队列的标志unsigned long queue_flags;/** protects queue structures from reentrancy*///指向请求队列锁的指针spinlock_t *queue_lock;/** queue kobject*///内嵌的kobjectstruct kobject kobj;/** queue settings*///请求队列中允许的最大请求数unsigned long nr_requests; /* Max # of requests */ //如果待请求的数目超过了该值,则认为该队列是拥挤的unsigned int nr_congestion_on;//如果待请求数目在这个阀值下,则认为该队列是不拥挤的unsigned int nr_congestion_off;//单个请求所能处理的最大扇区(可调的)unsigned short max_sectors;//单个请求所能处理的最大扇区(硬约束)unsigned short max_hw_sectors;//单个请求所能处理的最大物理段数unsigned short max_phys_segments;//单个请求所能处理的最大物理段数(DMA的约束) unsigned short max_hw_segments;//扇区中以字节为单位的大小unsigned short hardsect_size;//物理段的最大长度(以字节为单位)unsigned int max_segment_size;//段合并的内存边界屏弊字unsigned long seg_boundary_mask;//DMA缓冲区的起始地址和长度的对齐unsigned int dma_alignment;//空闲/忙标记的位图.用于带标记的请求struct blk_queue_tag *queue_tags;//请求队列的引用计数atomic_t refcnt;//请求队列中待处理的请求数unsigned int in_flight;/** sg stuff*///用户定义的命令超时unsigned int sg_timeout;//Not Useunsigned int sg_reserved_size;}request_queue表示的是一个请求队列,每一个请求都是用request来表示的.3.5: request结构:struct request {//用来形成链表struct list_head queuelist; /* looking for ->queue? you must _not_* access it directly, use* blkdev_dequeue_request! *///请求描述符的标志unsigned long flags; /* see REQ_ bits below *//* Maintain bio traversal state for part by part I/O submission.* hard_* are block layer internals, no driver should touch them!*///要传送的下一个扇区sector_t sector; /* next sector to submit *///要传送的扇区数目unsigned long nr_sectors; /* no. of sectors left to submit *//* no. of sectors left to submit in the current segment *///当前bio段传送扇区的数目unsigned int current_nr_sectors;//要传送的下一个扇区号sector_t hard_sector; /* next sector to complete *///整个过程中要传送的扇区号unsigned long hard_nr_sectors; /* no. of sectors left to complete *//* no. of sectors left to complete in the current segment */ //当前bio段要传送的扇区数目unsigned int hard_cur_sectors;/* no. of segments left to submit in the current bio *///unsigned short nr_cbio_segments;/* no. of sectors left to submit in the current bio */unsigned long nr_cbio_sectors;struct bio *cbio; /* next bio to submit *///请求中第一个没有完成的biostruct bio *bio; /* next unfinished bio to complete *///最后的biostruct bio *biotail;//指向I/O调度的私有区void *elevator_private;//请求的状态int rq_status; /* should split this into a few status bits */ //请求所引用的磁盘描述符struct gendisk *rq_disk;//统计传送失败的计数int errors;//请求开始的时间unsigned long start_time;/* Number of scatter-gather DMA addr+len pairs after* physical address coalescing is performed.*///请求的物理段数unsigned short nr_phys_segments;/* Number of scatter-gather addr+len pairs after* physical and DMA remapping hardware coalescing is performed.* This is the number of scatter-gather entries the driver* will actually have to deal with after DMA mapping is done.*///请求的硬段数unsigned short nr_hw_segments;//与请求相关的标识int tag;//数据传送的缓冲区,如果是高端内存,此成员值为NULLchar *buffer;//请求的引用计数int ref_count;//指向包含请求的请求队列描述符request_queue_t *q;struct request_list *rl;//指向数据传送终止的completionstruct completion *waiting;//对设备发达“特殊请求所用到的指针”void *special;/** when request is used as a packet command carrier*///cmd中的数据长度unsigned int cmd_len;//请求类型unsigned char cmd[BLK_MAX_CDB];//data中的数据长度unsigned int data_len;//为了跟踪所传输的数据而使用的指针void *data;//sense字段的数据长度unsigned int sense_len;//指向输出sense缓存区void *sense;//请求超时unsigned int timeout;/** For Power Management requests*///指向电源管理命令所用的结构struct request_pm_state *pm;}请求队列描述符与请求描述符都很复杂,为了简化驱动的设计,内核提供了一个API,供块设备驱动程序来初始化一个请求队列.这就是blk_init_queue().它的代码如下://rfn:驱动程序自动提供的操作I/O的函数.对应请求队列的request_fn//lock:驱动程序提供给请求队列的自旋锁request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock){request_queue_t *q;static int printed;//申请请求队列描述符q = blk_alloc_queue(GFP_KERNEL);if (!q)return NULL;//初始化q->request_listif (blk_init_free_list(q))goto out_init;if (!printed) {printed = 1;printk("Using %s io scheduler\n", chosen_elevator->elevator_name);}//初始化请求队列描述符中的各项操作函数q->request_fn = rfn;q->back_merge_fn = ll_back_merge_fn;q->front_merge_fn = ll_front_merge_fn;q->merge_requests_fn = ll_merge_requests_fn;q->prep_rq_fn = NULL;q->unplug_fn = generic_unplug_device;q->queue_flags = (1 << QUEUE_FLAG_CLUSTER);q->queue_lock = lock;blk_queue_segment_boundary(q, 0xffffffff);//设置q->make_request_fn函数,初始化等待队对列的定时器和等待队列blk_queue_make_request(q, __make_request);//设置max_segment_size,max_hw_segments,max_phys_segments blk_queue_max_segment_size(q, MAX_SEGMENT_SIZE);blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);/** all done*///设置等待队列的I/O调度程序if (!elevator_init(q, chosen_elevator))return q;//失败的处理blk_cleanup_queue(q);out_init:kmem_cache_free(requestq_cachep, q);return NULL;}这个函数中初始化了很多操作指针,这个函数在所有块设备中都是一样的,这样就为通用块设备层提供了一个统一的接口.对于块设备驱动的接口就是我们在blk_init_queue中设置的策略例程了.留意一下关于请求队列的各操作的设置,这在后续的分析中会用到.另外,在请求结构中涉及到了bio结构.bio表示一个段.目前内核中关于I/O的所有操作都是由它来表示的.它的结构如下所示:struct bio {//段的起始扇区sector_t bi_sector;//下一个biostruct bio *bi_next; /* request queue link *///段所在的块设备struct block_device *bi_bdev;//bio的标志unsigned long bi_flags; /* status, command, etc *///Read/Writeunsigned long bi_rw; /* bottom bits READ/WRITE,* top bits priority*///bio_vec的项数unsigned short bi_vcnt; /* how many bio_vec's *///当前正在操作的bio_vecunsigned short bi_idx; /* current index into bvl_vec *//* Number of segments in this BIO after* physical address coalescing is performed.*///结合后的片段数目unsigned short bi_phys_segments;/* Number of segments after physical and DMA remapping * hardware coalescing is performed.*///重映射后的片段数目unsigned short bi_hw_segments;//I/O计数unsigned int bi_size; /* residual I/O count *//** To keep track of the max hw size, we account for the* sizes of the first and last virtually mergeable segments* in this bio*///第一个可以合并的段大小unsigned int bi_hw_front_size;//最后一个可以合并的段大小unsigned int bi_hw_back_size;//最大的bio_vec项数unsigned int bi_max_vecs; /* max bvl_vecs we can hold *///bi_io_vec数组struct bio_vec *bi_io_vec; /* the actual vec list *///I/O完成的方法bio_end_io_t *bi_end_io;//使用计数atomic_t bi_cnt; /* pin count *///拥有者的私有区void *bi_private;//销毁此bio的方法bio_destructor_t *bi_destructor; /* destructor */}bio_vec的结构如下:struct bio_vec {//bi_vec所表示的页面struct page *bv_page;//数据区的长度unsigned int bv_len;//在页面中的偏移量unsigned int bv_offset;}关于bio与bio_vec的关系,用下图表示:现在,我们来思考一个问题:当一个I/O请求提交给请求队列后,它是怎么去调用块设备驱动的策略例程去完成这次I/O的呢?还有,当一个I/O请求被提交给请求队列时,会不会立即调用驱动中的策略例程去完成这次I/O呢?实际上,为了提高效率,所有的I/O都会在一个特定的延时之后才会调用策略例程去完成本次I/O.我们来看一个反面的例子,假设I/O在被提交后马上得到执行.例如.磁盘有磁针在磁盘12.现在有一个磁道1的请求.就会将磁针移动到磁道1.操作完后,又有一个请求过来了,它要操作磁道11.然后又会将磁针移到磁道11.操作完后,又有一个请求过来,要求操作磁道4.此时会将磁针移到磁道4.这个例子中,磁针移动的位置是:12->1->11->4.实际上,磁针的定位是一个很耗时的操作.这样下去,毫无疑问会影响整个系统的效率.我们可以在整个延时内,将所有I/O操作按顺序排列在一起,然后再调用策略例程.于是上例的磁针移动就会变成12->11->4->1.此时磁针只会往一个方向移动.至于怎么样排列请求和选取哪一个请求进行操作,这就是I/O调度的任务了.这部份我们在通用块层再进行分析.内核中有两个操作会完成上面的延时过程.即:激活块设备驱动程序和撤消块设备驱动程序.3.6:块设备驱动程序的激活和撤消激活块设备驱动程序和撤消块设备驱动程序在内核中对应的接口为blk_plug_device()和blk_remove_plug().分别看下它们的操作:void blk_plug_device(request_queue_t *q){WARN_ON(!irqs_disabled());/** don't plug a stopped queue, it must be paired with blk_start_queue()* which will restart the queueing*///如果设置了QUEUE_FLAG_STOPPED.直接退出if (test_bit(QUEUE_FLAG_STOPPED, &q->queue_flags))return;//为请求队列设置QUEUE_FLAG_PLUGGED.if (!test_and_set_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags))//如果之前请求队列的状态不为QUEUE_FLAG_PLUGGED,则设置定时器超时时间mod_timer(&q->unplug_timer, jiffies + q->unplug_delay);}int blk_remove_plug(request_queue_t *q){WARN_ON(!irqs_disabled());//将队列QUEUE_FLAG_PLUGGED状态清除if (!test_and_clear_bit(QUEUE_FLAG_PLUGGED,&q->queue_flags))//如果请求队列之前不为QUEUE_FLAG_PLUGGED标志,直接返回return 0;//如果之前是QUEUE_FLAG_PLUGGED标志,则将定时器删除del_timer(&q->unplug_timer);return 1;}如果请求队列状态为QUEUE_FLAG_PLUGGED,且定时器超时,会有什么样的操作呢?回忆在请求队列初始化函数中,blk_init_queue()会调用blk_queue_make_request().它的代码如下:void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn){…………q->unplug_delay = (3 * HZ) / 1000; /* 3 milliseconds */if (q->unplug_delay == 0)q->unplug_delay = 1;INIT_WORK(&q->unplug_work, blk_unplug_work, q);q->unplug_timer.function = blk_unplug_timeout;q->unplug_timer.data = (unsigned long)q;…………}上面设置了定时器的时间间隔为(3*HZ)/1000.定时器超时的处理函数为blk_unplug_timeout().参数为请求队列本身.blk_unplug_timeout()的代码如下:static void blk_unplug_timeout(unsigned long data){request_queue_t *q = (request_queue_t *)data;kblockd_schedule_work(&q->unplug_work);}从上面的代码看出,定时器超时之后,会唤醒q->unplug_work这个工作对列.在blk_queue_make_request()中,对这个工作队列的初始化为: INIT_WORK(&q->unplug_work, blk_unplug_work, q)即工作队列对应的函数为blk_unplug_work().对应的参数为请求队列本身.代码如下:static void blk_unplug_work(void *data){request_queue_t *q = data;q->unplug_fn(q);}到此,就会调用请求队列的unplug_fn()操作.在blk_init_queue()对这个成员的赋值如下所示:q->unplug_fn = generic_unplug_device;generic_unplug_device()对应的代码如下:void __generic_unplug_device(request_queue_t *q){//如果请求队列是QUEUE_FLAG_STOPPED 状态,返回if (test_bit(QUEUE_FLAG_STOPPED, &q->queue_flags))return;//如果请求队列的状态是QUEUE_FLAG_PLUGGED.就会返回1if (!blk_remove_plug(q))return;/** was plugged, fire request_fn if queue has stuff to do*///如果请求对列中的请求,则调用请求队列的reauest_fn函数.也就是驱动程序的//策略例程if (elv_next_request(q))q->request_fn(q);}blk_remove_plug()在上面已经分析过了.这里不再赘述.归根到底,最后的I/O完成操作都会调用块设备驱动的策略例程来完成.四:I/O调度层I/O调度对应的结构如下所示:struct elevator_s{//当要插入一个bio时会调用elevator_merge_fn *elevator_merge_fn;elevator_merged_fn *elevator_merged_fn;elevator_merge_req_fn *elevator_merge_req_fn;//取得下一个请求elevator_next_req_fn *elevator_next_req_fn;//往请求队列中增加请求elevator_add_req_fn *elevator_add_req_fn;elevator_remove_req_fn *elevator_remove_req_fn;elevator_requeue_req_fn *elevator_requeue_req_fn;elevator_queue_empty_fn *elevator_queue_empty_fn;elevator_completed_req_fn *elevator_completed_req_fn;elevator_request_list_fn *elevator_former_req_fn;elevator_request_list_fn *elevator_latter_req_fn;elevator_set_req_fn *elevator_set_req_fn;elevator_put_req_fn *elevator_put_req_fn;elevator_may_queue_fn *elevator_may_queue_fn;//初始化与退出操作elevator_init_fn *elevator_init_fn;elevator_exit_fn *elevator_exit_fn;void *elevator_data;struct kobject kobj;struct kobj_type *elevator_ktype;//调度算法的名字const char *elevator_name;}我们以最简单的NOOP算法为例进行分析.NOOP算法只是做简单的请求合并的操作.的定义如下:elevator_t elevator_noop = {.elevator_merge_fn = elevator_noop_merge,.elevator_merge_req_fn = elevator_noop_merge_requests, .elevator_next_req_fn = elevator_noop_next_request,.elevator_add_req_fn = elevator_noop_add_request,.elevator_name = "noop",}挨个分析里面的各项操作:elevator_noop_merge():在请求队列中寻找能否有可以合并的请求.代码如下:int elevator_noop_merge(request_queue_t *q, struct request **req,struct bio *bio){struct list_head *entry = &q->queue_head;struct request *__rq;int ret;//如果请求队列中有last_merge项.则判断last_merge项是否能够合并//在NOOP中一般都不会设置last_mergeif ((ret = elv_try_last_merge(q, bio))) {*req = q->last_merge;return ret;}//遍历请求队列中的请求while ((entry = entry->prev) != &q->queue_head) {__rq = list_entry_rq(entry);if (__rq->flags & (REQ_SOFTBARRIER | REQ_HARDBARRIER)) break;else if (__rq->flags & REQ_STARTED)break;//如果不是一个fs类型的请求?if (!blk_fs_request(__rq))continue;//判断能否与这个请求合并if ((ret = elv_try_merge(__rq, bio))) {*req = __rq;q->last_merge = __rq;return ret;}}return ELEVATOR_NO_MERGE;}Elv_try_merge()用来判断能否与请求合并,它的代码如下:inline int elv_try_merge(struct request *__rq, struct bio *bio) {int ret = ELEVATOR_NO_MERGE;/** we can merge and sequence is ok, check if it's possible *///判断rq与bio是否为同类型的请求if (elv_rq_merge_ok(__rq, bio)) {//如果请求描述符中的起始扇区+ 扇区数= bio的起始扇区//则将bio加到_rq的后面.//返回ELEVATOR_BACK_MERGEif (__rq->sector + __rq->nr_sectors == bio->bi_sector)ret = ELEVATOR_BACK_MERGE;//如果请求描述符中的起始扇区- 扇区数=bio的起始扇区//则将bio加到_rq的前面//返回ELEVATOR_FRONT_MERGEelse if (__rq->sector - bio_sectors(bio) == bio->bi_sector) ret = ELEVATOR_FRONT_MERGE;//如果不可以合并,返回ELEVATOR_NO_MERGE (值为0)return ret;}elv_rq_merge_ok()代码如下:inline int elv_rq_merge_ok(struct request *rq, struct bio *bio) {//判断rq是否可用if (!rq_mergeable(rq))return 0;/** different data direction or already started, don't merge*///操作是否相同if (bio_data_dir(bio) != rq_data_dir(rq))return 0;/** same device and no special stuff set, merge is ok*///要操作的对象是否一样if (rq->rq_disk == bio->bi_bdev->bd_disk &&!rq->waiting && !rq->special)return 1;return 0;}注意:如果检查成功返回1.失败返回0.elevator_noop_merge_requests():将next 从请求队列中取出.代码如下:void elevator_noop_merge_requests(request_queue_t *q, struct request *req,struct request *next){list_del_init(&next->queuelist);}从上面的代码中看到,NOOP算法从请求队列中取出请求,只需要取链表结点即可.不需要进行额外的操作.elevator_noop_next_request():取得下一个请求.代码如下:struct request *elevator_noop_next_request(request_queue_t *q){if (!list_empty(&q->queue_head))return list_entry_rq(q->queue_head.next);return NULL;}很简单,取链表的下一个结点.elevator_noop_add_request():往请求队列中插入一个请求.代码如下:void elevator_noop_add_request(request_queue_t *q, struct request *rq,int where){//默认是将rq插和到循环链表末尾struct list_head *insert = q->queue_head.prev;//如果要插到请求队列的前面if (where == ELEVATOR_INSERT_FRONT)insert = &q->queue_head;//不管是什么样的操作,都将新的请求插入到请求队列的末尾list_add_tail(&rq->queuelist, &q->queue_head);/** new merges must not precede this barrier*/if (rq->flags & REQ_HARDBARRIER)q->last_merge = NULL;else if (!q->last_merge)q->last_merge = rq;}五:通用块层的处理通用块层的入口点为generic_make_request().它的代码如下:void generic_make_request(struct bio *bio){request_queue_t *q;sector_t maxsector;//nr_sectors:要操作的扇区数int ret, nr_sectors = bio_sectors(bio);//可能会引起睡眠might_sleep();/* Test device or partition size, when known. *///最大扇区数目maxsector = bio->bi_bdev->bd_inode->i_size >> 9;if (maxsector) {//bio操作的起始扇区sector_t sector = bio->bi_sector;//如果最大扇区数<要操作的扇区数or 最大扇区数与起始扇区的差值小于要操作的扇区数//非法的情况if (maxsector < nr_sectors ||maxsector - nr_sectors < sector) {char b[BDEVNAME_SIZE];/* This may well happen - the kernel calls* bread() without checking the size of the* device, e.g., when mounting a device. */printk(KERN_INFO"attempt to access beyond end of device\n");printk(KERN_INFO "%s: rw=%ld, want=%Lu, limit=%Lu\n", bdevname(bio->bi_bdev, b),bio->bi_rw,(unsigned long long) sector + nr_sectors,(long long) maxsector);set_bit(BIO_EOF, &bio->bi_flags);goto end_io;}}/** Resolve the mapping until finished. (drivers are* still free to implement/resolve their own stacking* by explicitly returning 0)** NOTE: we don't repeat the blk_size check for each new device.* Stacking drivers are expected to know what they are doing.*/do {char b[BDEVNAME_SIZE];//取得块设备的请求对列q = bdev_get_queue(bio->bi_bdev);if (!q) {//请求队列不存在printk(KERN_ERR"generic_make_request: Trying to access ""nonexistent block-device %s (%Lu)\n",bdevname(bio->bi_bdev, b),(long long) bio->bi_sector);end_io://最终会调用bio->bi_end_iobio_endio(bio, bio->bi_size, -EIO);break;}//非法的情况if (unlikely(bio_sectors(bio) > q->max_hw_sectors)) {printk("bio too big device %s (%u > %u)\n",bdevname(bio->bi_bdev, b),bio_sectors(bio),q->max_hw_sectors);goto end_io;}//如果请求队列为QUEUE_FLAG_DEAD//退出if (test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))goto end_io;/** If this device has partitions, remap block n* of partition p to block n+start(p) of the disk.*///如果当前块设备是一个分区,则转到分区所属的块设备blk_partition_remap(bio);//调用请求队列的make_request_fn()ret = q->make_request_fn(q, bio);} while (ret);}在blk_init_queue()中对请求队列的make_request_fn的设置如下所示:blk_init_queue()—> blk_queue_make_request(q, __make_request)void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn){…………q->make_request_fn = mfn;……}这里,等待队对的make_request_fn就被设置为了__make_request.这个函数的代码如下:static int __make_request(request_queue_t *q, struct bio *bio) {struct request *req, *freereq = NULL;int el_ret, rw, nr_sectors, cur_nr_sectors, barrier, err;sector_t sector;//bio的起始扇区sector = bio->bi_sector;//扇区数目nr_sectors = bio_sectors(bio);//当前bio中的bio_vec的扇区数目cur_nr_sectors = bio_cur_sectors(bio);//读/写rw = bio_data_dir(bio);/** low level driver can indicate that it wants pages above a* certain limit bounced to low memory (ie for highmem, or even* ISA dma in theory)*///建立一个弹性回环缓存blk_queue_bounce(q, &bio);spin_lock_prefetch(q->queue_lock);barrier = bio_barrier(bio);if (barrier && !(q->queue_flags & (1 <<QUEUE_FLAG_ORDERED))) {err = -EOPNOTSUPP;goto end_io;}again:spin_lock_irq(q->queue_lock);//请求队列是空的if (elv_queue_empty(q)) {//激活块设备驱动blk_plug_device(q);goto get_rq;}if (barrier)goto get_rq;//调用I/O调度的elevator_merge_fn方法,判断这个bio能否和其它请求合并//如果可以合并,req参数将返回与之合并的请求描述符el_ret = elv_merge(q, &req, bio);switch (el_ret) {//可以合并.且bio加到req的后面case ELEVATOR_BACK_MERGE:BUG_ON(!rq_mergeable(req));if (!q->back_merge_fn(q, req, bio))break;req->biotail->bi_next = bio;req->biotail = bio;req->nr_sectors = req->hard_nr_sectors += nr_sectors; drive_stat_acct(req, nr_sectors, 0);if (!attempt_back_merge(q, req))elv_merged_request(q, req);goto out;//可以合并.且bio加到req的前面case ELEVATOR_FRONT_MERGE:BUG_ON(!rq_mergeable(req));if (!q->front_merge_fn(q, req, bio))break;bio->bi_next = req->bio;req->cbio = req->bio = bio;req->nr_cbio_segments = bio_segments(bio);req->nr_cbio_sectors = bio_sectors(bio);/** may not be valid. if the low level driver said* it didn't need a bounce buffer then it better* not touch req->buffer either...*/req->buffer = bio_data(bio);req->current_nr_sectors = cur_nr_sectors;req->hard_cur_sectors = cur_nr_sectors;req->sector = req->hard_sector = sector;req->nr_sectors = req->hard_nr_sectors += nr_sectors; drive_stat_acct(req, nr_sectors, 0);if (!attempt_front_merge(q, req))elv_merged_request(q, req);goto out;/** elevator says don't/can't merge. get new request*///不可以合并.申请一个新的请求,将且加入请求队列case ELEVATOR_NO_MERGE:break;default:printk("elevator returned crap (%d)\n", el_ret);BUG();}/** Grab a free request from the freelist - if that is empty, check * if we are doing read ahead and abort instead of blocking for* a free slot.*/get_rq://freereq:是新分配的请求描述符if (freereq) {req = freereq;freereq = NULL;} else {//分配一个请求描述符spin_unlock_irq(q->queue_lock);if ((freereq = get_request(q, rw, GFP_ATOMIC)) == NULL) { /** READA bit set*///分配失败err = -EWOULDBLOCK;if (bio_rw_ahead(bio))goto end_io;freereq = get_request_wait(q, rw);}goto again;}req->flags |= REQ_CMD;/** inherit FAILFAST from bio (for read-ahead, and explicit FAILFAST)*/if (bio_rw_ahead(bio) || bio_failfast(bio))req->flags |= REQ_FAILFAST;/** REQ_BARRIER implies no merging, but lets make it explicit */if (barrier)req->flags |= (REQ_HARDBARRIER | REQ_NOMERGE);//初始化新分配的请求描述符req->errors = 0;req->hard_sector = req->sector = sector;req->hard_nr_sectors = req->nr_sectors = nr_sectors;req->current_nr_sectors = req->hard_cur_sectors = cur_nr_sectors;req->nr_phys_segments = bio_phys_segments(q, bio);req->nr_hw_segments = bio_hw_segments(q, bio);req->nr_cbio_segments = bio_segments(bio);req->nr_cbio_sectors = bio_sectors(bio);req->buffer = bio_data(bio); /* see ->buffer comment above */req->waiting = NULL;//将bio 关联到请求描述符req->cbio = req->bio = req->biotail = bio;req->rq_disk = bio->bi_bdev->bd_disk;req->start_time = jiffies;//请将求描述符添加到请求队列中add_request(q, req);out: (R)if (freereq)__blk_put_request(q, freereq);//如果定义了BIO_RW_SYNC.//将调用__generic_unplug_device将块设备驱动,它会直接调用驱动程序的策略例程if (bio_sync(bio))__generic_unplug_device(q);spin_unlock_irq(q->queue_lock);return 0;end_io:bio_endio(bio, nr_sectors << 9, err);return 0;}这个函数的逻辑比较简单,它判断bio能否与请求队列中存在的请求合并,如果可以合并,将其它合并到现有的请求.如果不能合并,则新建一个请求描述符,然后把它插入到请求队列中.上面的代码可以结合之前分析的NOOP算法进行理解.重点分析一下请求描述符的分配过程:分配一个请求描述符的过程如下所示:if ((freereq = get_request(q, rw, GFP_ATOMIC)) == NULL) { /** READA bit set*///分配失败err = -EWOULDBLOCK;if (bio_rw_ahead(bio))goto end_io;freereq = get_request_wait(q, rw);}在分析这段代码之前,先来讨论一下关于请求描述符的分配方式.记得我们在分析请求队列描述符的时候,request_queue中有一个成员:struct request_list rq;它的数据结构如下:struct request_list {//读/写请求描述符的分配计数int count[2];//分配缓存池mempool_t *rq_pool;//如果没有空闲内存时.读/写请求的等待队列wait_queue_head_t wait[2];。
Linux虚拟文件系统分析
21 0 0年 第 9期
钟柏松 等 :iu Ln x虚拟 文件 系统分析
g j i d; i d
ud t i
_
7 7
型, 模 型能够表 示所 有支 持 的文 件系 统。因此 , 该
VS F 使得用户可 以直接使用 Lnx的系统调用而无 i u 需考 虑具 体文 件系统 和 实际 物理 介质 。当然 , 要实 现 每个 具体 的文件 系统 , 须将其 物 理组 织结 构转 换 为 必
现该 行 为分 离 开来 。
收 稿 日期 :0 0 41 2 1- ・9 0
图 1 V S的分层结构 F
Ⅶ S的通 用 文 件 模 型
V S的主要 思想 在 于 引 入 了一 个 通 用 的 文件 模 F
作者简介 : I ( 9 7) 男 , t g 1 8 , 江西 九江 人 , 昌大学 信 息工 程学 院硕 士研 究生 , 究方 向 :  ̄ k 南 研 系统 结 构 , 工智 能 ; 人 张字 成 ( 9 2 ) 男 , 西宜春人 , 18 . , 江 硕士研究生 , 研究方 向: 系统结 构 , 工智 能 ; 明建 ( 9 0) 男 , 人 周 17 - , 江西 吉安人 , 副教授 , 研究方 向 :
( 昌 大 学信 .  ̄ 学 院 , 西 南 昌 3 03 ) 南 gz r _ 江 3 0 1
摘要 : 首先 简要地介 绍 Lnx系统 中虚拟 文件 系统( F ) iu V S 的分层抽 象 , 然后 详细分析 V S通 用模 型 中的四个对 象类型及 F
内核 中实现它们的数据结构 , 最后介绍它们之 间是如何联 系起 来的。通过 对其分析 , 读者加 深对 V S系统的理解 。 使 F 关键词 :iu ;虚拟文件 系统 ; 级块 ;索引节点 Ln x 超 中图分类号 :P 0 T31 文献标识码 : A d i 0 3 6 /. s.0627 .0 00 .2 o:1 .9 9 ji n 10 -4 5 2 1 .9 0 1 s
linux系统的内核子系统之间的关系
linux系统的内核子系统之间的关系Linux系统的内核子系统之间的关系Linux操作系统的内核是其最核心的组成部分,它负责管理和控制整个系统的运行。
内核由多个子系统组成,每个子系统负责不同的功能模块,它们之间相互配合,共同完成系统的各项任务。
本文将介绍几个常见的内核子系统及其之间的关系。
1. 文件系统子系统文件系统子系统负责管理文件和目录的存储和访问。
它提供了对文件系统的抽象,使用户和应用程序可以通过文件路径来访问文件和目录。
文件系统子系统由虚拟文件系统层、各种具体的文件系统类型和存储设备驱动程序组成。
虚拟文件系统层提供了一个统一的接口,使不同的文件系统可以以相同的方式进行访问。
具体的文件系统类型如ext4、NTFS等负责实现不同的文件系统格式,而存储设备驱动程序则负责控制硬盘、闪存等存储设备的读写。
2. 进程管理子系统进程管理子系统负责管理系统中的进程。
它负责创建、终止和调度进程,并提供进程间通信和同步的机制。
进程管理子系统包括进程调度器、进程控制块、进程间通信和同步机制等。
进程调度器决定了系统中运行哪些进程以及它们的优先级和时间片分配。
进程控制块保存了进程的状态信息,包括程序计数器、寄存器和运行时堆栈等。
进程间通信和同步机制如管道、信号量、消息队列等,使不同进程之间可以进行数据交换和协调工作。
3. 设备驱动子系统设备驱动子系统负责管理和控制硬件设备的访问。
它提供了对设备的抽象接口,使应用程序可以通过统一的方式访问不同类型的设备。
设备驱动子系统包括字符设备驱动和块设备驱动。
字符设备驱动用于管理字符设备,如串口、键盘等,它提供了以字节为单位的读写接口。
块设备驱动用于管理块设备,如硬盘、闪存等,它提供了以块为单位的读写接口。
设备驱动子系统还包括中断处理、DMA控制等功能,用于处理设备的中断请求和数据传输。
4. 网络子系统网络子系统负责管理和控制系统的网络功能。
它提供了网络协议栈、网络接口和网络设备驱动等功能。
树结构的应用案例分析
树结构的应用案例分析树结构是计算机科学中常用的一种数据结构,它模拟了现实世界中树的形状和特点,具有分支层次结构和层级关系。
在本文中,将分析树结构的应用案例,以展示其在不同领域中的重要性和实际应用。
一、文件系统树结构在文件系统中的应用广泛。
文件系统通常由多个目录和文件组成,这些目录和文件之间存在层次关系。
使用树结构可以清晰地表示目录和文件之间的层级关系,便于用户查找和管理文件。
例如,当我们在计算机上打开文件资源管理器时,可以看到一个树形结构的文件目录,通过展开和折叠不同的目录,可以方便地在文件系统中进行导航和操作。
二、组织机构架构树结构在组织机构的架构中也应用广泛。
一个组织通常由一系列部门和职位组成,这些部门和职位之间存在上下级关系。
使用树结构可以清晰地表示各个部门和职位之间的层次关系,便于管理者进行组织架构的设计和人员的分配。
例如,一家公司的组织架构图以树形结构展示,每个部门和职位都连接在一起,形成一个层级分明的组织架构。
三、目录索引树结构在目录索引中的应用也非常重要。
在许多应用程序中,为了提高数据检索的效率,常常使用树结构来构建索引。
例如,在一本字典中,每个单词可以看作是树的一个节点,通过字母的顺序排列构成层级结构。
读者可以根据字母的顺序快速定位到目标单词,提高查找的效率。
类似地,在数据库的索引中,也常常使用树结构(如B树)来加速数据的检索。
四、人物关系树结构也可以用来表示人物关系。
在文学作品或影视剧中,人物之间的关系常常错综复杂,使用树结构可以清晰地展示各个人物之间的血缘关系、师徒关系、情侣关系等。
例如,在《红楼梦》中,可以将贾母、贾政、贾宝玉等主要人物用树结构连接起来,展示他们之间的关系和角色的重要性。
五、软件工程中的依赖关系在软件工程中,树结构也有着重要的应用,特别是用于表示软件的依赖关系。
在一个复杂的软件系统中,各个模块之间存在着依赖关系,使用树结构可以清晰地表示模块之间的层次关系和依赖关系,便于开发人员进行模块设计和代码编写。
树的应用数据结构中的实际案例分析
树的应用数据结构中的实际案例分析树(Tree)是一种非常重要的数据结构,它在各个领域都有广泛的应用。
本文将以实际案例的方式,分析树结构在数据管理、网络通信和图形图像处理领域的应用,以展示树的实际应用价值。
一、数据管理中的树应用案例1. 文件系统中的目录结构在操作系统中,文件系统通常采用树的结构来组织文件和目录。
每个文件或目录都是树中的节点,而它们之间的层次关系就构成了一个树结构。
通过树的遍历算法,我们可以方便地进行文件的查找、增加、删除和修改等操作,提高了文件系统的管理效率。
2. 数据库中的索引数据库系统中,常常需要对数据进行快速检索。
为了提高检索效率,通常使用B树或B+树来构建索引。
这些树结构可以快速定位到存储数据的位置,大大加快了数据库的查询速度。
二、网络通信中的树应用案例1. 网络路由协议在网络通信中,路由器通过路由协议来决定数据包的传输路径。
常用的路由协议,如OSPF和BGP,都采用了基于树的算法。
通过构建树状的路由表,路由器可以根据目的IP地址快速确定数据包的下一跳路径,实现了高效的网络通信。
2. 网页链接结构互联网上的网页链接结构也可以看作一种树结构。
每个网页可以看作一个节点,而网页之间的超链接关系则构成了树状结构。
通过网页中的树遍历算法,搜索引擎可以快速索引和抓取网页内容,为用户提供准确的搜索结果。
三、图形图像处理中的树应用案例1. 游戏中的场景管理在游戏开发中,场景管理是一个重要的任务。
常常使用场景树来管理游戏中的各个场景。
每个场景都是树中的一个节点,而场景之间的层次关系和跳转关系则构成了一个树结构。
通过树的遍历和搜索等算法,游戏引擎可以方便地进行场景的切换和管理。
2. 图像分析中的分割与分类在图像处理领域,常常需要对图像进行分割和分类。
为了实现自动化分析,可以使用树结构来表示图像的区域关系。
通过树的遍历算法和图像特征提取,可以实现对图像的自动分割和分类,提高了图像处理的效率和准确性。
Linux实验总结分析报告
Linux实验总结分析报告⼀、Linux系统概念模型 以宏观⾓度看,Linux体系结构可以分为⽤户空间和内核空间。
⽤户空间包含了C库、⽤户的应⽤程序;内核空间包含系统调⽤、内核、与平台架构相关的代码。
⽤户空间和内核空间是程序执⾏的两种不同状态,可以通过系统调⽤和硬件中断来进⾏⽤户空间到内核空间的状态转移。
Linux内核结构包含进程管理、内存管理、虚拟⽂件系统、⽹络管理、设备驱动等。
1、进程管理(Process Management) 进程实际是某特定应⽤程序的⼀个运⾏实体,进程管理是Linux内核中最重要的⼦系统,主要提供对CPU的访问控制。
因为在计算机中,CPU资源是有限的,⽽众多的应⽤程序都要使⽤CPU资源,所以需要进程管理对CPU进⾏调度管理。
内核通过 SCI 提供了⼀个应⽤程序编程接⼝(API)来创建⼀个新进程(fork、exec或Portable Operating System Interface[POSIX]函数),停⽌进程(kill、exit),并在它们之间进⾏通信和同步(signal 或者POSIX机制)。
进程的创建:在Linux系统中,除了系统启动之后的第⼀个进程由系统来创建,其余的进程都必须由已存在的进程来创建,新创建的进程叫做⼦进程,⽽创建⼦进程的进程叫做⽗进程。
那个在系统启动及完成初始化之后,Linux⾃动创建的进程叫做根进程。
Linux提供了⼀个系统调⽤fork()使⼀个进程中分裂出⼦进程,如果fork()失败,返回-1,否则会出现⽗进程和⼦进程两个进程,⼦进程fork()返回0,⽗进程fork()返回⼦进程的ID;Linux提供了系统调⽤execv()在程序运⾏中能够加载并运⾏⼀个可执⾏⽂件,如果⼀个进程调⽤了execv(),那么该函数便会把函数参数path所指定的可执⾏⽂件加载到进程的⽤户内存空间,并覆盖掉原⽂件,然后便运⾏这个新加载的可执⾏⽂件。
进程的停⽌:如果⼀个进程调⽤exit(),那么这个进程会⽴即退出运⾏,并负责释放被中⽌进程除了进程控制块之外的各种内核数据结构。
Linux操作系统案例教程电子教案 第3章 文件管理
第三章
文件管理
第三章 文件管理
3-1文件系统概述 文件系统概述
1、文件系统类型的含义 文件系统类型是指文件在存储介质上存 放及存储的组织方法和数据结构 2、Linux支持的文件系统类型 Linux采用虚拟文件系统技术(VFS)使 Linux支持以下文件系统类型:
第三章 文件管理
• • • • • • • • •
第三章 文件管理
4.2.2 与文件和目录相关的 与文件和目录相关的Shell命令 二) 命令(二 命令
(1)cat
• 格式:cat • 实例:
# cat # cat # cat # cat
<文件名>
/etc/inittab > /abc/abc.txt myfile1 > myfile2 myfile1 >> myfile2
EXT2 EXT3 SWAP FAT、FAT32 SYSV ISO9660 NFS Minix ……
二次扩展 三次扩展 交换文件系统 Unix的文件系统 光盘文件系统 网络文件系统
第三章 文件管理
Linux还支持基于Windows和Netware的文件系 统,例如UMSDOS、MSDOS、VFAT、HPFS、 UMSDOS MSDOS VFAT HPFS SMB和NCPFS,NTFS等。
# rmdir ./a1/ # rmdir /etc/a2/ # rmdir -p /d1/d2/
第三章 文件管理
案例二:
1. 2. 3. 4. 5. 6. 7. 在根目录(/)下新建目录test,test1,把/etc/passwd分别复制到/test1与 /test下,并分别改名为file1与file; 查看file1文件的前二行与最后二行,并记录; 查看/etc/目录下的文件,并记录前两个文件的文件名; 查看/etc/目录中所有的文件中包含有sys字母的文件并记录; 查看/etc/目录中文件包含有conf的前两个文件是什么; 把/test/file文件建一个软链接文件file.soft到/test1中; 清屏; (注:以上文件的查看均以正常模式查看、即按名称查看)
Linux字符设备中的两个重要结构体(file、inode)
Linux字符设备中的两个重要结构体(file、inode)对于Linux系统中,⼀般字符设备和驱动之间的函数调⽤关系如下图所⽰上图描述了⽤户空间应⽤程序通过系统调⽤来调⽤程序的过程。
⼀般⽽⾔在驱动程序的设计中,会关系 struct file 和 struct inode 这两个结构体。
⽤户空间使⽤open()系统调⽤函数打开⼀个字符设备时(int fd = open("dev/demo", O_RDWR) )⼤致有以下过程:1. 在虚拟⽂件系统VFS中的查找对应与字符设备对应 struct inode节点2. 遍历字符设备列表(chardevs数组),根据inod节点中的 cdev_t设备号找到cdev对象3. 创建struct file对象(系统采⽤⼀个数组来管理⼀个进程中的多个被打开的设备,每个⽂件秒速符作为数组下标标识了⼀个设备对象)4. 初始化struct file对象,将 struct file对象中的 file_operations成员指向 struct cdev对象中的 file_operations成员(file->fops = cdev->fops)5. 回调file->fops->open函数⼀、inode结构体VFS inode 包含⽂件访问权限、属主、组、⼤⼩、⽣成时间、访问时间、最后修改时间等信息。
它是Linux 管理⽂件系统的最基本单位,也是⽂件系统连接任何⼦⽬录、⽂件的桥梁。
内核使⽤inode结构体在内核内部表⽰⼀个⽂件。
因此,它与表⽰⼀个已经打开的⽂件描述符的结构体(即file ⽂件结构)是不同的,我们可以使⽤多个file ⽂件结构表⽰同⼀个⽂件的多个⽂件描述符,但此时,所有的这些file⽂件结构全部都必须只能指向⼀个inode结构体。
inode结构体包含了⼀⼤堆⽂件相关的信息,但是就针对驱动代码来说,我们只要关⼼其中的两个域即可:1. dev_t i_rdev; 表⽰设备⽂件的结点,这个域实际上包含了设备号。
Linux实验总结分析报告
Linux实验总结分析报告前⾔感谢中科⼤孟⽼师和李⽼师的精⼼讲授,在本门课中,我受益匪浅。
通过阅读孟⽼师的著作——《庖丁解⽜Linux》,更使我加深了对本门课的理解。
再次感谢两位⽼师。
⼀、精简的Linux系统的概念模型冯·诺依曼体系结构如图所⽰,其中运算器、存储器、控制器、输⼊设备和输出设备5⼤基本类型部件组成了计算机硬件;核⼼是CPU,与内存和输⼊输出(I/O)设备进⾏交互,是整个计算机的灵魂、⼤脑。
内存则是存放了指令和数据,⼆者皆以⼆进制形式存放。
输⼊输出设备则包括⿏标、键盘、显⽰器与磁盘等等设备,是计算机与外界进⾏交互的⼯具。
进程管理进程管理的重点是进程的执⾏。
在内核中,这些进程称为线程,代表了单独的处理器虚拟化(线程代码、数据、堆栈和 CPU寄存器)。
在⽤户空间,通常使⽤进程这个术语,不过 Linux 实现并没有区分这两个概念(进程和线程)。
内核通过 SCI 提供了⼀个应⽤程序编程(API)来创建⼀个新进程(fork、exec 或 Portable Operating System Interface [POSⅨ] 函数),停⽌进程(kill、exit),并在它们之间进⾏通信和同步(signal 或者 POSⅨ机制)。
进程管理还包括处理活动进程之间共享 CPU 的需求。
内核实现了⼀种新型的调度算法,不管有多少个线程在竞争 CPU,这种算法都可以在固定时间内进⾏操作。
这种算法就称为 O⑴调度程序,这个名字就表⽰它调度多个线程所使⽤的时间和调度⼀个线程所使⽤的时间是相同的。
O⑴调度程序也可以⽀持多处理器(称为对称多处理器或 SMP)。
您可以在 ./linux/kernel 中找到进程管理的源代码,在 ./linux/arch 中可以找到依赖于体系结构的源代码。
内存管理内核所管理的另外⼀个重要资源是内存。
Linux 包括了管理可⽤内存的⽅式,以及物理和虚拟映射所使⽤的硬件机制。
不过内存管理,要管理的可不⽌ 4KB缓冲区。
简述linux的文件系统结构
简述linux的文件系统结构
Linux的文件系统结构是一个层次结构,从根目录(/)开始,每个目录都可以包含文件和子目录。
下面是主要目录的简要说明:
·/bin - 包含系统中最基本的命令(例如cp、ls、mv、rm等)。
·/boot - 包含用于启动系统的文件,如内核、引导装载程序等。
·/dev - 包含设备文件,如磁盘、键盘、鼠标等硬件设备。
·/etc - 包含系统的配置文件。
·/home - 包含所有用户的主目录。
·/lib - 包含与系统运行有关的库文件,如动态链接库。
·/media - 包含可插入媒体(如CD-ROM、DVD、USB驱动器等)的挂载点。
·/mnt - 包含临时挂载的文件系统。
·/opt - 用于安装附加软件包。
·/proc - 动态映射到正在运行的进程,系统信息等。
·/root - 管理员的主目录。
·/sbin - 包含系统管理员使用的系统命令和实用程序。
·/tmp - 用于存储临时文件。
·/usr - 包含用户安装的应用程序和文件。
·/var - 用于存储程序数据和日志文件。
linux文件系统的组织结构
linux文件系统的组织结构Linux文件系统的组织结构采用树型结构,类似于Windows文件系统。
其主要的目录如下:1. 根目录(/): Linux文件系统的根目录,所有目录都是从根目录开始的。
2. bin目录(/bin): 存放系统的核心程序,包括各种系统命令和工具。
3. boot目录(/boot): 存放系统启动需要的文件,包括引导程序和内核。
4. dev目录(/dev): 存放设备文件,在Linux中一切设备都是文件,包括硬件设备、外部设备等。
5. etc目录(/etc): 存放系统的配置文件,包括密码文件、主机名等。
6. home目录(/home): 存放所有用户的home目录,包括个人设置、数据等。
7. lib目录(/lib): 存放系统的共享库文件,包括各种动态链接库。
8. media目录(/media): 用于挂载外部设备的目录,如U盘、CD/DVD等。
9. mnt目录(/mnt): 用于挂载文件系统的目录。
10. opt目录(/opt): 存放可选软件的安装目录。
11. proc目录(/proc): 存放系统内核信息和运行信息,如进程和内存使用情况。
12. root目录(/root): 默认的root用户的home目录。
13. sbin目录(/sbin): 存放系统管理员使用的系统命令。
14. srv目录(/srv): 存放服务器的数据文件。
15. sys目录(/sys): 存放设备驱动相关的信息。
16. tmp目录(/tmp): 存放各种临时文件,如进程间通信使用的文件、临时下载文件等。
17. usr目录(/usr): 存放系统软件和用户共享的文件。
18. var目录(/var): 存放系统的可变文件,如日志文件、邮件等。
以上是Linux文件系统的主要目录,其中一些目录又包含了更多子目录。
了解Linux文件系统的组织结构有助于用户更好地管理文件和文件夹。
数据结构的实际应用案例分析
数据结构的实际应用案例分析数据结构是计算机科学中非常重要的一个概念,它是指数据元素之间的关系,以及数据元素本身的存储结构。
在计算机领域中,数据结构被广泛应用于各种算法和数据处理过程中,能够提高程序的效率和性能。
本文将通过几个实际应用案例来分析数据结构在现实生活中的应用。
1. 社交网络中的好友关系图在社交网络中,人们之间的好友关系可以用图这种数据结构来表示。
每个人都是一个节点,而好友关系则是节点之间的边。
通过这种数据结构,社交网络平台可以快速地查找用户的好友、好友的好友,实现推荐好友、推荐兴趣爱好等功能。
同时,还可以通过分析好友关系图,发现用户之间的社交圈子、影响力等信息,为用户提供个性化的服务。
2. 文件系统中的目录结构在计算机的文件系统中,文件和目录之间的关系可以用树这种数据结构来表示。
树的根节点代表根目录,每个目录都是一个节点,而文件则是叶子节点。
通过树这种数据结构,文件系统可以实现文件的组织和管理,快速地查找文件、创建目录、移动文件等操作。
同时,还可以通过树的遍历算法,实现文件系统的备份、恢复等功能。
3. 路由器中的路由表在网络通信中,路由器通过路由表来决定数据包的转发路径。
路由表可以用哈希表、树等数据结构来实现,不同的数据结构对应不同的路由查找算法。
通过合理设计路由表的数据结构和算法,可以提高路由器的转发效率,减少网络延迟,提升网络性能。
4. 搜索引擎中的倒排索引在搜索引擎中,倒排索引是一种常用的数据结构,用于快速地查找包含特定关键词的文档。
倒排索引将关键词与包含该关键词的文档之间的映射关系存储起来,通过这种数据结构可以实现搜索引擎的关键词检索功能。
倒排索引可以通过哈希表、树等数据结构来实现,不同的数据结构对应不同的搜索算法,影响搜索引擎的检索速度和准确性。
5. 数据库系统中的索引结构在数据库系统中,索引是一种用于加快数据检索速度的数据结构。
数据库系统可以通过B树、B+树等数据结构来实现索引,提高数据库的查询效率。
linux各个子系统之间的关系
linux各个子系统之间的关系
Linux 各个子系统之间的关系是紧密联系的,它们共同构成了
完整的操作系统。
主要的子系统包括:
1. 内核(kernel):Linux 操作系统的核心部分,负责操作系
统的管理和控制,包括进程管理、内存管理、文件系统管理等。
内核提供了与硬件交互的接口,使得其他子系统可以利用硬件资源。
2. Shell:Shell 是连接用户和内核的接口。
用户通过 Shell 向内核发送命令,并接收内核的响应。
常见的 Shell 包括 Bash、
Zsh等。
3. 文件系统(File System):文件系统负责管理存储在硬盘上
的文件和目录,以及对它们的读写操作。
常见的文件系统包括ext4、FAT32等。
4. 网络子系统(Network Subsystem):网络子系统负责管理
计算机与网络之间的通信,包括网络配置、网络连接、数据传输等。
5. 图形用户界面(Graphical User Interface,GUI):图形用户
界面提供了一个直观的交互界面,使用户可以通过鼠标、键盘等设备进行操作。
常见的图形用户界面包括 GNOME、KDE、Xfce等。
6. 设备驱动程序(Device Drivers):设备驱动程序负责管理
和控制硬件设备,以便操作系统可以与各类设备交互。
常见的设备驱动包括显示器驱动、声卡驱动、打印机驱动等。
这些子系统之间紧密相连,相互协作,共同构成了 Linux 操作系统,提供了用户友好的界面、高效的资源管理和多样化的功能。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
文件系统相关数据结构及相互间的关系一.详细关系:1.进程要访问文件,就要首先与文件系统中要访问的文件建立连接,在进程数据结构task_struct中,有两个指针fs和files,一个指向fs_struct数据结构,是关于文件系统的信息;另一个指向files_struct数据结构,是关于已打开文件的信息。
2.fs_struct数据结构中有dentry结构指针,dentry结构中有inode结构指针。
Dentry结构所代表的是逻辑意义上的文件,记录的是其逻辑上的属性,而inode 结构所代表的是物理意义上的文件,记录的是物理上的属性。
它们之间的关系是多对一的关系。
Inode结构中定义union数据结构用于大致反应Linux内核目前所支持的各种文件系统。
2.1.dentry结构中有一个d_inode指针指向相应的inode结构,dentry结构代表的是逻辑意义上的文件,描述文件的逻辑属性,因此目录项在磁盘上并没有对应的映像;而inode结构代表的是物理意义上的文件,记录其物理属性,对与一个具体的文件系统,inode结构在磁盘上有对应的映像。
由此可见,一个索引节点对象可能对应多个目录项对象。
一个有效的dentry结构必定对应一个inode 结构,这是因为一个目录项要么代表一个文件,要么代表一个目录,而目录实际上也是文件。
所以只要dentry结构是有效的,则其指针d_inode必定指向一个inode结构。
反之则不成立,因为一个inode可以对应多个dentry结构,即一个文件可以有不止一个文件名或路径名。
因为一个已经建立的文件可以被链接到其他文件名。
所以inode结构中有一个i_dentry,凡是代表着同一个文件的所有目录项都通过其dentry结构体中的d_alias域挂入相应的inode结构体中的i_dentry队列中。
2.2.inode包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。
它是linux管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。
inode结构中的静态信息取自物理设备上的文件系统,由文件系统指定的函数填写,它只存在于内存中,可以通过inode缓存访问。
虽然每个文件都有相应的inode结点,但是只有在需要的时候系统才会在内存中为其建立相应的inode数据结构,建立的inode结构将形成一个链表,我们可以通过遍历这个链表去得到我们需要的文件结点,VFS也为已分配的inode构造缓存和hash table,以提高系统性能。
inode结构中的struct inode_operations *iop为我们提供了一个inode操作列表,通过这个列表提供的函数我们可以对VFS inode结点进行各种操作。
每个inode结构都有一个i结点号i_ino,在同一个文件系统中每个i结点号是唯一的。
3.在打开文件时,要执行路径名查找。
对于不在目录项高速缓存内的路径名元素,会创建一个新的目录项对象和索引节点对象。
当VFS访问一个Ext2磁盘索引节点时,它会创建一个ext2_inode_info类型的索引节点描述符二.数据结构分析:struct fs_struct {atomic_t count;/* 计数器*/rwlock_t lock;/* 读写锁*/int umask;struct dentry * root, * pwd, * altroot;/* 本进程根目录、进程当前所在目录、替换根目录*/struct vfsmount * rootmnt, * pwdmnt, * altrootmnt;/* 本进程根目录的"安装点"、进程当前所在目录的"安装点"、替换根目录的"安装点" */};struct dentry {atomic_t d_count; /* 目录项对象使用计数器,可以有未使用态,使用态和负状态*/unsigned int d_flags; /* 目录项标志*/struct inode * d_inode; /* 与文件名关联的索引节点*/struct dentry * d_parent; /* 父目录的目录项对象*/struct list_head d_hash; /* 散列表表项的指针*/struct list_head d_lru; /* 未使用链表的指针*/struct list_head d_child; /* 父目录中目录项对象的链表的指针*/struct list_head d_subdirs; /* 对目录而言,表示子目录目录项对象的链表*/struct list_head d_alias; /* 相关索引节点(别名)的链表*/int d_mounted; /* 对于安装点而言,表示被安装文件系统根项*/struct qstr d_name; /* 文件名*/unsigned long d_time; /* 被d_revalidate使用*/struct dentry_operations *d_op; /* 目录项方法*/struct super_block * d_sb; /* 文件的超级块对象*/vunsigned long d_vfs_flags; /* 标志位*/void * d_fsdata; /* 与文件系统相关的数据*/unsigned char d_iname [DNAME_INLINE_LEN]; /* 存放短文件名*/ };struct inode {struct hlist_node i_hash; /* 哈希表*/struct list_head i_list; /* 索引节点链表*/struct list_head i_dentry; /* 目录项链表*/unsigned long i_ino; /* 节点号*/atomic_t i_count; /* 引用记数*/umode_t i_mode; /* 访问权限控制*/unsigned int i_nlink; /* 硬链接数*/uid_t i_uid; /* 使用者id */gid_t i_gid; /* 使用者id组*/kdev_t i_rdev; /* 实设备标识符*/loff_t i_size; /* 以字节为单位的文件大小*/struct timespec i_atime; /* 最后访问时间*/struct timespec i_mtime; /* 最后修改(modify)时间*/ struct timespec i_ctime; /* 最后改变(change)时间*/ unsigned int i_blkbits; /* 以位为单位的块大小*/ unsigned long i_blksize; /* 以字节为单位的块大小*/ unsigned long i_version; /* 版本号*/unsigned long i_blocks; /* 文件的块数*/unsigned short i_bytes; /* 使用的字节数*/spinlock_t i_lock; /* 自旋锁*/struct rw_semaphore i_alloc_sem; /* 索引节点信号量*/ struct inode_operations *i_op; /* 索引节点操作表*/ struct file_operations *i_fop; /* 默认的索引节点操作*/ struct super_block *i_sb; /* 相关的超级块*/struct file_lock *i_flock; /* 文件锁链表*/struct address_space *i_mapping; /* 相关的地址映射*/ struct address_space i_data; /* 设备地址映射*/struct dquot *i_dquot[MAXQUOTAS];/* 节点的磁盘限额*/ struct list_head i_devices; /* 块设备链表*/struct pipe_inode_info *i_pipe; /* 管道信息*/struct block_device *i_bdev; /* 块设备驱动*/unsigned long i_dnotify_mask;/* 目录通知掩码*/struct dnotify_struct *i_dnotify; /* 目录通知*/unsigned long i_state; /* 状态标志*/unsigned long dirtied_when;/* 首次修改时间*/unsigned int i_flags; /* 文件系统标志*/unsigned char i_sock; /* 套接字*/atomic_t i_writecount; /* 写者记数*/void *i_security; /* 安全模块*/__u32 i_generation; /* 索引节点版本号*/union {struct minix_inode_info minix_i;/* minix:迷你版本的类Unix操作系统*/struct ext2_inode_info ext2_i; /* ext2:GNU/Linux 系统中标准的文件系统*/struct hpfs_inode_info hpfs_i;/* hpfs:IBM为PC开发的os/2操作系统所采用的文件系统。
这种格式只用于硬盘,而OS/2所用的软盘则与msdos相同*/struct ntfs_inode_info ntfs_i;/* ntfs:windows NT 的文件系统*/struct msdos_inode_info msdos_i;/* msdos: 微软非NT内核的操作系统,具有只读、系统、隐藏三个属性*/struct umsdos_inode_info umsdos_i;/* umsdos:一种特殊的“文件系统”,用msdos 文件系统来模拟Ext2文件系统。
其好处是可以再磁盘上的DOS分区中直接运行linux,而不需要先重新划分区并格式化,坏处首先是降低了运行的速度,而且这样一来就对DOS文件系统的病毒失去了免疫力*/struct iso_inode_info isofs_i;/* isofs:用于CDROM(光盘)*/struct nfs_inode_info nfs_i;/* nfs:“网络文件系统”NFS */struct sysv_inode_info sysv_i;/* sysv:unix系统V的文件系统S5FS */。