<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux 的文件系統(tǒng)及文件緩存知識點整理

          共 22133字,需瀏覽 45分鐘

           ·

          2020-10-30 22:45

          公眾號關(guān)注杰哥的IT之旅”,
          選擇“星標(biāo)”,重磅干貨,第一時間送達(dá)!


          Linux的文件系統(tǒng)特點

          • 文件系統(tǒng)要有嚴(yán)格的組織形式,使得文件能夠以塊為單位進(jìn)行存儲。

          • 文件系統(tǒng)中也要有索引區(qū),用來方便查找一個文件分成的多個塊都存放在了什么位置。

          • 如果文件系統(tǒng)中有的文件是熱點文件,近期經(jīng)常被讀取和寫入,文件系統(tǒng)應(yīng)該有緩存層。

          • 文件應(yīng)該用文件夾的形式組織起來,方便管理和查詢。

          • Linux內(nèi)核要在自己的內(nèi)存里面維護一套數(shù)據(jù)結(jié)構(gòu),來保存哪些文件被哪些進(jìn)程打開和使用。

          總體來說,文件系統(tǒng)的主要功能梳理如下:

          ext系列的文件系統(tǒng)的格式

          inode與塊的存儲

          硬盤分成相同大小的單元,我們稱為塊(Block)。一塊的大小是扇區(qū)大小的整數(shù)倍,默認(rèn)是4K。在格式化的時候,這個值是可以設(shè)定的。

          一大塊硬盤被分成了一個個小的塊,用來存放文件的數(shù)據(jù)部分。這樣一來,如果我們像存放一個文件,就不用給他分配一塊連續(xù)的空間了。我們可以分散成一個個小塊進(jìn)行存放。這樣就靈活得多,也比較容易添加、刪除和插入數(shù)據(jù)。

          inode就是文件索引的意思,我們每個文件都會對應(yīng)一個inode;一個文件夾就是一個文件,也對應(yīng)一個inode。

          inode數(shù)據(jù)結(jié)構(gòu)如下:

          struct ext4_inode {
              __le16  i_mode;     /* File mode */
              __le16  i_uid;      /* Low 16 bits of Owner Uid */
              __le32  i_size_lo;  /* Size in bytes */
              __le32  i_atime;    /* Access time */
              __le32  i_ctime;    /* Inode Change time */
              __le32  i_mtime;    /* Modification time */
              __le32  i_dtime;    /* Deletion Time */
              __le16  i_gid;      /* Low 16 bits of Group Id */
              __le16  i_links_count;  /* Links count */
              __le32  i_blocks_lo;    /* Blocks count */
              __le32  i_flags;    /* File flags */
          ......
              __le32  i_block[EXT4_N_BLOCKS];/* Pointers to blocks */
              __le32  i_generation;   /* File version (for NFS) */
              __le32  i_file_acl_lo;  /* File ACL */
              __le32  i_size_high;
          ......
          };

          inode里面有文件的讀寫權(quán)限i_mode,屬于哪個用戶i_uid,哪個組i_gid,大小是多少i_size_io,占用多少個塊i_blocks_io,i_atime是access time,是最近一次訪問文件的時間;i_ctime是change time,是最近一次更改inode的時間;i_mtime是modify time,是最近一次更改文件的時間等。

          所有的文件都是保存在i_block里面。具體保存規(guī)則由EXT4_N_BLOCKS決定,EXT4_N_BLOCKS有如下的定義:

          #define    EXT4_NDIR_BLOCKS        12
          #define    EXT4_IND_BLOCK          EXT4_NDIR_BLOCKS
          #define    EXT4_DIND_BLOCK         (EXT4_IND_BLOCK + 1)
          #define    EXT4_TIND_BLOCK         (EXT4_DIND_BLOCK + 1)
          #define    EXT4_N_BLOCKS           (EXT4_TIND_BLOCK + 1)

          在ext2和ext3中,其中前12項直接保存了塊的位置,也就是說,我們可以通過i_block[0-11],直接得到保存文件內(nèi)容的塊。

          但是,如果一個文件比較大,12塊放不下。當(dāng)我們用到i_block[12]的時候,就不能直接放數(shù)據(jù)塊的位置了,要不然i_block很快就會用完了。

          那么可以讓i_block[12]指向一個塊,這個塊里面不放數(shù)據(jù)塊,而是放數(shù)據(jù)塊的位置,這個塊我們稱為間接塊。如果文件再大一些,i_block[13]會指向一個塊,我們可以用二次間接塊。二次間接塊里面存放了間接塊的位置,間接塊里面存放了數(shù)據(jù)塊的位置,數(shù)據(jù)塊里面存放的是真正的數(shù)據(jù)。如果文件再大點,那么i_block[14]同理。

          這里面有一個非常顯著的問題,對于大文件來講,我們要多次讀取硬盤才能找到相應(yīng)的塊,這樣訪問速度就會比較慢。

          為了解決這個問題,ext4做了一定的改變。它引入了一個新的概念,叫作Extents。比方說,一個文件大小為128M,如果使用4k大小的塊進(jìn)行存儲,需要32k個塊。如果按照ext2或者ext3那樣散著放,數(shù)量太大了。但是Extents可以用于存放連續(xù)的塊,也就是說,我們可以把128M放在一個Extents里面。這樣的話,對大文件的讀寫性能提高了,文件碎片也減少了。

          Exents是一個樹狀結(jié)構(gòu):

          每個節(jié)點都有一個頭,ext4_extent_header可以用來描述某個節(jié)點。

          struct ext4_extent_header {
              __le16  eh_magic;   /* probably will support different formats */
              __le16  eh_entries; /* number of valid entries */
              __le16  eh_max;     /* capacity of store in entries */
              __le16  eh_depth;   /* has tree real underlying blocks? */
              __le32  eh_generation;  /* generation of the tree */
          };

          eh_entries表示這個節(jié)點里面有多少項。這里的項分兩種,如果是葉子節(jié)點,這一項會直接指向硬盤上的連續(xù)塊的地址,我們稱為數(shù)據(jù)節(jié)點ext4_extent;如果是分支節(jié)點,這一項會指向下一層的分支節(jié)點或者葉子節(jié)點,我們稱為索引節(jié)點ext4_extent_idx。這兩種類型的項的大小都是12個byte。

          /*
           * This is the extent on-disk structure.
           * It's used at the bottom of the tree.
           */

          struct ext4_extent {
              __le32  ee_block;   /* first logical block extent covers */
              __le16  ee_len;     /* number of blocks covered by extent */
              __le16  ee_start_hi;    /* high 16 bits of physical block */
              __le32  ee_start_lo;    /* low 32 bits of physical block */
          };
          /*
           * This is index on-disk structure.
           * It's used at all the levels except the bottom.
           */

          struct ext4_extent_idx {
              __le32  ei_block;   /* index covers logical blocks from 'block' */
              __le32  ei_leaf_lo; /* pointer to the physical block of the next *
                           * level. leaf or next index could be there */

              __le16  ei_leaf_hi; /* high 16 bits of physical block */
              __u16   ei_unused;
          };

          如果文件不大,inode里面的i_block中,可以放得下一個ext4_extent_header和4項ext4_extent。所以這個時候,eh_depth為0,也即inode里面的就是葉子節(jié)點,樹高度為0。

          如果文件比較大,4個extent放不下,就要分裂成為一棵樹,eh_depth>0的節(jié)點就是索引節(jié)點,其中根節(jié)點深度最大,在inode中。最底層eh_depth=0的是葉子節(jié)點。

          除了根節(jié)點,其他的節(jié)點都保存在一個塊4k里面,4k扣除ext4_extent_header的12個byte,剩下的能夠放340項,每個extent最大能表示128MB的數(shù)據(jù),340個extent會使你的表示的文件達(dá)到42.5GB。

          inode位圖和塊位圖

          inode的位圖大小為4k,每一位對應(yīng)一個inode。如果是1,表示這個inode已經(jīng)被用了;如果是0,則表示沒被用。block的位圖同理。

          在Linux操作系統(tǒng)里面,想要創(chuàng)建一個新文件,會調(diào)用open函數(shù),并且參數(shù)會有O_CREAT。這表示當(dāng)文件找不到的時候,我們就需要創(chuàng)建一個。那么open函數(shù)的調(diào)用過程大致是:要打開一個文件,先要根據(jù)路徑找到文件夾。如果發(fā)現(xiàn)文件夾下面沒有這個文件,同時又設(shè)置了O_CREAT,就說明我們要在這個文件夾下面創(chuàng)建一個文件。

          創(chuàng)建一個文件,那么就需要創(chuàng)建一個inode,那么就會從文件系統(tǒng)里面讀取inode位圖,然后找到下一個為0的inode,就是空閑的inode。對于block位圖,在寫入文件的時候,也會有這個過程。

          文件系統(tǒng)的格式

          數(shù)據(jù)塊的位圖是放在一個塊里面的,共4k。每位表示一個數(shù)據(jù)塊,共可以表示個數(shù)據(jù)塊。如果每個數(shù)據(jù)塊也是按默認(rèn)的4K,最大可以表示空間為個byte,也就是128M,那么顯然是不夠的。

          這個時候就需要用到塊組,數(shù)據(jù)結(jié)構(gòu)為ext4_group_desc,這里面對于一個塊組里的inode位圖bg_inode_bitmap_lo、塊位圖bg_block_bitmap_lo、inode列表bg_inode_table_lo,都有相應(yīng)的成員變量。

          這樣一個個塊組,就基本構(gòu)成了我們整個文件系統(tǒng)的結(jié)構(gòu)。因為塊組有多個,塊組描述符也同樣組成一個列表,我們把這些稱為塊組描述符表。

          我們還需要有一個數(shù)據(jù)結(jié)構(gòu),對整個文件系統(tǒng)的情況進(jìn)行描述,這個就是超級塊ext4_super_block。里面有整個文件系統(tǒng)一共有多少inode,s_inodes_count;一共有多少塊,s_blocks_count_lo,每個塊組有多少inode,s_inodes_per_group,每個塊組有多少塊,s_blocks_per_group等。這些都是這類的全局信息。

          最終,整個文件系統(tǒng)格式就是下面這個樣子。

          默認(rèn)情況下,超級塊和塊組描述符表都有副本保存在每一個塊組里面。防止這些數(shù)據(jù)丟失了,導(dǎo)致整個文件系統(tǒng)都打不開了。

          由于如果每個塊組里面都保存一份完整的塊組描述符表,一方面很浪費空間;另一個方面,由于一個塊組最大128M,而塊組描述符表里面有多少項,這就限制了有多少個塊組,128M * 塊組的總數(shù)目是整個文件系統(tǒng)的大小,就被限制住了。

          因此引入Meta Block Groups特性。

          首先,塊組描述符表不會保存所有塊組的描述符了,而是將塊組分成多個組,我們稱為元塊組(Meta Block Group)。每個元塊組里面的塊組描述符表僅僅包括自己的,一個元塊組包含64個塊組,這樣一個元塊組中的塊組描述符表最多64項。

          我們假設(shè)一共有256個塊組,原來是一個整的塊組描述符表,里面有256項,要備份就全備份,現(xiàn)在分成4個元塊組,每個元塊組里面的塊組描述符表就只有64項了,這就小多了,而且四個元塊組自己備份自己的。

          根據(jù)圖中,每一個元塊組包含64個塊組,塊組描述符表也是64項,備份三份,在元塊組的第一個,第二個和最后一個塊組的開始處。

          如果開啟了sparse_super特性,超級塊和塊組描述符表的副本只會保存在塊組索引為0、3、5、7的整數(shù)冪里。所以上圖的超級塊只在索引為0、3、5、7等的整數(shù)冪里。

          目錄的存儲格式

          其實目錄本身也是個文件,也有inode。inode里面也是指向一些塊。和普通文件不同的是,普通文件的塊里面保存的是文件數(shù)據(jù),而目錄文件的塊里面保存的是目錄里面一項一項的文件信息。這些信息我們稱為ext4_dir_entry。

          在目錄文件的塊中,最簡單的保存格式是列表,每一項都會保存這個目錄的下一級的文件的文件名和對應(yīng)的inode,通過這個inode,就能找到真正的文件。第一項是“.”,表示當(dāng)前目錄,第二項是“…”,表示上一級目錄,接下來就是一項一項的文件名和inode。

          如果在inode中設(shè)置EXT4_INDEX_FL標(biāo)志,那么就表示根據(jù)索引查找文件。索引項會維護一個文件名的哈希值和數(shù)據(jù)塊的一個映射關(guān)系。

          如果我們要查找一個目錄下面的文件名,可以通過名稱取哈希。如果哈希能夠匹配上,就說明這個文件的信息在相應(yīng)的塊里面。然后打開這個塊,如果里面不再是索引,而是索引樹的葉子節(jié)點的話,那里面還是ext4_dir_entry的列表,我們只要一項一項找文件名就行。通過索引樹,我們可以將一個目錄下面的N多的文件分散到很多的塊里面,可以很快地進(jìn)行查找。

          Linux中的文件緩存

          ext4文件系統(tǒng)層

          對于ext4文件系統(tǒng)來講,內(nèi)核定義了一個ext4_file_operations。

          const struct file_operations ext4_file_operations = {
          ......
              .read_iter  = ext4_file_read_iter,
              .write_iter = ext4_file_write_iter,
          ......
          }

          ext4_file_read_iter會調(diào)用generic_file_read_iter,ext4_file_write_iter會調(diào)用__generic_file_write_iter。

          ssize_t
          generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
          {
          ......
              if (iocb->ki_flags & IOCB_DIRECT) {
          ......
                  struct address_space *mapping = file->f_mapping;
          ......
                  retval = mapping->a_ops->direct_IO(iocb, iter);
              }
          ......
              retval = generic_file_buffered_read(iocb, iter, retval);
          }


          ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
          {
          ......
              if (iocb->ki_flags & IOCB_DIRECT) {
          ......
                  written = generic_file_direct_write(iocb, from);
          ......
              } else {
          ......
                  written = generic_perform_write(file, from, iocb->ki_pos);
          ......
              }
          }

          generic_file_read_iter和__generic_file_write_iter有相似的邏輯,就是要區(qū)分是否用緩存。因此,根據(jù)是否使用內(nèi)存做緩存,我們可以把文件的I/O操作分為兩種類型。

          第一種類型是緩存I/O。大多數(shù)文件系統(tǒng)的默認(rèn)I/O操作都是緩存I/O。對于讀操作來講,操作系統(tǒng)會先檢查,內(nèi)核的緩沖區(qū)有沒有需要的數(shù)據(jù)。如果已經(jīng)緩存了,那就直接從緩存中返回;否則從磁盤中讀取,然后緩存在操作系統(tǒng)的緩存中。對于寫操作來講,操作系統(tǒng)會先將數(shù)據(jù)從用戶空間復(fù)制到內(nèi)核空間的緩存中。這時對用戶程序來說,寫操作就已經(jīng)完成。至于什么時候再寫到磁盤中由操作系統(tǒng)決定,除非顯式地調(diào)用了sync同步命令。

          第二種類型是直接IO,就是應(yīng)用程序直接訪問磁盤數(shù)據(jù),而不經(jīng)過內(nèi)核緩沖區(qū),從而減少了在內(nèi)核緩存和用戶程序之間數(shù)據(jù)復(fù)制。

          如果在寫的邏輯__generic_file_write_iter里面,發(fā)現(xiàn)設(shè)置了IOCB_DIRECT,則調(diào)用generic_file_direct_write,里面同樣會調(diào)用address_space的direct_IO的函數(shù),將數(shù)據(jù)直接寫入硬盤。

          帶緩存的寫入操作

          我們先來看帶緩存寫入的函數(shù)generic_perform_write。

          ssize_t generic_perform_write(struct file *file,
                          struct iov_iter *i, loff_t pos)
          {
              struct address_space *mapping = file->f_mapping;
              const struct address_space_operations *a_ops = mapping->a_ops;
              do {
                  struct page *page;
                  unsigned long offset;   /* Offset into pagecache page */
                  unsigned long bytes;    /* Bytes to write to page */
                  status = a_ops->write_begin(file, mapping, pos, bytes, flags,
                                  &page, &fsdata);
                  copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
                  flush_dcache_page(page);
                  status = a_ops->write_end(file, mapping, pos, bytes, copied,
                                  page, fsdata);
                  pos += copied;
                  written += copied;


                  balance_dirty_pages_ratelimited(mapping);
              } while (iov_iter_count(i));
          }

          循環(huán)中主要做了這幾件事:

          • 對于每一頁,先調(diào)用address_space的write_begin做一些準(zhǔn)備;

          • 調(diào)用iov_iter_copy_from_user_atomic,將寫入的內(nèi)容從用戶態(tài)拷貝到內(nèi)核態(tài)的頁中;

          • 調(diào)用address_space的write_end完成寫操作;

          • 調(diào)用balance_dirty_pages_ratelimited,看臟頁是否太多,需要寫回硬盤。所謂臟頁,就是寫入到緩存,但是還沒有寫入到硬盤的頁面。

          對于第一步,調(diào)用的是ext4_write_begin來說,主要做兩件事:

          第一做日志相關(guān)的工作。

          ext4是一種日志文件系統(tǒng),是為了防止突然斷電的時候的數(shù)據(jù)丟失,引入了日志(Journal)模式。日志文件系統(tǒng)比非日志文件系統(tǒng)多了一個Journal區(qū)域。文件在ext4中分兩部分存儲,一部分是文件的元數(shù)據(jù),另一部分是數(shù)據(jù)。元數(shù)據(jù)和數(shù)據(jù)的操作日志Journal也是分開管理的。你可以在掛載ext4的時候,選擇Journal模式。這種模式在將數(shù)據(jù)寫入文件系統(tǒng)前,必須等待元數(shù)據(jù)和數(shù)據(jù)的日志已經(jīng)落盤才能發(fā)揮作用。這樣性能比較差,但是最安全。

          另一種模式是order模式。這個模式不記錄數(shù)據(jù)的日志,只記錄元數(shù)據(jù)的日志,但是在寫元數(shù)據(jù)的日志前,必須先確保數(shù)據(jù)已經(jīng)落盤。這個折中,是默認(rèn)模式。

          還有一種模式是writeback,不記錄數(shù)據(jù)的日志,僅記錄元數(shù)據(jù)的日志,并且不保證數(shù)據(jù)比元數(shù)據(jù)先落盤。這個性能最好,但是最不安全。

          第二調(diào)用grab_cache_page_write_begin來,得到應(yīng)該寫入的緩存頁。

          struct page *grab_cache_page_write_begin(struct address_space *mapping,
                              pgoff_t index, unsigned flags)

          {
              struct page *page;
              int fgp_flags = FGP_LOCK|FGP_WRITE|FGP_CREAT;
              page = pagecache_get_page(mapping, index, fgp_flags,
                      mapping_gfp_mask(mapping));
              if (page)
                  wait_for_stable_page(page);
              return page;
          }

          在內(nèi)核中,緩存以頁為單位放在內(nèi)存里面,每一個打開的文件都有一個struct file結(jié)構(gòu),每個struct file結(jié)構(gòu)都有一個struct address_space用于關(guān)聯(lián)文件和內(nèi)存,就是在這個結(jié)構(gòu)里面,有一棵樹,用于保存所有與這個文件相關(guān)的的緩存頁。

          對于第二步,調(diào)用iov_iter_copy_from_user_atomic。先將分配好的頁面調(diào)用kmap_atomic映射到內(nèi)核里面的一個虛擬地址,然后將用戶態(tài)的數(shù)據(jù)拷貝到內(nèi)核態(tài)的頁面的虛擬地址中,調(diào)用kunmap_atomic把內(nèi)核里面的映射刪除。

          size_t iov_iter_copy_from_user_atomic(struct page *page,
                  struct iov_iter *i, unsigned long offset, size_t bytes)
          {
              char *kaddr = kmap_atomic(page), *p = kaddr + offset;
              iterate_all_kinds(i, bytes, v,
                  copyin((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len),
                  memcpy_from_page((p += v.bv_len) - v.bv_len, v.bv_page,
                           v.bv_offset, v.bv_len),
                  memcpy((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
              )
              kunmap_atomic(kaddr);
              return bytes;
          }

          第三步中,調(diào)用ext4_write_end完成寫入。這里面會調(diào)用ext4_journal_stop完成日志的寫入,會調(diào)用block_write_end->__block_commit_write->mark_buffer_dirty,將修改過的緩存標(biāo)記為臟頁??梢钥闯?,其實所謂的完成寫入,并沒有真正寫入硬盤,僅僅是寫入緩存后,標(biāo)記為臟頁。

          第四步,調(diào)用 balance_dirty_pages_ratelimited,是回寫臟頁。

          /**
           * balance_dirty_pages_ratelimited - balance dirty memory state
           * @mapping: address_space which was dirtied
           *
           * Processes which are dirtying memory should call in here once for each page
           * which was newly dirtied.  The function will periodically check the system's
           * dirty state and will initiate writeback if needed.
            */

          void balance_dirty_pages_ratelimited(struct address_space *mapping)
          {
              struct inode *inode = mapping->host;
              struct backing_dev_info *bdi = inode_to_bdi(inode);
              struct bdi_writeback *wb = NULL;
              int ratelimit;
          ......
              if (unlikely(current->nr_dirtied >= ratelimit))
                  balance_dirty_pages(mapping, wb, current->nr_dirtied);
          ......
          }

          在balance_dirty_pages_ratelimited里面,發(fā)現(xiàn)臟頁的數(shù)目超過了規(guī)定的數(shù)目,就調(diào)用balance_dirty_pages->wb_start_background_writeback,啟動一個背后線程開始回寫。

          另外還有幾種場景也會觸發(fā)回寫:

          • 用戶主動調(diào)用sync,將緩存刷到硬盤上去,最終會調(diào)用wakeup_flusher_threads,同步臟頁;

          • 當(dāng)內(nèi)存十分緊張,以至于無法分配頁面的時候,會調(diào)用free_more_memory,最終會調(diào)用wakeup_flusher_threads,釋放臟頁;

          • 臟頁已經(jīng)更新了較長時間,時間上超過了設(shè)定時間,需要及時回寫,保持內(nèi)存和磁盤上數(shù)據(jù)一致性。

          帶緩存的讀操作

          看帶緩存的讀,對應(yīng)的是函數(shù)generic_file_buffered_read。

          static ssize_t generic_file_buffered_read(struct kiocb *iocb,
                  struct iov_iter *iter, ssize_t written)

          {
              struct file *filp = iocb->ki_filp;
              struct address_space *mapping = filp->f_mapping;
              struct inode *inode = mapping->host;
              for (;;) {
                  struct page *page;
                  pgoff_t end_index;
                  loff_t isize;
                  page = find_get_page(mapping, index);
                  if (!page) {
                      if (iocb->ki_flags & IOCB_NOWAIT)
                          goto would_block;
                      page_cache_sync_readahead(mapping,
                              ra, filp,
                              index, last_index - index);
                      page = find_get_page(mapping, index);
                      if (unlikely(page == NULL))
                          goto no_cached_page;
                  }
                  if (PageReadahead(page)) {
                      page_cache_async_readahead(mapping,
                              ra, filp, page,
                              index, last_index - index);
                  }
                  /*
                   * Ok, we have the page, and it's up-to-date, so
                   * now we can copy it to user space...
                   */

                  ret = copy_page_to_iter(page, offset, nr, iter);
              }
          }

          在generic_file_buffered_read函數(shù)中,我們需要先找到page cache里面是否有緩存頁。如果沒有找到,不但讀取這一頁,還要進(jìn)行預(yù)讀,這需要在page_cache_sync_readahead函數(shù)中實現(xiàn)。預(yù)讀完了以后,再試一把查找緩存頁。

          如果第一次找緩存頁就找到了,我們還是要判斷,是不是應(yīng)該繼續(xù)預(yù)讀;如果需要,就調(diào)用page_cache_async_readahead發(fā)起一個異步預(yù)讀。

          最后,copy_page_to_iter會將內(nèi)容從內(nèi)核緩存頁拷貝到用戶內(nèi)存空間。

          作者:luozhiyun
          來源:https://www.cnblogs.com/luozhiyun/p/13061199.html

          如果您覺得這篇文章對您有點用的話,麻煩您為本文來個四連:轉(zhuǎn)發(fā)分享、點贊、點在看、留言,因為這將是我寫作與分享更多優(yōu)質(zhì)文章的最強動力!


          本公眾號全部博文已整理成一個目錄,請在公眾號后臺回復(fù)「m」獲??!

          推薦閱讀:

          1、如何快速將 Linux 系統(tǒng)制作成 ISO 鏡像文件?
          2、深度好文:Linux 系統(tǒng)內(nèi)存知識
          3、Linux 進(jìn)程總結(jié)
          4、一行代碼如何隱藏 Linux 進(jìn)程?
          5、盤點多款國產(chǎn) Linux 桌面操作系統(tǒng)
          6、你不知道的 Linux 使用技巧
          7、不知道這十項 Linux 常識,就別說自己玩過 Linux!
          8、實用!五款新型 Linux 命令行工具
          9、Linux 自帶神器 logrotate 詳解!
          10、13 款 Linux 比較實用的工具
          關(guān)注微信公眾號「杰哥的IT之旅」,后臺回復(fù)「1024」查看更多內(nèi)容,回復(fù)「加群備注:地區(qū)-職業(yè)方向-昵稱 即可加入讀者交流群。

                 
                   
          點個[在看],是對杰哥最大的支持!
          瀏覽 38
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产足交视频 | 国产 乱伦 av | 国产麻豆剧传媒精品国产 | 护士AV无码A片在线观看 | 亚洲AV成人无码一区二区三区 |