『壹』 文件系统是如何建立的
右击,新建文件夹针对各层简要分析如下:1.首先我们来分析最低层——设备驱动层,该层负责与外设——磁盘等——通讯。文件系统都需要和存储设备打交道,而系统操作外设时离不开驱动程序。所以内核对文件的最后操作行为就是调用设备驱动程序完成从主存(内存)到辅存(磁盘)的数据传输。文件系统相关的多数设备都属于块设备,常见的块设备驱动程序有磁盘驱动,光驱驱动等,之所以称它们为块设备,一个原因是它们读写数据都是成块进行的,但是更重要的原因是它们管理的数据能够被随机访问——不需要像字符设备那样必须顺序访问。2.设备驱动层的上一层是物理I/O层,该层主要作为计算机外部环境和系统的接口,负责系统和磁盘之间数据块的交换。它要知道数据块在磁盘中地存储位置,也要知道文件数据块在内存缓冲中的位置,另外,它不需要了解数据或文件的具体结构。可以看到,这层最主要的工作是识别磁盘扇区和内存缓冲块[2]之间的映射关系。3.再上层是基础I/O监督层,该层主要负责选择文件 I/O需要的设备,调度磁盘请求等工作,另外分配I/O缓冲和磁盘空间也在该层完成。由于块设备需要随机访问数据,而且对速度响应要求较高,所以操作系统不能像对字符设备那样简单、直接地发送读写请求,而必须对读写请求重新优化排序,以能节省磁盘寻址时间,另外也必须对请求提交采取异步调度(尤其写操作)的方式进行。总而言之,内核必须管理块设备请求,而这项工作正是由该层负责的。4.倒数第二层是逻辑I/O层,该层允许用户和应用程序访问记录。它提供了通用的记录(record)I/O操作,同时还维护基本文件数据。为了方便用户操作和管理文件内容,文件内容往往被组织成记录形式,所以操作系统为操作文件记录提供了一个通用的逻辑操作层。
『贰』 如何写一个核心系统,数据库,分布式文件系统等等
当下,互联网行业发展非常迅猛,分布式文件系统在其中的应用也非常普遍。一些朋友问起一些相关问题,如:1/p/tfs/wiki/index/ 3. 视频类的,单个文件大小大多在几十兆或上百兆,与TAOTAO里的小图片(几K到几十K)又不一样。参照TAOBAO的文件系统,也许可以一试。GFS或者MooseFS也可以一试。商家,国内有个公司叫龙存,不知道费用如何。使用TFS,单文件跨块存储,如果支持的话,倒不妨一试。4. 这个问题应该分成两块,矢量图形数据本身,应该还要借助于空间数据库(如Oracle Spatial, PostGIS, ArcSDE引擎等)进行存储,因为它还是涉及到事务处理的。对外展现的图形(影像),分级分块,可以借助于小文件聚合的思想。但是数据量,未必有前边3个那么大。普通的可随机读写的GFS是不是能满足需求
『叁』 如何制作linux根文件系统
根文件系统一直以来都是所有类Unix操作系统的一个重要组成部分,也可以认为是嵌入式Linux系统区别于其他一些传统嵌入式操作系统的重要特征,它给Linux带来了许多强大和灵活的功能,同时也带来了一些复杂性。我们需要清楚的了解根文件系统的基本结构,以及细心的选择所需要的系统库、内核模块和应用程序等,并配置好各种初始化脚本文件,以及选择合适的文件系统类型并把它放到实际的存储设备的合适位置。Linux的根文件系统以树型结构组织,包含内核和系统管理所需要的各种文件和程序,一般说来根目录”/”下的顶层目录都有一些比较固定命名和用途。下面列出了一个Linux根文件系统中的比较常见的目录结构:/bin 存放二进制可执行命令的目录该目录下存放所有用户都可以使用的、基本的命令,这些命令在挂接其它文件系统之前就可以使用,所以/bin目录必须和根文件系统在同一个分区中。/bin目录下常用的命令有:cat,chgrp,chmod,cp,ls,sh,kill,mount,umount,mkdir,m knod,[,test等“[”命令其实就是test命令,我们在利用Busybox制作根文件系统时,在生成的bin目录下,可以看到一些可执行的文件,也就是可用的一些命令。/dev 存放设备文件的目录该目录下存放的是设备文件,设备文件是Linux中特有的文件类型,在Linux系统下,以文件的方式访问各种设备,即通过读写某个设备文件操作某个具体硬件。比如通过"dev/ttySAC0"文件可以操作串口0,通过"/dev/mtdblock1"可以访问MTD设备的第2个分区。/etc 存放系统管理和配置文件的目录该目录下存放着各种配置文件,对于PC上的Linux系统,/etc目录下的文件和目录非常多,这些目录文件是可选的,它们依赖于系统中所拥有的应用程序,依赖于这些程序是否需要配置文件。在嵌入式系统中,这些内容可以大为精减。/home 用户主目录,比如用户user的主目录就是/home/user,可以用~user表示用户目录,它是可选的,对于每个普通用户,在/home目录下都有一个以用户名命名的子目录,里面存放用户相关的配置文件。/lib 存放动态链接共享库的目录该目录下存放共享库和可加载(驱动程序),共享库用于启动系统。运行根文件系统中的可执行程序,比如:/bin /sbin 目录下的程序。/sbin存放系统管理员使用的管理程序的目录该目录下存放系统命令,即只有管理员能够使用的命令,系统命令还可以存放在/usr/sbin,/usr/local/sbin目录下,/sbin目录中存放的是基 本的系统命令,它们用于启动系统,修复系统等,与/bin目录相似,在挂接其他文件系统之前就可以使用/sbin,所以/sbin目录必须和根文件系统在同一个分区中。/sbin目录下常用的命令有:shutdown reboot fdisk fsck等,本地用户自己安装的系统命令放在/usr/local/sbin目录下。/tmp 公用的临时文件存储点用于存放临时文件,通常是空目录,一些需要生成临时文件的程序用到的/tmp目录下,所以/tmp目录必须存在并可以访问。/root 系统管理员的主目录根用户的目录,与此对应,普通用户的目录是/home下的某个子目录。/mnt 系统提供这个目录是让用户临时挂载其他的文件系统。用于临时挂载某个文件系统的挂接点,通常是空目录,也可以在里面创建一引起空的子目录,比如/mnt/cdram /mnt/hda1 。用来临时挂载光盘、硬盘。/proc 虚拟文件系统,可直接访问这个目录来获取系统信息。这是一个空目录,常作为proc文件系统的挂接点,proc文件系统是个虚拟的文件系统,它没有实际的存储设备,里面的目录,文件都是由内核临时生成的,用来表示系统的运行状态,也可以操作其中的文件控制系统。/usr 最庞大的目录,要用到的应用程序和文件几乎都在这个目录。/usr目录的内容可以存在另一个分区中,在系统启动后再挂接到根文件系统中的/usr目录下。里面存放的是共享、只读的程序和数据,这表明/usr目录下的内容可以在多个主机间共享,这些主要也符合FHS标准的。/usr中的文件应该是只读的,其他主机相关的,可变的文件应该保存在其他目录下,比如/var。/usr目录在嵌入式中可以精减。/var 某些大文件的溢出区与/usr目录相反,/var目录中存放可变的数据,比如spool目录(mail,news),log文件,临时文件。———————————————————————一、移植环境:1、 Ubuntu 10.10发行版2、 u-boot.bin3、 目标机:FS_S5PC100平台4、 交叉编译器 arm-cortex_a8-linux-gnueabi-gcc———————————————————————二、移植步骤1、 源码下载我们选择的版本是busybox-1.17.3.tar.bz2下载路径为:http://busybox.net/downloads/2、 解压源码$ tar xvf busybox-1.17.3.tar.bz23、 进入源码目录$ cd busybox-1.17.34、 配置源码$ make menuconfigBusybox Settings —> Build Options —> [*] Build BusyBox as a static binary (no shared libs) [ ] Force NOMMU build [ ] Build with Large File Support (for accessing files > 2 GB) (arm-cortex_a8-linux-gnueabi-) Cross Compiler prefix () Additional CFLAGS5、 编译$ make6、 安装busybox默认安装路径为源码目录下的_install$ make install7、 进入安装目录下$ cd _install$ lsbin linuxrc sbin usr8、 创建其他需要的目录$ mkdir dev etc mnt proc var tmp sys root9、 添加库在_install目录下创建一个lib文件夹,将工具链中的库拷贝到lib目录下$ mkdir lib$ cp /home/linux/x-tools/arm-cortex_a8-linux-gnueabi/arm-cortex_a8-linux-gnueabi/lib/* ./lib/删除lib下的所有目录、.o文件和.a文件,对库进行瘦身以减小文件系统的大小$ rm *.o *.a$ arm-cortex_a8-linux-gnueabi-strip lib/*10、 添加系统启动文件在etc下添加文件inittab$ vim /etc/inittab文件内容如下:#this is run first except when booting in single-user mode.:: sysinit:/etc/init.d/rcS# /bin/sh invocations on selected ttys# Start an "askfirst" shell on the console (whatever that may be)::askfirst:-/bin/sh# Stuff to do when restarting the init process::restart:/sbin/init# Stuff to do before rebooting::ctrlaltdel:/sbin/reboot在etc下添加文件fstab$ vim /etc/fstab文件内容如下:#device mount-point type options mp fsck orderproc /proc proc defaults 0 0tmpfs /tmp tmpfs defaults 0 0sysfs /sys sysfs defaults 0 0tmpfs /dev tmpfs defaults 0 0这里我们挂在的文件系统有三个proc、sysfs和tmpfs,在内核中proc和sysfs默认都支持,而tmpfs是没有支持的,我们需要添加tmpfs的支持修改内核配置:$ make menuconfigFile systems —> Pseudo filesystems —> [*] Virtual memory file system support (former shm fs) [*] Tmpfs POSIX Access Control Lists重新编译内核$ make zImage在etc下创建init.d目录,并在init.d下创建rcS文件$ mkdir /etc/init.d -p$ vim /etc/init.d/rcSrcS文件内容为:#!/bin/sh# This is the first script called by init process/bin/mount -a为rcS添加可执行权限:$ chmod +x init.d/rcS在etc下添加profile文件$ vim /etc/profile文件内容为:#!/bin/shexport HOSTNAME=farsightexport USER=rootexport HOME=root#export PS1="\[\[email protected]\h \W\]\$ "export PS1="[[email protected]$HOSTNAME \W]\# "PATH=/bin:/sbin:/usr/bin:/usr/sbinLD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATHexport PATH LD_LIBRARY_PATH11、 设备文件创建根文件系统中有一个设备节点是必须的,在dev下创建console节点$ mknod dev/console c 5 1重要:新制作的文件系统尺寸若超出8M,删除不需要的库文件。
『肆』 如何从零开始写一个简单的操作系统
(一)OS说明今后,我就要开始折腾操作系统,有了一点小小干劲。我的计划是,先看过一份用于教育目的的系统源码,再去翻找相应的资料(我手头已有绿宝书),在翻资料的同时开始写代码,然后做好移植真机的工作,DONE!我也明白,理性很丰满,现实很骨感,这过程不会如同我计划中这般简单和轻松。但是,见难而退可不是我的风格(那样我会被红叶二小姐调戏的),不管如何,我都会,怎么说呢,尽力吧。出于课程需求,斯坦福那些人亲自写了一个名为“pintos”的系统。pintos的结构比较简单,分为进程管理、文件系统、用户程序、虚拟内存等几个部分,也正是因为这个原因,我选择pintos作为我的参考蓝本,现在在读它的源码。在接下来的几个月时间里,不出意外的话,我会不断的在博客上更新我的进度。(三)交叉编译环境倘若我们要在ubuntu上编译另外一个完整的OS,交叉编译环境是必不可少的玩意,维基网络有云:交叉编译器(英语:Cross compiler)是指一个在某个系统平台下可以产生另一个系统平台的可执行文件的编译器。(想起以前,我为了给路由器编译OPENWRT,下载大量源码,愣是编译了几天几夜。那时候的我,真是“可爱”。)为了配置好交叉编译环境,我废了好大力气,最后勉强找到了组织。编译环境大致分为2部分,binutils和gcc。我先装好gcc-4.9.1,之后下载gcc-4.9.1和binutils-2.25的源代码,似乎gcc版本与binutils版本要对应来着…开始编译之前,需要准备全局变量(在命令行中敲入以下命令):export PREFIX=”$HOME/opt/cross”export TARGET=i686-elfexport PATH=”$PREFIX/bin:$PATH”编译Binutilscd $HOME/binutils-2.25mkdir build-binutilscd build-binutils#注意是在源码目录下面新建一个文件夹,然后cd到该文件夹里,然后才配置configure,不这么做的话,嘿嘿…./binutils-x.y.z/configure –target=$TARGET –prefix=”$PREFIX” –with-sysroot –disable-nls –disable-werrormakemake install–disable-nls 告诉binutils,不要添加本地语言支持–with-sysroot 告诉binutils,在交叉编译器中允许sysroot编译GCCcd $HOME/gcc-4.9.1mkdir build-gcccd build-gcc#注意是在源码目录下面新建一个文件夹,然后cd到该文件夹里,然后才配置configure,不这么做的话,嘿嘿…./gcc-x.y.z/configure –target=$TARGET –prefix=”$PREFIX” –disable-nls –enable-languages=c,c++ –without-headersmake all-gccmake all-target-libgccmake install-gccmake install-target-libgcc–disable-nls 告诉GCC,不要添加本地语言支持。–without-headers 告诉GCC,不要依赖任何本地库,我们必须在自己的OS中实现库。–enable-languages 告诉GCC,不要支持除了C、C++之外的语言。提醒不同机器配置不同,编译速度也不同。编译这两个软件,我花了近3个钟,机器配置之低自不必说,说了都是泪。如果任何人的任何编译过程出了任何问题,请仔细地、认真地、用心地再看看上面的命令,在你没有弄懂它的原理之前,请不要擅自做任何“改进”(血淋淋、赤裸裸的教训呀)。(五)OS模糊框架翻完了手头的绿宝书,我才晓得,人都是被逼出来的。操作系统的概念都差不多已经知道,接下来,该由“理论态”切换到“实践态”了喔(书还是不能看太多,会中毒的–)。对了,从别人推荐的地方弄来了一个框架(曾在android平台写了几万代码,我深深体会到框架的作用),轻松开工吧。
『伍』 如何用C写一个虚拟的磁盘和文件系统
我是高手~ 告诉我你邮箱 我发给你
『陆』 如何设计一个小型文件系统,提供创建、打开、读、写、关闭、删除文件的功能。
这是一个简单的程序设计问题,学过c的人都会。在c 里面提供了功能强大的文件管理函数,fopen ,fread,fwrite,fclose,等,多看一下,自己就会了
『柒』 如何制作和使用Jffs2文件系统
嵌入应用:如何制作和使用Jffs2文件系统 (zhuan)本文主要介绍如何在AT91SAM9261EK板子上制作和使用jffs2文件系统,使用的是linux-2.6.21内核。 首先配置MTD $ make menuconfig 进入 Memory Technology Devices (MTD) —> <*> Memory Technology Device (MTD) support [*] Debugging [*] MTD partitioning support [*] Command line partition table parsing [*] Direct char device access to MTD devices [*] Caching block device access to MTD devices RAM/ROM/Flash chip drivers —–> <*> Detect non-CFI AMD/JEDEC-compatible flash chips <*> Support for AMD/Fujitsu flash chips Mapping drivers for chip access —> [*] Support non-linear mappings of flash chips Self-contained MTD device drivers —> [*] Support for AT45… DataFlash NAND Flash Device Drivers —-> [*] NAND Device Support [*] Support for NAND Flash /SmartMedia on AT91 File systems —-> <*> Second extended fs support [*] Inotify file change notification support [*] Inotify support for user space <*> Filesystem in Userspace support Miscellaneous filesystems <*> Journalling Flash File System v2 (JFFS2) support [*] JFFS2 write-buffering support <*> Compressed ROM file system support (cramfs)以上配置中没有列出的,都没选;其配置仅做参考,可根据自己的需要自行配置。 $ make all 其次制作mtd-util工具 从网上下载zlib-1.2.3.tar.gz解压缩$ cd zlib-1.2.3 $ ./configure –prefix=/usr/local/arm/3.4.1/arm-linux –shared 修改Makefile如下: CC=arm-linux-gcc LDSHARED=arm-linux-ld -shared $ make all $ make install 注意:这里是安装在/usr/local/arm/3.4.1/arm-linux目录下 由于交叉编译mtd工具时需要zlib.h文件,所以在编译之前先安装zlib库文件。 从网上下载mtd-snapshot-20050519.tar.bz2 解压缩 $ cd mtd/util 修改该目录下的Makefile: CROSS=arm-linux- $ make all 然后将该目录下生成的 flash_erase,flash_eraseall, mkfs.jffs2工具放在ramdisk 文件系统中(我这里放在/bin目录下),另外在ramdisk文件系统的dev目录下要保证有mtd0~mtd9,mtdblock0~mtdblock9这些设备,如果没有可参考 ramdisk文件系统的制作,也可从pc机相同目录下拷贝,要加上文件属性。 另外,需要将/arm-linux/lib目录下的libz.so, libz.so.1, libz.so.1.2.3文件拷贝到ramdisk文件系统的/lib目录下,否则mkfs.jffs2工具不能使用。 最后将新生成的uImage和ramdisk文件下载到板子上,起动系统,使用命令 cat /proc/mtd可以看到 dev: size erasesize name mtd0: 00040000 00020000 "Partition 1" mtd1: 0ffc0000 00020000 "Partition 2" mtd2: 00420000 00000210 "spi0.0-AT45DB321x" 这里mtd0,mtd1是nandflash上的分区; mtd2是dataflsh上的分区,该分区上放有u-boot,uImage.img,ramdisk.img,所以我们这里可以使用空的nandflash上的两个分区。使用之前要先用工具flash_erase或者flash_eraseall擦除nandflash,具体使用的步骤如下: # flash_erase /dev/mtd1 制作jffs2映像 # cd /var/tmp # mkdir jffs2 (jffs2下的目录可以任意建) # mkfs.jffs2 –d jffs2/ -o jffs2.img # cp /var/tmp/jffs2/jffs2.img /dev/mtdblock1 最后# mount -t jffs2 /dev/mtdblock1 /mnt/mtd即可,使用结束可使用$ umount /mnt/mtd卸载. 如果只是当作普通的jffs2 来使用dataflash或者nandflash,可不必制作 jffs2映像,只需要最后一步,# mount -t jffs2 /dev/mtdblock1 /mnt/mtd即可。AT91sam9263EK使用JFFS2根文件系统 在AT91sam9263EK成功配置了NFS根文件系统后,后来又想把JFFS2也移植到AT91sam9263EK上吧,说干就干。1. 呵呵,其实很简单我使用的linux-2.6.20内核里已经有jffs2 文件系统支持,选上jffs2即可File systems —>Miscellaneous filesystems —><*> Journalling Flash File System v2 (JFFS2) support (0) JFFS2 debugging verbosity (0 = quiet, 2 = noisy) [*] JFFS2 write-buffering support [*] JFFS2 summary support (EXPERIMENTAL) [*] JFFS2 XATTR support (EXPERIMENTAL) [*] JFFS2 POSIX Access Control Lists [*] JFFS2 Security Labels [*] Advanced compression options for JFFS2 [*] JFFS2 ZLIB compression support [*] JFFS2 RTIME compression support [ ] JFFS2 RUBIN compression support JFFS2 default compression mode (priority) 选上MTD支持Device Drivers —>Memory Technology Devices (MTD) —><*> Memory Technology Device (MTD) support [ ] Debugging <*> MTD concatenating support [*] MTD partitioning support < > RedBoot partition table parsing [*] Command line partition table parsing <*> ARM Firmware Suite partition parsing — User Moles And Translation Layers <*> Direct char device access to MTD devices <*> Caching block device access to MTD devices <*> FTL (Flash Translation Layer) support <*> NFTL (NAND Flash Translation Layer) support [ ] Write support for NFTL 2. 从ttp://www.linux-mtd.infradead.org/上下载mtd-utils-1.0.0.tar.gz 到宿主机。解压 tar xvzf mtd-utils-1.0.0.tar.gz进入mtd-utils-1.0.0目录修改Makefile进行编译。编译出mkfs.jffs2,nandwrite等工具3. 创建jffs2根文件系统:首先要有一个其他类型的根文件系统,制作和获取方式可以参见<< AT91sam9263EK使用NFS根文件系统>>比如可以:mount -t cramfs rootfs_lnxdemo.cramfs tmp/ -o looprootfs_lnxdemo.cramfs由atmel提供当然你可以自己制作,这里直接使用atmel的省去了制作的麻烦。tar czvf target.tar.gz tmp/生成jffs2根文件系统镜像:mkfs.jffs2 –l –s 0x400 –e 0x20000 –d tmp/ -o target.jffs24. 烧写jffs2根文件系统镜像到nand flash。烧写方法应该有多种常用的有2种(呵呵自己认为的):a.通过nand编程器烧写镜像文件到nand flash芯片然后再焊接到pcb.b.挂载nfs文件系统,使用nandwrite工具写镜像文件到mtd设备。第一种适合生产时使用,而我们测试时候比较适合使用第二种方式。挂载nfs文件系统:nandwrite –o /dev/mtd1 target.jffs2或者是在nfs 根文件系统下:mount –t jffs2 /dev/mtdblock1 /mntcd /mnttar xvzf ../target.tar.gzumount /mnt5. 配置内核启动参数如下:setenv bootargs mem=64M console=ttyS0 115200 root=/dev/mtdblock1 rootfstype=jffs2 rw6. 重新启动,呵呵内核使用jffs2文件系统自动挂载根文件系统到mtdblock1设备上。ls 测试一下vi testhello this is for test jffs2 filesystem.保存退出。Sync重新启动vi test内容依旧。Ok jffs2根文件系统引导成功。Linux联盟收集整理 ,转贴请标明原始链接
『捌』 写一个文件系统类 表示一个文件的路径(Java)
一定有你需要的。System.out.println(Thread.currentThread().getContextClassLoader().getResource("")); System.out.println(Test.class.getClassLoader().getResource("")); System.out.println(ClassLoader.getSystemResource("")); System.out.println(Test.class.getResource("")); System.out.println(Test.class.getResource("/")); System.out.println(new File("").getAbsolutePath()); System.out.println(System.getProperty("user.dir"));本地运行结果:file:/D:/snsoft80/bin/apptrade/file:/D:/snsoft80/bin/apptrade/file:/D:/snsoft80/bin/apptrade/file:/D:/snsoft80/bin/apptrade/hsy/file:/D:/snsoft80/bin/apptrade/D:\snsoft80\Workspc\apptradeD:\snsoft80\Workspc\apptrade
『玖』 如何用C写一个虚拟的磁盘和文件系统①
#include <stdio.h>#include <stdlib.h> //为了使用抄exit()int main(){char ch;FILE* fp;char fname[50]; //用于存放袭文件名printf("输入文件名:");scanf("%s",fname);fp=fopen(fname,"r"); //只供读取if(fp==NULL) //如果失败了{
『拾』 如何实现一个文件系统
摘要:本文目的是分析在Linux系统中如何实现新的文件系统。在介绍文件系统具体实现前先介绍文件系统的概念和作用,抽象出了文件系统概念模型。熟悉文件系统的内涵后,我们再近一步讨论Linux系统中和文件系统的特殊风格和具体文件系统在Linux中组成结构,为读者勾画出Linux中文件系统工作的全景图。最后,我们再通过Linux中最简单的Romfs作实例分析实现文件系统的普遍步骤。(我们假定读者已经对Linux文件系统初步了解)什么是文件系统首先要谈的概念就是什么是文件系统,它的作用到底是什么。文件系统的概念虽然许多人都认为是再清晰不过的了,但其实我们往往在谈论中或多或少地夸大或片缩小了它的实际概念(至少我时常混淆),或者说,有时借用了其它概念,有时说的又不够全面。比如在操作系统中,文件系统这个术语往往既被用来描述磁盘中的物理布局,比如有时我们说磁盘中的“文件系统”是EXT2或说把磁盘格式化成FAT32格式的“文件系统”等——这时所说的“文件系统”是指磁盘数据的物理布局格式;另外,文件系统也被用来描述内核中的逻辑文件结构,比如有时说的“文件系统”的接口或内核支持Ext2等“文件系统”——这时所说的文件系统都是内存中的数据组织结构而并非磁盘物理布局。还有些时候说“文件系统”负责管理用户读写文件——这时所说的“文件系统”往往描述操作系统中的“文件管理系统”,也就是文件子系统。虽然上面我们列举了混用文件系统的概念的几种情形,但是却也不能说上述说法就是错误的,因为文件系统概念本身就囊括众多概念,几乎可以说在操作系统中自内存管理、系统调度到I/O系统、设备驱动等各个部分都和文件系统联系密切,有些部分和文件系统甚至未必能明确划分——所以不能只知道文件系统是系统中数据的存储结构,一定要全面认识文件系统在操作系统中的角色,才能具备自己开发新文件系统的能力。为了澄清文件系统的概念,必须先来看看文件系统在操作系统中处于何种角色,分析文件系统概念的内含外延。所以我们先抛开Linux文件系统的实例,而来看看操作系统中文件系统的普遍体系结构,从而增强对文件系统的理论认识。下面以软件组成的结构图[1]的方式描述文件系统所涉及的内容。 我们针对各层做以简要分析:首先我们来分析最低层——设备驱动层,该层负责与外设——磁盘等——通讯。基于磁盘的文件系统都需要和存储设备打交道,而系统操作外设离不开驱动程序。所以内核对文件的最后操作行为就是调用设备驱动程序完成从主存(内存)到辅存(磁盘)的数据传输。文件系统相关的多数设备都属于块设备,常见的块设备驱动程序有磁盘驱动,光驱驱动等,之所以称它们为块设备,一个原因是它们读写数据都是成块进行的,但是更重要的原因是它们管理的数据能够被随机访问——不需要向字符设备那样必须顺序访问。设备驱动层的上一层是物理I/O层,该层主要作为计算机外部环境和系统的接口,负责系统和磁盘交换数据块。它要知道据块在磁盘中存储位置,也要知道文件数据块在内存缓冲中的位置,另外它不需要了解数据或文件的具体结构。可以看到这层最主要的工作是标识别磁盘扇区和内存缓冲块[2]之间的映射关系。再上层是基础I/O监督层,该层主要负责选择文件 I/O需要的设备,调度磁盘请求等工作,另外分配I/O缓冲和磁盘空间也在该层完成。由于块设备需要随机访问数据,而且对速度响应要求较高,所以操作系统不能向对字符设备那样简单、直接地发送读写请求,而必须对读写请求重新优化排序,以能节省磁盘寻址时间,另外也必须对请求提交采取异步调度(尤其写操作)的方式进行。总而言之,内核对必须管理块设备请求,而这项工作正是由该层负责的。倒数第二层是逻辑I/O层,该层允许用户和应用程序访问记录。它提供了通用的记录(record)I/O操作,同时还维护基本文件数据。由于为了方便用户操作和管理文件内容,文件内容往往被组织成记录形式,所以操作系统为操作文件记录提供了一个通用逻辑操作层。和用户最靠近的是访问方法层,该层提供了一个从用户空间到文件系统的标准接口,不同的访问方法反映了不同的文件结构,也反映了不同的访问数据和处理数据方法。这一层我们可以简单地理解为文件系统给用户提供的访问接口——不同的文件格式(如顺序存储格式、索引存储格式、索引顺序存储格式和哈希存储格式等)对应不同的文件访问方法。该层要负责将用户对文件结构的操作转化为对记录的操作。对比上面的层次图我们再来分析一下数据流的处理过程,加深对文件系统的理解。假如用户或应用程序操作文件(创建/删除),首先需要通过文件系统给用户空间提供的访问方法层进入文件系统,接着由使用逻辑I/O层对记录进行给定操作,然后记录将被转化为文件块,等待和磁盘交互。这里有两点需要考虑——第一,磁盘管理(包括再磁盘空闲区分配文件和组织空闲区);第二,调度块I/O请求——这些由基础I/O监督层的工作。再下来文件块被物理I/O层传递给磁盘驱动程序,最后磁盘驱动程序真正把数据写入具体的扇区。至此文件操作完毕。当然上面介绍的层次结构是理想情况下的理论抽象,实际文件系统并非一定要按照上面的层次或结构组织,它们往往简化或合并了某些层的功能(比如Linux文件系统因为所有文件都被看作字节流,所以不存在记录,也就没有必要实现逻辑I/O层,进而也不需要在记录相关的处理)。但是大体上都需要经过类似处理。如果从处理对象上和系统独立性上划分,文件系统体系结构可以被分为两大部分:——文件管理部分和操作系统I/O部分。文件管理系统负责操作内存中文件对象,并按文件的逻辑格式将对文件对象的操作转化成对文件块的操作;而操作系统I/O部分负责内存中的块与物理磁盘中的数据交换。数据表现形式再文件操作过程中也经历了几种变化:在用户访问文件系统看到的是字节序列,而在字节序列被写入磁盘时看到的是内存中文件块(在缓冲中),在最后将数据写入磁盘扇区时看到的是磁盘数据块[3]。本文所说的实现文件系统主要针对最开始讲到第二种情况——内核中的逻辑文件结构(但其它相关的文件管理系统和文件系统磁盘存储格式也必须了解),我们用数据处理流图来分析一下逻辑文件系统主要功能和在操作系统中所处的地位。 其中文件系统接口与物理布局管理是逻辑文件系统要负责的主要功能。文件系统接口为用户提供对文件系统的操作,比如open、close、read、write和访问控制等,同时也负责处理文件的逻辑结构。物理存储布局管理,如同虚拟内存地址转化为物理内存地址时,必须处理段页结构一样,逻辑文件结构必须转化到物理磁盘中,所以也要处理物理分区和扇区的实际存储位置,分配磁盘空间和内存中的缓冲也要在这里被处理。所以说要实现文件系统就必须提供上面提到的两种功能,缺一不可。在了解了文件系统的功能后,我们针对Linux操作系统分析具体文件系统如何工作,进而掌握实现一个文件系统需要的步骤。Linux 文件系统组成结构Linux 文件系统的结构除了我们上面所提到的概念结构外,最主要有两个特点,一个是文件系统抽象出了一个通用文件表示层——虚拟文件系统或称做VFS。另外一个重要特点是它的文件系统支持动态安装(或说挂载、登陆等),大多数文件系统都可以作为根文件系统的叶子接点被挂在到根文件目录树下的子目录上。另外Linux系统在文件读写的I/O操作上也采取了一些先进技术和策略。我们先从虚拟文件系统入手分析linux文件系统的特性,然后介绍有关文件系统的安装、注册和读写等概念。虚拟文件系统虚拟文件系统为用户空间程序提供了文件系统接口。系统中所有文件系统不但依赖VFS共存,而且也依靠VFS系统协同工作。通过虚拟文件系统我们可以利用标准的UNIX文件系统调用对不同介质上的不同文件系统进行读写操作[4]。虚拟文件系统的目的是为了屏蔽各种各样不同文件系统的相异操作形式,使得异构的文件系统可以在统一的形式下,以标准化的方法访问、操作。实现虚拟文件系统利用的主要思想是引入一个通用文件模型——该模型抽象出了文件系统的所有基本操作(该通用模型源于Unix风格的文件系统),比如读、写操作等。同时实际文件系统如果希望利用虚拟文件系统,既被虚拟文件系统支持,也必须将自身的诸如,“打开文件”、“读写文件”等操作行为以及“什么是文件”,“什么是目录”等概念“修饰”成虚拟文件系统所要求的(定义的)形式,这样才能够被虚拟文件系统支持和使用。我们可以借用面向对象的一些思想来理解虚拟文件系统,虚拟文件系统好比一个抽象类或接口,它定义(但不实现)了文件系统最常见的操作行为。而具体文件系统好比是具体类,它们是特定文件系统的实例。具体文件系统和虚拟文件系统的关系类似具体类继承抽象类或实现接口。而在用户看到或操作的都是抽象类或接口,但实际行为却发生在具体文件系统实例上。至于如何将对虚拟文件系统的操作转化到对具体文件系统的实例,就要通过注册具体文件系统到系统,然后再安装具体文件系统才能实现转化,这点可以想象成面向对象中的多态概念。我们个实举例来说明具体文件系统如何通过虚拟文件系统协同工作。例如:假设一个用户输入以下shell命令:$ cp /hda/test1 /removable/test2其中 /removable是MS-DOS磁盘的一个安装点,而 /hda 是一个标准的第二扩展文件系统( Ext2)的目录。cp命令不用了解test1或test2的具体文件系统,它所看到和操作的对象是VFS。cp首先要从ext3文件系统读出test1文件,然后写入MS-DOS文件系统中的test2。VFS会将找到ext3文件系统实例的读方法,对test1文件进行读取操作;然后找到MS-DOS(在Linux中称VFAT)文件系统实例的写方法,对test2文件进行写入操作。可以看到 VFS是读写操作的统一界面,只要具体文件系统符合VFS所要求的接口,那么就可以毫无障碍地透明通讯了。 Unix风格的文件系统虚拟文件系统的通用模型源于Unix风格的文件系统,所谓Unix风格是指Unix传统上文件系统传统上使用了四种和文件系统相关的抽象概念:文件(file)、目录项(dentry)、索引节点(inode)和安装点(mount point)。文件——在Unix中的文件都被看做是一有序字节串,它们都有一个方便用户或系统识别的名称。另外典型的文件操作有读、写、创建和删除等。目录项——不要和目录概念搞混淆,在Linux中目录被看作文件。而目录项是文件路径中的一部分。一个文件路径的例子是“/home/wolfman/foo”——根目录是/,目录home,wolfman和文件foo都是目录项。索引节点——Unix系统将文件的相关信息(如访问控制权限、大小、拥有者、创建时间等等信息),有时被称作文件的元数据(也就是说,数据的数据)被存储在一个单独的数据结构中,该结构被称为索引节点(inode)。安装点——在Unix中,文件系统被安装在一个特定的安装点上,所有的已安装文件系统都作为根文件系统树中的叶子出现在系统中。上述概念是Unix文件系统的逻辑数据结构,但相应的Unix文件系统(Ext2等)磁盘布局也实现了部分上述概念,比如文件信息(文件数据元)存储在磁盘块中的索引节点上。当文件被载如内存时,内核需要使用磁盘块中的索引点来装配内存中的索引接点。类似行为还有超级块信息等。对于非Unix风格文件系统,如FAT或NTFS,要想能被VFS支持,它们的文件系统代码必须提供这些概念的虚拟形式。比如,即使一个文件系统不支持索引节点,它也必须在内存中装配起索引节点结构体——如同本身固有一样。或者,如果一个文件系统将目录看作是一种特殊对象,那么要想使用VFS,必须将目录重新表示为文件形式。通常,这种转换需要在使用现场引入一些特殊处理,使得非Unix文件系统能够兼容Unix文件系统的使用规则和满足VFS的需求。通过这些处理,非Unix文件系统便可以和VFS一同工作了,是性能上多少会受一些影响[5]。这点很重要,我们实现自己文件系统时必须提供(模拟)Unix风格文件系统的抽象概念。Linux文件系统中使用的对象Linux文件系统的对象就是指一些数据结构体,之所以称它们是对象,是因为这些数据结构体不但包含了相关属性而且还包含了操作自身结构的函数指针,这种将数据和方法进行封装的思想和面向对象中对象概念一致,所以这里我们就称它们是对象。Linux文件系统使用大量对象,我们简要分析以下VFS相关的对象,和除此还有和进程相关的一些其它对象。VFS相关对象这里我们不展开讨论每个对象,仅仅是为了内容完整性,做作简要说明。VFS中包含有四个主要的对象类型,它们分别是:超级块对象,它代表特定的已安装文件系统。索引节点对象,它代表特定文件。目录项对象,它代表特定的目录项。文件对象,它代表和进程打开的文件。每个主要对象中都包含一个操作对象,这些操作对象描述了内核针对主要对象可以使用的方法。最主要的几种操作对象如下:super_operations对象,其中包括内核针对特定文件系统所能调用的方法,比如read_inode()和sync_fs()方法等。inode_operations对象,其中包括内核针对特定文件所能调用的方法,比如create()和link()方法等。dentry_operations对象,其中包括内核针对特定目录所能调用的方法,比如d_compare()和d_delete()方法等。file对象,其中包括,进程针对已打开文件所能调用的方法,比如read()和write()方法等。除了上述的四个主要对象外,VFS还包含了许多对象,比如每个注册文件系统都是由file_system_type对象表示——描述了文件系统及其能力(如比如ext3或XFS);另外每一个安装点也都利用vfsmount对象表示——包含了关于安装点的信息,如位置和安装标志等。其它VFS对象系统上的每一进程都有自己的打开文件,根文件系统,当前工作目录,安装点等等。另外还有几个数据结构体将VFS层和文件的进程紧密联系,它们分别是:file_struct 和fs_structfile_struct结构体由进程描述符中的files项指向。所有包含进程的信息和它的文件描述符都包含在其中。第二个和进程相关的结构体是fs_struct。该结构由进程描述符的fs项指向。它包含文件系统和进程相关的信息。每种结构体的详细信息不在这里说明了。缓存对象除了上述一些结构外,为了缩短文件操作响应时间,提高系统性能,Linux系统采用了许多缓存对象,例如目录缓存、页面缓存和缓冲缓存(已经归入了页面缓存),这里我们对缓存做简单介绍。页高速缓存(cache)是 Linux内核实现的一种主要磁盘缓存。其目的是减少磁盘的I/O操作,具体的讲是通过把磁盘中的数据缓存到物理内存中去,把对磁盘的I/O操作变为对物理内存的I/O操作。页高速缓存是由RAM中的物理页组成的,缓存中每一页都对应着磁盘中的多个块。每当内核开始执行一个页I/O操作时(通常是对普通文件中页大小的块进行磁盘操作),首先会检查需要的数据是否在高速缓存中,如果在,那么内核就直接使用高速缓存中的数据,从而避免了访问磁盘。但我们知道文件系统只能以每次访问数个块的形式进行操作。内核执行所有磁盘操作都必须根据块进行,一个块包含一个或多个磁盘扇区。为此,内核提供了一个专门结构来管理缓冲buffer_head。缓冲头[6]的目的是描述磁盘扇区和物理缓冲之间的映射关系和做I/O操作的容器。但是缓冲结构并非独立存在,而是被包含在页高速缓存中,而且一个页高速缓存可以包含多个缓冲。我们将在文件后面的文件读写部分看到数据如何被从磁盘扇区读入页高速缓存中的缓冲中的。文件系统的注册和安装使用文件系统前必须对文件系统进行注册和安装,下面分别对这两种行为做简要介绍。文件系统的注册VFS要想能将自己定义的接口映射到实际文件系统的专用方法上,必须能够让内核识别实际的文件系统,实际文件系统通过将代表自身属性的文件类型对象(file_system_type)注册(通过register_filesystem()函数)到内核,也就是挂到内核中的文件系统类型链表上,来达到使文件系统能被内核识别的目的。反过来内核也正是通过这条链表来跟踪系统所支持的各种文件系统的。我们简要分析一下注册步骤:struct file_system_type {const char *name; /*文件系统的名字*/int fs_flags; /*文件系统类型标志*//*下面的函数用来从磁盘中读取超级块*/struct super_block * (*read_super) (struct file_system_type *, int,const char *, void *);struct file_system_type * next; /*链表中下一个文件系统类型*/struct list_head fs_supers; /*超级块对象链表*/};其中最重要的一项是read_super()函数,它用来从磁盘上读取超级块,并且当文件系统被装载时,在内存中组装超级块对象。要实现一个文件系统首先需要实现的结构体便是file_system_type结构体。注册文件系统只能保证文件系统能被系统识别,但此刻文件系统尚不能使用,因为它还没有被安装到特定的安装点上。所以在使用文件系统前必须将文件系统安装到安装点上。文件系统被实际安装时,将在安装点创建一个vfsmount结构体。该结构体用代表文件系统的实例——换句话说,代表一个安装点。vfsmount结构被定义在<linux/mount.h>中,下面是具体结构―――――――――――――――――――――――――――――――――――――――struct vfsmount{struct list_head mnt_hash; /*哈希表*/struct vfsmount *mnt_parent; /*父文件系统*/struct dentry *mnt_mountpoint; /*安装点的目录项对象*/struct dentry *mnt_root; /*该文件系统的根目录项对象*/struct super_block *mnt_sb; /*该文件系统的超级块*/struct list_head mnt_mounts; /*子文件系统链表*/struct list_head mnt_child; /*和父文件系统相关的子文件系统*/atomic_t mnt_count; /*使用计数*/int mnt_flags; /*安装标志*/char *mnt_devname; /*设备文件名字*/struct list_head mnt_list; /*描述符链表*/};――――――――――――――――――――――――――――――――――――――文件系统如果仅仅注册,那么还不能被用户使用。要想使用它还必须将文件系统安装到特定的安装点后才能工作。下面我们接着介绍文件系统的安装[7]过程。安装过程用户在用户空间调用mount()命令——指定安装点、安装的设备、安装类型等——安装指定文件系统到指定目录。mount()系统调用在内核中的实现函数为sys_mount(),该函数调用的主要例程是do_mount(),它会取得安装点的目录项对象,然后调用do_add_mount()例程。do_add_mount()函数主要做的是首先使用do_kern_mount()函数创建一个安装点,再使用graft_tree()将安装点作为叶子与根目录树挂接起来。整个安装过程中最核心的函数就是do_kern_mount()了,为了创建一个新安装点(vfsmount),该函数需要做一下几件事情:l 1 检查安装设备的权利,只有root权限才有能力执行该操作。l 2 Get_fs_type()在文件链表中取得相应文件系统类型(注册时被填加到练表中)。l 3 Alloc_vfsmnt()调用slab分配器为vfsmount结构体分配存储空间,并把它的地址存放在mnt局部变量中。l 4 初始化mnt->mnt_devname域l 5 分配新的超级块并初始化它。do_kern_mount( )检查file_system_type描述符中的标志以决定如何进行如下操作:根据文件系统的标志位,选择相应的方法读取超级块(比如对Ext2,romfs这类文件系统调用get_sb_dev();对于这种没有实际设备的虚拟文件系统如 ramfs调用get_sb_nodev())——读取超级块最终要使用文件系统类型中的read_super方法。安装过程做的最主要工作是创建安装点对象,挂接给定文件系统到根文件系统的指定接点下,然后初始化超级快对象,从而获得文件系统基本信息和相关操作方法(比如读取系统中某个inode的方法)。总而言之,注册过程是告之内核给定文件系统存在于系统内;而安装是请求内核对给定文件系统进行支持,使文件系统真正可用。转载
未经允许不得转载:山九号 » 如何去写一个文件系统|如何制作Linux根文件系统