寫一個塊設備驅動 4

第 4章

+---------------------------------------------------+
|                 寫一個塊設備驅動                   |
+---------------------------------------------------+
| 作者:趙磊                                         |
| email: [email protected]                      |
+---------------------------------------------------+
| 文章版權歸原作者所有。                             |
| 大家可以自由轉載這篇文章,但原版權信息必須保留。   |
| 如需用於商業用途,請務必與原作者聯繫,若因未取得   |
| 授權而收起的版權爭議,由侵權者自行負責。           |
+---------------------------------------------------+

上一章結束時說過,本章會準備一些不需要動腦子的內容,現在我們開始履行諾言

看上去簡單的事情實際上往往會被弄得很複雜,比如取消公僕們的招待費用問題;

看上去複雜的事情真正做起來也可能很簡單,比如本章中要讓我們的塊設備支持分區操作

談到分區,不懂電腦的人想到了去找“專家”幫忙;電腦入門者想到了“高手”這個名詞;

漸入佳境者想到了 fdisk ;資深級玩家想到了 dm ;紅點玩家想到了隱藏的系統恢復區;
程序員想到了分區表;病毒製造者想到了把分區表清空......

作爲塊設備驅動程序的設計者,我們似乎需要想的比他們更多一些,

我們大概需要在驅動程序開始識別塊設備時訪問設備上的分區表,讀出裏面的數據進行分析,

找出這個塊設備中包含哪一類的分區(奇怪吧,但真相是分區表確實有很多種,只是我們經常遇到的大概
只有 ibm類型罷了)、

幾個分區,每個分區在塊設備上的區域等信息,再在驅動程序中對每個分區進行註冊、創建其管理信

息 ......

讀到這裏,正在繫鞋帶準備溜之大吉的同學們請稍等片刻聽我說完,

雖然實際上作者也鼓勵同學們多作嘗試,甚至是這種無謂的嘗試,但本章中的做法卻比上述的內容簡單

得多

因爲這一回 linux居然幫了我們的忙,並且不是I/O調度器的那種倒忙

打開 linux代碼,我們會在 fs/partitions/目錄中發現一些文件,這些友好的文件將會默默無聞地幫

我們的大忙

而我們需要做的居然如此簡單,還記得 alloc_disk()函數嗎?
我們一直用 1作參數來調用它的,但現在,我們換成 64 ,這意味着設定塊設備最大支持 63個分區
然後 ......不要問然後,因爲已經做完了

當然,如果要讓代碼看起來漂亮一些的話,我們可以考慮用一個宏來定義最大分區數

----------------------- Page 26-----------------------

也就是,在文件的頭部增加:

/* usable partitions is SIMP_BLKDEV_MAXPARTITIONS - 1 */
#define SIMP_BLKDEV_MAXPARTITIONS      (64)

然後把

simp_blkdev_disk = alloc_disk(1);

改成

simp_blkdev_disk = alloc_disk(SIMP_BLKDEV_MAXPARTITIONS);

好了,真的改好了

上一章那樣改得太多看起來會讓讀者不爽,那麼這裏改得太少,是不是也同樣不爽?

大概有關部門深信老百姓接受不了有害物質含量過少的食品,因此制定了食品中三聚氰胺含量的標準

於是,今後我們大概會制定出一系列標準,比如插入多深才能叫強姦什麼的

爲了達到所謂的標準,我們破例補充介紹一下 alloc_disk()函數:

這個函數的原型爲:

struct gendisk *alloc_disk(int minors);
用於申請一個 gendisk結構,並做好一些初始化工作
minors用於指定這個設備使用的次設備號數量,因爲第一個次設備號已經用於表示整個塊設備了,
因此餘下的 minors-1個設備號用於表示塊設備中的分區,這就限制了這個塊設備中的最大可訪問分區

我們注意“最大可訪問分區數”這個詞:

“最大”雖然指的是上限,但並不意味這是唯一的上限

極端情況下如果這個塊設備只有 2個磁道,那麼無論minors多大,塊設備本身充其量也只能建立 2個分

這時再談minors值能到達多少簡直就是扯淡,就像腐敗不根除,建多少經濟適用房都是白搭一樣

“可訪問”指的是通過驅動程序可以訪問的分區數量,這是因爲我們只有那麼多次設備號

但這個數字並不妨礙用戶在塊設備上面建多少個區。比如我們把minors設定爲 4 ,那麼最大可訪問的分
區數量是 3 ,
足夠變態的用戶完全可以在塊設備上建立幾十個分區,只不過結果是隻能使用前3個分區而已

現在我們可以試試這個程序了

與以往相同的是,我們編譯和加載這個模塊:

# make
make -C /lib/modules/2.6.18-53.el5/build 
SUBDIRS=/root/test/simp_blkdev/simp_blkdev_step04 modules
make[1]: Entering directory `/usr/src/kernels/2.6.18-53.el5-i686'
  CC [M]  /root/test/simp_blkdev/simp_blkdev_step04/simp_blkdev.o
  Building modules, stage 2.
  MODPOST
  CC      /root/test/simp_blkdev/simp_blkdev_step04/simp_blkdev.mod.o
  LD [M]  /root/test/simp_blkdev/simp_blkdev_step04/simp_blkdev.ko

----------------------- Page 27-----------------------

make[1]: Leaving directory `/usr/src/kernels/2.6.18-53.el5-i686'
# insmod simp_blkdev.ko
#

與以往不同的是,這一次加載完模塊後,我們並不直接在塊設備上創建文件系統,而是進行分區:

# fdisk /dev/simp_blkdev
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF 
disklabel
Building a new DOS disklabel. Changes will remain in memory only,
until you decide to write them. After that, of course, the previous
content won't be recoverable.

Warning: invalid flag 0x       of partition table 4 will be corrected by w(rite)

Command (m for help):

關於 fdisk我們不打算在這裏介紹,因爲我們試圖讓這篇文檔看起來專家一些
使用 n命令創建第一個主分區:
Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-2, default 1): 1
Last cylinder or +size or +sizeM or +sizeK (1-2, default 2): 1

Command (m for help):
如果細心一些的話,在這裏可以看出一個小麻煩,就是:這塊磁盤一共只有 2個磁道
因此,我們只好指定第一個分區僅佔用 1個磁道 畢竟,還要爲第2個分區留一些空間

然後建立第二個分區:

Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 2
First cylinder (2-2, default 2): 2

Command (m for help):
這一步中由於只剩下 1個磁道,fdisk便不再問我們 Last cylinder ,而是自作主張地把最後一個磁道

分配給新的分區

這時我們的分區情況是:

Command (m for help): p

----------------------- Page 28-----------------------

Disk /dev/simp_blkdev: 16 MB, 16777216 bytes
255 heads, 63 sectors/track, 2 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

           Device Boot      Start         End      Blocks   Id  System
/dev/simp_blkdev1               1           1        8  1   83  Linux
/dev/simp_blkdev2               2           2        8032+  83  Linux

Command (m for help):
寫入分區,退出 fdisk :
Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
#

然後我們在這兩個分區中創建文件系統

# mkfs.ext3 /dev/simp_blkdev1
mke2fs 1.39 (29-May-2  6)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
2     inodes, 8     blocks
4   blocks (5.  %) reserved for the super user
First data block=1
Maximum filesystem blocks=8388608
1 block group
8192 blocks per group, 8192 fragments per group
2     inodes per group

Writing inode tables: done
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 27 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
# mkfs.ext3 /dev/simp_blkdev2
mke2fs 1.39 (29-May-2  6)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)

----------------------- Page 29-----------------------

Fragment size=1024 (log=0)
2  8 inodes, 8032 blocks
401 blocks (4.99%) reserved for the super user
First data block=1
Maximum filesystem blocks=8388608
1 block group
8192 blocks per group, 8192 fragments per group
2  8 inodes per group

Writing inode tables: done
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 23 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
#
然後 mount設兩個設備:
# mount /dev/simp_blkdev1 /mnt/temp1
# mount /dev/simp_blkdev2 /mnt/temp2
#

看看結果:

# mount
/dev/hda1 on / type ext3 (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
tmpfs on /dev/shm type tmpfs (rw)
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
/dev/simp_blkdev1 on /mnt/temp1 type ext3 (rw)
/dev/simp_blkdev2 on /mnt/temp2 type ext3 (rw)
#
然後讀/寫:
# cp /etc/init.d/* /mnt/temp1/
# cp /etc/passwd /mnt/temp2
# ls /mnt/temp1/
NetworkManager            avahi-dnsconfd      dund       ipmi        lost+found 
netfs     portmap          rpcsvcgssd      vncserver
NetworkManagerDispatcher  bluetooth           firstboot  iptables    lvm2-
monitor   netplugd  psacct           saslauthd       winbind
acpid                     capi                functions  irda        mcstrans 
network   rdisc            sendmail        wpa_supplicant
anacron                   conman              gpm        irqbalance  mdmonitor 
nfs       readahead_early  setroubleshoot  xfs
apmd                      cpuspeed            haldaemon  isdn        mdmpd 

----------------------- Page 30-----------------------

nfslock   readahead_later  single          ypbind
atd                       crond               halt       kdump       messagebus 
nscd      restorecond      smartd          yum-updatesd
auditd                    cups                hidd       killall 
microcode_ctl  ntpd      rhnsd            sshd
autofs                    cups-config-daemon  hplip      krb524      multipathd 
pand      rpcgssd          syslog
avahi-daemon              dhcdbd              ip6tables  kudzu       netconsole 
pcscd     rpcidmapd        vmware-tools
# ls /mnt/temp2
lost+found  passwd
#

收尾工作:

# umount /dev/temp1
# umount /dev/temp2
# rmmod simp_blkdev
#

看起來本章應該結束了,但爲了耽誤大家更多的時間,我們來回憶一下剛纔出現的小麻煩

我們發現這塊磁盤只有 2個磁道,由於分區是以磁道爲邊界的,因此最大隻能創建 2個分區
不過謝天謝地,好歹我們能夠證明我們的程序是支持“多個”分區的 ......儘管只有 2個

那麼爲什麼系統會認爲我們的塊設備只有 2個磁道呢?其實這不怪系統,因爲我們根本沒有告訴系統我

們的磁盤究竟有多少個磁道

因此係統只好去猜、猜、猜,結果就猜成 2個磁道了
好吧,說的細節一些,傳統的磁盤使用 8個位表示盤面數、6個位表示每磁道扇區數、1 個位表示磁道
數,因此盤面、每磁道扇區、磁道的最大數值分別爲 255、63和 1023
這也是傳說中啓動操作系統時的 1024柱面 (磁道)和硬盤容量8G限制的根源

現代磁盤採用線性尋址方式突破了這一限制,從本質上說,如果你的機器還沒生鏽,那麼你的硬盤無論

是內部結構還是訪問方式都與常識中的盤面、每磁道扇區、磁道無關

但爲了與原先的理解兼容,對於現代磁盤,我們在訪問時還是假設它具有傳統的結構。目前比較通用的

假設是:所有磁盤具有最大數目的(也就是恆定的)盤面和每磁道扇區數,而磁盤大小與磁道數與成正比

因此,對於一塊 80G的硬盤,根據假設,這塊磁盤的盤面和每磁道扇區數肯定是 255和 63 ,磁道數爲:
80*1024*1024*1024/512(字節每扇區)/255(盤面數)/63(每磁道扇區數)=1  43(小數部分看作不完
整的磁道被丟棄)
話歸原題,在驅動程序中我們指定了磁盤大小爲 16M ,共包含16*1024*1024/512=32768個扇區。假
設這塊磁盤具有最大盤面和每磁道扇區數後,它的磁道數就是:32768/255/63=2

我們看起開應該很happy ,因爲系統太看得起我們了,竟然把我們的塊設備看成現代磁盤進行磁道數的

換算處理

不過我們也可能 unhappy ,因爲這造成磁盤最大隻能被分成 2個區 (至於爲什麼分區以磁道作爲邊界,

----------------------- Page 31-----------------------

可以想象一下磁盤的結構)
但我們的磁盤只有區區16M啊,所以最好還是告訴系統我們的磁盤沒有那麼多的盤面數和每磁道扇區數,

這將讓磁道數來得多一些

在下一章中,我們打算搞定這個問題

<未完,待續>


發佈了0 篇原創文章 · 獲贊 2 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章