Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1691854
  • 博文数量: 177
  • 博客积分: 9416
  • 博客等级: 中将
  • 技术积分: 2513
  • 用 户 组: 普通用户
  • 注册时间: 2006-01-06 16:08
文章分类

全部博文(177)

文章存档

2013年(4)

2012年(13)

2011年(9)

2010年(71)

2009年(12)

2008年(11)

2007年(32)

2006年(25)

分类:

2008-01-13 14:53:02

一个物理磁盘可以由磁盘驱动分区出来的多个逻辑区组成,每个部分都有一个设备文件名。进程通过打开相应的设备文件来访问数据,并将其当作顺序的磁盘块来访问。每个逻辑区可能包含一个逻辑文件系统,包括boot块,super块,inode列表,数据块。
 
为了将一个逻辑区里面的文件系统连接到现有的文件系统中,需要使用mount(),而断开这个连接需要用umount()。下面我们就来讨论一下这两个函数。
 
mount文件系统
需要指定所要连接的源FS的设备文件,目的FS路径,以及mount选项。原型如下:
int mount(special pathname, directory pathname, options);
 
mount前后文件系统如下图所示:

 
为了保存mount信息,内核中有一个mount表来存放这些信息,一个表项对应一个已mount上的FS。每个表项包括:
  1. 设备号。源FS的设备号;
  2. 指向包含源FS超级块的缓冲的指针;
  3. 源FS根目录的inode指针;
  4. mount点的inode指针。
一个表项如下所示:
--------------------------------------
|dev_no|sb_ptr|src_pinode|dest_pinode|
--------------------------------------
         |          |           |
         |          |           ->指向mount点的inode
         |          ->指向源FS的inode
         ->指向源FS的super block
 
mount点的inode与mount源FS的inode之间由mount()建立的关联关系允许内核遍历文件系统而忽略mount这个事实。而这个关联,正是mount()和umount()的核心部分。
 
mount()的伪代码如下:
ReturnState mount(string devFileName, string mountPoint, MountOptions options)
{
    if(非超级用户)
        return error;
    INode * pDev = namei(devFileName);
    检查是否合法;
    INode * pMntPoint = namei(mountPoint);
    if (不是目录,或者引用计数大于1)
    {
        iput(pMntPoint);
        iput(pDev);
        return error;
    }
    从mount表中找到一个空槽;
    pDev->open();
    Buffer * pBuf = getblk();
    将super block读取到pBuf中;
    初始化super block各个域;
    iget()获取源FS的根目录inode,并存到mount表中;
    将mount point的inode标记上;
    iput(pDev);
    unlock(pMntPoint);
    return success;
}

执行完之后内核数据结构如下图所示(图片摘自《UNIX操作系统设计》中文版):

可以看出,图中“安装表”里建立好了mount point和源FS的根目录的inode之间的关联。

unmount文件系统
为了断开断开文件系统的连接,使用umount()函数。只需要指定源FS的设备文件名就可以查找到mount表项,解除该表项所代表的关联关系就可以完成umount(),如下所示:
ReturnState umount(string devPathName)
{
    if (!IsSuperUser())
    {
       return error;
    }
    INode * pDev = namei(devPathName);
    从pDev中获取主、次设备号;
    根据主、次设备号查找mount表项;
    iput(pDev);
    清除共享;
    更新super block,inode,清空缓冲;
    if (有文件正在使用)
    {
       return error;
    }
    从表项中去要unmount的FS的inode;
    Lock()该inode;
    iput()该inode;
    关闭还有该FS的设备;
    使该FS的缓冲在buffer cache中失效;
    获取mount point的inode——pMntPointInode;
    Lock(pMntPointInode);
    清除mount point标志;
    iput(pMntPointInode);
    释放super block占用的buffer(brelse());
    释放mount表项;
}

在umount()期间,需要将所有未写到磁盘的数据flush到磁盘上,同时将拆除的FS的buffer放入到buffer cache空闲buffer链表的首部,以使其尽快被利用,来减少其他有效buffer的频繁释放分配。

跨mount point的操作
由于mount point的存在,一个文件操作很可能是操作了跨mount point的文件,因此,需要对以前的一些函数做修订以满足该要求。最主要的就是iget()和namei()这两个,需要在其中加入对mount表项的处理,使得对mount point的inode的请求可以映射到mount的FS的请求。

对于iget(),参考“UNIX内核(4):inode及其相关操作”,其中对于iget()的描述,省略了对mount表的处理。对于那个iget(),添加下面的逻辑:
if (IsMountPoint(pInode))
{
    MountEntry * pEntry = FindEntry(pInode);
    INode * pFS = pEntry->mountedFsInode;
    pInode = pFs;
    continue;
}

对于namei(),参考“UNIX内核(5):inode与文件、目录”,在其中namei()的描述中加上对mount表的处理就OK了:
if (找到的inode是根,工作inode是根且分量为"..")
{
    MountEntry * pEntry = FindEntry(pInode);
    iput(pWorkingINode);
    pWorkingINode = pEntry->MountPointInode;
    Lock(pEntry->MountPointInode);
    ++pWorkingINode->refCount;
    继续查找"..";
}

因为在namei()先要调用iget(),因此此处添加的处理只需要是从文件系统层次中下层到上层查找时的映射处理。

PS. 从FS系统调用系列开始,笔者认为文章质量开始下降,描述很粗糙很笼统,因此打算暂停该系列,反思一段时间,以便写出质量高一些的文章。

参考:
The Design of The UNIX Operation System, by Maurice J. Bach
Linux Kernel Source Code v0.99.15, by Linus Torvalds
Linux Kernel Source Code v2.6.22, by Linus Torvalds and Linux community.
Understanding The Linux Kernel, 3rd edition, by Daniel P. Bovet, Marco Cesati
 
Copyleft (C) 2007, 2008 raof01. 本文可以用于除商业用途外的所有用途。若要用于商业用途,请与作者联系。
阅读(3277) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

fera2008-01-14 12:35:55

越来越像文抄公了……sigh……