前言:
本文經過一定的修正,有一些更改,通過本文你可以很清晰的理解設備唯一性是怎么確定的,同時也能理解
/proc/devices和 /dev 的關系,即主從關系,是一對多的關系。
什么是主設備號和次設備號?
一個字符設備或者塊設備都會有一個主設備號和次設備號(主設備號對應的就是某種驅動程序)。主設備號和次設備號統稱為設備號。
主設備號用來表示一個特定的驅動程序。次設備號用來表示使用該驅動程序的其他設備。(主設備號和控制這類設備的驅動是一一對應的)
通俗的說就是主設備號標識設備對應的驅動程序,告訴Linux內核使用哪一個驅動程序為該設備(也就是/dev下的設備文件)服務;而次設備號則用來標識具體且唯一的某個設備. /proc/devices類似一個類別,而/dev/下是某個類別下的項,由他們兩個最終確定了一對多關系。
以磁盤為例:
在同一個系統中,磁盤設備的主設備號是唯一的。比如:scsi設備,次設備號只是在提供給scsi驅動程序內部使用,系統內核直接把次設備號傳遞給應用程序,scsi設備由驅動程序管理,我們可能有多個scsi設備,每個scsi設備都會分配一個次設備號。為了保證驅動程序的通用性,避免驅動程序移植過程中出現主設備號沖突,linux系統設定了主設備號的規則,包括保留的主設備號。
主設備號用來區分不同種類的設備,而次設備號用來區分同一類型的多個設備。對於常用設備,Linux有約定俗成的編號。
如果你的系統內核是3.10,可以到 /usr/src/linux/include/linux/major.h 查看設備號的定義,但不是當前配置的設備號
查看當前設備的設備號需查看 /proc/devices
注: 主設備號和次設備號,在不同磁盤的工具中都有輸出顯示,所以對於這個概念的理解還是很重要,否則你看不懂很多命令的輸出。
尤其是 cat /proc/devices 輸出的意義等。
設備的分類(區別於設備種類,種類是磁盤、內存等)
以 Linux 的方式看待設備可區分為 3 種基本設備類型。 每個設備常常實現 3 種類型中的 1 種, 因此可分類成字符設備, 塊設備, 或者一個網絡設備。
這種將設備分成不同類型或類別的方法並非是固定不變的。
1、字符(char)設備
一個字符( char ) 設備是一種可以當作一個字節流來存取的設備( 如同一個文件 ); 字符驅動模塊負責實現這種行為。
這樣的驅動模塊至少實現 open, close, read, 和 write 系統調用。文本控制台( /dev/console )和串口( /dev/ttyS0 及其他 )是字符設備的例子, 因為它們很好地展現了流的抽象。 字符設備通過文件系統結點來存取, 例如 /dev/tty1 和 /dev/lp0。
2、塊(block)設備
如同字符設備, 塊設備通過位於 /dev 目錄的文件系統結點來存取. 一個塊設備(例如一個磁盤)應該是可以駐有一個文件系統的。
在大部分的 Unix 系統, 一個塊設備只能處理特定的 I/O 操作, 即傳送一個或多個長度經常是 512 字節( 或一個更大的 2 的冪的數 )的整塊。
Linux, 相反, 允許應用程序讀寫一個塊設備象一個字符設備一樣;它允許一次傳送任意數目的字節。
結果就是, 塊和字符設備的區別僅僅在內核內部管理數據的方式上, 和在內核/驅動的軟件接口上不同。
如同一個字符設備, 每個塊設備都通過一個文件系統結點被存取的, 它們之間的區別對用戶是透明的。 塊驅動和字符驅動相比, 與內核的接口完全不同。
3、網絡接口設備
任何網絡事務都通過一個接口來進行, 就是說, 一個能夠與其他主機交換數據的設備。
通常, 一個接口是一個硬件設備, 但是它也可能是一個純粹的軟件設備, 比如回環接口(lo)。一個網絡接口負責發送和接收數據報文, 在內核網絡子系統的驅動下,
不必知道單個事務是如何映射到實際的被發送的報文上的。很多網絡連接( 特別那些使用 TCP 的)是面向流的, 但是網絡設備卻常常設計成處理報文的發送和接收。
網絡驅動對單個連接一無所知; 它只處理報文。
設備名稱
fb::frame緩沖 fd:—–:軟驅 hd:—–:IDE 硬盤/光驅 md:—–:RAID設備(Metadisk) dm:—–:LVM設備(DeviceMapper) xd:—–:虛擬機中的硬盤驅動器 tty:—–:終端設備 psaux:—–:PS/2 鼠標設備 lp:—–:打印機 par:—–:並口 pt:—–:偽終端 |
s:—–:SCSI設備 scd:—–:SCSI音頻光驅 sd:—–:SCSI硬盤 sg:—–:SCSI通用設備 sr:—–:SCSI數據光驅 st:—–:SCSI磁帶 cdrom:—–:光驅的符號鏈接 mouse:—–:鼠標設備的符號鏈接 gpmdata:—–:偽設備 null:—–:寫入消失 zero:—–:一直產生零 |
---|
實驗環境:
linux 系統采樣,因為內核的版本對於/proc下的機制影響比較大,所以要注意版本 [root@fp-web-130 src]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) [root@fp-web-130 src]# uname -r 3.10.0-1160.45.1.el7.x86_64
一、 查找主設備號位置文件 (內核創建)
/proc/devices 文件顯示當前配置的各種字符和塊設備(不包括未加載模塊的設備)
[root@fp-web-130 src]# cat /proc/devices
//顯示結果
Character devices: //字符設備
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
21 sg
29 fb
99 ppdev
128 ptm
136 pts
162 raw
180 usb
188 ttyUSB
189 usb_device
202 cpu/msr
203 cpu/cpuid
226 drm
249 hidraw
250 usbmon
251 bsg
252 watchdog
253 rtc
254 tpm
Block devices: //塊設備
259 blkext
7 loop //回環設備
8 sd //scsi設備
9 md
11 sr //只讀光驅
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd vcsa
129 sd vcsa1
130 sd vcsa2
131 sd vcsa3
132 sd vcsa4
133 sd vcsa5
134 sd vcsa6
135 sd
說明: sd,md,loop等就是設備名
參看: https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt
實例:lsblk 命令可以查看塊設備的主設備號和次設備號
解釋:
loop0 的主設備號是 7:0 7表示的主設備號,0是系統為loop0回環設備編了號.這個主從就表示了唯一性([root@fp-web-130 src]# cat /proc/devices 得到的回環設備loop是7)
loop1 的主設備號是 7:1 7表示的主設備號,1是系統為loop1回環設備編了號.
基本上如果主設備號和次設備號明白了,那么lsblk這個命令的基本輸出結果理解上就沒有難度了
為了使應用程序區分所控制設備的類型,內核使用主設備號。而存在多台同類設備時,為了選擇其中的一種,設備驅動程序就使用次設備號
舉例: 這里sd是8(cat /proc/devices查的),看到有一列是8,16 這里8就是主設備號、16就是次設備號
有三個目錄說下 /dev/* , /proc/devices, /sys/devices
1./proc/devices/中的設備是通過insmod加載到內核的,它可產生一個major供mknod作為參數(那么必然可以通過lsmod查看) 2./dev/*.* 是通過mknod加上去的,設備節點,mknod(創建設備節點)后在此添加一子目錄 格式:mknod device1 c/b major minor 如:mknod dr1 c 254 0,用戶通過此設備名來訪問你的驅動。 /dev,設備文件存儲目錄,應用程序通過對這些文件的讀寫和控制,可以訪問實際的設備 3./sys/devices ,內核設備按總線類型分層放置的目錄結構,按照設備掛接的總線類型,組織成層次結構,保存了系統所有的設備,
/sys/dev 下有兩個子目錄block和char,存放的是塊設備和字符設備的主次號碼,形式為(major:minor),
它指向 /sys/devices 目錄下的設備。
注:sysfs最主要是用來描繪Linux kernel 2.6中的設備驅動模型,
用戶態的后台程序(udev)會動態地周期性的掃描/sys目錄中的屬性項來自動管理設備文件(也稱為設備節點),
從而在/dev目錄會建立或者刪除對應的設備文件。
lsmod用於顯示已載入系統的模塊信息和模塊間的依賴關系(sd舉例)
[root@ht8 include]# lsmod | grep sd sd_mod 46281 3 crc_t10dif 12912 1 sd_mod
//lsmod命令實際上是讀取並分析/proc/modules文件 [root@ht8 include]# cat /proc/modules | grep sd sd_mod 46281 3 - Live 0xffffffffc0392000 crc_t10dif 12912 1 sd_mod, Live 0xffffffffc02ae000
Module: 模塊的名字 Size: 模塊的大小,單位是字節。
Live表示模塊可用 模塊的起始地址
內核已加載的模塊信息也存在與/sys/modules目錄下
[root@ht8 include]# cd /sys/module/sd_mod/ [root@ht8 sd_mod]# ll //該模塊參數 total 0 -r--r--r-- 1 root root 4096 Mar 26 19:11 coresize drwxr-xr-x 2 root root 0 Jan 5 19:39 holders -r--r--r-- 1 root root 4096 Mar 26 19:11 initsize -r--r--r-- 1 root root 4096 Mar 26 19:11 initstate drwxr-xr-x 2 root root 0 Mar 26 19:10 notes -r--r--r-- 1 root root 4096 Mar 26 19:11 refcnt -r--r--r-- 1 root root 4096 Mar 26 19:11 rhelversion drwxr-xr-x 2 root root 0 Mar 26 19:10 sections -r--r--r-- 1 root root 4096 Mar 26 19:11 srcversion -r--r--r-- 1 root root 4096 Mar 26 19:11 taint --w------- 1 root root 4096 Mar 26 19:11 uevent
二、linux 次設備號查看,我只留下一部分來說明問題
[root@fp-web-130 src]# ls -l /dev
total 0
.....
brw-rw---- 1 root disk 7, 0 Feb 7 14:58 loop0
brw-rw---- 1 root disk 7, 1 Feb 7 14:58 loop1
brw-rw---- 1 root disk 7, 2 Feb 9 11:21 loop2
....
brw-rw---- 1 root disk 8, 0 May 27 2021 sda
brw-rw---- 1 root disk 8, 1 May 27 2021 sda1
brw-rw---- 1 root disk 8, 2 May 27 2021 sda2
brw-rw---- 1 root disk 8, 16 Feb 5 01:02 sdb
brw-rw---- 1 root disk 8, 17 Feb 5 01:02 sdb1
brw-rw---- 1 root disk 8, 18 Feb 5 01:02 sdb2
brw-rw---- 1 root disk 8, 21 Feb 5 01:04 sdb5
crw-rw-rw- 1 root tty 5, 0 Apr 27 2022 tty
.....
我們看到用,號分割的兩個數字,比如 sda是 8,0 這個兩個數字就是特定設備的主設備號和次設備號
sda設備主設備號在/proc/devices定義為8,次設備號為0,所以主設備號和次設備號確定了唯一的設備
更詳細的你可以下載源碼: E:\linux內核\linux-3.12.37\linux-3.12.37\Documentation\devices.txt 來查看
那么具體主設備號和次設備號為什么這么設計了呢,簡單描述就是
1)主設備號是關聯驅動程序的,比如 scsi硬盤的驅動,那么就是8來管理,一個主備號關聯多個次設備號
2)次設備號僅供主設備號指定的驅動程序(底層硬件接口)使用,這里就是8不能提供給例如cpu等設備使用。
內核的其他部分不使用它,只是將它傳遞給主設備號對應的驅動程序。
另外:
brw標識塊設備,crw標識為字符設備。
我們下面用簡單表格更能闡述上面說的內容:
/proc/devices | /dev下的設備(以逗號分割的兩列) | 主設備號 | 次設備號 | 設備名稱 | 設備驅動 |
8 | 8,0 | 8 | sda | 8對應scsi內核磁盤驅動程序 | |
8,1 | 1 | sda1 | 確定唯一設備sda1 | ||
8,2 | 2 | sda2 | 確定唯一設備sda2 | ||
8,3 | 3 | sda3 | 確定唯一設備sda3 | ||
8,4 | 4 | sda4 | 確定唯一設備sda4 |
圖來來自這里
三、保留的主設備號
linux 2.0內核支持128個設備;2.2 和 2.4將該數字增加到 256(同時保留值0和255以供將來使用)
60 到 63、120 到 127 和 240 到 254 范圍內的主編號保留供本地和實驗使用:不會為實際設備分配這樣的主編號
四、主設備號定義:
[root@aozhejin /]locate major.h [root@aozhejin /]/usr/include/linux/major.h //定義在這里 [root@aozhejin /]/usr/src/kernels/3.10.0-1160.62.1.el7.x86_64/include/uapi/linux/major.h //內核開發環境 [root@aozhejin /]#cat /usr/include/linux/major.h | more #ifndef _LINUX_MAJOR_H #define _LINUX_MAJOR_H /* * 這個文件定義了主設備號對於主設備號的分配,可以參看內核的文檔 Documentation/devices.txt.
下載內核源碼的 Documenttation下即有這個文件.
*/ #define UNNAMED_MAJOR 0 #define MEM_MAJOR 1 #define RAMDISK_MAJOR 1 #define FLOPPY_MAJOR 2 #define PTY_MASTER_MAJOR 2 #define IDE0_MAJOR 3 #define HD_MAJOR IDE0_MAJOR #define PTY_SLAVE_MAJOR 3 #define TTY_MAJOR 4 #define TTYAUX_MAJOR 5 #define LP_MAJOR 6 #define VCS_MAJOR 7 #define LOOP_MAJOR 7 #define SCSI_DISK0_MAJOR 8 //scsi設備,去到/proc/devides里面看下是否對應上? #define SCSI_TAPE_MAJOR 9 #define MD_MAJOR 9 #define MISC_MAJOR 10 #define SCSI_CDROM_MAJOR 11 #define MUX_MAJOR 11 /* PA-RISC only */ #define XT_DISK_MAJOR 13 #define INPUT_MAJOR 13 #define SOUND_MAJOR 14 #define CDU31A_CDROM_MAJOR 15 #define JOYSTICK_MAJOR 15 #define GOLDSTAR_CDROM_MAJOR 16 ......
五、內核文檔(kernel-doc)中查詢各主次設備號代表的意義.
[root@ht8 sd_mod]# rpm -qa | grep kernel-doc kernel-doc-3.10.0-1160.59.1.el7.noarch
//沒有的話,yum install kernel-doc
[root@ht8 sd_mod]#cat /usr/share/doc/kernel-doc-3.10.0/Documentation/devices.txt
Linux的設備管理是和文件系統緊密結合的,各種設備都以文件的形式存放在/dev目錄下,稱為設備文件。應用程序可以打開、關閉和讀寫這些設備文件,完成對設備的操作,就像操作普通的數據文件一樣。為了管理這些設備,系統為設備編了號,每個設備號又分為主設備號和次設備號。主設備號用來區分不同種類的設備,而次設備號用來區分同一類型的多個設備。對於常用設備,Linux有約定俗成的編號,如硬盤的主設備號是3。 一個字符設備或者塊設備都有一個主設備號和次設備號。主設備號和次設備號統稱為設備號。主設備號用來表示一個特定的驅動程序。次設備號用來表示使用該驅動程序的各設備。
Linux為所有的設備文件都提供了統一的操作函數接口,方法是使用數據結構struct file_operations。這個數據結構中包括許多操作函數的指針,如open()、close()、read()和write()等,但由於外設 的種類較多,操作方式各不相同。Struct file_operations結構體中的成員為一系列的接口函數,如用於讀/寫的read/write函數和用於控制的ioctl等。打開一個文件就是 調用這個文件file_operations中的open操作。不同類型的文l件有不同的file_operations成員函數,如普通的磁盤數據文件, 接口函數完成磁盤數據塊讀寫操作;而對於各種設備文件,則最終調用各自驅動程序中的I/O函數進行具體設備的操作。這樣,應用程序根本不必考慮操作的是設 備還是普通文件,可一律當作文件處理,具有非常清晰統一的I/O接口。所以file_operations是文件層次的I/O接口。
內核中如何標識設備號
設備號是在驅動module中分配並注冊的,而/dev目錄下的設備文件是根據這個設備號創建的,因此,當訪問/dev目錄下的設備文件時,驅動module就知道,自己該出場服務了(當然是由內核通知)。在Linux內核看來,主設備號標識設備對應的驅動程序,告訴Linux內核使用哪一個驅動程序為該設備(也就是/dev下的設備文件)服務;而次設備號則用來標識具體且唯一的某個設備。在內核中,用dev_t類型(其實就是一個32位的無符號整數)的變量來保存設備的主次設備號,其中高12位表示主設備號,低20位表示次設備號。
設備獲得主次設備號有兩種方式:一種是手動給定一個32位數,並將它與設備聯系起來(即用某個函數注冊);另一種是調用系統函數給設備動態分配一個主次設備號。
對於手動給定一個主次設備號,使用以下函數:
int register_chrdev_region(dev_t first,
unsigned int -count,
char *name)
其中first是我們手動給定的設備號,count是所請求的連續設備號的個數,而name是和該設備號范圍關聯的設備名稱,它將出現在/proc/devices和sysfs中。
比如,若first為0x3FFFF0,count為0x5,那么該函數就會為5個設備注冊設備號,分別是0x3FFFF0、 0x3FFFF1、 0x3FFFF2、 0x3FFFF3、 0x3FFFF4,其中0x3(高12位)為這5個設備所共有的主設備號(也就是說這5個設備都使用同一個驅動程序)。而0xFFFF0、 0xFFFF1、 0xFFFF2、 0xFFFF3、 0xFFFF4就分別是這5個設備的次設備號了。需要注意的是,若count的值太大了,那么所請求的設備號范圍可能會和下一個主設備號重疊。比如若 first還是為0x3FFFF0,而count為0x11,那么first+count=0x400001,也就是說為最后兩個設備分配的主設備號已經不是0x3,而是0x4了!用這種方法注冊設備號有一個缺點,那就是若該驅動module被其他人廣泛使用,那么無法保證注冊的設備號是其他人的 Linux系統中未分配使用的設備號。
與主次設備號相關的3個宏:
MAJOR(dev_t dev):根據設備號dev獲得主設備號;
MINOR(dev_t dev):根據設備號dev獲得次設備號;
MKDEV(int major, int minor):根據主設備號major和次設備號minor構建設備號
從Linux2.6內核版本開始,/proc文件系統遷移至改進后的/sys文件系統。/sys文件系統中加入對動態變更的支持,如添加和刪除LUN,而無需重新加載主機適配器驅動或重啟主機。
六、一些主要設備號被靜態分配給最常見的設備。 Documentation/devices.txt (參見內核源碼)
這些設備的列表可以在內核源代碼樹中找到 。由於已經分配了許多編號,因此為新驅動程序選擇一個唯一編號可能很困難
七、開發底層驅動程序
開發內核驅動程序的時候,你最好使用動態分配的方式獲取您的主設備號,而不是從當前可用的主設備號中隨機選擇一個。如果您的驅動程序希望被官方內核自動分配,則你需要申請分配一個主設備編號以供獨占使用。當然如果你是為了測試你完全可以使用保留的主設備號,一旦你在開發時被分配到了主設備號,則在/proc/devices文件中會看到,這樣你就可以創建唯一設備在/dev目錄下。
你需要關注:
E:\linux內核\linux-3.12.37\linux-3.12.37\Documentation\devices.txt
系統里面的 /usr/include/linux/major.h
當前系統的/proc/devices /dev
sysfs(/sys目錄)-sysfs 的設計原則是一個屬性文件只做一件事情,sysfs屬性文件一般只有一個值,直接讀取或寫入
sysfs 虛擬文件系統提供了一種比 proc(老的機制) 更為理想的訪問內核數據的途徑。sysfs 文件系統總是被掛載在 /sys 掛載點上。
sysfs 與 proc 相比有很多優點,最重要的莫過於設計上的清晰。一個 proc 虛擬文件可能有內部格式,如 /proc/scsi/scsi ,它是可讀可寫的,並且讀寫格式不一樣,代表不同的操作,應用程序中讀到了這個文件的內容一般還需要進行字符串解析,而在寫入時需要先用字符串格式化按指定的格式寫入字符串進行操作。
相比而言.
SYS 系統目錄結構
/sys | /sys/下的目錄結構是經過精心設計的: 在 /sys/devices 下是所有設備的真實對象,包括如視頻卡和以太網卡等真實的設備,也包括 ACPI 等不那么顯而易見的真實設備、還有 tty, bonding 等純粹虛擬的設備;在其它目錄如 class, bus 等中則在分類的目錄中含有大量對 devices 中真實對象引用的符號鏈接文件。 |
---|---|
/sys/devices | 這是內核對系統中所有設備的分層次表達模型,也是 /sys 文件系統管理設備的最重要的目錄結構 |
/sys/dev | 這個目錄下維護一個按字符設備和塊設備的主次號碼(major:minor)鏈接到真實的設備(/sys/devices下)的符號鏈接文件,它是在內核 2.6.26 首次引入; |
/sys/bus | 這是內核設備按總線類型分層放置的目錄結構,devices 中的所有設備都是連接於某種總線之下, 在這里的每一種具體總線之下可以找到每一個具體設備的符號鏈接,它也是構成 Linux 統一設備模型的一部分; |
/sys/class | 這是按照設備功能分類的設備模型,如系統所有輸入設備都會出現在 /sys/class/input 之下,而不論它們是以何種總線連接到系統。 它也是構成 Linux 統一設備模型的一部分; |
/sys/block | 這里是系統中當前所有的塊設備所在,按照功能來說放置在 /sys/class 之下會更合適, 在 2.6.26 內核中已正式移到 /sys/class/block |
/sys/firmware | 這里是系統加載固件機制的對用戶空間的接口,關於固件有專用於固件加載的一套API |
/sys/fs | 這里按照設計是用於描述系統中所有文件系統,包括文件系統本身和按文件系統分類存放的已掛載點, 但目前只有 fuse,gfs2 等少數文件系統支持 sysfs 接口,一些傳統的虛擬文件系統(VFS)層次控制參數仍然在 sysctl (/proc/sys/fs) 接口中中; |
/sys/kernel | 這里是內核所有可調整參數的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的 slab 分配器等幾項較新的設計在使用它, 其它內核可調整參數仍然位於 sysctl (/proc/sys/kernel) 接口中 |
/sys/module | 這里有系統中所有模塊的信息,不論這些模塊是以內聯(inlined)方式編譯到內核映像文件(vmlinuz)中還是編譯為外部模塊(ko文件),都可能會出現在 /sys/module 中 |
/sys/power | 這里是系統中電源選項,這個目錄下有幾個屬性文件可以用於控制整個機器的電源狀態,如可以向其中寫入控制命令讓機器關機、重啟等。 |
/sys/slab | (內核2.6.24 以后移至 /sys/kernel/slab)SLAB 內存分配器的實現 |
參考資料:
https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch03s02.html
https://distroid.net/mknod-command-linux/
http://www.cs.columbia.edu/~sedwards/classes/2023/4840-spring/device-drivers.pdf
https://static.lwn.net/images/pdf/LDD3/ch16.pdf