生活随笔
收集整理的這篇文章主要介紹了
Linux驱动编程 step-by-step (五)主要的文件操作方法实现
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
主要的文件操作方法實現(xiàn)
文件操作函數(shù)有很多的操作接口,驅(qū)動編程需要實現(xiàn)這些接口,在用戶編程時候系統(tǒng)調(diào)用時候會調(diào)用到這些操作
[cpp]?view plaincopy
struct?file_operations?{?? ????...?? ????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?*);?? ????int?(*open)?(struct?inode?*,?struct?file?*);?? ????int?(*release)?(struct?inode?*,?struct?file?*);?? ????...?? };??
以上只列出了主要的操作,下面會依次介紹:
本次的測試代碼上傳在:char_step2
結(jié)構(gòu)體:
首先 我們會模擬寫一個不操作任何設(shè)備,而僅僅是存儲的一個驅(qū)動。
定義自己的一個結(jié)構(gòu)體為:
[cpp]?view plaincopy
struct?simple_dev{?? ????char?data[MAX_SIMPLE_LEN];?? ????loff_t?count;?? ????struct?semaphore?semp;?? };??
data 保存數(shù)據(jù), count表示文件的數(shù)據(jù)有效的位置, semp是一個信號量鎖,在以后的編程中使用,
之后的程序中結(jié)構(gòu)體也會做相應(yīng)的變化,以適應(yīng)linux編寫驅(qū)動的習(xí)慣
open方法:
打開設(shè)備并進(jìn)一步初始化工作,在沒有定義open方法時內(nèi)核以一種默認(rèn)的方式打開設(shè)備,保證每次都能正確打開。
open方法中有有struct inode參數(shù),包含了設(shè)備號,程序中可以使用次設(shè)備號得到正操作的設(shè)備
在struct file中主要的操作是private_data指針,他可以傳遞任何自己創(chuàng)建的結(jié)構(gòu)。
總得說來open方法的作用有3
1、獲得操作的設(shè)備(通過設(shè)備號)
2、進(jìn)一步的初始化設(shè)備
3、初始化file結(jié)構(gòu)體的private_data
[cpp]?view plaincopy
static?int?simple_open(struct?inode?*inodp,?struct?file?*filp)?? {?? ????struct?simple_dev?*temp_dev?=?NULL;?? ????int?minor?=?0;?? #if?SIMPLE_DEBUG?? ????printk(KERN_INFO?"In?%s?\n",?__func__);?? #endif?? ????minor?=?iminor(inodp);?? ????if(minor?>?DEV_COUNT-1){?? ????????printk(KERN_ERR?"the?char?dev?in?invalid?\n");?? ????????return?-ENODEV;?? ????}?? #if?SIMPLE_DEBUG?? ????printk(KERN_INFO?"the?minor?is??%d?\n",?minor);?? #endif?? ?????? ????temp_dev?=?&char2_dev[minor];?? ?????? ????filp->private_data?=?temp_dev;??? ?? ????return?0;?? }??
release方法:
主要是對open進(jìn)一步初始化的操作的反操作
比如open時候分配了內(nèi)存,在release時就需要釋放它等
例子中因為操作內(nèi)存設(shè)備,故在release時無需做什么事
read方法:
read 是把設(shè)備中的數(shù)據(jù)傳遞給調(diào)用者
主要步驟
1、檢測偏移量有效(有些設(shè)備驅(qū)動不需要檢測)
2、檢測用戶空間地址有效
3、將數(shù)據(jù)傳給用戶(在此步驟中調(diào)用的函數(shù)可能會自己檢測步驟2)
4、調(diào)整偏移量
5、返回讀到的數(shù)據(jù)長度
(read write 用法相對靈活,不要依賴上邊的步驟,設(shè)備驅(qū)動程序要根據(jù)設(shè)備特性去設(shè)計此方法)
這里先介紹一個會檢測用戶空間地址是否有效的copy函數(shù)
用戶調(diào)用read讀設(shè)備,而在內(nèi)核空間就是將數(shù)據(jù)傳給用戶,是一個to的操作
[cpp]?view plaincopy
unsigned?long?__must_check?copy_to_user(void?__user?*to,?const?void?*from,?unsigned?long?n)??
__must_check表述必須檢測其返回值,操作成功返回0,不成功返回負(fù)的錯誤碼
to是用戶空間指針 也就是read函數(shù)傳入的用戶空間的指針,
from指向設(shè)備要傳送的數(shù)據(jù)
n標(biāo)識傳入長度
上圖是 摘自LDD3上的經(jīng)典視圖, 應(yīng)該比較能說明read的方法
[cpp]?view plaincopy
static?ssize_t?simple_read(struct?file?*filp,?char?__user?*userstr,?size_t?count,?loff_t?*loff)?? {?? ?????? ????struct?simple_dev?*dev?=?NULL;?? ????int?data_remain?=?0;?? ????int?err;?? #if?SIMPLE_DEBUG?? ????printk(KERN_INFO?"In?%s?\n",?__func__);?? #endif?? ?????? ????dev?????????=?filp->private_data;?? ????data_remain?=?dev->count?-?*loff;?? ????if(MAX_SIMPLE_LEN?<?*loff)?? ????{?? ????????printk(KERN_ERR?"the?offset?is?illegal?in?func?%s?\n",__func__?);?? ????????return?-EINVAL;?? ????}?? ????else?if(data_remain?<=?0)?? ????{?? ????????printk(KERN_WARNING?"there?was?not?much?data?in?the?device\n");?? ????????return?0;?? ????}?? ????else?? ????{?? ????????if(count?>?data_remain)?? ????????{?? #if?SIMPLE_DEBUG?? ????????????printk(KERN_INFO?"the?data?is?less?than?the?user?want?to?read\n");?? #endif?? ????????????count?=?data_remain;?? ????????}?? ????????else?? ????????{?? ?????????? ????????}?? ????}?? ????err?=?copy_to_user(userstr,?(dev->data)+(*loff),?count);??? ????if(err?!=?0)?? ????{?? ????????printk(KERN_ERR?"an?error?occured?when?copy?data?to?user\n");?? ????????return?err;?? ????}?? ????else?? ????{?? ?????? #if?SIMPLE_DEBUG?? ????????printk(KERN_INFO?"data?copy?to?user?OK\n");?? #endif?? ????????*loff?=?*loff?+?count;??? ????????return?count;??? ????}?? }??
write方法:
與read類似 它是從用戶傳數(shù)據(jù)給設(shè)備驅(qū)動
從內(nèi)核空間看就是一個從用戶空間取數(shù)據(jù) 是一個from操作
[cpp]?view plaincopy
long?__must_check?strncpy_from_user(char?*dst,?const?char?__user?*src,?long?count)??
dst 驅(qū)動保存數(shù)據(jù)的地址
src 用戶空間傳入的數(shù)據(jù)
count 標(biāo)識數(shù)據(jù)長度
[cpp]?view plaincopy
static?ssize_t?simple_write(struct?file?*filp,?const?char?__user?*userstr,?size_t?count,?loff_t?*loff)?? {?? ????struct?simple_dev?*dev?=?NULL;?? ????int?err;?? ????int?remain_space?=?0;?? #if?SIMPLE_DEBUG?? ????printk(KERN_INFO?"In?%s\n",__func__);?? #endif?? ?? ????dev??????????=?filp->private_data;?? ????if(MAX_SIMPLE_LEN?<=?*loff)??? ????{?? ????????printk(KERN_ERR?"the?offset?is?illegal?in?func?%s\n",?__func__);?? ????????return?-EINVAL;?? ????}?? ????else?? ????{?? ????????remain_space?=?MAX_SIMPLE_LEN?-?*loff;?? ????????if(count?>?remain_space)?? ????????{?? #if?SIMPLE_DEBUG?? ????????????printk(KERN_WARNING?"the?data?is?to?long?to?write?to?the?device\n");?? #endif?? ????????????count?=?remain_space;?? ????????}?? ????????else?? ????????{?? ?????????????? ????????}?? ????}?? ????err?=?copy_from_user((dev->data)+(*loff),userstr,count);?? ????if(err?!=?0)?? ????{?? ????????printk(KERN_ERR?"an?error?occured?when?copy?data?from?user\n");?? ????????return?err;?? ????}?? ????else?? ????{?? ?????? #if?SIMPLE_DEBUG?? ????????printk(KERN_INFO?"data?copy?from?user?OK\n");?? #endif?? ????????*loff?=?*loff?+?count;??? ????????if(*loff?>?dev->count)?? ????????{?? ????????????dev->count?=?*loff;?? ????????}?? ????????else?? ????????{?? ?????????? ????????}?? ????????return?count;??? ????}?? }??
lseek方法:
根據(jù)用戶傳入的參數(shù)調(diào)整文件偏移
mode
| SEEK_SET | 從文件起始處開始偏移 |
| SEEK_CUR | 從文件當(dāng)前位置計算偏移 |
| SEEK_END | 從文件末尾計算偏移 |
file結(jié)構(gòu)的f_pos保存了文件的偏移量
在調(diào)整文件偏移后需要 更新file中得f_pos成員
[cpp]?view plaincopy
static?loff_t?simple_llseek(struct?file?*filp,?loff_t?loff,?int?mode)?? {?? ????struct?simple_dev?*dev?=?NULL;?? ????loff_t?tmp_len;?? #if?SIMPLE_DEBUG?? ????printk(KERN_INFO?"In?%s\n",__func__);?? #endif?? ?? ????dev??????????=?filp->private_data;?? ?? ????switch?(?mode?)?? ????{?? ????????case?SEEK_SET:?? ????????????if(?loff?<?0?)?? ????????????{?? ????????????????printk(KERN_ERR?"can't?move?above?file?line?%d?\n",?__LINE__);?? ????????????????return?-1;?? ????????????}?? ????????????else?if(loff?>?dev->count)?? ????????????{?? ????????????????printk(KERN_ERR?"offset?is?too?long?line?%d\n",?__LINE__);?? ????????????????return?-1;?? ????????????}?? ????????????else?? ????????????{?? ????????????????filp->f_pos?=?loff;?? ????????????}?? ????????????break;?? ????????case?SEEK_CUR:?? ????????????if((tmp_len?=?filp->f_pos+loff)?<?0)?? ????????????{?? ????????????????printk(KERN_ERR?"can't?move?above?file?line?%d?\n",?__LINE__);?? ????????????????return?-1;?? ????????????}?? ????????????else?if(tmp_len?>?dev->count)?? ????????????{?? ????????????????printk(KERN_ERR?"offset?is?too?long?line?%d\n",?__LINE__);?? ????????????????return?-1;?? ????????????}?? ????????????else?? ????????????{?? ????????????????filp->f_pos?=?tmp_len;?? ????????????}?? ????????????break;?? ????????case?SEEK_END:?? ????????????if((tmp_len?=?dev->count+loff?)?<?0)?? ????????????{?? ????????????????printk(KERN_ERR?"can't?move?above?file?line?%d?\n",?__LINE__);?? ????????????????return?-1;?? ????????????}?? ????????????else?if(tmp_len?>?dev->count)?? ????????????{?? ????????????????printk(KERN_ERR?"offset?is?too?long?line?%d\n",?__LINE__);?? ????????????????return?-1;?? ????????????}?? ????????????else?? ????????????{?? ????????????????filp->f_pos?=?tmp_len;?? ????????????}?? ????????????break;?? ????????default?:?? ????????????printk(KERN_INFO?"illigal?lseek?mode!?\n");?? ????????????return?-1;?? ????????????break;?? ????}?? ????return?filp->f_pos;?? }??
總結(jié)
以上是生活随笔為你收集整理的Linux驱动编程 step-by-step (五)主要的文件操作方法实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。