日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

浅谈linux字符设备注册

發(fā)布時(shí)間:2024/9/21 linux 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅谈linux字符设备注册 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Linux中有兩種字符設(shè)備注冊的方法:

這里所提到的函數(shù)在文件:fs/char_dev.c中定義,在頭文件include/linux/cdev.h中聲明。

一、?????????????老方法:

如果你深入瀏覽?2.6?內(nèi)核的大量驅(qū)動(dòng)代碼,?你可能注意到有許多字符驅(qū)動(dòng)使用這種方法.?你見到的是還沒有更新到?2.6內(nèi)核接口的老代碼.?因?yàn)槟莻€(gè)代碼實(shí)際上能用,?這個(gè)更新可能很長時(shí)間不會(huì)發(fā)生. 。

注冊一個(gè)字符設(shè)備的經(jīng)典方法是使用:

????? int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

這里, major?是感興趣的主編號, name?是驅(qū)動(dòng)的名子(出現(xiàn)在?/proc/devices), fops?是缺省的?file_operations?結(jié)構(gòu).?一個(gè)對?register_chrdev?的調(diào)用為給定的主編號注冊?0 - 255?的次編號,?并且為每一個(gè)建立一個(gè)缺省的?cdev?結(jié)構(gòu).?使用這個(gè)接口的驅(qū)動(dòng)必須準(zhǔn)備好處理對所有?256?個(gè)次編號的?open?調(diào)用(?不管它們是否對應(yīng)真實(shí)設(shè)備?),?它們不能使用大于?255?的主或次編號.

如果你使用?register_chrdev,?從系統(tǒng)中去除你的設(shè)備的正確的函數(shù)是:

int unregister_chrdev(unsigned int major, const char *name);

major?和?name?必須和傳遞給?register_chrdev?的相同,?否則調(diào)用會(huì)失敗.

二、???????????新方法

第一步、

內(nèi)核在內(nèi)部使用類型?struct cdev?的結(jié)構(gòu)來代表字符設(shè)備.?在內(nèi)核調(diào)用你的設(shè)備操作前,?你編寫分配并注冊一個(gè)或幾個(gè)這些結(jié)構(gòu).新方法就是利用我們這里的?cdev?接口。定義在?linux/cdev.h。

struct cdev {
??????? struct kobject kobj;
??????? struct module *owner;?? //所屬模塊
??????? const struct file_operations *ops;???
??? ??? ??? ??? //文件操作結(jié)構(gòu),在寫驅(qū)動(dòng)時(shí),其結(jié)構(gòu)體內(nèi)的大部分函數(shù)要被實(shí)現(xiàn)
??????? struct list_head list;
??????? dev_t dev;????????? //設(shè)備號,int?類型,高12位為主設(shè)備號,低20位為次設(shè)備號
??????? unsigned int count;
};

可以使用如下宏調(diào)用來獲得主、次設(shè)備號:

MAJOR(dev_t dev)
MINOR(dev_t dev)

有?2?種方法來分配和初始化一個(gè)這些結(jié)構(gòu),呵呵,這里又是兩種哦。

1、如果你想在運(yùn)行時(shí)獲得一個(gè)獨(dú)立的?cdev?結(jié)構(gòu),?你可以為此使用這樣的代碼:

struct cdev *my_cdev = cdev_alloc(); my_cdev->ops = &my_fops;

cdev_alloc源碼如下

504 /**

505??* cdev_alloc() - allocate a cdev structure

506??*

507??* Allocates and returns a cdev structure, or NULL on failure.

508??*/

509 struct cdev *cdev_alloc(void)

510 {

511?????struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);

512?????if (p) {

513?????????p->kobj.ktype = &ktype_cdev_dynamic;

514?????????INIT_LIST_HEAD(&p->list);

515?????????kobject_init(&p->kobj);

516?????}

517?????return p;

518 }

?

從函數(shù)名稱和第511行的代碼可以看出:這個(gè)函數(shù)動(dòng)態(tài)申請結(jié)構(gòu)體struct cdev,并對其進(jìn)行初始化,最后將其指針返回。下面結(jié)合cdev_init進(jìn)行進(jìn)一步說明。

2、但是,?偶爾你會(huì)想將?cdev?結(jié)構(gòu)嵌入一個(gè)你自己的設(shè)備特定的結(jié)構(gòu);?在這種情況下,?你應(yīng)當(dāng)初始化你已經(jīng)分配的結(jié)構(gòu),使用:

void cdev_init(struct cdev *cdev, struct file_operations *fops);

cdev_init源碼如下

520 /**

521??* cdev_init() - initialize a cdev structure

522??* @cdev: the structure to initialize

523??* @fops: the file_operations for this device

524??*

525??* Initializes @cdev, remembering @fops, making it ready to add to the

526??* system with cdev_add().

527??*/

528 void cdev_init(struct cdev *cdev, const struct file_operations *fops)

529 {

530?????memset(cdev, 0, sizeof *cdev);

531?????INIT_LIST_HEAD(&cdev->list);

532?????cdev->kobj.ktype = &ktype_cdev_default;

533?????kobject_init(&cdev->kobj);

534?????cdev->ops = fops;

535 }

?

cdev_alloc和cdev_init的主要區(qū)別是:前者動(dòng)態(tài)申請結(jié)構(gòu)體struct cdev并對其進(jìn)行初始化,后者將通過參數(shù)傳進(jìn)來的結(jié)構(gòu)體struct cdev進(jìn)行初始化。

?

另一個(gè)主要區(qū)別是:cdev_alloc函數(shù)中沒有對struct cdev的ops域進(jìn)行初始化,需要在cdev_alloc函數(shù)調(diào)用之后有專門的代碼對struct cdev的ops域進(jìn)行初始化,而cdev_init函數(shù)中使用通過參數(shù)傳進(jìn)來的struct file_operations結(jié)構(gòu)體指針對struct cdev的ops域進(jìn)行初始化,所以在函數(shù)cdev_init調(diào)用之后不需要再對struct cdev的ops域進(jìn)行初始化。在這之前你就應(yīng)該做好。這兩個(gè)函數(shù)就是互斥關(guān)系哈。可知,任一方法,?你都得對?struct cdev?成員file_operations結(jié)構(gòu)初始化。

第二步、

函數(shù)cdev_alloc和cdev_init只是(申請)并初始化了(部分)結(jié)構(gòu)體struct cdev,此時(shí),struct cdev和內(nèi)核還沒有任何關(guān)系。

函數(shù)cdev_add就是將函數(shù)cdev_alloc和cdev_init初始化后的struct cdev結(jié)構(gòu)體注冊到內(nèi)核中(第461行),自此內(nèi)核就可以訪問設(shè)備了。注冊設(shè)備,通常發(fā)生在驅(qū)動(dòng)模塊的加載函數(shù)中。這里, dev?是?cdev?結(jié)構(gòu), dev?是這個(gè)設(shè)備響應(yīng)的第一個(gè)設(shè)備號, count?是應(yīng)當(dāng)關(guān)聯(lián)到設(shè)備的設(shè)備號的數(shù)目.?常常?count?是?1,?但是有多個(gè)設(shè)備號對應(yīng)于一個(gè)特定的設(shè)備的情形.

cdev_add

447 /**

448??* cdev_add() - add a char device to the system

449??* @p: the cdev structure for the device

450??* @dev: the first device number for which this device is responsible

451??* @count: the number of consecutive minor numbers corresponding to this

452??*?????????device

453??*

454??* cdev_add() adds the device represented by @p to the system, making it

455??* live immediately.??A negative error code is returned on failure.

456??*/

457 int cdev_add(struct cdev *p, dev_t dev, unsigned count)

458 {

459?????p->dev = dev;

460?????p->count = count;

461?????return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);

462 }

在調(diào)用cdev_add()函數(shù)向系統(tǒng)注冊字符設(shè)備之前應(yīng)該先調(diào)用:int register_chrdev_region(dev_t from,unsigned count,const char *name)函數(shù)為其分配設(shè)備號,此函數(shù)可用:int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name)函數(shù)代替,他們之間的區(qū)別在于:register_chrdev_region()用于已知起始設(shè)備號時(shí),而MKDEV(int major,int minor)?可通過主次設(shè)備號來生成dev_t即設(shè)備號。
另一個(gè)用于設(shè)備號未知,動(dòng)態(tài)申請,其優(yōu)點(diǎn)在于不會(huì)造成設(shè)備號重復(fù)的沖突。在注銷之后,應(yīng)調(diào)用:void unregister_chrdev_region(dev_t from,unsigned count)函數(shù)釋放原先申請的設(shè)備號。
他們之間的順序關(guān)系如下:
register_chrdev_region()-->cdev_add()???? //此過程在加載模塊中
cdev_del()-->unregister_chrdev_region()???? //此過程在卸載模塊中

?

cdev_del

本函數(shù)和函數(shù)cdev_add功能相反,從內(nèi)核中刪除設(shè)備。通常發(fā)生在驅(qū)動(dòng)模塊的卸載函數(shù)中

?

總結(jié)

以上是生活随笔為你收集整理的浅谈linux字符设备注册的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。