这些说明将提供有关Linux系统上的文件系统的一些额外细节。本文介绍的大部分内容都详细介绍了在进行各种Linux系统调用时内核内部发生的情况。在系统编程课程中通常不会涉及这些内容,而在操作系统课程中通常会非常详细地介绍这些内容。由于我们目前没有提供有关操作系统的课程,所以我在这里提供的这些材料是对我们其他课程材料的补充。
这些笔记中的大部分内容来自Michael Kerrisk所著的the Linux Programming Interface的第14章和第18章。
当赢博体育程序想要与文件交互时,它将使用各种系统调用,如open()和read()。这些系统调用转到内核,然后内核将与文件系统通信,文件系统又将与设备驱动程序通信以访问设备上的文件。
设备
在Linux系统中,每个存储设备都是一个设备。内核使用设备驱动程序与设备通信。设备驱动程序支持赢博体育用于处理文件的常用系统调用,例如open()、close()、read()和write()。
系统将一个称为设备文件的特殊文件与系统中的每个设备相关联。这些设备文件可以在/dev目录下找到。此外,当前活动的设备信息被导出到/sys下的文件中。
每个设备文件都有一个主ID号,它将文件与特定的设备驱动程序关联起来,还有一个次要ID号,它用于为每种设备类型中的设备编号。
CD /sys ls - 1
每个磁盘被划分为一个或多个(不重叠的)分区。内核将每个分区视为驻留在/dev目录下的独立设备。磁盘分区可以保存任何类型的信息,但通常包含以下内容之一:
Linux系统实际上可以使用几种不同类型的文件系统,包括ext2、ext3、ext4、FAT32、NTFS、HFS和NFS。
为什么有这么多不同的文件系统?
/ proc
Directory使用一个特殊的虚拟文件系统,作为一个特殊文件的集合来显示正在运行的进程的信息。尽管Linux文件系统看起来是一个以/为根的单一、统一的文件系统,但实际上,给定的Linux系统可以在目录树的不同位置使用许多不同的文件系统。
mount命令用于将设备挂载到指定目录。
$ mount设备目录
您还可以使用mount命令查看当前挂载的设备及其挂载点的列表。
$ mount /dev/sda6 on / type ext4 (rw) proc on /proc type proc (rw) sysfs on /sys type sysfs (rw) devpts on /dev/pts type devpts (rw,mode=0620,gid=5) /dev/sda8 on /home type ext3 (rw,acl,user_xattr) /dev/sda1 on /windows/C type vfat (rw,noexec,nosuid,nodev) /dev/sda9 on /home/mtk/test type reiserfs (rw)
Linux系统上最初的文件系统是ext文件系统。后来,ext2文件系统取代了它,它支持更大的文件和更大的分区。反过来,ext2已经被ext3和ext4所取代,它们提供了更高级的功能,比如日志记录。
Ext2是一个很好的文件系统示例,因为它非常简单,很容易理解。
在ext2中,每个文件都由一个inode表示,inode是磁盘上一个固定大小的结构,用于存储有关文件的数据,并提供有关组成文件的数据块的信息。
ext2的结构如下:
超级块包含有关文件系统本身的信息。索引节点表包含一个索引节点列表,每个索引节点由一个索引节点号标识。索引节点从1开始编号。Inode 1包含关于不可使用的坏数据块的信息。Inode 2提供了关于文件系统上的根目录的信息。可以从该根目录开始访问文件系统的赢博体育其他部分。当文件系统被挂载到Linux系统的完整文件系统中时,文件系统的根目录被映射到完整文件系统中的挂载点。
i节点的结构:
inode的“其他文件信息”部分包含该文件的文件元数据。元数据包括:
在程序中,您可以使用stat()函数访问大部分元数据。Stat()以Stat结构返回有关文件的信息:
Struct stat {dev_t st_dev;/*包含文件的设备ID */ ino_t st_ino;/* Inode号*/ mode;/*文件类型和模式*/ nlink_t st_nlink;/*硬链接数*/ uid_t st_uid;/*赢博体育者的用户ID */ gid_t st_gid;/*用户组ID */ dev_t st_rdev;/*设备ID(如果特殊文件)*/ off_t st_size;/*总大小,单位为字节*/ blksize_t st_blksize;/*文件系统I/O块大小*/ blkcnt_t st_blocks;/* 512B内存块分配数量*/ struct timespec st_atim;/*最后一次访问时间*/ struct timespec st_mtim;/*上次修改时间*/ struct timespec st_ctim;/*最后一次状态改变的时间*/ /*向后兼容*/ #define st_atime。定义st_mtime。定义st_ctime。tv_sec};
在终端中,ls命令及其各种选项也显示此信息。
使用statvfs()和fstatvfs()库函数获取挂载的文件系统信息。
#include <sys/statvfs.h> int statvfs(const char *pathname, struct statvfs *statvfsbuf);Int fstatvfs(Int fd, struct statvfs *statvfsbuf);
这两个函数之间的唯一区别在于如何标识文件系统。对于statvfs(),我们使用pathname来指定文件系统中任何文件的名称。对于fstatvfs(),我们指定一个打开的文件描述符fd,它引用文件系统中的任何文件。这两个函数都返回一个statvfs结构,其中包含statvfsbuf所指向的缓冲区中有关文件系统的信息。这个结构有以下形式:
Struct statvfs {unsigned long f_bsize;/*文件系统块大小(单位:bytes) */ unsigned long f_frsize;/*基本文件系统块大小(以字节为单位)*/ fsblkcnt_t f_blocks;/*文件系统总块数(以‘f_frsize’为单位)*/ fsblkcnt_t f_bfree;/*空闲块总数*/ fsblkcnt_t f_bavail;/*非特权进程可用的空闲块数*/ fsfilcnt_t f_files;/* i节点总数*/ fsfilcnt_t f_ffree;/*空闲i节点总数*/ fsfilcnt_t f_favail;/ / unsigned long f_fsid; /*无特权进程可使用的i节点数(在Linux上设置为‘f_ffree’) *//*文件系统ID */ unsigned long f_flag;/* Mount flags */ unsigned long f_namemax;/*文件系统中文件名的最大长度*/};
赢博体育文件系统都将文件组织到目录中。目录只是一组文件和其他目录的容器。例如,在ext2中,每个目录实际上是一个特殊的文件,其中包含一个名称和inode号表。其中一些索引节点将引用文件,而其他索引节点将引用目录。
目录也可以包含链接。链接只是指向别的东西。
ln命令在两个文件之间创建一个链接。该链接可以是硬链接,也可以是软链接。
$ echo -n ‘收集东西很好,’ b> abc $ ls -li abc 122232 -rw-r——r——1 mtk users ‘ 29 june 15 17:07 abc $ ln abc xyz $ echo ’但最好是去散步。收集东西是件好事,但散步更好。$ ls -li abc xyz 122232 -rw-r——r——2 mtk users 63 Jun 15 17:07
在shell中,使用ln -s命令创建符号链接。ls -F命令在符号链接的末尾显示一个@字符。
rename()系统调用既可用于重命名文件,也可用于将其移动到同一文件系统上的另一个目录中。
#include <stdio.h> int rename(const char *oldpath, const char *newpath);
mkdir()系统调用创建一个新目录。
#include <sys/stat.h> int mkdir(const char *pathname, mode_t mode);
rmdir()系统调用会删除pathname中指定的目录,pathname可以是绝对路径名,也可以是相对路径名。
#include <unistd.h> int rmdir(const char *pathname);
remove()库函数的作用是:删除文件或空目录。
#include <stdio.h> int remove(const char *pathname);
opendir()函数打开一个目录并返回一个句柄,该句柄可用于在以后的调用中引用该目录。
#include < direct .h> DIR *opendir(const char *dirpath);
readdir()函数从目录流中读取连续的条目。
#include < DIR .h> struct DIR *readdir(DIR *dirp);
每次调用readdir()从dirp引用的目录流中读取下一个目录条目,并返回一个指向静态分配的dient类型结构的指针,其中包含有关该条目的以下信息:
Struct dient {ino_t d_ino;/*文件i-node号*/ char d_name[];/*以空结尾的文件名*/};
这个结构在每次调用readdir()时会被覆盖。
closedir()函数的作用是关闭dirp引用的打开目录流,释放该流使用的资源。
#include < DIR .h> int close (DIR *dirp);
nftw()函数遍历dirpath指定的目录树,并为目录树中的每个文件调用程序员定义的函数func一次。
#define _XOPEN_SOURCE 500 #include < ftww .h> int nftw(const char *dirpath, int (* function) (const char *pathname, const struct stat *statbuf, int typeflag, struct FTW *ftwbuf), int nopenfd, int flags);
进程可以使用getcwd()检索其当前工作目录。
#include <unistd.h> char *getcwd(char *cwdbuf, size_t size);
如果成功,getcwd()返回一个指向cwdbuf的指针作为函数结果。如果当前工作目录的路径名超过size字节,则getcwd()返回NULL, errno设置为ERANGE。
chdir()系统调用将调用进程的当前工作目录更改为pathname中指定的相对或绝对路径名(如果它是符号链接,则对其进行解引用)。
#include <unistd.h> int chdir(const char *pathname);
每个进程都有一个根目录,这是解释绝对路径名(即以/开头的路径名)的地方。默认情况下,这是文件系统的实际根目录。(新进程继承其父进程的根目录。)有时,进程更改其根目录很有用,特权进程可以使用chroot()系统调用执行此操作。
#define _BSD_SOURCE #include <unistd.h> int chroot(const char *pathname);
chroot()系统调用将进程的根目录更改为由pathname指定的目录(如果它是一个符号链接,则取消引用)。此后,赢博体育绝对路径名都被解释为从文件系统中的该位置开始。这有时被称为设置chroot jail,因为这样程序就被限制在文件系统的特定区域。
realpath()库函数解引用pathname(一个以null结尾的字符串)中的赢博体育符号链接,并解析对/的赢博体育引用。和/ . .生成包含相应绝对路径名的以空结尾的字符串。
#include <stdlib.h> char *realpath(const char *pathname, char *resolved_path);
dirname()和basename()函数将路径名字符串分解为目录和文件名部分。
#include <libgen.h> char *dirname(char *pathname);Char *basename(Char *pathname);