生活随笔
收集整理的這篇文章主要介紹了
Linux驱动编程 step-by-step (五)主要的文件操作方法实现
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
主要的文件操作方法實(shí)現(xiàn)
文件操作函數(shù)有很多的操作接口,驅(qū)動(dòng)編程需要實(shí)現(xiàn)這些接口,在用戶編程時(shí)候系統(tǒng)調(diào)用時(shí)候會(huì)調(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?*);?? ????...?? };??
以上只列出了主要的操作,下面會(huì)依次介紹: 本次的測(cè)試代碼上傳在:char_step2
結(jié)構(gòu)體:
首先 我們會(huì)模擬寫一個(gè)不操作任何設(shè)備,而僅僅是存儲(chǔ)的一個(gè)驅(qū)動(dòng)。 定義自己的一個(gè)結(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是一個(gè)信號(hào)量鎖,在以后的編程中使用, 之后的程序中結(jié)構(gòu)體也會(huì)做相應(yīng)的變化,以適應(yīng)linux編寫驅(qū)動(dòng)的習(xí)慣
open方法:
打開設(shè)備并進(jìn)一步初始化工作,在沒有定義open方法時(shí)內(nèi)核以一種默認(rèn)的方式打開設(shè)備,保證每次都能正確打開。 open方法中有有struct inode參數(shù),包含了設(shè)備號(hào),程序中可以使用次設(shè)備號(hào)得到正操作的設(shè)備 在struct file中主要的操作是private_data指針,他可以傳遞任何自己創(chuàng)建的結(jié)構(gòu)。 總得說來(lái)open方法的作用有3 1、獲得操作的設(shè)備(通過設(shè)備號(hào)) 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方法:
主要是對(duì)open進(jìn)一步初始化的操作的反操作 比如open時(shí)候分配了內(nèi)存,在release時(shí)就需要釋放它等 例子中因?yàn)椴僮鲀?nèi)存設(shè)備,故在release時(shí)無(wú)需做什么事
read方法:
read 是把設(shè)備中的數(shù)據(jù)傳遞給調(diào)用者 主要步驟 1、檢測(cè)偏移量有效(有些設(shè)備驅(qū)動(dòng)不需要檢測(cè)) 2、檢測(cè)用戶空間地址有效 3、將數(shù)據(jù)傳給用戶(在此步驟中調(diào)用的函數(shù)可能會(huì)自己檢測(cè)步驟2) 4、調(diào)整偏移量 5、返回讀到的數(shù)據(jù)長(zhǎng)度 (read write 用法相對(duì)靈活,不要依賴上邊的步驟,設(shè)備驅(qū)動(dòng)程序要根據(jù)設(shè)備特性去設(shè)計(jì)此方法) 這里先介紹一個(gè)會(huì)檢測(cè)用戶空間地址是否有效的copy函數(shù) 用戶調(diào)用read讀設(shè)備,而在內(nèi)核空間就是將數(shù)據(jù)傳給用戶,是一個(gè)to的操作
[cpp] ?view plaincopy
unsigned? long ?__must_check?copy_to_user( void ?__user?*to,? const ? void ?*from,?unsigned? long ?n)??
__must_check表述必須檢測(cè)其返回值,操作成功返回0,不成功返回負(fù)的錯(cuò)誤碼 to是用戶空間指針 也就是read函數(shù)傳入的用戶空間的指針, from指向設(shè)備要傳送的數(shù)據(jù)
n標(biāo)識(shí)傳入長(zhǎng)度 上圖是 摘自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ū)動(dòng) 從內(nèi)核空間看就是一個(gè)從用戶空間取數(shù)據(jù) 是一個(gè)from操作
[cpp] ?view plaincopy
long ?__must_check?strncpy_from_user( char ?*dst,? const ? char ?__user?*src,? long ?count)??
dst 驅(qū)動(dòng)保存數(shù)據(jù)的地址 src 用戶空間傳入的數(shù)據(jù) count 標(biāo)識(shí)數(shù)據(jù)長(zhǎng)度
[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)前位置計(jì)算偏移 SEEK_END 從文件末尾計(jì)算偏移
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 (五)主要的文件操作方法实现 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。