第七、八章学习笔记
文件操作
文件操作级别
文件操作分为五个级别,按照从高到低的顺序如下:
(1)硬件级别:硬件级别的文件操作包括:
-
fdisk:将硬盘、U盘或SDC盘分区。
-
mkfs:格式化磁盘分区、为系统做好准备。
-
fsck:检查和维修系统。
-
碎片整理:压缩文件系统中的文件。
大多数是针对系统的实用程序。普通用户永远可能都不需要它们,但是它们是创建和维护系统不可或缺的工具。
(2)操作系统内核中的文件系统函数:每个操作系统内核均可为基本文件操作提供支持。
每个操作系统均可为基本文件操作提供支持。
•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)系统调用:用户模式程序使用系统调用来访问内核函数。
以下程序可读取文件的第二个1024字节
#include <fcnt1.h>
int main(int argc,char *argv[]){//run as a.out filename
int fd,n;
char buf[1024];
if((fd=open(argv[1],O_RDONLY))<0)//if open() fails
exit(1);
lseek(fd,1024,SEEK_SET);//lseek to byte 1024
n = read(fd,buf,1024);//try to read 1024 bytes.
close(fd);
}
每个C语言库函数都会发出一个系统调用,使进程进程进入内核模式来执行相应的内核函数,例如open可以进入kopen()。进程结束执行内核函数的时候,会返回到用户模式,并得到所需结果。在用户模式和内核模式之间切换需要大量的操作(和时间)。内核与用户空间之间的数据传输成本昂贵。
(4)I/O库函数:系统调用何以让用户读/写多个数据块,这些数据块只是一系列字节。
为方便用户使用,C语言提供了一些列标准的I/O函数,同时提高了运行效率。除了读/写内存位置的sscanf()/sprintf()函数之外,所有其他I/O库函数都建立在系统调用之上,也就是说,他们最终会通过系统内核发出实际数据传输的系统调用。
(5)用户命令:用户可以使用Linux/Unix命令来执行文件操作。
可参考链接: Linux命令大全----常用文件操作命令_Evankaka的专栏-CSDN博客
(6)sh脚本:虽然比系统调用方便得多,但是必须要手动输入命令,如果使用的是GUI,必须拖放文件图标和点击指向设备来输入,操作烦琐且耗时。
文件I/O操作
在图7.1中,双线上半部分表示内核空间,下半部分为进程的用户空间。
低级别文件操作
分区
一个块存储设备,如硬盘、U盘、SD卡等,可以分为几个逻辑单元,称为分区。各分区均可以格式化为特定的文件系统,也可以安装在不同的操作系统上。分区表位于第一个扇区的字节偏移446(0x1BE)处,该扇区称为设备的主引导记录(MBR)
扩展类型分区(类型编号=5),每个扩展类型分区的第一个扇区是一个本地MBR。每个本地MBR在字节偏移量0x1BE处也有一个分区表,只包含两个条目。
-
第一个条目定义了扩展分区的起始扇区和大小;
-
第二个条目定义指向下一个本地MBR。所有本地MBR的扇区编号都与P4的起始扇区有关。
根据书上所给代码,创建名为mydisk的虚拟磁盘映像文件。
fdisk mydisk
DOS (MBR) a 开关 可启动 标志 b 编辑嵌套的 BSD 磁盘标签 c 开关 dos 兼容性标志
常规 d 删除分区 F 列出未分区的空闲区 l 列出已知分区类型 n 添加新分区 p 打印分区表 t 更改分区类型 v 检查分区表 i 打印某个分区的相关信息
杂项 m 打印此菜单 u 更改 显示/记录 单位 x 更多功能(仅限专业人员)
脚本 I 从 sfdisk 脚本文件加载磁盘布局 O 将磁盘布局转储为 sfdisk 脚本文件
保存并退出 w 将分区表写入磁盘并退出 q 退出而不保存更改
新建空磁盘标签 g 新建一份 GPT 分区表 G 新建一份空 GPT (IRIX) 分区表 o 新建一份的空 DOS 分区表 s 新建一份空 Sun 分区表
格式化分区
fdisk只是将一个存储设备划分为多个分区。每个分区都有特定的文件系统类型,但是分区还不能使用。存储文件必须先为特定的文件系统准备好分区。称为格式化磁盘或磁盘分区。
EXT2文件系统
多年来,Linux一直使用EXT2作为默认文件系统。EXT3中增加的主要内容是一个日志文件,记录系统更改记录。日志可在文件系统崩溃时更快从错误中恢复。
-
Block#0: 引导块,文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。
-
Block#1: 超级块(在硬盘分区中字节偏移量为1024)。用于容纳关于整个文件系统的信息。 超级块中一些重要字段
struct et2_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; /* Al1ocation 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 * /
u32s_mtime; /* Mount time * /
u32s_wtime; /* write time */
u16s_mnt_count; /* Mount coune* /
s16 s_max_ntcount; /* Maximal mount count */
u16 B_magic; /* Magic signature */
//more non-essential fields
u16 s_inode_size; /* size of inode structure*/
}
s_first_data_block:0表示4KB块大小,1表示1KB块大小。它用于确定块组描述符的起始块,即s_first_data_block +1。 s_log_block_size确定文件块大小,为1KB*(2**s_log_block_size),例如0表示 1KB块大小,1表示2KB块大小,2表示4KB块大小,等等。最常用的块大小是用于小文件系统的1KB和用于大文件系统的4KB。 s_mnt_count:已挂载文件系统的次数。当挂载计数达到max_mount_count时,fsck会话将*检查文件系统的一致性。 s_magic是标识文件系统类型的幻数。EXT2/3/4文件系统的幻数是OxEF53。
-
Block#2 块组描述符(硬盘上的s_first_data_blocks-1) EXT2将磁盘块分成几个组,每个组有8192个块(硬盘上的大小为32K)
struct ext2_group_dese {
u32 bg_b1ock_bitmap; //Bmap bloak number
u32 bg_inode_bitmap; //Imap block number
u32 bg_inode_table; //Inodes 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];
};
由于一个软盘只有1440个块,B2只包含一个块组描述符。其余的都是0。在有大量块组的硬盘上,块组描述符可以跨越多个块。块组描述符中最重要的字段是bg_block_bitmap.bg_inode_bitmap和 bg_inode_table,它们分别指向块组的块位图、索引节点位图和索引节点起始块。对于Linux格式的EXT2文件系统,保留了块3到块7。所以,bmap=8,imap=9,inode_table= 10。
-
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字节)的独特索引节点结构体表示。
将文件路径名转换为索引节点
各文件系统的根本问题是将文件名转换为文件系统中的文件数据结构表示形式。在EXT2/3/4文件系统中,相当于将路径名转换为文件的INODE。
使用系统调用进行文件操作
操作系统中,进程以两种不同模式运行:
-
内核模式(Kmode):执行特殊权限操作;
-
用户模式(Umode):权限非常有限,不可执行任何需要特殊权限的操作;
系统调用(syscall)一种允许进程进入Kmode执行Umode不允许操作的机制。
man2子目录列出了所有系统调用手册页。手册页的SYNOPSIS(摘要)部分列出了系统调用所需的头文件。
系统调用必须由程序发出。
简单的系统调用:
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:将文件系统添加到挂载点目录上 intmount(char *specialfile, char *mountDir); umount:分离挂载的文件系统 int umount (char *dir) ; mknod:创建特殊文件
常用系统调用
-
stat:获取文件状态信息 open:打开一个文件进行读、写、追加 int open(char *file, int flags, int mode)close:关闭打开的文件描述符 int close(int fd) read:读取打开的文件描述符 int read(int fd,char buf[ 1, int count)write:写入打开的文件描述符 int write(int fa,char buf[ 1, int count) lseek:重新定位文件描述符的读/写偏移量int lseek ( int fa, int offset, int whence) dup:将文件描述符复制到可用的最小描述符编号中int dup(int oldfd) ; dup2:将oldfd复制到newfd 中,如果newfd已打开,先将其关闭int dup2(int oldfa,int newfd) link:将新文件硬链接到旧文件 int link(char *o1dPath, char *newPath) unlink:取消某个文件的链接;如果文件链接数为0,则删除文件int unlink(char *pathname) ; symlink:创建一个符号链接 int symlink(char *target,char *newpath) readlink:读取符号链接文件的内容 int readlink(char *path,char *buf, int bufsize) umask:设置文件创建掩码;文件权限为( mask & ~umask)
stat与文件索引节点
stat与文件索引节点:
stat的工作原理:每个文件都有一个独有的索引节点数据结构,包含文件的所有信息。每对设备由主、次设备号标识,stat系统调用只是查找文件的索引节点并将信息从索引节点复制到stat结构体中。(st_dev和st_ino除外,他们分别是设备号和索引节点编号)