鳥瞰linux的檔案讀寫全過程

2021-05-22 09:07:20 字數 4154 閱讀 3174

讀寫檔案,是作為乙個作業系統所提供的最基本介面之一。

我們就從寫檔案過程:open,write,close這幾個介面來說起,描述寫檔案的那些事兒。

平時,我們做應用程式的時候,常常用到讀寫檔案的函式介面,就拿寫檔案來說,我們用c/c++編寫時,用到了以下的函式介面:

1> 

file* fopen(const char* restrict filename,const char* restrict mode);

2>  size_t fwrite(const void* restrict buffer,size_t size,size_t n,file * restrict fp);

3>  int fclose(file * fp) ;

以上這幾個函式介面大家都比較熟悉,如果按照這個來分析似乎更加明了。然而,上面的這些介面已經是現代版本的介面,其實現依賴於現在的成熟系統,分析現行 系統的龐大**我還嫩了點,所以就拿過去版本的linux系統和一些原始介面進行分析吧。(其實大家都知道,現行作業系統核心的**量已經不是乙個人一輩 子能看完的了,我們主要是借鑑linux的系統思想,去作我們自己的嵌入式作業系統)

老版本的介面是這個樣子的:

1>  int open(const char* filename,int flag,...) ;

2>  int write(int fildes,const char* buf,off_t count) ;

3>  int close(int fildes) ;

這幾個介面的宣告在標頭檔案中,實現在系統的lib庫檔案中,所以使用的時候,我們只需要包含幾個相應的標頭檔案,然後使用介面,在編譯的時候,編譯器把 lib庫檔案中的二進位制實現鏈結進去,這樣就行了。

當然,僅僅是使用不是本文的目的,我們是要**的是這個使用的背後是什麼,作業系統為我們做了什麼。

首先,庫檔案中的open是怎麼實現的呢?

int open(const char * filename,int flag,...)

庫檔案中的open函式封裝了彙編**「

呼叫系統 」,這個系 統呼叫的

返回值 通過

eax暫存器 傳遞給了

res , 系統呼叫的

輸入引數 分別存放在

ebx,ecx,edx暫存器 中。

系統呼叫是乙個中斷,是由組合語言

int中斷號 促發,所以好多教材上稱其為軟中斷或軟體中斷。

系統中斷中斷發生,cpu停止當前任務的處理,把使用者態的五個關鍵資訊儲存在核心態棧中,分別是:eflag,ss,esp,eip和cs暫存器,他們記 錄著程序使用者態的關鍵資訊(恢復使用者態執行時用到),把他們壓棧到核心棧中。當然,核心棧位址在程序結構資訊中早有記錄,上邊的五個暫存器的

使用者態資訊儲存與賦予核心態資訊 這個過程是由cpu自動完成的,只要我們 在前邊的任務資料結構中設定好了就行。

任務執行在核心態中,這裡有一切系統的**(包括各種中斷處理程式和檔案系統以及各種裝置的驅動程式)。

呃。。。寫部落格好費時間啊,不過也是個再次詳細學習的過程,值了,畢竟能說出來才說明掌握的透徹,不像現在,邊寫邊翻資料。。。吃飯去了,回來再寫。。。

呵呵,再次拿起來這個帖子,都過去一周了。接著寫,總比玩遊戲強。

依據中斷向量表的設定,程式執行到軟中斷處理程式的入口處(此時,使用者態的關鍵資訊eflag,ss,esp,eip和cs都已經儲存到核心棧中了),在 這裡(是用彙編寫的)手工壓棧儲存使用者態的其他資訊,注意,這裡的儲存,在中斷退出時,還要手工退棧恢復原:

push %ds

push %es

push %fs

pushl

%eax

pushl %edx

pushl %ecx

pushl %ebx

movl

$0x10 ,%edx            #0x10即

0001,0

000 (綠色 兩位是請求特權級,紅色 一 位是gdt(0)/ldt(1),藍色 四位是第幾項),這麼 說,edx暫存器中放的是gdt表的第0x001,0 +1項 (即第3項,0x0000是第一項),是系統資料段的選擇符 。#把

系統資料段的 選擇符

放入ds和es暫存器中,則用到的資料都將是系統資料段(ds指示)中的資料。

mov %dx,%ds

mov %dx,%es

movl

$0x17, %edx           #0x17即

0001,0

111 (綠色 兩位是請求特權級,紅色 一 位是gdt(0)/ldt(1),藍色 四位是第幾項),這麼 說,edx暫存器中存放的是ldt表的第

0x001,0 +1 項(即第3項,0x0000是第一項),是使用者態資料段的選擇符 。#把

使用者態資料段 的選擇符

放入fs 寄 存器中,則可以通過fs暫存器來實現從核心態 讀/寫使用者態 的資料(在核心中,有好多這樣的操作,諸 如:get_fs_long(),set_fs_long(),get_fs_word(),set_fs_word()等等)。

mov %dx,

%fs......

call _sys_call_table(,%eax,4)

pushl

%eax       #把eax中的返回值壓入棧

......

#中斷返回時候,手工恢復各暫存器成使用者態時的內容

popl

%eax         #儲存著系統呼叫的返回值,放入eax,在使用者態的庫函式open中的返回值就是通過這裡的eax傳遞的。

popl %ebx

popl %ecx

popl %edx

#此時,理論上應該 popl %eax 了,但是。。。

addl $4,%esp      #放棄 系統中斷呼叫時的 壓棧儲存的eax(上邊

紅色eax 保 存著

軟 中斷的向量號碼)

pop %fs

pop %es

pop %ds

iret                     #中斷返回

上邊的call _sys_call_table(,%eax,4)就是呼叫具體的系統呼叫 中斷處理函式,_sys_call_table是定義在其他檔案中定義的函式指標

陣列 :

fn_ptr sys_call_table[sys_setup,sys_exit,sys_fork,sys_read,sys_write,sys_open ,......];

fn_ptr :typedef int (*fn_ptr)() ;

sys_open:extern int sys_open() ;

可以看到,sys_open在陣列的第六項,這就對應了上邊在使用者態定義的

#define __nr_open  5

而extern int sys_open對應的

實現函式在另外的檔案fs/open.c 所實現:

int sys_open(const char* filename,int flag,int mode)

f = 0 + file_table ;

for(i=0;if_count) break ;

}current->filp[fd] = f ;

open_namei( filename,flag,mode,&inode

) ;f->f_mode = inode->i_mode ;

f->f_flags = flag ;

f->f_count = 1 ;

f->f_inode = inode ;

f->f_pos = 0 ;

return (fd) ; 

}int open_namei(const char* pathname,int flag,int mod,struct m_inode ** res_inode)

inr = de->inode ;

dev = dir->i_dev ;

brelse(bh) ;  

inode = follow_link(dir,iget(dev,inr)) ;

*res_inode = inode ;

return 0 ;

}呵呵,檔案系統是作業系統最複雜的部分,所以**量也最大,先寫到這裡,再有空了接著寫,總不能不工作,天天寫這個東西呀。這是學習,學習就是為了更好的 工作,不工作了哪行。下次接著寫,呵呵

LED燈驅動編寫全過程

led驅動實驗步驟 準備工作 led驅動 1 準備測試的工具 2 準備相關字元裝置驅動的知識 3 準備相關驅動 led 檔案 4 準備相關的硬體知識,獲取硬體開發人員提供的led介面。driver char psled player 701 kill 701 ledtest 0 0 ledtest ...

linux下安裝mongodb全過程

mongodb linux x86 64 amazon latest.tgz mongodb linux i686 v3.0 latest.tgz 2.解壓 tar xzf mongodb linux x86 64 amazon latest.tgz 3.進入到解壓目錄,將bin拷貝到常用的集中安裝...

Linux系統啟動全過程

分為兩部分,第一部分是硬體本身需要載入的資訊,之後才是載入linux相關資訊,因為有裝有雙系統的機器嘛 1.計算機加電 2.bios開始執行,檢測硬體 cpu 記憶體 硬碟等 3.bios讀取cmos儲存器中的引數,選擇啟動裝置 就是我們安裝系統時很常見的那也選擇引導頁面,預設都是第乙個選項從本地 ...