第七章:文件操作
文件操作级别
硬件级别
- disk:将硬盘、U盘或SDC盘分区
- mkfs:格式化磁盘分区,为系统做好准备
- fsck:检查和维修系统
- 碎片整理:压缩文件系统中的文件
操作系统内核中的文件系统函数
- 类Unix系统内核中的一些函数,其中前缀k表示内核函数
系统调用
- 用户模式程序使用系统调用来访问内核函数
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命令来执行文件操作
sh脚本
文件I/O操作
- 用户模式下的程序执行操作
- 打开一个读/写文件流
FILE *fp = fopen("file","r");or FILE *fp = fopen("file","w");
- 打开一个读/写文件流
- fopen()在用户空间中创建一个FILE结构体向内核中的kopen()发出一个fd = open("file",flags = READ or WRITE)系统调用,构建一个Opentable表示打开文件示例。OpenTable的mptr指向内存中的文件INODE。成功后,fp会指向FILE结构体。
- 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
- 内核中的文件系统函数
- 假设非特殊文件的read(fd,fbuf[],BLKSIZE)系统调用
- 在read()系统调用中,fd是一个打开的文件描述符,它是运行进程的fd数组中的一个索引,指向一个表示打开文件的OpenTable
- OpenTable包含文件的打开模式、一个指向内存中文件INODE的指针和读/写文件当前字节偏移量
- 计算逻辑块编号1bk
- 通过INODE.i_block[ ]数组将逻辑块编号转换为物理块编号blk
- Minode包含文件的内存INODE。EMODE.i_block[]数组包含指向物理磁盘块的指针。
- 为提高磁盘效率,操作系统内核会使用一组I/O缓冲区作为高速缓存。
- 对于read(fd, buf, BLKSIZE)系统调用,要确定所需的(dev,blk)编号,然后查询I/O缓冲区高速缓存
- 对于write(fd, fbuf, BLKSIZE)系统调用,要确定需要的(dev,blk)编号,然后查询I/O缓冲区高速缓存
- 设备I/O:I/O缓冲区上的物理IO最终会仔细检查设备驱动程序,设备驱动程序由上半部分的start_io()和下半部分的磁盘中断处理程序组成
低级别文件操作
分区
- 一个块存储设备,如硬盘、U盘、SD卡等,可以分为几个逻辑单元,称为分区。分区表位于第一个扇区的字节偏移446(0x1BE)处,成为设备的主引导记录(MBR)
格式化分区
- 为了存储文件,必须先为特定的文件系统准备好分区,该操作称为格式化磁盘或磁盘分区。在Linux中,称为mkfs,表示Make文件系统
- 挂载分区
- 用dd命令创建一个虚拟磁盘映像:
dd if=/dev/zero of=vdisk bs=1024 count=32768
- 在vdisk上运行fdisk来创建一个分区P1:
fdisk vdisk
- 在vdisk的分区1上创建一个循环设备
- 格式化/dev/loop1
- 挂载循环设备
mount /dev/loop1/mnt
- 访问作为文件系统一部分的挂载设备
- 设备使用完毕后,将其卸载
umount /mnt
- 循环设备使用完毕后,将其断开
losetup -d /dev/loop1
- 用dd命令创建一个虚拟磁盘映像:
第八章:使用系统调用进行文件操作
系统调用
- 在操作系统中,进程以两种不同的模式运行,即内核模式和用户模式。系统调用是一种允许进程进入Kmode以执行Umode不允许操作的机制。
系统调用手册
- sh命令man 2 NAME现实而来系统调用名称的手册页
使用系统调用进行文件操作
- 示例
#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);
}
- 简单的系统调用
常用的系统调用
- 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
- 创建从newpath到oldpath的硬链接。
link(char *oldpath,char *newpath)
- 减少文件链接数
unlink(char *pathname)
符号链接文件
- 软链接命令
ln -s oldpath newpath
- 创建从newpath到oldpath的软链接
symlink(char *oldpath,char *newpath)
stat系统调用
stat/lstat/fstat系统调用可将一个文件的信息返回。
- stat文件状态
- stat结构体
- st_size使用字节表示的文件大小
- st_block值是使用512字节表示的文件大小
- stat与文件索引节点
- 每个索引节点在存储设备上都有唯一的索引节点编号(ino)。每个设备都由一对设备号标识,例如0x0302表示/dev/hda2等
- 文件类型和权限
- 只有st_mode字段需要进行说明:
16位,前4位是文件类型,接下来的3位表示文件的特殊用法,其余9位是文件保护权限位。 - st_mode:第一个字母表示文件类型,后面9个字符基于权限位,如果位是1,每个字符打印为r|w|x;如果位是0,则打印-。x位表示是否允许访问目录。
- 只有st_mode字段需要进行说明:
- opendir-readdir函数
- 目录也是一个文件
- 具体实现:
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
- readlink函数
- 将符号链接文件的内容复制到bufsize的buf[]中,并将实际复制的字节数返回
int readlink(char *pathname,char buf[],int bufsize);
- 将符号链接文件的内容复制到bufsize的buf[]中,并将实际复制的字节数返回
- ls程序
- open-close-lseek系统调用
- open:打开一个文件,读、写、追加
- close:关闭打开的文件描述符
- read:读取打开的文件描述符
- write:写入
- lseek:将文件描述符的字节偏移量重新定位为偏移量
- umask:设置文件创建掩码;
问题
内核模式和用户模式有什么区别,各有什么优缺点?
硬链接文件和软链接文件的区别?
苏格拉底提问