http://blog.sina.com.cn/s/blog_61869e800100ek8w.html
讀寫檔案,是作為一個作業系統所提供的最基本接口之一。
我們就從寫檔案過程: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,...){
register int res ;
va_list arg ;
va_start(arg,flag) ;
__asm__("int $0x80"
:"=a"(res)
:""(__NR_open),"b"(filename),"c"(flag),"d"(va_arg(arg,int))
);
if(res>=0)
return res ;
errno = -res ;
return -1 ;
}
庫檔案中的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 0 00 (綠色 兩位是請求特權級,紅色 一 位是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 1 11 (綠色 兩位是請求特權級,紅色 一 位是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){
struct m_inode * inode ;
struct file * f ;
int i,fd ;
for(fd=0;fd<NR_OPEN;fd++){
if( !current->filp[fd] ) break ;
}
f = 0 + file_table ;
for(i=0;i<NR_FILE;i++,f++){
if(!f->f_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){
struct m_inode * dir,* inode ;
struct buffer_head * bh ;
struct dir_entry * de ;
dir = dir_namei( pathname,&namelen,&basename,NULL) ;
bh = find_empty( &dir,basename,namelen,&de) ;
if(!bh){
inode = new_inode(dir->i_dev) ;
inode->i_uid = current->euid ;
inode->i_mode = mode ;
inode->i_dirt = 1 ;
bh = add_entry(dir,basename,namelen,&de) ;
de->inode = inode->i_num ;
de->b_dirt = 1 ;
brelse(bh) ;
iput(dir) ;
* res_inode = inode ;
return 0 ;
}
inr = de->inode ;
dev = dir->i_dev ;
brelse(bh) ;
inode = follow_link(dir,iget(dev,inr)) ;
*res_inode = inode ;
return 0 ;
}
呵呵,檔案系統是作業系統最複雜的部分,是以代碼量也最大,先寫到這裡,再有空了接着寫,總不能不工作,天天寫這個東西呀。這是學習,學習就是為了更好的 工作,不工作了哪行。下次接着寫,呵呵