实验四 文件系统
一、实验目的
1、 熟悉Linux文件系统的文件和目录结构,掌握Linux文件系统的基本特征;
2、 模拟实现Linux文件系统的简单I/O流操作:备份文件。
二、实验内容
1、 浏览Linux系统根目录下的子目录,熟悉每个目录的文件和功能;
2、 设计程序模拟实现Linux文件系统的简单I/O流操作:备份文件。
三、实验原理
1、 Linux各种发行版的目录结构基本一致,各个目录简单介绍如下:
目录 |
描述 |
/ |
根目录 |
/bin |
做为基础系统所需要的最基础的命令就是放在这里。比如 ls、cp、mkdir等命令;功能和/usr/bin类似,这个目录中的文件都是可执行的,普通用户都可以使用的命令。 |
/boot |
Linux的内核及引导系统程序所需要的文件,比如 vmlinuz initrd.img 文件都位于这个目录中。在一般情况下,GRUB或LILO系统引导管理器也位于这个目录;启动装载文件存放位置,如kernels,initrd,grub。一般是一个独立的分区。 |
/dev |
一些必要的设备,声卡、磁盘等。还有如 /dev/null. /dev/console /dev/zero /dev/full 等。 |
/etc |
系统的配置文件存放地. 一些服务器的配置文件也在这里;比如用户帐号及密码配置文件; /etc/opt:/opt对应的配置文件 /etc/X11:Xwindows系统配置文件 /etc/xml:XML配置文件 …… |
/home |
用户工作目录,和个人配置文件,如个人环境变量等,所有的账号分配一个工作目录。一般是一个独立的分区。 |
/lib |
库文件存放地。bin和sbin需要的库文件。类似windows的DLL。 |
/media |
可拆卸的媒介挂载点,如CD-ROMs、移动硬盘、U盘,系统默认会挂载到这里来。 |
/mnt |
临时挂载文件系统。这个目录一般是用于存放挂载储存设备的挂载目录的,比如有cdrom 等目录。可以参看/etc/fstab的定义。 |
/opt |
可选的应用程序包。 |
/proc |
操作系统运行时,进程(正在运行中的程序)信息及内核信息(比如cpu、硬盘分区、内存信息等)存放在这里。/proc目录伪装的文件系统proc的挂载目录,proc并不是真正的文件系统,它的定义可以参见 /etc/fstab 。 |
/root |
Root用户的工作目录 |
/sbin |
和bin类似,是一些可执行文件,不过不是所有用户都需要的,一般是系统管理所需要使用得到的。 |
/tmp |
系统的临时文件,一般系统重启不会被保存。 |
/usr |
包含了系统用户工具和程序。 /usr/bin:非必须的普通用户可执行命令 /usr/include:标准头文件 /usr/lib:/usr/bin/ 和 /usr/sbin/的库文件 /usr/sbin:非必须的可执行文件 /usr/src:内核源码 /usr/X11R6:X Window System, Version 11, Release 6. |
/srv |
该目录存放一些服务启动之后需要提取的数据 |
2、 Linux的文件结构是单个的树状结构.可以用tree进行展示。文件操作命令见附录1,
3、 Linux文件系统:Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等。通过使用同一套文件 I/O 系统 调用即可对 Linux 中的任意文件进行操作而无需考虑其所在的具体文件系统格式;更进一步,对文件的 操作可以跨文件系统而执行。“一切皆是文件”是 Unix/Linux 的基本哲学之一。不仅普通的文件,目录、字符设备、块设备、 套接字等在 Unix/Linux 中都是以文件被对待;它们虽然类型不同,但是对其提供的却是同一套操作界面。
4、 虚拟文件系统(Virtual File System, 简称 VFS), 是 Linux 内核中的一个软件层,用于给用户空间的程序提供文件系统接口;同时,它也提供了内核中的一个 抽象功能,允许不同的文件系统共存。系统中所有的文件系统不但依赖 VFS 共存,而且也依靠 VFS 协同工作。为了能够支持各种实际文件系统,VFS 定义了所有文件系统都支持的基本的、概念上的接口和数据 结构;同时实际文件系统也提供 VFS 所期望的抽象接口和数据结构,将自身的诸如文件、目录等概念在形式 上与VFS的定义保持一致。换句话说,一个实际的文件系统想要被 Linux 支持,就必须提供一个符合VFS标准 的接口,才能与 VFS 协同工作。实际文件系统在统一的接口和数据结构下隐藏了具体的实现细节,所以在VFS 层和内核的其他部分看来,所有文件系统都是相同的。
5、利用C库函数fopen(), fread(), fwrite(), fclose() 来实现简单的文件备份, 即将一个文件的内容拷贝到另一个文件中去。
四、实验中用到的系统调用函数(包括实验原理中介绍的和自己采用的),自己采用的系统调用函数要按照指导书中的格式说明进行介绍。
1. open()函数
功能描述:用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。
所需头文件:#include <sys/types.h>,#include <sys/stat.h>,#include <fcntl.h>
函数原型:int open(const char *pathname,int flags,int perms)
参数:
pathname:被打开的文件名(可包括路径名如"dev/ttyS0")
flags:文件打开方式,
O_RDONLY:以只读方式打开文件
O_WRONLY:以只写方式打开文件
O_RDWR:以读写方式打开文件
O_CREAT:如果改文件不存在,就创建一个新的文件,并用第三个参数为其设置权限
O_EXCL:如果使用O_CREAT时文件存在,则返回错误消息。这一参数可测试文件是否存在。此时open是原子操作,防止多个进程同时创建同一个文件
O_NOCTTY:使用本参数时,若文件为终端,那么该终端不会成为调用open()的那个进程的控制终端
O_TRUNC:若文件已经存在,那么会删除文件中的全部原有数据,并且设置文件大小为0
O_APPEND:以添加方式打开文件,在打开文件的同时,文件指针指向文件的末尾,即将写入的数据添加到文件的末尾
O_NONBLOCK: 如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I/O操作设置非阻塞方式。
O_SYNC:使每次write都等到物理I/O操作完成。
O_RSYNC:read 等待所有写入同一区域的写操作完成后再进行
在open()函数中,falgs参数可以通过“|”组合构成,但前3个标准常量(O_RDONLY,O_WRONLY,和O_RDWR)不能互相组合。
perms:被打开文件的存取权限,可以用两种方法表示,可以用一组宏定义:S_I(R/W/X)(USR/GRP/OTH),其中R/W/X表示读写执行权限,
USR/GRP/OTH分别表示文件的所有者/文件所属组/其他用户,如S_IRUUR|S_IWUUR|S_IXUUR,(-rex------),也可用八进制800表示同样的权限
返回值:
成功:返回文件描述符
失败:返回-1
2. close()函数
功能描述:用于关闭一个被打开的的文件
所需头文件: #include <unistd.h>
函数原型:int close(int fd)
参数:fd文件描述符
函数返回值:0成功,-1出错
3. read()函数
功能描述: 从文件读取数据。
所需头文件: #include <unistd.h>
函数原型:ssize_t read(int fd, void *buf, size_t count);
参数:
fd: 将要读取数据的文件描述词。
buf:指缓冲区,即读取的数据会被放到这个缓冲区中去。
count: 表示调用一次read操作,应该读多少数量的字符。
返回值:返回所读取的字节数;0(读到EOF);-1(出错)。
4. write()函数
功能描述: 向文件写入数据。
所需头文件: #include <unistd.h>
函数原型:ssize_t write(int fd, void *buf, size_t count);
返回值:写入文件的字节数(成功);-1(出错)
功能:write 函数向 filedes 中写入 count 字节数据,数据来源为 buf 。返回值一般总是等于 count,否则就是出错了。常见的出错原因是磁盘空间满了或者超过了文件大小限制。
对于普通文件,写操作始于 cfo 。如果打开文件时使用了 O_APPEND,则每次写操作都将数据写入文件末尾。成功写入后,cfo 增加,增量为实际写入的字节数。
五、实验步骤
1、阅读实验原理,认真学习关于根目录下的文件和功能。
2、使用C库函数实现备份文件:
(1)使用fopen()函数以只读方式打开想要备份的源文件source和以只写方式打开想要写入内容的目标文件target。
(2)使用fread()循环读取源文件一个缓冲区大小的内容,使用fwrite()将内容写入目标文件。
(3)读取与写入完毕,使用fclose()关闭读写文件流。
3、使用系统调用函数实现备份文件:
(1)使用open()系统调用函数以只读方式打开想要备份的源文件source和以只写方式打开想要写入内容的目标文件target。
(2)使用read()循环读取源文件一个缓冲区大小的内容,使用write()将内容写入目标文件。
(3)读取与写入完毕,使用close()关闭读写文件流。
六、实验结果分析(截屏的实验结果,与实验结果对应的实验分析)
使用c库函数实现备份文件:
初始文件source.dat:
target.dat:
编译并执行程序:
检查target.dat文件:
备份文件成功!
使用系统调用函数实现备份文件的效果如出一辙,在此就不附上图。
六、思考题
1、使用系统调用函数open(),read(),write(),close()实现简单文件备份的原理是什么?
系统调用是操作系统的一个接口,通过调用函数在内核地址空间执行,屏蔽了对硬件资源的操作细节。
open():
1)根据给定的文件名查找文件目录。如果找到该文件,则把相应的文件控制块调入内存的活动文件控制块区。
2)检测打开文件的合法性。如果用户指定的打开文件之后的操作与文件创建时规定的存取权限不符,则不能打开该文件,返回不成功标志。如果权限相符,则建立文件系统内部控制结构之间的通路联系,返回相应的文件描述字fd。
read():
1)根据打开文件时得到的文件描述字找到相应的文件控制块,确定读操作的合法性,设置工作单源初值。
2)把文件的逻辑块号转换为物理块号,申请缓冲区。
3)启动磁盘I/O操作,把盘块中的信息读入缓冲区,然后传送到指定的内存区,同时修改读指针,供后面读写定位之用。
Write():
1)根据文件描述字找到文件控制块,确认写操作的合法性,置工作单元初值。
2)由当前写指针的值得到逻辑块号,然后申请空闲物理盘块,申请缓冲区。
3)把指定用户内存区中的信息写入缓冲区,然后启动磁盘进行I/O操作,将缓冲区中信息写到相应盘上。
4)修改写指针的值。
Close():
如果该文件的最后一块尚未写到盘上,则强行写盘,不管该块是否为满块。系统根据文件描述字依次找到相应的内部控制结构,切断彼此间联系,释放相应的控制表格。
2、使用C库函数fopen(), fread(), fwrite(), fclose() 来实现简单文件备份的原理是什么?
使用C库函数fopen()建立文件数据流,返回一个FILE结构指针,fread(),fwrite()运用FILE指针对文件流进行操作,fclose()关闭刚才建立的流。使用C库函数实现文件备份实际上是建立两个文件流,先读取其中一个流的数据,再将读取出来的数据写入另一个流中。
3、上述二者的区别在哪里?
C库调用是调用函数库中的一个程序,在用户态下进行,而系统调用是调用系统内核的服务,在内核模式中运行。库函数调用与用户程序相关联,但系统调用是操作系统的一个进入点。实际上C库函数调用可以理解为对系统调用的一层封装,系统调用的目的明确,执行高效简洁,C库函数就是在此基础上添加一些其他的操作,比如构造成流态等提供调用。
八、实验数据及源代码(学生必须提交自己设计的程序源代码,并有注释,源代码电子版也一并提交),包括思考题的程序。
程序完整代码请转至个人GitHub仓库(如果喜欢,麻烦点个star✨谢谢~)
结语:随笔仅供参考,千万不要照抄哦,我相信你可以的~!