第七、八章学习笔记
第七章 文件操作
1.文件操作级别
- (1)硬件级别:
- fdisk:将硬件、U盘或SDC盘分区。
- mkfs:格式化磁盘分区,为系统做好准备。
- fsck:检查和维修系统。
- 碎片整理:压缩文件系统中的文件。
- (2)操作系统中的文件系统函数
- (3)系统调用
- (4)I/O库函数
- (5)用户命令
- (6)sh脚本
2.文件I/O库操作
- (1) 用户模式下的程序执行操作
FILE *fp = fopen("file", "r");
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章中讨论。
-
(9-1)对于read(fd, buf, BLKSIZE)系统调用,要确定所需的(dev,blk)编号,然后查询I/O缓冲区高速缓存。
-
(9-2)对于write(fd, fbuf, BLKSIZE)系统调用,要确定需要的(dev,blk)编号,然后查询I/O缓冲区高速缓存。
get a buffer =(dev, b1k) ;
write data to the I/O buffer ;
mark buffer as dataValid and DIRTY(for delay-write to disk);
release the buffer to buffer cache ;
- (10)设备I/O:Io缓冲区上的物理IO最终会仔细检查设备驱动程序,设备驱动程序由上半部分的start_io()和下半部分的磁盘中断处理程序组成。
3.低级别文件操作
1、分区
一个区块存储设备可以分为几个逻辑单元,成为分区。分区表位于第一个扇区的字节偏移446(ox1BE)处,改扇区称为设备的主引导记录。
如果某分区是扩展类型(类型编号=5),那么它可以划分为更多分区。假设分区P4是扩展类型,它被划分为扩展分区P5、P6、P7。扩展分区在扩展分区区域内形成一个链表,每个扩展分区的第一个扇区是一个本地MBR。每个本地MBR在字节偏移量0xIBE处也有一个分区表,只包含两个条目。第一个条目定义了扩展分区的起始扇区和大小。第二个条目指向下一个本地MBR。所有本地MBR的扇区编号都与P4的起始扇区有关。照例,链表以最后一个本地MBR中的0结尾。在分区表中,CHS值仅对小于8GB的磁盘有效。对大于8GB但小于4G扇区的磁盘,只有最后两个条目start _sector 和nr sector有意义。
2、格式化分区
fdisk只是将一个存储设备划分为多个分区。每个分区都有特定的文件系统类型,但是分区还不能使用。为了存储文件,必须先为特定的文件系统准备好分区。该操作习惯上称为格式化磁盘或磁盘分区。
在Linux中,命令
mkfs -t TYPE [-b bsize] device nblocks
在一个nblocks设备上创建一个TYPE文件系统,每个块都是bsize字节。如果bsize未指定,则默认大小为1KB.
在Linux中,还不能访问新的文件系统。它必须挂在到跟文件系统中的现有目录。由于细腻文件系统不是真正的设备i,他们必须作为循环设备挂载,如:
sudo mount -o loop vdisk /mnt
3、挂载分区
man 8 losetup : 显示永固系统管理的losetup实用工具命令。
4.EXT2文件系统简介
Block#0:引导块 B0是引导块,文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。
Block#1:超级块(在硬盘分区中字节偏移量为1024) B1是超级块,用于容纳关于整个文件系统的信息。
Block#2:块组描述块(硬盘上的s_first_data_blocks-1) EXT2将磁盘块分成几个组。每个组有8192个块(硬盘上的大小为32K)。每组用一个块组描述符结构体描述。
Block#8:块位图是用来表示某种项的位序列,例如:磁盘块或索引节点。
Block#9:索引节点位图,一个索引节点就是用来代表一个文件的数据结构。
Block#10:索引(开始)节点块,每个文件都用一个128节点的独特索引节点结构体表示。
直接块:指向直接磁盘块;
间接块:每个块编号指向一个磁盘块;
双重间接块:每个块指向256个磁盘块;
三重间接块:对于小型的“EXT2”文件系统,可以忽略这个块。
数据块:紧跟在索引节点块后面的是文件存储块。
第八章 使用系统调用进行文件操作
系统调用必须由程序发出,其最终用法像普通函数一样。每个系统调用都是一个库函数,它汇集系统调用参数,并最终向操作系统内核发出一个系统调用
int syscall(int a, int b, int c, int d);
- 其中,第一个参数a是系统调用编号,b、c、d是对应内核函数的参数。在基于Intel x86的Linux中,系统调用是由INT Ox80汇编指令实现的,可将CPU 从用户模式切换到内核模式。内核的系统调用处理程序根据系统调用编号将调用路由到一个相应的内核函数。当进程结束执行内核函数时,会返回到用户模式,并得到所需的结果。返回值≥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): - getewd:获取CWD的绝对路径名
char *getcwd(char *buf, int aize): - mkdir:创建目录
tnt mkdir(char *pathname,mode_t mode); - rmdir:移除目录(必须为空)
int rmdir(char *pathname); - link:将新文件名硬链接到旧文件名
tnt 1ink(char *o1dpath,char *newpath); - umlink:减少文件的链接数;如果链接数达到0,则删除文件
int unlink(char *pathname); - symlink:为文件创建一个符号链接
int symlink(char o1dpath, charnewpath); - 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 *dix); - mknod:创建特殊文件
int mknod(char *path,int mode, int device);
- access: 检查对某个文件的权限
- 常用的系统调用
- 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 fd, char buf[ ], int count) - lseek:重新定位文件描述符的读/写偏移量
int 1seek(int fd, int offset, int whence) - dup:将文件描述符复制到可用的最小描述符编号中
int dup(int oldfd); - dup2:将oldfd复制到newfd中,如果newfd已打开,先将其关闭
int dup2(int oldfd, int newfd) - link:将新文件硬链接到旧文件
int link(char *oldPath, 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)
int umask(int umask) ;