Ⅰ. ext2文件系统
- 文件系统中存储的最小单位是块(Block),一个块究竟多大是在格式化时确定的,例如 mke2fs 的-b 选项可以设定块大小为 1024、2048 或 4096 字节。
- 启动块(Boot Block)
大小就是 1KB,由PC标准规定,用来存储磁盘分区信息和启动信息,任何文 件系统都不能使用该块。 - 超级块(Super Block)
描述整个分区的文件系统信息,例如块大小、文件系统版本号、上次 mount 的时间等等。超级块在每个块组(Blcok Group)的开头都有一份拷贝。 - 块组描述符表(GDT, Group Descriptor Table)
由很多块组描述符组成,整个分区分成多少个块组就对应有多少个块组描述符。每个块组描述符存储一个块组的描述信息,包括 inode 表(inode table)哪里开始,数据块(Data Blocks)哪里开始,空闲的 inode 和数组块还有多少个等。块组描述符表在每个块组的开头也都有一 份拷贝,这些信息是非常重要的,因此它们都有多份拷贝。 - 块位图(Block Bitmap)
块位图就是用来描述整个块组中哪些块已用哪些块空闲的,本身占一个块,其中的每个bit代表本块组中的一个块,这个bit为1表示该块已用,这个bit为0表示该块空闲可用。 - inode位图(inode Bitmap)
和块位图(Block Bitmap)类似,本身占一个块,其中每个bit表示一个inode是否空闲可用。 - inode表(inode Table)
文件类型(常规、日录、符号链接等) ,权限,文件大小,创建/修改/访问时间等信息存在inode中,每个文件都有一个inode。 - 数据块(Data Block)
- 常规文件:文件的数据存储在数据块中。
- 目录:该日录下的所有文件名和目录名存储在数据块中。(注意:文件名保存在它所在目录的数据块中,其它信息都保存在该文件的inode中,可以通过存储的inode地址获取到)
- 符号链接:如果目标路径名较短则直接保存在inode中以便更快地查找,否则分配一个数据块来保存
- 设备文件、FIFO和socket等特殊文件:没有数据块,设备文件的主设备号和次设备号保存在inode中
Ⅱ. stat(2)
读取文件的inode,然后把inode中的各种文件属性填入一个struct stat结构体传出给调用者。stat(1)命令是基于stat函数实现的。stat需要根据传入的文件路径找到inode
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
假设一个路径是/opt/file,则查找的顺序是:
- 读出inode表中第2项,也就是根目录的inode,从中找出根目录数据块的位置
- 从根目录的数据块中找出文件名为opt的记录,从记录中读出它的inode号
- 读出opt目录的inode,从中找出它的数据块的位置
- 从opt目录的数据块中找出文件名为file的记录,从记录中读出它的inode号
- 读出file文件的inode
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
struct stat st;
if (argc < 2) {
printf("usage: cmd + filename/dirname\n");
}
stat(argv[1], &st);
// if (S_ISDIR(st.st_mode)) {
// printf("direcory\n");
// } else {
// printf("other file type\n");
// }
switch (st.st_mode & S_IFMT) {
case S_IFBLK:
printf("block device\n");
break;
case S_IFCHR:
printf("character device\n");
break;
case S_IFDIR:
printf("directory\n");
break;
case S_IFIFO:
printf("FIFO/pipe\n");
break;
case S_IFLNK:
printf("symlink\n");
break;
case S_IFREG:
printf("regular file\n");
break;
case S_IFSOCK:
printf("socket\n");
break;
default:
printf("unknown?\n");
break;
}
return 0;
}
Ⅲ.其它
下面的系统调用都和inode有关:
- access(2)
- chmod(2) / fchmod(2)
- chown(2) / fchown(2) / lchown(2)
- utime(2)
- truncate(2) / ftruncate(2)
- link(2)
- unlink(2)
- rename(2)
- readlink(2)
- mkdir(2)
- rmdir(2)
Ⅳ. opendir(3)
opendir()用来打开参数name指定的目录,并返回一个DIR *指针代表这个目录,它是一个类似FILE *指针的句柄,接下来对目录的读取和搜索都要使用此返回值。
#include<sys/types.h>
#include<dirent.h>
DIR *opendir(const char *name);
- 返回值:成功则返回DIR* 指针,打开失败则返回NULL并设置errno。
- name:路径名
Ⅴ. readdir(3)
readdir()返回参数dir目录流的下个目录进入点。 结构体struct dirent定义如下:
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
- d_ino:此目录进入点的inode
- d_off:目录文件开头至此目录进入点的位移
- d_reclen:该记录的长度,不包含NULL字符
- d_type:d_name所指的文件类型,不是所有文件系统类型都被支持
- d_name:文件名
#include<sys/types.h>
#include<dirent.h>
struct dirent *readdir(DIR *dir);
Ⅵ.closedir(3)
closedir用于关闭使用opendir(3)返回的DIR* 句柄
#include<sys/types.h>
#include<dirent.h>
int closedir(DIR *dir);
- 返回值:成功返回0,失败返回-1并设置errno
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
/*
打印目录argv[1]下的所有文件和文件夹
*/
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("usage: cmd + path\n");
return -1;
}
DIR* dir;
struct dirent* dp;
dir = opendir(argv[1]);
if (!dir) {
perror("opendir");
exit(-1);
}
while (dp = readdir(dir)) {
printf("%s\t", dp->d_name);
}
putchar(10);
closedir(dir);
return 0;
}
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void printDir(char* dir_name) {
DIR* dir;
struct dirent* dp;
char path_name[1024];
struct stat st;
dir = opendir(dir_name);
if (!dir) {
perror("opendir");
exit(-1);
}
while (dp = readdir(dir)) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
continue;
}
sprintf(path_name, "%s/%s", dir_name, dp->d_name);
if (stat(path_name, &st) < 0) {
perror("stat");
exit(-2);
}
if (S_ISDIR(st.st_mode)) {
printDir(path_name);
}
printf("%s\t", dp->d_name);
}
putchar(10);
closedir(dir);
}
/*
递归打印目录argv[1]下的所有文件和文件夹
*/
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("usage: cmd + path\n");
return -1;
}
printDir(argv[1]);
return 0;
}
Ⅶ. VFS
Linux支持各种各样的文件系统格式,然而这些文件系统都可以mount到某个目录下,使我们看到一个统一的目录树,各种文件系统上的目录和文件我们用ls命令看起来是一样的,读写操作用起来也都是一样的,这是怎么做到的呢? Linux内核在各种不同的文件系统格式之上做了一个抽象层,使得文件、目录、读写访问等概念成为抽象层的概念,因此各种文件系统看起来用起来都一样,这个抽象层称为虚拟文件系统(VFS, VirtualFileSystem)。
Ⅷ. dup / dup2
dup和dup2都可用来复制一个现存的文件描述符,使两个文件描述符指向同一个file结构体。如果两个文件描述符指向同一个file结构体, File Status Flag和读写位置只保存一份在file结构体中,并且file结构体的引用计数是2。如果两次open同一文件得到两个文件描述符,则每个描述符对应一个不同的file结构体,可以有不同的File Status Flag和读写位置。请注意区分这两种情况:
#include <unistd.h>
int dup(int oldfd);
dup()用来复制参数oldfd所指的文件描述词,并将它返回。此新的文件描述词和参数oldfd指的是同一个文件,共享所有的锁定、读写位置和各项权限或flag
- 返回值:当复制成功时,则返回最小及尚未使用的文件描述词。若有错误则返回-1,并设置errno
- oldfd:要复制的文件描述符
#include <unistd.h>
int dup2(int oldfd, int newfd);
dup2()用来复制参数oldfd所指的文件描述词,并将它拷贝至参数newfd后一块返回。若参数newfd为一已打开的文件描述词,则newfd所指的文件会先被关闭。
- 返回值:当复制成功时,则返回最小及尚未使用的文件描述词。若有错误则返回-1,并设置errno
- oldfd:要复制的文件描述符
- newfd:指定要复制的目标文件描述符
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd, save_fd;
if ((fd = open("test.txt", O_RDWR)) < 0) { // fd == 3
perror("open");
exit(-1);
}
save_fd = dup(1); //保存一下stdout, save_fd == 4,与标准输入指向相同地方
dup2(fd, 1); //将标准输入指向fd
close(fd);
write(1, "hello world\n", 12); //往标准输出写东西,实际上是写到test.txt中,即实现了标准输出的重定向
dup2(save_fd, 1); //把标准输出还原,1原本指向的文件test.txt会被关闭
write(1, "hello world\n", 12); //往标准输出写东西
return 0;
}
评论 (0)