讀寫檔案,是作為乙個作業系統所提供的最基本介面之一。
我們就從寫檔案過程: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儲存器中的引數,選擇啟動裝置 就是我們安裝系統時很常見的那也選擇引導頁面,預設都是第乙個選項從本地 ...