第七章:文件操作
文件操作级别
-
硬件级别
- fdisk:将硬盘、U盘或SDC盘分区
- mkfs:格式化磁盘分区,为系统做好准备
- fsck:检查和维修系统
- 碎片整理:压缩文件系统中的文件
-
操作系统内核中的文件系统函数
-
每个操作系统内核均可为基本文件操作提供支持
-
-
系统调用
- 用户模式使用系统调用来访问内核函数
-
I/O库函数
-
系统调用可让用户读/写多个数据块,这些数据块只是一些字节。C语言库提供一系列标准的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()
-
-
用户命令:用户可以使用Unix/Linux命令来执行文件操作,而不是编写程序
mkdir,rmdir,cd,pwd,ls,link,unlink,rm,cat,cp,mv,chmod,etc
-
sh脚本
文件I/O操作
-
1.用户模式下的程序执行操作
FILE *fp = fopen("file","r");or FILE *fp = fopen("file","w");
-
2.fopen在用户(heap)空间中创建一个FILE结构体,向内核中的kopen()发出一个fd = open("file",flags = READ or WRITE)系统调用,构建一个Opentable表示打开文件实例。成功后,fp会指向FILE结构体.
-
3.fread(ubuf,size,nitem,fp)`:将nitem个size字节读取到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
-
5.内核中的文件系统函数:
假设非特殊文件的read(fd,fbuf[],BLKSIZE)系统调用。 -
6.read()系统调用中,fd是一个打开的文件描述符,指向一个表示打开文件的OpenTable
-
7.OpenTable包含文件的打开模式、一个指向内存中文件INODE的指针和读/写文件当前字节偏移量
- 计算逻辑块编号1bk
- 通过INODE.i_block[ ]数组将逻辑块编号转换为物理块编号blk
-
8.Minode包含文件的内存INODE
-
9.为了提高磁盘I/O效率,操作内核通常会使用一组I/O缓冲区作为高速缓存
-
(9a).对于read(fd, buf, BLKSIZE)系统调用,要确定所需的(dev,blk)编号,然后查询I/O缓冲区高速缓存
-
(9b).对于write(fd, fbuf, BLKSIZE)系统调用,要确定需要的(dev,blk)编号,然后查询I/O缓冲区高速缓存
-
10.设备I/O:I/O缓冲区上的物理IO最终会仔细检查设备驱动程序,设备驱动程序由上半部分的start_io()和下半部分的磁盘中断处理程序组成
低级别文件操作
- 分区
- 一个块存储设备,如硬盘、U盘、SD卡等,可以分为几个逻辑单元,称为分区。分区表位于第一个扇区的字节偏移446(0x1BE)处,成为设备的主引导记录(MBR)。每一个扩展分区的第一个扇区是一个本地MBR
- 格式化分区
- 为了存储文件,必须先为特定的文件系统准备好分区,该操作成为格式化磁盘或磁盘分区。在Linux中,称为mkfs,表示Make文件系统。Linux支持多种不同类型的文件系统。
- 挂载分区
EXT2文件系统简介
-
Linux一直使用EXT2作为默认文件系统,EXT3是EXT2的扩展,增加的主要是一个日志文件,EXT4是EXT3的扩展,增加了磁盘块的分配
-
EXT2文件系统数据结构
-
创建一个包含简单EXT2文件系统的虚拟盘
dd if=/dev/zero of=mydisk bs=1024 count=1440 mke2fs -b 1024 mydisk 1440
Block#0:引导块,文件系统不使用它,用于容纳从磁盘引导操作系统的引导程序
-
-
超级块
- Block#1:超级块,用于容纳关于整个文件系统的信息。
-
块组描述符
- Block#2:块组描述符块,EXT2将磁盘分成几个组(每组8192块),每组用一个块组描述符结构体描述
-
位图
- Block#8:块位图,用来表示某种项的位序列
- Block#9:索引节点位图,用来代表一个文件的数据结构
-
索引节点
- Block#10:索引节点,每个文件都用一个128字节(EXT4的是256字节)的独特索引节点结构体表示
第八章:使用系统调用进行文件操作
系统调用
-
在操作系统中,以内核模式和用户模式运行,简称Kmode和Umode,syscall,是一种允许进程进入Kmode以执行Umode不允许操作的机制。
-
系统调用手册页
man 2 stat man 2 open man 2 read
-
使用系统调用进行文件操作
mkdir、chdir、getcwd系统调用
#include<stdio.h> #include<errno.h> int main() { char buf[256],*s; int r; r = mkdir("newdir",0766); //mkdir syscall if(r<0) printf("error=%d : %s\n",errno,strerror(errno)); r = chdir("newdir"); //cd into newdir s = getcwd(buf,256); //get CWD string int buf[] printf("CWD = %s\n",s); }
该程序发出一个mkdir()系统调用来创建新目录
常用的系统调用
-
open:打开一个文件进行读、写、追加
int open(char *file, int flags, int mode);
-
close:关闭打开的文件描述符
int close(int fd);
-
read:读取打开的文件描述符
int read(int fd, char buf[], int count);
-
write:写入打开的文件描述符
int write(int fd, char buf[], int count);
-
dup:将文件描述符复制到可用的最小描述符编号中
int dup(int oldfd);
-
dup2:将oldfd复制到newfd中,如果文件链接数为0,则删除文件
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);
-
mknod:创建特殊文件
int mknod(char *path,int mode,int device)
链接文件
-
硬链接文件
- 命令:
ln oldpath newpath
- 对应系统调用:
link(char *oldpath,char *newpath)
- 减少文件链接数:
unlink(char *pathname)
- 命令:
-
符号链接文件
- 命令:
ln -s oldpath newpath
- 对应系统调用:
symlink(char *oldpath,char *newpath)
- 命令:
stat系统调用
-
stat文件状态
-
stat结构体
st_size
使用字节表示的文件大小st_block
值是使用512字节表示的文件大小- 并非所有的Linux文件系统都能实现所有的时间字段
-
stat与文件索引节点
- 每个文件都有一个独有的索引节点数据结构,包含文件的所有信息
- 每个索引节点在存储设备上都有唯一的索引节点编号(ino)。每个设备都由一对(主,次)设备号标识,例如0x0302表示/dev/hda2,0x0803表示/dev/sda等
-
文件类型与权限
- st_mode的类型是一个u16类型
- 前四位:表示文件类型
- 接下来的三位:表示文件的特殊用法
- 剩余九位:文件保护权限位
- st_mode的类型是一个u16类型
-
opendir-readdir函数
- 目录也是一个文件,我们应该像其他文件一个打开一个READ目录,然后读取和显示它的内容。
-
readlink函数
- 读取符号链接文件的内容,必须使用readlink系统调用,即:
int readlink(char *pathname,char buf[],int bufsize);
将符号连接文件的内容复制到buf中,并将世纪复制的字节返回。
- 读取符号链接文件的内容,必须使用readlink系统调用,即:
-
ls程序
-
open-close-lseek系统调用
- open:打开一个文件,读、写、追加
- close:关闭打开的文件描述符
- read:读取打开的文件描述符
- write:写入
- lseek:将文件描述符的字节偏移量重新定位为偏移量
- umask:设置文件创建掩码;
苏格拉底挑战
文件I/O操作的苏格拉底挑战
stat结构体的苏格拉底挑战
遇到的问题
问题:文件的读取和写入操作过程中,如何处理异常和错误?
解决方法:问gpt
gpt的回答