第9章 文件系统
文件系统的选择是系统能够正常运行的基本条件。 使用了某种文件系统,就必须遵守其中的规则,否则,您在使用计算机的过程中就会遇到一些问题。
本章主要介绍了: ◆ Linux的文件系统结构。 ◆ Linux采用的VFS,支持如romfs、ramfs、ex2、jffs2、ms-dos、nfs等各种文件系统。 ◆ Linux源程序和开发板根文件系统romfs的目录结构和功能描述。 ◆ 在uClinux中文件的打开和读写操作示例。
9.1 文件系统结构 文件系统是操作系统的重要组成部分。 Linux特性之一就是对多种文件系统的 支持。 9.1 文件系统结构 文件系统是操作系统的重要组成部分。 Linux特性之一就是对多种文件系统的 支持。 这种特性使得Linux很容易地同其他操作 系统共存。
文件系统的概念使得用户能够查看存储设备上的文件和路径而无须考虑实际物理设备的文件系统类型。 Linux透明的支持许多不同的文件系统,将各种安装的文件和文件系统以一个完整的虚拟文件系统的形式呈现给用户。
Linux在早期采用的是minix文件系统,但minix只是一种教学型的文件系统,它所管理的文件系统最大为64MB,文件名小于14个字符。 目前Linux中,使用的主流文件系统是Ext2/3。
除Ext2/3以外,在Linux发展过程中,设计人员比较早的考虑到了对其他类型文件系统的支持。 要实现这一目的,需要将它们的操作和管理纳入统一的框架当中来,使得内核的文件系统接口,如同标准的的文件系统“总线”,让用户通过同一组系统调用来管理和操作不同类型文件系统上的文件。
这个统一的、抽象的、虚拟的文件系统接口,被称为VFS(virtual FileSystem Switch),它主要包含一组标准的文件操作接口。 通过VFS,用户看到的都是一个个的VFS文件,忽略了文件本身所处文件系统的差别。
将内核比作计算机主板,那么VFS就是一种标准的系统总线,各种文件系统就是插在该总线上的子卡,虽然各个子卡的内部线路不同,但在总线接口处一致。 同时,内核看到的所有子卡的特性也映射为具有共性的几类。 如图9-1所示。
图9-1 VFS与具体文件系统的关系
目前Linux系统支持的文件系统种类很多,包括: ntfs(windows NT的文件系统), msdos(dos的文件系统), isofs(光盘文件系统), nfs(网络文件系统),
romfs(rom中的文件系统), smbfs(即samba,可以在网络中与 win98、winnt共享), proc(目录/proc下的特殊文件系统), jffs(flash中的文件系统)。
当用户访问一个磁盘文件系统时,对于目录树下的访问,最终将转换成对于这一部分物理磁盘的访问。 对于设备文件的访问,最终将转换成对于对于驱动程序的访问。
对于其他特殊文件的访问,一般是在Linux的内存数据结构中完成的。 对于磁盘文件系统中的文件的访问和磁盘设备文件的访问最终都反应在磁盘驱动程序上,它们有什么不同吗?
对于磁盘文件系统中文件的访问,是在文件目录树的结构下,对于有组织的数据进行的; 而对磁盘设备文件进行访问,则是对于线性空间内数据的访问,也就是无法看到数据的组织情况。 文件系统的层次结构如图9-2所示。
图9-2 Linux文件系统层次图
9.2 文件系统类型 在uClinux的系统中,由于硬件资源和使用环境的限制,一般内存的数量比较小(8~32M字节),外存只配置小容量的FLASH;所以,在uClinux中,比较常用的文件系统有romfs文件系统,虚拟ramdisk,JFFS文件系统。 对于有网络设备的系统,还可以使用NFS文件系统。
9.2.1 romfs文件系统 1. romfs特点 romfs(rom file system)是一种只读文件系统,占用系统资源也比较小。
起初,设计它的目的是在启动盘(包括光盘和软盘)等场合下,提供一个比普通文件系统(如功能强大的ext2)更加节省空间的文件系统。 创建romfs文件系统需要使用genromfs工具。
目前romfs已经获得了广泛的应用,日趋成熟。 根据romfs开发者的介绍,如果把文件系统编译成为Linux的一个内核module进行加载,那么minix文件系统编译出来的module的大小将会超过20KB(经测试在2.4.7内核中编译出来的minix.o是33909字节),而采用i586编码(即在普通奔腾机上)的romfs的module可以不超过1个代码页即4KB(测试结果2.4.7内核中是7501字节,超过了4KB)。
此外虽然Linux下的msdos文件系统根本就未支持设备文件和符号链接,msdos文件系统在kernel 2. 4 此外虽然Linux下的msdos文件系统根本就未支持设备文件和符号链接,msdos文件系统在kernel 2.4.7下编译出来的module(msdos.o)大小仍然达到了10670B。
从上面的比较可以看出,在romfs中,文件系统的管理代码占用的空间比较小,这—点就可以从一个方面看出romfs比较节省空间。
需要注意的是,romfs的文件访问权限和属主这些信息尚没有完全实现。 要满足这些写操作的要求,只能在编译的时候加上写访问功能,或者采取变通的手段,在运行的时候另外生成一个RAMdisk宋暂存数据。
2. romfs文件系统结构 romfs文件系统是为了对块设备进行高效管理而开发的。所有的romfs文件,经过genromfs程序生成之后,合并到一个文件(即romfs的映象文件)中去。
只需要采用mount命令将这个文件挂接到任何一个目录下,就可以对这个romfs中的文件按照正常方式进行访问了。
表 9-1 romfs映像文件头部结构
romfs映像文件开始的8个字节存放了ASCII码“-ROM1FS-”,之后存放了这个文件系统中的字节总数。 checksum处存放的是从文件头开始的512个字节的校验码。然后是文件系统的卷标名称,该名称以ASCII值为0的字符结尾,所占用的空间为16字节的整数倍。 后面就开始存放各个文件的头部结构了。 文件的表示结构如表9-2所示。
表 9-2 romfs中的文件头结构
其中,spec.info域根据该文件(或者目录)的类别而有不同的含义。 主要有如下几种情况:
0:硬链接,此时spec.info域的内容为用于链接的目标文件
4:块设备,此时spec.info域的内容为16bit的主设备号和16bit的从设备号 6:网络socket套接字,此时spec.info域的内容无效,应该设置为0值 7:fifo管道文件,此时spec.info域的内容无效,应该设置为0值
由于romfs文件系统为了减小module的大小省略了很多完整性检测的代码,所以使用者要自己注意,如不要生成硬链接循环,要自己为当前目录和父目录生成“.”和“..”链接。 当然,这些工作genromfs工具通常都会自动做好的。
3. romfs使用 EV44B0II的romfs的制作过程如图9-3所示。
图9-3 EV44B0II下的romf文件系统的制作
(1) 在主机的/romfs目录下生成需要的目录结构 (2) 利用genromfs工具, 将/cygdrive/d/aaaa/uClinux-44b0ii/romfs目录生成为ROMdisk的romfs映像文件romfs.img genromfs–v–V"ROMdisk"-f/cygdrive/d/aaaa/images/romfs.img –d/cygdrive/d/aaaa/uClinux-44b0ii/romfs
(3) 将romfs.img作为初始化数据连接到Linux内核当中 (4) 使用romfs的文件系统的时候,可以采用mount命令直接将其挂接到合适的目录下,例如(假设/dev/ram0是romfs文件系统的设备): mount -t romfs /dev/rom0 /var
将会把/dev/ram0挂接到/var目录上去,此后直接对/var目录进行操作即可。但是,romfs映射到内存中,它仍旧无法支持动态擦写保存。 对于系统需要动态保存的数据可以采用虚拟ramdisk或JFFS的方法进行处理。
9.2.2 ramfs文件系统 ramdisk是使用RAM虚拟的磁盘(该驱动程序在/linux/drivers/block/rd.c),是一个内存块,作为一个盘分区使用;或者说将内存模拟为硬盘空间,从而可以像对待硬盘空间一样在其上保存文件。
使用ramdisk主要是为了提高访问速度,若事先知道特定的文件将被高频率访问,通过将文件存放在内存里就可以提高性能。 在这个虚拟磁盘的基础上,加载EXT2文件系统来管理和操作文件。
使用ramdisk是非常简单的。 ramdisk的大小是可以配置的,其配置项是/config/.config文件中的CONFIG_USER_RAMIMAGE_RAMFS128(128K)。ramdisk的加载过程包含在/vendors/micetek/44b/rc文件中: bin/expand /etc/ramfs.img /dev/ram0 mount -t ext2 /dev/ram0 /var
首先将预先生成的EXT2的磁盘映像的压缩格式,释放到ram块设备上。 然后使用mount命令将ram块设备中的文件系统安装到根文件系统的/var下。 随后就可以对ramdisk进行文件操作了。
9.2.3 JFFS与JFFS2文件系统 ramdisk是运行在内存中的,所以在系统掉电后,所有保存的数据都会丢失。 这时,一个比较好的选择是JFFS文件系统(最新的版本是JFFS2),JFFS文件系统一般使用FLASH作为磁盘,所以在掉电后数据不会丢失; 而且,JFFS可以对由于在掉电时刻操作导致的不完整数据进行修复。
1.JFFS (1) 存储格式 第一版本的JFFS是一个日志结构的文件系统。在Flash的存储空间中,数据和辅助信息都依次存放于其中。 在该文件系统中,只有一种文件节点,它通过jffs_raw_inode这个结构进行描述。每个这样的节点都关联到某个文件上,其中包含了一个简单的头部、辅助信息以及存储的数据。
其中存储的辅助信息不仅包括通常所记录的文件inode信息(如uid,gid等),也包含了它所关联的文件的名字等。 由于在JFFS中,大的文件都分为很多节点存放,所以除了必须存放数据之外,还要额外保存这段数据在文件中的偏移量。
设备文件节点和符号链接等特殊的文件只需要存储很少量的文件信息,例如设备文件只需要在数据区存放主次设备号即可,而符号链接文件则只需要在数据区存放链接目标的文件名称即可。 所以这种文件只需要占用很小的空间,通常不会占用超过一个节点。
删除文件的时候,采用的方法是在辅助信息存放的地方设置一个已删除标记,所有关于被删除文件的Flash块都会加上这个已删除标记,在使用该文件的文件句柄释放之后,这些节点就成为废弃节点了。
此外,在更新文件的时候,新的文件存放的节点会添加到所有已经使用的存储空间的末尾,旧版本的文件也会被废弃,等待在回收空间的时候重新分配利用。 第一版本JFFS只有一种记录节点格式,所以尚不支持硬链接。
(2) 文件系统操作的实施过程 ① 挂接 挂接JFFS文件系统时,会把整个存储设备扫描一次,每个存储块都读取一次,然后根据所有节点中存储的信息来生成一个文件系统的目录树,同时也自动生成了一个文件在Flash中物理存储位置的对应表,用来进行文件的寻址操作。
② 读取 文件目录结构可以通过mount时生成的信息获得,cd和rmdir等目录操作都可以直接根据这些信息来确定如何操作。 而读取文件的内容的时候,利用mount时生成的物理地址表可以轻松地将指定的内容读入到缓存区中以备使用。
③ 改变属性 例如改变文件的属主(chown命令)和操作权限(chmod命令)等操作,只需要将一个记录新的信息的节点写到Flash已利用的存储空间的末尾,然后将旧节点标记为废弃节点即可。 写文件等操作也与此类似,并不是通过直接改写旧节点来完成的。
(3) 空间回收 可以看出,在JFFS的操作中,是不会对废弃的存储块来进行重新利用的。随着对文件系统进行过的操作不断积累,占用的存储块会越来越多,已经利用的Flash空间就会不断地增长。 当所有存储块都被占用了之后(可能有一些标记为废弃,但仍然是被占用的),文件系统就要开始对废弃的存储块进行回收再利用了。
当然,回收废弃空间的时候,如果发现所有回收的空间加起来仍不足以满足当前操作的需求,那么系统不能继续当前的操作; 如果是内核线程请求分配空间,那么该线程进入睡眠状态;如果是用户程序请求进行写操作,那么将会返回ENOSPC错误信息。
在进行空间回收时,系统自动从所有存储块中的第一个开始进行分析,不断将废弃的;节点回收,将尚在使用的节点进行合并,这样最终合成出整个的——块废弃的Flash存储块, 这样就可以将这一整块存储块的内容一次性擦除,成为新的空闲块。
(4) 缺陷 关于空间回收,可以看出,JFFS并没有进行太多优化。 它是按照顺序,从第一个数据块开始腾出空间,如果第一块写满了有效数据,它也会将这些数据后移,腾出第一块来作为空闲块。
在极端的情况下,效率会非常低。当然,这种方式也保证了Flash的每一块都可以得到相同的擦写次数,有利于提高整个Flash设备的使用寿命。
JFFS尚不支持对数据进行压缩之后存储。 而在嵌入式系统中,如果数据可以得到最大限度的压缩,可以提高资源的利用率,有利于提高性能、节省成本。
不支持硬链接,每一个存储块中都保存了对应的文件名。 这样,就算是很常用的改名操作,也需要增加一个新的需要保存全部所需数据的存储块进行存储。
2. JFFS2的主要改进 (1) JFFS2的节点头部中增加了一些新的信息,包括CRC校验码和节点类型等。 (2) 由于JFFS空间回收方式的缺陷,在JFFS2中,所有的存储节点都不可以跨越Flash的块界限了。
这样,就可以在回收空间的时候,按照Flash的各个块为单位,进行选择,将最合适的块腾出来,擦除之后作为新的空闲块。 这样,虽然不能像JFFS对每个Flash块都保证同样的擦除次数,但是可以提高效率与利用率。
(3) JFFS2不再像JFFS中只有——种节点,现在有3种节点类型了,分别用于表示擦除块的标记、普通文件、目录。 其中,第一种是在Flash擦除了一块之后建立的,惟一的用处就是表明F1ash的块擦除工作顺利完成。
(4) 文件系统的信息并不像JFFS中那样,全部保存在内存之中,可以很快取得的数据并不保存在内存之中,这样可以提高内存的利用率。 (5) 增加了对数据的压缩。 (6) 开始支持硬链接。
3.JFFS文件系统的结构 JFFS文件系统的结构如图9-4所示。
图9-4 JFFS文件系统结构图
MTD的全称是内存技术设备子系统,它负责操作和管理FLASH和RAM物理器件,由于内置了对于NORFLASH、NANDFLASH、DOC(disk on chip)和RAM的支持,所以MTD能胜任目前大多数应用中的存储器件,并且可以自动识别该器件,所以使用起来很方便
MTD还支持将一块FLASH器件分为不同的分区管理,这样可以将Linux内核和根文件系统分别存在不同的分区,避免由于FLASH擦写和文件系统组织结构的相互影响。
MTD向JFFS提供了字符型和块设备,对于需要无延时的写入使用字符设备,如Linux内核的升级。 块设备为JFFS文件系统服务,在加载JFFS文件系统后,可以操作和管理文件。
对于EV44B0II在使用JFFS文件系统时,要进行特殊处理,因为S3C44B0X的中断向量表在FLASH中,所以在操作FLASH擦写时,要关闭中断和快速中断,以避免FLASH在擦写状态下,返回的状态数据,被误解成跳转地址。
9.2.4 EXT2文件系统 Linux中主流的文件系统一直是EXT2文件系统。 在Linux中,普通文件和目录文件保存在称为块物理设备的磁盘或者磁带上。一套Linux系统支持若干物理盘,每个物理盘可定义一个或者多个文件系统(微机上等价于磁盘分区)。
每个文件系统由逻辑块的序列组成,一个逻辑盘空间一般划分为几个用途各不相同的部分,即引导块、超级块、inode区以及数据区等。
◆ 引导块:在文件系统的开头,通常为一个扇区,其中存放引导程序,用于读人并启动操作系统。 ◆ 超级块(superblock):用于记录文件系统的管理信息。特定的文件系统定义了特定超级块。
◆ inode区(索引节点):一个文件(或目录)占据一个索引节点。第一个索引节点是该文件系统的根节点。利用根节点,可以把一个文件系统挂在另一个文件系统的非叶节点上。 ◆ 数据区:用于存放文件数据或者管理数据(如一级间址块、二级间址块等)。
Linux最早引入的文件系统类型是MINIX。 第一个专门为Linux设计的文件系统类型是EXT(Extended File System,1992年4月),但目前流行最广的是1993年提出的EXT2(Second Extended File System)。
EXT2是Linux中的一个可扩展的强有力的文件系统。 通过VFS的超级块(struct ext2_sb_info ext2_sb)可以访问EXT2的超级块,通过VFS的inode(stuct ext2_inode_info ext2_i)可以访问EXT2的inode。
文件系统EXT2的源代码在/usr/src/linux/fs/ext2目录下,它的数据结构在文件/usr/src/linux/include/linux/ext2_fs.h以及同一目录下的文件ext2_fs_i.h和ext2_fs_sb.h中定义。
在EXT2中,文件也是由逻辑块的序列组成。所有的数据块的长度相同,但是对于不同的EXT2文件系统,其长度可以变化。当然,对于给定的EXT2文件系统,其块的大小在创建时就会固定下来。
EXT2文件系统中的每个文件都用一个单独的inode(即stuct ext2_inode结构)来描述,而每个inode都有一个唯一的标志号。 EXT2文件系统中的每个文件都用一个单独的inode(即stuct ext2_inode结构)来描述,而每个inode都有一个唯一的标志号。 EXT2通过使用inode来定义文件系统的结构以及描述系统中每个文件的管理信息。
挂接(mount)为ext2文件系统 mount -t ext2 /dev/ram0 /tmp 将/dev/ram0上ext2文件系统挂接到/tmp目录上。
9.3 文件系统目录结构 嵌入式Linux中,文件系统通常还是按照标准的目录结构来存放所有的文件的。 在本节之中,选择了uClinux(Micetek)开发环境中的默认目录、文件层次来对嵌入式Linux中的目录与文件进行介绍。 另外,为了阅读和分析Linux代码,也给出了源程序的目录结构。
9.3.1 romfs文件系统目录结构 首先需要了解这个romfs目录树中究竟提供了哪些功能。 因为在嵌入式环境下的资源是非常有限的,所以目录树中的所有文件都应该是系统提供的功能所必需的文件,以免浪费宝贵的存储空间。
在这个开发环境中,使用的是uClinux-2.4.x内核,以及对应的工具。 这块开发板在默认情况下已经提供了通常的Linux文件操作功能以及telnet服务、http服务等,此外还需要对Flash进行擦除操作,所以这些程序及相关的配置文件都会被编译到该目录树中。
图9-5所示的就是uClinux开发环境下romfs文件系统的根目录中的目录结构。
图9-5 uClinux生成的目录结构
这个目录结构基本上同普通Linux系统中的目录是相同的,其中 /bin和/sbin存放了可执行程序; /dev目录存放的是系统设备文件,提供系统中各种设备的说明;
/etc目录存放系统中各种关于用户帐号、网络等的配置文件和启动脚本; /lib下存放了库文件; /proc下面是系统信息(本目录是虚拟目录,并不存放在romfs中,而是在系统运行的时候自动生成);
/usr目录是Linux系统里面占用磁盘空间最大的目录,是用户共享文件目录; /home是系统默认的普通用户的主目录的根目录; /var、/tmp是一些系统记录文件和临时文件的存放地。 本节主要介绍/etc,/bin,/sbin和/dev目录。
1./etc目录 在/etc目录中,通常保存的是启动脚本和应用程序的配置文件。 在uClinux中,/etc目录下默认文件如表9-3所示。
表9-3 /etc目录下主要文件及用途
(1) inetd.conf(未用) 文件inetd.conf中的内容如下: telnet stream tcp nowait root /sbin/telnetd http stream tcp nowait root /sbin/httpd -i uptime stream tcp nowait root /bin/cat /proc/uptime /etc/motd
在现在的发行版本中,inetd.conf通常都被xinetd.conf所取代了。 由于在这个环境中还是使用inetd进行网络服务的管理,所以配置文件依然是inetd.conf。
从这个文件中的内容来看,对telnet的登录请求,将会由/sbin/telnetd来处理; 对http请求,则会由/sbin/http来处理; 对于uptime,则会将/proc/uptime的内容和/etc/issue中的内容显示出来,也就是系统已经启动的时间加上该uClinux中的欢迎信息。
(2) inittab 由于要在启动的时候通过串行口向调试机发回提示信息、错误信息等,因此文件inittab这里要进行终端的初始化。
(3) motd(或issue) 文件motd中的内容主要是uClinux的一些欢迎信息,在启动MICETEK开发版的时候,会在登录之前显示这些欢迎信息。可以将该文件的内容替换为希望显示的内容。
(4) passwd 文件passwd中的内容:root ab6TRGT20sY26r 在MICETEK开发系统中,默认情况下可以使用任何用户登录(通过改动login的源代码来实现的),这里passwd文件尚未使用。
(5) rc 文件rc中的内容: hostname EV44B0II /bin/expand /etc/ramfs.img /dev/ram0 mount -t proc proc /proc mount -t ramfs /dev/ram0 /var
mkdir /var/config mkdir /var/tmp mkdir /var/log mkdir /var/run mkdir /var/lock cat /etc/motd
ifconfig lo 127.0.0.1 route add -net 127.0.0.0 netmask 255.255.255.0 lo dhcpcd -p -a eth0 & ifconfig eth0 192.168.1.20 MDB :9999&
在本系统的rc文件中主要完成了下面的功能: ◆ 设定hostname为EV44B0II。 ◆ 使用ifconfig设定网络(ip地址、网关等)。 ◆ 使用expand将ramfs展开到/dev/ramO。 ◆ 挂接/var(ram盘)和/proc文件系统。 ◆ 建立一些目录,显示欢迎信息,启动MDB调试服务程序。
(6) resolv.conf(未用) resolv.conf中设定了DNS服务器的中地址
(7) services TCP和UDP可以同时被多个进程使用,需要给这些进程分配不同的端口号加以区别。 Services文件包含分配给各种服务的端口号。 如telnet的TCP端口号为23,http为80,ftp为21等。
2./bin、/sbin目录 这个目录是系统中最主要的可执行文件的存放地,这些可执行文件大都是uClinux系统里最常用的命令了,一般用户和超级用户都会经常使用的命令,可以供开发者在选择时参考。 这些命令可以大致归为下面几类:
(1)文件操作:cat,cmp,cp,ln,ls,mkdir,more,mv,pwd,rm,rmdir,cd,chmod,chown,expand,umask。 (2)系统管理:df,free,,kill,mount,ps,sh,shutdown,umount,init,exec,mknod,sleep,telnet,date,echo。
(3) 网络管理:hostname,login,ping,ftp,ifconfig,finger,route。 还有不少很有用的程序(例如grep等)都未被收入,主要是因为体积和功能要求必须进行折衷的原因。
3./dev目录 在普通Linux系统里,采用makedev脚本一劳永逸地生成了几乎所有的可能用到的设备文件,而其中大多数都是实际上根本不可能用得到的。
在采用了devfs(设备文件系统)之后,部分地解决了这个问题。 在嵌入式环境下的/dev目录绝对不能按照普通Linux发行版中的/dev一样。 在嵌入式应用领域中,通常采用的方式则是手工选择创建哪些设备文件节点,尽量不创建用不到的设备文件。
在这个目录下,就仅仅包含了终端、串口、ROM和那些必须具有的设备文件(如mem,null等),构造了一个非常精简的/dev目录。 此外,需要使用什么设备,再添加相应的设备文件。 例如,假如需要支持framebuffer使用图形界面,就可以添加fbO等设备文件的节点,然后使用genromfs重新生成romfs映像文件,上传到MICETEK开发板上即可。
9.3.2 uClinux源程序结构 在MICETECK开发板的uClinux源程序目录为: uclinx-44b0ii,其目录结构描述如下:
1.lib/uclibc lib是用户应用程序库目录,包括libc、libm等函数库。uclibc包括uc-libc、uc-libm等函数库。 2.usr 它包括用户模式下的应用软件,如:ftp,httpd,ifattach,init,mircrowindows,mount,tftp,tftpd,route等各种免费应用软件包。
3.config 针对不同厂商开发板的配置情况,裁减配置uClinux内核,形成的配置脚本文件。此目录提供一个交互的Linux配置程序。 4.vendors 此目录下包括各种开发平台的特殊结构信息和支持文件。如MICETEK平台的支持文件就在ventors/micetek/44b0目录下。
5.romfs 它是一种romfs文件系统,用于构造开发平台的root根文件系统,包括/bin、/etc、/dec、/home、/lib、/mnt、/proc、/usr、/var等字目录。
6.images 包含linux_bootram.bin、linux_bootrom.bin和romfs.img三个文件, linux_bootram.bin是下载到开发平台ram区执行文件, linux_rom.bin是烧制开发平台的flash区的文件, romfs.img是开发平台的root根文件系统romfs映象文件。
7.Linux 2.4.x 它是Linux 2.4.x内核源代码文件目录,下面详细介绍目录文件的分布情况。
(1)kernel目录 此目录下文件实现了大多数Linux系统的核心函数,其中最重要、最主要的文件当属sched.c。 sched.c文件定义的函数有:
◆ 调度程序schedule及相关操作。 ◆ 等待队列及相关操作。 ◆ 基准时钟及相关操作。 ◆ 定时器相关操作任务队列及相关操作。 ◆ 关于调度策略控制的goodness、nice等。 ◆ 各种用户标识、组标识的set和get。
关于进程控制的文件也位于此目录下。其中包括fork.c文件创建、“克隆”子进程的函数do_fork(),exit 关于进程控制的文件也位于此目录下。其中包括fork.c文件创建、“克隆”子进程的函数do_fork(),exit.c文件定义结束自身进程的do_exit(),各种wait操作,以及发送信号(signal)操作send_sig。 而signal.c文件中的函数都是关于信号控制的。
Linux设计一种模块机制。这种机制可以让诸如设备驱动程序等软件模块动态地连接到系统核心中。而ksyms Linux设计一种模块机制。这种机制可以让诸如设备驱动程序等软件模块动态地连接到系统核心中。而ksyms.c文件定义不属于任何子系统的Linux的全程变量。 此外,该目录下还包含一些重要的文件,如:
◆ time.c,提供用户程序与系统之间关于时间的操作界面。 ◆ resource.c,关于I/0端口资源(port)的管理。 ◆ dma.c,关于DMA通道的管理。 ◆ softirq.c,关于bottom half队列的操作。 ◆ itimer.c,关于itimer定时器读写的系统调用。 ◆ printk.c,展示系统工作参数的操作,如printk。
(2)mm/nommu目录 Linux中独立于CPU体系结构特征的内存管理文件几乎都集中在此目录下。如页式存储管理、内存的分配和释放等等。在nommu目录下是没有mmu的内存管理。
文件swap.c并不实现任何交换算法,它仅仅处理命令行选项“swap=”和“buff=”。 swapfile.c文件才是管理交换文件和交换设备的源程序所在。它包含swapon、swapoff系统调用的执行程序,以及从交换空间申请空闲页面的操作get_swap_page。
page_io.c文件则实现了与交换空间底层的数据传输。 swap_state.c文件维护swap缓存,它包含(可以说)存储管理系统中最复杂、最难懂的操作和结构。 vmscan.c文件定义后台交换进程kswapd的代码,以及内存空间扫描函数,实现Linux的页面换出策略,如try_to_free_pages。
所有的存储分配策略都在mm目录里实现。例如,kmalloc 所有的存储分配策略都在mm目录里实现。例如,kmalloc.c实现核心内存块(以数据结构free_area表为人口)的操作kmalloc()、kfree()等. vmalloc.c里包含vmalloc()、vfree()、vremap()等函数。 物理页面的申请函数源程序集中在page_alloc.c中,如页面申请函数—get_free_pages()。
内存管理中底层核心函数大多安排在memory 内存管理中底层核心函数大多安排在memory.c文件中,如缺页中断响应函数do_no_page(),实现Copy on Write(COW)特性的do_wp_page(),以及众多页表管理函数。
虚拟空间映射(mapping)操作do_mmap()和do_munmap(),以及系统调用brk的响应函数,都涉及进程虚拟空间地址的调整,相关源代码在mmap.c文件中。 对mremap的操作代码则在mremap.c文件中。
此外,mlock. c文件实现四个关于内存vma段加锁操作的系统调用mlock、munlock、mlock-a11、munlockall。 此外,mlock.c文件实现四个关于内存vma段加锁操作的系统调用mlock、munlock、mlock-a11、munlockall。 mprotect.C实现mprotect系统调用mprotect。
(3) fs目录 文件处理是所有UNIX系统都提供的基本功能。fs目录源程序涵盖各种类型的文件系统和文件操作。 该目录下的每个子目录则分门别类地描述了某个特定的文件系统。
直接隶属该目录的文件分别是: ◆ exec.c 实现execve系统调用。其余五种关于装入执行程序的函数都由C语言库文件实现。execve支持脚本(script)文件和多种格式的可执行文件。 ◆ devices.c 负责设备的注册和注销,定义缺省的打开设备操作和释放设备操作。
◆ block_dev.c。包含缺省的读、写设备操作 ◆ super.c。 定义超级块的读操作,以及文件系统的安装、卸装操作。 ◆ inode.c。VFS inode的读写操作,以及维护inode缓存的程序。 ◆ dcache.c。 维护dcache的文件。
◆ namei. c。 访问权限检查。根据路径检索dentry的相关操作,如open_namei、path_walk、buffer ◆ namei.c。 访问权限检查。根据路径检索dentry的相关操作,如open_namei、path_walk、buffer.c 实现缓存区缓存的文件。 ◆ open.c。文件的打开、关闭操作。系统调用chown、chmod、fchown、fchmod、chroot、chdir、fchdir等也由该文件实现。
◆ read_write.c。系统调用read、write、lseek、llseek的源程序 ◆ oreaddir.c。读目录项的系统调用readdir和getdents由此文件实现。 ◆ select.c。集中存放select操作的几乎全部源代码。
◆ pipe. c和fifo. c。实现管道(pipe)和命名管道(fifo) ◆ pipe.c和fifo.c。实现管道(pipe)和命名管道(fifo).除了fifo_open()和fifo_init()函数在fifo.c文件外,其余函数均存放于pipe.c文件中。 ◆ ioctl.c。实现系统调用ioctl。 ◆ fcntl.c。实现关于fcntl操作命令的源代码。 ◆ dquot.c。支持磁盘配额机制(quota)。
(4) arch目录 与CPU类型相关的子目录和文件均集中安排在此目录下。 这里又有子目录alpha、arm、armnommu、i386、ia64、m68k、mips、mips64、parisc、ppc、s390、sh、sparc,每个子目录对应一种CPU,例如/arch/armnommu/mach-micetek就是关于MICETEK板,没有mmu的arm CPU 子目录。
(5) include目录 容纳Linux源程序的所有头文件(header file)。其中,与平台无关的头文件在include/linux子目录下,与平台无关低级头文件在asm-generic目录下,与MICETEK 平台相关的头文件在include/asm-armnommu/arch-micetek子目录下。 另外,还有关于网络设备的头文件目录include/net。
(6) net目录 在net目录中存放的是和Linux网络相关的C文件。其中每一种网络地址族都为一个目录,如appletalk、Ipv4等等。在core目录下是各种网络地址族公用的文件。 另外在sched目录下存放的是对高性能网络的QoS支持,khttpd目录中存放的是内核级别的-Web服务器支持。
(7) drivers 在drivers目录下,存放各种设备的驱动程序。其中ev44b0_sound、ev44b0_USB、ev44b0_kbd、ev44b0_ts是MICETEK平台的声卡、USB、键盘和触摸屏的驱动程序在/drivers/char 目录下。Smc9113网卡驱动程序在/drivers/net目录下。 除上述目录外,Linux源程序清单中还有ipc、init等目录。
9.4 简单编程事例 熟悉uClinux环境下文件系统,了解uClinux中文件系统的体系结构,编写文件系统处理程序。 9.4 简单编程事例 熟悉uClinux环境下文件系统,了解uClinux中文件系统的体系结构,编写文件系统处理程序。 例1.在uClinux中实现文件的打开和读写操作,同时对ROMFS和RAMDISK中的EXT2文件系统进行了操作,从而了解了ROMFS文件系统的特性,对在RAMDISK中保存数据有了一些认识。
下面对本例题涉及到的函数进行简要介绍: int creat(const char *pathname,mode_t mode); 以mode方式创建一个以pathname为文件名的文件,返回新的文件句柄fd,错误返回-1及错误代码errno。
Int open(const char *pathname,int flags); 以flags方式打开一个以pathname为文件名的文件,正确返回0,错误返回-1及错误代码errno。
size_t read(int fd,void *buf, size_t count); 把fd指向的文件传送count字节到buf指针所指向的内存中,正确返回实际写入的字节数,错误返回-1及错误代码errno。
size_t write(int fd,void *buf, size_t count); 把buf指针所指向的内存count字节传送到fd指向的文件中,正确返回读到的字节数或0,错误返回-1及错误代码errno。
off_t lseek(int fd,off_t offset,int where); 将fd所指文件的读写指针在where位置移动offset个位移量。 int close(int fd); 关闭fd所指文件,顺利关闭返回0,错误返回-1。 程序框图如图9-6所示。
源程序如下: #include "stdio.h" #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define TEST_STRING "TEST file operation on different FS"
int main(int argc,char * argv[]) { int fd,op_flag; char * filename; int len; char buf[128]; /* check input parameters */
if(argc < 2) { fprintf(stderr,"please give filename"); exit(1); } /* open file */ filename = argv[1]; op_flag = O_RDWR | O_CREAT;
if((fd=open(filename,op_flag)) <0) {/* open error */ fprintf(stderr,"open file %s error",filename); exit(1); } /* write file */
if((len = write(fd,TEST_STRING,strlen(TEST_STRING)+1)) if((len = write(fd,TEST_STRING,strlen(TEST_STRING)+1)) != strlen(TEST_STRING)+1) {/* write file error */ fprintf(stderr,"write file %s error",filename); exit(1); } /* seek current file pointer to the start of file */
if(lseek(fd,0,SEEK_SET) != 0) { fprintf(stderr,"fseek file %s error",filename); exit(1); } /* read file */
if((len = read(fd,buf,128)) != strlen(TEST_STRING)+1) {/* read file error */ fprintf(stderr,"read file %s len error",filename); exit(1); } /* compare write-read */
if(memcmp(TEST_STRING,buf,len) != 0) {/* content different */ fprintf(stderr,"check file %s content error",filename); exit(1); }
printf("\nfile %s open-write-read ok \n",filename); /* close fd */ close(fd); return 0; }
9.5 本章小结 本章主要介绍了Linux的文件系统结构,了解了Linux采用VFS,支持如romfs、ramfs、ex2、jffs2、ms-dos、nfs等各种文件系统; 给出了Linux源程序和开发板根文件系统romfs的目录结构和功能描述; 另外给出了在uClinux中文件的打开和读写操作示例。
练习题 1.简述Linux文件系统结构,并说明VFS的作用。 2.简述romfs和ramfs文件系统结构,并说明区别。 3.简述jffs和jffs2文件系统结构。 4.简述uClinux各个子目录的作用。