作業系統 再識 Linux從檔案到字元裝置

2021-06-14 14:09:29 字數 4002 閱讀 8786

在/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不就完了麼。到後來隨著學習的深入,漸漸明白,微控制器下的程式可以認為是乙個單執行緒的程式,只能在這個超級迴圈中往復的執行,最多加一些中...