linux0.95(VFS重點)源碼通俗解讀(施工中)

文件系統在磁盤中的體現

下面是磁盤的內容,其中i節點就是一個inode數組,邏輯塊就是數據塊可用于存放數據
在這里插入圖片描述

操作系統通過將磁盤數據讀入到內存中指定的緩沖區塊來與磁盤交互,對內存中的緩沖區塊修改后寫回磁盤。

進程(task_struct * task[NR_TASKS] = {&(init_task.task), }; )、
系統打開文件表(file file_table[NR_FILE])、
超級塊、
inode
等等在linux中都有唯一且有限的全局數組,比如創建新進程或者打開新的文件時就需要在這個數組中找到一個空位(槽)填寫相應內容否則不允許進行,因為這些都是系統資源,你可以理解為os只能管理有限的資源

bread(int dev,int block)函數返回一個緩沖區塊的頭部(用于解釋緩沖區,其內部有指針指向具體緩沖區塊的地址)的地址,作用是從設備號為dev的設備中讀取第block塊數據塊,緩沖區塊和文件系統的塊大小一樣!

若干個…的扇區作為一個數據塊(linux0.11中1個數據塊是兩個扇區即1MB),若干個數據塊作為一個簇,因為隨著磁盤容量增大,如果分配空間的單位不增大會導致數據塊位圖增大從而又浪費了磁盤空間。其中,數據塊是邏輯上的,也就是通過軟件實現的,具體到讀寫數據塊(即利用匯編提供的讀寫磁盤中斷)時,仍然是以扇區為單位讀寫

inode的i_count和file的f_count區別

inode的i_count:
file的f_count:

磁盤上的inode表并不會被加載到OS

只有當具體某個文件被讀或寫時,其inode才會被加載到內存的inode緩存表中,即inode_table[NR_INODE],每個inode元素都被初始化為0了,相當于提前先生成inode對象,使得內存中常駐NR_INODE個inode可被使用,而不必臨時new一個,只需要直接初始化空閑inode的每個屬性,這會快很多

linux0.11需要手動將臟緩沖區同步到磁盤上

除了少數條件會自動觸發自動同步之外,碼農需要手動調用sys_sync系統調用使得剛剛寫的文件會立刻同步到磁盤上,否則你得等。linux0.11做的僅僅是標記該緩沖區為臟。在2.6版本,linux會專門有個pdflush線程周期性地檢查是否有臟緩沖區并且自動同步到磁盤

linux0.11下設備文件的inode->i_zone[0]是設備號,而0.11后用inode->rdev代表設備號

多個空閑緩沖區是資源,共用一個等待隊列,當沒有空閑緩沖區時,申請空閑緩沖區的進程加入到該等待隊列并阻塞,但每個緩沖區自身還有一個等待隊列用于互斥,比如這時候突然有一個空閑緩沖區了,那么申請空閑緩沖區的隊列中的所有進程都被喚醒,全部(n)去爭奪這個新的空閑緩沖區,于是(n-1)都加入到該緩沖區的等待隊列

這樣設計是可理解的,沒有空閑緩沖區的時候,申請該資源的進程沒有可以加入的隊列(不知道加入到哪個緩沖區),現在專門用一個隊列來讓他們排隊,同時也能阻塞他們了

linux下的五種進程狀態

#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define TASK_ZOMBIE 3
#define TASK_STOPPED 4

1.TASK_RUNNING:可運行狀態,處于該狀態的進程可以被調度執行而成為當前進程.
2.TASK_INTERRUPTIBLE:可中斷睡眠狀態,處于該狀態的進程在所需資源有效時被喚醒,也可以通過信號或者定時中斷喚醒.
3.TASK_UNINTERRUPTIBLE:不可中斷睡眠狀態,處于該狀態的進程僅當所需資源有效時被喚醒.
4.TASK_ZOMBLE:僵尸狀態,表示進程結束且釋放資源.但其task_struct仍未釋放.
5.TASK_STOPPED:暫停狀態.處于該狀態的進程通過其他進程的信號才能被喚醒

sched.c

sleep_on(struct task_struct **p)

作用:將當前執行該函數的進程即CURRENT插入到等待隊列的隊首并阻塞,其中p是等待某個資源的隊列的隊首的pcb的指針的指針

實現:

  1. 調用__sleep_on(p,TASK_UNINTERRUPTIBLE);

__sleep_on(struct task_struct **p, int state)

作用:將當前進程插入到等待進程隊列的隊首指針p(頭插),并將隊首指針指向當前進程

實現:

  1. 如果進程0嘗試阻塞即if (current == &(init_task.task))則直接報錯
  2. 將當前進程CURRENT插入到等待隊列(千萬注意!這里的隊列其實是棧,只是我們很少說等待棧,反正就是后到的進程先出)的隊首p即tmp = *p; *p = current; current->state = state; 這三行代碼隱含了一個等待隊列(而不是顯式),實現十分巧妙,因為當前進程CURRENT執行__sleep_on這種內核函數會專門有自己的內核棧來保存臨時變量,因此tmp被保存在CURRENT的內核棧中,于是CURRENT通過tmp能夠找到等待隊列中的前一個等待進程,此時隊首指針p指向當前進程(其中p永遠指向隊首進程),然后將當前進程狀態設置為阻塞態
  3. 打開中斷即匯編sti指令
  4. 調度其他進程運行即schedule(),這時同樣需要該資源的新進程就可以開始執行__sleep_on函數(通常,參考),經歷多輪時鐘中斷和調度后,阻塞事件完成或資源空閑了(會主動調用釋放解鎖了,比如讀寫塊到緩沖區函數ll_rw_block底層在一開始上鎖,當且僅當讀寫完成才解鎖),當等待隊列的隊首進程被喚醒即wake_up后(喚醒前也是卡在schedule()),繼續向下執行,令隊首進程的下一個等待進程作為隊首即*p = tmp,并且將新的隊首進程喚醒即tmp->state=0,依次進行下去,即舊隊首喚醒新隊首,最后整個等待隊列都被喚醒重新一起爭奪資源(資源一旦空閑,所有等待進程被喚醒)

wake_up(struct task_struct **p)

作用:將傳入的進程p喚醒

實現:

  1. 將傳入的進程p的狀態修改為就緒態即(**p).state=0;

super.c

超級塊全局數組super_block super_block[NR_SUPER]
在這里插入圖片描述設備號為0==空閑超級塊槽

get_super(int dev)

返回值: super_block *

作用:從超級塊全局數組中獲取設備號對應設備的超級塊

實現:遍歷超級塊全局數組,直到當前被遍歷超級塊的設備號與dev相等

put_super(int dev)

作用:釋放(即清空、初始化)超級塊全局數組中設備號所對應設備的超級塊

實現:

  1. 對該超級塊上鎖
  2. 該超級塊的設備號設置為0(作為空閑超級塊槽的依據)
  3. 釋放(brelse)i節點位圖和邏輯塊位圖所占用的緩沖區塊
  4. 解鎖該超級塊,并喚醒等待超級塊全局數組空槽的進程

read_super(int dev)

返回值:super_block *

作用:找到超級塊全局數組空槽并從該設備讀取超級塊到空槽中

實現:

  1. 遍歷超級塊全局數組(緩存)查找是否已經有此超級塊,有則直接返回
  2. 遍歷超級塊全局數組找到dev==0的空槽
  3. 讀取即bh = bread(dev,1)超級塊并對超級塊上鎖
  4. 初始化空槽
  5. 釋放該緩沖區塊
  6. 根據剛才讀取的超級塊確定i節點位圖和邏輯塊位圖
  7. 分別讀取兩個位圖到超級塊結構體中的s_imap數組和s_zmap數組(每次讀一塊并且將緩沖頭地址賦給數組當前元素)
  8. 將i節點位圖和邏輯塊位圖中第一個數據塊設置為已被占用(不許用,為了后面方便)
  9. 解鎖超級塊

sys_umount(char * dev_name)

返回值:int

作用:根據設備名(即dev_name,準確說是全路徑名)卸載指定設備。注意!對于設備文件,其inode的i_zone[0]是設備號

實現:

  1. 根據dev_name全路徑名獲取(namei)到該設備的inode
  2. 判斷如果不是塊設備則釋放(iput)設備inode并返回錯誤
  3. 釋放設備inode
  4. 判斷如果設備號是根設備的則返回錯誤
  5. 判斷如果讀取超級塊(get_super)失敗或者設備未掛載(super_block->s_imount==0)則返回錯誤
  6. 判斷如果掛載的節點的掛載數為0(super_block->s_imount->i_mount==0)則返回錯誤(你說你掛載在某個inode,但是這個inode根本就沒有表明自己被掛載了)

namei(char * pathname)

返回值:inode *

作用:根據全路徑名獲取到該文件的inode

實現:

set_bit(bitnr,addr)

返回值:register int

作用:返回起始于addr內存段中的第bitnr位的值

實現:

register int __res __asm__("ax"); 
__asm__("bt %2,%3;setb %%al":
"=a" (__res):"a" (0),
"r" (bitnr),"m" (*(addr))); 
__res;內聯匯編的輸入。
"a"(0), eax = 0;
"r"(bitnr), 任意空閑寄存器(假設為ebx), ebx=bitnr;
"m"(*(addr)), 內存變量*(addr);
內聯匯編語句。
bt %2, %3 -> bt ebx, *addr, 
檢測*addr的ebx位是否為1,1則eflag.CF=1, 否則eflag.CF=0;
setb %%al, al=CF;
內聯匯編輸出。
__res = eax。
__res作為set_bit(bitnr, addr)宏代表表達式的最終值。*/

mount_root()

作用:開始加載文件系統

實現:

  1. 初始化全局文件打開表的每個元素的引用數為0(file_table[i].f_count=0,文件每被open一次f_count加1)
  2. 初始化全局超級塊數組
  3. 讀取根設備的超級塊(p=read_super(ROOT_DEV))
  4. 從根設備上讀取第ROOT_INO個inode(mi=iget(ROOT_DEV,ROOT_INO),其中ROOT_INO==1即根inode)
  5. ????(mi->i_count += 3)
  6. p->s_isup = p->s_imount = mi(分別是當前文件系統的根inode以及掛載點的inode,比如現在有根文件系統,插入u盤后需要掛載到根文件系統中,因此此時u盤的超級塊中的s_isup是u盤的根inode,s_imount是位于根文件系統的掛載點的inode)
  7. 將當前進程的當前工作目錄和根目錄均設置為根inode(current->pwd = mi;current->root = mi)
  8. 利用位圖統計空閑inode數和空閑數據塊數
	free=0;i=p->s_nzones;/*p->s_nzones是當前文件系統的總數據塊數即圖中的藍色部分。 i是int類型,16位*/               while (-- i >= 0)if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data))free++;/*i是當前數據塊的序號,8191的二進制是連續13個1,即i&8191取低13位即求出當前數據塊在緩沖區塊中的偏移,i>>13即求出當前數據塊屬于第幾個緩沖區塊,i>>13的值在0~8之間,因為i是16位*/printk("%d/%d free blocks\n\r",free,p->s_nzones);free=0;i=p->s_ninodes+1;while (-- i >= 0)if (!set_bit(i&8191,p->s_imap[i>>13]->b_data))free++;printk("%d/%d free inodes\n\r",free,p->s_ninodes);

調用鏈:init.c->sys_setup->mount_root

inode.c

iget(int dev,int nr)

返回值:inode *

作用:從設備號為dev的設備上讀取第nr個inode

實現:

_bmap(struct inode * inode,int block,int create)

返回值:int

作用:根據inode的i_data得到該文件邏輯塊號為block的全局物理塊號。create=1時,如果邏輯塊block還未被分配全局物理塊號,則按照分配算法分配給該邏輯塊,將該邏輯塊映射到物理塊。create=0時即使未被分配也不管,直接返回初始值。
在這里插入圖片描述

實現:

  1. 若block在直接尋址的范圍內,則直接返回inode->i_data[block]
  2. 若block在一次尋址的范圍內,則先讀取一次間址塊bh=bread(inode->i_dev,inode->i_data[7]),獲取對應全局物理塊號i = ((unsigned short *) (bh->b_data))[block];,然后釋放緩沖區,最后返回塊號i
  3. 同理若block在二次尋址的范圍內,則需要讀兩次間址塊最后返回塊號
  4. 如果create=1且以上過程中發現邏輯塊block沒有對應的全局物理塊號即初始值0(或-1???)則調用minix_new_block(int dev)通過分配算法分配塊,然后inode設置為臟

get_empty_inode()

返回值:inode *

作用:從inode緩存表中找到空閑的inode節點并初始化

實現:

  1. 遍歷inode_table,如果當前inode引用計數i_count==0,則符合最低條件(還不是最優),然后繼續循環,只有當前inode引用計數i_count==0并且該文件未被修改過即i_dirt以及未被上鎖即i_lock,則立即跳出循環,該inode為最優
  2. 如果找到的空閑inode符合最低條件,但該inode被修改過,則需要先把inode寫回磁盤(write_inode(inode)->minix_write_inode(inode)),然后該inode才可以被使用
  3. 初始化得到的空閑inode即memset(inode,0,sizeof(*inode));
  4. 將inode引用計數設置為1即inode->i_count = 1;

namei.c—/fs/minix/namei.c

minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev)

返回值:int

作用:在minix文件系統下創建設備文件。

實現:

  1. 根據basename在dir中檢查該設備文件是否已經存在bh = minix_find_entry(&dir,basename,namelen,&de),已存在則直接返回 文件已存在錯誤
  2. 在dir所在設備中找到一個空閑的inode即inode = minix_new_inode(dir->i_dev);,并將inode的idev初始化為所在目錄inode的設備號即inode->i_dev = dev;
  3. 根據傳入的mode初始化該inode即inode->i_mode = mode;
  4. 根據mode判斷如果是設備文件(塊設備文件或者字符設備文件)則用rdev初始化inode所代表的設備的設備號即inode->i_rdev = rdev;
  5. 將該inode設為臟即inode->i_dirt = 1;
  6. 在dir的目錄文件中找到空閑目錄項并將其目錄項名設置name即bh = minix_add_entry(dir,name,len,&de),這里的傳入的de被設置為空閑目錄項的地址
  7. 將空閑目錄項的inode編號設置為在2所找到的空閑inode即de->inode = inode->i_ino,并將該目錄項所在緩沖區設臟即bh->b_dirt = 1;

minix_add_entry(struct inode * dir,const char * name, int namelen, struct minix_dir_entry ** res_dir)

返回值:buffer_head *

作用:在minix文件系統下,只把name加入目錄文件dir的目錄即minix_dir_entry數組,inode統一被初始化為0

實現:

  1. 先獲取首個(直接索引)邏輯塊的物理塊號block = dir->i_data[0]
  2. 讀入首個邏輯塊即bh = bread(dir->i_dev,block)
  3. 開始遍歷目錄文件dir的目錄(讀入目錄文件的內容即minix_dir_entry數組到緩沖區),直到de->inode==0即目錄項空閑(如果遍歷到最后一個目錄項仍不空閑,則選擇最后一個目錄項的下一個目錄項(且修改dir的文件大小和設臟,因為新增了目錄項),并且如果目錄項的數目剛好占用一個塊,則申請一個新的物理塊并建立好邏輯塊與物理塊的映射即block = minix_create_block(dir,i/DIR_ENTRIES_PER_BLOCK),然后讀入該新物理塊即
    bh = bread(dir->i_dev,block)),則把name填入到de->name[]中且緩沖區設臟,minix文件系統對文件名長度進行了限制即MINIX_NAME_LEN=14,如果name的長度超出則截斷,沒超出則填充0即
    for (i=0; i < MINIX_NAME_LEN ; i++)
    de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
  4. 將新目錄項de(是地址)賦值給res_dir即*res_dir = de;
  5. 返回空閑目錄項所在緩沖區的頭部bh

dir_namei(const char * pathname,int * namelen, const char ** name)

返回值:inode *

作用:獲取pathname中最底層目錄的inode,并且用戶傳入的name會被賦值為最底層的目錄名或者文件名(如/a/b/c得到c)

實現:

  1. 調用get_dir()獲取目錄pathname的inode
  2. 循環遍歷pathname,每遇到“/”就令name指向/的下一個字符的地址,最后會得到最底層的目錄名(如/a/b/c得到c)

get_dir(const char * pathname)

返回值:inode *

作用:獲取目錄pathname最底層目錄的inode。比如/var/log/httpd,將只返回 log/目錄的inode,/var/log/httpd/則返回httpd/的目錄

實現:

  1. 判斷pathname第一個字符是否為/,是則代表pathname為絕對路徑,令臨時變量
    inode = current->root,pathname++。否則
    inode = current->pwd;
  2. 目錄引用數加1即inode->i_count++;
  3. 依次遍歷pathname中的每一個目錄,即依次獲得兩個/之間夾住的目錄名,根據該名字thisname和當前父目錄的inode有
    bh = find_entry(&inode,thisname,namelen,&de)找到該子目錄項de即dir_entry類型
    釋放高速緩沖區bh即brelse()以及inode節點iput()
    根據de獲得該子目錄inode的編號即
    inr = de->inode,又根據當前父目錄獲得設備號idev = inode->i_dev,于是得到該子目錄inode即inode = iget(idev,inr),回到3

minix_find_entry(struct inode * dir,const char * name, int namelen, struct minix_dir_entry ** res_dir)

返回值:buffer_head *

作用:根據目錄dir的inode找到其下名為name的目錄項。其中res_dir存放該目錄項,而返回值是該目錄項所在高速緩沖區的頭部。

實現:

  1. 根據entries =
    (*dir)->i_size / (sizeof (struct minix_dir_entry));得到該目錄下目錄項的數目
  2. 先得到第一個邏輯塊的對應物理塊號
    block = (*dir)->i_zone[0]
  3. 讀取第一個邏輯塊bh = bread((*dir)->i_dev,block)
  4. 使數據塊可以按目錄項來遍歷de = (struct minix_dir_entry *) bh->b_data;,此時的de是第一個目錄項的地址
  5. 利用目錄項的數目entries開始遍歷第一個數據塊的目錄項,如果當前目錄項de對應名字和name匹配即minix_match(namelen,name,de),則返回該目錄項所在高速緩沖區的頭部,以及將de賦值給res_dir。如果不匹配,則de++,并且如果de已經超出當前邏輯塊,則根據當前已遍歷的目錄項數目i求得新的邏輯塊號然后根據bmap得到該邏輯塊號對應物理塊號block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK),然后讀入該物理塊并且使數據塊可以按目錄項來遍歷。

namei.c—/fs/namei.c

sys_mknod(const char * filename, int mode, int dev)

返回值:int

作用:可以基于任意文件系統創建設備文件(體現VFS)。與sys_creat(創建普通文件,底層調用的是sys_open)不同在于sys_mknod可以輸入設備號參數即dev。

實現:

  1. 判斷是否為超級用戶suser(),不是則不執行直接返回
  2. 獲取最底層目錄的inode即
    dir = dir_namei(filename,&namelen,&basename),
    basename被賦值為最底層的目錄名或者文件名(比如filename是/dev/usb,則dir是dev/目錄的inode,basename是usb)
  3. 判斷dir的寫權限,無則不執行直接返回
  4. 判斷dir是否有mknod的函數指針即
    if (!dir->i_op || !dir->i_op->mknod),無則不執行直接返回
  5. 調用dir的mknod函數(體現VFS)即dir->i_op->mknod(dir,basename,namelen,mode,dev);(假如磁盤是minix文件系統,那么dir->i_op->mknod就是minix_mknod)

bitmap.c—/fs/minix/bitmap.c

minix_new_block(int dev)

返回值:int

作用:從設備號為dev的設備中找到空閑的數據塊并返回該塊在整個設備的塊號

實現:

  1. 從dev設備中獲取超級塊即sb = get_super(dev)
  2. 通過遍歷dev設備的數據塊位圖,找到空閑的數據塊即j=find_first_zero(bh->b_data)然后置為占用(且緩沖區設為臟)
  3. 獲取空閑數據塊在整個設備中的全局編號j += i*8192 + sb->s_firstdatazone-1;
  4. 讀入該空閑數據塊即bh=getblk(dev,j)
  5. 將緩沖區的空閑數據塊清0即clear_block(bh->b_data),并將緩沖區設為臟
  6. 返回空閑數據塊在整個設備中的全局編號j

minix_new_inode(int dev)

返回值:inode *

作用:在minix文件系統下,在磁盤找到空閑的inode并初始化

實現:

  1. 從inode緩沖表中獲得一個空閑inode即inode=get_empty_inode()
  2. 根據參數dev即設備號初始化得到的inode的超級塊指針即
    inode->i_sb = get_super(dev)
  3. 根據剛剛得到的超級塊指針可以得到8個inode位圖的緩沖區地址(minix文件系統的磁盤的inode位圖和數據塊位圖均占用8個緩沖塊即8M),依次遍歷這8個緩沖區,對于每個緩沖區遍歷每個位 直到找到第一個為0的位即find_first_zero(bh->b_data)相當于找到磁盤中空閑的inode
  4. 將剛剛在inode位圖中找到的空閑位設置為1,即被占用
  5. 將緩沖區設置為臟使得剛剛在緩沖區的inode位圖的空閑位設置為1能寫回磁盤即bh->b_dirt = 1;
  6. 初始化該inode,inode設置為臟,inode的全局編號(即在磁盤的inode數組的第幾個)根據3也可以確定下來,其中最重要的是將minix文件系統對inode的操作函數指針賦值給該inode,即inode->i_op = &minix_inode_operations;

inode.c

minix_write_inode(struct inode * inode)

返回值:inode *

作用:將傳入的minix文件系統的inode標記為臟

實現:

  1. 找到傳入的inode所在的物理磁盤塊(
    block =
    2 +
    inode->i_sb->s_imap_blocks + inode->i_sb->s_zmap_blocks +
    (inode->i_ino-1)/
    MINIX_INODES_PER_BLOCK;)
    并且通過bh=bread(inode->i_dev,block)讀入這個塊,然后根據bh->data得到該minix_inode即(raw_inode =
    ((struct minix_inode *)bh->b_data) +
    (inode->i_ino-1)%MINIX_INODES_PER_BLOCK;)
    這里必須要先把inode所在塊讀入而不是直接寫inode到磁盤,因為內存與磁盤的交互是緩沖塊,只有inode這一小部分的內容(還不夠一個塊的大小)那直接寫回就會喪失了其余部分
  2. 將傳入的通用inode中的屬性賦值給minix_inode的對應屬性(由于raw_inode是bh->b_data的地址,所以raw_inode被賦值的時候緩沖區內容也被修改了)
  3. 如果傳入的inode是設備文件的(塊設備文件或者字符設備文件),則只需要賦值首個塊號即raw_inode->i_zone[0] = inode->i_rdev;(inode有i_dev和i_rdev,對于普通文件的i_dev表明該文件所在磁盤的設備號,對于設備文件的i_rdev表明該設備的設備號)。否則(普通文件的inode)需要賦值每個索引塊指明文件所在物理塊的位置。
  4. 將緩沖區設置為臟即bh->b_dirt=1(原因如第2點),將inode設置為未修改即inode->i_dirt=0(因為已經為該inode修改完成)

minix_create_block(struct inode * inode, int block)

返回值:int

作用:在inode中獲取第block個邏輯塊的物理塊,如果發現該邏輯塊還沒有被分配物理塊,則通過遍歷數據塊位圖找到空閑的物理塊分配給該邏輯塊

實現:

  1. 調用_bmap(inode,block,1);

buffer.c

wait_on_buffer(struct buffer_head * bh)

作用:令當前進程等待緩沖區資源。如果緩沖區無人占用則無需等待,否則將當前進程加入到該資源的等待隊列并阻塞

實現:

  1. 關中斷,確保執行2的時候只有一個進程訪問資源的鎖,否則一旦鎖空閑,所有進程都不阻塞了
  2. 循環判斷資源是否被上鎖占用了,是則將當前進程加入到該資源的等待隊列并阻塞即
    while (bh->b_lock) sleep_on(&bh->b_wait);
  3. 開中斷

bread(int dev,int block)

返回值:buffer_head *

作用:

實現:
1.
2.

BADNESS宏定義

定義:BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)

返回值:char(但是數字)

作用:分配空閑緩沖區時衡量緩沖區的好壞(空閑)程度。同樣沒上鎖的兩個緩沖區,被修改過的那個是更壞的緩沖區(2)。同樣沒被修改過的兩個緩沖區,上鎖的那個是更壞的緩沖區(1)。既被上鎖還被修改過則是最壞的緩沖區(3),既沒被上鎖也沒被修改過則是最好的緩沖區(0)(b_dirt和b_lock的值只會為0或1)。

getblk(int dev,int block)

返回值:buffer_head *

作用:返回裝有設備號為dev的設備中塊號為block的內容的緩沖區

實現:

  1. 通過哈希值看指定塊是否已存在即
    bh = get_hash_table(dev,block),bh是最終要返回的緩沖區
  2. 循環遍歷空閑緩沖區鏈表(雙向鏈表)free_list,如果當前緩沖區的被引用數b_count>0,則說明緩沖區正在被使用(???那為什么還把它放進空閑緩沖區鏈表???),看下一個空閑緩沖區。如果當前緩沖區空閑且在此之前未找到空閑緩沖區即bh==NULL,或者當前緩沖區比已找到的空閑緩沖區bh(BADNESS(tmp)<BADNESS(bh)),則令當前緩沖區作為已找到的空閑緩沖區即bh = tmp,如果當前緩沖區不僅比已找到的空閑緩沖區bh好而且是完全空閑即BADNESS(tmp)==0,則直接退出循環,否則一直遍歷到表尾
  3. 如果遍歷完后bh仍為空,說明沒有空閑緩沖區或者所有緩沖區的引用數都>0,則當前進程阻塞等待即加入到空閑緩沖區資源的等待隊列的隊首sleep_on(&buffer_wait),直到被喚醒(有空閑緩沖區了),則再次回到2執行(goto語句)
  4. 到了這里說明已經找到空閑緩沖區了即bh!=NULL,開始等待緩沖區解鎖即wait_on_buffer(bh)(???我對這里的空閑的定義很模糊???)
  5. 如果被喚醒后緩沖區引用數>0,則再次回到2執行
  6. 循環檢查緩沖區是否被修改過,是則將其寫回設備,并再次等待緩沖區解鎖,然后執行5,然后再次檢查
  7. 執行到這里的時間已經過去很久,可以通過遍歷檢查是否其他進程已經讀入了find_buffer(dev,block),是則返回
  8. 初始化得到的空閑緩沖區。占用該緩沖區即bh->b_count=1,不必寫回磁盤即bh->b_dirt=0,未更新bh->b_uptodate=0;

read_write.c

sys_write(unsigned int fd,char * buf,unsigned int count)

返回值:int

作用:

實現:
1.
2.

end

返回值:

作用:

實現:
1.
2.

結構體

struct inode_operations minix_inode_operations = {minix_create,minix_lookup,minix_link,minix_unlink,minix_symlink,minix_mkdir,minix_rmdir,minix_mknod,minix_rename,minix_readlink,minix_open,minix_release,minix_follow_link
};struct dir_entry {unsigned short inode;          //inode節點的編號char name[NAME_LEN];           //文件名
};struct buffer_head {char * b_data;			/* pointer to data block (1024 bytes) */unsigned long b_blocknr;	/* block number */unsigned short b_dev;		/* device (0 = free) */unsigned char b_uptodate;unsigned char b_dirt;		/* 0-clean,1-dirty */unsigned char b_count;		/* users using this block */unsigned char b_lock;		/* 0 - ok, 1 -locked */struct task_struct * b_wait;struct buffer_head * b_prev;struct buffer_head * b_next;struct buffer_head * b_prev_free;struct buffer_head * b_next_free;
};struct inode {dev_t	i_dev;ino_t	i_ino;//在磁盤中的inode表排第幾個umode_t	i_mode;
/*i_mode一共10位,第一位表明結點文件類型,后9位依次為:
i結點所有者、所屬組成員、其他成員的權限
(權限有讀寫執行三種)*/nlink_t	i_nlink;uid_t	i_uid;gid_t	i_gid;dev_t	i_rdev;off_t	i_size;//文件大小(字節數)time_t	i_atime;time_t	i_mtime;time_t	i_ctime;unsigned long i_data[16];struct inode_operations * i_op;struct super_block * i_sb;struct task_struct * i_wait;struct task_struct * i_wait2;	/* for pipes */unsigned short i_count;//i節點被使用的次數unsigned char i_lock;unsigned char i_dirt;unsigned char i_pipe;unsigned char i_mount;unsigned char i_seek;unsigned char i_update;
};struct file {unsigned short f_mode;unsigned short f_flags;unsigned short f_count;struct inode * f_inode;struct file_operations * f_op;off_t f_pos;
};struct super_block {unsigned short s_ninodes;unsigned short s_nzones;unsigned short s_imap_blocks;unsigned short s_zmap_blocks;unsigned short s_firstdatazone;unsigned short s_log_zone_size;unsigned long s_max_size;unsigned short s_magic;
/* These are only in memory */struct buffer_head * s_imap[8];struct buffer_head * s_zmap[8];unsigned short s_dev;struct inode * s_covered;struct inode * s_mounted;unsigned long s_time;struct task_struct * s_wait;unsigned char s_lock;unsigned char s_rd_only;unsigned char s_dirt;
};struct file_operations {int (*lseek) (struct inode *, struct file *, off_t, int);int (*read) (struct inode *, struct file *, char *, int);int (*write) (struct inode *, struct file *, char *, int);
};struct inode_operations {int (*create) (struct inode *,const char *,int,int,struct inode **);int (*lookup) (struct inode *,const char *,int,struct inode **);int (*link) (struct inode *,struct inode *,const char *,int);int (*unlink) (struct inode *,const char *,int);int (*symlink) (struct inode *,const char *,int,const char *);int (*mkdir) (struct inode *,const char *,int,int);int (*rmdir) (struct inode *,const char *,int);int (*mknod) (struct inode *,const char *,int,int,int);int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int);int (*readlink) (struct inode *,char *,int);int (*open) (struct inode *, struct file *);void (*release) (struct inode *, struct file *);struct inode * (*follow_link) (struct inode *, struct inode *);
};struct minix_inode {unsigned short i_mode;unsigned short i_uid;unsigned long i_size;unsigned long i_time;unsigned char i_gid;unsigned char i_nlinks;unsigned short i_zone[9];
};struct minix_super_block {unsigned short s_ninodes;unsigned short s_nzones;unsigned short s_imap_blocks;unsigned short s_zmap_blocks;unsigned short s_firstdatazone;unsigned short s_log_zone_size;unsigned long s_max_size;unsigned short s_magic;
};struct minix_dir_entry {unsigned short inode;char name[MINIX_NAME_LEN];
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/35211.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35211.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35211.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Mysql中如果建立了索引,索引所占的空間隨著數據量增長而變大,這樣無論寫入還是查詢,性能都會有所下降,怎么處理?

索引所占空間的增長確實會對MySQL數據庫的寫入性能和查詢性能造成影響&#xff0c;這主要是由于索引數據過多時會導致磁盤I/O操作變得非常頻繁&#xff0c;從而使性能下降。為此&#xff0c;可以采取以下幾種方式來減緩這種影響&#xff1a; 1. 限制索引的大小&#xff1a;可以…

Netty框架技術文檔-基本概念

Netty: Home https://github.com/netty/netty 基本概念 NIO&#xff08;Non-blocking I/O&#xff0c;非阻塞I/O&#xff09;&#xff1a;NIO是一種Java平臺的I/O模型&#xff0c;它使用Channel和Buffer來進行數據傳輸&#xff0c;而不是傳統的Stream。NIO模型可以處理大量并…

TCP除了3次握手,其他的這些你知道嗎?

文章首發地址 MSS&#xff1a; MSS&#xff08;Maximum Segment Size&#xff09;表示TCP報文段的最大長度&#xff0c;通常是MSSMTU-TCP頭部長度。由于數據鏈路層協議的MTU可能不同&#xff0c;因此TCP連接建立時會通過MSS選項告知對方報文段的最大長度。MTU&#xff1a; MTU…

【探索SpringCloud】服務發現-Nacos使用

前言 在聊服務注冊中心時&#xff0c;便提到了Nacos。這次便來認識一下。當然&#xff0c;這自然沒有官方介紹那般詳盡&#xff0c;權當是學習了解Nacos原理的一個過程吧。 Nacos簡介 Nacos&#xff0c;全名&#xff1a;dynamic Naming And Configuration Service. 而這個名…

Java JDBC,輕松構建數據庫連接:代碼教程詳解

JDBC的概述 Java Database Connectivity&#xff08;JDBC&#xff09;是 Java 中用于與數據庫進行通信的 API。它提供了一套標準的 API&#xff0c;并允許 Java 應用程序連接到各種關系型數據庫&#xff0c;如 MySQL、Oracle、PostgreSQL 等&#xff0c;從而可以執行 SQL 查詢…

win10在vmware15中安裝macos10.13系統

第一步、安裝vmware版本信息如下 第二步、下載unlocker-main和darwin.iso放到安裝文件夾 第三步、管理員身份運行win-install.cmd 第四步、運行vmware新建虛擬機 第五步、啟動新創建的虛擬機macOS 10.13并選擇語言 第六步、選擇磁盤工具抹掉磁盤 第七步、格式化完成后退出磁盤工…

flutter 隨筆

萬物 皆可 結構 概念 ?狀態 插件類 flutter系統類 MaterialApp源App應? 事件 很簡單/簡單/較復雜/復雜/很復雜 結構體 MaterialApp(xx:) 公開坑位屬性&#xff1a;所配置內容 Widget 插件事件 function 函數事件 flutter/dart 事件結構描述void Function() 外層主事件 內層回…

數據結構:交換排序

冒泡排序 起泡排序&#xff0c;別名“冒泡排序”&#xff0c;該算法的核心思想是將無序表中的所有記錄&#xff0c;通過兩兩比較關鍵字&#xff0c;得出升序序列或者降序序列。 算法步驟 比較相鄰的元素。如果第一個元素大于第二個元素&#xff0c;就交換它們。對每一對相鄰…

Python-OpenCV中的圖像處理-圖像金字塔

Python-OpenCV中的圖像處理-圖像金字塔 圖像金字塔高斯金字塔拉普拉斯金字塔 金字塔圖像融合 圖像金字塔 同一圖像的不同分辨率的子圖集合&#xff0c;如果把最大的圖像放在底部&#xff0c;最小的放在頂部&#xff0c;看起來像一座金字塔&#xff0c;故而得名圖像金字塔。cv2…

小程序發布注意事項

1、使用HBuildx的 發布 功能發布小程序&#xff0c;因為編譯完的代碼目錄不是同一個 如果使用 運行 到小程序&#xff0c;最后發布的版本會顯示”無法連接本地服務器“ 2、使用unicloud的云服務 uniCloud發行 | uni-app官網 阿里云的unicloud的話&#xff0c;使用request域名…

Spring中Bean的循環依賴問題

1.什么是Bean的循環依賴&#xff1f; 簡單來說就是在A類中&#xff0c;初始化A時需要用到B對象&#xff0c;而在B類中&#xff0c;初始化B時需要用到A對象&#xff0c;這種狀況下在Spring中&#xff0c;如果A和B同時初始化&#xff0c;A&#xff0c;B同時都需要對方的資源&…

電腦開機出現Boot Device怎么辦?

開機出現Boot Device這個問題很常見&#xff0c;有時還會出現No Boot Device的問題&#xff0c;雖然多了一個單詞&#xff0c;但意思是相同的&#xff0c;這些問題說明你的系統盤出現了問題&#xff0c;或者是引導出現了問題。這該如何解決呢&#xff1f; 方法1. 檢查主板或硬盤…

【算法——雙指針】LeetCode 283 移動零

題目描述&#xff1a; 思路&#xff1a; (雙指針) O(n)O(n)O(n) 給定一個數組 nums&#xff0c;要求我們將所有的 0 移動到數組的末尾&#xff0c;同時保持非零元素的相對順序。 如圖所示&#xff0c;數組nums [0,1,0,3,12]&#xff0c;移動完成后變成nums [1,3,12,0,0] &am…

若依vue -【 100 ~ 更 ~ 110 】

100 主子表代碼生成詳解 1 新建數據庫表結構&#xff08;主子表&#xff09; -- ---------------------------- -- 客戶表 -- ---------------------------- drop table if exists sys_customer; create table sys_customer (customer_id bigint(20) not null…

Docker部署rabbitmq遇到的問題 Stats in management UI are disabled on this node

1. Stats in management UI are disabled on this node #進入rabbitmq容器 docker exec -it {rabbitmq容器名稱或者id} /bin/bash#進入容器后&#xff0c;cd到以下路徑 cd /etc/rabbitmq/conf.d/#修改 management_agent.disable_metrics_collector false echo management_age…

談談語音助手

目錄 1.什么是語音助手 2.語音助手的發展過程 3.現在有哪些成熟的語音助手 4.語音助手對人類發展的影響 1.什么是語音助手 語音助手是一種能夠通過語音交互與用戶進行溝通和執行任務的虛擬助手。它基于人工智能和自然語言處理技術&#xff0c;能夠理解用戶的語音指令&#x…

數據結構-隊列的實現(C語言版)

前言 隊列是一種特殊的線性表&#xff0c;它只允許在一端對數據進行插入操作&#xff0c;在另一端對數據進行刪除操作的特殊線性表&#xff0c;隊列具有先進先出的&#xff08;FIFO&#xff09;的 特性&#xff0c;進行插入操作的一端稱為隊尾&#xff0c;進行刪除操作的一端稱…

JZ37序列化二叉樹

題目地址&#xff1a;序列化二叉樹_牛客題霸_牛客網 題目回顧&#xff1a; 解題思路&#xff1a; 首先&#xff0c;序列化就是將二叉樹的節點值放入一個字符串中&#xff0c;這里可以按照前序遍歷的思路來進行操作&#xff0c;謙虛遍歷是&#xff1a;根左右的情況&#xff0c;…

什么是React?React與VU的優缺點有哪些?

什么是React&#xff1f;什么是VUE&#xff1f; 維基百科上的概念解釋&#xff0c;Vue.js是一個用于創建用戶界面的開源MVVM前端JavaScript框架&#xff0c;也是一個創建單頁應用的Web應用框架。Vue.js由尤雨溪&#xff08;Evan You&#xff09;創建&#xff0c;由他和其他活躍…

Cmd部署HexoGithub443問題

git config --global http.proxy “localhost:7890” 配置下代理即可 本文由 mdnice 多平臺發布