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

歡迎訪問 生活随笔!

生活随笔

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

linux

Linux驱动编程 step-by-step (五)主要的文件操作方法实现

發(fā)布時(shí)間:2024/9/21 linux 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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);//獲得操作的設(shè)備的次設(shè)備號(hào)??
  • ????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];//獲得真正操作的設(shè)備??
  • ????/*?進(jìn)一步?初始化設(shè)備?因?yàn)槭遣僮饕粋€(gè)模擬的設(shè)備?故省去*/??
  • ????filp->private_data?=?temp_dev;?//初始化?private_data??
  • ??
  • ????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)//檢測(cè)偏移量??
  • ????{??
  • ????????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);?//調(diào)用內(nèi)核函數(shù)進(jìn)行數(shù)據(jù)拷貝,它會(huì)檢測(cè)用戶地址是否有效??
  • ????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;?//調(diào)整偏移量??
  • ????????return?count;?//返回寫入的數(shù)據(jù)量??
  • ????}??
  • }??

  • 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)?//檢測(cè)偏移量??
  • ????{??
  • ????????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);//取得數(shù)據(jù)??
  • ????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;?//返回寫入的數(shù)據(jù)量??
  • ????}??
  • }??

  • 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ò),歡迎將生活随笔推薦給好友。