此文着重介绍系统是如何表示目录的以及pwd命令的编写。
我们都知道Unix将磁盘分为三部分:超级块(superblock),节点表(inode table)以及数据区。超级块中记录文件系统本身的结构信息。节点表中记录文件的属性,文件系统中每个文件在表中都至少有一个i-节点,表中每个节点的大小相同。数据区就是文件数据真实的存储位置。
简要介绍磁盘知识后,我们来谈谈目录。在Unix中,目录是包含了文件名字列表的特殊文件,其抽象模型是一个包含i-节点号和文件名的表。所以一个目录的内容有两个部分:一个i-节点表和文件名表,其相互对应。对于文件来说,目录是找到该文件的一个索引(链接),本质上就是负责记录文件i-节点表与文件名的对应关系(一个文件可能对应好多个i-节点)。对于open、cat、cp等命令,都是通过在目录中寻找目的文件名,然后根据对应关系获取i-节点号以获取文件属性,最终获取文件内容来实现的。
假设目录demodir中有一个文件y,y文件对应的i-节点号是491。从系统角度来看,目录中有一个包含文件名y和i-节点号为491的入口,也可以说目录demodir中有一个指向i-节点号为491的链接,这个链接所附加的文件名为y。
说来说去这么绕,书上有一句结论概括地很好:目录包含的是文件的引用,每个引用被成为链接。文件的内容存储在数据块,文件的属性被记录在一个被称为i-节点的结构中,i-节点的编号和文件名存储在目录中。就像我前面说的,目录本质上是记录对应关系。
了解了pwd的内核属性后,我们来实现一下pwd。
此程序中使用递归语句实现,从当前目录不断循环深入直到”.”与”..”的i-节点号相同,即代表到达根目录,再由内向外打印出文件夹名。
逻辑不难理解,有一点需要注意,通过stat结构体我们可以获得”.”与”..”目录的i-节点号,我们需要通过节点号获取文件夹名。有点类似于一个trick,通过获得父目录的DIR结构体,取出父目录中包含当前目录在内的文件及文件夹的dirent结构体,通过比对结构体中d_ino字段与当前目录的inode,找到此inode所对应的的d_name即可。因此,程序中chdir必须在num_to_name之前。
/*pwd.c -print the directory where you in*/
#include "stdio.h"
#include "string.h"
#include "dirent.h"
#include "sys/stat.h"
#include "unistd.h"
#include "stdlib.h"
#define SIZE 100
ino_t get_inode(char *path);
void print_path(ino_t cur_inode);
void num_to_name(ino_t inode, char *name);
int main(int argc, char const *argv[])
{
ino_t cur_inode;
cur_inode = get_inode(".");
print_path(cur_inode);
printf("\n");
return 0;
}
ino_t get_inode(char *path){
struct stat info;
if(stat(path, &info) == -1){
perror("Get Inode");
exit(1);
}
return info.st_ino;
}
void print_path(ino_t cur_inode){
char dname[SIZE];
ino_t next_inode;
if(cur_inode != get_inode("..")){
chdir("..");
num_to_name(cur_inode, dname); //必须放在chdir之后,要从父目录中找到innode和文件夹名的对应关系
next_inode = get_inode(".");
print_path(next_inode);
printf("/%s", dname);
}
}
void num_to_name(ino_t inode, char *name){
DIR *dir_ptr;
struct dirent *direntp;
dir_ptr = opendir(".");
if(dir_ptr == NULL){
perror("opendir");
exit(1);
}
while((direntp = readdir(dir_ptr)) != NULL){
if(direntp->d_ino == inode)
{
strcpy(name, direntp->d_name);
closedir(dir_ptr);
return;
}
}
perror("Can not fine with the innode");
exit(1);
}