20201320第四周学习笔记
第七章文件操作
一、文件操作级别
1、硬件级别操作:硬件级别的文件操作包括:
-fdisk:将硬盘、u盘或SDC盘分区。
-mkfs:格式化磁盘分区,为系统做好准备。
-fsck:检查和维修系统。
-碎片整理:压缩文件系统中的文件。
2、操作系统内核中的文件系统函数:每个操作系统内核均可为基本文件操作提供支持,下文列出了类unix系统内核中的一些函数,其中前缀k表示内核函数。
kmount () . kumount ( ) (mount/umount file systems)
kmkdir(), krmdir() (make/ remove directory)
kchdir(), 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() (lseak/close file descriptors)
ksymlink(), kreadlink () (create/read symbolic link files)
kstat(), kfstat(),klstat() (get file status/information)
kopendir(), kreaddiz() (open/read airectories)
3、系统调用:用户模式程序使用系统调用来访问内核函数
open(),read(),lseek()和close()都是c语言库函数。每个库函数都会发出一个系统调用,使进程进入内核模式来执行相应的内核函数,当进程结束执行内核函数使,会返回到用户模式,并得到所需的结果。
4、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脚本:虽然比系统调用方便的多,但是必须要手动呼入命令,如果使用的是GUI,必须要拖放文件图标和点击指向设备来输入,操作繁琐而且耗时。
二、文件I/O操作
双线上方的上半部分表示内核空间,下半部分表示进程的用户空间。控制流用标签(1)到(10)表示,说明如下。
(1)用户模式下的程序执行操作
FILE *fp =fopen(“file”,”r”);
(2)fopen()在用户( heap)空间中创建一个FILE结构体,包含一个文件描述符fd、一个fbuf [BLKSIZE]和一些控制变量。它会向内核中的 kopen()发出一个fd = open("file",flags=READ or WRITE)系统调用,构建一个OpenTable来表示打开文件示例。OpenTable的mptr指向内存中的文件INODE。对于非特殊文件,INODE 的i_block 数组指向存储设备上的数据块。成功后,fp会指向FILE结构体,其中f(是 open()系统调用返回的文件描述符。
(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()系统调用,但仅在必要时发出,而且它们会以块集大小来传输数据,提高效率。同样,其他库IO函数,如 fgetc/fputc、fgetsllputs、fscanf/fprintf等也可以在用户空间内的FILE结构体中对fbuf进行操作。
(5)内核中的文件系统函数:
假设非特殊文件的read(fd, fbuf[ ], BLKSIZE)系统调用。
(6)在read()系统调用中,fd是一个打开的文件描述符,它是运行进程的fd数组中的一个索引,指向一个表示打开文件的openTable。
(7)(OpenTable包含文件的打开模式、一个指向内存中文件 INODE的指针和读/写文件的当前字节偏移量。从 OpenTable的偏移量,
计算逻辑块编号1bk。
通过INODE.i_block[ ]数组将逻辑块编号转换为物理块编号blk。
(8) Minode包含文件的内存 INODE.EMODE.i_block[ ]数组包含指向物理磁盘块的指针。文件系统可使用物理块编号从磁盘块直接读取数据或将数据直接写入磁盘块,但将会导致过多的物理磁盘I/O。
(9)为提高磁盘I/O效率,操作系统内核通常会使用一组IO缓冲区作为高速缓存,以减少物理I/O的数量。磁盘1/O缓冲区管理将在第12章中讨论。
(9a)对于read(fd, buf, BLKSIZE)系统调用,要确定所需的( dev, blk)编号,然后查询I/O缓冲区高速缓存,以执行以下操作:
(9b)对于write(fd,fbuf, BLKSIZE)系统调用,要确定需要的( dev,blk)编号,然后查询I/O缓冲区高速缓存,以执行以下操作:
(10)设备IO:IO缓冲区上的物理IO最终会仔细检查设备驱动程序,设备驱动程序由上半部分的start_io()和下半部分的磁盘中断处理程序组成。
三、分区
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实用工具命令。
四、EXT2文件系统简介
1、EXT2文件系统数据结构
2、超级块
s_first_data_block:0表示4KB块大小,1表示1KB块大小。它用于确定块组描述符的起始块,s_first_data_block+ 1。
s_log_block_size确定文件块大小,为1KB*(2**s_log_block_size),例如О表示1KB块大小,1表示2KB块大小,2表示4KB块大小,等等。最常用的块大小是用于小文件系统的1KB和用于大文件系统的4KB。
s_mnt_count :已挂载文件系统的次数。当挂载计数达到max_mount_count时,fsck 会话将被迫检查文件系统的一致性。
s_magic是标识文件系统类型的幻数。EXT2/3/4文件系统的幻数是0xEF53。
3、块组描述符
EXT2将磁盘块分成几个组。每个组都有8192个块。每个组用一个块组描述符结构体描述。
4、位图
块位图是用来表示某种项的为序列。
索引节点位图一个索引节点就是用来代表一个文件的数据结构。
第八章
一、系统调用手册
在Unix以及大多数版本的Linux中,在线手册页保存在/usr/man/目录中。man2子目录中列出了所有系统调用手册页。sh命令man2 NAME显示了系统调用名称的手册页。
二、系统调用进行文件操作
系统调用必须由程序发出。他们的用法就像普通函数调用一样。每个系统调用都是一个库函数,它汇集系统调用参数,并最终向操作系统内核发出一个系统调用。下面是一些简单的文件操作系统调用:
1 access:检查对某个文件的权限 2 int access(char *pathname,int mode) ; 3 chdir:更改目录 4 int chdir(const char *path); 5 chmod:更改某个文件的权限 6 int chmod(char *path,mode_t mode) ; 7 chown:更改文件所有人 8 int chown (char *name, int uid, int gid); 9 chroot:将(逻辑)根目录更改为路径名 10 int chroot(char *pathname ) ; 11 getcwd:获取cWD的绝对路径名 12 char *getcwd(char *buf, int size); 13 mkdir :创建目录 14 int mkdir(char *pathname,mode_t mode) ; 15 rmdir:移除目录(必须为空) 16 int rmdir(char *pathname ); 17 link:将新文件名硬链接到旧文件名 18 int link(char *oldpath, char *newpath) ; 19 unlink:减少文件的链接数;如果链接数达到0,则删除文件 20 int unlink (char *pathname); 21 symlink:为文件创建一个符号链接 22 int symlink(char *oldpath,char *newpath); 23 rename:更改文件名称 24 int rename (char *oldpath, char *newpath); 25 utime:更改文件的访问和修改时间 26 int utime(char *pathname, struct utimebuf *time) 27 以下系统调用需要超级用户权限。 28 mount:将文件系统添加到挂载点目录上 29 int mount(char *specialfile,char *mountDir); 30 umount:分离挂载的文件系统 31 int umount (chaz *dir); 32 mknod:创建特殊文件 33 int mknod(char *path, int mode,int device);
三、常用的系统调用
1 stat:获取文件状态信息 2 int stat (char*filename, struct stat *buf) 3 int fstat(int tiledes,struct stat *buf) 4 int lstat (char*至ilename, struct stat *buf) 5 open:打开一个文件进行读、写、追加 6 int open(char *file, int flags, int mode) 7 close:关闭打开的文件描述符 8 int close(int fd) 9 read:读取打开的文件描述符 10 int read(int fd,char buf[ ], int count) 11 write:写入打开的文件描述符 12 int write(int fa,char buf[ 1, int count) 13 lseck:重新定位文件描述符的读/写偏移量 14 int lseek(int fa, int offset,int whence) 15 dup:将文件描述符复制到可用的最小描述符编号中 16 int dup(int oldrd); 17 dup2:将oldfd复制到newfd中,如果newfd已打开,先将其关闭 18 intdup2(int oldfd, int newfd) 19 link:将新文件硬链接到旧文件 20 int link ( char *o1dPath, char *newPath) 21 unlink:取消某个文件的链接;如果文件链接数为0,则删除文件 22 int unlink(char *pathname) ; 23 symlink:创建一个符号链接 24 int symlink(char *target, char *newpath) 25 readlink:读取符号链接文件的内容 26 int readlink(char *path,char *buf,int bufsize) 27 umask:设置文件创建掩码;文件权限为(mask & ~umask) 28 int umask (int umask);
四、链接文件
1、硬链接:命令
ln oldpath newpath
创建从newpath到oldpath的硬链接。对应的系统调用为:
link(char *oldpath,char *newpath)
硬链接文件会共享文件系统中相同的文件表示数据结构。文件链接会记录链接到同一索引结点的硬连接数量。相反,系统调用:
unlink(char *pathname)
会减少文件的链接数。如果链接变为0,文件就被完全删除。
2、软链接:命令
ln -s oldpath newpath #in command with the -s flag
创建从newpath到oldpath的软链接或符号链接。对应的系统调用是:
symlink(char *oldpath,chat *newpath)
newpath是LNK类型的普通文件,包含oldpath字符串。与硬链接不同,软链接适用于任何文件,包括目录。
五、stat系统调用
1、stat/lstat/fstat系统调用可将一个文件的信息返回。命令 man 2 stat 会显示stat系统调用的手册页。
概要
#include <sys/ types.h> #include <sys/stat.h> #include <unistd.h> int stat (const char *file_name,struct stat *buf); int fstat (int filedes,struct stat *buf) ; int lstat(const char *file_name,struct stat *buf) ;
描述
这些函数会返回指定文件的信息。不需要拥有文件的访问权限即可获取该信息,但是需要指向文件的路径中所有指定目录的搜索权限。
stat按文件名统计指向文件,并在缓冲区中填写stat信息。
lstat与 stat相同,除非是符号链接,统计链接本身,而不是链接所引用文件。所以,stat和 >1stat的区别是:stat遵循链接,但1stat不是。
fstat 与 stat相同,也只在文件名处说明filedes(由open ( 2)返回)所指向的打开文件。
2、文件类型和权限
在stat结构体中,大多数字段都无须解释。只有st_mode字段需要进行说明:mode_t st_mode;
/ * copied from i_mode of INODE*/
st_mode的类型是一个u16 ( 16位),这16位的含义如下:
前4位是文件类型,可以(以八进制形式)解释为:
S_IFMT 0170000 bitmask for the file type bitfields S_IFSOCK 0140000 socket s_IFLNK 0120000 symbolic link s_IFREG 0100000 regular file s_IFBLK 0060000 block device s_IFDIR 0040000 directory s_IFCHR 0020000 character device S_IFIFO 0010000 fifo
3、opendir-readdir函数
目录也是一个文件。我们应该能像其他任何普通文件一样,打开一个一个READ目录,然后读取他的内容。然而根据文件系统的不同,目录文件的内容可能会有不同。因此,用户可能无法正确读取和解释目录的内容。鉴于此,POSIX为目录文件指定了以下接口函数。
4、readlink函数
linux的open()系统调用遵循符号链接。因此,无法打开符号链接文件并读取其内容。要想读取符号链接文件的内容,我们必须使用readlink系统调用,即
int readlink(char *pathname,char buf[],int bufsize)
他将符号链接文件的内容复制到bufsize的buf[]中,并将实际复制的字节数返回。
遇到问题及解决办法
尝试了文件的几种创建方法
创建链接文件
硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。
用man -k ln查了ln的帮助文档
ln命令的四种格式:
ln [参数] [-T] [源文件] [连接文件]
ln [参数] [源文件]
ln [参数] [源文件] [新建连接文件所在的目录]
ln [参数] [-t] [新建连接文件所在的目录] [源文件]
建了一个简单的ln.txt,以此来创建两个硬链接linktonew和linknode
继续以ln.txt为例子,用-s或者--symbolic创建软连接。
软链接文件的索引节点和大小均与源文件不同。