unix系统中的文件解析(附:find命令的简单实现)

小生想从源码的角度解释一下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             

 

你可能感兴趣的:(unix)