Linux 磁盤與磁盤分區

  Linux 系統中所有的硬件設備都是通過文件的方式來表現和使用的,我們將這些文件稱爲設備文件,硬盤對應的設備文件一般被稱爲塊設備文件。本文介紹磁盤設備在 Linux 系統中的表示方法以及如何創建磁盤分區。
  
  磁盤分類
  
  比較常見的磁盤類型有服務器中使用的 SCSI 硬盤和消費類市場中的 SATA 硬盤,當然還有當下大熱的各種固態硬盤。
  
  SCSI 硬盤
  
  SCSI 硬盤即採用 SCSI 接口的硬盤。它由於性能好、穩定性高,因此在服務器上得到廣泛應用。同時其價格也不菲,正因它的價格昂貴,所以在普通PC上很少見到它的蹤跡。SCSI 硬盤使用 50 針接口,外觀和普通硬盤接口有些相似(下圖來自互聯網):
  
  SATA 硬盤
  
  SATA(Serial ATA)口的硬盤又叫串口硬盤,Serial ATA 採用串行連接方式,串行 ATA 總線使用嵌入式時鐘信號,具備了更強的糾錯能力,與以往相比其最大的區別在於能對傳輸指令(不僅僅是數據)進行檢查,如果發現錯誤會自動矯正,這在很大程度上提高了數據傳輸的可靠性。串行接口還具有結構簡單、支持熱插拔的優點(下圖來自互聯網):
  
  固態硬盤
  
  固態硬盤(Solid State Disk),一般稱之爲 SSD 硬盤,固態硬盤是用固態電子存儲芯片陣列而製成的硬盤,由控制單元和存儲單元(FLASH芯片、DRAM芯片)組成。其主要特點是沒有傳統硬盤的機械結構,讀寫速度非常快(下圖來自互聯網):
  
  磁盤設備在 Linux 下的表示方法
  
  在 Linux 系統中磁盤設備文件的命名規則爲:
  
  主設備號 + 次設備號 + 磁盤分區號
  
  對於目前常見的磁盤,一般表示爲:
  
  sd[a-z]x
  
  主設備號代表設備的類型,相同的主設備號表示同類型的設備。當前常見磁盤的主設備號爲 sd。
  
  次設備號代表同類設備中的序號,用 "a-z" 表示。比如 /dev/sda 表示第一塊磁盤,/dev/sdb 表示第二塊磁盤。
  
  x 表示磁盤分區編號。在每塊磁盤上可能會劃分多個分區,針對每個分區,Linux 用 /dev/sdbx 表示,這裏的 x 表示第二塊磁盤的第 x 個分區。
  
  如下圖所示:
  
  該系統中一共有四塊磁盤 /dev/sda,/dev/sdb,/dev/sdc 和 /dev/sdd。其中的 /dev/sda 上創建了三個分區,分別是 /dev/sda1,/dev/sda2,/dev/sda5;/dev/sdb 上只有一個分區 /dev/sdb1。而 /dev/sdc 和 /dev/sdd 則尚未分區(也肯能是隻有一個分區,分區的名稱和磁盤的名稱相同)。
  
  磁盤分區
  
  創建磁盤分區大概有下面幾個目的:
  
  提升數據的安全性(一個分區的數據損壞不會影響其他分區的數據)
  
  支持安裝多個操作系統
  
  多個小分區對比一個大分區會有性能提升
  
  更好的組織數據
  
  磁盤的分區由主分區、擴展分區和邏輯分區組成。在一塊磁盤上,主分區的最大個數是 4,其中擴展分區也是一個主分區,並且最多只能有一個擴展分區,但可以在擴展分區上創建多個邏輯分區。因此主分區(包括擴展分區)的範圍是 1-4,邏輯分區從 5 開始。對於邏輯分區,Linux 規定它們必須建立在擴展分區上,而不是建立在主分區上。
  
  主分區的作用是用來啓動操作系統的,主要存放操作系統的啓動或引導程序,因此建議操作系統的引導程序都放在主分區,比如 Linux 的 /boot 分區,最好放在主分區上:
  
  擴展分區只不過是邏輯分區的 "容器"。實際上只有主分區和邏輯分區是用來進行數據存儲的,因而可以將數據集中存放在磁盤的邏輯分區中。
  
  我們可以通過 fdisk 命令來查看磁盤分區的信息:
  
  $ sudo fdisk -l /dev/sda
  
  輸出中的前幾行是磁盤的基本信息,比如總大小爲 80G,一共有多少個扇區(sector),每個扇區的大小等等。紅框中的則是我們比較關注的分區信息:
  
  第一列 Device 顯示了磁盤分區對應的設備文件名。
  
  第二列 Boot 顯示是否爲引導分區,上圖中的 /dev/sda1 就是引導分區。
  
  第三列 Start 表示磁盤分區的起始位置。
  
  第四列 End 表示磁盤分區的結束位置。
  
  第五列 Sectors 表示分區佔用的扇區數目。
  
  第六列 Size 顯示分區的大小。
  
  第七列和第八列顯示的內容相同,分別是數值 ID 及其文字描述。 Id 列顯示了磁盤分區對應的 ID,根據分區的不同,分區對應的 ID 號也不相同。Linux 下用 83 表示主分區和邏輯分區,5 表示擴展分區,8e 表示 LVM 分區,82 表示交換分區,7 表示 NTFS 分區。
  
  上圖中的信息表明:/dev/sda1 是一個主分區並且被用作引導分區;/dev/sda2 是擴展分區,其中只有一個邏輯分區,即 /dev/sda5,這點可以通過兩個分區相同的大小證明。
  
  利用 fdisk 劃分磁盤分區
  
  fdisk 是 Linux 系統中一款功能強大的磁盤分區管理工具,可以觀察硬盤的使用情況,也可以用來管理磁盤分區。本文僅介紹如何使用 fdisk 創建新的磁盤分區。
  
  假設我們的 Linux 系統中增加了一塊新的磁盤,系統對應的設備名爲 /dev/sdd,下面我們通過 fdisk 命令對這個磁盤進行分區。
  
  $ sudo fdisk /dev/sdd
  
  輸入命令 n 來創建新分區:
  
  根據上面的提示,我們選擇 p 來創建主分區,然後提示我們輸入分區的編號:
  
  主分區的編號爲 1- 4,這裏我們輸入了 1。接下來是設置分區的大小:
  
  分區的大小是通過設置分區開始處的扇區和結束處的扇區設置的。這裏如果回車兩次會把整個磁盤劃分爲一個分區,也就是整個磁盤的容器都分給了一個分區。這樣一個簡單的分區就差不多完成了,注意此時的分區信息還沒有寫入到磁盤中,在這裏還可以反悔,如果確認執行上面的分區,執行 w 命令就行了:
  
  這時分區操作已經完成了,我們可以通過下面的命令查看分區的結果:
  
  NioEventLoop#openSelector()實現了創建selector的功能,默認情況下,使用SelectorProvider#openSelector()方法創建一個新個selector:
  
  final Selector unwrappedSelector = provider.openSelector();
  
  如果設置環境變量io.netty.noKeySetOptimization=true, 會創建一個selectedKeySet = new SelectedSelectionKeySet(), 然後使用java的反射機制把selector的selectedKeys和publicSelectedKeys替換成selectedKeySet,具體步驟是:
  
  1.得到selector的真正類型: sun.nio.ch.SelectorImpl
  
  Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
  
  @Override
  
  public Object run() {
  
  try {
  
  return Class.forName(
  
  "sun.nio.ch.SelectorImpl",
  
  false,
  
  PlatformDependent.getSystemClassLoader());
  
  } catch (Throwable cause) {
  
  return cause;
  
  }
  
  }
  
  });
  
  final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;
  
  2.替換selector是屬性unwrappedSelector
  
  Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
  
  Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
  
  selectedKeysField.set(unwrappedSelector, selectedKeySet);
  
  publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
  
  之所以會設計一個這樣的優化選項,是因爲一般情況下調用完selector的select或selectNow方法後需要調用Selector#selectedKeys()得到觸發NIO事件的的SelectableChannel,這樣優化之後,可以直接從selectedKeySet中得到已經觸發了NIO事件的SelectableChannel。
  
  在selector上註冊channel感興趣的NIO事件
  
  NioEventLoop提供了unwrappedSelector方法,這個方法返回了它創建好的Selector實例。這樣任何的外部類都可以把任意的SelectableChannel註冊到這selector上。在AbstractNioChannel中, doRegister方法的實現就是使用了這個方法:
  
  selectionKey = javaChannel(www.michenggw.com).register(eventLoop().unwrappedSelector(www.dasheng178.com/), 0, this);
  
  另外,它還提供了一個register方法:
  
  public void register(final SelectableChannel ch, final int interestOps, final NioTask<?> task)
  
  這個方法會把task當成SelectableChannel的附件註冊到selector上:
  
  ch.register(selector, interestOps, task);
  
  實現EventExecutor的run方法,定義NIO事件和Executor任務的處理流程
  
  在NioEventLoop的run方法中實現NIO事件和EventExecutor的任務處理邏輯,這個run方法在io.netty.util.concurrent.SingleThreadEventExecutor中定義。在上一章中,我們看到了DefaultEventExecutor中是如何實現這個run方法的,這裏我們將要看到這run方法的另一個實現。和SingleThreadEventExecutor中的run方法相比,NioEventLoop的run方法不僅要及時地執行taskQueue中的任務,還要能及時地處理NIO事件,因此它會同時檢查selector中的NIO事件和和taskQueue隊列,任何一箇中有事件需要處理或有任務需要執行,它不會阻塞線程。同時它也保證了在沒有NIO事件和任務的情況下線程不會無謂的空轉浪費CUP資源。
  
  run主要實現如下,爲了更清晰的說明它的主要功能,我對原來的代碼進行了一些刪減。
  
  for(;;){
  
  try{
  
  //phase1: 同時檢查NIO事件和任務
  
  switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
  
  case SelectStrategy.CONTINUE:
  
  continue;
  
  case SelectStrategy.SELECT:
  
  select(wakenUp.getAndSet(www.yigouyule2.cn false)); //在taskQueue中沒有任務的時候執行select
  
  }
  
  //phase2: 進入處理NIO事件,執行executor任務
  
  try{
  
  //處理NIO事件
  
  processSelectedKeys();
  
  }finally{
  
  //處理taskQueu中的任務
  
  runAllTasks();
  
  }
  
  }catch(Throwable t){
  
  handleLoopException(t);
  
  $ sudo fdisk -l /dev/sdd
  
  如果嫌上面的執行過程麻煩,可以用下面的一行命令起到相同的效果:
  
  $ (echo n; echo p; echo 1; echo ; echo ; echo w) | sudo fdisk /dev/sdd
  
  更改分區的類型
  
  上面創建的分區類型默認爲 83(Linux),如果想要一個 8e(Linux LVM)類型的分區該怎麼辦?我們可以繼續使用 fdisk 命令修改分區的類型,這次輸入 t 命令來修改分區的類型:
  
  接下來可以選擇要修改的分區號,我們只有一個分區,所以默認就是 1。
  
  下面我們可以通過 L 命令來查看 fdisk 命令支持的分區類型:
  
  我們需要創建 LVM,因此我們使用 LVM 的類型代碼 8e:
  
  最後輸入 w 命令確認變更。再次查看 /dev/sdd 的分區信息,此時分區類型已經變成了 Linux LVM:
  
  總結
  
  分區是使用磁盤的基礎,在分區完成後還需要對分區進行格式化,並把格式化後的文件系統掛載到 Linux 系統之後才能存儲文件。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章