在/dev下面有很多裝置,其中也有大家廣為使用的字元裝置,呢裡面的檔案是如何與字元裝置掛鉤的呢~ 平時我們使用的open函式是如何動態載入字元裝置的操作集的呢~
下面就讓我們慢慢剖析~ (以核心2.6.26為參考)
一.首先是檔案系統~ 需要動態解析檔案路徑名 像/dev/ts0 在檔案系統裡分為3個部分 1./(根檔案目錄) 2.dev(根檔案目錄下的dev目錄) 3.ts0(dev目錄下的ts0檔案)
我們使用open(「/dev/ts0」,」開啟方式」)之後~ 就進入了系統呼叫sys_open之中~,如下圖左a所示~ 呼叫一直深入到do_file_open中後開始真正的路徑解析
現在只簡單的講述一下~ path_look_open這個函式用於路徑解析與nameidata結構的賦值 nameidata_to_filp這個函式用於nameidata轉換為file結構
在這些步驟裡~ 我只點出幾步比較重要的賦值~
a.首先是path_lookup_open函式
1.在real_lookup中(/fs/namei.c):
520 result = dir->i_op->lookup(dir,dentry,nd); //這一步是根據檔案所在的檔案系統呼叫相應的lookup函式~ 我使用的檔案系統為ext3~ 則呼叫ext3_lookup
2.在ext3_iget中(/fs/ext3/inode.c):
檢測檔案標識inode->i_mode~ 不為普通檔案,目錄及鏈結檔案則判定為字元檔案,接著呼叫init_special_inode函式
3.在init_special_inode中(/fs/inode.c):
繼續判斷檔案識別符號,我們關心的是判斷為字元檔案這一段
1427 inode->i_fop = &def_chr_fops;
1428 inode->i_rdev = rdev;
這裡賦了操作集與裝置號,為後面的裝置查詢判斷埋下伏筆
b.現在path_lookup_open函式呼叫完成~ 進入到nameidata_to_filp函式中
1. 在__dentry_open中(/fs/open.c):
825 f->f_op = fops_get(inode->i_fop); //在這裡進行賦值,f->f_op = &def_chr_fops,注意上文inode->i_fop中的賦值
832 if(!open && f->f_op) //在呼叫__dentry_open時open賦值為空,所以!open為真
833 open = f->f_op->open; //在這裡將open賦為chrdev_open
835 error = open(inode,f); //這裡呼叫chrdev_open
2. 在chrdev_open中(/fs/char_dev.v):
371 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); //執行kobj_lookup函式,在cdev_map裡尋找相應的inode->i_rdev裝置
cdev_map是乙個256個probe結構組成的陣列,用於查詢具有相應裝置號的裝置,如何查詢這個結構,將在後面的文章中詳細介紹
inode->i_rdev為裝置號 在前面已經提示了
374 new = container_of(kobj, struct cdev, kobj); //從kobj的位置倒算出cdev的記憶體位址.獲得包含相應kobj的cdev
378 inode->i_cdev = p = new; //到這裡p已經為我們要的裝置cdev了
390 filp->f_op = fops_get(p->ops); //終於拿到了夢寐以求的cdev操作集,至此~ 以後read,write操作都通過file->f_op直接與我們要的裝置操作集掛鉤了
二現在詳細講述cdev_map這個重要的結構
cdev_map是管理linux中字元裝置的陣列,它的描述為static struct kobj_map *cdev_map;
而kobj_map的相關**在/drivers/base/map.c裡
struct kobj_map *probes[255];
struct mutex *lock;
};這就是kobj_map的結構
下面來看一下操作函式,首先是初始化函式kobj_map_init,這個函式在chrdev_init中呼叫(/fs/char_dev.c)
cdev_map = kobj_map_init(base_probe, &chrdevs_lock); //初始化cdev_map
struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
//檢測分配是否成功
base->dev = 1;
base->range = ~0; //~0為4294967295
base->get = base_probe; //以上三項初始化probe
for (i = 0; i < 255; i++)
p->probes = base; //將kobj_map中的256項probe均指向base
p->lock = lock;
return p;
}初始完後的陣列如下圖
然後是分配函式kobj_map,該函式負責插入乙個包含私有資料的probe到陣列裡
在新增字元裝置的時候cdev_add負責呼叫此函式
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
其中比較重要的是exact_match這個函式,他用於提取私有資料中的kobj
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,
int (*lock)(dev_t, void *), void *data)
//初始化取得的probe結構
mutex_lock(domain->lock);
for (i = 0, p -= n; i < n; i++, p++, index++)
mutex_unlock(domain->lock);
return 0;
}插入乙個probe後的陣列如下圖
關於kobj_map的題外話,在插入中我們可以看到,**並不對裝置號進行檢測,也就說有可能有兩個完全一樣的裝置號裝置,如下圖
linux並沒有這樣的檢測機制~ 只能全憑人工分配裝置號的時候進行仔細的檢查,也可能是我沒發現其中的檢測機制,望大家指出,萬分感謝
現在到了大家最關心的,如何使用裝置號查詢對應的裝置呢?
這個函式是kobj_lookup,它負責進行查詢
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
mutex_unlock(domain->lock);
kobj = probe(dev, index, data); //提取私有資料中的kobj
/* currently ->owner protects _only_ ->probe() itself. */
module_put(owner);
if (kobj)
return kobj; //返回kobj
goto retry;
}mutex_unlock(domain->lock);
return null;
}
作業系統 再識 Linux 驅動模型
1 底層機制 linux 的裝置驅動模型的底層機制主要包括 kobject,kobj type,kset 等幾個結構。這幾個 結構的定義在include linux kobject.h 中。1.1 kobject 代表裝置驅動模型中乙個基本物件,類似於mfc 中最頂層的基類cobject。每個kob...
作業系統 再識 linux 等待佇列
linux 核心的等待佇列是以雙迴圈鍊錶為基礎資料結構,與程序排程機制緊密結合,能夠用於實現核心的非同步事件通知機制。在這個鍊錶中,有兩種資料結構 等待佇列頭 wait queue head t 和等待佇列項 wait queue t 等待佇列頭和等待佇列項中都包含乙個list head型別的域作為...
從裸機到作業系統
開始學習微控制器那是大二時候的事了,當時最為困惑的是為什麼主函式中要是乙個while 1 在windows下寫c語言程式不就是主函式裡面直接寫,到最後return 0不就完了麼。到後來隨著學習的深入,漸漸明白,微控制器下的程式可以認為是乙個單執行緒的程式,只能在這個超級迴圈中往復的執行,最多加一些中...