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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux注册函数机制,Linux可信计算机制模块详细分析之函数实现机制(1)字符设备驱动...

發布時間:2024/8/5 linux 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux注册函数机制,Linux可信计算机制模块详细分析之函数实现机制(1)字符设备驱动... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原標題:Linux可信計算機制模塊詳細分析之函數實現機制(1)字符設備驅動

2.3 函數實現機制

2.3.1 Linux 字符設備驅動

在linux 3.5.4中,用結構體cdev描述字符設備,cdev結構體在文件include/linux/cdev.h中定義,如下所示:

struct cdev {

struct kobject kobj;

struct module *owner;

const struct file_operations *ops;

struct list_head list;

dev_t dev;

unsigned int count;

};

類型

字段

說明

struct kobject

kobj

設備在底層的統一接口,是設備模型的核心結構,每個cdev結構體都有一個kobject,每個在內核中注冊的kobject對象都對應于sysfs文件中的一個目錄

struct module *

owner

指向實現驅動的模塊

struct file_operations *

ops

操作設備文件的方法,實現與具體硬件通信的操作,也是字符設備驅動程序的主體部分

struct list_head

list

實現一個雙向鏈表,其中包含表示該設備的設備文件的inode

dev_t

dev

表示設備號,一個設備號由主設備號和次設備號組成

unsigned int

count

表示與該設備關聯的次設備號的數目

表21結構體cdev 各字段說明

flle_operations結構體中定義了操作設備文件的方法,都是一些函數指針,其在/include/linux/fs.h文件中定義如下:

struct file_operations {

struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

. . . . . . .

ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

int (*readdir) (struct file *, void *, filldir_t);

long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);

int (*open) (struct inode *, struct file *);

int (*release) (struct inode *, struct file *);

int (*fsync) (struct file *, loff_t, loff_t, int datasync);

int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

int (*check_flags)(int);

. . . . . . . .

};

字段名

說明

owner

指向擁有該結構體的驅動模塊指針

llseek

用于修改文件讀寫位置

read

從字符設備中讀取數據

write

向字符設備發送數據

aio_read

初始化一個異步的讀取操作

aio_write

初始化一個異步的寫入操作

readdir

用于讀取目錄,對于字符設備文件,該字段為NULL

unlocked_ioctl

執行設備I/O控制命令

mmap

用于將設備內存映射到進程地址空間

open

打開操作

release

關閉操作

synch

刷新待處理的數據

lock

文件鎖操作

check_flags

標志位檢查

表2-2 file_operations結構體字段說明

內核中所有已分配的字符設備編號都記錄在一個名為chrdevs散列表里。該散列表中的每一個元素是一個char_device_struct結構,內核并不是為每一個字符設備編號定義一個char_device_struct結構,而是為一組對應同一個字符設備驅動的設備編號范圍定義一個char_device_struct結構。chrdevs散列表的大小是255。char_device_struct結構在文件/fs/char_dev.c中定義,如下所示:

static struct char_device_struct {

struct char_device_struct *next;

unsigned int major;

unsigned int baseminor;

int minorct;

char name[64];

struct cdev *cdev;/* will die */

} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

類型

字段

說明

struct char_device_struct*

next

指向散列鏈表中的下一個元素的指針

unsigned int

major

主設備號

usigned int

baseminor

次設備號范圍中的起始設備號

int

minorct

次設備號范圍

char

name[64]

與設備編號關聯的設備驅動名稱

struct cdev*

cdev

指向字符設備描述結構體的指針,在未來內核版本中會刪除此字段

表2-3 char_device_struct結構個字段說明

用于操作cdev結構體的函數的具體實現在文件/fs/char_dev.c中,cdev_init()函數用于初始化cdev成員,并建立cdev和file_operations之間的連接實現,代碼如下:

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

{

memset(cdev, 0, sizeof *cdev);

INIT_LIST_HEAD(&cdev->list);

kobject_init(&cdev->kobj, &ktype_cdev_default);

cdev->ops = fops;

}

cdev_init()函數執行流程如下:

(1)調用memset()函數初始化cdev結構體實例。

(2)初始化cdev結構體實例鏈表。

(3)初始化kobject結構體。

(4)將cdev結構體實例與對應file_operations結構體對應。

cdev_alloc()函數用于動態申請一個cdev內存,具體代碼在文件fs/char_dev.c中,代碼如下:

struct cdev *cdev_alloc(void)

{

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

if (p) {

INIT_LIST_HEAD(&p->list);

kobject_init(&p->kobj, &ktype_cdev_dynamic);

}

return p;

}

cdev_alloc()函數執行流程如下:

(1)調用kzalloc()給cdev結構體分配空間。

(2)如果分配空間成功,初始化cdev結構體實例鏈表并初始化kobject結構體。

(3)返回cdev結構體指針

cdev_add()函數用于向系統添加一個cdev對象,完成字符設備的注冊,對cdev_add()的調用通常發生在字符設備驅動模塊加載函數中。在調用cdev_add()函數前,要首先調用register_chrdev_region()或者alloc_chrdev_region()函數向系統申請設備號。具體代碼在文件fs/char_dev.c中,代碼如下:

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

{

p->dev = dev;

p->count = count;

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

}

cdev_add執行流程如下:

(1)初始化cdev結構體中的dev、count字段,dev是設備號,count是設備號范圍,及與該設備驅動相關的次設備號范圍。

(2)調用kobj_map()函數,返回結果。kobj_map()用于用來把字符設備編號和cdev 結構變量一起保存到cdev_map這個散列表里。當后續要打開一個字符設備文件時,通過調用kobj_lookup()函數,根據設備編號就可以找到cdev結構變量,從而取出其中的ops字段

cdev_del()函數用于刪除一個cdev對象,完成字符設備的銷毀,通常在字符設備驅動模塊卸載函數中調用,在調用cdev_del()函數前,要調用unregister_chrdev_region()釋放原先申請的設備號。具體代碼在文件fs/char_dev.c中,代碼如下。

void cdev_del(struct cdev *p)

{

cdev_unmap(p->dev, p->count);

kobject_put(&p->kobj);

}

cdev_del()執行流程如下:

(1)調用cdev_unmap()函數從cdev_map散列表中刪除cdev設備實例和相應設備編號。

(2)將kobject結構體實例數量減一。

register_chrdev_region()函數用于向系統申請設備號,函數有三個參數:第一個為初始的設備號,第二個是請求的設備號的大小,即次設備號的大小,第三個參數是這個范圍(次設備號大小)內的設備號對應的設備驅動程序。該函數先執行個for循環檢查請求的設備號范圍是否超過了次設備號范圍,然后在每個設備號范圍上調用__register_chrdev_region()函數,函數執行成功返回0,錯誤返回錯誤代碼。具體代碼在文件fs/char_dev.c中,代碼如下:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

{

struct char_device_struct *cd;

dev_t to = from + count;

dev_t n, next;

for (n = from; n < to; n = next) {

next = MKDEV(MAJOR(n)+1, 0);

if (next > to)

next = to;

cd = __register_chrdev_region(MAJOR(n), MINOR(n),

next - n, name);

if (IS_ERR(cd))

goto fail;

}

return 0;

fail:

to = n;

for (n = from; n < to; n = next) {

next = MKDEV(MAJOR(n)+1, 0);

kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));

}

return PTR_ERR(cd);

}

register_chrdev_region()函數執行流程如下:

(1)通過MKDEV()函數得到一個設備號存儲于next中。

(2)判斷在from的基礎上再追加count個設備是否已經溢出到下一個主設備號,。如果溢出(next大于to),那么整個for語句就只執行個一次__register_chrdev_region函數;否則當設備號沒溢出時,會循環調用__register_chrdev_region函數,直到next大于to。

(3)如果在某個小范圍調用__register_chrdev_region時出現了失敗,那么會將此前分配的設備號都釋放。

alloc_chrdev_region()函數也是向系統申請設備號,但相比register_chrdev_region()函數,其可以動態分配一個主設備號。函數有四個參數:第一個為初始設備號,第二個是請求分配的次設備號的初始值,第三個是次設備號的范圍,第四個是次設備號對應的驅動程序的名稱,此函數最終調用__register_chrdev_region()函數實現。具體代碼在文件fs/char_dev.c中,如下所示:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,

const char *name)

{

struct char_device_struct *cd;

cd = __register_chrdev_region(0, baseminor, count, name);

if (IS_ERR(cd))

return PTR_ERR(cd);

*dev = MKDEV(cd->major, cd->baseminor);

return 0;

}

register_chrdev()函數是一個老式分配設備編號范圍的函數,其是__register_chrdev()函數的封裝,__register_chrdev()函數內部調用__register_chrdev_region()函數實現。其分配一個單獨主設備號和0 ~ 255的次設備號范圍。如果申請的主設備號為0則動態分配一個。該函數還需傳入一個file_operations結構的指針,函數內部自動分配了一個新的cdev結構。

resgister_chrdev_region()、alloc_chrdev_region()、register_chrdev()三個字符設備注冊函數最終都調用了__register_chrdev_region()函數。__register_chrdev_region()函數用于向系統注冊一個設備號,有四個參數,第一個是設備的主設備號,第二個是初始此設備號,第三個是次設備號范圍,第四個是與設備號對應的設備驅動名稱。__register_chrdev_region()函數代碼在文件/fs/char_dev.c中。__register_chrdev_region()函數執行流程圖如下所示:

圖2-4 __register_chrdev_region()函數執行流程圖

chrdev_open()函數用于打開字符設備文件,其有兩個參數,第一個是文件索引節點結構體指針,第二個參數是文件對象結構體指針。chrdev_open()函數執行流程圖如下:

圖2-5 chrdev_open()函數執行流程圖

unregister_chrdev_region()函數用于注銷設備號,其調用__unregister_chrdev_region()函數具體,代碼在文件fs/char_dev.c中,如下所示:

void unregister_chrdev_region(dev_t from, unsigned count)

{

dev_t to = from + count;

dev_t n, next;

for (n = from; n < to; n = next) {

next = MKDEV(MAJOR(n)+1, 0);

if (next > to)

next = to;

kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));

}

}

unregister_chrdev()函數也是用于注銷一個設備號,與設備號注冊函數register_chrdev()對應,其是對__unregister_chrdev()函數的封裝,__unregister_chrdev()函數中調用了__unregister_chrdev_region()。__unregister_chrdev()函數代碼在文件fs/char_dev.c中,如下所示:

void __unregister_chrdev(unsigned int major, unsigned int baseminor,

unsigned int count, const char *name)

{

struct char_device_struct *cd;

cd = __unregister_chrdev_region(major, baseminor, count);

if (cd && cd->cdev)

cdev_del(cd->cdev);

kfree(cd);

}

__unregister_chrdev_region()是注銷設備號的核心函數,代碼在文件fs/char_dev.c中。其執行流程比較簡單,先從chrdevs散列表里中找到需要釋放的char_device_struct結構實例,再將char_device_struct結構實例從chrdevs散列表中刪除。返回搜狐,查看更多

責任編輯:

總結

以上是生活随笔為你收集整理的linux注册函数机制,Linux可信计算机制模块详细分析之函数实现机制(1)字符设备驱动...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。