讀寫文件,是作為一個(gè)操作系統(tǒng)所提供的最基本接口之一。
我們就從寫文件過程:open,write,close這幾個(gè)接口來說起,描述寫文件的那些事兒。
平時(shí),我們做應(yīng)用程序的時(shí)候,常常用到讀寫文件的函數(shù)接口,就拿寫文件來說,我們用C/C++編寫時(shí),用到了以下的函數(shù)接口:
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) ;
以上這幾個(gè)函數(shù)接口大家都比較熟悉,如果按照這個(gè)來分析似乎更加明了。然而,上面的這些接口已經(jīng)是現(xiàn)代版本的接口,其實(shí)現(xiàn)依賴于現(xiàn)在的成熟系統(tǒng),分析現(xiàn)行系統(tǒng)的龐大代碼我還嫩了點(diǎn),所以就拿過去版本的linux系統(tǒng)和一些原始接口進(jìn)行分析吧。(其實(shí)大家都知道,現(xiàn)行操作系統(tǒng)內(nèi)核的代碼量已經(jīng)不是一個(gè)人一輩子能看完的了,我們主要是借鑒linux的系統(tǒng)思想,去作我們自己的嵌入式操作系統(tǒng))
老版本的接口是這個(gè)樣子的:
1>?
?int open(const char* filename,int flag,...) ;
2>?
?int write(int fildes,const char* buf,off_t count) ;
3>?
?int close(int fildes) ;
這幾個(gè)接口的聲明在頭文件中,實(shí)現(xiàn)在系統(tǒng)的LIB庫(kù)文件中,所以使用的時(shí)候,我們只需要包含幾個(gè)相應(yīng)的頭文件,然后使用接口,在編譯的時(shí)候,編譯器把LIB庫(kù)文件中的二進(jìn)制實(shí)現(xiàn)鏈接進(jìn)去,這樣就行了。
當(dāng)然,僅僅是使用不是本文的目的,我們是要探究的是這個(gè)使用的背后是什么,操作系統(tǒng)為我們做了什么。
首先,庫(kù)文件中的open是怎么實(shí)現(xiàn)的呢?
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 ;
}
庫(kù)文件中的open函數(shù)封裝了匯編代碼“
調(diào)用系統(tǒng)
”,這個(gè)系統(tǒng)調(diào)用的
返回值
通過
eax寄存器
傳遞給了
res
,系統(tǒng)調(diào)用的
輸入?yún)?shù)
分別存放在
ebx,ecx,edx寄存器
中。
系統(tǒng)調(diào)用是一個(gè)中斷,是由匯編語言
int
?
中斷號(hào)
促發(fā),所以好多教材上稱其為軟中斷或軟件中斷。
系統(tǒng)中斷中斷發(fā)生,cpu停止當(dāng)前任務(wù)的處理,把用戶態(tài)的五個(gè)關(guān)鍵信息保存在內(nèi)核態(tài)棧中,分別是:eflag,ss,esp,eip和cs寄存器,他們記錄著進(jìn)程用戶態(tài)的關(guān)鍵信息(恢復(fù)用戶態(tài)運(yùn)行時(shí)用到),把他們壓棧到內(nèi)核棧中。當(dāng)然,內(nèi)核棧地址在進(jìn)程結(jié)構(gòu)信息中早有記錄,上邊的五個(gè)寄存器的
用戶態(tài)信息保存與賦予內(nèi)核態(tài)信息
這個(gè)過程是由CPU自動(dòng)完成的,只要我們?cè)谇斑叺娜蝿?wù)數(shù)據(jù)結(jié)構(gòu)中設(shè)置好了就行。
任務(wù)運(yùn)行在內(nèi)核態(tài)中,這里有一切系統(tǒng)的代碼(包括各種中斷處理程序和文件系統(tǒng)以及各種設(shè)備的驅(qū)動(dòng)程序)。
呃。。。寫博客好費(fèi)時(shí)間啊,不過也是個(gè)再次詳細(xì)學(xué)習(xí)的過程,值了,畢竟能說出來才說明掌握的透徹,不像現(xiàn)在,邊寫邊翻資料。。。吃飯去了,回來再寫。。。
呵呵,再次拿起來這個(gè)帖子,都過去一周了。接著寫,總比玩游戲強(qiáng)。
依據(jù)中斷向量表的設(shè)置,程序運(yùn)行到軟中斷處理程序的入口處(此時(shí),用戶態(tài)的關(guān)鍵信息eflag,ss,esp,eip和cs都已經(jīng)保存到內(nèi)核棧中了),在這里(是用匯編寫的)手工壓棧保存用戶態(tài)的其他信息,注意,這里的保存,在中斷退出時(shí),還要手工退棧恢復(fù)原:
push %ds
push %es
push %fs
pushl?
%eax
pushl %edx
pushl %ecx
pushl %ebx
???
movl?
$0x10
,%edx?
?
?
?
?
?
?
?
?
?
?
?#0x10即
0001,0
0
00?
(
綠色
兩位是請(qǐng)求特權(quán)級(jí),
紅色
一位是GDT(0)/LDT(1),
藍(lán)色
四位是第幾項(xiàng)),這么說,edx寄存器中放的是GDT表的第
0x001,0
+1項(xiàng)(即第3項(xiàng),0x0000是第一項(xiàng)),是
系統(tǒng)數(shù)據(jù)段的選擇符
。
#把
系統(tǒng)數(shù)據(jù)段的選擇符
放入ds和es寄存器中,則用到的數(shù)據(jù)都將是系統(tǒng)數(shù)據(jù)段(ds指示)中的數(shù)據(jù)。
mov %dx,%ds
mov %dx,%es
movl?
$0x17,
%edx?
?
?
?
?
?
?
?
?
?
?#0x17即
0001,0
1
11?
(
綠色
兩位是請(qǐng)求特權(quán)級(jí),
紅色
一位是GDT(0)/LDT(1),
藍(lán)色
四位是第幾項(xiàng)),這么說,edx寄存器中存放的是LDT表的第
0x001,0
+1項(xiàng)(即第3項(xiàng),0x0000是第一項(xiàng)),是
用戶態(tài)數(shù)據(jù)段的選擇符
。
#把
用戶態(tài)數(shù)據(jù)段的選擇符
放入
fs
寄存器中,則可以通過fs寄存器來實(shí)現(xiàn)從
內(nèi)核態(tài)
讀/寫
用戶態(tài)
的數(shù)據(jù)(在內(nèi)核中,有好多這樣的操作,諸如:get_fs_long(),set_fs_long(),get_fs_word(),set_fs_word()等等)。
mov %dx,
%fs
......
call _sys_call_table(,%eax,4)
pushl?
%eax???????
#把eax中的返回值壓入棧
......
#中斷返回時(shí)候,手工恢復(fù)各寄存器成用戶態(tài)時(shí)的內(nèi)容
popl?
%eax
?
?
?
?
?
?
?
?#保存著系統(tǒng)調(diào)用的返回值,放入eax,在用戶態(tài)的庫(kù)函數(shù)open中的返回值就是通過這里的eax傳遞的。
popl %ebx
popl %ecx
popl %edx
#此時(shí),理論上應(yīng)該 popl %eax 了,但是。。。
addl $4,%esp
?
?
?
?
?#放棄 系統(tǒng)中斷調(diào)用時(shí)的 壓棧保存的eax(上邊
紅色eax
保存著
軟
中斷的向量號(hào)碼)
pop %fs
pop %es
pop %ds
iret?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?#中斷返回
上邊的
call _sys_call_table(,%eax,4)
就是調(diào)用具體的系統(tǒng)調(diào)用 中斷處理函數(shù),_sys_call_table是定義在其他文件中定義的
函數(shù)指針
數(shù)組
:
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在數(shù)組的第六項(xiàng),這就對(duì)應(yīng)了上邊在用戶態(tài)定義的
#define __NR_open??5
而
?
extern int sys_open
對(duì)應(yīng)的
實(shí)現(xiàn)函數(shù)在另外的文件
fs/open.c
所實(shí)現(xiàn):
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 ;
}
呵呵,文件系統(tǒng)是操作系統(tǒng)最復(fù)雜的部分,所以代碼量也最大,先寫到這里,再有空了接著寫,總不能不工作,天天寫這個(gè)東西呀。這是學(xué)習(xí),學(xué)習(xí)就是為了更好的工作,不工作了哪行。下次接著寫,呵呵
轉(zhuǎn)自: http://blog.sina.com.cn/s/blog_61869e800100ek8w.html
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

