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

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

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

      《Linux內核設計與實現》讀書筆記(十四)- 塊I/O層

      主要內容:

      • 塊設備簡介
      • 內核訪問塊設備的方法
      • 內核I/O調度程序

       

      1. 塊設備簡介

      I/O設備主要有2類:

      • 字符設備:只能順序讀寫設備中的內容,比如 串口設備,鍵盤
      • 塊設備:能夠隨機讀寫設備中的內容,比如 硬盤,U盤

      字符設備由于只能順序訪問,所以應用場景也不多,這篇文章主要討論塊設備。

      塊設備是隨機訪問的,所以塊設備在不同的應用場景中存在很大的優化空間。

       

      塊設備中最重要的一個概念就是塊設備的最小尋址單元。

      塊設備的最小尋址單元就是扇區,扇區的大小是2的整數倍,一般是 512字節。

      扇區是物理上的最小尋址單元,而邏輯上的最小尋址單元是塊。

      為了便于文件系統管理,塊的大小一般是扇區的整數倍,并且小于等于頁的大小。

       

      查看扇區和I/O塊的方法:

      [wangyubin@localhost]$ sudo fdisk -l
      
      WARNING: GPT (GUID Partition Table) detected on '/dev/sda'! The util fdisk doesn't support GPT. Use GNU Parted.
      
      
      Disk /dev/sda: 500.1 GB, 500107862016 bytes, 976773168 sectors
      Units = sectors of 1 * 512 = 512 bytes
      Sector size (logical/physical): 512 bytes / 4096 bytes
      I/O size (minimum/optimal): 4096 bytes / 4096 bytes
      Disk identifier: 0x00000000
      View Code

      上面的 Sector size 就是扇區的值,I/O size就是 塊的值

      從上面顯示的結果,我們發現有個奇怪的地方,扇區的大小有2個值,邏輯大小是 512字節,而物理大小卻是 4096字節。

      其實邏輯大小 512字節是為了兼容以前的軟件應用,而實際物理大小 4096字節是由于硬盤空間越來越大導致的。

      具體的來龍去脈請參考:4KB扇區的原因

       

      2. 內核訪問塊設備的方法

      內核通過文件系統訪問塊設備時,需要先把塊讀入到內存中。所以文件系統為了管理塊設備,必須管理[塊]和內存頁之間的映射。

      內核中有2種方法來管理 [] 和內存頁之間的映射。

      • 緩沖區和緩沖區頭
      • bio

       

      2.1 緩沖區和緩沖區頭

      每個 [] 都是一個緩沖區,同時對每個 [] 都定義一個緩沖區頭來描述它。

      由于 [] 的大小是小于內存頁的大小的,所以每個內存頁會包含一個或者多個 []

       

      緩沖區頭定義在 <linux/buffer_head.h>: include/linux/buffer_head.h

      struct buffer_head {
          unsigned long b_state;            /* 表示緩沖區狀態 */
          struct buffer_head *b_this_page;/* 當前頁中緩沖區 */
          struct page *b_page;            /* 當前緩沖區所在內存頁 */
      
          sector_t b_blocknr;        /* 起始塊號 */
          size_t b_size;            /* buffer在內存中的大小 */
          char *b_data;            /* 塊映射在內存頁中的數據 */
      
          struct block_device *b_bdev; /* 關聯的塊設備 */
          bh_end_io_t *b_end_io;        /* I/O完成方法 */
           void *b_private;             /* 保留的 I/O 完成方法 */
          struct list_head b_assoc_buffers;   /* 關聯的其他緩沖區 */
          struct address_space *b_assoc_map;    /* 相關的地址空間 */
          atomic_t b_count;                    /* 引用計數 */
      };
      View Code

      整個 buffer_head 結構體中的字段是減少過的,以前的內核中字段更多。

      各個字段的含義通過注釋都很明了,只有 b_state 字段比較復雜,它涵蓋了緩沖區可能的各種狀態。

       

      enum bh_state_bits {
          BH_Uptodate,    /* 包含可用數據 */
          BH_Dirty,    /* 該緩沖區是臟的(說明緩沖的內容比磁盤中的內容新,需要回寫磁盤) */
          BH_Lock,    /* 該緩沖區正在被I/O使用,鎖住以防止并發訪問 */
          BH_Req,        /* 該緩沖區有I/O請求操作 */
          BH_Uptodate_Lock,/* 由內存頁中的第一個緩沖區使用,使得該頁中的其他緩沖區 */
      
          BH_Mapped,    /* 該緩沖區是映射到磁盤塊的可用緩沖區 */
          BH_New,        /* 緩沖區是通過 get_block() 剛剛映射的,尚且不能訪問 */
          BH_Async_Read,    /* 該緩沖區正通過 end_buffer_async_read() 被異步I/O讀操作使用 */
          BH_Async_Write,    /* 該緩沖區正通過 end_buffer_async_read() 被異步I/O寫操作使用 */
          BH_Delay,    /* 緩沖區還未和磁盤關聯 */
          BH_Boundary,    /* 該緩沖區處于連續塊區的邊界,下一個塊不在連續 */
          BH_Write_EIO,    /* 該緩沖區在寫的時候遇到 I/O 錯誤 */
          BH_Ordered,    /* 順序寫 */
          BH_Eopnotsupp,    /* 該緩沖區發生 “不被支持” 錯誤 */
          BH_Unwritten,    /* 該緩沖區在磁盤上的位置已經被申請,但還有實際寫入數據 */
          BH_Quiet,    /* 該緩沖區禁止錯誤 */
      
          BH_PrivateStart,/* 不是表示狀態,分配給其他實體的私有數據區的第一個bit */
      };
      View Code

       

      在2.6之前的內核中,主要就是通過緩沖區頭來管理 [塊] 和內存之間的映射的。

      用緩沖區頭來管理內核的 I/O 操作主要存在以下2個弊端,所以在2.6開始的內核中,緩沖區頭的作用大大降低了。

      - 弊端 1

      對內核而言,操作內存頁是最為簡便和高效的,所以如果通過緩沖區頭來操作的話(緩沖區 即[塊]在內存中映射,可能比頁面要小),效率低下。

      而且每個 [塊] 對應一個緩沖區頭的話,導致內存的利用率降低(緩沖區頭包含的字段非常多)

      - 弊端 2

      每個緩沖區頭只能表示一個 [塊],所以內核在處理大數據時,會分解為對一個個小的 [塊] 的操作,造成不必要的負擔和空間浪費。

       

      2.2 bio

      bio結構體的出現就是為了改善上面緩沖區頭的2個弊端,它表示了一次 I/O 操作所涉及到的所有內存頁。

      /*
       * I/O 操作的主要單元,針對 I/O塊和更低級的層 (ie drivers and
       * stacking drivers)
       */
      struct bio {
          sector_t        bi_sector;    /* 磁盤上相關扇區 */
          struct bio        *bi_next;    /* 請求列表 */
          struct block_device    *bi_bdev; /* 相關的塊設備 */
          unsigned long        bi_flags;    /* 狀態和命令標志 */
          unsigned long        bi_rw;        /* 讀還是寫 */
      
          unsigned short        bi_vcnt;    /* bio_vecs的數目 */
          unsigned short        bi_idx;        /* bio_io_vect的當前索引 */
      
          /* Number of segments in this BIO after
           * physical address coalescing is performed.
           * 結合后的片段數目
           */
          unsigned int        bi_phys_segments;
      
          unsigned int        bi_size;    /* 剩余 I/O 計數 */
      
          /*
           * To keep track of the max segment size, we account for the
           * sizes of the first and last mergeable segments in this bio.
           * 第一個和最后一個可合并的段的大小
           */
          unsigned int        bi_seg_front_size;
          unsigned int        bi_seg_back_size;
      
          unsigned int        bi_max_vecs;    /* bio_vecs數目上限 */
          unsigned int        bi_comp_cpu;    /* 結束CPU */
      
          atomic_t        bi_cnt;        /* 使用計數 */
          struct bio_vec        *bi_io_vec;    /* bio_vec 鏈表 */
          bio_end_io_t        *bi_end_io; /* I/O 完成方法 */
          void            *bi_private;    /* bio結構體創建者的私有方法 */
      #if defined(CONFIG_BLK_DEV_INTEGRITY)
          struct bio_integrity_payload *bi_integrity;  /* data integrity */
      #endif
          bio_destructor_t    *bi_destructor;    /* bio撤銷方法 */
          /*
           * We can inline a number of vecs at the end of the bio, to avoid
           * double allocations for a small number of bio_vecs. This member
           * MUST obviously be kept at the very end of the bio.
           * 內嵌在結構體末尾的 bio 向量,主要為了防止出現二次申請少量的 bio_vecs
           */
          struct bio_vec        bi_inline_vecs[0];
      };
      View Code

       

      幾個重要字段說明:

      • bio 結構體表示正在執行的 I/O 操作相關的信息。
      • bio_io_vec 鏈表表示當前 I/O 操作涉及到的內存頁
      • bio_vec 結構體表示 I/O 操作使用的片段
      • bi_vcnt bi_io_vec鏈表中bi_vec的個數
      • bi_idx 當前的 bi_vec片段,通過 bi_vcnt(總數)和 bi_idx(當前數),就可以跟蹤當前 I/O 操作的進度

       

      bio_vec 結構體很簡單,定義如下:

      struct bio_vec {
          struct page    *bv_page;       /* 對應的物理頁 */
          unsigned int    bv_len;     /* 緩沖區大小 */
          unsigned int    bv_offset;  /* 緩沖區開始的位置 */
      };

       

      每個 bio_vec 都是對應一個頁面,從而保證內核能夠方便高效的完成 I/O 操作

      bio, bio_vec和page之間的關系

       

      2.3 2種方法的對比

      緩沖區頭和bio并不是相互矛盾的,bio只是緩沖區頭的一種改善,將以前緩沖區頭完成的一部分工作移到bio中來完成。

      bio中對應的是內存中的一個個頁,而緩沖區頭對應的是磁盤中的一個塊。

      對內核來說,配合使用bio和緩沖區頭 比 只使用緩沖區頭更加的方便高效。

      bio相當于在緩沖區上又封裝了一層,使得內核在 I/O操作時只要針對一個或多個內存頁即可,不用再去管理磁盤塊的部分。

       

      使用bio結構體還有以下好處:

      • bio結構體很容易處理高端內存,因為它處理的是內存頁而不是直接指針
      • bio結構體既可以代表普通頁I/O,也可以代表直接I/O
      • bio結構體便于執行分散-集中(矢量化的)塊I/O操作,操作中的數據可以取自多個物理頁面

       

      3. 內核I/O調度程序

      緩沖區頭和bio都是內核處理一個具體I/O操作時涉及的概念。

      但是內核除了要完成I/O操作以外,還要調度好所有I/O操作請求,盡量確保每個請求能有個合理的響應時間。

       

      下面就是目前內核中已有的一些 I/O 調度算法。

      3.1 linus電梯

      為了保證磁盤尋址的效率,一般會盡量讓磁頭向一個方向移動,等到頭了再反過來移動,這樣可以縮短所有請求的磁盤尋址總時間。

      磁頭的移動有點類似于電梯,所有這個 I/O 調度算法也叫電梯調度。

      linux中的第一個電梯調度算法就是 linus本人所寫的,所有也叫做 linus 電梯。

       

      linus電梯調度主要是對I/O請求進行合并和排序。

      當一個新請求加入I/O請求隊列時,可能會發生以下4種操作:

      1. 如果隊列中已存在一個對相鄰磁盤扇區操作的請求,那么新請求將和這個已存在的請求合并成一個請求
      2. 如果隊列中存在一個駐留時間過長的請求,那么新請求之間查到隊列尾部,防止舊的請求發生饑餓
      3. 如果隊列中已扇區方向為序存在合適的插入位置,那么新請求將被插入該位置,保證隊列中的請求是以被訪問磁盤物理位置為序進行排列的
      4. 如果隊列中不存在合適的請求插入位置,請求將被插入到隊列尾部

       

      linus電梯調度程序在2.6版的內核中被其他調度程序所取代了。

       

      3.2 最終期限I/O調度

      linus電梯調度主要考慮了系統的全局吞吐量,對于個別的I/O請求,還是有可能造成饑餓現象。

      而且讀寫請求的響應時間要求也是不一樣的,一般來說,寫請求的響應時間要求不高,寫請求可以和提交它的應用程序異步執行,

      但是讀請求一般和提交它的應用程序時同步執行,應用程序等獲取到讀的數據后才會接著往下執行。

      因此在 linus 電梯調度程序中,還可能造成 寫-饑餓-讀(wirtes-starving-reads)這種特殊問題。

       

      為了盡量公平的對待所有請求,同時盡量保證讀請求的響應時間,提出了最終期限I/O調度算法。

      最終期限I/O調度 算法給每個請求設置了超時時間,默認情況下,讀請求的超時時間500ms,寫請求的超時時間是5s

      但一個新請求加入到I/O請求隊列時,最終期限I/O調度和linus電梯調度相比,多出了以下操作:

      1. 新請求加入到 排序隊列(order-FIFO),加入的方法類似 linus電梯新請求加入的方法
      2. 根據新請求的類型,將其加入 讀隊列(read-FIFO) 或者寫隊列(wirte-FIFO) 的尾部(讀寫隊列是按加入時間排序的,所以新請求都是加到尾部)
      3. 調度程序首先判斷 讀,寫隊列頭的請求是否超時,如果超時,從讀,寫隊列頭取出請求,加入到派發隊列(dispatch-FIFO)
      4. 如果沒有超時請求,從 排序隊列(order-FIFO)頭取出一個請求加入到 派發隊列(dispatch-FIFO)
      5. 派發隊列(dispatch-FIFO)按順序將請求提交到磁盤驅動,完成I/O操作

       

      最終期限I/O調度 算法也不能嚴格保證響應時間,但是它可以保證不會發生請求在明顯超時的情況下仍得不到執行。

      最終期限I/O調度 的實現參見: block/deadline-iosched.c

       

      3.3 預測I/O調度

      最終期限I/O調度算法優先考慮讀請求的響應時間,但系統處于寫操作繁重的狀態時,會大大降低系統的吞吐量。

      因為讀請求的超時時間比較短,所以每次有讀請求時,都會打斷寫請求,讓磁盤尋址到讀的位置,完成讀操作后再回來繼續寫。

      這種做法保證讀請求的響應速度,卻損害了系統的全局吞吐量(磁頭先去讀再回來寫,發生了2次尋址操作)

       

      預測I/O調度算法是為了解決上述問題而提出的,它是基于最終期限I/O調度算法的。

      但有一個新請求加入到I/O請求隊列時,預測I/O調度與最終期限I/O調度相比,多了以下操作:

      1. 新的讀請求提交后,并不立即進行請求處理,而是有意等待片刻(默認是6ms)
      2. 等待期間如果有其他對磁盤相鄰位置進行讀操作的讀請求加入,會立刻處理這些讀請求
      3. 等待期間如果沒有其他讀請求加入,那么等待時間相當于浪費掉
      4. 等待時間結束后,繼續執行以前剩下的請求

       

      預測I/O調度算法中最重要的是保證等待期間不要浪費,也就是提高預測的準確性,

      目前這種預測是依靠一系列的啟發和統計工作,預測I/O調度程序會跟蹤并統計每個應用程序的I/O操作習慣,以便正確預測應用程序的讀寫行為。

       

      如果預測的準確率足夠高,那么預測I/O調度和最終期限I/O調度相比,既能提高讀請求的響應時間,又能提高系統吞吐量。

      預測I/O調度的實現參見: block/as-iosched.c

       

      :預測I/O調度是linux內核中缺省的調度程序。

       

      3.4 完全公正的排隊I/O調度

      完全公正的排隊(Complete Fair Queuing, CFQ)I/O調度 是為專有工作負荷設計的,它和之前提到的I/O調度有根本的不同。

      CFQ I/O調度 算法中,每個進程都有自己的I/O隊列,

      CFQ I/O調度程序以時間片輪轉調度隊列,從每個隊列中選取一定的請求數(默認4個),然后進行下一輪調度。

       

      CFQ I/O調度在進程級提供了公平,它的實現位于: block/cfq-iosched.c

       

      3.5 空操作的I/O調度

      空操作(noop)I/O調度幾乎不做什么事情,這也是它這樣命名的原因。

      空操作I/O調度只做一件事情,當有新的請求到來時,把它與任一相鄰的請求合并。

       

      空操作I/O調度主要用于閃存卡之類的塊設備,這類設備沒有磁頭,沒有尋址的負擔。

      空操作I/O調度的實現位于: block/noop-iosched.c

       

      3.6 I/O調度程序的選擇

      2.6內核中內置了上面4種I/O調度,可以在啟動時通過命令行選項 elevator=xxx 來啟用任何一種。

      elevator選項參數如下:

      參數

      I/O調度程序

      as 預測
      cfq 完全公正排隊
      deadline 最終期限
      noop 空操作

      如果啟動預測I/O調度,啟動的命令行參數中加上 elevator=as

      posted @ 2017-08-25 11:56  閆寶平  閱讀(302)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产成人高清亚洲综合| 99国产精品一区二区蜜臀| 日韩精品成人网页视频在线| 华人在线亚洲欧美精品| 亚洲第四色在线中文字幕| 99久久精品看国产一区| 中文字幕精品av一区二区五区| 国产熟睡乱子伦视频在线播放| 国产精品视频一区二区噜| 国产精品v片在线观看不卡| 热久在线免费观看视频 | 国产精品久久久久鬼色| 国产精品成人一区二区三区| 在线观看潮喷失禁大喷水无码| 国产亚洲欧美精品久久久| 开心五月深深爱天天天操| 超碰人人超碰人人| 久久综合色最新久久综合色| 久久精品国产中文字幕| 艳妇乳肉豪妇荡乳av| 亚洲国产在一区二区三区| 精品国偷自产在线视频99| 久久婷婷五月综合色精品| 久青草视频在线观看免费| 色狠狠色婷婷丁香五月| 久久天天躁狠狠躁夜夜网站| 免费播放一区二区三区| 2021AV在线无码最新| 深夜视频国产在线观看| 无码伊人66久久大杳蕉网站谷歌| 无码视频伊人| 国产在线午夜不卡精品影院 | 国产最新精品系列第三页| 国精品午夜福利视频不卡| 国产精品欧美福利久久| 国产亚洲精品日韩av在| 日韩在线视频网| 亚洲熟妇丰满多毛xxxx| 深夜福利资源在线观看| 平泉县| 亚洲色成人网站www永久四虎|