<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      文件隨機或順序讀寫原理深入淺出

      一、文件讀寫的用戶程序、操作系統(tǒng)、磁盤交互原理

        最近為了徹底搞懂文件讀寫原理,我特意查詢了很多資料,包括Java讀寫文件的API代碼、操作系統(tǒng)處理文件以及磁盤硬件知識等。由于網(wǎng)上現(xiàn)存技術(shù)文章,幾乎沒有找到一篇能夠徹底綜合講明白這個原理的文章。心中還是有很多疑問。且有不少文章包括書籍所闡述的隨機/順序讀寫原理講述的都是錯誤或誤導(dǎo)性的。所以我綜合了一下我能查閱到的所有資料,深入細節(jié)知識,給大家徹底講明白這事。原創(chuàng)文章,轉(zhuǎn)發(fā)請保留第一作者著作權(quán)。謝謝!
        如下圖所示。我們編寫的用戶程序讀寫文件時必須經(jīng)過的OS和硬件交互的內(nèi)存模型。

      1、讀文件

        用戶程序通過編程語言提供的讀取文件api發(fā)起對某個文件讀取。此時程序切換到內(nèi)核態(tài),用戶程序處于阻塞狀態(tài)。由于讀取的內(nèi)容還不在內(nèi)核緩沖區(qū)中,導(dǎo)致觸發(fā)OS缺頁中斷異常。然后由OS負責(zé)發(fā)起對磁盤文件的數(shù)據(jù)讀取。讀取到數(shù)據(jù)后,先存放在OS內(nèi)核的主存空間,叫PageCache。然后OS再將數(shù)據(jù)拷貝一份至用戶進程空間的主存ByteBuffer中。此時程序由內(nèi)核態(tài)切換至用戶態(tài)繼續(xù)運行程序。程序?qū)yteBuffer中的內(nèi)容讀取到本地變量中,即完成文件數(shù)據(jù)讀取工作。

      2、寫文件

        用戶程序通過編程語言提供的寫入文件api發(fā)起對某個文件寫入磁盤。此時程序切換到內(nèi)核態(tài)用戶程序處于阻塞狀態(tài),由OS負責(zé)發(fā)起對磁盤文件的數(shù)據(jù)寫入。用戶寫入數(shù)據(jù)后,并不是直接寫到磁盤的,而是先寫到ByteBuffer中,然后再提交到PageCache中。最后由操作系統(tǒng)決定何時寫入磁盤。數(shù)據(jù)寫入PageCache中后,此時程序由內(nèi)核態(tài)切換至用戶態(tài)繼續(xù)運行。
      用戶程序?qū)?shù)據(jù)寫入內(nèi)核的PageCache緩沖區(qū)后,即認為寫入成功了。程序由內(nèi)核態(tài)切換回用于態(tài),可以繼續(xù)后續(xù)的工作了。PageCache中的數(shù)據(jù)最終寫入磁盤是由操作系統(tǒng)異步提交至磁盤的。一般是定時或PageCache滿了的時候?qū)懭搿H绻脩舫绦蛲ㄟ^調(diào)用flush方法強制寫入,則操作系統(tǒng)也會服從這個命令。立即將數(shù)據(jù)寫入磁盤然后由內(nèi)核態(tài)切換回用戶態(tài)繼續(xù)運行程序。但是這樣做會損失性能,但可以確切的知道數(shù)據(jù)是否已經(jīng)寫入磁盤了。

      一、文件讀寫詳細過程

      1、讀文件

        如下所示為一典型Java讀取某文件內(nèi)容的用戶編程代碼。接下來我們詳細解說讀取文件過程。
                  // 一次讀多個字節(jié)
                  byte[] tempbytes = new byte[100];
                  int byteread = 0;
                  in = new FileInputStream(fileName);//
                  ReadFromFile.showAvailableBytes(in);
                  // 讀入多個字節(jié)到字節(jié)數(shù)組中,byteread為一次讀入的字節(jié)數(shù)
                  while ((byteread = in.read(tempbytes)) != -1) { //
                      System.out.write(tempbytes, 0, byteread);
                  }
      View Code
        首先通過位置①的代碼發(fā)起一個open的系統(tǒng)調(diào)用,程序由用戶態(tài)切換到內(nèi)核態(tài)。操作系統(tǒng)通過文件全路徑名在文件目錄中找到目標文件名對應(yīng)的文件iNode標識ID,然后用這個iNode標識ID在iNode索引文件找到目標文件iNode節(jié)點數(shù)據(jù)并加載到內(nèi)核空間中。這個iNode節(jié)點包含了文件的各種屬性(創(chuàng)建時間,大小以及磁盤塊空間占用信息等等)。然后再由內(nèi)核態(tài)切換回用戶態(tài),這樣程序就獲得了操作這個文件的文件描述。接下來就可以正式開始讀取文件內(nèi)容了。
        然后再通位置②,循環(huán)數(shù)次獲取固定大小的數(shù)據(jù)。通過發(fā)起read系統(tǒng)調(diào)用,操作系統(tǒng)通過文件iNode文件屬性中的磁盤塊空間占用信息得到文件起始位的磁盤物理地址。再從磁盤中將要取得數(shù)據(jù)拷貝到PageCache內(nèi)核緩沖區(qū)。然后將數(shù)據(jù)拷貝至用戶進程空間。程序由內(nèi)核態(tài)切換回用戶態(tài),從而可以讀取到數(shù)據(jù),并放入上面代碼中的臨時變量tempbytes中。
        整個過程如下圖所示。
        至于上面說到的操作系統(tǒng)通過iNode節(jié)點中的磁盤塊占用信息去定位磁盤文件數(shù)據(jù)。其細節(jié)過程如下圖所示。

      ①根據(jù)文件路徑從文件目錄中找到iNode ID。

        用戶讀取一個文件,首先需要調(diào)用OS中文件系統(tǒng)的open方法。該方法會返回一個文件描述符給用戶程序。OS首先根據(jù)用戶傳過來的文件全路徑名在目錄索引數(shù)據(jù)結(jié)構(gòu)中找到文件對應(yīng)的iNode標識ID。目錄數(shù)據(jù)是存在于磁盤上的,在OS初始化時就會加載到內(nèi)存中,由于目錄數(shù)據(jù)結(jié)構(gòu)并不會很龐大,一次性加載駐留到內(nèi)存也不是不可以或者部分加載,等需要的時候在從磁盤上調(diào)度進內(nèi)存也可以。根據(jù)文件路徑在目錄中查找效率應(yīng)該是很高的,因為目錄本身就是一棵樹,應(yīng)該也是類似數(shù)據(jù)庫的樹形索引結(jié)構(gòu)。所以它的查找算法時間復(fù)雜度就是O(logN)。具體細節(jié)我暫時還沒弄清楚,這不是重點。
        iNode就是文件屬性索引數(shù)據(jù)了。磁盤格式化時OS就會把磁盤分區(qū)成iNode區(qū)和數(shù)據(jù)區(qū)。iNode節(jié)點就包含了文件的一些屬性信息,比如文件大小、創(chuàng)建修改時間、作者等等。其中最重要的是還存有整個文件數(shù)據(jù)在磁盤上的分布情況(文件占用了哪些磁盤塊)。

      ②根據(jù)iNode ID從Inode索引中找到文件屬性。

        得到iNode標識的ID后,就可以去iNode數(shù)據(jù)中查找到對應(yīng)的文件屬性了,并加載到內(nèi)存,方便后續(xù)讀寫文件時快速獲得磁盤定位。iNode數(shù)據(jù)結(jié)構(gòu)應(yīng)該類似哈希結(jié)構(gòu)了,key就是iNode標識ID,value就是具體某個文件的屬性數(shù)據(jù)對象了。所以它的算法時間復(fù)雜度就是O(1)。具體細節(jié)我暫時還沒弄清楚,這不是重點。
        我們系統(tǒng)中的文件它的文件屬性(iNode)和它的數(shù)據(jù)正文是分開存儲的。文件屬性中有文件數(shù)據(jù)所在磁盤塊的位置信息。

      ③根據(jù)文件屬性中的磁盤空間塊信息找到需要讀取的數(shù)據(jù)所在的磁盤塊的物理位置

        文件屬性也就是iNode節(jié)點這個數(shù)據(jù)結(jié)構(gòu),里面包含了文件正文數(shù)據(jù)在磁盤物理位置上的分布情況。磁盤讀寫都是以塊為單位的。所以這個位置信息其實也就是一個指向磁盤塊的物理地址指針。
        其結(jié)構(gòu)圖如下。
        文件屬性里就包含了文件正文數(shù)據(jù)占有磁盤所有信息。但是由于文件屬性大小有限制,而文件大小沒有限制。這樣會導(dǎo)致磁盤塊占用信息超出限制。所以最后一個磁盤數(shù)據(jù)項設(shè)計為特殊的作用。它是一個指向更多磁盤占用信息數(shù)據(jù)的指針。這些更多信息存放在普通的數(shù)據(jù)區(qū)。這樣當文件iNode加載到內(nèi)存后,可以把其他更多磁盤塊信息一起加載進來。這樣就避免了iNode索引文件太大的問題。
      后續(xù)的文件讀寫系統(tǒng)調(diào)用,由用戶態(tài)切換至內(nèi)核態(tài)。操作系統(tǒng)就可以根據(jù)文件數(shù)據(jù)的相對位置(偏移量)快速從iNode中的磁盤塊占用數(shù)據(jù)結(jié)構(gòu)中找到其對應(yīng)的磁盤物理位置在哪里了。很明顯這個數(shù)據(jù)結(jié)構(gòu)類似哈希結(jié)構(gòu),其算法復(fù)雜度就是O(1)。
        比如我們現(xiàn)在討論的讀取數(shù)據(jù)。每次用戶代碼的api調(diào)用read方法時。由于時從頭開始讀取,所以O(shè)S就從上圖中“磁盤塊0”數(shù)據(jù)項開始迭代,獲取對應(yīng)的物理磁盤塊起始地址開始讀取數(shù)據(jù)并拷貝至PageCache緩沖區(qū),再拷貝至用戶進程緩沖區(qū)。這樣用戶代碼就可以獲取這些數(shù)據(jù)了。
      考慮到另外一種隨機讀的場景。我們并不是把整個文件從頭開始讀一遍。而是需要直接定位到文件的中間某個位置開始 讀取部分內(nèi)容。如下所示。
        RandomAccessFile raf=new RandomAccessFile(new File("D:\\3\\test.txt"), "r");   
                  //獲取RandomAccessFile對象文件指針的位置,初始位置是0  
                  System.out.println("RandomAccessFile文件指針的初始位置:"+raf.getFilePointer());  
                  raf.seek(pointe);//移動文件指針位置  
                  byte[]  buff=new byte[1024];  
                  //用于保存實際讀取的字節(jié)數(shù)  
                  int hasRead=0;  
                  //循環(huán)讀取  
                  while((hasRead=raf.read(buff))>0){  
                      //打印讀取的內(nèi)容,并將字節(jié)轉(zhuǎn)為字符串輸入  
                      System.out.println(new String(buff,0,hasRead));       
                  }  
      View Code
        程序代碼調(diào)用seek方法直接定位到某個文件相對位置開始讀取內(nèi)容。實際上就是調(diào)用了OS管理文件的系統(tǒng)調(diào)用seek函數(shù)。這個系統(tǒng)調(diào)用需要傳遞一個文件相對位置也就是偏移量,不是指磁盤的物理位置。文件的相對位置偏移量是從0開始的,結(jié)束位置和文件的大小字節(jié)數(shù)相等。操作系統(tǒng)拿到這個偏移量后,就可以計算出文件所屬的邏輯塊編號。因為每個塊是固定大小的,所以能計算出來。通過文件屬性的邏輯磁盤塊信息就能得到磁盤塊的物理位置。從而可以快速直接定位到磁盤物理塊讀取到需要的數(shù)據(jù)。這里說的邏輯塊和物理塊的概念是有區(qū)別的。邏輯塊屬于當前的文件從0開始編號,物理塊才是磁盤真正的存放數(shù)據(jù)的區(qū)域,屬于全局的。編號自然不是從0開始的。

      2、寫文件

        寫文件的過程和前面闡述的差不多,相關(guān)的知識點也在讀文件中已經(jīng)順帶描述了。就不在贅述了。這里就說些特別需要注意的點就行。

        ③根據(jù)空閑塊索引找到可以寫入的物理位置并寫入

        如上圖所示,OS寫文件內(nèi)容時首先要訪問磁盤空閑塊索引表。這是個什么東西呢?由于磁盤很大,不可能每次寫數(shù)據(jù)時,都讓磁頭從頭到尾遍歷一次才能找到空閑位置。這樣效率可想而知的差勁。所以O(shè)S會把磁盤上的空閑塊索引起來存放在磁盤某個位置上。后續(xù)磁盤存儲和刪除文件內(nèi)容時都通過這個空閑塊索引表快速定位,同時刪除數(shù)據(jù)也會更新索引表增加空閑塊。
      空閑塊記錄索引的實現(xiàn)常用有兩種,一種是我們熟悉的鏈表結(jié)構(gòu),還有一種是位圖結(jié)構(gòu)。這里就不詳細討論了。

        ④寫入數(shù)據(jù)后更新iNode里的磁盤占用塊索引

        數(shù)據(jù)寫入后,那么這個空閑塊就被占用了,自然也就需要更新下iNode文件屬性里的磁盤占用塊索引數(shù)據(jù)了。
      我們前面說的寫文件都是只講了尾部追加這種方式。但是實際上我們可以通過RandomAccessFile類實現(xiàn)文件隨機位置寫功能。但是我們同時也有一些困惑。為啥不能直接在中間某個位置插入我們要寫的內(nèi)容,而是要先把插入位置后面的內(nèi)容截取放入臨時文件中。插入新內(nèi)容后,再把臨時文件內(nèi)容尾部追加到原來的文件中來實現(xiàn)文件修改?代碼如下所示。
      public static void insert(String fileName, long pos, String insertContent) throws IOException{
              File file = File.createTempFile("tmp", null);
              file.deleteOnExit();
              RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
              FileInputStream fileInputStream = new FileInputStream(file);
              FileOutputStream fileOutputStream = new FileOutputStream(file);
              raf.seek(pos);
              byte[] buff = new byte[64];
              int hasRead = 0;
              while((hasRead = raf.read(buff)) > 0){
                  fileOutputStream.write(buff);
              }
              raf.seek(pos);
              raf.write(insertContent.getBytes());
              //追加文件插入點之后的內(nèi)容
              while((hasRead = fileInputStream.read(buff)) > 0){
                  raf.write(buff, 0, hasRead);
              }
              raf.close();
              fileInputStream.close();
              fileOutputStream.close();
          }
      View Code
        按照我們上面的闡述,寫入的文件內(nèi)容完全可以存入磁盤上的一個新塊,然后更新下iNode屬性里的占用磁盤塊索引數(shù)據(jù)即可。也不需要真的去移動磁盤上的所有數(shù)據(jù)塊。看上去成本也很小,可為啥我們的編程api卻都不支持呢?
        我想答案可能是這樣的。假如允許我們上面那種操作,如果一個很大的文本文件。你現(xiàn)在只是編輯了文本中間某個位置的一個字,即插入了一個文本字符。那么此時這個新增的文本內(nèi)容就得在磁盤上找到一個新的塊存儲下來。這樣是不是有點浪費空間呢?因為磁盤上的一個塊只能分配給一個文件使用,塊大小如果是64kb的話,一個字符也就占用了2個字節(jié)的空間。更要命的是這樣一搞,使得原本滿存狀態(tài)的塊,出現(xiàn)很多不連續(xù)的空洞。這樣就會使得讀取文件時數(shù)據(jù)是不連續(xù)的,系統(tǒng)需要額外信息記錄這些中間的存儲空洞。加大了讀取難度。這就是我猜測的原因。實際上操作系統(tǒng)層面也沒有這種操作插入的系統(tǒng)調(diào)用函數(shù)。故編程語言層面也就沒法支持了。
        操作系統(tǒng)層面給上層應(yīng)用程序提供了寫文件的兩個系統(tǒng)調(diào)用。write和append,其中append是write的限制形式,即只能在文件尾部追加。而write雖然提供了隨機位置寫,但是并不是將新內(nèi)容插入其中,而是覆蓋原有的數(shù)據(jù)。我們平時使用Word文本編輯軟件時,如果對一個很大的文件進行編輯,然后點擊保存,你會發(fā)現(xiàn)很慢。同時你還能看到文件所在的目錄下生成了一個新的處于隱藏狀態(tài)的臨時文件。這些現(xiàn)象也能說明我們上面的觀點。即編輯文件時,需要一個成本很高的過程。如下圖所示。
       

      三、常見認知誤區(qū)澄清

      1、磁盤上文件存儲數(shù)據(jù)結(jié)構(gòu)是鏈表,每一塊文件數(shù)據(jù)節(jié)點里有一個指針指向下一塊數(shù)據(jù)節(jié)點。理解錯誤!

        很多人都知道磁盤存儲一個文件不可能是連續(xù)分配空間的。而是東一塊西一塊的存儲在磁盤上的。就誤以為這些分散的數(shù)據(jù)節(jié)點就像鏈表一樣通過其中一個指針指向下一塊數(shù)據(jù)節(jié)點。如下圖所示。
        怎么說呢?這種方案以前也是有一些文件系統(tǒng)實現(xiàn)過的方案,但是現(xiàn)在常見的磁盤文件系統(tǒng)都不再使用這種落后的方案。而是我前面提到的iNode節(jié)點方案。也就是說磁盤上存儲的文件數(shù)據(jù)塊就是純數(shù)據(jù),沒有其他指針之類的額外信息。之所以我們能順利定位這些數(shù)據(jù)塊,都全靠iNode節(jié)點屬性中磁盤塊信息的指針。

      2、append文件尾部追加方法是順序?qū)懀簿褪谴疟P會分配連續(xù)的空間給文件存儲。理解錯誤!

        這種觀點,包括網(wǎng)上和某些技術(shù)書籍里的作者都有這種觀點。實際上是錯誤的。或許是他們根本沒有細究文件存儲底層OS和磁盤硬件的工作原理導(dǎo)致。我這里就重新總結(jié)糾正一下這種誤導(dǎo)性觀點。
      前面說過,append系統(tǒng)調(diào)用是write的限制形式,即總是在文件末尾追加內(nèi)容。看上去好像是說順序?qū)懭胛募?shù)據(jù),因為是在尾部追加啊!所以這樣很容易誤導(dǎo)大家以為這就是順序?qū)懀创疟P存儲時分配連續(xù)的空間給到文件,減少了磁盤尋道時間。
        事實上,磁盤從來都不會連續(xù)分配空間給哪個文件。這是我們現(xiàn)代文件系統(tǒng)的設(shè)計方案。前面介紹iNode知識時也給大家詳細說明了。所以就不再贅述。我們用戶程序?qū)懳募?nèi)容時,提交給OS的緩沖區(qū)PageCache后就返回了。實際這個內(nèi)容存儲在磁盤哪個位置是由OS決定的。OS會根據(jù)磁盤未分配空間索引表隨機找一個空塊把內(nèi)容存儲進去,然后更新文件iNode里的磁盤占用塊索引數(shù)據(jù)。這樣就完成了文件寫入操作。所以append操作不是在磁盤上接著文件末尾內(nèi)容所在塊位置連續(xù)分配空間的。最多只能說邏輯上是順序的。
        那么邏輯上的隨機寫write是不是會慢呢?根據(jù)前面介紹的原理,應(yīng)該是相同的效率。我們可以做如下測試驗證。使用RandomAccessFile 實現(xiàn),因為只有這個類支持隨機位置寫入,其他寫文件類都只提供尾部追加方式。打開一個20M的文本文件“test.txt”。分別采用如下兩個方法,隨機位置寫入和尾部追加寫入做100000次操作,多次測得大概的平均耗時數(shù)據(jù)。
      // 文件隨機位置寫入 耗時:1000ms
      public static void randomWrite1(String path,String content) throws Exception {
      RandomAccessFile raf=new RandomAccessFile(path,"rw");
      Random random=new Random();
      for(int i=0;i<100000;i++){
      raf.seek(random.nextInt((int)raf.length())); // 在文件隨機位置寫入覆蓋
      raf.write((i+content+System.lineSeparator()).getBytes());
      }
      raf.close();
      }
      // 文件尾部位置寫入 耗時:800ms
      public static void randomWrite2(String path,String content) throws Exception {
      RandomAccessFile raf=new RandomAccessFile(path,"rw");
      for(int i=0;i<100000;i++){
      raf.seek(raf.length()); // 總是在文件尾部追加
      raf.write((i+content+System.lineSeparator()).getBytes());
      }
      raf.close();
      }
      View Code
        看上去采用尾部追加性能略高。實際上也相差不大。多出的200ms只是生成隨機數(shù)消耗的。因為如果說隨機位置寫需要某些人認為的磁盤磁頭來回反復(fù)移動,則性能不可能只差這么一丟丟。實際上我去掉隨機數(shù)生成的代碼,改用固定中間位置寫入,這兩個方法耗時幾乎沒有區(qū)別了。這說明無論是尾部追加還是隨機位置寫入方式,性能都是一樣的。因為根據(jù)前面介紹的原理,OS通過iNode中的磁盤占用塊哈希表,可以快速定位到目標磁盤物理位置,其算法時間復(fù)雜度是O(1)。所以尾部追加也是一樣的定位效率。
      可能也有些人想說怎么不試試BufferWriter、FileWriter等寫入類,效率高幾個數(shù)量級比RandomAccessFile 。我也的確測試過,但是這些類都是采用尾部追加模式,無法和其隨機位置寫入做比較。所以沒法拿出來測試說明。但是這些類寫入性能之所以遠高于直接用RandomAccessFile 尾部追加。我想是因為api方法做了用戶程序?qū)用娴膬?yōu)化,比如批量寫入,批量轉(zhuǎn)化成Byte之類的。而RandomAccessFile 可能就是最原始的直接對接OS系統(tǒng)調(diào)用層的API了。

      3、mmap內(nèi)存映射技術(shù)之所以快,是因為直接把磁盤文件映射到用戶空間內(nèi)存,不走內(nèi)核態(tài)。理解錯誤!

        這也是一種常見的認知誤區(qū),實際上這個技術(shù)是操作系統(tǒng)給用戶程序提供的一個系統(tǒng)調(diào)用函數(shù)。它把文件映射到OS內(nèi)核緩沖區(qū)空間,同時共享給用戶進程,也可以共享給多個用戶進程。映射過程中不會產(chǎn)生實際的數(shù)據(jù)從磁盤真正調(diào)取動作,只有用戶程序需要的時候才會調(diào)入部分數(shù)據(jù)。總之也是和普通文件讀取一樣按需調(diào)取。那么mmap技術(shù)為什么在讀取數(shù)據(jù)時會比普通read操作快幾個數(shù)量級呢?
      上面我們講述了普通讀寫操作的內(nèi)存模型。用戶程序要讀取到磁盤上的數(shù)據(jù)。要經(jīng)歷4次內(nèi)核態(tài)切換以及2次數(shù)據(jù)拷貝操作。那么mmap技術(shù)由于是和用戶進程共享內(nèi)核緩沖區(qū),所以少了一次拷貝操作(數(shù)據(jù)從內(nèi)核緩沖區(qū)到用戶進程緩沖區(qū))。從而大大提高了性能。如下圖所示。

      4、mmap內(nèi)存映射技術(shù)寫文件快是因為順序?qū)懘疟P。理解錯誤!

        上面的問題基本已經(jīng)讓我們理解了mmap技術(shù)的內(nèi)存模型。同樣的,我們寫文件時,由于也少了一次數(shù)據(jù)從用戶緩沖區(qū)到內(nèi)核緩沖區(qū)的拷貝操作。使得我們的寫效率非常的高。并不是很多人認為的數(shù)據(jù)直達磁盤,中間不經(jīng)過內(nèi)核態(tài)切換,并且連續(xù)在磁盤上分配空間寫入。這些理解都是錯誤的。

      5、隨機讀寫文件比順序讀寫文件慢,是因為磁盤移動磁頭來回隨機移動導(dǎo)致。理解錯誤!

        這也是一種常見的誤區(qū)。我看過很多文章都是這樣認為的。其實所有的寫操作在硬件磁盤層面上都是隨機寫。這是由現(xiàn)代操作系統(tǒng)的文件系統(tǒng)設(shè)計方案決定的。我們用戶程序?qū)懭霐?shù)據(jù)提交給OS緩沖區(qū)之后,就與我們沒關(guān)系了。操作系統(tǒng)決定何時寫入磁盤中的某個空閑塊。所有的文件都不是連續(xù)分配的,都是以塊為單位分散存儲在磁盤上。原因也很簡單,系統(tǒng)運行一段時間后,我們對文件的增刪改會導(dǎo)致磁盤上數(shù)據(jù)無法連續(xù),非常的分散。
        當然OS提交PageCache中的寫入數(shù)據(jù)時,也有一定的優(yōu)化機制。它會讓本次需要提交給磁盤的數(shù)據(jù)規(guī)劃好磁頭調(diào)度的策略,讓寫入成本最小化。這就是磁盤調(diào)度算法中的電梯算法了。這里就不深入講解了。
        至于讀文件,順序讀也只是邏輯上的順序,也就是按照當前文件的相對偏移量順序讀取,并非磁盤上連續(xù)空間讀取。即便是seek系統(tǒng)調(diào)用方法隨機定位讀,理論上效率也是差不多的。都是使用iNode的磁盤占用塊索引文件快速定位物理塊。

       

      posted @ 2021-10-25 23:22  月光冷鋒  閱讀(3635)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 久久老熟女一区二区蜜臀| 老司机午夜福利视频| 亚洲人成网线在线播放VA| 欧美白妞大战非洲大炮| 免费吃奶摸下激烈视频| 日韩精品一区二区av在线| 国产精品久久久一区二区| 亚洲国产成人久久综合一区77| 国产精品自拍午夜福利| 日本一区不卡高清更新二区| 亚洲熟女综合色一区二区三区| 人妻互换一二三区激情视频 | 泸水县| 国产精品熟女乱色一区二区| 日本午夜精品一区二区三区电影| 精品国产中文字幕在线看| 日本欧美大码a在线观看| 亚洲成色精品一二三区| 国产成人无码区免费内射一片色欲 | 成人三级视频在线观看不卡 | 九九热免费在线观看视频| 亚洲一区二区在线无码| 欧美在线观看www| 国产欧美性成人精品午夜| 国产精品国产高清国产一区| 55大东北熟女啪啪嗷嗷叫| AV极品无码专区亚洲AV| 国产精品一区二区色综合| 亚洲日本韩国欧美云霸高清| 中文字幕av中文字无码亚 | 亚洲日韩欧美一区二区三区在线| 怡春院久久国语视频免费| 日本韩国一区二区精品| 丁香花在线观看免费观看图片 | 中文文字幕文字幕亚洲色| 69人妻精品中文字幕| 精品婷婷色一区二区三区| 国产精品激情av在线播放| 99在线国内在线视频22| 狠狠cao日日穞夜夜穞av| 久久婷婷五月综合色和啪|