日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁 > 單片機 > 程序喵大人

MOUNT

的文件系統(tǒng)中,有個很重要的概念就是掛載,掛載大家應(yīng)該都很熟悉,除了根文件系統(tǒng),其他所有文件系統(tǒng)都要先掛載到根文件系統(tǒng)中的某個目錄之后才能訪問。

所謂的根文件系統(tǒng)就是系統(tǒng)啟動的時候安裝的第一個文件系統(tǒng),它也是內(nèi)核映像所在的文件系統(tǒng)。而掛載到某個目錄的某個目錄就是所謂的掛載點。

中有專門的命令來掛載文件系統(tǒng),mount device dir, 為要掛載的設(shè)備文件名, 為掛載點。這里所說的設(shè)備不是真的指單個實體設(shè)備,而是其上的邏輯設(shè)備,比如說一個磁盤上的不同分區(qū)都可以看作是不同的設(shè)備。

每個設(shè)備都有一個設(shè)備號來標識,設(shè)備號可以分為兩部分,一部分叫做主設(shè)備號 ,它用來標識某一類型的設(shè)備,比如說磁盤。另一部分叫做次設(shè)備號 ,它來標識某一具體設(shè)備,比如說磁盤上的某一具體分區(qū)。

當(dāng)文件系統(tǒng)掛載到某個目錄后,我們就可以通過這個目錄來訪問該文件系統(tǒng),很多地方就只是簡單的這樣講了一下,但其實這只能說是掛載的作用,那到底什么是掛載,要想解決這個問題還是只能從源碼著手。下面我將根據(jù) 的代碼來講述掛載,涉及的東西比較多,我們只討論相關(guān)的部分。

數(shù)據(jù)結(jié)構(gòu)

m_inode,內(nèi)存中的 inode

struct m_inode { /********略*********/ unsigned short i_mode; //文件類型和屬性 unsigned char i_mount; //是否有文件系統(tǒng)掛載到這兒 unsigned short i_zone[9]; //索引取,如果是塊/字符設(shè)備文件i_zone[0]是設(shè)備號 /********略*********/ };

super_block,內(nèi)存中的超級塊

struct super_block { /********略*********/ unsigned short s_magic; //文件系統(tǒng)魔數(shù) /********略*********/ unsigned short s_dev; //設(shè)備號 /********略*********/ struct m_inode * s_isup; //被掛載的文件系統(tǒng)的根目錄inode struct m_inode * s_imount; //該文件系統(tǒng)被安裝到此inode };

所謂的內(nèi)存中的超級塊和 指的是兩者在內(nèi)存中的緩存:

struct super_block super_block[NR_SUPER]; #define NR_SUPER 8 struct m_inode inode_table[NR_INODE]; #define NR_INODE 32 

可以看出在 里面內(nèi)存中最多同時存在 8 個超級塊和 32 個文件的 。這個緩存與我們平時所說的緩存差不多,當(dāng)系統(tǒng)要想獲取一個 時,會先在 中尋找有沒有該 ,如果有的話就直接返回,如果沒有,就從設(shè)備上將 讀到 之后再返回。

相關(guān)操作

在看 掛載之前,先來看看一些操作函數(shù)。

static struct super_block * read_super(int dev);

如果緩存區(qū)中沒有該設(shè)備的超級塊,則先找一個空閑的超級塊槽,然后從設(shè)備上讀取超級塊到找到的空閑超級塊槽。如果該設(shè)備的超級塊已經(jīng)在緩存區(qū)中且數(shù)據(jù)有效,則直接返回該超級塊的指針。

struct super_block * get_super(int dev);

根據(jù)設(shè)備號 從超級塊數(shù)組當(dāng)中獲取超級塊。

void put_super(int dev);

釋放指定的設(shè)備超級塊,也就是將超級塊的 字段清 0,如此使得該超級塊槽空閑出來。

struct m_inode * namei(const char * pathname);

函數(shù)根據(jù)路徑 獲取末尾文件的 ,這個函數(shù)應(yīng)該是有印象的吧,在 中也有類似的函數(shù)。如果不太清楚的話,我這里舉個例子:如果參數(shù)路徑是/a/b/c,調(diào)用 之后就會返回文件

掛載

掛載的實現(xiàn)還是挺簡單的,來看代碼

int sys_mount(char * dev_name, char * dir_name, int rw_flag) //將名稱為dev_name的設(shè)備上的文件系統(tǒng)掛載到目錄dir_name上 { struct m_inode * dev_i, * dir_i; struct super_block * sb; int dev; if (!(dev_i=namei(dev_name))) //沒有找到該設(shè)備 return -ENOENT;
 dev = dev_i->i_zone[0]; if (!S_ISBLK(dev_i->i_mode)) { //不是塊設(shè)備 iput(dev_i); return -EPERM;
 }
 iput(dev_i); //釋放該設(shè)備的inode if (!(dir_i=namei(dir_name))) //解析獲取掛載點的inode return -ENOENT; if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO) { //如果掛載點的引用數(shù)不等于1獲取掛載點為根目錄 iput(dir_i); return -EBUSY;
 } if (!S_ISDIR(dir_i->i_mode)) { //掛載點不是目錄 iput(dir_i); return -EPERM;
 } if (!(sb=read_super(dev))) { //將設(shè)備上的文件系統(tǒng)的超級塊讀取到內(nèi)存中 iput(dir_i); return -EBUSY;
 } if (sb->s_imount) { //如果該文件系統(tǒng)已掛載 iput(dir_i); return -EBUSY;
 } if (dir_i->i_mount) { //如果掛載點已經(jīng)掛載了其他文件系統(tǒng) iput(dir_i); return -EPERM;
 }
 sb->s_imount=dir_i; //將掛載點的inode記錄到設(shè)備的超級塊中 dir_i->i_mount=1; //表示該掛載點已經(jīng)掛載了該文件系統(tǒng) dir_i->i_dirt=1; /* NOTE! we don't iput(dir_i) */ //我們不會釋放掛載點的inode return 0; /* we do that in umount */ //我們在umount卸載文件系統(tǒng)時釋放 }

其流程圖為:

上圖為 的實現(xiàn)過程,除了各種檢查之外, 實際上只做了兩件事:

  1. 將要掛載的文件系統(tǒng)的超級塊讀到內(nèi)存里的超級塊槽
  2. 設(shè)置該超級塊的 字段為 掛載點的 ,表示文件系統(tǒng)掛載到這個目錄上,設(shè)置掛載點 字段為 1 表示該目錄上掛載的有文件系統(tǒng)

另外再解釋幾點:

  1. 字符設(shè)備,提供連續(xù)的數(shù)據(jù)流,應(yīng)用程序可以順序讀取數(shù)據(jù),通常不支持隨機存取,像前面提到過的鍵盤串口都屬于字符設(shè)備。塊設(shè)備,應(yīng)用程序能夠隨機訪問設(shè)備的數(shù)據(jù),程序可以自行確定數(shù)據(jù)的位置,比如說硬盤就是典型的塊設(shè)備。很明顯地文件系統(tǒng)只能存放在塊設(shè)備上。
  2. 掛載點只能是個目錄文件,文件系統(tǒng)都是掛載到目錄上的
  3. 掛載文件系統(tǒng)時,掛載點這個目錄文件只能在當(dāng)前引用,也就是說在掛載文件系統(tǒng)的時候,還有其他地方正在使用掛載點目錄的話就不對

這就是掛載的本質(zhì),有沒有感覺簡單的同時又還是模模糊糊的?為什么將文件系統(tǒng)掛載到某個目錄之后,這個目錄就能表示被掛載的文件系統(tǒng)。解決這個問題還是要再來捋捋文件系統(tǒng)是如何尋找一個文件的,也就是 函數(shù),比如說給定一個路徑/a/b,這是一個絕對路徑,如何從最開始的根目錄尋到文件 的呢?

這個問題我在 文件系統(tǒng)里面也詳細說過, 里也類似。這里我們假設(shè) 文件都是目錄文件, 是一個普通文件。首先根目錄文件就是一個個目錄項,在其中尋找文件名為 的目錄項,從中獲取 目錄文件的 ,根據(jù) 的索引字段找到 目錄文件的數(shù)據(jù),也是一個個目錄項,在其中尋找文件名為 的目錄項,從中獲取普通文件 然后返回。

上述所說的獲取某個 ,使用的函數(shù)是,,其意為從設(shè)備 中獲取編號為 。這個函數(shù)就會判斷編號為 上是否掛載的有文件系統(tǒng),來看相關(guān)代碼:

struct m_inode * iget(int dev, int nr){ /*********略**********/ inode = inode_table; //從inode表中第一個元素開始 while (inode < NR_INODE+inode_table) { //掃描內(nèi)存里緩存的inode表 if (inode->i_dev != dev || inode->i_num != nr) { //如果設(shè)備號對不上或者inode編號對不上 inode++; //下一個 continue;
 }
 wait_on_inode(inode); //等待該inode解鎖 if (inode->i_dev != dev || inode->i_num != nr) { //因為等待過程中inode可能會發(fā)生變化,所以再次判斷 inode = inode_table; continue;
 }
 inode->i_count++; //找到了該inode,將其引用數(shù)加1 if (inode->i_mount) { //如果該inode上掛載的有文件系統(tǒng) int i; for (i = 0 ; i//在內(nèi)存中緩存的超級塊中尋找掛載點為當(dāng)前inode的超級塊 if (super_block[i].s_imount==inode) //找到了,break break; if (i >= NR_SUPER) { //沒找到,返回 printk("Mounted inode hasn't got sb\n"); if (empty) 
 iput(empty); return inode;
 }
 iput(inode); //釋放當(dāng)前inode dev = super_block[i].s_dev; //將設(shè)備號重新設(shè)置為被掛載的文件系統(tǒng)所在的設(shè)備號 nr = ROOT_INO; //將要尋找的inode編號重新設(shè)置為根目錄的inode編號 inode = inode_table; //從內(nèi)存的inode表第一個元素重新開始尋找inode continue;
 } if (empty) //釋放臨時找的空閑inode iput(empty); return inode; //返回獲取到的inode }
}

其完整的流程圖如下:

這是我根據(jù)趙炯畫的圖改編,因為沒有詳細講述 的代碼,所以主要關(guān)注虛線方框里面的就行。

如果在 中找到相應(yīng)的 ,就判斷 ,如果為真,表示該 表示的目錄文件上面掛載的有文件系統(tǒng)。此時這個目錄應(yīng)該表示被掛載的文件系統(tǒng)的根目錄,所以設(shè)置 超級塊表示的設(shè)備,,原目錄就被隱藏掉了。舉個例子再說明一下,假如調(diào)用 ,本來我是要獲取 1 號設(shè)備的第 99 個 ,然后發(fā)現(xiàn)這個 指向的目錄上面掛載的有 2 號設(shè)備的文件系統(tǒng),那么我們就去尋找 2 號設(shè)備的根目錄 然后返回。所以看起來調(diào)用 實則調(diào)用的 ,這也就是為什么說將文件系統(tǒng)掛載到某個目錄之后,這個目錄就被屏蔽了的原因所在。

到此,對文件系統(tǒng)的掛載應(yīng)該有個很清晰的認識呢,最后來看看文件系統(tǒng)的卸載,基本上就是掛載的逆操作,來簡單看看:

int sys_umount(char * dev_name) { struct m_inode * inode; struct super_block * sb; int dev; if (!(inode=namei(dev_name))) //解析獲取設(shè)備文件的inode return -ENOENT;
 dev = inode->i_zone[0]; //對于塊/字符設(shè)備,設(shè)備號記錄在i_zone[0] if (!S_ISBLK(inode->i_mode)) { //如果不是塊設(shè)備 iput(inode); return -ENOTBLK;
 }
 iput(inode); //釋放設(shè)備文件的inode if (dev==ROOT_DEV) //如果要卸載的是根文件系統(tǒng) return -EBUSY; if (!(sb=get_super(dev)) || !(sb->s_imount)) //如果沒有獲取到設(shè)備的超級塊或者如果掛載點為空 return -ENOENT; if (!sb->s_imount->i_mount) //如果掛載點的掛載標識為空 printk("Mounted inode has i_mount=0\n"); //檢查是否有進程在使用將要卸載文件系統(tǒng)上的文件 for (inode=inode_table+0 ; inode if (inode->i_dev==dev && inode->i_count) return -EBUSY;
 sb->s_imount->i_mount=0; //掛載標識設(shè)為0 iput(sb->s_imount); //釋放掛載點的inode sb->s_imount = NULL; //超級塊的掛載點字段設(shè)為空 iput(sb->s_isup); //釋放被卸載的文件系統(tǒng)的根目錄inode sb->s_isup = NULL; //根目錄inode字段清0 put_super(dev); //釋放設(shè)備超級塊 sync_dev(dev); //更新的信息同步到設(shè)備 return 0;
}

文件系統(tǒng)的卸載主要就是釋放超級塊,然后將一些字段值復(fù)原,具體見上面注釋就不細說了。

好了本文關(guān)于文件系統(tǒng)的掛載就這么多,所以回到開頭什么是掛載,但從實現(xiàn)上來說,就是將超級塊加載到內(nèi)存里面,因為超級塊就是一個文件系統(tǒng)的元信息集合,超級塊就能代表一個文件系統(tǒng),所以將超級塊加載到內(nèi)存里面,我們就可以認為掛載了相應(yīng)的文件系統(tǒng)。當(dāng)然掛載這個機制不可能就只是靠超級塊是否在內(nèi)存里面來決定實現(xiàn),還需要其他的函數(shù)來輔助,就比如說獲取 函數(shù),這個函數(shù)就會來判斷當(dāng)前獲取的這個 是否為掛載點,如果是,那就需要屏蔽當(dāng)前這個 指向的目錄文件,然后將其替換為被掛載的文件系統(tǒng)的根目錄。這些總總加起來應(yīng)該才算是掛載這個機制的實現(xiàn),而不是說單靠一個 函數(shù)就實現(xiàn)了掛載的機制。

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉