第七章 文件操作
7.1文件操作级别
文件操作分为五个级别
(1)硬件级别:
- fdisk:将硬盘、U盘或SDC盘分区。
- mkfs:格式化磁盘分区,为系统做好准备。
- fsck:检查和维修系统。
- 碎片整理:压缩文件系统中的文件。
(2)操作系统内核中的文件系统函数:
每个操作系统内核均可为基本文件操作提供支持。k表示内核函数。
kumount(),kumount()
(mount/umount file systems)
kmkdir(),krmdir()
(make/remove directory)
kchair(),kgetCwd()
(change directory,get CWD pathname)
klink(),kunlink()
(hard link/unlink files)
kchmod(),kchown(),kutime() (change r|w|x permissions,owner,time)
kcreat(),kopen()
(create/open file for R,W,RW,APPEND)
kread(),kwrite() (read/write opened files)
klseek(),kclose()
(Lseek/close file descriptors)
keymlink(),kreadlink ()
(create/read symbolic 1ink files)
kstat(),kfstat(),klatat() (get file status/information)
kopendir(),kreaddir()
(open/read directories)
(3)系统调用
用户模式程序使用系统调用来访问内核函数
(4)1/O库函数:
存在内置的标准1/O函数,提高了运行效率。 I/O库函数包括:
FILE mode I/O:fopen(),fread(); fwrite(),fseek(),fclose(),fflush()
char mode I/O:getc(), getchar(),ugetc(); putc(),putchar()
line mode I/O:gets(),fgets(); puts(),fputs()
formatted I/O:scanf(),fscanf(),sscanf(); printf(),fprintf(),sprintf()
(5)用户命令:
用户可以使用Unix/Linux命令来执行文件操作,而不是编写程序。
mkdir, rmdir,cd,pwd,ls,link,unlink,rm,cat,cp,mv chmod,etc
(6)sh脚本
7.2 文件I/O操作
文件操作示意图
用户模式下的操作:
(1)用户模式下的程序执行操作:
FILE *p = fopen("file", "r"); or FILE *p = fopen( "file", "w");
可以打开一个读/写文件流。
(2)fopen()在用户(heap)空间中创建一个FILE结构体,包含一个文件描述符fd、一个fbuf[BLKSIZE]和一些控制变量。它会向内核中的kopen()发出一个fd = open("file",flags=READ or WRITE)系统调用,构建一个OpenTable来表示打开文件示例。OpenTable的mptr指向内存中的文件INODE。对于非特殊文件,INODE 的i_block数组指向存储设备上的数据块。成功后,fp会指向FILE结构体,其中fd是open()系统调用返回的文件描述符。
(3)fread(ubuf, size,nitem, fp)
:将nitem个size字节读取到ubuf上,通过:
-
将数据从FILE结构体的fbuf上复制到ubuf上,若数据足够、则返回。
-
如果fbuf没有更多数据,则执行(a)。
(4a)发出read(fd, fbuf, BLKSIZE)
系统调用,将文件数据块从内核读取到fbuf上,然后将数据复制到ubuf上,直到数据足够或者文件无更多数据可复制。
(4b)fwrite(ubuf, size, nitem, fp)
:将数据从ubuf复制到 fbuf。
-
若(fbuf有空间):将数据复制到fbuf上,并返回。
-
若(fbuf已满):发出 write(fd, fbuf, BLKSIZE)系统调用,将数据块写入内核,然后再次写入fbuf。
(5)内核中的文件系统函数:
假设非特殊文件的read(fd, fbuf[], BLKSIZE)
系统调用。
(6)在read()系统调用中,fd是一个打开的文件描述符,它是运行进程的fd数组中的一个索引,指向一个表示打开文件的 OpenTable。
(7)OpenTable包含文件的打开模式、一个指向内存中文件 INODE的指针和读/写文件的当前字节偏移量。从OpenTable的偏移量,
-
计算逻辑块编号lbk。
-
通过 INODE.i_block[]数组将逻辑块编号转换为物理块编号blk 。
(8)Minode包含文件的内存INODE。EMODE.i_block[]数组包含指向物理磁盘块的指针。文件系统可使用物理块编号从磁盘块直接读取数据或将数据直接写入磁盘块,但将会导致过多的物理磁盘I/O。
(9)为提高磁盘VO效率,操作系统内核通常会使用一组I/O缓冲区作为高速缓存,以减少物理I/O的数量。
(9a)对于read(fd,buf,BLKSIZE)系统调用,要确定所需的(dev,blk)编号,然后查询I/O缓冲区高速缓存
(9b)对于write(fd,buf,BLKSIZE)系统调用,要确定所需的(dev,blk)编号,然后查询I/O缓冲区高速缓存
(10)设备I/O:I/O缓冲区上的物理I/O最终会仔细检查设备驱动程序,设备驱动程序由上半部分的start_io()和下半部分的磁盘中断处理程序组成。
7.3 低级别文件操作
7.3.1 分区
一个块存储设备,如硬盘、U盘、SD卡等,可以分为几个逻辑单元,称为分区。各分区均可以格式化为特定的文件系统,也可以安装在不同的操作系统上,分区表位于第一个扇区的字节偏移446处,该扇区称为设备的主引导记录(MBR)。
7.3.2 格式化分区
fdisk只是将一个存储设备划分为多个分区。每个分区都有特定的文件系统类型,但是分区还不能使用。为了存储文件,必须先为特定的文件系统准备好分区。该操作习惯上称为格式化磁盘或磁盘分区。在Linux中,它被称为mkfs,表示Make文件系统。Linux支持多种不同类型的文件系统。
7.4 EXT2文件系统简介
- Block#0:
引导块,文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。 - Block#1:
超级块(在硬盘分区中字节偏移量为1024)。用于容纳关于整个文件系统的信息。 - Block#2:
块组描述符(硬盘上的s_first_data_blocks-1)EXT2将磁盘块分成几个组,每个组有8192个块(硬盘上的大小为32K) - Block #8:块位图(Bmap)
用来表示某种项的位序列。0表示对应项处于FREE状态,1表示处于IN_USE状态。1个软盘有1440个块,但Block#0未被文件系统使用,所以对应位图只有1439个有效位,无效位视作IN_USE处理,设置为1. - Block #9:索引节点位图(Imap)
一个索引节点就是用来代表一个文件的数据结构。EXT2文件系统是使用有限数量的索引节点创建的。各索引节点的状态用B9 中 Imap中的一个位表示。在EXT2 FS 中,前10个索引节点是预留的。所以,空EXT2FS的Imap 以10个1开头,然后是0。无效位再次设置为1。 - Block #10:索引(开始)节点块(bg_inode_table)
每个文件都用一个128字节(EXT4中的是256字节)的独特索引节点结构体表示。
i_block[15]数组包含指向文件磁盘块的指针,有
- 直接块:i block[0] 至 i-block[11],指向直接磁盘块。
- 间接块:i-block[12]指向一个包含256个块编号(对于1KB BLKSIZE)的磁盘块每个块编号指向一个磁盘块。
- 双重间接块:i block[13]指向一个指向 256 个块的块,每个块指向 256 个磁盘块·三重间接块:i block[14] 是三重间接块。对于“小型”EXT2 文件系统,我们可以忽略它。
第八章 使用系统调用进行文件操作
8.1系统调用
在操作系统中,进程以两种不同的模式运行,即内核模式和用户模式,简称Kmode和 Umode。在Umode中,进程的权限非常有限。它不能执行任何需要特殊权限的操作。特殊权限的操作必须在Kmode下执行。系统调用(简称syscall)是一种允许进程进入Kmode以执行Umode不允许操作的机制。复刻子进程、修改执行映像,甚至是终止等操作都必须在内核中执行。
8.3 使用系统调用进行文件操作
系统调用必须由程序发出。它们的用法就像普通函数调用一样。每个系统调用都是一个库函数,它汇集系统调用参数,并最终向操作系统内核发出一个系统调用。
int syscall(int a,int b,int c,int d);
第一个参数a是系统调用编号,b、c、d是对应内核函数的参数。内核的系统调用处理程序根据系统调用编号将调用路由到一个相应的内核函数。当进程结束执行内核函数时,会返回到用户模式,并得到所需的结果。返回值≥0表示成功,-1表示失败。如果失败,errno变量(在errno.h中)会记录错误编号,它们会被映射到描述错误原因的字符串。
简单的系统调用
access | 检查对某个文件的权限 | int access(char *pathname, int mode); |
---|---|---|
chdir | 更改目录 | int chdir(const char *path); |
chmod | 更改某个文件的权限 | int chmod(char *path, mode_t mode); |
chown | 更改文件所有人 | int chown(char *name, int uid, int gid); |
chroot | 将(逻辑)根目录更改为路径名 | int chroot(char *pathname); |
getcwd | 获取CWD的绝对路径名 | char *getcwd(char *buf, int size); |
mkdir | 创建目录 | int mkdir(char *pathname, mode_t mode); |
rmdir | 移除目录(必须为空) | int rmdir(char *pathname); |
link | 将新文件名硬链接到旧文件名 | int link(char *oldpath, char *newpath); |
unlink | 减少文件的链接数;如果链接数达到0,则删除文件 | int unlink(char *pathname); |
symlink | 为文件创建一个符号链接 | int symlink(char *oldpath, char *newpath); |
rename | 更改文件名称 | int rename(char *oldpath,char *newpath); |
utime | 更改文件的访问和修改时间 | int utime(char *pathname, struct utimebuf *time) |
mount | 将文件系统添加到挂载点目录上 | int mount(char *specialfile,char *mountDir); |
mknod | 创建特殊文件 |
8.5 链接文件
在Unix/Linux中,每个文件都有一个路径名。但是,Unix/Linux允许使用不同的路径名来表示同一个文件。这些文件叫做LINK(链接)文件。有两种类型的链接,即是硬链接和软链接或符号链接。
硬链接文件
硬链接:命令
ln oldpath newpath
创建从newpath到oldpath的硬链接。对应的系统调用为:
link(char *oldpath, char *newpath)
符号链接文件
软链接:命令
ln -s oldpath newpath #ln command with the -s flag
创建从newpath到oldpath的软链接或符号链接。对应的系统调用是:
symlink(char *oldpath, char *newpath)
向chatgpt提问
- 1
- 2