Linux FILE 結構詳解

來源:互聯網
上載者:User
 在Linux裡,每一個檔案都有一個file結構和inode結構,inode結構是用來讓Kernel做管理的,而file結構則是我們平常對檔案讀寫或開啟,關閉所使用的。當然,從user的觀點來看是看不出什麼的。在Linux裡,檔案的觀念應用的蠻廣泛的,甚至是寫一個driver你也只要提供一組的file operations就可以完成了。我們現在來看看File結構的內容。 
  
  struct file { 
  struct file *f_next,**f_pprev; 
  struct dentry *f_dentry; 
  struct file_operations *f_op; 
  mode_t f_mode; 
  loff_t f_pos; 
  unsigned int f_count,f_flags; 
  unsigned long f_reada,f_ramax,f_raend,f_ralen,f_rawin; 
  struct fown_struct f_owner; 
  unsigned long f_version; 
  void *private_data; 
  }; 
  
  比起super_block和inode結構,file結構就顯得小多了,file結構也是用串列來管理,f_next會指到下一個file結構,而f_pprev則是會指到上一個file結構地址的地址,不過,這個欄位的用法跟一般指到前一個file結構的用法不太一樣,有機會再跟各位討論,f_dentry會記錄其inode的dentry地址,f_mode為檔案存取的種類,f_pos則是目前檔案的offset,每次讀寫都從offset記錄的位置開始讀寫,f_count是此file結構的reference cout,f_flags則是開啟此檔案的模式,f_reada,f_ramax,f_raend,f_ralen,f_rawin則是控制read ahead的參數,f_owner記錄了要接收SIGIO和SIGURG的行程ID或行程群組ID,private_data則是tty driver所使用的欄位。最後,我們來看看f_op這個欄位。這個欄位記錄了一組的函式是專門用來使用檔案的。 
  
  · llseek(file,offset,where) 
  
  我們寫程式會呼叫lseek()系統呼叫設定從檔案那個位置開始讀寫,這個函式你可以不用提供,因為系統已經有一個寫好的,但是系統提供的llseek()沒有辦法讓你將where設為SEEK_END,因為系統不知道你的檔案長度是多少,所以沒辦法提供這樣的服務。如果你不提供llseek()的話,那系統會直接使用它已經有的llseek()。llseek()必須要將file->offset的值做改新。 
  
  · read(file,buf,buflen,poffset) 
  
  當我們讀取一個檔案時,最終就是會呼叫read()這個函式來讀取檔案內容。這些參數VFS會替我們準備好,至於poffset則是offset的指標,這是要告訴read()從那裡開始讀,讀完之後必須更新poffset的內容。請注意,在這裡buf是一個地址,而且是一個位於user space的地址。 
  
  · write(file,buf,buflen,poffset) 
  
  write()的動作就跟read()是相反的,參數也都一樣,buf依然是位於user space的地址。 
  
  · readdir(file,dirent,filldir) 
  
  這是用來讀取目錄的下一個direntry的,其中file是file結構地址,dirent則是一個readdir_callback結構,這個結構裡包含了使用者呼叫readdir()系統呼叫時所傳過去的dirent結構地址,filldir則是一個函式指標,這個函式在VFS已經有提供了,這個函式其實是增加了kernel在讀取dirent方面的彈性。當檔案系統的readdir()被呼叫時,在它把下一個dirent取出來之後,應該要呼叫filldir(),讓它把所需的資料寫到user space的dirent結構裡,也許還會多做些處理。有興趣的朋友可以參考的filldir()函式。 
  
  · poll(file,poll_table) 
  
  之前的Kernel版本本來是在file_operations結構裡有select()函式而不是poll()函式的。但是,這並不代表Linux不提供select()系統呼叫,相反的,Linux仍然提供select()系統呼叫,只不過select()系統呼叫implement的方式是使用poll()函式來做的。 
  
  · ioctl(inode,file,cmd,arg) 
  
  ioctl()這個函式其實有很大的用途,尤其它可以做為user space的程式對Kernel的一個溝通管道。那ioctl()是什麼時候被呼叫呢? 還記得平常寫程式時偶而會用到ioctl()系統呼叫來直接控制檔案或device嗎? 是的,ioctl()系統呼叫最後就是把命令交給檔案的f_op->ioctl()來執行。f_op->ioctl()要做的事很簡單,只要根據cmd的值,做出適當的行為,並傳回值即可。但是,ioctl()系統呼叫其實是分幾個步驟的,第一,系統有幾個內定的command它自己可以處理,在這種情形下,它不會呼叫f_op->ioctl()來處理。如果user指定的command是以下的一種,那VFS會自己處理。 
  
  o FIONCLEX 
  
  清除檔案的close-on-exec位。 
  
  o FIOCLEX 
  
  設定檔案的close-on-exec位。 
  
  o FIONBIO 
  
  如果arg傳過來的值為0的話,就將檔案的O_NONBLOCK屬性去掉,但是如果不等於0的話,就將O_NONBLOCK屬性設起來。 
  
  o FIOASYNC 
  
  如果arg傳過來的值為0的話,就將檔案的O_SYNC屬性去掉,但是如果不等於0的話,就將O_SYNC屬性設起來。只是在Kernel 2.2.1時,這個屬性的功能還沒完成。 
  
  如果cmd的值不是以上數種,而且如果file所代表的不是普通的檔案的話,像是device之類的特殊檔案,VFS會直接呼叫f_op->ioctl()去處理。但是如果file代表普通檔案的話,那VFS會呼叫file_ioctl()做另外的處理。何謂另外的處理呢? file_ioctl()會再過澽一次cmd的值,如果是以下數種,它會先做些處理,然後再呼叫f_op->ioctl(),不管怎麼樣,file_ioctl()最後都會再呼叫f_op->ioctl()去處理。 
  
  o FIBMAP 
  
  先將arg指到的檔案block number取出來,並呼叫f_op->bmap()計算出其disk上的block number,最後再將計算出來的block number放到arg參數裡。 
  
  o FIGETBSZ 
  
  先取得檔案系統block的大小並放入arg的參數裡。 
  
  o FIONREAD 
  
  將檔案剩下尚未讀取的長度寫到arg裡。比方說檔案大小是1000,而f_op->offset的值是300,表示還有700個byte尚未讀取,所以,將700寫到arg參數裡。 
  
  · mmap(file,vmarea) 
  
  這個函式是用來將檔案的部分內容映像到記憶體中的,file是指要被映像的檔案,而vmarea則是用來描述到映像到記憶體的那裡。 
  
  · open(inode,file) 
  
  當我們呼叫open()系統呼叫來開啟檔案時,open()會把所有的事都做好,最後則會呼叫f_op->open()看檔案系統是否要做些什麼事,一般來講,VFS已經把事做好了,所以很多系統事實上根本不提供這個函式,當然,你要提供也可以,比方說,你可以在這個函式裡計算這個檔案系統的檔案被使用過多少次等。 
  
  · flush(file) 
  
  這個函式也是新增加的,這個函式是在我們呼叫close()系統呼叫來關閉檔案時所呼叫的。只要你呼叫close()系統呼叫,那close()就會呼叫flush(),不管那個時候f_count的值是否為0。這個函式我不是很確定在做什麼的,事實上,在Ext2裡也沒有提供這麼一個函式,也許是在關閉檔案之前,VFS允許檔案系統先做些backup的動作吧。 
  
  · release(inode,file) 
  
  這個函式也是在close()系統呼叫裡使用的,當然,不盡在close()中使用,在別的地方也是有使用到。基本上,這個函式的定位跟open()很像,不過,當我們對一個檔案呼叫close()時,只有當f_count的值歸0時,VFS才會呼叫這個函式做處理。一般來講,如果你在open()時配置了一些東西,那應該在release()時將配置的東西釋放掉。至於f_count的值則是不用在open()和release()中控制,VFS已經在fget()和fput()中增減f_count了。 
   
  
  · fsync(file,dentry) 
  
  fsync()這個函式主要是由buffer cache所使用,它是用來跟file這個檔案的資料寫到disk上。事實上,Linux裡有兩個系統呼叫,fsync()和fdatasync(),都是呼叫f_op->fsync()。它們幾乎是一模一樣的,差別在於fsync()呼叫f_op->fsync()之前會使用semaphore將f_op->fsync()設成critical section,而fdatasync()則是直接呼叫f_op->fsync()而不設semaphore。 
  
  · fasync(fd,file,on) 
  
  當我們呼叫fcntl()系統呼叫,並使用F_SETFL命令來設定檔案的參數時,VFS就會呼叫fasync()這個函式,而當讀寫檔案的動作完成時,行程會收到SIGIO的訊息。 
  
  · check_media_change(dev) 
  
  這個函式只對可以使用可移動的disk的block device有效而已,像是MO,CDROM,floopy disk等等。為什麼對這些可以把disk隨時抽取的需要提供這麼一個函式呢? 其實,從字面上我們大概可以知道,這是用來檢查disk是否換過了,以CDROM為例,每一個光碟片片都代表一個檔案系統,如果今天我們把光碟片片換掉了,那表示這個檔案系統不存在了,如果user此時去讀取這個檔案系統的資料,那會發生什麼事? 很有可能系統就這麼出事了。所以,對於這種的device,每當在mount時,我們就必須檢查其中的disk是否換過了,如何檢查呢? 當然只有檔案系統本身才知道,所以,檔案系統必須提供此函式。 
  
  · revalidate(dev) 
  
  這個函式跟上面的check_media_change()有著相當的關係。當user執行mount要掛上一個檔案系統時,mount會先呼叫裡的check_disk_change(),如果檔案系統所屬的device有提供這個函式的話,那check_disk_change()會先呼叫f_op->check_media_change()來檢查是否其中的disk有換過,如果有則呼叫invalidate_inodes()和invalidate_buffers()將跟原本disk有關的buffer或inode都設為無效,如果檔案系統所屬的device還有提供revalidate()的話,那就再呼叫revalidate()將此device的資料記錄好。 
  
  · lock(file,cmd,file_lock) 
  
  這個函式也是新增加的,在Linux裡,我們可對一
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.