8.6.1 管道文件系统pipefs

pipefs是一种简单的、虚拟的文件系统类型,因为它没有对应的物理设备,因此其安装时不需要块设备,在第10章将看到,大部分文件系统是以模块的形成来实现的。该文件系统相关的代码在fs/pipe.c中:

  

static DECLARE_FSTYPE(pipe_fs_type, "pipefs", pipefs_read_super,

                      FS_NOMOUNT|FS_SINGLE);

 

static int __init init_pipe_fs(void)

{

         int err = register_filesystem(&pipe_fs_type);

         if (!err) {

                 pipe_mnt = kern_mount(&pipe_fs_type);

                 err = PTR_ERR(pipe_mnt);

                 if (IS_ERR(pipe_mnt))

                         unregister_filesystem(&pipe_fs_type);

                 else

                         err = 0;

         }

         return err;

}

 

static void __exit exit_pipe_fs(void)

{

        unregister_filesystem(&pipe_fs_type);

        mntput(pipe_mnt);

    }

 

module_init(init_pipe_fs)

module_exit(exit_pipe_fs)

 

pipefs文件系统是作为一个模块来安装的,其中module_init()是模块的初始化函数,module_exit()是模块的卸载函数,其更详细的解释将在第10章给出。

DECLARE_FSTYPE()宏定义可以看出,pipefs文件系统的FS_NOMOUNTFS_SINGLE标志位为1,这就意味着该文件系统不能从用户空间进行安装,并且在整个系统范围内只有一个超级块。FS_SINGLE标志也意味着在通过register_filesystem()成功地注册了该文件系统后,应该通过kern_mount()来安装。

register_filesystem()函数把pipe_fs_type链接到file_systems链表,因此,你可以通过读/proc/filesystems找到“pipefs”入口点,在那里,“nodev”标志表示没有设置FS_REQUIRES_DEV标志,即该文件系统没有对应的物理设备。

kern_mount()类似于do_mount(),用来安装pipefs文件系统。当安装出现错误时,则调用unregister_filesystem()把pipe_fs_typefile_systems链表中拆除。

   现在,pipefs文件系统已被注册,并成为内核中的一个模块,从此我们就可以使用它了。Pipefs文件系统的入口点就是pipe()系统调用,其内核实现函数为sys_pipe(),而真正的工作是调用do_pipe()函数来完成的,其代码在/fs/pipe.c中,我们并同时给出了对代码的注释。

  int do_pipe(int *fd)

   {

         struct qstr this;

         char name[32];

         struct dentry *dentry;

         struct inode * inode;

             struct file *f1, *f2;   /*进程对每个已打开文件的操作是通过file结构进行的。

一个管道实际上就是一个存在于内存的文件,

对这个文件的操作要通过两个已打开的文件进行,

f1f2分别代表该管道的两端。*/

         int error;

        int i,j;

 

         error = -ENFILE;

        f1 = get_empty_filp();         /*管道两端各分配一个file 结构*/

 

         if (!f1)

                 goto no_files;

 

         f2 = get_empty_filp();

         if (!f2)

                 goto close_f1;

 

               inode = get_pipe_inode();  /*每个文件都有一个inode结构。由于管道文件在管道创建之前并不存在,因此,在创建管道时临时创建一个inode结构。*/

 

         if (!inode)

                 goto close_f12;

 

         error = get_unused_fd();      * 分配打开文件号*/

       if (error < 0)

                 goto close_f12_inode;

         i = error;

 

         error = get_unused_fd();

         if (error < 0)

                 goto close_f12_inode_i;

         j = error;

 

         error = -ENOMEM;

         sprintf(name, "[%lu]", inode->i_ino);

         this.name = name;

         this.len = strlen(name);

         this.hash = inode->i_ino; /* will go */

         dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this);  /* File结构中有个指针f_dentry指向所打开文件的目录项dentry结构,而dentry 中有个指针指向相应的inode结构。所以,调用d_alloc()分配一个目录项是为了把file结构与inode结构联系起来。*

 

         if (!dentry)

                 goto close_f12_inode_i_j;

        dentry->d_op = &pipefs_dentry_operations; 

 

         d_add(dentry, inode);  *使已分配的inode 结构与已分配的目录项结构挂勾*

                               

        f1->f_vfsmnt = f2->f_vfsmnt = mntget(mntget(pipe_mnt));   /* pipe_mnt就是在init_pipe_fs()中所获得的指向vfsmount结构的指针,因为这个结构多了两个使用者,因此调用两次mntget   ()使其引用计数加2 *

 

        f1->f_dentry = f2->f_dentry = dget(dentry); *让两个已打开文件中的f_dentry指针都指向这个目录项,并使目录项的引用计数加1*

                               

                       /* read file */

         f1->f_pos = f2->f_pos = 0;

         f1->f_flags = O_RDONLY;

         f1->f_op = &read_pipe_fops;

         f1->f_mode = 1;

         f1->f_version = 0;

 

         /* write file */

         f2->f_flags = O_WRONLY;

         f2->f_op = &write_pipe_fops;

         f2->f_mode = 2;

         f2->f_version = 0;

 

             fd_install(i, f1);                              /*将已打开文件结构与分配得的打开文件号相关联(打开文件号只在一个进程的范围类有效)。*/

 

         fd_install(j, f2); 

         fd[0] = i;    *使得fd[0]为管道读出端的打开文件号*/

         fd[1] = j;    *使得fd[1]为管道写出端的打开文件号*/

 

        return 0;

    *以下为释放各种资源*/

 close_f12_inode_i_j:

         put_unused_fd(j);

 close_f12_inode_i:

         put_unused_fd(i);

 close_f12_inode:

         free_page((unsigned long) PIPE_BASE(*inode));

         kfree(inode->i_pipe);

         inode->i_pipe = NULL;

         iput(inode);

 close_f12:

         put_filp(f2);

 close_f1:

         put_filp(f1);

no_files:

        return error;  

 }

     下面对管道的单向性再做进一步的说明。从代码看出,把f1一端设置成“只读(O_RDONLY)”,另一端则设置成“只写(O_WRONLY)”。同时,两端的文件操作也分别设置成read_pipe_fopswrite_pipe_fops,其定义于pipe.c中:

     struct file_operations read_pipe_fops = {

         llseek:         pipe_lseek,

         read:           pipe_read,

         write:          bad_pipe_w,

         poll:           pipe_poll,

         ioctl:          pipe_ioctl,

         open:           pipe_read_open,

        release:        pipe_read_release,

 };

 

struct file_operations write_pipe_fops = {

         llseek:         pipe_lseek,

         read:           bad_pipe_r,

         write:          pipe_write,

         poll:           pipe_poll,

         ioctl:          pipe_ioctl,

         open:           pipe_write_open,

         release:        pipe_write_release,

 };

     read_pipe_fops()中的写操作函数为bad_pipe_w(),而在write_pipe_fops()中的读操作函数为bad_pipe_r(),这两个函数分别返回一个出错代码。尽管代表着管道两端的两个已打开文件一个只能读,一个只能写。但是,另一方面,这两个逻辑上已打开的文件指向同一个inode,即用作管道的缓冲区,显然,这个缓冲区既支持读也支持写。这进一步说明了file inodedentry之间的不同和联系。