学习笔记4

第七章 文件操作

文件操作级别

按照从低到高的顺序排列如下:

1. 硬件级别

fdisk:将硬盘、U盘或SDC盘分区
mkfs:格式化磁盘分区,为系统做好准备
fsck:检查和维修系统
碎片整理:压缩文件系统中的文件

2. 操作系统内核中的文件系统函数

注:前缀k表示内核函数。

3. 系统调用

用户模式程序使用系统调用来访问内核函数。
open()、read()、lseek()和 close()函数都是C语言库函数。每个库函数都会发出一个系统调用,使进程进入内核模式来执行相应的内核函数。

4. I/O库函数

系统调用可让用户读/写多个数据块,这些数据块只是一系列字节。用户通常需要读/写单独的字符、行或数据结构记录等。如果只有系统调用,用户模式程序则必须自己从缓冲区执行这些操作。大多数用户会认为这非常不方便。为此,C语言库提供了一系列标准的I/O函数,同时也提高了运行效率
I/O库函数包括:

  • FILE mode I/O: fopen(),fread(); fwrite(),fseek(),fclose(),fflush()
  • char mode I/O: gete(), getchar(),ugete(); putc(),putchar()
  • line mode I/O: gets(),fgets0; puts(),fputs()
  • formatted I/O: scanf(),fscanf(),sscanf(); printf(),fprintf(),sprintf()
    除了读/写内存位置的sscanf0/sprintf0函数之外,所有其他I/O库函数都建立在系统调用之上,它们最终会通过系统内核发出实际数据传输的系统调用。

5. 用户命令

用户可以使用Unix/Linux命令来执行文件操作,而不是编写程序。如:mkdir,rmdir,cd,pwd,ls,link,unlink,rm,cat,cp,mv,chmod,etc.
每个用户命令实际上是一个可执行程序(cd除外)。

6. sh 脚本

虽然比系统调用方便很多,但是必须要手动输入命令。

文件I/O操作

  • (1)用户模式下的程序执行操作
    FILE *fp = fopen("file", "r") or 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)。
  • (4)
    • (a) 发出read(fd,fbuf, BLKSIZE)系统调用,将文件数据块从内核读取到 fbuf上,然后将数据复制到ubuf上,直到数据足够或者文件无更多数据可复制。
    • (b) 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章中讨论。
    • (a) 对于read(fd, buf, BLKSIZE)系统调用,要确定所需的(dev,blk)编号,然后查询I/O缓冲区高速缓存。
invalid) 
{
start_ io on buffer;
wait for I/O completion;
}
. copy datafrom buffer to fbuf ;
.release buffer to buffer cache ;
  • (b) 对于write(fd, fbuf, BLKSIZE)系统调用,要确定需要的(dev,blk)编号,然后查询I/O缓冲区高速缓存
 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()和下半部分的磁盘中断处理程序组成。

低级别文件操作

1. 分区:

一个块存储设备,如硬盘、U盘、SD卡等,可以分为几个逻辑单元,称为分区。各分区均可以格式化为特定的文件系统,也可以安装在不同的操作系统上。大多数引导程序,如GRUB、LILO等,都可以配置为从不同的分区引导不同的操作系统。分区表位于第一个扇区的字节偏移446(0x1BE)处,该扇区称为设备的主引导记录(MBR)。表有4个条目,每个条目由一个16字节的分区结构体定义,即:

u8 drive; // 0x80 - active
u8 head; // starting head
u8 sector; // starting sector
u8 cylinder; // starting cylinder
u8 sys_type; // partition type
u8 end_head; // end head
u8 end_sector; // end sector
u8 end_cylinder; // end cylinder
u32 start_sector; // starting sector counting from 0
u32 nr_sectors; // number of sectors in partition
};

disk mydisk 在磁盘映像文件上运行fdisk

fdisk是一个交互程序。它有一个显示所有命令的帮助菜单。它收集用户的输入,在内存中 创

建一个分区表,该分区表仅在用户输入w命令时才被写入磁盘映像的MBR。

在内存 中,它允许用户创建、检査和修改分区。将分区写入磁盘后,通过q命令退出fdisk

通过t命令将它们更改为不同的文件系统类型。

2. 格式化分区

fdisk只是将一个存储设备划分为多个分区。每个分区都有特定的文件系统类型,但是分区还不能使用。为了存储文件,必须先为特定的文件系统准备好分区。该操作习惯上称为格式化磁盘或磁盘分区。在Linux中,它被称为mkfs,命令:
在一个nblocks设备上创建一个TYPE文件系统,每个块都是bsize字节。如果bsize未指定,则默认块大小为1KB

EXT2 文件系统简介

数据结构

在Linux下,我们可以创建一个包含简单EXT2文件系统的虚拟磁盘。
dd if=/dev/zero of=mydisk bs=1024 count=1440
mke2fs -b 1024 mydisk 1440
得到的EXT2文件系统有1440个块,每个块大小为1KB。

Block#0:引导块,文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。
Block#1: 超级块(在硬盘分区中字节偏移量为1024)。用于容纳关于整个文件系统的信息。
Block#2:块组描述符(硬盘上的s _ first _ data _ blocks-1) EXT2将磁盘块分成几个组,每个组有8192个块(硬盘上的大小为32K)
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字节)的独特索引节点结构体表示。

第八章:使用系统调用进行文件操作

系统调用

在操作系统中,进程有两种模式:

内核 (Kmode)
用户 (Umode)
系统调用(简称syscall)是一种允许进程进入Kmode以 执行Umode不允许操作的机制
复刻子进程、修改执行映像.甚至是终止等操作都必须在内核中执行。
系统调用(简称syscall)是一种允许进程进入Kmode以 执行Umode不允许操作的机制。

系统调用手册页

在线手册页保存在/usr/man/目录中(Goldt等 1995 ;
Kcrrisk 2010, 2017 )o 而在 Ubuntu Linux 中,则保存在 /usr/share/man 目录中。
man2 子目录中列出了所有系统调用手册页。

使用系统调用进行文件操作

系统调用必须由程序发出。它们的用法就像普通函数调用一样。每个系统调用都是一个库函数,它汇集系统调用参数,并最终向操作系统内核发出一个系统调用。例如:
int syscall(int a,int b,int c,int d);
其中,第一个参数a是系统调用编号,b、c、d是对应内核函数的参数。内核的系统调用处理程序根据系统调用编号将调用路由到一个相应的内核函数。当进程结束执行内核函数时,会返回到用户模式,并得到所需的结果。返回值≥0表示成功,-1表示失败。如果失败,errno变量(在errno.h中)会记录错误编号,它们会被映射到描述错误原因的字符串。

简单的系统调用:

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:将文件系统添加到挂载点目录上 int mount(char *specialfile,char *mountDir);
umount:分离挂载的文件系统 intumount(char *dir);
mknod:创建特殊文件 int mknod(char *path,int mode, int device);

常用的系统调用:

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)
Iseek:重新定位文件描述符的读/写偏移量int Iseek(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 uniink(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);

链接文件

Unix/Linux允许使用不同的路径名来表示同一个文件。这些文件叫作LINK文件。链接有两种类型:硬链接和软链接(符号链接)

  • 硬链接 命令

    ln oldpath newpath
    创建从newpath到oldpath的硬链接。硬链接文件会共享文件系统中相同的文件表示数据结构(索引节点)。文件链接数会记录链接到同一索引节点的硬链接
    数量。硬链接仅适用于非目录文件。否则,它可能会在文件系统名称空间中创建循环。系统调用:
    unlink(char *pathname)
    会减少文件的链接数。如果链接数变为0,文件会被完全删除。这就是rm(file)命令的作用。

  • 软链接 命令

    ln -s oldpath newpath
    创建从newpath到oldpath的软链接或符号链接。newpath是LNK类型的普通文件,包含oldpath字符串。软链接适用于任何文件,包括目录。软链接在以下
    情况下非常有用。
    (1)通过一个较短的名称来访问一个经常使用的较长路径名称。
    (2)将标准动态库名称链接到实际版本的动态库。

stat系统调用

stat/lstat/fstat系统调用可将一个文件的信息返回

stat文件状态
名称
stat, fstat, Istat - get file status
概要
#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 Istat(const char *file_name, struct stat *buf);
posted @   池鱼12156  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示