操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    譯自: https://www.linux.com/blog/learn/intro-to-linux/2018/4/linux-filesystem-explained

    作者: Paul Brown

    譯者: amwps290

    這篇教程將幫你快速了解 Linux 文件系統。

    早在 1996 年,在真正理解文件系統的結構之前,我就學會了如何在我嶄新的 Linux 上安裝軟件。這是一個問題,但對程序來說不是大問題,因為即使我不知道實際的可執行文件在哪里,它們也會神奇地工作。問題在于文檔。

    你知道,那時候,Linux 不是像今天這樣直觀、用戶友好的系統。你必須讀很多東西。你必須知道你的 CRT 顯示器的掃描頻率以及撥號調制解調器的噪音來龍去脈,以及其他數以百計的事情。 我很快就意識到我需要花一些時間來掌握目錄的組織方式以及 /etc(不是用于“其它”文件),/usr(不是用于“用戶”文件)和 /bin (不是“垃圾桶”)的意思。

    本教程將幫助你比我當時更快地了解這些。

    結構

    從終端窗口探索 Linux 文件系統是有道理的,這并不是因為作者是一個脾氣暴躁的老人,并且對新孩子和他們漂亮的圖形工具不以為然(盡管某些事實如此),而是因為終端,盡管只是文本界面,才是更好地顯示 Linux 目錄樹結構的工具。

    事實上,幫助你了解這一切的、應該首先安裝的第一個工具的名為:tree。如果你正在使用 Ubuntu 或 Debian ,你可以:

    sudo apt install tree

    在 Red Hat 或 Fedora :

    sudo dnf install tree

    對于 SUSE/openSUSE 可以使用 zypper:

    sudo zypper install tree

    對于使用 Arch (Manjaro,Antergos,等等)使用:

    sudo pacman -S tree

    ……等等。

    一旦安裝好,在終端窗口運行 tree 命令:

    tree /

    上述指令中的 / 指的是根目錄。系統中的其他目錄都是從根目錄分支而出,當你運行 tree 命令,并且告訴它從根目錄開始,那么你就可以看到整個目錄樹,系統中的所有目錄及其子目錄,還有它們的文件。

    如果你已經使用你的系統有一段時間了,這可能需要一段時間,因為即使你自己還沒有生成很多文件,Linux 系統及其應用程序總是在記錄、緩存和存儲各種臨時文件。文件系統中的條目數量會快速增長。

    不過,不要感到不知所措。 相反,試試這個:

    tree -L 1 /

    你應該看到如圖 1 所示。

    tree

    上面的指令可以翻譯為“只顯示以 /(根目錄) 開頭的目錄樹的第一級”。 -L 選項告訴樹你想看到多少層目錄。

    大多數 Linux 發行版都會向你顯示與你在上圖中看到的相同或非常類似的結構。 這意味著,即使你現在感到困惑,掌握這一點,你將掌握大部分(如果不是全部的話)全世界的 Linux 文件系統。

    為了讓你開始走上掌控之路,讓我們看看每個目錄的用途。 當我們查看每一個目錄的時候,你可以使用 ls 來查看他們的內容。

    目錄

    從上到下,你所看到的目錄如下

    /bin

    /bin 目錄是包含一些二進制文件的目錄,即可以運行的一些應用程序。 你會在這個目錄中找到上面提到的 ls 程序,以及用于新建和刪除文件和目錄、移動它們基本工具。還有其它一些程序,等等。文件系統樹的其他部分有更多的 bin 目錄,但我們將在一會兒討論這些目錄。

    /boot

    /boot 目錄包含啟動系統所需的文件。我必須要說嗎? 好吧,我會說:不要動它! 如果你在這里弄亂了其中一個文件,你可能無法運行你的 Linux,修復被破壞的系統是非常痛苦的一件事。 另一方面,不要太擔心無意中破壞系統:你必須擁有超級用戶權限才能執行此操作。

    /dev

    `/dev 目錄包含設備文件。 其中許多是在啟動時或甚至在運行時生成的。 例如,如果你將新的網絡攝像頭或 USB 隨身碟連接到你的機器中,則會自動彈出一個新的設備條目。

    /etc

    /etc 的目錄名稱會讓人變得非常的困惑。/etc 得名于最早的 Unix 系統們,它的字面意思是 “etcetera”(諸如此類) ,因為它是系統文件管理員不確定在哪里放置的文件的垃圾場。

    現在,說 /etc 是“ 要配置的所有內容(Everything To Configure)”更為恰當,因為它包含大部分(如果不是全部的話)的系統配置文件。 例如,包含系統名稱、用戶及其密碼、網絡上計算機名稱以及硬盤上分區的安裝位置和時間的文件都在這里。 再說一遍,如果你是 Linux 的新手,最好是不要在這里接觸太多,直到你對系統的工作有更好的理解。

    /home

    /home 是你可以找到用戶個人目錄的地方。在我的情況下,/home 下有兩個目錄:/home/paul,其中包含我所有的東西;另外一個目錄是 /home/guest 目錄,以防有客人需要使用我的電腦。

    /lib

    /lib 是庫文件所在的地方。庫是包含應用程序可以使用的代碼文件。它們包含應用程序用于在桌面上繪制窗口、控制外圍設備或將文件發送到硬盤的代碼片段。

    在文件系統周圍散布著更多的 lib 目錄,但是這個直接掛載在 / 的 /lib 目錄是特殊的,除此之外,它包含了所有重要的內核模塊。 內核模塊是使你的顯卡、聲卡、WiFi、打印機等工作的驅動程序。

    /media

    在 /media 目錄中,當你插入外部存儲器試圖訪問它時,將自動掛載它。與此列表中的大多數其他項目不同,/media 并不追溯到 1970 年代,主要是因為當計算機正在運行而動態地插入和檢測存儲(U 盤、USB 硬盤、SD 卡、外部 SSD 等),這是近些年才發生的事。

    /mnt

    然而,/mnt 目錄是一些過去的殘余。這是你手動掛載存儲設備或分區的地方。現在不常用了。

    /opt

    /opt 目錄通常是你編譯軟件(即,你從源代碼構建,并不是從你的系統的軟件庫中安裝軟件)的地方。應用程序最終會出現在 /opt/bin 目錄,庫會在 /opt/lib 目錄中出現。

    稍微的題外話:應用程序和庫的另一個地方是 /usr/local,在這里安裝軟件時,也會有 /usr/local/bin 和 /usr/local/lib 目錄。開發人員如何配置文件來控制編譯和安裝過程,這就決定了軟件安裝到哪個地方。

    /proc

    /proc,就像 /dev 是一個虛擬目錄。它包含有關你的計算機的信息,例如關于你的 CPU 和你的 Linux 系統正在運行的內核的信息。與 /dev 一樣,文件和目錄是在計算機啟動或運行時生成的,因為你的系統正在運行且會發生變化。

    /root

    /root 是系統的超級用戶(也稱為“管理員”)的主目錄。 它與其他用戶的主目錄是分開的,因為你不應該動它。 所以把自己的東西放在你自己的目錄中,伙計們。

    /run

    /run 是另一個新出現的目錄。系統進程出于自己不可告人的原因使用它來存儲臨時數據。這是另一個不要動它的文件夾。

    /sbin

    /sbin 與 /bin 類似,但它包含的應用程序只有超級用戶(即首字母的 s )才需要。你可以使用 sudo 命令使用這些應用程序,該命令暫時允許你在許多 Linux 發行版上擁有超級用戶權限。/sbin 目錄通常包含可以安裝、刪除和格式化各種東西的工具。你可以想象,如果你使用不當,這些指令中有一些是致命的,所以要小心處理。

    /usr

    /usr 目錄是在 UNIX 早期用戶的主目錄所處的地方。然而,正如我們上面看到的,現在 /home 是用戶保存他們的東西的地方。如今,/usr 包含了大量目錄,而這些目錄又包含了應用程序、庫、文檔、壁紙、圖標和許多其他需要應用程序和服務共享的內容。

    你還可以在 /usr 目錄下找到 bin,sbin,lib 目錄,它們與掛載到根目錄下的那些有什么區別呢?現在的區別不是很大。在早期,/bin 目錄(掛載在根目錄下的)只會包含一些基本的命令,例如 ls、mv 和 rm ;這是一些在安裝系統的時候就會預裝的一些命令,用于維護系統的一個基本的命令。 而 /usr/bin 目錄則包含了用戶自己安裝和用于工作的軟件,例如文字處理器,瀏覽器和一些其他的軟件。

    但是許多現代的 Linux 發行版只是把所有的東西都放到 /usr/bin 中,并讓 /bin 指向 /usr/bin,以防徹底刪除它會破壞某些東西。因此,Debian、Ubuntu 和 Mint 仍然保持 /bin 和 /usr/bin (和 /sbin 和 /usr/sbin )分離;其他的,比如 Arch 和它衍生版,只是有一個“真實”存儲二進制程序的目錄,/usr/bin,其余的任何 bin 目錄是指向 /usr/bin` 的“假”目錄。

    /srv

    /srv 目錄包含服務器的數據。如果你正在 Linux 機器上運行 Web 服務器,你網站的 HTML文件將放到 /srv/http(或 /srv/www)。 如果你正在運行 FTP 服務器,則你的文件將放到 /srv/ftp。

    /sys

    /sys 是另一個類似 /proc 和 /dev 的虛擬目錄,它還包含連接到計算機的設備的信息。

    在某些情況下,你還可以操縱這些設備。 例如,我可以通過修改存儲在 /sys/devices/pci0000:00/0000:00:02.0/drm/card1/card1-eDP-1/intel_backlight/brightness 中的值來更改筆記本電腦屏幕的亮度(在你的機器上你可能會有不同的文件)。但要做到這一點,你必須成為超級用戶。原因是,與許多其它虛擬目錄一樣,在 /sys 中打亂內容和文件可能是危險的,你可能會破壞系統。直到你確信你知道你在做什么。否則不要動它。

    /tmp

    /tmp 包含臨時文件,通常由正在運行的應用程序放置。文件和目錄通常(并非總是)包含應用程序現在不需要但以后可能需要的數據。

    你還可以使用 /tmp 來存儲你自己的臨時文件 —— /tmp 是少數掛載到根目錄下而你可以在不成為超級用戶的情況下與它進行實際交互的目錄之一。

    /var

    /var 最初被如此命名是因為它的內容被認為是 可變的(variable),因為它經常變化。今天,它有點用詞不當,因為還有許多其他目錄也包含頻繁更改的數據,特別是我們上面看到的虛擬目錄。

    不管怎樣,/var 目錄包含了放在 /var/log 子目錄的日志文件之類。日志是記錄系統中發生的事件的文件。如果內核中出現了什么問題,它將被記錄到 /var/log 下的文件中;如果有人試圖從外部侵入你的計算機,你的防火墻也將記錄嘗試。它還包含用于任務的假脫機程序。這些“任務”可以是你發送給共享打印機必須等待執行的任務,因為另一個用戶正在打印一個長文檔,或者是等待遞交給系統上的用戶的郵件。

    你的系統可能還有一些我們上面沒有提到的目錄。例如,在屏幕截圖中,有一個 /snap 目錄。這是因為這張截圖是在 Ubuntu 系統上截取的。Ubuntu 最近將 snap 包作為一種分發軟件的方式。/snap 目錄包含所有文件和從 snaps 安裝的軟件。

    更深入的研究

    這里僅僅談了根目錄,但是許多子目錄都指向它們自己的一組文件和子目錄。圖 2 給出了基本文件系統的總體概念(圖片是在 Paul Gardner 的 CC BY-SA 許可下提供的), Wikipedia 對每個目錄的用途進行了總結 。

    圖 2:標準 Unix 文件系統

    要自行探索文件系統,請使用 cd 命令:cd將帶你到你所選擇的目錄( cd 代表更改目錄)。

    如果你不知道你在哪兒,pwd會告訴你,你到底在哪里,( pwd 代表打印工作目錄 ),同時 cd命令在沒有任何選項或者參數的時候,將會直接帶你到你自己的主目錄,這是一個安全舒適的地方。

    最后,cd ..將會帶你到上一層目錄,會使你更加接近根目錄,如果你在 /usr/share/wallpapers 目錄,然后你執行 cd .. 命令,你將會跳轉到 /usr/share 目錄

    要查看目錄里有什么內容,使用 ls 或這簡單的使用 l 列出你所在目錄的內容。

    當然,你總是可以使用 tree 來獲得目錄中內容的概述。在 /usr/share 上試試——里面有很多有趣的東西。

    總結

    盡管 Linux 發行版之間存在細微差別,但它們的文件系統的布局非常相似。 你可以這么說:一旦你了解一個,你就會都了解了。 了解文件系統的最好方法就是探索它。 因此,伴隨 tree ,ls 和 cd 進入未知的領域吧。

    你不會只是因為查看文件系統就破壞了文件系統,因此請從一個目錄移動到另一個目錄并進行瀏覽。 很快你就會發現 Linux 文件系統及其布局的確很有意義,并且你會直觀地知道在哪里可以找到應用程序,文檔和其他資源。

    通過 Linux 基金會和 edX 免費的 “ Linux入門 ” 課程了解更多有關 Linux 的信息。


    via: https://www.linux.com/blog/learn/intro-to-linux/2018/4/linux-filesystem-explained

    作者: PAUL BROWN 選題: lujun9972 譯者: amwps290 校對: wxy

    本文由 LCTT 原創編譯, Linux中國 榮譽推出

    點擊“了解更多”可訪問文內鏈接

    何使用 cat、more、head 和 tail 命令查看 Linux 文件的內容,而不僅僅是文本文件。

    • 來源:https://linux.cn/article-12340-1.html
    • 作者:Sandra Henry-stocker
    • 譯者:geekpi

    (本文字數:5839,閱讀時長大約:7 分鐘)


    Linux 提供了許多命令來查看文件的內容,包 括 cat、more、head 和 tail,但這只是一個開始。

    一方面,即使是最顯而易見的命令也有很多許多用戶不會去使用的選項。還有一些普普通通的命令提供了一些獨特的功能。在本文中,我們將介紹查看文件內容的命令,以及如何定制這些視圖以更好地滿足你的需求的選項。

    cat

    cat 命令將文本文件的全部內容發送到終端窗口以供查看。實際上,如果你輸入 cat,然后輸入包含數千行內容的文件名,那么這些行將以極快的速度在你的窗口中滾動,你將無法看到除了最后一屏外的其他文本。對于 Linux 用戶來說 cat 命令很熟悉,但即使是這個基本命令也提供了許多有用的選項,例如對輸出中的行進行編號,這是我們許多人可能從未使用過的。更進一步,你不僅可以對行進行編號,還可以選擇如何編號。

    對每行進行編號就像這樣:

    $ cat -n msg
         1  Hello --
         2
         3  I hope you are having a wonderful day!
         4
         5
         6  That's it for ...       now
         7
         8  bye!
         9
        10  s.
    

    你也可以只對有內容的行編號。請注意,對于此命令,僅包含空格的行不被視為“空”,而是會被編號。

    $ cat -b msg
         1  Hello --
    
         2  I hope you are having a wonderful day!
    
    
         3  That's it for ...       now
    
         4  bye!
    
         5  s.
    

    cat 命令允許你使用 -s 選項忽略重復的空白行,但是要完全忽略空白行你必須添加另一個命令。

    $ cat -s msg
    Hello --
    
    I hope you are having a wonderful day!
    
    That's it for ...       now
    
    bye!
    
    s.
    

    要忽略所有空白行,只需如下將 cat 的輸出通過管道傳遞給 grep 命令。 點(.)匹配包含任意字符的文本,因此它將顯示任意非空的行,用于結束一行的回車換行做匹配(LCTT 譯注:此處原文有誤,徑改)。

    $ cat msg | grep .
    Hello --
    I hope you are having a wonderful day!
    That's it for ...       now
    bye!
    s.
    

    -E 選項通過在每行末尾加 $ 符提供視覺提示,來顯示行尾是否還有多余的空格。

    $ cat -E msg
    Hello --$
    $
    I hope you are having a wonderful day!  $
    $
    $
    That's it for ...       now$
    $
    bye!$
    $
    s.$
    

    使用 -A 時,既可以在每行的末尾顯示 $ 字符,并且制表符會顯示為 ^I 而不是空白。

    $ cat -A msg
    Hello --$
    $
    I hope you are having a wonderful day!$
    $
    $
    That’s it for ...^Inow$
    $
    bye!$
    $
    s.$
    

    使用 head 和 tail 顯示文件部分內容

    head 和 tail 顯示文件的頭部或尾部,默認為十行。 你可以使用 -3(顯示 3 行)或 -11(顯示 11 行)之類的字符串來指定要查看的其它行數。tail 命令與 head 的工作方式相同,但是顯示文件的尾部而不是頭部。

    $ head -3 msg
    Hello --
    I hope you are having a wonderful day!
    $ tail -3 msg
    bye!
    
    s.
    

    你還可以結合使用 head 和 tail 命令來查看文件中間的文本。你只需要選擇起點和想要查看行數即可。在此例中,命令將在文件中顯示第二個一百行,并在 cat 的幫助下為這些行編號。

    $ cat -b mybigfile | head -200 | tail -100
       101  Invoice #2020-06-07a sent to vendor
       ...
    

    使用 more 或者 less 瀏覽一屏文本

    more 命令是一次瀏覽一屏內容的自然之選,而 less 通過使用上下鍵盤箭頭增加了在文件中上下移動的能力,這樣你就可以遍歷內容,然后在文件中回退。

    使用 od 查看文本的兩種方法

    od(八進制轉儲)命令能夠以常規文本和一系列 ASCII 值(即該文本在文件中的實際編碼方式)的形式查看文件。在下面的例子中可以看到,帶編號的行顯示了 ASCII 數字值,而其他行則顯示了文本和不可打印的字符。

    $ od -bc msg
    0000000 110 145 154 154 157 040 055 055 012 012 111 040 150 157 160 145
              H   e   l   l   o       -   -  \n  \n   I       h   o   p   e
    0000020 040 171 157 165 040 141 162 145 040 150 141 166 151 156 147 040
                  y   o   u       a   r   e       h   a   v   i   n   g
    0000040 141 040 167 157 156 144 145 162 146 165 154 040 144 141 171 041
              a       w   o   n   d   e   r   f   u   l       d   a   y   !
    0000060 012 012 012 124 150 141 164 047 163 040 151 164 040 146 157 162
             \n  \n  \n   T   h   a   t   '   s       i   t       f   o   r
    0000100 040 056 056 056 011 156 157 167 012 012 142 171 145 041 012 012
                  .   .   .  \t   n   o   w  \n  \n   b   y   e   !  \n  \n
    0000120 163 056 012
              s   .  \n
    

    請注意,換行符顯示為 \n(八進制 012),而制表符顯示為 \t(八進制 011)。

    od 命令特別有用的用途之一是查看非文本文件以獲取可以標識文件類型的信息。在這里,我們看到 JFIF(JPEG 文件交換格式)標簽,該標簽讓 file 之類報告文件類型的命令將它標示為 jpg 文件。這里還有很多其他有用的信息,特別是如果你對這些文件的格式感到好奇的話。

    在接下來的命令中,我們查看 jpg 文件的開始部分。

    $ od -bc arrow.jpg | head -12
    0000000 377 330 377 340 000 020 112 106 111 106 000 001 001 000 000 001
            377 330 377 340  \0 020   J   F   I   F  \0 001 001  \0  \0 001
    0000020 000 001 000 000 377 333 000 103 000 003 002 002 002 002 002 003
             \0 001  \0  \0 377 333  \0   C  \0 003 002 002 002 002 002 003
    0000040 002 002 002 003 003 003 003 004 006 004 004 004 004 004 010 006
            002 002 002 003 003 003 003 004 006 004 004 004 004 004  \b 006
    0000060 006 005 006 011 010 012 012 011 010 011 011 012 014 017 014 012
            006 005 006  \t  \b  \n  \n  \t  \b  \t  \t  \n  \f 017  \f  \n
    0000100 013 016 013 011 011 015 021 015 016 017 020 020 021 020 012 014
             \v 016  \v  \t  \t  \r 021  \r 016 017 020 020 021 020  \n  \f
    0000120 022 023 022 020 023 017 020 020 020 377 333 000 103 001 003 003
            022 023 022 020 023 017 020 020 020 377 333  \0   C 001 003 003
    

    如果我們要 file 命令提供有關此圖像的信息,我們可能會看到類似下面這樣的信息。file 命令從文件開頭的數據中提取了所有這些描述性信息:

    $ file arrow.jpg
    arrow.png: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 500x375, components 3
    

    使用 jp2a 將文件視為基于文本的圖像

    如果你只能在命令行工作,并且想了解特定圖像包含的內容,那么可以使用 jp2a(jpeg to ascii)之類的工具提供字符渲染。圖像在這種格式下的識別程度取決于文件。不要有太多期待,因為你將看到的圖像版本是“低分辨率”下的測試!這是一只分辨率很低的帝王企鵝。(請離遠點看)

    $ jp2a Emperor_Penguin.jpg
    MMMMMMMMWOdkNMMMMMMMMMMMMMMMMMMM
    MMMXK0kc.... ,OKMMMMMMMMMMMMMMMM
    MMNK0Ol...   :Xx'dNMMMMMMMMMMMMM
    MMMMMMMd;lx00Oo. ..xMMMMMMMMMMMM
    MMMMMMK.OXMMMMMN,...lMMMMMMMMMMM
    MMMMMMx'KXNNMMMMK....0MMMMMMMMMM
    MMMMMMx:kkKNWWMMMl.. 'NMMMMMMMMM
    MMMMMMddx0NNNWMMMK'...;NMMMMMMMM
    MMMMMMck0NNWWWWWMMd  ..lMMMMMMMM
    MMMMMM.d0KXNWWWWMMo  ...WMMMMMMM
    MMMMMM.xOXNNWNMMMW. ....KMMMMMMM
    MMMMMM'kKNKWXWMMMK  ..'.0MMMMMMM
    MMMMMMxckXNNNNMMMX  .:..XMMMMMMM
    MMMMMMW;xKNWWWMMMM. .;. NMMMMMMM
    MMMMMMMok0NNWNWMMMx .l..MMMMMMMM
    MMMMMMMkxOKXWXNMMMMl.:'dMMMMMMMM
    MMMMMMM0dKOdKXXNMMMMNx,WMMMMMMMM
    MMMMMMMWoKxldXKNNMMMMM;MMMMMMMMM
    MMMMMMMMxxxxdNWNXNMMMM;MMMMMMMMM
    MMMMMMMMxOcoo0XOOOOWMW,kMMMMMMMM
    MMMMMMM0xK;.cO0dNX:0XXd;NMMMMMMM
    MMMNkdd:,'ldXXO0xl;x0kx:;lKMMMMM
    MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
    

    總結

    Linux 上有很多命令可以通過各種方式查看文件的內容。其中一些選項在你需要處理文件內容時可能會非常有用。其它的只是……有趣。


    via: https://www.networkworld.com/article/3561490/5-ways-to-examine-the-content-of-files-on-linux.html

    作者: Sandra Henry-Stocker 選題: lujun9972 譯者: geekpi 校對: wxy

    本文由 LCTT 原創編譯, Linux中國 榮譽推出

    點擊“了解更多”可訪問文內鏈接


    文件系統的基本組成

    文件系統是操作系統中負責管理持久數據的子系統,說簡單點,就是負責把用戶的文件存到磁盤硬件中,因為即使計算機斷電了,磁盤里的數據并不會丟失,所以可以持久化的保存文件。

    文件系統的基本數據單位是文件,它的目的是對磁盤上的文件進行組織管理,那組織的方式不同,就會形成不同的文件系統。

    Linux 最經典的一句話是:「一切皆文件」,不僅普通的文件和目錄,就連塊設備、管道、socket 等,也都是統一交給文件系統管理的。

    Linux 文件系統會為每個文件分配兩個數據結構:索引節點(index node)和目錄項(directory entry,它們主要用來記錄文件的元信息和目錄層次結構。

    • 索引節點,也就是 inode,用來記錄文件的元信息,比如 inode 編號、文件大小、訪問權限、創建時間、修改時間、數據在磁盤的位置等等。索引節點是文件的唯一標識,它們之間一一對應,也同樣都會被存儲在硬盤中,所以索引節點同樣占用磁盤空間
    • 目錄項,也就是 dentry,用來記錄文件的名字、索引節點指針以及與其他目錄項的層級關聯關系。多個目錄項關聯起來,就會形成目錄結構,但它與索引節點不同的是,目錄項是由內核維護的一個數據結構,不存放于磁盤,而是緩存在內存

    由于索引節點唯一標識一個文件,而目錄項記錄著文件的名,所以目錄項和索引節點的關系是多對一,也就是說,一個文件可以有多個別字。比如,硬鏈接的實現就是多個目錄項中的索引節點指向同一個文件。

    注意,目錄也是文件,也是用索引節點唯一標識,和普通文件不同的是,普通文件在磁盤里面保存的是文件數據,而目錄文件在磁盤里面保存子目錄或文件。

    目錄項和目錄是一個東西嗎?

    雖然名字很相近,但是它們不是一個東西,目錄是個文件,持久化存儲在磁盤,而目錄項是內核一個數據結構,緩存在內存。

    如果查詢目錄頻繁從磁盤讀,效率會很低,所以內核會把已經讀過的目錄用目錄項這個數據結構緩存在內存,下次再次讀到相同的目錄時,只需從內存讀就可以,大大提高了文件系統的效率。

    注意,目錄項這個數據結構不只是表示目錄,也是可以表示文件的。

    那文件數據是如何存儲在磁盤的呢?

    磁盤讀寫的最小單位是扇區,扇區的大小只有 512B 大小,很明顯,如果每次讀寫都以這么小為單位,那這讀寫的效率會非常低。

    所以,文件系統把多個扇區組成了一個邏輯塊,每次讀寫的最小單位就是邏輯塊(數據塊),Linux 中的邏輯塊大小為 4KB,也就是一次性讀寫 8 個扇區,這將大大提高了磁盤的讀寫的效率。

    以上就是索引節點、目錄項以及文件數據的關系,下面這個圖就很好的展示了它們之間的關系:

    索引節點是存儲在硬盤上的數據,那么為了加速文件的訪問,通常會把索引節點加載到內存中。

    另外,磁盤進行格式化的時候,會被分成三個存儲區域,分別是超級塊、索引節點區和數據塊區。

    • 超級塊,用來存儲文件系統的詳細信息,比如塊個數、塊大小、空閑塊等等。
    • 索引節點區,用來存儲索引節點;
    • 數據塊區,用來存儲文件或目錄數據;

    我們不可能把超級塊和索引節點區全部加載到內存,這樣內存肯定撐不住,所以只有當需要使用的時候,才將其加載進內存,它們加載進內存的時機是不同的:

    • 超級塊:當文件系統掛載時進入內存;
    • 索引節點區:當文件被訪問時進入內存;

    虛擬文件系統

    文件系統的種類眾多,而操作系統希望對用戶提供一個統一的接口,于是在用戶層與文件系統層引入了中間層,這個中間層就稱為虛擬文件系統(Virtual File System,VFS)。

    VFS 定義了一組所有文件系統都支持的數據結構和標準接口,這樣程序員不需要了解文件系統的工作原理,只需要了解 VFS 提供的統一接口即可。

    在 Linux 文件系統中,用戶空間、系統調用、虛擬機文件系統、緩存、文件系統以及存儲之間的關系如下圖:

    Linux 支持的文件系統也不少,根據存儲位置的不同,可以把文件系統分為三類:

    • 磁盤的文件系統,它是直接把數據存儲在磁盤中,比如 Ext 2/3/4、XFS 等都是這類文件系統。
    • 內存的文件系統,這類文件系統的數據不是存儲在硬盤的,而是占用內存空間,我們經常用到的 /proc/sys 文件系統都屬于這一類,讀寫這類文件,實際上是讀寫內核中相關的數據數據。
    • 網絡的文件系統,用來訪問其他計算機主機數據的文件系統,比如 NFS、SMB 等等。

    文件系統首先要先掛載到某個目錄才可以正常使用,比如 Linux 系統在啟動時,會把文件系統掛載到根目錄。


    文件的使用

    我們從用戶角度來看文件的話,就是我們要怎么使用文件?首先,我們得通過系統調用來打開一個文件。

    write 的過程

    fd=open(name, flag); # 打開文件
    ...
    write(fd,...);         # 寫數據
    ...
    close(fd);             # 關閉文件
    

    上面簡單的代碼是讀取一個文件的過程:

    • 首先用 open 系統調用打開文件,open 的參數中包含文件的路徑名和文件名。
    • 使用 write 寫數據,其中 write 使用 open 所返回的文件描述符,并不使用文件名作為參數。
    • 使用完文件后,要用 close 系統調用關閉文件,避免資源的泄露。

    我們打開了一個文件后,操作系統會跟蹤進程打開的所有文件,所謂的跟蹤呢,就是操作系統為每個進程維護一個打開文件表,文件表里的每一項代表「文件描述符」,所以說文件描述符是打開文件的標識。

    打開文件表

    操作系統在打開文件表中維護著打開文件的狀態和信息:

    • 文件指針:系統跟蹤上次讀寫位置作為當前文件位置指針,這種指針對打開文件的某個進程來說是唯一的;
    • 文件打開計數器:文件關閉時,操作系統必須重用其打開文件表條目,否則表內空間不夠用。因為多個進程可能打開同一個文件,所以系統在刪除打開文件條目之前,必須等待最后一個進程關閉文件,該計數器跟蹤打開和關閉的數量,當該計數為 0 時,系統關閉文件,刪除該條目;
    • 文件磁盤位置:絕大多數文件操作都要求系統修改文件數據,該信息保存在內存中,以免每個操作都從磁盤中讀取;
    • 訪問權限:每個進程打開文件都需要有一個訪問模式(創建、只讀、讀寫、添加等),該信息保存在進程的打開文件表中,以便操作系統能允許或拒絕之后的 I/O 請求;

    在用戶視角里,文件就是一個持久化的數據結構,但操作系統并不會關心你想存在磁盤上的任何的數據結構,操作系統的視角是如何把文件數據和磁盤塊對應起來。

    所以,用戶和操作系統對文件的讀寫操作是有差異的,用戶習慣以字節的方式讀寫文件,而操作系統則是以數據塊來讀寫文件,那屏蔽掉這種差異的工作就是文件系統了。

    我們來分別看一下,讀文件和寫文件的過程:

    • 當用戶進程從文件讀取 1 個字節大小的數據時,文件系統則需要獲取字節所在的數據塊,再返回數據塊對應的用戶進程所需的數據部分。
    • 當用戶進程把 1 個字節大小的數據寫進文件時,文件系統則找到需要寫入數據的數據塊的位置,然后修改數據塊中對應的部分,最后再把數據塊寫回磁盤。

    所以說,文件系統的基本操作單位是數據塊


    文件的存儲

    文件的數據是要存儲在硬盤上面的,數據在磁盤上的存放方式,就像程序在內存中存放的方式那樣,有以下兩種:

    • 連續空間存放方式
    • 非連續空間存放方式

    其中,非連續空間存放方式又可以分為「鏈表方式」和「索引方式」。

    不同的存儲方式,有各自的特點,重點是要分析它們的存儲效率和讀寫性能,接下來分別對每種存儲方式說一下。

    連續空間存放方式

    連續空間存放方式顧名思義,文件存放在磁盤「連續的」物理空間中。這種模式下,文件的數據都是緊密相連,讀寫效率很高,因為一次磁盤尋道就可以讀出整個文件。

    使用連續存放的方式有一個前提,必須先知道一個文件的大小,這樣文件系統才會根據文件的大小在磁盤上找到一塊連續的空間分配給文件。

    所以,文件頭里需要指定「起始塊的位置」和「長度」,有了這兩個信息就可以很好的表示文件存放方式是一塊連續的磁盤空間。

    注意,此處說的文件頭,就類似于 Linux 的 inode。

    連續空間存放方式

    連續空間存放的方式雖然讀寫效率高,但是有「磁盤空間碎片」和「文件長度不易擴展」的缺陷。

    如下圖,如果文件 B 被刪除,磁盤上就留下一塊空缺,這時,如果新來的文件小于其中的一個空缺,我們就可以將其放在相應空缺里。但如果該文件的大小大于所有的空缺,但卻小于空缺大小之和,則雖然磁盤上有足夠的空缺,但該文件還是不能存放。當然了,我們可以通過將現有文件進行挪動來騰出空間以容納新的文件,但是這個在磁盤挪動文件是非常耗時,所以這種方式不太現實。

    磁盤碎片

    另外一個缺陷是文件長度擴展不方便,例如上圖中的文件 A 要想擴大一下,需要更多的磁盤空間,唯一的辦法就只能是挪動的方式,前面也說了,這種方式效率是非常低的。

    那么有沒有更好的方式來解決上面的問題呢?答案當然有,既然連續空間存放的方式不太行,那么我們就改變存放的方式,使用非連續空間存放方式來解決這些缺陷。

    非連續空間存放方式

    非連續空間存放方式分為「鏈表方式」和「索引方式」。

    我們先來看看鏈表的方式。

    鏈表的方式存放是離散的,不用連續的,于是就可以消除磁盤碎片,可大大提高磁盤空間的利用率,同時文件的長度可以動態擴展。根據實現的方式的不同,鏈表可分為「隱式鏈表」和「顯式鏈接」兩種形式。

    文件要以「隱式鏈表」的方式存放的話,實現的方式是文件頭要包含「第一塊」和「最后一塊」的位置,并且每個數據塊里面留出一個指針空間,用來存放下一個數據塊的位置,這樣一個數據塊連著一個數據塊,從鏈頭開是就可以順著指針找到所有的數據塊,所以存放的方式可以是不連續的。

    隱式鏈表

    隱式鏈表的存放方式的缺點在于無法直接訪問數據塊,只能通過指針順序訪問文件,以及數據塊指針消耗了一定的存儲空間。隱式鏈接分配的穩定性較差,系統在運行過程中由于軟件或者硬件錯誤導致鏈表中的指針丟失或損壞,會導致文件數據的丟失。

    如果取出每個磁盤塊的指針,把它放在內存的一個表中,就可以解決上述隱式鏈表的兩個不足。那么,這種實現方式是「顯式鏈接」,它指把用于鏈接文件各數據塊的指針,顯式地存放在內存的一張鏈接表中,該表在整個磁盤僅設置一張,每個表項中存放鏈接指針,指向下一個數據塊號

    對于顯式鏈接的工作方式,我們舉個例子,文件 A 依次使用了磁盤塊 4、7、2、10 和 12 ,文件 B 依次使用了磁盤塊 6、3、11 和 14 。利用下圖中的表,可以從第 4 塊開始,順著鏈走到最后,找到文件 A 的全部磁盤塊。同樣,從第 6 塊開始,順著鏈走到最后,也能夠找出文件 B 的全部磁盤塊。最后,這兩個鏈都以一個不屬于有效磁盤編號的特殊標記(如 -1 )結束。內存中的這樣一個表格稱為文件分配表(File Allocation Table,FAT

    顯式鏈接

    由于查找記錄的過程是在內存中進行的,因而不僅顯著地提高了檢索速度,而且大大減少了訪問磁盤的次數。但也正是整個表都存放在內存中的關系,它的主要的缺點是不適用于大磁盤

    比如,對于 200GB 的磁盤和 1KB 大小的塊,這張表需要有 2 億項,每一項對應于這 2 億個磁盤塊中的一個塊,每項如果需要 4 個字節,那這張表要占用 800MB 內存,很顯然 FAT 方案對于大磁盤而言不太合適。

    接下來,我們來看看索引的方式。

    鏈表的方式解決了連續分配的磁盤碎片和文件動態擴展的問題,但是不能有效支持直接訪問(FAT除外),索引的方式可以解決這個問題。

    索引的實現是為每個文件創建一個「索引數據塊」,里面存放的是指向文件數據塊的指針列表,說白了就像書的目錄一樣,要找哪個章節的內容,看目錄查就可以。

    另外,文件頭需要包含指向「索引數據塊」的指針,這樣就可以通過文件頭知道索引數據塊的位置,再通過索引數據塊里的索引信息找到對應的數據塊。

    創建文件時,索引塊的所有指針都設為空。當首次寫入第 i 塊時,先從空閑空間中取得一個塊,再將其地址寫到索引塊的第 i 個條目。

    索引的方式

    索引的方式優點在于:

    • 文件的創建、增大、縮小很方便;
    • 不會有碎片的問題;
    • 支持順序讀寫和隨機讀寫;

    由于索引數據也是存放在磁盤塊的,如果文件很小,明明只需一塊就可以存放的下,但還是需要額外分配一塊來存放索引數據,所以缺陷之一就是存儲索引帶來的開銷。

    如果文件很大,大到一個索引數據塊放不下索引信息,這時又要如何處理大文件的存放呢?我們可以通過組合的方式,來處理大文件的存。

    先來看看鏈表 + 索引的組合,這種組合稱為「鏈式索引塊」,它的實現方式是在索引數據塊留出一個存放下一個索引數據塊的指針,于是當一個索引數據塊的索引信息用完了,就可以通過指針的方式,找到下一個索引數據塊的信息。那這種方式也會出現前面提到的鏈表方式的問題,萬一某個指針損壞了,后面的數據也就會無法讀取了。

    鏈式索引塊

    還有另外一種組合方式是索引 + 索引的方式,這種組合稱為「多級索引塊」,實現方式是通過一個索引塊來存放多個索引數據塊,一層套一層索引,像極了俄羅斯套娃是吧。

    多級索引塊

    Unix 文件的實現方式

    我們先把前面提到的文件實現方式,做個比較:

    那早期 Unix 文件系統是組合了前面的文件存放方式的優點,如下圖:

    早期 Unix 文件系統

    它是根據文件的大小,存放的方式會有所變化:

    • 如果存放文件所需的數據塊小于 10 塊,則采用直接查找的方式;
    • 如果存放文件所需的數據塊超過 10 塊,則采用一級間接索引方式;
    • 如果前面兩種方式都不夠存放大文件,則采用二級間接索引方式;
    • 如果二級間接索引也不夠存放大文件,這采用三級間接索引方式;

    那么,文件頭(Inode)就需要包含 13 個指針:

    • 10 個指向數據塊的指針;
    • 第 11 個指向索引塊的指針;
    • 第 12 個指向二級索引塊的指針;
    • 第 13 個指向三級索引塊的指針;

    所以,這種方式能很靈活地支持小文件和大文件的存放:

    • 對于小文件使用直接查找的方式可減少索引數據塊的開銷;
    • 對于大文件則以多級索引的方式來支持,所以大文件在訪問數據塊時需要大量查詢;

    這個方案就用在了 Linux Ext 2/3 文件系統里,雖然解決大文件的存儲,但是對于大文件的訪問,需要大量的查詢,效率比較低。

    為了解決這個問題,Ext 4 做了一定的改變,具體怎么解決的,本文就不展開了。


    空閑空間管理

    前面說到的文件的存儲是針對已經被占用的數據塊組織和管理,接下來的問題是,如果我要保存一個數據塊,我應該放在硬盤上的哪個位置呢?難道需要將所有的塊掃描一遍,找個空的地方隨便放嗎?

    那這種方式效率就太低了,所以針對磁盤的空閑空間也是要引入管理的機制,接下來介紹幾種常見的方法:

    • 空閑表法
    • 空閑鏈表法
    • 位圖法

    空閑表法

    空閑表法就是為所有空閑空間建立一張表,表內容包括空閑區的第一個塊號和該空閑區的塊個數,注意,這個方式是連續分配的。如下圖:

    空閑表法

    當請求分配磁盤空間時,系統依次掃描空閑表里的內容,直到找到一個合適的空閑區域為止。當用戶撤銷一個文件時,系統回收文件空間。這時,也需順序掃描空閑表,尋找一個空閑表條目并將釋放空間的第一個物理塊號及它占用的塊數填到這個條目中。

    這種方法僅當有少量的空閑區時才有較好的效果。因為,如果存儲空間中有著大量的小的空閑區,則空閑表變得很大,這樣查詢效率會很低。另外,這種分配技術適用于建立連續文件。

    空閑鏈表法

    我們也可以使用「鏈表」的方式來管理空閑空間,每一個空閑塊里有一個指針指向下一個空閑塊,這樣也能很方便的找到空閑塊并管理起來。如下圖:

    空閑鏈表法

    當創建文件需要一塊或幾塊時,就從鏈頭上依次取下一塊或幾塊。反之,當回收空間時,把這些空閑塊依次接到鏈頭上。

    這種技術只要在主存中保存一個指針,令它指向第一個空閑塊。其特點是簡單,但不能隨機訪問,工作效率低,因為每當在鏈上增加或移動空閑塊時需要做很多 I/O 操作,同時數據塊的指針消耗了一定的存儲空間。

    空閑表法和空閑鏈表法都不適合用于大型文件系統,因為這會使空閑表或空閑鏈表太大。

    位圖法

    位圖是利用二進制的一位來表示磁盤中一個盤塊的使用情況,磁盤上所有的盤塊都有一個二進制位與之對應。

    當值為 0 時,表示對應的盤塊空閑,值為 1 時,表示對應的盤塊已分配。它形式如下:

    1111110011111110001110110111111100111 ...
    

    在 Linux 文件系統就采用了位圖的方式來管理空閑空間,不僅用于數據空閑塊的管理,還用于 inode 空閑塊的管理,因為 inode 也是存儲在磁盤的,自然也要有對其管理。


    文件系統的結構

    前面提到 Linux 是用位圖的方式管理空閑空間,用戶在創建一個新文件時,Linux 內核會通過 inode 的位圖找到空閑可用的 inode,并進行分配。要存儲數據時,會通過塊的位圖找到空閑的塊,并分配,但仔細計算一下還是有問題的。

    數據塊的位圖是放在磁盤塊里的,假設是放在一個塊里,一個塊 4K,每位表示一個數據塊,共可以表示 4 * 1024 * 8=2^15 個空閑塊,由于 1 個數據塊是 4K 大小,那么最大可以表示的空間為 2^15 * 4 * 1024=2^27 個 byte,也就是 128M。

    也就是說按照上面的結構,如果采用「一個塊的位圖 + 一系列的塊」,外加「一個塊的 inode 的位圖 + 一系列的 inode 的結構」能表示的最大空間也就 128M,這太少了,現在很多文件都比這個大。

    在 Linux 文件系統,把這個結構稱為一個塊組,那么有 N 多的塊組,就能夠表示 N 大的文件。

    下圖給出了 Linux Ext2 整個文件系統的結構和塊組的內容,文件系統都由大量塊組組成,在硬盤上相繼排布:

    最前面的第一個塊是引導塊,在系統啟動時用于啟用引導,接著后面就是一個一個連續的塊組了,塊組的內容如下:

    • 超級塊,包含的是文件系統的重要信息,比如 inode 總個數、塊總個數、每個塊組的 inode 個數、每個塊組的塊個數等等。
    • 塊組描述符,包含文件系統中各個塊組的狀態,比如塊組中空閑塊和 inode 的數目等,每個塊組都包含了文件系統中「所有塊組的組描述符信息」。
    • 數據位圖和 inode 位圖, 用于表示對應的數據塊或 inode 是空閑的,還是被使用中。
    • inode 列表,包含了塊組中所有的 inode,inode 用于保存文件系統中與各個文件和目錄相關的所有元數據。
    • 數據塊,包含文件的有用數據。

    你可以會發現每個塊組里有很多重復的信息,比如超級塊和塊組描述符表,這兩個都是全局信息,而且非常的重要,這么做是有兩個原因:

    • 如果系統崩潰破壞了超級塊或塊組描述符,有關文件系統結構和內容的所有信息都會丟失。如果有冗余的副本,該信息是可能恢復的。
    • 通過使文件和管理數據盡可能接近,減少了磁頭尋道和旋轉,這可以提高文件系統的性能。

    不過,Ext2 的后續版本采用了稀疏技術。該做法是,超級塊和塊組描述符表不再存儲到文件系統的每個塊組中,而是只寫入到塊組 0、塊組 1 和其他 ID 可以表示為 3、 5、7 的冪的塊組中。


    目錄的存儲

    在前面,我們知道了一個普通文件是如何存儲的,但還有一個特殊的文件,經常用到的目錄,它是如何保存的呢?

    基于 Linux 一切皆文件的設計思想,目錄其實也是個文件,你甚至可以通過 vim 打開它,它也有 inode,inode 里面也是指向一些塊。

    和普通文件不同的是,普通文件的塊里面保存的是文件數據,而目錄文件的塊里面保存的是目錄里面一項一項的文件信息。

    在目錄文件的塊中,最簡單的保存格式就是列表,就是一項一項地將目錄下的文件信息(如文件名、文件 inode、文件類型等)列在表里。

    列表中每一項就代表該目錄下的文件的文件名和對應的 inode,通過這個 inode,就可以找到真正的文件。

    目錄格式哈希表

    通常,第一項是「.」,表示當前目錄,第二項是「..」,表示上一級目錄,接下來就是一項一項的文件名和 inode。

    如果一個目錄有超級多的文件,我們要想在這個目錄下找文件,按照列表一項一項的找,效率就不高了。

    于是,保存目錄的格式改成哈希表,對文件名進行哈希計算,把哈希值保存起來,如果我們要查找一個目錄下面的文件名,可以通過名稱取哈希。如果哈希能夠匹配上,就說明這個文件的信息在相應的塊里面。

    Linux 系統的 ext 文件系統就是采用了哈希表,來保存目錄的內容,這種方法的優點是查找非常迅速,插入和刪除也較簡單,不過需要一些預備措施來避免哈希沖突。

    目錄查詢是通過在磁盤上反復搜索完成,需要不斷地進行 I/O 操作,開銷較大。所以,為了減少 I/O 操作,把當前使用的文件目錄緩存在內存,以后要使用該文件時只要在內存中操作,從而降低了磁盤操作次數,提高了文件系統的訪問速度。


    軟鏈接和硬鏈接

    有時候我們希望給某個文件取個別名,那么在 Linux 中可以通過硬鏈接(Hard Link軟鏈接(Symbolic Link 的方式來實現,它們都是比較特殊的文件,但是實現方式也是不相同的。

    硬鏈接是多個目錄項中的「索引節點」指向一個文件,也就是指向同一個 inode,但是 inode 是不可能跨越文件系統的,每個文件系統都有各自的 inode 數據結構和列表,所以硬鏈接是不可用于跨文件系統的。由于多個目錄項都是指向一個 inode,那么只有刪除文件的所有硬鏈接以及源文件時,系統才會徹底刪除該文件。

    硬鏈接

    軟鏈接相當于重新創建一個文件,這個文件有獨立的 inode,但是這個文件的內容是另外一個文件的路徑,所以訪問軟鏈接的時候,實際上相當于訪問到了另外一個文件,所以軟鏈接是可以跨文件系統的,甚至目標文件被刪除了,鏈接文件還是在的,只不過指向的文件找不到了而已。

    軟鏈接


    文件 I/O

    文件的讀寫方式各有千秋,對于文件的 I/O 分類也非常多,常見的有

    • 緩沖與非緩沖 I/O
    • 直接與非直接 I/O
    • 阻塞與非阻塞 I/O VS 同步與異步 I/O

    接下來,分別對這些分類討論討論。

    緩沖與非緩沖 I/O

    文件操作的標準庫是可以實現數據的緩存,那么根據「是否利用標準庫緩沖」,可以把文件 I/O 分為緩沖 I/O 和非緩沖 I/O

    • 緩沖 I/O,利用的是標準庫的緩存實現文件的加速訪問,而標準庫再通過系統調用訪問文件。
    • 非緩沖 I/O,直接通過系統調用訪問文件,不經過標準庫緩存。

    這里所說的「緩沖」特指標準庫內部實現的緩沖。

    比方說,很多程序遇到換行時才真正輸出,而換行前的內容,其實就是被標準庫暫時緩存了起來,這樣做的目的是,減少系統調用的次數,畢竟系統調用是有 CPU 上下文切換的開銷的。

    直接與非直接 I/O

    我們都知道磁盤 I/O 是非常慢的,所以 Linux 內核為了減少磁盤 I/O 次數,在系統調用后,會把用戶數據拷貝到內核中緩存起來,這個內核緩存空間也就是「頁緩存」,只有當緩存滿足某些條件的時候,才發起磁盤 I/O 的請求。

    那么,根據是「否利用操作系統的緩存」,可以把文件 I/O 分為直接 I/O 與非直接 I/O

    • 直接 I/O,不會發生內核緩存和用戶程序之間數據復制,而是直接經過文件系統訪問磁盤。
    • 非直接 I/O,讀操作時,數據從內核緩存中拷貝給用戶程序,寫操作時,數據從用戶程序拷貝給內核緩存,再由內核決定什么時候寫入數據到磁盤。

    如果你在使用文件操作類的系統調用函數時,指定了 O_DIRECT 標志,則表示使用直接 I/O。如果沒有設置過,默認使用的是非直接 I/O。

    如果用了非直接 I/O 進行寫數據操作,內核什么情況下才會把緩存數據寫入到磁盤?

    以下幾種場景會觸發內核緩存的數據寫入磁盤:

    • 在調用 write 的最后,當發現內核緩存的數據太多的時候,內核會把數據寫到磁盤上;
    • 用戶主動調用 sync,內核緩存會刷到磁盤上;
    • 當內存十分緊張,無法再分配頁面時,也會把內核緩存的數據刷到磁盤上;
    • 內核緩存的數據的緩存時間超過某個時間時,也會把數據刷到磁盤上;

    阻塞與非阻塞 I/O VS 同步與異步 I/O

    為什么把阻塞 / 非阻塞與同步與異步放一起說的呢?因為它們確實非常相似,也非常容易混淆,不過它們之間的關系還是有點微妙的。

    先來看看阻塞 I/O,當用戶程序執行 read ,線程會被阻塞,一直等到內核數據準備好,并把數據從內核緩沖區拷貝到應用程序的緩沖區中,當拷貝過程完成,read 才會返回。

    注意,阻塞等待的是「內核數據準備好」和「數據從內核態拷貝到用戶態」這兩個過程。過程如下圖:

    阻塞 I/O

    知道了阻塞 I/O ,來看看非阻塞 I/O,非阻塞的 read 請求在數據未準備好的情況下立即返回,可以繼續往下執行,此時應用程序不斷輪詢內核,直到數據準備好,內核將數據拷貝到應用程序緩沖區,read 調用才可以獲取到結果。過程如下圖:

    非阻塞 I/O

    注意,這里最后一次 read 調用,獲取數據的過程,是一個同步的過程,是需要等待的過程。這里的同步指的是內核態的數據拷貝到用戶程序的緩存區這個過程。

    舉個例子,訪問管道或 socket 時,如果設置了 O_NONBLOCK 標志,那么就表示使用的是非阻塞 I/O 的方式訪問,而不做任何設置的話,默認是阻塞 I/O。

    應用程序每次輪詢內核的 I/O 是否準備好,感覺有點傻乎乎,因為輪詢的過程中,應用程序啥也做不了,只是在循環。

    為了解決這種傻乎乎輪詢方式,于是 I/O 多路復用技術就出來了,如 select、poll,它是通過 I/O 事件分發,當內核數據準備好時,再以事件通知應用程序進行操作。

    這個做法大大改善了應用進程對 CPU 的利用率,在沒有被通知的情況下,應用進程可以使用 CPU 做其他的事情。

    下圖是使用 select I/O 多路復用過程。注意,read 獲取數據的過程(數據從內核態拷貝到用戶態的過程),也是一個同步的過程,需要等待:

    I/O 多路復用

    實際上,無論是阻塞 I/O、非阻塞 I/O,還是基于非阻塞 I/O 的多路復用都是同步調用。因為它們在 read 調用時,內核將數據從內核空間拷貝到應用程序空間,過程都是需要等待的,也就是說這個過程是同步的,如果內核實現的拷貝效率不高,read 調用就會在這個同步過程中等待比較長的時間。

    而真正的異步 I/O 是「內核數據準備好」和「數據從內核態拷貝到用戶態」這兩個過程都不用等待。

    當我們發起 aio_read 之后,就立即返回,內核自動將數據從內核空間拷貝到應用程序空間,這個拷貝過程同樣是異步的,內核自動完成的,和前面的同步操作不一樣,應用程序并不需要主動發起拷貝動作。過程如下圖:

    異步 I/O

    下面這張圖,總結了以上幾種 I/O 模型:

    在前面我們知道了,I/O 是分為兩個過程的:

    1. 數據準備的過程
    2. 數據從內核空間拷貝到用戶進程緩沖區的過程

    阻塞 I/O 會阻塞在「過程 1 」和「過程 2」,而非阻塞 I/O 和基于非阻塞 I/O 的多路復用只會阻塞在「過程 2」,所以這三個都可以認為是同步 I/O。

    異步 I/O 則不同,「過程 1 」和「過程 2 」都不會阻塞。

    用故事去理解這幾種 I/O 模型

    舉個你去飯堂吃飯的例子,你好比用戶程序,飯堂好比操作系統。

    阻塞 I/O 好比,你去飯堂吃飯,但是飯堂的菜還沒做好,然后你就一直在那里等啊等,等了好長一段時間終于等到飯堂阿姨把菜端了出來(數據準備的過程),但是你還得繼續等阿姨把菜(內核空間)打到你的飯盒里(用戶空間),經歷完這兩個過程,你才可以離開。

    非阻塞 I/O 好比,你去了飯堂,問阿姨菜做好了沒有,阿姨告訴你沒,你就離開了,過幾十分鐘,你又來飯堂問阿姨,阿姨說做好了,于是阿姨幫你把菜打到你的飯盒里,這個過程你是得等待的。

    基于非阻塞的 I/O 多路復用好比,你去飯堂吃飯,發現有一排窗口,飯堂阿姨告訴你這些窗口都還沒做好菜,等做好了再通知你,于是等啊等(select 調用中),過了一會阿姨通知你菜做好了,但是不知道哪個窗口的菜做好了,你自己看吧。于是你只能一個一個窗口去確認,后面發現 5 號窗口菜做好了,于是你讓 5 號窗口的阿姨幫你打菜到飯盒里,這個打菜的過程你是要等待的,雖然時間不長。打完菜后,你自然就可以離開了。

    異步 I/O 好比,你讓飯堂阿姨將菜做好并把菜打到飯盒里后,把飯盒送到你面前,整個過程你都不需要任何等待。

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有