7.1文件操作级别
文件操作分为五个级别:
(1)硬件级别:
·fdisk:将硬件、U盘或SDC盘分区。
·mkfs:格式化磁盘分区,为系统做好准备。
·fsck:检查和维修系统。
·碎片整理:压缩文件系统中的文件
(2)操作系统中的文件系统函数:每个操作系统内核均可为基本文件操作提供支持。
(3)系统调用:用户模式程序使用系统调用来访问内核函数。
(4)I/O库函数:
(5)用户命令:mkdir、cd、rm、touch、cat等命令,用户使用命令进行文件操作
(6)sh脚本
2.文件I/O库操作
(1) 用户模式下的程序执行操作
FILE *fp = fopen("file", "r");or FILE *fp = fopen("file", "w");
可以打开一个读/写文件流。
(2) fopen()在用户( heap)空间中创建一个FILE结构体,包含一个文件描述符fd、一个fbuf [BLKSIZE]和一些控制变量。
(3) fread(ubuf, size, nitem, fp):将nitem个size字节读取到ubuf上,通过:·将数据从FILE结构体的fbuf上复制到ubuf上,若数据足够,则返回。·如果 fbuf没有更多数据,则执行(4a)。
(4a)发出read(fd,fbuf, BLKSIZE)系统调用,将文件数据块从内核读取到 fbuf上,然后将数据复制到ubuf上,直到数据足够或者文件无更多数据可复制。
(4b) fwrite(ubuf, size, nitem, fp):将数据从ubuf复制到fbuf。·若(fbuf有空间):将数据复制到fbuf 上,并返回。若(fbuf已满):发出 write(fd,fbuf,BLKSIZE)系统调用,将数据块写入内核,然后再次写入fbuf。这样,fread()/fwrite()会向内核发出read(/write)系统调用,但仅在必要时发出,而且它们会以块集大小来传输数据,提高效率。同样,其他库I/O函数,如 fgetc/fputc、fgetsllputs、fscanf/fprintf等也可以在用户空间内的FILE结构体中对fbuf进行操作。
(5)内核中的文件操作:假设非特殊文件的 read(fd, fbuf[ ], BLKSIZE)系统调用。
(6)在read()系统调用中,fd是一个打开的文件描述符,它是运行进程的f数组中的一个索引,指向一个表示打开文件的OpenTable。
(7)OpenTable包含文件的打开模式、一个指向内存中文件INODE的指针和读/写文件的当前字节偏移量。从 OpenTable的偏移量,计算逻辑块编号1bk。通过INODE.i_block[ ]数组将逻辑块编号转换为物理块编号blk。
(8) Minode包含文件的内存 INODE。EMODE.i_block[ ]数组包含指向物理磁盘块的指针。文件系统可使用物理块编号从磁盘块直接读取数据或将数据直接写入磁盘块,但将会导致过多的物理磁盘I/O。
(9)为提高磁盘IO效率,操作系统内核通常会使用一组IO缓冲区作为高速缓存,以减少物理I/O的数量。磁盘I/O缓冲区管理将在第12章中讨论。
4.EXT2文件系统简介
多年来,Linux一直使用EXT2(Card等1995;EXT2 2001)作为默认文件系统。EXT3(EXT3 2015)是EXT2的扩展。EXT3中增加的主要内容是一个日认文件系统。,的日志中。日志可在文件系统崩溃时更快从错误中恢复。EXT3的最新扩展是EXT4(Cao等2007)。Ex4的主要变化是盘块的分配。在EXT4中,块编号是48位。EXT4不是分配不连续的磁盘块,而是分配连续的磁盘块区,称为区段。
EXT2文件系统数据结构
Block#0:引导块 文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。
超级块
Block#1:超级块(在硬盘分区中字节偏移量为1024)B1是超级块,用于容纳关于整个文件系统的信息。下文说明了超级块结构中的一些重要字段。
struct ext2_super_block {
u32 s_inodes_count; // Inodes count
u32 s_blocks_count; // Blocks count
u32 s_r_blocks_count; // Reserved blocks count
u32 s_free_blocks_count; // Free blocks count
u32 s_free_inodes_count; // Free inodes count
u32 s_first_data_block; // First Data Block
u32 s_log_block_size; // Block size
u32 s_log_cluster_size; // Allocation cluster size
u32 s_blocks_per_group; // # Blocks per group
u32 s_clusters_per_group; // # Fragments per group
u32 s_inodes_per_group; // # Inodes per group
u32 s_mtime; // Mount time
u32 s_wtime; // Write time
u32 s_mnt_count; // Mount count
u16 s_max_mnt_count; // Maximal mount count
u16 s_magic; // Magic signature
// more non-essential fields
u16 s_inode_size; // size of inode structure
};
块组描述符
Block#2:块组描述符块(硬盘上的s_first_data_blocks-1)EXT2将磁盘块分成几个组。每个组有8192个块(硬盘上的大小为32K)。每组用一个块组描述符结构体描述。
struct ext2_ group_ desc (
u32
bg_ block_ bi tmap; // Bmap block number
u32 bg inode_ bi tmap; //Imap b1ock number
u32 bg inode_ table; // Indes begin block number
u16 bg_ free_ blocks_ count ; // THESE are OBVIOUS
u16 bg_ free_ inodes_ count ;
u16 bg_ used_ dirs_ count;
u16 bg_ pad; //ignore these
u32 bg_ reserved[3] ;
};
位图
Block#8:块位图(Bmap)(bg_block_bitmap)位图用来表示某种项的位序列,例如,磁盘块或索引节点。位图用于分配和回收项。在位图中,0位表示对应项处于FREE状态,1位表示对应项处于IN_USE状态。一个软盘有1440块。
Block#9:索引节点位图(Imap)(bg_inode_bitmap)一个索引节点就是用来代表一个文件的数据结构。EXT2文件系统是使用有限数量的索引节点创建的。各索引节点的状态用B9中Imap中的一个位表示。在EXT2 FS中,前10个索引节点是预留的。
索引节点
Block#10:索引(开始)节点块(bg_inode_table)每个文件都用一个128字节(EXT4中的是256字节)的独特索引节点结构体表示。
struct ext2_ inode {
u16 i_ mode;// 16 bits - ttttlugsIrwxJrwxIrwxl
u16 i_ uid;//owner uid
u32 i_ size;//file size in bytes
u32 i_ atime;//time fields in seconds
u32 i_ ctime;// since 00:00:00,1-1-1970
u32 i_ mtime;
u32 i_ dtime;
u16 i_ gid;// group ID
u16 i_ 1 inks_ count;// hard-link count
u32 i_ blocks;// number of 512-byte sectors
u32 i_ flags;//IGNORE
u32 i_ reserved1 ;//IGNORE
u32 i_ b1ock[15] ;//See details below
u32 i_ pad[7] ;//for inode size = 128 bytes
}
直接块:i_block[0]至i_block[11]指向直接磁块盘
间接块:i_block[12]指向一个包含256个块编号的磁盘块,每个块编- - 号指向一个磁盘块
双重间接块:i_block[13]指向一个指向256个块的块,每个块指向256个磁盘块
三重间接块:i_block[14]对于小型EXT2文件可忽略
目录条目
EXT2目录条目;目录包含dir_entry_2结构,即:
struct ext2_dir_entry_2 {
u32 inode;
u16 rec_len;
u8 name_len;
u8 file_type;
char name[EXT2_NAME_LEN];
};
EXT2文件系统
文件系统中存储的最小单元是块(block),一个块的大小是在格式化时确定的。启动块(Boot Block)的大小为1KB,由PC标准规定,用来存储磁盘分区信息和启动信息,任何文件系统都不能修改启动块。
启动块之后才是ext2文件系统的开始,ext2文件系统将整个分区划分成若干个同样大小的块组(Block Group)。
在整体的规划当中,文件系统最前面有一个启动扇区(boot sector),这个启动扇区可以安装启动管理程序, 这是个非常重要的设计,因为如此一来我们就能够将不同的启动管理程序安装到个别的文件系统最前端,而不用覆盖整颗硬盘唯一的 MBR 。
每个块组的组成:
超级块(Super Block)描述整个分区的文件系统信息,如inode/block的大小、总量、使用量、剩余量,以及文件系统的格式与相关信息。超级块在每个块组的开头都有一份拷贝(第一个块组必须有,后面的块组可以没有)。 为了保证文件系统在磁盘部分扇区出现物理问题的情况下还能正常工作,就必须保证文件系统的super block信息在这种情况下也能正常访问。所以一个文件系统的super block会在多个block group中进行备份,这些super block区域的数据保持一致。
超级块记录的信息有:
1、block 与 inode 的总量(分区内所有Block Group的block和inode总量);
2、未使用与已使用的 inode / block 数量;
3、block 与 inode 的大小 (block 为 1, 2, 4K,inode 为 128 bytes);
4、filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘 (fsck) 的时间等文件系统的相关信息;
5、一个 valid bit 数值,若此文件系统已被挂载,则 valid bit 为 0 ,若未被挂载,则 valid bit 为 1 。
每个区段与 superblock 的信息都可以使用 dumpe2fs 这个命令查询
块组描述符表(GDT,Group Descriptor Table)由很多块组描述符组成,整个分区分成多个块组就对应有多少个块组描述符。
每个块组描述符存储一个块组的描述信息,如在这个块组中从哪里开始是inode Table,从哪里开始是Data Blocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有一份拷贝。
块位图(Block Bitmap)用来描述整个块组中哪些块已用哪些块空闲。块位图本身占一个块,其中的每个bit代表本块组的一个block,这个bit为1代表该块已用,为0表示空闲可用。假设格式化时block大小为1KB,这样大小的一个块位图就可以表示1024*8个块的占用情况,因此一个块组最多可以有10248个块。
inode位图(inode Bitmap)和块位图类似,本身占一个块,其中每个bit表示一个inode是否空闲可用。 Inode bitmap的作用是记录block group中Inode区域的使用情况,Ext文件系统中一个block group中可以有16384个Inode,代表着这个Ext文件系统中一个block group最多可以描述16384个文件。
inode表(inode Table)由一个块组中的所有inode组成。一个文件除了数据需要存储之外,一些描述信息也需要存储,如文件类型,权限,文件大小,创建、修改、访问时间等,这些信息存在inode中而不是数据块中。inode表占多少个块在格式化时就要写入块组描述符中。
在Ext2/Ext3文件系统中,每个文件在磁盘上的位置都由文件系统block group中的一个Inode指针进行索引,Inode将会把具体的位置指向一些真正记录文件数据的block块,需要注意的是这些block可能和Inode同属于一个block group也可能分属于不同的block group。我们把文件系统上这些真实记录文件数据的block称为Data blocks。
数据块(Data Block)是用来放置文件内容数据的地方。
一、系统调用手册
在Unix以及大多数版本的Linux中,在线手册页保存在/usr/man/目录中。man2子目录中列出了所有系统调用手册页。sh命令man2 NAME显示了系统调用名称的手册页。
二、系统调用进行文件操作
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 *patiiname);
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 uniink(char *pathname);
symlink:为文件创建一个符号链接
int symliak(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)/
umount:分离挂载的文件系统
int umount(char *dir);
mknod:创建特殊文件
int mknod(char *path, int mode, int device);
常用系统调用
文件操作读写:
函数 |
作用 |
fcntl |
文件控制 |
open |
打开文件 |
creat |
创建新文件 |
close |
关闭文件描述字 |
read |
读文件 |
write |
写文件 |
readv |
从文件读入数据到缓冲数组中 |
writev |
将缓冲数组里的数据写入文件 |
pread |
对文件随机读 |
pwrite |
对文件随机写 |
lseek |
移动文件指针 |
_llseek |
在64位地址空间里移动文件指针 |
dup |
复制已打开的文件描述字 |
dup2 |
按指定条件复制文件描述字 |
flock |
文件加/解锁 |
poll |
I/O多路转换 |
truncate |
截断文件 |
ftruncate |
参见truncate |
umask |
设置文件权限掩码 |
fsync |
把文件在内存中的部分写回磁盘 |
文件系统操作:
函数 |
作用 |
stat |
取文件状态信息 |
lstat |
参见stat |
fstat |
参见stat |
statfs |
取文件系统信息 |
fstatfs |
参见statfs |
readdir |
读取目录项 |
getdents |
读取目录项 |
mkdir |
创建目录 |
mknod |
创建索引节点 |
rmdir |
删除目录 |
rename |
文件改名 |
link |
创建链接 |
symlink |
创建符号链接 |
unlink |
删除链接 |
readlink |
读符号链接的值 |
mount |
安装文件系统 |
umount |
卸下文件系统 |
ustat |
取文件系统信息 |
utime |
改变文件的访问修改时间 |
utimes |
参见utime |
quotactl |
控制磁盘配额 |