小生想从源码的角度解释一下Unix的文件系统。(前天因为不能发在首页所以重发,为什么有3天的限制呢,真是的哦)
首先一个文件在没有被打开的情况下,需要什么贮存在磁盘呢?
一般而言,需要的是:
1,一个目录项
2,一个磁盘Inode项
3,磁盘上的n块(n可为0)
目录项是一个结构,最重要的元素一个为名字,一个为指向inode结构的指针。另外目录的执行权是为了把文件名转换为Inode而存在的,具体的操作函数为namei。(大家可参阅具体的源码)
Inode项也是一个结构,v7实现如下
struct dinode
{
unsigned short di_mode; /* mode and type of file */
short di_nlink; /* number of links to file */
short di_uid; /* owner's user id */
short di_gid; /* owner's group id */
off_t di_size; /* number of bytes in file */
char di_addr[40]; /* disk block addresses */
time_t di_atime; /* time last accessed */
time_t di_mtime; /* time last modified */
time_t di_ctime; /* time created */
};
元素所表示的内容相关的注释都已经说明了,不过小生还是介绍一下具体的内容
di_mode说明文件的权限和类型
di_nlink说明文件的链接数
di_uid文件的用户,di_gid文件的组
di_size逻辑上文件的大小(以字节计数)
di_addr文件所占用的磁盘块的地址
3个时间表示访问,修改和创建时间
磁盘块没什么好说的,具体实现可能512字节一块
当引用一个文件时(并非打开一个文件)可能需要的如下:
1, 一个内存Inode项
内存inode项的v7实现可能如下:
struct inode
{
Char i_flag;
char i_count; /* reference count */
dev_t i_dev; /* device where inode resides */
ino_t i_number; /* i number, 1-to-1 with device address */
unsigned short i_mode;
short i_nlink; /* directory entries */
short i_uid; /* owner */
short i_gid; /* group of owner */
off_t i_size; /* size of file */
union {
struct {
daddr_t i_addr[NADDR]; /* if normal file/directory */
daddr_t i_lastr; /* last logical block read (for read-ahead) */
};
struct {
daddr_t i_rdev; /* i_addr[0] */
struct group i_group; /* multiplexor group file */
};
} i_un;
};
基本的内容和磁盘的一样,多出了一些内容,最重要的是多出了i_count用以记录引用次数
当一个文件被打开时需要的内容如下:
1, 用户u区中的u_ofile数组中的一项
2, 文件表中的一项
u_ofile存放的是指向具体的file结构中的一项的指针,所谓的文件描述符就是u_ofile数组的下标
文件表v7的实现如下:
struct file
{
char f_flag;
char f_count; /* reference count */
struct inode *f_inode; /* pointer to inode structure */
union {
off_t f_offset; /* read/write character pointer */
struct chan *f_chan; /* mpx channel pointer */
} f_un;
};
文件表最初似乎是为了共享偏移量(f_offset)而设计的,因为有注释就不多赘叙了,正如大家所知道的,用dup复制一个文件描述符和再次打开一个文件是不一样,具体的差别就是f_count的值。实际上大家可以自己思考一下所有的文件打开关闭,link和unlink等等文件操作(我想这是非常有益,不过大家不必真的去参阅源码,只要自己想想是怎么回事就行,unix源码水平极高,如果真的全部看懂需要很多时间)。
另外鉴于要不发布要不烂掉的原则,我将本人昨天晚上写的源码发布。
源码是find命令的简单实现,直接的参数是-name, -user, -group,具体用法如下
Find 目录(文件)列表 可选的参数
源码的水平并不高,而且实现也不是很好,不过我想初学c的读者还是能得到一定的帮助的,
如果有高手觉得哪里需要修改的,请注明
1 /* 2 * 作者: 莫尘 欢迎大家指出bug和修改扩展程序 3 * 用法 find dir1 ... dirn -name filename -user username -group groupname 4 */ 5 6 #include <pwd.h> 7 #include <grp.h> 8 #include <fts.h> 9 #include <sys/stat.h> 10 #include <stdio.h> 11 #include <errno.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 static char *mc_prog_name; /* 程序的名字, 不包括"./"这两个字符 */ 16 static char *mc_dot_av[] = { ".", NULL }; /* 当没有指定的目录或文件时,在当前目录查找 */ 17 static char *mc_file_name, *mc_user_name, *mc_group_name; /* 参数所指定的文件名,用户名, 组名 */ 18 19 static void mc_do_find(char **path_argv); /* 在指定位置根据参数查找文件或者目录 */ 20 21 int 22 main(int argc, char *argv[]) 23 { 24 char *p, **tmp, **pathav; 25 26 mc_prog_name = (p = strchr(*argv, '/')) ? ++p : *argv; 27 p = *argv; 28 pathav = argv + 1; /* pathav指向指定的目录或者文件 */ 29 tmp = (char **)NULL; /* 为了以后面呼应 */ 30 while(--argc > 0 && (*++argv)[0] != '-') 31 ; 32 if(argc == 0){ /* 以下几行判断要查找的文件或者目录 */ 33 if(strcmp(*argv, p) == 0) 34 pathav = mc_dot_av; 35 } 36 else if(strcmp(argv[-1], p) == 0) 37 pathav = mc_dot_av; 38 else if(argv[0][0] == '-') 39 tmp = argv; 40 while(--argc > 0 && (*argv)[0] == '-'){ 41 if(strcmp(argv[0] + 1, "group") == 0) 42 mc_group_name = *++argv; 43 else if(strcmp(argv[0] + 1, "name") == 0) 44 mc_file_name = *++argv; 45 else if(strcmp(argv[0] + 1, "user") == 0) 46 mc_user_name = *++argv; 47 else{ 48 fprintf(stderr, "%s: unknow predicate '%s'\n", mc_prog_name, argv[0]); 49 exit(1); 50 } 51 ++argv; 52 } 53 if(tmp) 54 *tmp = NULL; /* 为了fts_open的调用规则而采取的动作 */ 55 mc_do_find(pathav); 56 return 0; 57 } 58 59 static void 60 mc_do_find(char **path_argv) 61 { 62 FTS *ftsp; 63 int fts_options; 64 register FTSENT *p; 65 66 fts_options = 0; /* 程序默认采用了0来调用fts_open, 不过为了能够扩展,所以使用了一个变量 */ 67 ftsp = fts_open(path_argv, fts_options, NULL); /* fts_open和fts_read具体参阅头文件和man fts_open,fts函数还是不错的,本人的ls命令也用到了 */ 68 while(p = fts_read(ftsp)){ 69 if(p->fts_info == FTS_DC){ /* 目录中不能存在循环 */ 70 fprintf(stderr, "%s: a cycle in the tree\n", mc_prog_name); 71 exit(1); 72 } 73 if(p->fts_info == FTS_ERR || p->fts_info == FTS_DNR 74 || p->fts_info == FTS_NS){ 75 errno = p->fts_errno; /* fts函数不会这是errno */ 76 fprintf(stderr, "%s: %s: %s\n", 77 mc_prog_name, p->fts_name, strerror(errno)); 78 continue; 79 } 80 if(p->fts_info == FTS_F || p->fts_info == FTS_D || p->fts_info == FTS_SL){ 81 if(mc_file_name){ 82 if(strcmp(mc_file_name, p->fts_name)) 83 continue; 84 } 85 if(mc_user_name){ /* getpwnam和getgrnam参阅联机帮助 */ 86 static struct passwd *wp; 87 88 if(wp == NULL){ 89 wp = getpwnam(mc_user_name); 90 if(wp == NULL){ 91 fprintf(stderr, "%s: '%s' is not the name of a know user\n", 92 mc_prog_name, mc_user_name); 93 exit(1); 94 } 95 } 96 if(wp->pw_uid != p->fts_statp->st_uid) 97 continue; 98 } 99 if(mc_group_name){ 100 static struct group *gp; 101 102 if(gp == NULL){ 103 gp = getgrnam(mc_group_name); 104 if(gp == NULL){ 105 fprintf(stderr, "%s: '%s' is not the name of a know group\n", 106 mc_prog_name, mc_group_name); 107 exit(1); 108 } 109 } 110 if(gp->gr_gid != p->fts_statp->st_gid) 111 continue; 112 } 113 printf("%s\n", p->fts_path); 114 } 115 } 116 fts_close(ftsp); /* fts自动释放内存 */ 117 } 118 119