第七、八章学习笔记

第七、八章学习笔记

第七章 文件操作

1.文件操作级别

  • (1)硬件级别:

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

  • (3)系统调用

  • (4)I/O库函数

  • (5)用户命令

  • (6)sh脚本

2.文件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)。

  • (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章中讨论。

  • (9a)对于read(fd, buf, BLKSIZE)系统调用,要确定所需的(dev,blk)编号,然后查询I/O缓冲区高速缓存。

get abuffer = (dev,b1k) ;. if(buffer's data are
invalid) 
{
start_ io on buffer;
wait for I/O completion;
}
. copy datafrom buffer to fbuf ;
.release buffer to buffer cache ;
  • (9b)对于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. 分区
  • 一个块存储设备,如硬盘、U盘、SD卡等,可以分为几个逻辑单元,称为分区。各分区均可以格式化为特定的文件系统,也可以安装在不同的操作系统上。大多数引导程序,如GRUB、LILO等,都可以配置为从不同的分区引导不同的操作系统。分区表位于第一个扇区的字节偏移446(0x1BE)处,该扇区称为设备的主引导记录(MBR)。表有4个条目,每个条目由一个16字节的分区结构体定义,即:
struct partition {
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
};
  1. 格式化分区
  • fdisk只将一个存储设备划分为多个分区。每个分区都有特定的文件系统类型,但是分区还不能使用。为了存储文件,必须先为特定的文件系统准备好分区。该操作习惯上称为格式化磁盘或磁盘分区。在Linux中,它被称为mkfs,表示Make文件系统。Linux支持多种不同类型的文件系统。每个文件系统都期望存储设备上有特定的格式。在Linux中,命令:
mkfs -t TYPE [-b bsize] device nblocks
  1. 挂载分区
  • man 8 losetup:显示用于系统管理的losetup 实用工具命令:
    (1)用dd命令创建-一个虚拟磁盘映像:
dd if=/dev/zero of=vdisk bs=1024 count=32768 #32K (1KB) blocks

(2)在vdisk. 上运行fdisk来创建一一个分区P1:

fdisk vdisk

输人n(new)命令,使用默认的起始和最后扇区编号来创建一个分区Pl。然后,输人w命令将分区表写人vdisk并退出fdisko vdisk 应包含-个分区P1 [start=2048, end=65535]。该分区的大小是63488个扇区。
(3)使用以下扇区数在vdisk的分区1上创建一个循环设备:

losetup -o $(expr 2048 * 512) --sizelimit $(expr 65535 * 512) /dev/1oop1vdisk

losetup需要分区的开始字节(start_ sector512) 和结束字节(end_ sector512)。 读者可手动计算这些数值,并在losetup命令中使用它们。可用类似方法设置其他分区的循环设备。循环设备创建完成后,读进程可以使用命令
losetup - a 将所有循环设备显示为/dev/loopN。
(4)格式化/dev/loop1,它是一个EXT2文件系统:

mke2fs -b 4096 /dev/loop1 7936 # mke2fs with 7936 4KB blocks

该分区的大小是63488个扇区。4KB块的扇区大小是63488 /8=7936
(5)挂载循环设备:

mount /dev/ 1oop1 /mnt # mount as loop device

(6)访问作为文件系统一部分的挂载设备:

(cd /mnt; mkdir bin boot dev etc user) # populate with DIRs

(7)设备使用完毕后,将其卸载。

umount /mnt

(8)循环设备使用完毕后,通过以下命令将其断开:

losetup -a /dev/loop1 # detach a loop device.

4.EXT2文件系统简介

  • 多年来,Linux一直使用EXT2(Card等1995;EXT2 2001)作为默认文件系统。EXT3(EXT3 2015)是EXT2的扩展。EXT3中增加的主要内容是一个日认文件系统。,的日志中。日志可在文件系统崩溃时更快从错误中恢复。EXT3的最新扩展是EXT4(Cao等2007)。Ex4的主要变化是盘块的分配。在EXT4中,块编号是48位。EXT4不是分配不连续的磁盘块,而是分配连续的磁盘块区,称为区段。
  1. EXT2文件系统数据结构
  • Block#0:引导块 文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。
  1. 超级块
  • Block#1:超级块(在硬盘分区中字节偏移量为1024)B1是超级块,用于容纳关于整个文件系统的信息。下文说明了超级块结构中的一些重要字段。
    struct ext2_super_block {
    u32 s_inodes_count; // Inodes count
    u32 s_blocks_count; // Blocks count
    u32 s_r_blocks_count; // Reserved blocks count
    u32 s_free_blocks_count; // Free blocks count
    u32 s_free_inodes_count; // Free inodes count
    u32 s_first_data_block; // First Data Block
    u32 s_log_block_size; // Block size
    u32 s_log_cluster_size; // Allocation cluster size
    u32 s_blocks_per_group; // # Blocks per group
    u32 s_clusters_per_group; // # Fragments per group
    u32 s_inodes_per_group; // # Inodes per group
    u32 s_mtime; // Mount time
    u32 s_wtime; // Write time
    u32 s_mnt_count; // Mount count
    u16 s_max_mnt_count; // Maximal mount count
    u16 s_magic; // Magic signature
    // more non-essential fields
    u16 s_inode_size; // size of inode structure
    };
  1. 块组描述符
  • Block#2:块组描述符块(硬盘上的s_first_data_blocks-1)EXT2将磁盘块分成几个组。每个组有8192个块(硬盘上的大小为32K)。每组用一个块组描述符结构体描述。
    struct ext2_ group_ desc (
    u32
    bg_ block_ bi tmap; // Bmap block number
    u32 bg inode_ bi tmap; //Imap b1ock number
    u32 bg inode_ table; // Indes begin block number
    u16 bg_ free_ blocks_ count ; // THESE are OBVIOUS
    u16 bg_ free_ inodes_ count ;
    u16 bg_ used_ dirs_ count;
    u16 bg_ pad; //ignore these
    u32 bg_ reserved[3] ;
    };
  1. 位图
  • Block#8:块位图(Bmap)(bg_block_bitmap)位图用来表示某种项的位序列,例如,磁盘块或索引节点。位图用于分配和回收项。在位图中,0位表示对应项处于FREE状态,1位表示对应项处于IN_USE状态。一个软盘有1440块。
  • Block#9:索引节点位图(Imap)(bg_inode_bitmap)一个索引节点就是用来代表一个文件的数据结构。EXT2文件系统是使用有限数量的索引节点创建的。各索引节点的状态用B9中Imap中的一个位表示。在EXT2 FS中,前10个索引节点是预留的。
  1. 索引节点
  • Block#10:索引(开始)节点块(bg_inode_table)每个文件都用一个128字节(EXT4中的是256字节)的独特索引节点结构体表示。
    struct ext2_ inode {
    u16 i_ mode;// 16 bits - ttttlugsIrwxJrwxIrwxl
    u16 i_ uid;//owner uid
    u32 i_ size;//file size in bytes
    u32 i_ atime;//time fields in seconds
    u32 i_ ctime;// since 00:00:00,1-1-1970
    u32 i_ mtime;
    u32 i_ dtime;
    u16 i_ gid;// group ID
    u16 i_ 1 inks_ count;// hard-link count
    u32 i_ blocks;// number of 512-byte sectors
    u32 i_ flags;//IGNORE
    u32 i_ reserved1 ;//IGNORE
    u32 i_ b1ock[15] ;//See details below
    u32 i_ pad[7] ;//for inode size = 128 bytes
    }
  • 直接块:i_block[0]至i_block[11]指向直接磁块盘
  • 间接块:i_block[12]指向一个包含256个块编号的磁盘块,每个块编- - 号指向一个磁盘块
  • 双重间接块:i_block[13]指向一个指向256个块的块,每个块指向256个磁盘块
  • 三重间接块:i_block[14]对于小型EXT2文件可忽略
  1. 目录条目
  • EXT2目录条目;目录包含dir_entry_2结构,即:
    struct ext2_dir_entry_2 {
    u32 inode;
    u16 rec_len;
    u8 name_len;
    u8 file_type;
    char name[EXT2_NAME_LEN];
    };

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

  1. 系统调用
  • 手册页
    使用 man 2 NAME查看对应手册
  1. 使用系统调用进行文件操作
    系统调用必须由程序发出,其最终用法像普通函数一样。每个系统调用都是一个库函数,它汇集系统调用参数,并最终向操作系统内核发出一个系统调用
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);
  • 常用的系统调用
    • 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) ;
原文地址:https://www.cnblogs.com/zhangwenying/p/15381191.html