(萊昂氏unix原始碼分析導讀-43) 檔案系統的mount
By cszhao1980
當一個裝置被mount進系統,就會在“mount表”中佔據一個表項,mount表的定義如下:
0272: struct mount
0273: {
0274: int m_dev /* device mounted */
0275: int *m_bufp; /* pointer to superblock */
0276: int *m_inodp; /* pointer to mounted on inode */
0277: } mount[NMOUNT];
m_dev記錄被mount
m_bufp指向一個快取,其內容是裝置的“超級塊”;
m_inodep指向所謂的“mount on” inode,我們會在後面的討論中對其進一步瞭解。
首先,我們來看一看root裝置是怎樣被mount進系統的。
首先,rootdev的定義如下:
4695: int rootdev {(0<<8)|0};
顯然,其裝置號為0,即我們的RK磁碟裝置。
然後,在main()函式中呼叫iinit()函式——它負責把rootdev Load進系統:
1615: iinit();
6922: iinit()
6923: {
6924: register *cp, *bp;
6925:
6926: (*bdevsw[rootdev.d_major].d_open)(rootdev, 1);
6927: bp = bread(rootdev, 1);
6928: cp = getblk(NODEV);
6929: if(u.u_error)
6930: panic("iinit");
6931: bcopy(bp->b_addr, cp->b_addr, 256);
6932: brelse(bp);
6933: mount[0].m_bufp = cp;
6934: mount[0].m_dev = rootdev;
6935: cp = cp->b_addr;
6936: cp->s_flock = 0;
6937: cp->s_ilock = 0;
6938: cp->s_ronly = 0;
6939: time[0] = cp->s_time[0];
6940: time[1] = cp->s_time[1];
6941: }
如果您對上一章的內容比較清楚的話,iinit()函式就顯得很簡單了:
(1) 利用塊裝置表呼叫rootdev(RK磁碟)的d_open函式,如果您還記得的話,
該函式是個空函式,也就是說對RK磁碟來說,無需進行open操作,就直接可用;
(2) 然後,呼叫bread()函式將rootdev的#1塊(即超級塊)讀入快取;
(3) 接著它又呼叫getblk(NODEV)申請了一個“空閒裝置”快取——該快取沒有與任何
實際裝置相關連。接著將超級塊的內容通過bcopy函式,拷貝到給快取中。
(4) 呼叫brelse函式,將rootdev快取送還AV佇列;
(5) 將mount表的#0表項分配給rootdev,其m_dev設定為rootdev(0),而m_bufp指向裝
有超級塊內容的“空閒裝置”快取;
(6) 接下來的操作就跟超級塊的內容有關了,我們接下來就看一下超級塊。
超級塊的內容如下所示:
5561: struct filsys
5562: {
5563: int s_isize; /* size in blocks of I list */
5564: int s_fsize; /* size in blocks of entire volume */
5565: int s_nfree; /* number of in core free blocks (0-100) */
5566:
5567: int s_free[100]; /* in core free blocks */
5568: int s_ninode; /* number of in core I nodes (0-100) */
5569: int s_inode[100]; /* in core free I nodes */
5570: char s_flock; /* lock during free list manipulation */
5571: char s_ilock; /* lock during I list manipulation */
5572: char s_fmod; /* super block modified flag */
5573: char s_ronly; /* mounted read-only flag */
5574: int s_time[2]; /* current date of last update */
5575: int pad[50];
5576: };
內容不少,我們先了解其中幾項:
(1) s_isize:即有多少個塊是用來存放inde列表的;
(2) s_fsize:整個檔案系統共有多少個塊;
(3) s_ninode:讀入記憶體的inode項數;
(4) s_inode[100]:存放讀入記憶體的indode項;
在上述操作中,並沒有設定mount陣列項中所謂的“mount on”inode指標——這時因為rootdev
的mount是很“自然”的事,它的檔案系統根目錄,直接變成了系統根目錄,沒有mount點之說。
下面我們來看smount ()函式——它用來處理mount sys call,即通過該函式可以將一個裝置mount到系統中。
mount的過程和上述過程類似,萊昂解釋的也比較清晰,我只簡單說幾點。
1. 引數傳遞
smount有三個引數,都通過.u_arg[]陣列傳遞:
(1) u_arg[0] (u.dirp) ——指向一個Path name字串,該Path name的檔案inode-i_dev為要mount的
裝置的裝置號;smount會呼叫getmdev()函式來得到裝置id;
(該檔案應該為一裝置描述檔案,也許就是“特殊塊裝置”(IFBLK)檔案);
(2) u_arg[1] ——指向“要mount到的”目錄的Path name;
(3) u_arg[2] —— read only flag;
2. mount點
被mount到的目錄被稱為mount點,該目錄的inode會設定IMOUNT標誌。
3. IFBLK/ IFCHR分別為特殊塊裝置、特殊字元裝置標誌;
很自然的,我們接下來看一下sumount函式,即unmount sys call的處理函式。
我們基本上可以將其理解為smount的“反”函式,即:
(1) 將該裝置從系統mount表中刪除;
(2) 將mount點的IMOUNT標誌清除;
(3) 釋放該檔案系統的超級塊。
除此之外,還需要注意兩點:
(1) 在unmount裝置時,會Loop系統的inode陣列,如果陣列項中有該裝置上的檔案
(即該檔案處理active狀態),則禁止unmount裝置;
(2) 在所有操作之前,呼叫update()進行了檔案系統更新。
update(7201)執行三種更新:
(1) Loop mount表,將每個檔案系統的超級塊(記憶體中的)的內容寫回磁碟;
(2) Loop inode陣列,將inode寫回磁碟;
(3) 呼叫bflush,將“延遲寫”塊寫入磁碟。