操作系統 4.5-文件使用磁盤的實現

通過文件進行磁盤操作入口

// 在fs/read_write.c中
int sys_write(int fd, const char* buf, int count)
{struct file *file = current->filp[fd];struct m_inode *inode = file->inode;if (S_ISREG(inode->i_mode))return file_write(inode, file, buf, count);
}

  1. 進程控制塊(PCB)

    • current 是指向當前進程控制塊(PCB)的指針。PCB 包含了進程的所有信息,包括打開的文件列表。

  2. 文件指針數組(filp)

    • current->filp[fd] 訪問當前進程的文件指針數組,獲取與文件描述符 fd 關聯的 file 結構。

  3. 文件結構(file)

    • struct file 包含了打開文件的所有信息,包括指向 inode 的指針。

  4. inode 結構

    • struct m_inode 是文件的元數據結構,包含了文件的屬性(如權限、大小、數據塊位置等)。

    • inode->i_mode 包含了文件的類型和權限信息。

  5. 文件寫入操作

    • file_write(inode, file, buf, count) 是實際執行文件寫入操作的函數,它將 buf 中的 count 字節數據寫入到由 inodefile 描述的文件中。

  6. 文件類型檢查

    • S_ISREG(inode->i_mode) 檢查 inode 表示的是否是一個普通文件。如果是,才執行寫入操作。

  7. 數據流和控制流

    • 圖中展示了從 write(fd) 調用開始,通過 PCB 和文件表,最終訪問到 inode 和數據盤塊的過程。

File_write

工作流程分析

  1. 確定要寫入的字符段

    • 首先,需要確定要寫入文件的字符段。這通常由文件的讀寫指針決定,該指針指示了文件中的當前位置。通過修改這個指針(例如使用 fseek),可以改變寫入的起始位置,然后加上要寫入的字符數量(count)來確定結束位置。

  2. 找到要寫的盤塊號

    • 確定了要寫入的字符段后,下一步是找到這些字符對應的磁盤塊號。這是通過文件的 inode 結構來實現的,inode 包含了文件的元數據,包括文件數據塊的位置信息。

  3. 形成寫入請求并執行寫入

    • 使用找到的盤塊號和要寫入的數據(buf),形成一個寫入請求(request),然后將這個請求放入磁盤調度算法(如電梯算法)中進行處理,以優化磁盤I/O操作。

函數參數解釋:

  • inode

    • 這是一個指向文件的 inode 結構的指針。inode 包含了文件的元數據,如文件類型、權限、所有者、文件大小以及指向文件數據塊的指針等。在寫入操作中,inode 用于查找文件數據塊的位置。

  • file

    • 這是一個指向文件結構的指針。文件結構包含了打開文件的所有信息,包括文件狀態標志、文件的讀寫指針等。在寫入操作中,file 結構用于獲取文件的當前位置和狀態。

  • buf

    • 這是一個指向要寫入文件的數據緩沖區的指針。buf 包含了實際要寫入文件的數據。

  • count

    • 這是一個整數,表示要寫入文件的字節數。countbuf 參數一起,指定了要寫入文件的數據量。

實現

以下是帶有詳細注解的 file_write 函數代碼,解釋了每一部分的功能和作用:

// file_write函數用于將數據從用戶緩沖區寫入到文件對應的磁盤塊中
int file_write(struct m_inode *inode, struct file *filp, char *buf, int count)
{off_t pos; // 定義一個變量pos,用于記錄寫入文件的起始位置
?// 根據文件的讀寫標志(O_APPEND)和當前文件位置(filp->f_pos)確定寫入的起始位置if (filp->f_flags & O_APPEND)pos = inode->i_size; // 如果設置了追加標志,則從文件末尾開始寫入elsepos = filp->f_pos; // 否則,從文件當前位置開始寫入
?// 當還有字節需要寫入時,繼續循環while (i < count) {block = create_block(inode, pos / BLOCK_SIZE); // 創建或分配一個新的磁盤塊bh = bread(inode->i_dev, block); // 讀取指定的磁盤塊到內存中,并將塊地址存儲在bh中
?int c = pos % BLOCK_SIZE; // 計算當前塊內的偏移char *pc = c + bh->b_data; // 計算內存中塊數據區域的起始地址bh->b_dirt = 1; // 標記塊為臟塊,表示該塊已被修改,需要寫回磁盤c = BLOCK_SIZE - c; // 計算剩余需要寫入的字節數pos += c; // 更新文件位置
?// 將用戶緩沖區buf中的數據逐字節寫入到內存中的塊數據區域bh->b_datawhile (c-- > 0)*(pc++) = get_fs_byte(buf++);
?brelse(bh); // 釋放緩沖區,將其返回到緩沖區管理中}
?filp->f_pos = pos; // 更新文件的當前位置
}
  1. 確定寫入位置

    • 通過檢查文件的讀寫標志和當前文件位置,確定寫入操作的起始位置。

  2. 循環寫入數據

    • 使用 while 循環,根據要寫入的字節數 count,逐塊寫入數據。

  3. 創建和讀取磁盤塊

    • create_block 函數用于創建或分配一個新的磁盤塊。

    • bread 函數用于讀取指定的磁盤塊到內存中,并將塊地址存儲在 bh(緩沖頭)中。

  4. 計算塊內偏移和剩余字節數

    • 計算當前塊內的偏移 c 和剩余需要寫入的字節數。

  5. 寫入數據

    • 將用戶緩沖區 buf 中的數據逐字節寫入到內存中的塊數據區域 bh->b_data

  6. 標記塊為臟塊

    • bh->b_dirt 設置為 1,表示該塊已被修改,需要寫回磁盤。

  7. 釋放緩沖區

    • 使用 brelse 函數釋放緩沖區,將其返回到緩沖區管理中。

  8. 更新文件位置

    • 更新文件的當前位置 filp->f_pos,以便后續操作可以從正確的位置開始。

通過這些步驟,file_write 函數實現了將用戶數據寫入文件的完整過程,包括確定寫入位置、創建和讀取磁盤塊、寫入數據、標記塊為臟塊以及更新文件位置等。

Create_block

圖中展示了 create_block 函數和 _bmap 函數的代碼,這些函數用于在文件系統中創建和映射磁盤塊。以下是提取的代碼和注解:

提取的代碼:

// create_block函數用于根據inode和塊號分配或創建一個新的磁盤塊
int create_block(struct m_inode *inode, int block)
{while (i < count) {block = create_block(inode, pos / BLOCK_SIZE);bh = bread(inode->i_dev, block);}
?int _bmap(m_inode *inode, int block, int create){if (block < 7) {if (create && !inode->i_zone[block]) {inode->i_zone[block] = new_block(inode->i_dev);inode->i_ctime = CURRENT_TIME;inode->i_dirt = 1;}return inode->i_zone[block];}block -= 7;if (block < 512) {bh = bread(inode->i_dev, inode->i_zone[7]);return (bh->b_data)[block];}}
}

代碼注解:

  1. create_block函數

    • 這個函數用于根據給定的 inode 和塊號 block 分配或創建一個新的磁盤塊。

    • 它通過調用 _bmap 函數來實現塊的映射和創建。

  2. _bmap函數

    • 這是一個輔助函數,用于處理塊號的映射。

    • 它首先檢查塊號是否小于7,這7個塊號直接存儲在 inodei_zone 數組中。

    • 如果塊號小于7,并且需要創建新塊(create 為真且當前塊號未分配),則調用 new_block 函數分配新塊,更新 inodei_ctimei_dirt 標志。

    • 如果塊號大于等于7且小于512,表示這是一個一重間接塊,需要讀取間接塊表并找到對應的塊號。

  3. new_block函數

    • 這個函數用于分配一個新的磁盤塊。具體實現未在圖中展示,但通常涉及查找空閑塊、分配塊號等操作。

  4. bread函數

    • 這個函數用于讀取指定的磁盤塊到內存中。它將塊地址存儲在 bh(緩沖頭)中,以便后續操作。

總結:

這段代碼實現了文件系統中磁盤塊的分配和映射機制。通過 create_block_bmap 函數,系統可以根據文件的 inode 和塊號動態分配和映射磁盤塊。這種機制支持文件的動態增長和高效的磁盤空間管理。

通過間接塊表(如一重間接和二重間接塊表),文件系統還可以支持非常大的文件,即使單個文件的數據量超過了直接塊表所能表示的范圍。

m_inode

圖中展示了與設備文件的 inode 結構相關的代碼,以及如何通過 sys_open 函數打開設備文件。以下是提取的代碼和注解:

提取的代碼:

// m_inode結構體定義,用于表示設備文件的inode
struct m_inode {unsigned short i_mode; // 文件的類型和屬性unsigned short i_zone[9]; // 指向文件內容數據塊struct task_struct *i_wait; // 多個進程共享的打開這個inode,有的進程等待...unsigned short i_count; // unsigned char i_lock; // unsigned char i_dirt; // 
};
?
// sys_open函數用于打開文件或設備
int sys_open(const char* filename, int flag)
{if (S_ISCHR(inode->i_mode)) { // 檢查是否為字符設備文件if (MAJOR(inode->i_zone[0]) == 4) { // 檢查設備文件類型current->tty = MINOR(inode->i_zone[0]); // 設置當前進程的tty}}
}
?
// 宏定義,用于從設備號中提取主設備號和次設備號
#define MAJOR(a) ((((unsigned)(a)) >> 8)) // 取高字節
#define MINOR(a) ((a) & 0xff) // 取低字節

代碼注解:

  1. m_inode結構體

    • i_mode:表示文件的類型和屬性,例如普通文件、目錄、字符設備等。

    • i_zone:一個數組,用于存儲指向文件內容數據塊的指針或設備號等信息。

    • i_wait:指向等待該inode的進程的task_struct結構體。

    • i_count:表示當前有多少個進程打開了該inode。

    • i_lock:用于控制對inode的訪問,防止并發訪問導致的問題。

    • i_dirt:表示inode是否被修改,需要寫回磁盤。

  2. sys_open函數

    • 該函數用于打開文件或設備。

    • S_ISCHR(inode->i_mode):檢查文件是否為字符設備文件。

    • MAJOR(inode->i_zone[0])MINOR(inode->i_zone[0]):從設備號中提取主設備號和次設備號。

    • current->tty:設置當前進程的tty(終端設備)。

  3. 宏定義

    • MAJOR(a):從設備號中提取主設備號(高字節)。

    • MINOR(a):從設備號中提取次設備號(低字節)。

總結:

這段代碼展示了如何使用 m_inode 結構體來表示設備文件的inode,并演示了如何通過 sys_open 函數打開設備文件。

文件視圖的兩條路

讀寫磁盤的過程:

  1. 用戶發起文件操作

    • 用戶通過 writeread 系統調用操作文件。

    • 傳遞參數:文件描述符(FD)、內存緩沖區(buff)、操作字節數(count)。

  2. 系統調用轉換

    • write 調用內核中的 sys_writeread 調用 sys_read

    • 需要獲取文件的 inode(索引節點)信息。

  3. 計算目標磁盤塊

    • 通過文件結構 file 獲取文件指針 f_pos,確定字符流中的位置。

    • 根據 inode 及索引結構,計算出對應的磁盤塊號(block)。

    • 采用直接索引、一級索引、二級索引等方式解析出數據塊。

  4. 磁盤操作

    • 讀操作:調用 bread() 讀取磁盤數據到緩存,再復制到用戶緩沖區。

    • 寫操作:調用 bwrite() 將用戶緩沖區數據寫入磁盤緩存,并同步到磁盤。

  5. 磁盤調度與完成

    • 磁盤請求加入電梯調度隊列。

    • 設備驅動完成磁盤讀寫,系統中斷通知完成。

    • 若是寫操作,需更新 f_pos 以維護文件流的連續性。


輸出到顯示器的過程:

  1. 用戶發起輸出請求

    • 例如 printf()write(1, buff, count),其中 1 代表標準輸出(stdout)。

  2. 系統調用轉換

    • 進入內核,調用 sys_write 處理。

    • 識別文件描述符 1 代表標準輸出(顯示器)。

  3. 數據傳輸到終端設備

    • 由于 stdout 不是磁盤文件,而是字符設備,直接走 tty 終端驅動。

    • 終端驅動 tty_write 處理輸出,將數據放入 tty 緩沖區。

  4. 驅動程序與硬件交互

    • tty 設備驅動調用 console_driver,執行 putc() 逐字符輸出到屏幕。

    • 可能涉及 VGAframebufferUART 串口等設備。

  5. 顯示完成

    • 終端驅動完成數據輸出,可能觸發屏幕刷新或回顯。

    • 若是行緩沖模式,遇到 \n 或緩沖區滿才實際輸出。

對比總結

  • 讀寫磁盤涉及塊設備(block device),數據按塊存取,可能需要磁盤調度與緩沖管理。

  • 輸出到顯示器涉及字符設備(char device),數據按字符流傳輸,即時性更強。

  • 兩者都通過系統調用進入內核,但處理方式不同,磁盤用 bread/bwrite,顯示器用 tty_write

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

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

相關文章

libreoffice-help-common` 的版本(`24.8.5`)與官方源要求的版本(`24.2.7`)不一致

出現此錯誤的原因主要是軟件包依賴沖突&#xff0c;具體分析如下&#xff1a; ### 主要原因 1. **軟件源版本不匹配&#xff08;國內和官方服務器版本有差距&#xff09; 系統中可能啟用了第三方軟件源&#xff08;如 PPA 或 backports 源&#xff09;&#xff0c;導致 lib…

使用Geotools中的原始方法來操作PostGIS空間數據庫

目錄 前言 一、原生PostGIS連接介紹 1、連接參數說明 2、創建DataStore 二、工程實戰 1、Maven Pom.xml定義 2、空間數據庫表 3、讀取空間表的數據 三、總結 前言 在當今數字化與信息化飛速發展的時代&#xff0c;空間數據的處理與分析已成為眾多領域不可或缺的一環。從…

訊飛語音合成(流式版)語音專業版高質量的分析

一、引言 在現代的 Web 應用開發中&#xff0c;語音合成技術為用戶提供了更加便捷和人性化的交互體驗。訊飛語音合成&#xff08;流式版&#xff09;以其高效、穩定的性能&#xff0c;成為了眾多開發者的首選。本文將詳細介紹在 Home.vue 文件中實現訊飛語音合成&#xff08;流…

走進未來的交互世界:下一代HMI設計趨勢解析

在科技日新月異的今天&#xff0c;人機交互界面&#xff08;HMI&#xff09;設計正以前所未有的速度發展&#xff0c;不斷引領著未來的交互世界。從簡單的按鈕和圖標&#xff0c;到如今的智能助手和虛擬現實&#xff0c;HMI設計不僅改變了我們的生活方式&#xff0c;還深刻影響…

洛谷題單3-P1217 [USACO1.5] 回文質數 Prime Palindromes-python-流程圖重構

題目描述 因為 151 151 151 既是一個質數又是一個回文數&#xff08;從左到右和從右到左是看一樣的&#xff09;&#xff0c;所以 151 151 151 是回文質數。 寫一個程序來找出范圍 [ a , b ] ( 5 ≤ a < b ≤ 100 , 000 , 000 ) [a,b] (5 \le a < b \le 100,000,000…

學習筆記,DbContext context 對象是保存了所有用戶對象嗎

DbContext 并不會將所有用戶對象保存在內存中&#xff1a; DbContext 是 Entity Framework Core (EF Core) 的數據庫上下文&#xff0c;它是一個數據庫訪問的抽象層它實際上是與數據庫的一個連接會話&#xff0c;而不是數據的內存緩存當您通過 _context.Users 查詢數據時&…

本地命令行啟動服務并連接MySQL8

啟動服務命令 net start mysql8 關閉服務命令 net stop mysql8 本地連接MySQL數據庫mysql -u [用戶名] -p[密碼] 這里&#xff0c;我遇到了個問題 —— 啟動、關閉服務時&#xff0c;顯示 “發生系統錯誤 5。拒絕訪問。 ” 解法1&#xff1a;在 Windows 上以管理員身份打開…

數據蒸餾:Dataset Distillation by Matching Training Trajectories 論文翻譯和理解

一、TL&#xff1b;DR 數據集蒸餾的任務是合成一個較小的數據集&#xff0c;使得在該合成數據集上訓練的模型能夠達到在完整數據集上訓練的模型相同的測試準確率&#xff0c;號稱優于coreset的選擇方法本文中&#xff0c;對于給定的網絡&#xff0c;我們在蒸餾數據上對其進行幾…

【spring cloud Netflix】Ribbon組件

1.基本概念 SpringCloud Ribbon是基于Netflix Ribbon 實現的一套客戶端負載均衡的工具。簡單的說&#xff0c;Ribbon 是 Netflix 發布的開源項目&#xff0c;主要功能是提供客戶端的軟件負載均衡算法&#xff0c;將 Netflix 的中間層服務連接在一 起。Ribbon 的客戶端組件提供…

P1036 [NOIP 2002 普及組] 選數(DFS)

題目描述 已知 n 個整數 x1?,x2?,?,xn?&#xff0c;以及 1 個整數 k&#xff08;k<n&#xff09;。從 n 個整數中任選 k 個整數相加&#xff0c;可分別得到一系列的和。例如當 n4&#xff0c;k3&#xff0c;4 個整數分別為 3,7,12,19 時&#xff0c;可得全部的組合與它…

在響應式網頁的開發中使用固定布局、流式布局、彈性布局哪種更好

一、首先看下固定布局與流體布局的區別 &#xff08;一&#xff09;固定布局 固定布局的網頁有一個固定寬度的容器&#xff0c;內部組件寬度可以是固定像素值或百分比。其容器元素不會移動&#xff0c;無論訪客屏幕分辨率如何&#xff0c;看到的網頁寬度都相同。現代網頁設計…

二分查找與二叉樹中序遍歷——面試算法

目錄 二分查找與分治 循環方式 遞歸方式 元素中有重復的二分查找 基于二分查找的拓展問題 山脈數組的頂峰索引——局部有序 旋轉數字中的最小數字 找缺失數字 優化平方根 中序與搜索樹 二叉搜索樹中搜索特定值 驗證二叉搜索樹 有序數組轉化為二叉搜索樹 尋找兩個…

字符串——面試考察高頻算法題

目錄 轉換成小寫字母 字符串轉化為整數 反轉相關的問題 反轉字符串 k個一組反轉 僅僅反轉字母 反轉字符串里的單詞 驗證回文串 判斷是否互為字符重排 最長公共前綴 字符串壓縮問題 轉換成小寫字母 給你一個字符串 s &#xff0c;將該字符串中的大寫字母轉換成相同的…

現代復古電影海報品牌徽標設計襯線英文字體安裝包 Thick – Retro Vintage Cinematic Font

Thick 是一種大膽的復古字體&#xff0c;專為有影響力的標題和懷舊的視覺效果而設計。其厚實的字體、復古魅力和電影風格使其成為電影海報、產品標簽、活動品牌和編輯設計的理想選擇。無論您是在引導電影的黃金時代&#xff0c;還是在現代布局中注入復古活力&#xff0c;Thick …

[C++面試] new、delete相關面試點

一、入門 1、說說new與malloc的基本用途 int* p1 (int*)malloc(sizeof(int)); // C風格 int* p2 new int(10); // C風格&#xff0c;初始化為10 new 是 C 中的運算符&#xff0c;用于在堆上動態分配內存并調用對象的構造函數&#xff0c;會自動計算所需內存…

Unity URP管線與HDRP管線對比

1. 渲染架構與底層技術 URP 渲染路徑&#xff1a; 前向渲染&#xff08;Forward&#xff09;&#xff1a;默認單Pass前向&#xff0c;支持少量實時光源&#xff08;通常4-8個逐物體&#xff09;。 延遲渲染&#xff08;Deferred&#xff09;&#xff1a;可選但功能簡化&#…

JDK8卸載與安裝教程(超詳細)

JDK8卸載與安裝教程&#xff08;超詳細&#xff09; 最近學習一個項目&#xff0c;需要使用更高級的JDK&#xff0c;這里記錄一下卸載舊版本與安裝新版本JDK的過程。 JDK8卸載 以windows10操作系統為例&#xff0c;使用快捷鍵winR輸入cmd&#xff0c;打開控制臺窗口&#xf…

python爬蟲:DrissionPage實戰教程

如果本文章看不懂可以看看上一篇文章&#xff0c;加強自己的基礎&#xff1a;爬蟲自動化工具&#xff1a;DrissionPage-CSDN博客 案例解析&#xff1a; 前提&#xff1a;我們以ChromiumPage為主&#xff0c;寫代碼工具使用Pycharm&#xff08;python環境3.9-3.10&#xff09; …

07-01-自考數據結構(20331)- 排序-內部排序知識點

內部排序算法是數據結構核心內容,主要包括插入類(直接插入、希爾)、交換類(冒泡、快速)、選擇類(簡單選擇、堆)、歸并和基數五大類排序方法。 知識拓撲 知識點介紹 直接插入排序 定義:將每個待排序元素插入到已排序序列的適當位置 算法步驟: 從第二個元素開始遍歷…

Go語言-初學者日記(八):構建、部署與 Docker 化

&#x1f9f1; 一、go build&#xff1a;最基礎的構建方式 Go 的構建工具鏈是出了名的輕量、簡潔&#xff0c;直接用 go build 就能把項目編譯成二進制文件。 ? 構建當前項目 go build -o myapp-o myapp 指定輸出文件名默認會構建當前目錄下的 main.go 或 package main &a…