一、知識準備
1、在linux中,一切皆為檔案,所有不同種類的型別都被抽象成檔案(比如:塊裝置,socket套接字,pipe佇列)
2、操作這些不同的型別就像操作檔案一樣,比如增刪改查等
3、塊裝置是將資訊儲存在大小固定的塊中,每一個塊都有自己的地址,塊裝置支援隨機訪問。典型的塊裝置比如我們使用的硬碟
二、環境準備
元件 | 版本 |
---|---|
OS | Ubuntu 16.04.4 LTS |
三、主裝置號(major)與次裝置號(minor)
● 當一塊磁碟被註冊到作業系統的時候,會被分配主裝置號與次裝置號
● 其中主裝置號代表了該裝置屬於的型別,次裝置號代表了該裝置在作業系統中的唯一標識
主裝置號
root@Bastion:~# ls -l /dev/sd*
brw-rw---- 1 root disk 8, 0 9月 30 17:47 /dev/sda
brw-rw---- 1 root disk 8, 1 9月 30 17:47 /dev/sda1
sda的主裝置號是8,代表了sda這塊磁碟是屬於8這個型別的,那8是什麼型別的?
root@Bastion:~# grep 8 /proc/devices
108 ppp
128 ptm
248 pps
8 sd
在/proc/devices告訴我們,8是屬於sd型別的,那sd又是啥意思?
開啟 https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
8 block SCSI disk devices (0-15)
0 = /dev/sda First SCSI disk whole disk
16 = /dev/sdb Second SCSI disk whole disk
32 = /dev/sdc Third SCSI disk whole disk
...
240 = /dev/sdp Sixteenth SCSI disk whole disk
Partitions are handled in the same way as for IDE
disks (see major number 3) except that the limit on
partitions is 15.
終於清楚的看到,8這個型別代表了塊裝置,並且是SCSI 硬碟
次裝置號
由於/dev/sda做了1個分割槽/dev/sda1,再加上原有的/dev/sda,作業系統核心給二者打上了唯一的標記:
8,0
代表了/dev/sda
8,1
代表了/dev/sda1
小結一下:
主裝置號:代表著某一型別的裝置,比如SCSI硬碟、虛擬硬碟、USB等等
次裝置號:作業系統分配的整數,與主裝置號一起(major,minor),組成了該裝置在作業系統當中唯一的ID
四、塊裝置檔案
● 塊裝置檔案是連線使用者空間和核心空間的橋樑,通過塊裝置檔案描述符,能夠找到核心中的裝置驅動程式
● 通過核心中的驅動程式從而對該裝置進行讀寫
+----------------------+
| user space |
| |
| +---------+ |
| | test.py | |
| +---------+ |
+----------------------+
|
|
+----v----+
|/dev/sda1|
+----+----+
|
|(8,1)
|
+---------------------------------------------
| kernel space | |
| | |
| v |
| +-----+----+ +---------------+ |
| | major:8 | | device driver | |
| +-----+----+ | | |
| | +---------------+ | +--------+
| +---------->| minor:1 |-------->| device |
| +---------------+ | +--------+
| | | |
| +---------------+ |
+--------------------------------------------+
五、測試
(a)首先虛擬一個塊裝置檔案
root@Bastion:~# dd if=/dev/zero of=/tmp/device_test bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.0890014 s, 1.2 GB/s
root@Bastion:~# mknod /dev/device_test b 7 80
root@Bastion:~# losetup /dev/device_test /tmp/device_test
我們已經虛擬出一個塊裝置檔案了,由於是通過losetup虛擬出來的,所以major號只能為7
下面將該塊裝置格式化、掛載:
root@Bastion:~# mkfs.ext4 /dev/device_test
mke2fs 1.42.13 (17-May-2015)
Discarding device blocks: done
Creating filesystem with 102400 1k blocks and 25688 inodes
Filesystem UUID: f38c24be-851b-41ff-8d55-4e692d5a4c83
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
root@Bastion:~# mount /dev/device_test /mnt
root@Bastion:~# df -h | grep /mnt
/dev/device_test 93M 1.6M 85M 2% /mnt
root@Bastion:/mnt# ls -l /dev/device_test
brw-r--r-- 1 root root 7, 80 Nov 12 09:54 /dev/device_test
至此,我們擁有了一個塊裝置,並且大小為100M
(b)測試指令碼
準備一個python檔案,每秒往test.log寫入hello world
root@Bastion:/mnt# more device_test.py
import time
f = open(`test.log`,`a+`)
while 1:
f.write(`hello world
`)
time.sleep(1)
執行並檢視其開啟的檔案描述符
root@Bastion:/mnt# python device_test.py &
[1] 25873
root@Bastion:/mnt# lsof -n | grep 25873
COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME
...
python 25873 root 3u REG 7,80 8923 14 /mnt/test.log
整理一下我們的資訊:
● 首先執行指令碼,它的程式號為25873。指令碼的邏輯是需要開啟test.log,然後進行讀寫
● 由於指令碼中是相對路徑,並且當前目錄在/mnt下,/mnt
相關聯的硬碟是/dev/device_test
● 程式通過/dev/device_test
拿到了該裝置的裝置號(7,80)
● 通過裝置號在記憶體中找到對應的裝置驅動程式,然後通過裝置驅動程式對塊裝置進行讀寫
● 在塊裝置上沒有發現test.log,首先建立一個,然後開始對該檔案每秒寫入一句`hello world`
六、小結
● 當塊裝置掛載的時候,會先在作業系統的/dev下建立一個塊裝置檔案,並且分配主裝置號與次裝置號
● 塊裝置檔案是連線使用者空間和核心空間的橋樑,應用程式通過它能夠找到在核心中的裝置驅動,從而實現對裝置的讀寫
至此,本文結束
在下才疏學淺,有撒湯漏水的,請各位不吝賜教…