inode 譯成中文就是索引節點。每個存儲設備或存儲設備的分區(存儲設備是硬盤、軟盤、U盤 ... ... )被格式化爲文件系統後,應該有兩部份,一部份是inode,另一部份是Block。Block是用來存儲數據用的。而inode呢,就是用來存儲這些數據的信息,這些信息包括文件大小、屬主、歸屬的用戶組、讀寫權限等。inode爲每個文件進行信息索引,所以就有了inode的數值。操作系統根據指令,能通過inode值最快的找到相對應的文件。
inode本質上就是一個結構體,所以通過對Linux下的ROMFS文件系統代碼inode.c進行分析能夠讓我們瞭解一個文件系統的執行過程需要做什麼樣的工作,從而對操作系統有更深入的瞭解.
Linux下的ROMFS文件系統源碼 inode.c 解析(2)
不用再去翻一遍目錄了😀,圖片可以點擊放大。我本來打算取消粉絲可見的設置。不過,我想想算了。
因爲:
你可以通過粉絲數來判定,有多少和你選一樣作業的朋友,目前90+個同學和你選的題目一樣😂
題外話:
這是我 2015 的大學實驗,倒是沒想到操作系統大作業,這都六七年了還沒換,好好理解一遍,答辯的時候能夠把你的理解說出來,得分也挺高的。大三了吧,祝你們一帆風順,所行即所願。
/*2-1-1*/
/*
* linux/fs/inode.c
*
* (C) 1991 Linus Torvalds
*/
#include <STRING.H> // 字符串頭文件。主要定義了一些有關字符串操作的嵌入函數。
#include <SYS stat.h> // 文件狀態頭文件。含有文件或文件系統狀態結構stat{}和常量。
#include <LINUX sched.h> // 調度程序頭文件,定義了任務結構task_struct、初始任務0 的數據,還有一些有關描述符參數設置和獲取的嵌入式彙編函數宏語句。
#include <LINUX kernel.h> // 內核頭文件。含有一些內核常用函數的原形定義。
#include <LINUX mm.h> // 內存管理頭文件。含有頁面大小定義和一些頁面釋放函數原型。
#include <ASM system.h> // 系統頭文件。定義了設置或修改描述符/中斷門等的嵌入式彙編宏。
struct m_inode inode_table[NR_INODE] = { {0,}, }; // 內存中i_node表(NR_INODE=32 項)
思考:file_table可以打開64個文件(可以考慮一下NR_INODE是否太小,容易產生當機的情況)。
static void read_inode (struct m_inode *inode);
static void write_inode (struct m_inode *inode);
/*2-1-2*代碼解釋及流程圖/
/*等待指定的i_node可用 */
//如果i_node已被鎖定則將當前任務置爲不可中斷的等待狀態。直到該i_node解鎖
鎖定的意思爲:正在被一個用戶使用,執行讀或寫,此時等待的是一個ID(UID),上一個用戶使用完畢後,發出指令後,系統會釋放i_node//
static inline void wait_on_inode (struct m_inode *inode)
{
cli (); //關中斷,先使用可以避免想要的執行被中斷(後邊有比較詳細的解讀)
while (inode->i_lock) // 如果i_node已被鎖定
sleep_on (&inode->i_wait); // 不可中斷的等待狀態,進入睡眠等待
sti (); //開中斷
}
/*2-1-3*代碼解釋及流程圖/
/* 對指定的i 節點上鎖 */
// 先判斷i_node的可用性,如果i_node已被鎖定 則將當前任務置爲不可中斷的等待狀態。
// 直到該i_node解鎖 然後對其上鎖。否則對指定的i_node上鎖鎖定指定的i_node//
static inline void lock_inode (struct m_inode *inode)
{
cli ();
while (inode->i_lock)
sleep_on (&inode->i_wait);
inode->i_lock = 1; // 置鎖定標誌。
sti ();
}
/*2-1-4*代碼解釋及流程圖/
/* 對指定的i_node解鎖 */
// 重置i_node的鎖定標誌並直接喚醒等待此i節點的進程。
static inline void unlock_inode (struct m_inode *inode)
{
inode->i_lock = 0; // 重置i_node的鎖定標誌
wake_up (&inode->i_wait); // 喚醒等待此i_node的進程
}
/*2-1-5*代碼解釋及流程圖/
/* 釋放內存中設備dev 的所有i_node*/
在類Unix操作系統中,設備文件系統允許軟件通過標準輸入輸出系統調用與驅動程序交互,從而簡化了許多任務。
設備文件系統包括設備文件、設備節點、設備特定文件,它們是驅動程序的接口,而在文件系統中,它們就像是普通文件。
在微軟的MS-DOS和 Windows等操作系統中,也有專門的設備文件。
設備dev包括:字符設備、塊設備、僞設備,僞設備又包括的如:
/dev/full、/dev/null、/dev/loop、/dev/zero、/dev/random、/dev/urandom
// 掃描內存中的i_node表數組如果是指定設備使用的i_node就釋放。
void invalidate_inodes (int dev)
{
int i; //定義int型數據i
struct m_inode *inode; //定義指定結構體節點i_node
inode = 0 + inode_table; //指針指向i_node表指針數組的首項
for (i = 0; i < NR_INODE; i++, inode++) // 掃描i_node表指針數組中的所有i節點
{
wait_on_inode (inode); // 等待該i_node可用(解鎖)
if (inode->i_dev == dev) // 如果是指定設備的i_node則進入執行
{
if (inode->i_count) // 如果其引用數不爲0則給出警告(設備還在調用)
printk ("inode in use on removed disk\n\r");
inode->i_dev = inode->i_dirt = 0; // 釋放該i_node(置設備號爲0等)
}
}
}
/*2-1-6*代碼解釋及流程圖/
/* 同步內存(i_node數組)與設備上的所有i_node的信息 */
void sync_inodes (void)
{
int i;
struct m_inode *inode; //定義
inode = 0 + inode_table; // 指針指向i_node表指針數組的首項
for (i = 0; i < NR_INODE; i++, inode++) // 掃描i_node表指針數組中的所有i_node
{
wait_on_inode (inode); // 等待該i_node可用(解鎖)
if (inode->i_dirt && !inode->i_pipe) // 如果該i_node已修改且不是管道節點
(管道流量和流向發生改變的點)
write_inode (inode); // 所有i_node信息寫入磁盤
}
}
管道是linux提供的一種常見的進程通信工具,也是很多shell命令能夠靈活組合產生強大用途的一個重要工具。管道,顧名思義就是個管子,裏面可以流過去很多東西。舉個栗子 ls | morels輸出列出來的文件目錄就通過‘|’這個管道流向了more這個文本瀏覽器。相同的功能我們也可以通過ls > tmp ; tmp > more來完成。實際上管道的功能和第二個方法也很像。管道也是一個文件ls的輸出送到這個文件,more再從這個文件將東西拿走。所不同的是管道不同於普通的文件,是一套特殊的文件pipefs,在磁盤中沒有映像,只在內存中存在,而且只存在於存在親緣關係的進程之間。然後省略若干和文件系統。
/*2-1-7*代碼解釋及流程圖/
/* 文件數據塊映射到盤塊的處理操作,可能需要建立新的邏輯塊,獲取邏輯塊號*/
//1、 塊映射處理操作。(block位圖處理函數bmap全名:block map)
//2、 參數:inode爲i_node指針block爲數據塊號create爲創建標誌
//3、 如果創建標誌置位,則在對應邏輯塊不存在時就申請新磁盤塊
//4、 返回block數據塊對應在設備上的邏輯塊號
//5、 block是相對於文件而言,但是i_zone[block]則是相對於設備而言的。
//6、 block可能不止i_zone數組大小,所以如果block的值大於7時,需要使用間接來尋址。
//7、 如下的映射:
// |-----------| |------------------------------|
// |file block | ---> | disk block |
// |-----------| |------------------------------|
// 如果create = 1,需要建立新的邏輯塊,
static int_bmap(struct m_inode *inode, int block, int create)
{
struct buffer_head *bh; //定義緩衝區頭部對象
!每一個緩衝區都有一個緩衝區頭部來唯一地標識與描述該緩衝區。
Linux通過數據結構buffer_head來定義緩衝區頭部。
這一部分用於緩存物理磁盤上的磁盤塊,從而加快對磁盤上數據的訪問。
int i;
if (block < 0) // 如果數據塊號小於0則結束程序,同時給出塊號小於0的警告
panic("_bmap: block<0");
if (block >= 7 + 512 + 512 * 512) // 如果塊號大於直接塊數+單級塊數+兩級塊數
//超出文件系統表示範圍 則結束程序並給出塊數過大的警告
panic("_bmap: block>big");
!文件在磁盤上存儲時,並不是在連續的空間存儲的。
一個文件可能對應的是幾個設備邏輯塊號(設備的邏輯塊號同樣決定了系統支持的最大的文件長度)
這些信息存儲在i_zone中。
if (block < 7) // 如果該塊號小於7則使用直接塊表示
{
if (create && !inode->i_zone[block]) // 創建標誌置位, 並且i_node中對應該塊的邏輯塊(區段)字段爲0
if (inode->i_zone[block] = new_block(inode->i_dev))
// 向相應設備申請一磁盤塊
{
inode->i_ctime = CURRENT_TIME; // 設置i_node修改時間
inode->i_dirt = 1; // 置i_node已修改標誌
}
return inode->i_zone[block]; // 返回邏輯塊號
}
block -= 7; // 將block減去直接塊所容納的塊數(7)
if (block < 512) // 如果該塊號>=7,並且小於7+512,則說明是單級塊。下面對單級塊進行處理。
{
if (create && !inode->i_zone[7]) // 如果是創建,並且該i_node中對應單級塊字段爲0,
//i_zone[7]指向單級塊,存儲的是相對於軟盤的邏輯塊號,在以後讀取該塊時,
//使用下面的據對軟盤號。注意:首次使用間接塊需申請一磁盤塊用於存放間接塊信息。
if (inode->i_zone[7] = new_block(inode->i_dev))
{
// 同上設置i_node已修改標誌和修改時間
inode->i_dirt = 1;
inode->i_ctime = CURRENT_TIME;
}
if (!inode->i_zone[7]) // 若此時i 節點間接塊字段中爲0,表明申請磁盤塊失敗
return 0;
/* 讀取設備上的單級塊 */
// 1、瞭解到i_zone[7]對應邏輯塊號
// 2、將該邏輯塊號對應內容讀入到內存中
// 3、在內存中查找(間接)查找邏輯塊號
if (!(bh = bread(inode->i_dev, inode->i_zone[7])))
return 0; //讀取該單級塊的直接塊(讀取設備上的數據)
i = ((unsigned short *)(bh->b_data))[block]; // 取該間接塊上第block 項中的邏輯塊號(盤塊號)
if (create && !i) // 如果是創建並且間接塊的第block 項中的邏輯塊號
爲0 if (i = new_block(inode->i_dev)) // 申請一磁盤塊(邏輯塊)
{
((unsigned short *)(bh->b_data))[block] = i; // 間接塊中的第block項等於該新邏輯塊塊號
bh->b_dirt = 1; // 置位間接塊的已修改標誌
}
brelse(bh); // 釋放該間接塊
return i; // 返回磁盤上新申請的對應block 的邏輯塊的塊號
}
block -= 512; // 將block再減去間接塊所容納的塊數(512)
if (create && !inode->i_zone[8]) // 如果是新創建並且i_node的兩級間接塊字段爲0,
//則需申請一磁盤塊用於存放兩級間接塊的單級塊信息,
//並將此實際磁盤塊號填入兩級間接塊字段中。之後置i_node已修改編制和修改時間。
if (inode->i_zone[8] = new_block(inode->i_dev))
{
// 置i_node已修改編制和修改時間
inode->i_dirt = 1;
inode->i_ctime = CURRENT_TIME;
}
if (!inode->i_zone[8]) // 若此時i節點兩級間接塊字段爲0表明申請磁盤塊失敗,
//返回0退出。i_zone[8] 指向軟盤的二級邏輯塊。 return 0;
if (!(bh = bread(inode->i_dev, inode->i_zone[8])))
return 0; // 讀取該兩級間接塊的單級塊(讀取設備上的數據)。即是說明的是間接尋找邏輯塊的信息時存儲在設備上
i = ((unsigned short *)bh->b_data)[block >> 9]; // 取該二次間接塊的一級塊上第(block/512)項中的邏輯塊號
if (create && !i)// 如果是創建並且二次間接塊的一級塊上第(block/512)項中的邏輯塊號爲0 的話
if (i = new_block(inode->i_dev)) // 申請一磁盤塊(邏輯塊)作爲二次間接塊的二級塊
{
((unsigned short *)(bh->b_data))[block >> 9] = i;
bh->b_dirt = 1; // 置位兩級間接塊的單級塊已修改標誌
}
brelse(bh); // 釋放二次間接塊的一級塊
if (!i)
return 0; // 如果二次間接塊的二級塊塊號爲0,表示申請磁盤塊失敗
if (!(bh = bread(inode->i_dev, i)))
return 0; // 讀取二次間接塊的二級塊
i = ((unsigned short *)bh->b_data)[block & 511]; // 取該二級塊上第block 項中的邏輯塊號
if (create && !i) // 如果是創建並且二級塊的第block 項中的邏輯塊號爲0 的話,則申請一磁盤塊(邏輯塊)
if (i = new_block(inode->i_dev))
{
((unsigned short *)(bh->b_data))[block & 511] = i;
bh->b_dirt = 1; // 置位兩級間接塊的兩級塊已修改標誌
}
brelse(bh); // 最後釋放該二次間接塊的二級塊
return i; // 返回磁盤上新申請的對應block 的邏輯塊的塊號
}
/* 根據文件的塊偏移量來得到設備上的邏輯塊號 */
/* 根據文件的m_inode節點信息,和在文件中的偏移量block來影射到實際的 */
/* 邏輯塊號,有返回值int返回 */
/*2-1-8*/
// 在該函數調用時,create = 0,該函數只是返回的是return
int bmap (struct m_inode *inode, int block)
{
inode->i_zone[block]; //只是返回相應的信息
return _bmap (inode, block, 0);
}
/*2-1-9*/
//創建文件數據塊block在設備上對應的邏輯塊,並返回設備上對應的邏輯塊號
int create_block (struct m_inode *inode, int block)
{
return _bmap (inode, block, 1);
}