Linux工作原理3設備

来源:https://www.cnblogs.com/testing-/archive/2023/05/31/17439516.html
-Advertisement-
Play Games

# Linux Kernel 許可權提升漏洞(CVE-2023-32233) 修複 2023年5月5日,MITRE發佈了Linux Kernel 許可權提升漏洞(CVE-2023-32233):Linux Kernel 的 Netfilter nf_tables子系統存在釋放後重用漏洞,在處理 Netf ...


本章是對正常運行的Linux系統中內核提供的設備基礎設施的基本考察。縱觀Linux的歷史,在內核如何向用戶展示設備方面已經有了許多變化。我們將從傳統的設備文件系統開始,看看內核如何通過sysfs提供設備配置信息。我們的目標是能夠提取系統中的設備信息,以便瞭解一些基本的操作。後面的章節將更詳細地介紹與特定類型設備的交互。
瞭解當出現新設備時,內核如何與用戶空間進行交互是很重要的。udev系統使用戶空間的程式能夠自動配置和使用新設備。你將看到內核如何通過udev向用戶空間進程發送消息的基本工作原理,以及該進程如何處理這些消息。

3.1 設備文件

操作Unix系統中的大多數設備是很容易的,因為內核將許多設備的I/O介面以文件形式呈現給用戶進程。這些設備文件有時被稱為設備節點。除了程式員使用常規的文件操作來處理設備外,一些設備也可以被像cat這樣的標準程式訪問。然而,你能用文件介面做的事情是有限的,所以不是所有的設備或設備功能都能用標準的文件I/O訪問。

設備文件在/dev目錄下,運行ls /dev可以看到/dev中的相當多的文件。那麼,你是如何處理設備的呢?


$ echo blah blah > /dev/null

就像其他帶有重定向輸出的命令一樣,這個命令將標準輸出中的內容發送到文件中。然而,這個文件是/dev/null,是一個設備,所以內核繞過了通常的文件操作,對寫入這個設備的數據使用了設備驅動程式。在/dev/null的情況下,內核只是接受了輸入的數據並將其丟棄。
要識別一個設備並查看其許可權,可以使用ls -l。下麵是一些例子:

$ ls -l
brw-rw---- 1 root disk 8, 1 Sep 6 08:37 sda1
crw-rw-rw- 1 root root 1, 3 Sep 6 08:37 null
prw-r-r-- 1 root root 0 Mar 3 19:17 fdata
srw-rw-rw- 1 root root 0 Dec 18 07:43 log

註意每一行的第一個字元(文件模式的第一個字元)。如果這個字元是b、c、p或s,則該文件是一個設備。這些字母分別代表塊、字元、管道和套接字:

  • 塊設備
    程式以固定的塊來訪問塊設備的數據。前面例子中的sda1是一個磁碟設備,是塊設備的一種類型。磁碟可以很容易地被分割成數據塊。因為塊設備的總大小是固定的,並且容易索引,程式在內核的幫助下可以快速隨機訪問設備中的任何塊。

  • 字元設備
    字元設備與數據流一起工作。你只能從字元設備中讀取字元或向字元設備中寫入字元,就像前面用/dev/null演示的那樣。字元設備沒有大小之分;當你從設備中讀出或寫入時,內核通常對它進行讀或寫操作。直接連接到你的電腦上的印表機是由字元設備表示的。值得註意的是,在字元設備的交互過程中,內核在將數據傳遞給設備或進程後,不能備份和重新檢查數據流。

  • 管道設備
    命名的管道就像字元設備,在I/O流的另一端是另一個進程,而不是內核驅動。

  • 套接字設備
    套接字是特殊用途的介面,經常用於進程間通信。它們經常在/dev目錄之外被髮現。套接字文件代表Unix域套接字;你將在第10章中瞭解更多關於這些套接字的信息。
    在來自ls -l的塊和字元設備的文件列表中,日期前面的數字是主要和次要的設備號,內核用它來識別設備。類似的設備通常有相同的主設備號,比如sda3和sdb1(都是硬碟分區)。

註意
不是所有的設備都有設備文件,因為塊和字元設備的I/O介面不是在所有情況下都合適。例如,網路介面沒有設備文件。理論上,使用單一的字元設備與網路介面交互是可能的,但是由於這很困難,內核提供了其他的I/O介面。

3.2 sysfs設備路徑

傳統的Unix的/dev目錄是一種方便的方式,用戶進程可以引用和連接內核支持的設備,但這也是一種非常簡單的方案。在/dev中的設備名稱告訴你關於該設備的一些情況,但通常不夠有用。另一個問題是,內核是按照找到設備的順序來分配設備的,所以設備在重啟之間可能有不同的名字。

為了提供基於實際硬體屬性的附加設備的統一視圖,Linux內核通過文件和目錄系統提供sysfs介面。設備的基本路徑是/sys/devices。例如,位於/dev/sda的SATA硬碟在sysfs中可能有如下路徑:


/sys/devices/pci0000:00/0000:00:17.0/ata3/host0/target0:0:0/0:0:0:0/block/sda

你可以看到,與/dev/sda文件名相比,這個路徑相當長,它也是一個目錄。但你不能真正比較這兩個路徑,因為它們有不同的目的。/dev文件使用戶進程能夠使用設備,而/sys/devices路徑是用來查看信息和管理設備的。如果你列出設備路徑的內容,比如前面的那個,你會看到類似下麵的內容:


alignment_offset  discard_alignment  holders   removable  size       uevent
bdi               events             inflight  ro         slaves
capability        events_async       power     sda1       stat
dev               events_poll_msecs  queue     sda2       subsystem
device            ext_range          range     sda5       trace

這裡的文件和子目錄主要是供程式而不是人閱讀的,但是你可以通過查看/dev文件這樣的例子來瞭解它們包含和代表的內容。在這個目錄下運行cat dev會顯示數字8:0,這恰好是/dev/sda的主設備號和次設備號。
在/sys目錄下有一些快捷方式。例如,/sys/block應該包含一個系統上所有可用的塊設備。然而,這些只是符號鏈接;你應該運行ls -l /sys/block來顯示真正的sysfs路徑。
要在/dev中找到一個設備的sysfs位置可能很困難。使用如下的udevadm命令來顯示路徑和其他一些有趣的屬性:


$ udevadm info --query=all --name=/dev/sda

你會在第3.5節找到更多關於udevadm和整個udev系統的細節。

3.3 dd和設備

當你在處理塊和字元設備時,dd程式是非常有用的。它的唯一功能是從輸入文件或流中讀出,然後寫到輸出文件或流中,在這個過程中可能會進行一些編碼轉換。對於塊設備來說,dd的特別有用的功能是,你可以在文件的中間處理一大塊數據,而忽略之前或之後的內容。

警告:
dd的功能非常強大,所以當你運行它時,請確保你知道你在做什麼。如果不小心犯了錯誤,很容易損壞設備上的文件和數據。如果你不確定它將會做什麼,通常可以將輸出寫入一個新的文件。
dd以固定大小的塊複製數據。下麵是如何在字元設備上使用dd,利用一些常見的選項:


$ dd if=/dev/zero of=new_file bs=1024 count=1

正如你所看到的,dd的選項格式與大多數其他Unix命令的選項格式不同;它是基於舊的IBM工作控制語言(JCL)風格。你不是用破折號(-)來表示一個選項,而是用等號(=)來命名一個選項並設置其值。前面的例子從/dev/zero複製了一個1,024位元組的塊(一個連續的零位元組流)到new_file。
這些是重要的dd選項:

  • if=file 輸入文件。預設是標準輸入。
  • of=file 輸出文件。預設是標準輸出。
  • bs=size 區塊大小。dd一次讀寫這麼多位元組的數據。為了縮寫大塊的數據,你可以用b和k分別表示512和1024個位元組。因此,前面的例子可以讀取bs=1k,而不是bs=1024。
  • ibs=size, obs=size 輸入和輸出塊的大小。如果你能對輸入和輸出使用相同的塊大小,請使用bs選項;如果不能,請對輸入和輸出分別使用ibs和obs。
  • count=num 要複製的塊的總數。當處理巨大的文件時,或者處理提供無盡數據流的設備,比如/dev/zero,你希望dd在固定的點上停止;否則,你可能會浪費大量的磁碟空間、CPU時間,或者兩者都浪費。使用count和跳過參數,從大文件或設備中複製一小段。
  • skip=num 跳過輸入文件或數據流中的第一個num塊,不把它們複製到輸出。

3.4 設備名稱摘要

有時很難找到設備的名稱(例如,在給磁碟分區時)。這裡有幾個方法可以找出它是什麼:

  • 用udevadm查詢udevd(見第3.5節)。

  • 在/sys目錄下尋找設備。

  • 從journalctl -k命令(列印內核信息)或內核系統日誌(見第7.1節)的輸出中猜測其名稱。這個輸出可能包含了對你系統中設備的描述。

  • 對於系統已經可見的磁碟設備,你可以檢查mount命令的輸出。

  • 運行cat /proc/devices來查看你的系統目前有驅動的塊和字元設備。每一行都包括編號和名稱。數字是設備的主要編號,如第3.1節所述。如果你能從名稱中猜出設備,在/dev中尋找具有相應主要編號的字元或塊設備,你就找到了設備文件。

在這些方法中,只有第一個方法是可靠的,但它確實需要udev。如果你遇到udev不可用的情況,可以嘗試其他方法,但要記住,內核可能沒有適合你的硬體的設備文件。
下麵的章節列出了最常見的Linux設備和它們的命名規則。

3.4.1 硬碟: /dev/sd*

大多數連接到當前Linux系統的硬碟對應於帶有sd首碼的設備名,比如/dev/sda,/dev/sdb,等等。這些設備代表整個磁碟;內核為磁碟上的分區製作單獨的設備文件,如/dev/sda1和/dev/sda2。
這個命名規則需要解釋一下。名稱中的sd部分代表SCSI磁碟。小型電腦系統介面(SCSI)最初是作為一種硬體和協議標準開發的,用於磁碟和其他外圍設備等設備之間的通信。雖然傳統的SCSI硬體在大多數現代機器中沒有使用,但由於SCSI協議的適應性,它無處不在。例如,USB存儲設備使用它進行通信。SATA(串列ATA,PC上常見的存儲匯流排)磁碟上的情況要複雜一些,但Linux內核在與它們交談時,仍然在一定程度上使用SCSI命令。
要列出你系統上的SCSI設備,可以使用一個工具來行走由sysfs提供的設備路徑。其中一個最簡潔的工具是lsscsi。當你運行它時,你可以看到以下內容:


$ lsscsi
[0:0:0:0]1  disk2  ATA     WDC WD3200AAJS-2  01.0  /dev/sda3
[2:0:0:0]    disk    FLASH   Drive UT_USB20    0.00  /dev/sdb

第一列標識了系統中設備的地址,第二列描述了它是什麼類型的設備,最後一列3出在哪裡可以找到設備文件。其他的都是廠商信息。
Linux按照其驅動程式遇到設備的順序將設備分配給設備文件。所以,在前面的例子中,內核首先找到了磁碟,其次才是快閃記憶體驅動器。
不幸的是,當你重新配置硬體時,這種設備分配方案歷來會引起問題。舉例來說,你有一個有三個磁碟的系統: /dev/sda,/dev/sdb,和/dev/sdc。如果/dev/sdb爆炸了,你必須把它移走,以便機器能夠重新工作,那麼以前的/dev/sdc就會移到/dev/sdb上,而不再有/dev/sdc了。如果你直接參考fstab文件中的設備名稱(見第4.2.8節),你就必須對該文件做一些修改,以便使事情(大部分)恢復正常。為瞭解決這個問題,許多Linux系統使用通用唯一標識符(UUID;見第4.2.4節)和/或邏輯捲管理器(LVM)來穩定磁碟設備映射。
關於如何在Linux系統上使用磁碟和其他存儲設備,本文的討論幾乎沒有觸及錶面。關於使用磁碟的更多信息,見第4章。在本章的後面,我們將研究SCSI支持在Linux內核中是如何工作的。

3.4.2 虛擬磁碟: /dev/xvd, /dev/vd

一些磁碟設備為虛擬機進行了優化,如AWS實例和VirtualBox。Xen虛擬化系統使用/dev/xvd首碼,而/dev/vd是一種類似的類型。

3.4.3 非易失性記憶體設備: /dev/nvme*

一些系統現在使用非易失性記憶體快車(NVMe)介面來與某些類型的固態存儲進行對話。在Linux中,這些設備顯示在/dev/nvme*。你可以使用nvme list命令來獲得你系統上這些設備的列表。

3.4.4 設備映射器: /dev/dm-, /dev/mapper/

在一些系統上,比磁碟和其他直接塊存儲更高級別的是LVM,它使用一個叫做設備映射器的內核系統。如果你看到以/dev/dm-開頭的塊設備和/dev/mapper的符號鏈接,你的系統可能使用了它。你將在第4章中瞭解這一切。

3.4.5 CD和DVD驅動器: /dev/sr*

Linux將大多數光存儲驅動器識別為SCSI設備/dev/sr0、/dev/sr1,等等。然而,如果驅動器使用舊的介面,它可能會顯示為一個PATA設備,如下所述。/dev/sr*設備是只讀的,它們只用於從磁碟上讀取。對於光學設備的寫入和重寫能力,你將使用 "通用 "SCSI設備,如/dev/sg0。

3.4.6 PATA硬碟: /dev/hd*

PATA(Parallel ATA)是一種較早的存儲匯流排。Linux塊設備/dev/hda、/dev/hdb、/dev/hdc和/dev/hdd在舊版本的Linux內核和舊硬體上很常見。這些是基於介面0和1的設備對的固定分配。 有時,你可能會發現SATA驅動器被識別為這些磁碟之一。這意味著該SATA驅動器在相容模式下運行,這阻礙了性能。檢查你的BIOS設置,看看你是否可以將SATA控制器切換到其原始模式。

3.4.7 終端: /dev/tty, /dev/pts/, 和 /dev/tty

終端是在用戶進程和I/O設備之間移動字元的設備,通常用於向終端屏幕輸出文字。終端設備介面可以追溯到很久之前,當時的終端是基於打字機的設備,很多都是連接在一臺機器上的。
大多數終端是偽終端設備,是理解真正終端的I/O特性的模擬終端。內核不是與真正的硬體對話,而是將I/O介面呈現給軟體,例如你可能在其中輸入大部分命令的shell終端視窗。
兩個常見的終端設備是/dev/tty1(第一個虛擬控制台)和/dev/pts/0(第一個偽終端設備)。/dev/pts目錄本身是一個專門的文件系統。
/dev/tty設備是當前進程的控制終端。如個程式當前正在從終端讀寫,這個設備就是該終端的同義詞。進程不需要連接到終端。

  • 顯示模式和虛擬控制台

Linux有兩種主要的顯示模式:文本模式和圖形模式(第14章介紹了使用這種模式的視窗系統)。儘管Linux系統傳統上是以文本模式啟動的,但現在大多數發行版使用內核參數和臨時圖形顯示機制(bootsplashes,如plymouth)來完全隱藏系統啟動時的文本模式。在這種情況下,系統會在啟動過程接近尾聲時切換到全圖形模式。
Linux支持虛擬控制台來複用顯示。每個虛擬控制台可以在圖形或文本模式下運行。當處於文本模式時,你可以通過ALT功能鍵組合在控制台之間進行切換--例如,ALT-F1會帶你到/dev/tty1,ALT-F2會到/dev/tty2,以此類推。許多這樣的虛擬控制台可能被運行登錄提示的getty進程所占據,如第7.4節所述。

在圖形模式下使用的虛擬控制台略有不同。除非被指示使用一個特定的虛擬控制台,否則圖形環境會接管空閑的虛擬控制台,而不是從初始配置中獲得一個虛擬控制台的分配。例如,如果你有getty進程在tty1和tty2上運行,新的圖形環境會占用tty3。此外,一旦進入圖形模式,你通常必須按CTRL-ALT-功能鍵組合來切換到另一個虛擬控制台,而不是更簡單的ALT-功能鍵組合。
所有這些的結果是,如果你想在系統啟動後看到你的文本控制台,按CTRL-ALT-F1。要返回到圖形環境,按ALT-F2、ALT-F3,以此類推,直到你進入圖形環境。

註意:有些發行版在圖形模式下使用tty1。在這種情況下,你將需要嘗試其他控制台。

如果你在切換控制台時由於輸入機制的故障或其他情況而遇到麻煩,你可以嘗試用chvt命令強迫系統改變控制台。例如,要切換到tty1,以root身份運行以下命令:


# chvt 1

3.4.8 串列埠: /dev/ttyS, /dev/ttyUSB, /dev/ttyACM*

較早的RS-232類型和類似的串列埠被表示為真正的終端設備。你不能在命令行上對串口設備做很多事情,因為有太多的設置需要擔心,比如波特率和流量控制,但是你可以使用screen命令,通過添加設備路徑作為參數來連接到終端。你可能需要該設備的讀寫許可權;有時你可以通過將自己添加到特定的組(如dialout)來實現。
在Windows上被稱為COM1的埠是/dev/ttyS0;COM2是/dev/ttyS1;以此類推。插入式USB串列適配器顯示為USB和ACM,名稱為/dev/ttyUSB0、/dev/ttyACM0、/dev/ttyUSB1、/dev/ttyACM1,等等。
一些涉及到串列埠的最有趣的應用是基於微控制器的板子,你可以把它插入你的Linux系統進行開發和測試。例如,你可以通過USB串列介面訪問CircuitPython板的控制台和讀-評-印迴圈。你所需要做的就是插上一個,尋找設備(通常是/dev/ttyACM0),然後用屏幕連接到它。

3.4.9 並行埠: /dev/lp0和/dev/lp1

單向並口設備/dev/lp0和/dev/lp1代表著一種介面類型,在很大程度上已經被USB和網路所取代,在Windows中對應於LPT1:和LPT2:。你可以用cat命令將文件(如要列印的文件)直接發送到並口,但你可能需要在之後給印表機一個額外的進紙或複位。像CUPS這樣的列印伺服器在處理與印表機的交互方面要好得多。
雙向的並行埠是/dev/parport0和/dev/parport1。

3.4.10 音頻設備: /dev/snd/*, /dev/dsp, /dev/audio, 以及更多

Linux有兩套音頻設備。有獨立的設備用於高級Linux聲音架構(ALSA)系統介面和較早的開放聲音系統(OSS)。ALSA設備在/dev/snd目錄下,但很難直接使用它們。使用ALSA的Linux系統支持OSS的後向相容設備,如果目前載入了OSS的內核支持。
對OSS的dsp和音頻設備可以進行一些基本的操作。例如,電腦會播放你發送到/dev/dsp的任何WAV文件。然而,由於頻率不匹配,硬體可能做不到你期望的那樣。此外,在大多數系統上,該設備往往在你登錄後就開始忙碌。

註意:由於涉及到許多層次,Linux的聲音是一個混亂的主題。我們只談了內核級的設備,但通常還有用戶空間的伺服器,如 pulseaudio,管理來自不同來源的音頻,作為聲音設備和其他用戶空間進程之間的中介。

3.4.11 創建設備文件

在任何合理的最近的Linux系統上,你都不會創建自己的設備文件;它們是由devtmpfs和udev創建的(見第3.5節)。然而,看看如何創建設備文件是很有意義的,在罕見的情況下,你可能需要創建命名的管道或套接字文件。
mknod命令創建設備。你必須知道設備的名稱以及它的主號和次號。例如,創建/dev/sda1只需使用下麵的命令:


# mknod /dev/sda1 b 8 1

b 8 1指定了一個主數為8、次數為1的塊設備。對於字元或命名的管道設備,使用c或p而不是b(對於命名的管道,省略主要和次要數字)。
在舊版本的Unix和Linux中,維護/dev目錄是一個挑戰。隨著每一次重要的內核升級或驅動程式的增加,內核可以支持更多種類的設備,這意味著將有一組新的主要和次要數字被分配給設備文件名。為瞭解決這個維護難題,每個系統都有一個MAKEDEV程式,在/dev中創建設備組。當你升級你的系統時,你將試圖找到MAKEDEV的更新,然後運行它以創建新的設備。
這個靜態的系統變得很難看,所以一個替換是必要的。解決這個問題的第一個嘗試是devfs,一個內核空間的/dev實現,包含了當前內核支持的所有設備。然而,有一些限制,這導致了udev和devtmpfs的發展。

3.5 udev

我們已經談到了內核中不必要的複雜性是很危險的,因為你太容易引入系統的不穩定性。設備文件管理就是一個例子:你可以在用戶空間創建設備文件,那麼你為什麼要在內核中這樣做?Linux內核可以在檢測到系統中的新設備時(例如,當有人安裝了U盤時)向udevd的用戶空間進程發送通知。這個udevd進程可以檢查新設備的特性,創建設備文件,然後執行任何設備初始化。

註意:你幾乎肯定會看到udevd以systemd-udevd的形式在你的系統上運行,因為它是你將在第六章看到的啟動機制的一部分。

這就是理論。不幸的是,這種方法有一個問題--設備文件在啟動過程的早期是必需的,所以udevd也必須提前啟動。但是為了創建設備文件,udevd不能依賴於任何它應該創建的設備,它需要非常快速地執行其初始啟動,這樣系統的其他部分就不會因為等待udevd的啟動而被耽擱。

3.5.1 devtmpfs

devtmpfs文件系統是為瞭解決啟動過程中的設備可用性問題而開發的(關於文件系統的更多細節,見第4.2節)。這個文件系統類似於較早的devfs支持,但有所簡化。內核在必要時創建設備文件,但它也會通知udevd新的設備是可用的。收到這個信號後,udevd不會創建設備文件,但它會執行設備初始化,同時設置許可權並通知其他進程新設備可用。此外,它在/dev中創建一些符號鏈接,以進一步識別設備。你可以在/dev/disk/by-id目錄下找到例子,每個連接的磁碟都有一個或多個條目。

例如,考慮典型的磁碟(連接在/dev/sda)和它在/dev/disk/by-id中的分區的鏈接:


$ ls -l /dev/disk/by-id
lrwxrwxrwx 1 root root  9 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671 -> ../../sda
lrwxrwxrwx 1 root root 10 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671-part1 ->
../../sda1
lrwxrwxrwx 1 root root 10 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671-part2 ->
../../sda2
lrwxrwxrwx 1 root root 10 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671-part5 ->
../../sda5

udevd進程按介面類型命名鏈接,然後按製造商和型號信息、序列號和分區(如果適用)命名。

註意:devtmpfs中的 "tmp "表示文件系統駐留在主記憶體中,具有用戶空間進程的讀/寫能力;這個特性使udevd能夠創建這些符號鏈接。我們將在第4.2.12節看到更多細節。

但udevd如何知道要創建哪些符號鏈接,以及如何創建它們?下一節將描述udevd是如何工作的。然而,你不需要知道這些或本章中的任何其他材料來繼續閱讀本書。事實上,如果這是你第一次研究Linux設備,我們強烈建議你跳到下一章,開始學習如何使用磁碟。

udevd的操作和配置

udevd守護進程的運作方式如下:

  • 內核通過內部網路鏈接向udevd發送通知事件,稱為uevent。
  • udevd載入uevent中的所有屬性。
  • udevd解析其規則,根據這些規則過濾和更新uevent,並採取相應的行動或設置更多屬性。
    udevd從內核收到的傳入的uevent可能是這樣的(你會在第3.5.4節學習如何用udevadm monitor --property命令獲得這個輸出):

ACTION=change
DEVNAME=sde
DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/host4/
target4:0:0/4:0:0:3/block/sde
DEVTYPE=disk
DISK_MEDIA_CHANGE=1
MAJOR=8
MINOR=64
SEQNUM=2752
SUBSYSTEM=block
UDEV_LOG=3

這個特殊的事件是對設備的改變。在接收到uevent後,udevd知道了設備的名稱、sysfs設備路徑和其他一些與屬性相關的屬性;它現在準備開始處理規則。
規則文件在/lib/udev/rules.d和/etc/udev/rules.d目錄下。/lib中的規則是預設的,而/etc中的規則是重寫的。對規則的全面解釋會很乏味,你可以從udev(7)手冊中瞭解更多,但這裡有一些關於udevd如何讀取規則的基本信息:

  • udevd從規則文件的開始到結束讀取規則。
  • 在讀完一條規則並可能執行其動作後,udevd 繼續閱讀當前的規則文件,尋找更多適用的規則。
  • 有一些指令(如GOTO)可以在必要時跳過規則文件的部分內容。這些指令通常放在規則文件的頂部,如果它與udevd正在配置的特定設備無關,就跳過整個文件。

讓我們看一下第3.5.1節中/dev/sda例子中的符號鏈接。這些鏈接是由/lib/udev/rules.d/60-persistent-storage.rules中的規則定義的。在裡面,你會看到以下幾行:


# ATA
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode"

# ATAPI devices (SPC-3 or later)
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5",ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode"

這些規則與通過內核的SCSI子系統(見第3.6節)呈現的ATA磁碟和光學介質相匹配。你可以看到有一些規則來捕捉設備可能的不同表現方式,但想法是udevd將嘗試匹配以sd或sr開頭但沒有數字的設備(用KERNEL"sd[!0-9]|sr"表達式),以及子系統(SUBSYSTEMS"scsi"),最後還有一些其他屬性,取決於設備的類型。如果所有這些條件表達式在任何一條規則中都為真,udevd就會轉到下一個也是最後表達式:


IMPORT{program}="ata_id --export $tempnode"

這不是一個條件。相反,它是一個指令,從/lib/udev/ata_id命令中導入變數。如果你有這樣一個磁碟,自己在命令行上試試。它看起來會像這樣:


# /lib/udev/ata_id --export /dev/sda
ID_ATA=1
ID_TYPE=disk
ID_BUS=ata
ID_MODEL=WDC_WD3200AAJS-22L7A0
ID_MODEL_ENC=WDC\x20WD3200AAJS22L7A0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
\x20\x20\x20\x20\x20\x20\x20\x20\x20
ID_REVISION=01.03E10
ID_SERIAL=WDC_WD3200AAJS-22L7A0_WD-WMAV2FU80671
--snip--

現在,導入設置了環境,使這個輸出中的所有變數名都被設置為所示的值。例如,接下來的任何規則現在都會將 ENV{ID_TYPE} 識別為磁碟。

在我們到目前為止看到的兩條規則中,特別值得註意的是ID_SERIAL。在每條規則中,這個條件都出現在第二條:

env{id_serial}!="?*"

如果ID_SERIAL沒有被設置,這個表達式會評估為真。因此,如果ID_SERIAL被設置了,條件就是假的,整個當前規則就不適用,udevd就會轉到下一條規則。
為什麼會出現在這裡?這兩條規則的目的是運行ata_id來查找磁碟設備的序列號,然後將這些屬性添加到uevent的當前工作副本中。你會在許多udev規則中發現這個一般模式。
設置了ENV{ID_SERIAL}後,udevd現在可以在後面的規則文件中評估這個規則,它尋找任何連接的SCSI磁碟:

KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*",SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"

你可以看到,這條規則要求ENV{ID_SERIAL}被設置,它有一個指令:

SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"

這個指令告訴udevd為進入的設備添加一個符號鏈接。所以,現在你知道設備的符號鏈接是怎麼來的了!
你可能想知道如何區分條件表達式和指令。條件表達式用兩個等號(==)或砰的一聲等號(!=)表示,指令用一個等號(=)、一個加號(+=)或一個冒號等號(:=)表示。

3.5.3 udevadm

udevadm程式是udevd的管理工具。 你可以重新載入udevd規則和觸發事件,但udevadm最強大的功能可能是搜索和探索系統設備的能力,以及udevd從內核接收uevent時監控uevent的能力。不過,命令的語法可能有點兒複雜。大多數選項都有長短兩種形式,我們在這裡使用長的。
讓我們從檢查一個系統設備開始。回到第3.5.2節中的例子,為了查看所有的udev屬性,以及與/dev/sda這樣的設備的規則一起生成的屬性,運行以下命令:

$ udevadm info --query=all --name=/dev/sda
P: /devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
N: sda
S: disk/by-id/ata-WDC_WD3200AAJS-22L7A0_WD-WMAV2FU80671
S: disk/by-id/scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671
S: disk/by-id/wwn-0x50014ee057faef84
S: disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0
E: DEVLINKS=/dev/disk/by-id/ata-WDC_WD3200AAJS-22L7A0_WD-WMAV2FU80671 /dev/disk/by-id/scsi
-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671 /dev/disk/by-id/wwn-0x50014ee057faef84 /dev/disk/by
-path/pci-0000:00:1f.2-scsi-0:0:0:0
E: DEVNAME=/dev/sda
E: DEVPATH=/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
E: DEVTYPE=disk
E: ID_ATA=1
E: ID_ATA_DOWNLOAD_MICROCODE=1
E: ID_ATA_FEATURE_SET_AAM=1
--snip--

每行的首碼表示設備的一個屬性或其他特征。在這個例子中,頂部的P:是sysfs設備路徑,N:是設備節點(也就是給/dev文件起的名字),S:表示udevd根據其規則放在/dev中的設備節點的符號鏈接,E:是udevd規則中提取的額外設備信息。(這個例子中的輸出遠遠超過了這裡需要展示的內容;自己嘗試一下這個命令,感受一下它的作用)。

3.5.4 設備監控

要用udevadm監視uevents,請使用monitor命令:

$ udevadm monitor
KERNEL[658299.569485] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
KERNEL[658299.569667] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
KERNEL[658299.570614] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/host15 
(scsi)
KERNEL[658299.570645] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/ 
host15/scsi_host/host15 (scsi_host)
UDEV [658299.622579] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
UDEV [658299.623014] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0 (usb)
UDEV [658299.623673] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/host15 
(scsi)
UDEV [658299.623690] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/
host15/scsi_host/host15 (scsi_host)
--snip--

在這個輸出中,每條信息都有兩份,因為預設行為是同時列印來自內核的傳入信息(用KERNEL標記)和來自udevd的處理信息。 要想只看到內核事件,請添加--kernel選項,要想只看到udevd處理事件,請使用--udev。要看到整個傳入的uevent,包括3.5.2節中顯示的屬性,使用--property選項。--udev和--property選項一起顯示處理後的uevent。
你還可以按子系統過濾事件。例如,要想只看到與SCSI子系統的變化有關的內核信息,使用這個命令:

$ udevadm monitor --kernel --subsystem-match=scsi

關於udevadm的更多信息,請看udevadm(8)手冊頁。
udev的內容還有很多。例如,有一個叫做udisksd的守護進程,它監聽事件,以便自動連接磁碟,並通知其他進程有新的磁碟可用。

3.6 深入瞭解: SCSI和Linux內核

在這一節中,我們將看一下Linux內核中的SCSI支持,作為探索Linux內核結構的一部分。你不需要為了使用磁碟而瞭解這些信息,所以如果你急於使用磁碟,請繼續閱讀第四章。此外,這裡的材料比你到目前為止所看到的更高級,更具有理論性,所以如果你想保持動手能力,你肯定應該跳到下一章。
讓我們從一點背景開始。傳統的SCSI硬體設置是一個主機適配器通過SCSI匯流排與一連串的設備相連,如圖3-1所示。主機適配器被連接到電腦上。主機適配器和設備都有一個SCSI ID,根據SCSI版本,每條匯流排可以有8或16個ID。一些管理員可能會用SCSI目標這個詞來指代設備和它的SCSI ID,因為在SCSI協議中,會話的一端被稱為目標。

圖3-1:帶有主機適配器和設備的SCSI匯流排

任何設備都可以通過SCSI命令集以點對點的關係與另一個設備進行通信。電腦沒有直接連接到設備鏈上,所以它必須通過主機適配器才能與磁碟和其他設備通信。通常情況下,電腦向主機適配器發送SCSI命令以轉達給設備,而設備則通過主機適配器轉達響應。
較新版本的SCSI,如串列連接SCSI(SAS),提供了卓越的性能,但你可能不會在大多數機器中找到真正的SCSI設備。你會更經常地遇到使用SCSI命令的USB存儲設備。此外,支持ATAPI的設備(如CD/DVD-ROM驅動器)使用SCSI命令集的一個版本。
SATA磁碟也作為SCSI設備出現在你的系統上,但它們略有不同,因為它們中的大多數是通過libata庫中的轉換層進行通信的(見3.6.2節)。一些SATA控制器(特別是高性能的RAID控制器)在硬體中進行這種轉換。
這一切是如何組成的?考慮一下下麵系統中顯示的設備:

$ lsscsi
[0:0:0:0]diskATAWDC WD3200AAJS-201.0/dev/sda
[1:0:0:0]cd/dvdSlimtypeDVD A DS8A5SHXA15/dev/sr0
[2:0:0:0]diskUSB2.0CardReader CF0100/dev/sdb
[2:0:0:1]diskUSB2.0CardReader SM XD0100/dev/sdc
[2:0:0:2]diskUSB2.0CardReader MS0100/dev/sdd
[2:0:0:3]diskUSB2.0CardReader SD0100/dev/sde
[3:0:0:0]diskFLASHDrive UT_USB200.00/dev/sdf

方括弧內的數字從左到右分別是SCSI主機適配器號、SCSI匯流排號、設備SCSI ID和LUN(邏輯單元號,是設備的進一步細分)。在這個例子中,有四個連接的適配器(scsi0、scsi1、scsi2和scsi3),每個都有個匯流排(都是匯流排號0),每個匯流排上只有一個設備(都是目標0)。位於2:0:0的USB讀卡器有四個邏輯單元,但每一種快閃記憶體卡都可以被插入。內核給每個邏輯單元分配了一個不同的設備文件。
儘管不是SCSI設備,NVMe設備有時會在lsscsi輸出中顯示出N作為適配器編號。
註意:如果你想自己嘗試lsscsi,你可能需要把它作為一個額外的軟體包來安裝。
圖3-2顯示了這個特定系統配置的內核中的驅動和介面層次,從單個設備驅動到塊驅動。它不包括SCSI通用(sg)驅動。
儘管這是一個龐大的結構,一開始可能會讓人不知所措,但是圖中的數據流是非常線性的。讓我們從SCSI子系統和它的三層驅動開始剖析:
頂層處理一類設備的操作。例如,sd(SCSI磁碟)驅動程式就在這一層;它知道如何將來自內核塊設備介面的請求翻譯成SCSI協議中的磁碟特定命令,反之亦然。
中間層在頂層和底層之間調節和路由SCSI信息,並跟蹤所有的SCSI匯流排和連接到系統的設備。
底層處理特定的硬體動作。這裡的驅動程式向特定的主機適配器或硬體發送傳出的SCSI協議信息,並從硬體提取傳入的信息。與頂層分離的原因是,儘管SCSI消息對於一個設備類別(如磁碟類別)是統一的,但不同種類的主機適配器有不同的發送相同消息的程式。

圖3-2: Linux SCSI子系統原理圖
上層和下層包含許多不同的驅動,但是重要的是要記住,對於你系統上的任何一個設備文件,內核(幾乎總是)使用一個上層驅動和一個下層驅動。在我們的例子中,對於位於/dev/sda的磁碟,內核使用sd頂層驅動和ATA橋層驅動。
有些時候,你可能會為一個硬體設備使用不止一個上層驅動(見第3.6.3節)。對於真正的硬體SCSI設備,比如連接到SCSI主機適配器或硬體RAID控制器的磁碟,下層驅動程式直接與下麵的硬體對話。然而,對於你發現連接到SCSI子系統的大多數硬體來說,情況就不同了。

3.6.1 USB存儲和SCSI

為了讓SCSI子系統與普通的USB存儲硬體對話,如圖3-2所示,內核需要的不僅僅是一個低層的SCSI驅動。一個由/dev/sdf代表的U盤可以理解SCSI命令,但是為了和驅動器進行實際的通信,內核需要知道如何通過USB系統進行對話。
從抽象的角度來看,USB與SCSI非常相似--它有設備類別、匯流排和主機控制器。因此,Linux內核包括一個與SCSI子系統非常相似的三層USB子系統也就不足為奇了,它的頂端是設備類驅動程式,中間是匯流排管理核心,底部是主機控制器驅動程式。與SCSI子系統在其組件之間傳遞SCSI命令一樣,USB子系統在其組件之間傳遞USB信息。甚至還有一個lsusb命令,與lsscsi相似。
我們在這裡真正感興趣的部分是頂部的USB存儲驅動器。這個驅動充當了翻譯者的角色。在一端,驅動程式說的是SCSI,而在另一端,它說的是USB。因為存儲硬體在其USB信息中包含SCSI命令,所以驅動程式的工作相對容易:它主要是重新包裝數據。
有了SCSI和USB子系統,你幾乎擁有了與快閃記憶體盤對話所需的一切。最後缺失的環節是SCSI子系統中的下層驅動,因為USB存儲驅動是USB子系統的一部分,而不是SCSI子系統。(由於組織上的原因,這兩個子系統不應該共用一個驅動。)為了使子系統能夠相互交談,一個簡單的、低層的SCSI橋接驅動連接到USB子系統的存儲驅動。

3.6.2 SCSI和ATA

圖3-2中的SATA硬碟和光碟機都使用相同的SATA介面。為了將內核的SATA驅動連接到SCSI子系統,內核採用了一個橋接驅動,就像對待USB驅動器一樣,但是有不同的機制和額外的複雜情況。光碟機說的是ATAPI,這是ATA協議中編碼的SCSI命令的一個版本。然而,硬碟不使用ATAPI,也不對任何SCSI命令進行編碼!
Linux內核使用一個叫做libata的庫的一部分來調和SATA(和ATA)驅動器與SCSI子系統。對於講ATAPI的光碟機來說,這是一個相對簡單的任務,即把SCSI命令打包並提取到ATA協議中去。但對硬碟來說,這項任務要複雜得多,因為庫必須做一個完整的命令轉換。
光碟機的工作類似於將一本英文書打入電腦。你不需要為了完成這項工作而理解書的內容,甚至也不需要理解英語。但硬碟的任務更像是閱讀一本德語書,並將其作為英文翻譯輸入電腦。在這種情況下,你需要理解兩種語言以及書中的內容。
儘管有這樣的困難,libata還是執行了這項任務,並使將ATA/SATA介面和設備連接到SCSI子系統成為可能。(除了圖3-2中所示的一個SATA主機驅動程式外,通常還涉及更多的驅動程式,但為了簡單起見,我們沒有顯示出來)。

3.6.3 通用SCSI設備

當用戶空間進程與SCSI子系統通信時,它通常是通過塊設備層和/或其他位於SCSI設備類驅動(如sd或sr)之上的內核服務進行的。換句話說,大多數用戶進程不需要知道任何關於SCSI設備或它們的命令。
然而,用戶進程可以繞過設備類驅動程式,通過其通用設備直接向設備發出SCSI協議命令。例如,考慮第3.6節中描述的系統,但這次,看看當你為lsscsi添加-g選項以顯示通用設備時會發生什麼:

$ lsscsi -g
[0:0:0:0]   disk    ATA       WDC WD3200AAJS-2  01.0  /dev/sda 1/dev/sg0
[1:0:0:0]   cd/dvd  Slimtype  DVD A DS8A5SH     XA15  /dev/sr0   /dev/sg1
[2:0:0:0]   disk    USB2.0    CardReader CF     0100  /dev/sdb   /dev/sg2
[2:0:0:1]   disk    USB2.0    CardReader SM XD  0100  /dev/sdc   /dev/sg3
[2:0:0:2]   disk    USB2.0    CardReader MS     0100  /dev/sdd   /dev/sg4
[2:0:0:3]   disk    USB2.0    CardReader SD     0100  /dev/sde   /dev/sg5
[3:0:0:0]   disk    FLASH     Drive UT_USB20    0.00  /dev/sdf   /dev/sg6

除了通常的塊設備文件外,每個條目在最後一列1中列出SCSI通用設備文件。例如,位於/dev/sr0的光碟機的通用設備是/dev/sg1。
為什麼你想使用一個通用設備?答案是與內核中代碼的複雜性有關。隨著任務變得越來越複雜,最好把它們留在內核之外。考慮一下CD/DVD的寫入和讀取。讀取光碟是相當簡單的操作,而且有專門的內核驅動。
然而,寫光碟要比讀光碟困難得多,而且沒有關鍵的系統服務依賴於寫光碟的操作。沒有理由用這種活動來威脅內核空間。因此,要在Linux中寫入光碟,你需要運行一個用戶空間程式,與一個通用的SCSI設備對話,比如/dev/sg1。這個程式可能比內核驅動的效率低一些,但它更容易建立和維護。

3.6.4 單一設備的多種訪問方法

圖3-3展示了Linux SCSI子系統從用戶空間訪問光碟機的兩個點(sr和sg)(SCSI下層的任何驅動都被省略了)。進程A使用sr驅動從驅動器中讀取,進程B使用sg驅動向驅動器寫入。然而,像這樣的進程通常不會同時運行來訪問同一個設備。

圖3-3:光學設備驅動原理圖
在圖3-3中,進程A從塊設備中讀取數據。但是,用戶進程真的會以這種方式讀取數據嗎?通常情況下,答案是否定的,不是直接的。在塊設備上面還有更多的層,甚至還有更多的硬碟訪問點,你將在下一章中學習。

釘釘或微信號: pythontesting 微信公眾號:pythontesting
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 數組(Array) 數組是一個存儲相同類型元素的固定大小的順序集合。數組是用來存儲數據的集合,通常認為數組是一個同一類型變數的集合。 聲明數組變數並不是聲明 number0、number1、...、number99 一個個單獨的變數,而是聲明一個就像 numbers 這樣的變數,然後使用 nu ...
  • 今天這一份資料庫可以幫助學習認識簡繁體字。資料庫提供簡繁欄位,可以根據簡體選擇繁體,也可以根據繁體選擇簡體。 需要說明的是: 1.有很多簡體繁體字是一樣的,簡體字繁體字不一樣的記錄一共僅有3168條。 2.簡字中存在相同字多條記錄的情況,如下圖,不相同的簡字統計共有2萬多個。 3.同2一樣繁字中也存 ...
  • 很多時間,寫代碼並不能一擼到底,中間都是經歷過無數次的調試,才能正常正確的運行起來。就好像一臺設備剛買來也需要不斷的調試才能達到最佳狀態。 DotNet程式的調試,是DotNet程式員必備的技能之一,開發出穩定的程式、解決程式的疑難雜症都需要很強大的調試能力。DotNet調試有很多方法和技巧。 1. ...
  • C# 流程語句分為:順序結構,選擇結構,迴圈結構。 1. 順序結構 順序結構指的是程式一步步向下執行。 int a =10; int b =a; a++; 2. 選擇結構 C# 提供以下類型的選擇語句。 聲明描述 if聲明 一個if語句包含一個布爾表達式後跟一個或多個語句。 if...else聲明 ...
  • 在用雲伺服器搭建網站的時候,我們通常在Windows上寫好網站,再使用FTP客服端把寫好的網頁傳到Linux伺服器上。用Nginx搭建web伺服器時,預設的網站目錄是/var/www/html,該目錄的所有者是root和root組,如果用非root許可權的普通用戶登陸FTP,是無法對/var/www/ ...
  • 大家好,我是 god23bin。歡迎大家繼續圍觀《一分鐘學一個 Linux 命令》,每天只需一分鐘,記住一個 Linux 命令不成問題。本篇文章將聚焦於 pwd 命令,一個超級簡單又常用的命令。 ...
  • 哈嘍大家好,我是鹹魚 我們知道,隨著企業規模或者說業務規模的不斷擴大,為了應對不斷增長的業務需求和提高系統的可伸縮性、可靠性和性能,電腦系統由一開始的單體系統逐漸發展成分散式系統 那麼今天鹹魚給大家介紹一些關於小白在學習分散式系統遇到的一些常見誤解 ## 誤解1.網路是可靠的 **在分散式系統中, ...
  • JRE 和 JDK JRE:Java運行環境,如果想要運行Java程式至少要安裝JRE JDK:Java開發環境(開發工具包),如果要開發Java程式,必須安裝JDK JRE = JVM + 核心類庫 JDK = JRE + 開發工具包 JDK > JRE >JVM 關係如圖所示: JDK下載地址: ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...