3. Linux文件系统结构
3.1 文件系统结构
(1)超级块
文件系统中的第一个块被称为超级块。这个块存放文件系统本身的信息。比如,记录了每个区域的大小,也存放了未被使用的磁盘块的信息。其包含的信息如下:
主要域 |
含义 |
Magic Number |
用来检验是否是一个真正的EXT2文件系统超级块 |
Revision Level |
主从修订版本号。让安装代码据此判断文件系统是否支持只存在于某个特定版本文件系统中的属性。 |
Block Group Number |
超级块的组数量 |
Block Size |
以字节记数的文件系统块大小,如1024字节 |
Blocks per Group |
每个组中块的数目。当文件系统创建时,这个大小就被固定下来。 |
Free Blocks |
文件系统中空闲块数 |
Free Inodes |
文件系统中空闲的inode数 |
First Inode |
文件系统中第一个inode号。一般就是根分区“/”的目录入口 |
(2)inode表:
每个子目录和文件只有唯一的一个 inode 块。它包含了文件系统中文件的基本属性(文件的长度、创建及修改时间、权限、所属关系、硬链接数及指向数据块位置的指针链表等相关信息
(3) 数据区
文件系统的第三个部分是数据区。文件的内容保存在这个区域。若文件太大时,会占用多个 block。为了提高目录访问效率,Linux 还提供了表示路径与 inode 对应关系的目录块(dentry结构)。它描述了路径信息并连接到节点 inode,它包括各种目录信息,还指向了 inode 和超级块。大小为 4096字节,而数据块记录着具体内容。注意Linux中文件和目录本质都是文件,所以它们本身的数据都存储在数据区中。
3.2 在文件系统中搜寻文件:以搜索/var/test.txt为例
(1)从超级块中拿到根目录inode节点信息(inode为2)。
(2)然后根据根目录的inode找到根目录项,从中找到var的inode节点。
(3)根据var的inode(786433)找到var的目录项,从中找到test.txt文件的inode节点(796326)。
(4)最后根据test.txt的inode节点信息,就可以找到test.txt文件所在的数据块。
3.3 硬链接和软链接
(1)硬链接的操作函数:link和ulink函数
头文件 |
#include<unistd.h> |
函数 |
int link(const char* existingpath, const char* newpath); //创建硬链接 int unlink(const char* pathname); //删除链接(软、硬链接均可删除) |
返回值 |
若成功则为0,若出错则为-1 |
功能 |
创建一个指向现有文件的硬链接或删除指定的硬链接。 |
备注 |
(1)硬链接创建的条件 ①针对文件创建链接;②必须是同一个分区(不能跨文件系统);③只有超级用户才能对目录建立链接。 (2)文件删除条件 ①链接计数为0;②无其他进程打开文件 |
【编程实验】创建硬链接
//file_hardlink.c
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> //为文件创建多个硬链接 int main(int argc, char* argv[]) { if(argc < 2){ fprintf(stderr, "usage: %s srcfile files\n", argv[0]); exit(1); } int i=0; for(i=2; i<argc; i++){ if(link(argv[1], argv[i]) < 0 ){ perror("link error"); continue; } } printf("create success\n"); return 0; }
(2)软链接的操作函数:symlink和readlink函数
symlink函数 |
|
头文件 |
#include<unistd.h> |
函数 |
int symlink(const char* actualpath, const char* sympath); |
返回值 |
若成功则为0,若出错则为-1 |
功能 |
创建一个符号链接(软链接) |
备注 |
(1)创建符号链接并不要求actualpath存在。 (2)可以跨文件系统建立符号链接 |
radlink函数 |
|
函数 |
int readlink(const char* pathname, char* buf, size_t bufsize); |
返回值 |
成功返回读到的字节数,出错返回-1 |
功能 |
打开该链接本身,并读该链接中的名字。 |
备注 |
open函数打开的是软链接引用的文件,而readlink打开的是软链接本身。 |
【编程实验】创建软链接
//file_softlink.c
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <memory.h> int main(int argc, char* argv[]) { if(argc < 3){ fprintf(stderr, "usage: %s srcfile linkfile\n", argv[0]); exit(1); } //对源文件创建符号链接(软链接) if(symlink(argv[1], argv[2]) < 0){ perror("open error"); exit(1); } char buffer[1024]; ssize_t size; int fd = open(argv[2], O_RDONLY); if(fd < 0){ perror("open error"); exit(1); } //读取软链接文件引用的源文件内容 memset(buffer, 0, sizeof(buffer)); if((size = read(fd, buffer, sizeof(buffer))) < 0){ perror("read error"); exit(1); }else{ write(STDOUT_FILENO, buffer, size); } close(fd); printf("\n"); //读取软链接本身的内容 memset(buffer, 0, sizeof(buffer)); if((size = readlink(argv[2], buffer, sizeof(buffer))) < 0 ) { perror("readlink error"); exit(1); }else{ write(STDOUT_FILENO, buffer,size); } printf("\n"); return 0; }
(3)软硬链接的区别与联系
①软链接有自己的文件属性及权限等,而硬链接只有目录项,没有独立的inode节点。
②可对不存在的文件或目录创建软链接。只不过,此时其数据块中的值不存放任何文件的路径而己。
③软链接可以交叉文件系统。但硬链接不可以跨文件系统,因为他们指向的是同一个inode节点。
④创建软链接时,源文件的计数不会增加。但为源文件创建硬链接时,计算每次加1。
⑤删除软链接并不影响指向的文件。但若被指向的原文件被删除(相当于上图中的test.txt被删除),则该软链接会因找不到这个test.txt而成为死链接。如果其所向的文件重新被恢复,则死链接可恢复为正常的软件链接。
⑥删除源文件或硬链接会使硬链接计数减1,当计数为0时,相应的数据块将被物理删除。
(4)remove和rename函数
remove函数 |
|
头文件 |
#include<unistd.h> |
函数 |
int remove(const char* pathname); |
返回值 |
若成功则为0,若出错则为-1 |
功能 |
解除对一个文件或目录的链接 |
备注 |
(1)对于文件:remove的功能相当于unlink (2)对于目录:remove的功能相当于rmdir |
rename函数 |
|
函数 |
int renamelink(const char* oldname, const char* newname); |
返回值 |
成功返回读到的字节数,出错返回-1 |
功能 |
文件或目录的更名 |
备注 |
open函数打开的是软链接引用的文件,而readlink打开的是软链接本身。 |
3.4 文件时间
(1)struct stat结构体中表示文件的3种时间
字段 |
说明 |
例子 |
ls选项 |
st_atime |
文件数据最后访问时间 |
read |
-u |
st_mtime |
文件数据最后修改时间 |
write |
默认 |
st_ctime |
i节点最后更改时间 |
chmod,chown |
-c |
(2)utime函数
头文件 |
#include<sys/types.h> #include<utime.h> |
函数 |
int utime(const char* pathname, struct utimbuf* times); |
返回值 |
若成功则为0,若出错则为-1 |
功能 |
更改文件的存取和修改时间 |
参数 |
(1)utimbuf结构体 struct utimbuf{ time_t actime; //access time time_t modtime //modification time } (2)参数times ①NULL:由取当前时间(但要求进程的有效ID必须等于该文件的所有者ID;或者进程对该文件具有写权限) ②非空:取times结构体中的时间(要求进程的有效ID必须等于该文件的所有者ID;或者进程是超级用户进程) |
备注 |
utime操作会自动更新st_ctime的值。 |
【编程实验】更改文件时间
//file_utime.c
#include <time.h> #include <utime.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <memory.h> //输出文件时间信息 void out(struct stat* buff) { //ctime函数将时间转为字符串 printf("atime: %s", ctime(&buff->st_atime)); printf("mtime: %s", ctime(&buff->st_mtime)); printf("ctime: %s", ctime(&buff->st_ctime)); } //获得文件属性 struct stat get_stat(char* file) { struct stat ret; memset(&ret, 0, sizeof(ret)); if(lstat(file, &ret) < 0){ perror("lstat error"); exit; } return ret; } int main(int argc, char* argv[]) { if (argc < 2){ fprintf(stderr, "usage: %s file\n", argv[0]); exit(1); } struct stat buff; buff = get_stat(argv[1]); //备份原先的时间 struct stat backup = buff; //输出文件时间 out(&buff); printf("-------------------------------\n"); //设置文件的时间为当前系统时间 utime(argv[1], NULL); buff = get_stat(argv[1]); out(&buff); printf("-------------------------------\n"); //将文件时间恢复为原先时间 struct utimbuf timebuf; timebuf.actime = backup.st_atime; timebuf.modtime = backup.st_mtime; utime(argv[1], &timebuf); buff = get_stat(argv[1]); out(&buff); return 0; } /*输出结果: atime: Sun Jan 22 19:04:24 2017 mtime: Sun Jan 22 19:04:00 2017 ctime: Sun Jan 22 19:58:39 2017 //注意,这个时间utime会自动更新 ------------------------------- atime: Sun Jan 22 20:01:08 2017 mtime: Sun Jan 22 20:01:08 2017 ctime: Sun Jan 22 20:01:08 2017 //注意,这个时间utime会自动更新 ------------------------------- atime: Sun Jan 22 19:04:24 2017 mtime: Sun Jan 22 19:04:00 2017 ctime: Sun Jan 22 20:01:08 2017 //注意,这个时间utime会自动更新 */