裝置檔案節點的生成
在linux系統下,裝置檔案是種特殊的檔案型別,其存在的主要意義是溝通使用者空間程式和核心空間驅動程式。換句話說,使用者空間的應用程式要想使用驅動程式提供的服務,需要經過裝置檔案來達成。當然,如果你的驅動程式只是為核心中的其他模組提供服務,則沒有必要生成對應的裝置檔案。
按照通用的規則,linux系統所有的裝置檔案都位於/dev目錄下。/dev目錄在linux系統中算是乙個比較特殊的目錄,在linux系統早期還不支援動態生成裝置節點時,/dev目錄就是掛載的根檔案系統下的/dev,對這個目錄下所有檔案的操作使用的是根檔案系統提供的介面。比如,如果linux系統掛載的根檔案系統是ext3,那麼對/dev目錄下所有目錄/檔案的操作都將使用ext3檔案系統的介面。隨著後來linux核心的演進,開始支援動態裝置節點的生成[1],使得系統在啟動過程中會自動生成各個裝置節點,這就使得/dev目錄不必要作為乙個非易失的檔案系統的形式存在。因此,當前的linux核心在掛載完根檔案系統之後,會在這個根檔案系統的/dev目錄上重新掛載乙個新的檔案系統devtmpfs,後者是個基於系統ram的檔案系統實現。當然,對動態裝置節點生成的支援並不意味著一定要將根檔案系統中的/dev目錄重新掛載到乙個新的檔案系統上,事實上動態生成裝置節點技術的重點並不在檔案系統上面。
動態裝置節點的特性需要其他相關技術的支援,在後續的章節中會詳細描述這些特性。目前先假定裝置節點是通過linux系統下的mknod命令靜態建立。為方便敘述,下面用乙個具體的例子來描述裝置檔案產生過程中的一些關鍵要素,這個例子的任務很簡單:在乙個ext3型別的根檔案系統中的/dev目錄下用mknod命令來建立乙個新的裝置檔案節點demodev,對應的驅動程式使用的裝置主裝置號為2,次裝置號是0,命令形式為:
root@linuxdev:/home/dennis#mknod /dev/demodev c 2 0
root@linuxdev:/home/dennis#strace mknod /dev/demodev c 2 0
execve("/bin/mknod",["mknod", "/dev/demodev", "c","30","0"], [/* 36 vars */]) = 0
mknod("/dev/demodev",s_ifchr|0666, makedev(30,0)) = 0
可見linux下的mknod命令最終是通過呼叫mknod函式來實現的,呼叫時的重要引數有兩個,一是裝置檔名("/dev/demodev"),二是裝置號(makedev(30,0))。裝置檔名主要在使用者空間使用(比如使用者空間程式呼叫open函式時),而核心空間則使用inode來表示相應的檔案。本書只關注核心空間的操作,對於前面的mknod命令,它將通過系統呼叫sys_mknod進入核心空間,這個系統呼叫的原型是:
long sys_mknod(const char __user *filename, int mode, unsigned dev);
注意sys_mknod的最後乙個引數dev,它是由使用者空間的mknod命令構造出的裝置號。sys_mknod系統呼叫將通過/dev目錄上掛載的檔案系統介面來為/dev/demodev生成乙個新的inode[2],裝置號將被記錄到這個新的inode物件上。
圖2-7展示了通過ext3檔案系統在/dev目錄下生成乙個新的裝置節點/dev/demodev的主要流程。
圖2-7 ext3檔案系統mknod的主要流程
完整了解裝置節點產生的整個過程需要知曉vfs和特定檔案系統的技術細節。然而從驅動程式員的角度來說,沒有必要知道檔案系統相關的所有細節,只需關注檔案系統和驅動程式間是如何建立上關聯的就足夠了。
sys_mknod首先在根檔案系統ext3的根目錄「/」下尋找dev目錄所對應的inode,圖中對應的inode編號為168,ext3檔案系統的實現會通過某種對映機制,通過inode編號最終得到該inode在記憶體中的實際位址(圖中由標號1的線段表示)。接下來會通過dev的inode結構中的i_op成員指標所指向的ext3_dir_inode_operations(這是個struct inode_operations型別的指標),來呼叫該物件中的mknod方法,這將導致ext3_mknod函式被呼叫。
ext3_mknod函式的主要作用是生成乙個新的inode(用來在核心空間表示demodev裝置檔案節點,demodev裝置節點檔案與新生成的inode之間的關聯在圖2-7中由標號5的線段表示)。在ext3_mknod中會呼叫乙個和裝置驅動程式關係密切的init_special_inode函式,其定義如下:
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
inode->i_mode = mode;
if (s_ischr(mode)) else if (s_isblk(mode)) else if (s_isfifo(mode))
inode->i_fop =&def_fifo_fops;
else if (s_issock(mode))
inode->i_fop =&bad_sock_fops;
else
printk(kern_debug"init_special_inode: bogus i_mode (%o) for"
" inode %s:%lu\n", mode,inode->i_sb->s_id,
inode->i_ino);
這個函式最主要的功能便是為新生成的inode初始化其中的i_fop和i_rdev成員。裝置檔案節點inode中的i_rdev成員用來表示該inode所對應裝置的裝置號,通過引數rdev為其賦值。裝置號在由sys_mknod發起的整個核心呼叫鏈中進行傳遞,最早來自於使用者空間的mknod命令列引數。
i_fop成員的初始化根據是字元裝置還是塊裝置而有不同的賦值。對於字元裝置,fop指向def_chr_fops,後者主要定義了乙個open操作:
const struct file_operations def_chr_fops = {
.open = chrdev_open,
相對於字元裝置,塊裝置的def_blk_fops的定義則要有點複雜:
const struct file_operations def_blk_fops = {
.open = blkdev_open,
.release = blkdev_close,
.llseek = block_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = blkdev_aio_write,
.mmap = generic_file_mmap,
.fsync = blkdev_fsync,
.unlocked_ioctl = block_ioctl,
#ifdef config_compat
.compat_ioctl = compat_blkdev_ioctl,
#endif
.splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write,
關於塊裝置,將在本書第11章「塊裝置驅動程式」中詳細討論,這裡依然把考察的重點放在字元裝置上。字元裝置inode中的i_fop指向def_chr_fops。至此,裝置節點的所有相關鋪墊工作都已經結束,接下來可以看看開啟乙個裝置檔案到底意味著什麼。
——本段文字節選自《深入linux裝置驅動程式核心機制》
圖書詳細資訊:
[1] 這裡動態生成裝置節點的說法是相對於使用mknod命令生成裝置節點而言的,前者直接通過檔案系統介面來生成對應的裝置節點。
[2] 對於實際的檔案系統,比如ext3檔案系統,產生乙個node的過程因為同時要涉及底層儲存裝置的操作,因而會變得很複雜。
生成裝置節點
雜項裝置的主裝置號是10,在任何linux 系統中它都是固定的。這樣雜項裝置的引入即解決了裝置號數量少的問題,又降低了使用難度,還能防止碎片化,一舉多得。雜項裝置的標頭檔案在 include linux miscdevice.h 雜項裝置註冊函式 一般在probe 中呼叫,引數是miscdevice...
android如何手動生成裝置節點
init.rc裡所有可用的command都定義在system core init keyword.h裡,預設是不包含mknod的。事實上,android的init程序會通過kenel的uevent來自動建立裝置節點 見system core init devices.c裡的make device 函...
裝置節點檔案建立函式
裝置節點檔案建立函式 使用雜項字元裝置註冊裝置驅動時,可以自動建立裝置節點檔案。但是使用早期字元裝置和標準字元裝置註冊裝置節點檔案時並不能自動建立裝置節點檔案。猜測 雜項字元裝置註冊函式 中,包含有裝置節點檔案註冊的函式!雜項字元裝置註冊函式分析 misc register misc registe...