java的NIO基礎知識

java的NIO基礎知識

    java的NIO是一個高可複用性的I/O工具,在linux系統中底層使用的epoll工具。將原來的老式的文件流系統,改爲了事件流。要學習NIO,必須瞭解一些預備知識,下面收集一些知識基礎和NIO的特性。
java

一、Linux的文件系統

        linux經過文件系統將硬件存儲設備映射爲一個邏輯文件系統的一個目錄。正統的linux文件系統將硬盤分區劃分爲目錄塊、inode Table區、data block數據區域。一個文件由一個目錄項、inode表和數據區塊組成。Inode包含了文件的屬性(如讀、寫、owner、block地址),數據區域則是文件內容的二進制數據。
node

        當查看某個文件的時候,會先從inode table中查找文件的屬性和數據block地址,再從對應的block讀取數據。
linux

        目錄表
緩存

    

        inode結構:
bash

        

        硬鏈接:是給文件一個副本,同時創建二者之間的鏈接關係。修改其中一個,與其鏈接的文件同時被修改。若是刪除其中任意一個其他的文件將不受影響(因爲目錄表中的數據標識沒有刪完,Inode和Block中的數據就不會被刪掉)。函數

        軟鏈接 : 也叫符號鏈接,他只是對源文件在新的位置創建一個「快捷(借用一下wondows經常使用詞)」,因此,當源文件刪除時,符號鏈接的文件將成爲無源之水->僅僅剩下個文件名了,固然刪除這個鏈接,也不會影響到源文件,但對鏈接文件的使用、引用都是直接調用源文件的。工具

#建立一個硬連接和軟鏈接
$: touch 1.txt
$: echo "aaa" >> 1.txt
$: ln 1.txt 2.txt
$: ln -s 1.txt 3.txt

    從截圖能夠看出硬連接的inode是相同的,也就是它們是一個文件的不一樣目錄表標識。而3.txt的inode和源文件的Inode不一樣,並且沒有Blocks,直接從inode指向1.txt.佈局

二、文件描述符

        文件描述符:內核爲了高效的管理已被打開的文件所建立的索引,是一個非負整數,用於指代被打開的文件,全部執行的I/O操做的系統調用都經過文件描述符。內核爲每個進程維護一個文件描述符表,當線程打開才運行的時候:0(標準輸入:stdin),1(標準輸出),2(標準錯誤)會被默認佔用,打開的其它I/O會從3開始。學習

        文件描述附表大概以下
spa

        一個進程打開的最大文件數是系統內存的10%(以KB計算),也能夠經過下面的命令查看和修改

#查看linux的每一個進程文件描述符的個數(俗稱fd數)
:ulimit -n
1024(centOS7 2^10)
#修改成2^16。8位
:ulimit -HSn 65536

      在實際的應用中會出現下面的運行圖

     

     一、文件描述符表:用戶態 

            ps:已打開的文件在內核中用file結構體表示,文件描述符表(file_struct:進程中)中的指針指向file結構體

     二、內核和用戶程序之間的橋樑

             在file結構體(內核中)中維護File Status Flag(file結構體的成員f_flags)和當前讀寫位置(file結構體的成員f_pos)。在上圖中,進程1和進程2都打開同一文件,可是對應不一樣的file結構體,所以能夠有不一樣的File Status Flag和讀寫位置。file結構體中比較重要的成員還有f_count,表示引用計數(Reference Count),後面咱們會講到,dup、fork等系統調用會致使多個文件描述符指向同一個file結構體,例若有fd1和fd2都引用同一個file結構體,那麼它的引用計數就是2,當close(fd1)時並不會釋放file結構體,而只是把引用計數減到1,若是再close(fd2),引用計數就會減到0同時釋放file結構體,這才真的關閉了文件。

         (    

                f_flags:操做方式

                f_pos:當前讀寫位置

                f_count:統計該file結構體被使用多少次,若是是f_count=0則關閉。

        )

            每一個file結構體都指向一個file_operations結構體,這個結構體的成員都是函數指針,指向實現各類文件操做的內核函數。好比在用戶程序中read一個文件描述符,read經過系統調用進入內核,而後找到這個文件描述符所指向的file結構體,找到file結構體所指向的file_operations結構體,調用它的read成員所指向的內核函數以完成用戶請求。

             在用戶程序中調用lseek、read、write、ioctl、open等函數,最終都由內核調用file_operations的各成員所指向的內核函數完成用戶請求。

             file_operations結構體中的release成員用於完成用戶程序的close請求,之因此叫release而不叫close是由於它不必定真的關閉文件,而是減小引用計數,只有引用計數減到0才關閉文件。

             對於同一個文件系統上打開的常規文件來講,read、write等文件操做的步驟和方法應該是同樣的,調用的函數應該是相同的,因此圖中的三個打開文件的file結構體指向同一個file_operations結構體。

             若是打開一個字符設備文件,那麼它的read、write操做確定和常規文件不同,不是讀寫磁盤的數據塊而是讀寫硬件設備,因此file結構體應該指向不一樣的file_operations結構體,其中的各類文件操做函數由該設備的驅動程序實現。

             file_operations結構體:內核提供給用戶態使用的文件操做函數指針。lseek,write,ioctl,open等 (ps:        release(使用close()時候使用)。內核不必定關閉文件,而是減小f_count。當到0的時候關閉文件   二、同一文件系統使用同一file_operations)

     三、徹底的內核態(和bash同樣經過文件系統直接查找)

            每一個file結構體都有一個指向dentry結構體的指針,「dentry」是directory entry(目錄項)的縮寫。咱們傳給open、stat等函數的參數的是一個路徑,

            例如/home/akaedu/a,須要根據路徑找到文件的inode。爲了減小讀盤次數,內核緩存了目錄的樹狀結構,稱爲dentry cache,其中每一個節點是一個dentry結構體,只要沿着路徑各部分的dentry搜索便可,從根目錄/找到home目錄,而後找到akaedu目錄,而後找到文件a。dentry cache只保存最近訪問過的目錄項,若是要找的目錄項在cache中沒有,就要從磁盤讀到內存中。

      a、從目錄項緩存中找到inode、若是沒有則直接從硬盤中讀取    

            每一個dentry結構體都有一個指針指向inode結構體。inode結構體保存着從磁盤inode讀上來的信息。在上圖的例子中,有兩個dentry,分別表示/home/akaedu/a和/home/akaedu/b,它們都指向同一個inode,說明這兩個文件互爲硬連接。inode結構體中保存着從磁盤分區的inode讀上來信息,例如全部者、文件大小、文件類型和權限位等。每一個inode結構體都有一個指向inode_operations結構體的指針,後者也是一組函數指針指向一些完成文件目錄操做的內核函數。和file_operations不一樣,inode_operations所指向的不是針對某一個文件進行操做的函數,而是影響文件和目錄佈局的函數,例如添加刪除文件和目錄、跟蹤符號連接等等,屬於同一文件系統的各inode結構體能夠指向同一個inode_operations結構體。

     b、直接從硬盤中讀取字節流

            inode結構體有一個指向super_block結構體的指針。super_block結構體保存着從磁盤分區的超級塊讀上來的信息,例如文件系統類型、塊大小等。super_block結構體的s_root成員是一個指向dentry的指針,表示這個文件系統的根目錄被mount到哪裏,在上圖的例子中這個分區被mount到/home目錄下。

            file、dentry、inode、super_block這幾個結構體組成了VFS的核心概念。對於ext2文件系統來講,在磁盤存儲佈局上也有inode和超級塊的概念,因此很容易和VFS中的概念創建對應關係。而另一些文件系統格式來自非UNIX系統(例如Windows的FAT3二、NTFS),可能沒有inode或超級塊這樣的概念,但爲了能mount到Linux系統,也只好在驅動程序中硬湊一下,在Linux下看FAT32和NTFS分區會發現權限位是錯的,全部文件都是rwxrwxrwx,由於它們原本就沒有inode和權限位的概念,這是硬湊出來的。    

最後列下文件描述符的函數:

  open:打開一個文件,並指定訪問該文件的方式,調用成功後返回《 一個文件描述符》。
  creat:打開一個文件,若是該文件不存在,則建立它,調用成功後返回《  一個文件描述符》。
  close:關閉文件,進程對文件所加的鎖全都被釋放。
  read:從文件描述符對應的文件中讀取數據,調用成功後返回《 讀出的字節數》。
  write:向文件描述符對應的文件中寫入數據,調用成功後返回《 寫入的字節數》。
  
  ftruncate:把文件描述符對應的文件縮短到指定的長度,調用成功後返回《 0》。
  lseek:在文件描述符對應的文件裏把文件指針設定到指定的位置,調用成功後返回《 新指針的位置》。
  fsync:將全部已寫入文件中的數據真正寫到磁盤或其餘下層設備上,調用成功後返回《 0》。
  fstat:返回文件描述符對應的文件的相關信息,把結果保存在struct stat中,調用成功後返回《 0》。
  fchown:改變與打開文件相關聯的全部者和全部組,調用成功後返回《 0》。
  fchmod:把文件描述符對應的文件的權限位改成指定的八進制模式,調用成功後返回《 0》。
  flock:用於向文件描述符對應的文件施加建議性鎖,調用成功後返回《 0》。
  fcntl:既能施加建議性鎖也能施增強制性鎖,能創建記錄鎖、讀取鎖和寫入鎖,調用成功後返回《 0》。
  dup:複製文件描述符,返回《 沒使用的文件描述符中最小的編號》。
  dup2:由用戶指定返回的《 文件描述符的值》,用來從新打開或重定向一個文件描述符。
  select:同時從多個文件描述符讀取數據或向多個文件描述符寫入數據
相關文章
相關標籤/搜索