貼心網(wǎng)絡(luò)推廣方法免費(fèi)的seo教程
一、Linux內(nèi)核對(duì)文件的分類(lèi)
Linux的文件種類(lèi)
- 1、-:普通文件
- 2、d:目錄文件
- 3、p:管道文件
- 4、s:本地socket文件
- 5、l:鏈接文件
- 6、c:字符設(shè)備
- 7、b:塊設(shè)備
Linux內(nèi)核按驅(qū)動(dòng)程序?qū)崿F(xiàn)模型框架的不同,將設(shè)備分為三類(lèi)
- 1、字符設(shè)備:按字節(jié)流形式讀取的設(shè)備,一般情況下按順序訪問(wèn),數(shù)據(jù)量不大,一般不設(shè)緩存
- 2、塊設(shè)備:按塊進(jìn)行讀寫(xiě)的設(shè)備,最小的塊大小為256字節(jié)(一個(gè)扇區(qū)),塊的大小必須是扇區(qū)的整數(shù)倍。Linux塊的大小一般為4096字節(jié),隨機(jī)訪問(wèn),設(shè)有緩存以提高效率
- 3、網(wǎng)絡(luò)設(shè)備:針對(duì)網(wǎng)絡(luò)數(shù)據(jù)收發(fā)的設(shè)備
框圖
二、設(shè)備號(hào)
- 用于區(qū)分內(nèi)核中的同類(lèi)設(shè)備
內(nèi)核中用設(shè)備號(hào)來(lái)區(qū)分同類(lèi)里不同的設(shè)備,設(shè)備號(hào)是一個(gè)無(wú)符號(hào)的32位整數(shù),數(shù)據(jù)類(lèi)型為dev_t,設(shè)備號(hào)分為兩部分:
- 1、主設(shè)備號(hào):占高12位,用來(lái)表示驅(qū)動(dòng)程序相同的一類(lèi)設(shè)備
- 2、次設(shè)備號(hào):占低20位,用來(lái)表示被操作的具體設(shè)備
應(yīng)用程序打開(kāi)一個(gè)設(shè)備時(shí),通過(guò)設(shè)備號(hào)來(lái)查找定位內(nèi)核中管理的具體設(shè)備
MKDEV - 宏
- 將主設(shè)備號(hào)和次設(shè)備號(hào)組合成32位的完整設(shè)備號(hào),用法:
dev_t id;
int major = 251;//主設(shè)備號(hào)
int minor = 2;//次設(shè)備號(hào)
id = MKDEV(251, 2);
MAJOR - 宏
- 將主設(shè)備號(hào)從設(shè)備號(hào)中分離出來(lái),用法:
dev_t id = MKDEV(251, 2);
int major = MAJOR(id);
MINOR - 宏
- 將次設(shè)備號(hào)從設(shè)備號(hào)中分離出來(lái),用法:
dev_t id = MKDEV(251, 2);
int minor = MINOR(id);
如果已知一個(gè)設(shè)備的主次設(shè)備號(hào),應(yīng)用指定好設(shè)備文件名,可以用mknod
命令在/dev目錄下創(chuàng)建代表這個(gè)設(shè)備的文件,即此后應(yīng)用程序?qū)Υ宋募牟僮骶褪谴韺?duì)此設(shè)備的操作,用法:
cd /dev
mknod 設(shè)備文件名 設(shè)備種類(lèi)(c位字符設(shè)備,b為塊設(shè)備) 主設(shè)備號(hào) 次設(shè)備號(hào)
在應(yīng)用程序中如果要?jiǎng)?chuàng)建設(shè)備可以調(diào)用系統(tǒng)調(diào)用函數(shù)mknod
,用法:
int mknod(const char *pathname,mode_t mode,dev_t dev);
pathname:帶路徑的設(shè)備文件名,無(wú)路徑默認(rèn)為當(dāng)前目錄,一般都創(chuàng)建在/dev下
mode:文件權(quán)限或S_IFCHR/S_IFBLK
dev:32位設(shè)備號(hào)
三申請(qǐng)和注冊(cè)設(shè)備號(hào)
字符設(shè)備驅(qū)動(dòng)的第一步是通過(guò)模塊的入口函數(shù)__init向內(nèi)核添加設(shè)備驅(qū)動(dòng)的代碼框架
- 1、申請(qǐng)?jiān)O(shè)備號(hào)
- 2、定義、初始化、向內(nèi)核添加代表本設(shè)備的結(jié)構(gòu)體元素
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:手動(dòng)分配設(shè)備號(hào),先驗(yàn)證設(shè)備號(hào)是否被使用,如果沒(méi)有就申請(qǐng)?jiān)撛O(shè)備號(hào)
參數(shù):from:自己指定的設(shè)備號(hào)count:申請(qǐng)的設(shè)備數(shù)量name:/proc/devices文件中與該設(shè)備對(duì)應(yīng)的名字,方便用戶(hù)層查詢(xún)主設(shè)備號(hào)
返回值:成功為0,失敗是負(fù)數(shù),絕對(duì)值為錯(cuò)誤碼
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count, const char *name)
功能:自動(dòng)分配設(shè)備號(hào),查詢(xún)內(nèi)核里未被占用的設(shè)備號(hào),如果找到則占用該設(shè)備號(hào)
參數(shù):dev:分配設(shè)備號(hào)成功后用來(lái)存放分配到的設(shè)備號(hào)baseminor:起始的次設(shè)備號(hào),一般為0count:申請(qǐng)?jiān)O(shè)備號(hào)的數(shù)量name:/proc/devices文件中與該設(shè)備對(duì)應(yīng)的名字,方便用戶(hù)層查詢(xún)主次設(shè)備號(hào)
返回值:成功后為0,失敗為負(fù)數(shù),絕對(duì)值為錯(cuò)誤碼
-
成功分配后在
/proc/devices
可以查看到申請(qǐng)到的主設(shè)備號(hào)和設(shè)備名 -
3、釋放設(shè)備號(hào)
void unregister_chrdev_region(dev_t from, unsigned count)
功能:釋放設(shè)備號(hào)
參數(shù):from:已經(jīng)成功分配到的設(shè)備號(hào)將被釋放count:申請(qǐng)成功的設(shè)備數(shù)量
- 釋放后
/proc/devices
路徑下對(duì)應(yīng)的設(shè)備文件記錄消失
四:注冊(cè)字符設(shè)備
- 1、字符設(shè)備結(jié)構(gòu)體
#include <linux/cdev.h>
struct cdev
{struct kobject kobj;//表示該類(lèi)型實(shí)體是一種內(nèi)核對(duì)象(kernel object)struct module *owner;//填THIS_MODULE,表示還字符設(shè)備屬于哪個(gè)內(nèi)核模塊const struct file_operations *ops;//指向內(nèi)核空間中存放該設(shè)備的各種操作函數(shù)地址struct list_head list;//鏈表指針域dev_t dev;//設(shè)備號(hào)unsigned int count;//設(shè)備數(shù)量
};
自己定義的結(jié)構(gòu)體中必須要有一個(gè)成員為struct cdev cdev ,兩種定義方法:
-
1、直接定義:定義結(jié)構(gòu)體全局變量
-
2、動(dòng)態(tài)申請(qǐng):
struct cdev * cdev_alloc()
-
初始化
void cdev_init(struct cdev *cdev,const struct file_operations *fops)
功能:初始化字符設(shè)備結(jié)構(gòu)體
參數(shù):cdev:設(shè)備號(hào)fops:操作函數(shù)
- 字符設(shè)備結(jié)構(gòu)體與設(shè)備號(hào)綁定
int cdev_add(struct cdev *p,dev_t dev,unsigned int count)
功能:將指定字符設(shè)備添加到內(nèi)核
參數(shù):p:指向被添加的設(shè)備dev:設(shè)備號(hào)count:設(shè)備數(shù)量
- 移除字符設(shè)備
void cdev_del(struct cdev *p)
功能:從內(nèi)核中移除一個(gè)字符設(shè)備
參數(shù):p:設(shè)備號(hào)
- 系統(tǒng)調(diào)用函數(shù)結(jié)構(gòu)體
#include <linux/fs.h>
struct file_operations
{struct module *owner; //填THIS_MODULE,表示該結(jié)構(gòu)體對(duì)象從屬于哪個(gè)內(nèi)核模塊int (*open) (struct inode *, struct file *); //打開(kāi)設(shè)備int (*release) (struct inode *, struct file *); //關(guān)閉設(shè)備ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); //讀設(shè)備ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); //寫(xiě)設(shè)備loff_t (*llseek) (struct file *, loff_t, int); //定位long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);//讀寫(xiě)設(shè)備參數(shù),讀設(shè)備狀態(tài)、控制設(shè)備unsigned int (*poll) (struct file *, struct poll_table_struct *); //POLL機(jī)制,實(shí)現(xiàn)多路復(fù)用的支持int (*mmap) (struct file *, struct vm_area_struct *); //映射內(nèi)核空間到用戶(hù)層int (*fasync) (int, struct file *, int); //信號(hào)驅(qū)動(dòng)//......
};
該對(duì)象的函數(shù)指針成員對(duì)應(yīng)相應(yīng)的系統(tǒng)調(diào)用函數(shù),應(yīng)用層通過(guò)系統(tǒng)調(diào)用函數(shù)來(lái)間接調(diào)用這些函數(shù)指針指向的設(shè)備驅(qū)動(dòng)函數(shù)
一般定義一個(gè)struct file_operations
類(lèi)型的全局變量,并用自己實(shí)現(xiàn)的各種操作函數(shù)名對(duì)其初始化
總結(jié):
字符設(shè)備開(kāi)發(fā)步驟:
- 如果設(shè)備有自己的控制數(shù)據(jù),則定義一個(gè)包含
struct cdev cdev
成員的結(jié)構(gòu)體(方便管理),其他成員根據(jù)設(shè)備需求。設(shè)備簡(jiǎn)單直接用struct cdev
- 2、定義一個(gè)
struct cdev
的全局變量來(lái)表示該設(shè)備 - 3、定義三個(gè)全局變量分別來(lái)表示:主設(shè)備號(hào)、次設(shè)備號(hào)、設(shè)備數(shù)量
- 4、定義一個(gè)
struct file_operations
結(jié)構(gòu)體變量,其owner成員賦值為T(mén)HIS_MODULE - 5、module init函數(shù)流程:a. 申請(qǐng)?jiān)O(shè)備號(hào) b. 如果是全局設(shè)備指針則動(dòng)態(tài)分配代表本設(shè)備的結(jié)構(gòu)體元素 c. 初始化
struct cdev
成員,設(shè)置struct cdev
的owner成員為T(mén)HIS_MODULE e. 添加字符設(shè)備到內(nèi)核 - 6、module exit函數(shù):a. 注銷(xiāo)設(shè)備號(hào) b. 從內(nèi)核中移除
struct cdev
. 如果如果是全局設(shè)備指針則釋放其指向空間 - 7、編寫(xiě)各個(gè)操作函數(shù)并將函數(shù)名初始化給struct file_operations結(jié)構(gòu)體變量
驗(yàn)證步驟:
- 1、編寫(xiě)驅(qū)動(dòng)代碼
- 2、make生成ko文件
- 3、insmod內(nèi)核模塊
- 4、查閱字符設(shè)備用到的設(shè)備號(hào)(主設(shè)備號(hào)):cat /proc/devices | grep 申請(qǐng)?jiān)O(shè)備號(hào)時(shí)用的名字
- 5、創(chuàng)建設(shè)備文件(設(shè)備節(jié)點(diǎn)) : mknod /dev/??? c 上一步查詢(xún)到的主設(shè)備號(hào) 代碼中指定初始次設(shè)備號(hào)
- 6、編寫(xiě)app驗(yàn)證驅(qū)動(dòng)(testmychar_app.c)
- 7、編譯運(yùn)行app,dmesg命令查看內(nèi)核打印信息
示例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>//設(shè)備結(jié)構(gòu)體
struct cdev cdev;//設(shè)備信息
dev_t id;
int major, minor;//驅(qū)動(dòng)操作函數(shù)
//打開(kāi)設(shè)備
int char_open (struct inode * inode, struct file * f) //打開(kāi)設(shè)備
{return 0;
}//讀
ssize_t char_read (struct file * f, char __user * user, size_t size, loff_t * loff) //讀設(shè)備
{return size;
}//寫(xiě)
ssize_t char_write (struct file * d , const char __user * user, size_t size, loff_t * loff) //寫(xiě)設(shè)備
{return size;
}//關(guān)閉
int char_close (struct inode * inod, struct file * f) //關(guān)閉設(shè)備
{return 0;
}struct file_operations fops = {.owner = THIS_MODULE,.open = char_open,.release = char_close,.write = char_write,.read = char_read,
};int __init init_test(void)
{int ret;//申請(qǐng)?jiān)O(shè)備號(hào)(自動(dòng)分配)alloc_chrdev_region(&id, 0, 1, "char");if(ret){printk("alloc err \r\n");return -EIO;}//初始化設(shè)備結(jié)構(gòu)體cdev_init(&cdev, &fops);//設(shè)置cdev的owner成員為T(mén)HIS_MODULEcdev.owner = THIS_MODULE;//添加字符設(shè)備到內(nèi)核cdev_add(&cdev, id, 1);return 0;
}void __exit exit_test(void)
{//移除設(shè)備cdev_del(&cdev);//注銷(xiāo)設(shè)備號(hào)unregister_chrdev_region(id, 1);
} MODULE_LICENSE("GPL");
module_init(init_test);
module_exit(exit_test);
五、字符設(shè)備驅(qū)動(dòng)框架解析
設(shè)備的操作函數(shù)如果比喻為樁的話(huà),則:
- 驅(qū)動(dòng)實(shí)現(xiàn)操作函數(shù):做樁
- insmod調(diào)用init函數(shù):釘樁
- 應(yīng)用層通過(guò)系統(tǒng)調(diào)用函數(shù)間接調(diào)用這些設(shè)備的操作函數(shù):用樁
- rmmod調(diào)用exit函數(shù):拔樁
操作函數(shù)中常用的結(jié)構(gòu)體
- 1、內(nèi)核中記錄文件信息的結(jié)構(gòu)體
struct inode{//....dev_t i_rdev;//設(shè)備號(hào)struct cdev *i_cdev;//如果是字符設(shè)備才有此成員,指向?qū)?yīng)設(shè)備驅(qū)動(dòng)程序中的加入系統(tǒng)的struct cdev對(duì)象//....
}
/*1、該結(jié)構(gòu)體對(duì)象對(duì)應(yīng)著一個(gè)實(shí)際的設(shè)備,一對(duì)一2、open一個(gè)文件時(shí),如果內(nèi)核中存在該文件對(duì)應(yīng)的inode對(duì)象就不再創(chuàng)建,不存在則創(chuàng)建3、內(nèi)核中用此類(lèi)型關(guān)聯(lián)到此文件的操作函數(shù)集(對(duì)設(shè)備而言就是關(guān)聯(lián)到具體的驅(qū)動(dòng)代碼)
*/
- 2、讀寫(xiě)文件內(nèi)容過(guò)程中用到的一些控制數(shù)據(jù)組合的對(duì)象
struct file
{//...mode_t?f_mode;//不同用戶(hù)的操作權(quán)限,驅(qū)動(dòng)一般不用loff_t?f_pos;//數(shù)據(jù)位置指示器,需要控制數(shù)據(jù)開(kāi)始讀寫(xiě)位置的設(shè)備有用unsigned?int?f_flags;//open是的第二個(gè)參數(shù)flags存放在此,驅(qū)動(dòng)中常用struct?file_operations?*f_op;//open時(shí)從struct inode中i_cdev的對(duì)應(yīng)成員獲得地址。驅(qū)動(dòng)開(kāi)發(fā)中用來(lái)協(xié)助理解工作原理,內(nèi)核中使用void?*private_data;//本次打開(kāi)文件的私有數(shù)據(jù)驅(qū)動(dòng)中常用來(lái)在幾個(gè)操作函數(shù)中傳遞數(shù)據(jù)struct?dentry?*f_dentry;//驅(qū)動(dòng)中一般不用,除非需要訪問(wèn)對(duì)應(yīng)文件的inode,用法flip->f_dentry->d_inodeint refcnt;//引用計(jì)數(shù),保存著該對(duì)象地址的位置個(gè)數(shù),close時(shí)發(fā)現(xiàn)refcnt為0才會(huì)銷(xiāo)毀該struct file對(duì)象//...
};
/*1、open函數(shù)被調(diào)用一次,則創(chuàng)建一個(gè)該對(duì)象,可以認(rèn)為一個(gè)該對(duì)象對(duì)應(yīng)一次指定文件的操作2、open一個(gè)文件多次,每次open都會(huì)創(chuàng)建一個(gè)該對(duì)象3、文件描述符中存放的地址指向該類(lèi)型的對(duì)象4、每個(gè)文件描述符都對(duì)應(yīng)一個(gè)struct file對(duì)象的地址
*/
字符設(shè)備驅(qū)動(dòng)程序框架
-
驅(qū)動(dòng)實(shí)現(xiàn)端:
-
驅(qū)動(dòng)使用端:
參考原理圖
常用操作函數(shù)
int (*open) (struct inode *, struct file *); //打開(kāi)設(shè)備
/*指向函數(shù)一般用來(lái)對(duì)設(shè)備進(jìn)行硬件上的初始化,對(duì)于一些簡(jiǎn)單的設(shè)備該函數(shù)只需要return 0,對(duì)應(yīng)open系統(tǒng)調(diào)用,是open系統(tǒng)調(diào)用函數(shù)實(shí)現(xiàn)過(guò)程中調(diào)用的函數(shù),
*/int (*release) (struct inode *, struct file *); //關(guān)閉設(shè)備
/*,指向函數(shù)一般用來(lái)對(duì)設(shè)備進(jìn)行硬件上的關(guān)閉操作,對(duì)于一些簡(jiǎn)單的設(shè)備該函數(shù)只需要return 0,對(duì)應(yīng)close系統(tǒng)調(diào)用,是close系統(tǒng)調(diào)用函數(shù)實(shí)現(xiàn)過(guò)程中調(diào)用的函數(shù)
*/ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); //讀設(shè)備
/*指向函數(shù)用來(lái)將設(shè)備產(chǎn)生的數(shù)據(jù)讀到用戶(hù)空間,對(duì)應(yīng)read系統(tǒng)調(diào)用,是read系統(tǒng)調(diào)用函數(shù)實(shí)現(xiàn)過(guò)程中調(diào)用的函數(shù)
*/ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); //寫(xiě)設(shè)備
/*指向函數(shù)用來(lái)將用戶(hù)空間的數(shù)據(jù)寫(xiě)進(jìn)設(shè)備,對(duì)應(yīng)write系統(tǒng)調(diào)用,是write系統(tǒng)調(diào)用函數(shù)實(shí)現(xiàn)過(guò)程中調(diào)用的函數(shù)
*/loff_t (*llseek) (struct file *, loff_t, int); //數(shù)據(jù)操作位置的定位
/*指向函數(shù)用來(lái)獲取或設(shè)置設(shè)備數(shù)據(jù)的開(kāi)始操作位置(位置指示器),對(duì)應(yīng)lseek系統(tǒng)調(diào)用,是lseek系統(tǒng)調(diào)用函數(shù)實(shí)現(xiàn)過(guò)程中調(diào)用的函數(shù)
*/long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);//讀寫(xiě)設(shè)備參數(shù),讀設(shè)備狀態(tài)、控制設(shè)備
/*指向函數(shù)用來(lái)獲取、設(shè)置設(shè)備一些屬性或設(shè)備的工作方式等非數(shù)據(jù)讀寫(xiě)操作,對(duì)應(yīng)ioctl系統(tǒng)調(diào)用,是ioctl系統(tǒng)調(diào)用函數(shù)實(shí)現(xiàn)過(guò)程中調(diào)用的函數(shù)
*/unsigned int (*poll) (struct file *, struct poll_table_struct *);//POLL機(jī)制,實(shí)現(xiàn)對(duì)設(shè)備的多路復(fù)用方式的訪問(wèn)
/*指向函數(shù)用來(lái)協(xié)助多路復(fù)用機(jī)制完成對(duì)本設(shè)備可讀、可寫(xiě)數(shù)據(jù)的監(jiān)控,對(duì)應(yīng)select、poll、epoll_wait系統(tǒng)調(diào)用,是select、poll、epoll_wait系統(tǒng)調(diào)用函數(shù)實(shí)現(xiàn)過(guò)程中調(diào)用的函數(shù)
*/int (*fasync) (int, struct file *, int); //信號(hào)驅(qū)動(dòng)
/*指向函數(shù)用來(lái)創(chuàng)建信號(hào)驅(qū)動(dòng)機(jī)制的引擎,對(duì)應(yīng)fcntl系統(tǒng)調(diào)用的FASYNC標(biāo)記設(shè)置,是fcntl系統(tǒng)調(diào)用函數(shù)FASYNC標(biāo)記設(shè)置過(guò)程中調(diào)用的函數(shù)
*/
六、讀操作實(shí)現(xiàn)
ssize_t xxx_read(struct file *filp, char __user *pbuf, size_t count, loff_t *ppos);
完成功能:讀取設(shè)備產(chǎn)生的數(shù)據(jù)
參數(shù):filp:指向open產(chǎn)生的struct file類(lèi)型的對(duì)象,表示本次read對(duì)應(yīng)的那次openpbuf:指向用戶(hù)空間一塊內(nèi)存,用來(lái)保存讀到的數(shù)據(jù)count:用戶(hù)期望讀取的字節(jié)數(shù)ppos:對(duì)于需要位置指示器控制的設(shè)備操作有用,用來(lái)指示讀取的起始位置,讀完后也需要變更位置指示器的指示位置返回值:本次成功讀取的字節(jié)數(shù),失敗返回-1
- 由于內(nèi)核不能直接和用戶(hù)空間進(jìn)行數(shù)據(jù)傳輸,因此要使用函數(shù)實(shí)現(xiàn):
- 從內(nèi)核中復(fù)制數(shù)據(jù)到用戶(hù)空間
#include <asm-generic/uaccess.h>
unsigned long copy_to_user (void __user * to, const void * from, unsigned long n)
成功為返回0,失敗非0
七、寫(xiě)操作實(shí)現(xiàn)
ssize_t xxx_write (struct file *filp, const char __user *pbuf, size_t count, loff_t *ppos);
完成功能:向設(shè)備寫(xiě)入數(shù)據(jù)
參數(shù):filp:指向open產(chǎn)生的struct file類(lèi)型的對(duì)象,表示本次write對(duì)應(yīng)的那次openpbuf:指向用戶(hù)空間一塊內(nèi)存,用來(lái)保存被寫(xiě)的數(shù)據(jù)count:用戶(hù)期望寫(xiě)入的字節(jié)數(shù)ppos:對(duì)于需要位置指示器控制的設(shè)備操作有用,用來(lái)指示寫(xiě)入的起始位置,寫(xiě)完后也需要變更位置指示器的指示位置返回值:本次成功寫(xiě)入的字節(jié)數(shù),失敗返回-1
- 從用戶(hù)空間復(fù)制數(shù)據(jù)到內(nèi)核中
#include <asm-generic/uaccess.h>
unsigned long copy_from_user (void * to, const void __user * from, unsigned long n)成功為返回0,失敗非0
八、ioctl的實(shí)現(xiàn)
- 可以已知結(jié)構(gòu)體成員獲得所在結(jié)構(gòu)體變量的地址
container_of(成員地址,結(jié)構(gòu)體類(lèi)名,成員的名稱(chēng))
long xxx_ioctl (struct file *filp, unsigned int cmd, unsigned long arg);
功能:對(duì)相應(yīng)設(shè)備做指定的控制操作(各種屬性的設(shè)置獲取等等)
參數(shù):filp:指向open產(chǎn)生的struct file類(lèi)型的對(duì)象,表示本次ioctl對(duì)應(yīng)的那次opencmd:用來(lái)表示做的是哪一個(gè)操作arg:和cmd配合用的參數(shù)
返回值:成功為0,失敗-1
- cmd的組成
1. dir(direction),ioctl 命令訪問(wèn)模式(屬性數(shù)據(jù)傳輸方向),占據(jù) 2 bit,可以為 _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分別指示了四種訪問(wèn)模式:無(wú)數(shù)據(jù)、讀數(shù)據(jù)、寫(xiě)數(shù)據(jù)、讀寫(xiě)數(shù)據(jù);
2. type(device type),設(shè)備類(lèi)型,占據(jù) 8 bit,在一些文獻(xiàn)中翻譯為 “幻數(shù)” 或者 “魔數(shù)”,可以為任意 char 型字符,例如 ‘a(chǎn)’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的設(shè)備標(biāo)識(shí);
3. nr(number),命令編號(hào)/序數(shù),占據(jù) 8 bit,可以為任意 unsigned char 型數(shù)據(jù),取值范圍 0~255,如果定義了多個(gè) ioctl 命令,通常從 0 開(kāi)始編號(hào)遞增;
4. size,涉及到 ioctl 函數(shù) 第三個(gè)參數(shù) arg ,占據(jù) 13bit 或者 14bit(體系相關(guān),arm 架構(gòu)一般為 14 位),指定了 arg 的數(shù)據(jù)類(lèi)型及長(zhǎng)度,如果在驅(qū)動(dòng)的 ioctl 實(shí)現(xiàn)中不檢查,通??梢院雎栽搮?shù);
九、創(chuàng)建多個(gè)次設(shè)備
每一個(gè)具體設(shè)備(次設(shè)備不一樣),必須有一個(gè)struct cdev
來(lái)代表它
并且需要以下三個(gè)操作:
- cdev_init
- cdev.owner賦值
- cdev_add
十、(多個(gè)次設(shè)備同時(shí)獲取設(shè)備號(hào)、同時(shí)創(chuàng)建設(shè)備)在驅(qū)動(dòng)中創(chuàng)建設(shè)備
內(nèi)核中 實(shí)現(xiàn) 創(chuàng)建 設(shè)備文件 /dev/xxx
1、創(chuàng)建設(shè)備
- 手動(dòng)方式 創(chuàng)建設(shè)備文件
sudo mknod /dev/xxx c 243 0 設(shè)備節(jié)點(diǎn)名 字符型 主設(shè)備號(hào) 次設(shè)備號(hào) 在Linux內(nèi)核 3.0后 引入了 udev服務(wù) 該服務(wù)可以用于 檢查驅(qū)動(dòng)的 設(shè)備文件添加請(qǐng)求, 然后添加對(duì)應(yīng)的設(shè)備文件
- 自動(dòng)創(chuàng)建設(shè)備文件
#include <linux/device.h>
struct class * class_create(owner, name);
owner 所有者 THIS_MODULE
name 類(lèi)的名字 創(chuàng)建之后可以看到該名字 `cat /proc/devices | grep 類(lèi)的名字`返回值: struct class * 出差 返回 錯(cuò)誤指針
2、關(guān)聯(lián)設(shè)備節(jié)點(diǎn)與設(shè)備文件名
#include <linux/device.h>
struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...);
例子:dev = device_create(cls, NULL, id, NULL, "char%d", 1);
3、卸載驅(qū)動(dòng)時(shí)銷(xiāo)毀設(shè)備節(jié)點(diǎn)
void device_destroy(struct class *cls, dev_t devt);
4、銷(xiāo)毀類(lèi)
void class_destroy(struct class *cls);
示例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>//設(shè)備結(jié)構(gòu)體
struct cdev cdev;//設(shè)備信息
dev_t id;
int major, minor;//class類(lèi)型
struct class *cls;struct device *dev;//驅(qū)動(dòng)操作函數(shù)
//打開(kāi)設(shè)備
int char_open (struct inode * inode, struct file * f) //打開(kāi)設(shè)備
{return 0;
}//讀
ssize_t char_read (struct file * f, char __user * user, size_t size, loff_t * loff) //讀設(shè)備
{return size;
}//寫(xiě)
ssize_t char_write (struct file * d , const char __user * user, size_t size, loff_t * loff) //寫(xiě)設(shè)備
{return size;
}//關(guān)閉
int char_close (struct inode * inod, struct file * f) //關(guān)閉設(shè)備
{return 0;
}struct file_operations fops = {.owner = THIS_MODULE,.open = char_open,.release = char_close,.write = char_write,.read = char_read,
};int __init init_test(void)
{int ret;int i;//申請(qǐng)?jiān)O(shè)備號(hào)(自動(dòng)分配)四個(gè)設(shè)備alloc_chrdev_region(&id, 0, 4, "char_dev");if(ret){printk("alloc err \r\n");return -EIO;}//初始化設(shè)備結(jié)構(gòu)體cdev_init(&cdev, &fops);//設(shè)置cdev的owner成員為T(mén)HIS_MODULEcdev.owner = THIS_MODULE;//添加字符設(shè)備到內(nèi)核ret = cdev_add(&cdev, id, 4);if(ret){printk("cdev add err \r\n");goto add_err;}//生成設(shè)備文件cls = class_create(THIS_MODULE, "char_class");if(IS_ERR(cls)){printk("class create err\r\n");goto class_err; }//關(guān)聯(lián)設(shè)備節(jié)點(diǎn)和設(shè)備名 循環(huán)創(chuàng)建4個(gè)for(i =0 ;i < 4; i++){dev = device_create(cls, NULL, id + i, NULL, "char_dev%d", i);if(IS_ERR(dev)){printk("device create err\r\n");goto device_err; } }return 0;//回滾
device_err:for(i--; i >= 0; i--){device_destroy(cls, id + i);}//銷(xiāo)毀classclass_destroy(cls);class_err://移除設(shè)備cdev_del(&cdev);add_err://注銷(xiāo)設(shè)備號(hào)unregister_chrdev_region(id, 4);return -EIO;
}void __exit exit_test(void)
{int i;//取消設(shè)備節(jié)點(diǎn)for(i =0 ;i < 4; i++){device_destroy(cls, id + i);}//銷(xiāo)毀classclass_destroy(cls);//移除設(shè)備cdev_del(&cdev);//注銷(xiāo)設(shè)備號(hào)unregister_chrdev_region(id, 4);
} MODULE_LICENSE("GPL");
module_init(init_test);
module_exit(exit_test);