《字元装置驱动程式》PPT课件.ppt

上传人:wuy****n92 文档编号:79006213 上传时间:2023-03-19 格式:PPT 页数:53 大小:316.99KB
返回 下载 相关 举报
《字元装置驱动程式》PPT课件.ppt_第1页
第1页 / 共53页
《字元装置驱动程式》PPT课件.ppt_第2页
第2页 / 共53页
点击查看更多>>
资源描述

《《字元装置驱动程式》PPT课件.ppt》由会员分享,可在线阅读,更多相关《《字元装置驱动程式》PPT课件.ppt(53页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、Chap 3-字元裝置驅動程式OutlineIntroduction3.1 scull的設計藍圖3.2 主編號與次編號3.3 檔案架構3.4 file 結構3.5 open 與 release3.6 Scull 的記憶體用法規劃3.7 相競狀況3.8 read 與 write3.9 try scull3.10 devfs 檔案系統3.11 回溯相容性3.12 速查參考3-Introduction本章目標:寫出一個完整的字元裝置驅動程式(char device driver)-簡稱char driver終極目標:寫出一個模組化的char driver範例:scull-(Simple Charac

2、ter Utility for Loading Localities)Makefile,main.c,access.c,empty.c,pipe.c,scull.h,scull.init,scull_load,scull_unload,alpha.checkthemscull的作用是“讓使用者可把一塊記憶區當成字元裝置來使用”scull所驅動的目標裝置是一塊記憶區不需依賴任何“特殊”硬體只要有linux平台就可以編譯與執行未提供任何實用功能,只展示核心與char driver之間軟體介面3.1-scull的設計藍圖定義驅動程式要提供哪些功能給user-sapce的程式可循序存取(字元裝置)or

3、 可隨機存取(區塊裝置)模擬單一裝置(ex:一機多體 or 多個同類裝置)Scull所模擬出的每一種裝置,分別由不同類型的模組予以實現(相同的mechanism差別在於policy的不同)scull0scull3四個由記憶區所構成的裝置,兼具“共通”“持續”scullpipe0scullpipe3四個FIFO裝置(blocking與nonblocking)Scullsingle一次只容許一個被行程存取Scullpriv允許每個終端機都有權開啟一次,分屬不同行程Sculluid允許開始多次,限同一使用者。(回傳錯誤碼)Scullwuid允許開始多次,限同一使用者。(推延,等待)Ch_5.63.2

4、-主編號與次編號主編號(major number)(0255)代表裝置所配合的驅動程式當核心收到open()系統呼叫時,就是依據“主編號”來選擇驅動程式次編號(minor number)(0255)驅動程式以次編號來辨認同類裝置的個體核心本身用不到,只有驅動程式自己才知道次編號的意義當使用者要存取字元裝置時,必須透過檔案系統裡的“代表名稱”特殊檔(special file)、裝置檔(device file)、檔案系統樹的節點(node),集中在/dev/目錄下。裝置類型:“c”代表char driver的特殊檔“b”代表block driver的裝置檔3.2-主編號與次編號 ls al/dev

5、/|lessbrw-rw-1 root disk 66,72 Apr 11 2002 sdak8brw-rw-1 root disk 66,73 Apr 11 2002 sdak9crw-r-r-1 root root 253,1 Mar 1 22:58 scull1crw-r-r-1 root root 253,2 Mar 1 22:58 scull2crw-r-r-1 root root 253,3 Mar 1 22:58 scull3brw-rw-1 root disk 8,0 Apr 11 2002 sdacrw-rw-1 root uucp 154,18 Apr 11 2002 tt

6、ySR18裝置類型裝置類型主編號主編號次編號次編號代表名稱代表名稱3.2-主編號與次編號檔案系統製作裝置節點的命令是mknod,必須有特權身分(root)才能使用此工具。至少需要四個引數 mknod/dev/ant c 252 0像任何儲存在磁碟上的普通檔案一樣,mknod所產生的裝置節點會被保存下來,除非刻意刪除它們。用一般的rm命令即可辦到 rm/dev/ant3.2.1-隨機取得主編號大部份常見的裝置幾乎都有固定的主編號,可在核心源碼樹的Documentation/devices.txt檔案內找到一份“裝置-主編號”對照表。“實驗性或自家使用”的主編號:6063、120127、240.2

7、54“隨機索取主編號”呼叫register_chrdev()定義在extern int register_chrdev(unsigned int,const char*,struct file_operations*);extern int unregister_chrdev(unsigned int,const char*);3.2.1-隨機取得主編號在呼叫register_chrdev()時:major引數給0:=回傳值為0&0&回傳值為0表示核心同意你的要求發生錯誤時:=回傳值為負數如果你的驅動程式會被用於廣大群眾,或者有可能被納入正式核心,則須設法申請專用的主編號。3.2.1-隨機取得

8、主編號 cat/proc/devicesCharacter devices:1 mem 2 pty226 drm253 scull254 pcmciaBlock devices:2 fd 3 ide03.2.1-隨機取得主編號缺點:因為模組分配到的主編號不一定每次都一樣,所以無法事先建立裝置節點。因此,光靠insmod是不夠的,還必須查閱/proc/devices的內容後,找出其隨機取得的主編號,再製作成適當的裝置節點(甚至要刪除上次留下的無用節點)可寫成一個shell script來一次解決範例:scull_load =每次開機時透過/etc/rc.local來執行 scull_unload

9、=卸載模組及清理/dev/下的相關節點 scull.init =由系統開機/關機程序自動載入/卸載 模組。接受傳統命令start,stop,restartCh_11#!/bin/shmodule=scull“device=scullmode=664#Group:since distributions do it differently,look for wheel or use staffif grep staff:/etc/group /dev/null;then group=staffelse group=wheelfi#invoke insmod with all arguments w

10、e got#and use a pathname,as newer modutils dont look in.by default/sbin/insmod-f./$module.o$*|exit 1major=cat/proc/devices|awk$2=$module print$1#Remove stale nodes and replace them,then give gid and perms#Usually the script is shorter,its scull that has several devices in it.rm-f/dev/$device0-3mknod

11、/dev/$device0 c$major 0mknod/dev/$device1 c$major 1mknod/dev/$device2 c$major 2mknod/dev/$device3 c$major 3ln-sf$device0/dev/$devicechgrp$group/dev/$device0-3chmod$mode /dev/$device0-3#!/bin/shmodule=scull“device=scull#invoke rmmod with all arguments we got/sbin/rmmod$module$*|exit 1#Remove stale no

12、desrm-f/dev/$device/dev/$device0-3rm-f/dev/$deviceprivrm-f/dev/$devicepipe/dev/$devicepipe0-3rm-f/dev/$devicesinglerm-f/dev/$deviceuidrm-f/dev/$devicewuidscull_load&scull_unload也適用其他其他驅動程式,只需重新定義變數,並稍微調整mknod那幾行即可。因為指令稿需要特權身分才能執行,而新建出來的特殊檔自然會屬於root。但裝置節點的存取權限通常不是這樣,所以要修改權限的擁有權。3.2.2移除驅動程式在模組被卸載之前,他必

13、須先釋放主編號,而這動作可由unregister_chrdev()完成,應該在模組的清理函式理呼叫他:major表示要被釋放的主編號;name是當初註冊的名稱對於動態取得主編號的驅動程式,如果在卸載模組之後沒有順便除掉相關的/dev結點,就可能產生意料外的錯誤。譬如,已經沒有作用的/dev/framegrabber可能在一個月後指向火災警報器int unregister_chrdev(unsigned int major,const char*name)int unregister_chrdev(unsigned int major,const char*name):3.2.3-副編號(dev

14、_t&kdev_t)每當核心要呼叫驅動程式,都必須讓驅動程式知道,他應該作用在哪一個裝置上;驅動程式以裝置編號來辨認其作用對象。(裝置編號=主編號+次編號)dev_tUnix以dev_t(代表“device type”)作為裝置編號的資料型別定義在更改不易,因太多應用程式“知道”其內部結構kdev_tLinux核心內部採用另一種不同的資料型別定義在黑盒子每個核心函式都不必知道其內部結構,而應用程式可以不知道kdev_t的存在。=核心改版可以放手修正,不會影響任何驅動程式3.2.3-副編號(dev_t&kdev_t)可操作的kdev_t巨集與函式:MAJOR(kdev_t dev)=從kdev結

15、構取得主編號MINOR(kdev_t dev)=從kdev結構取得次編號MKDEV(int major,int minor)=以指定的主編號與次編號建立一個kdev_t結構kdev_t_to_nr(kdev_t dev)=將一個kdev_t結構轉換成一個數值(dev_t)to_kdev_t(int dev)=將一個數值轉換成kdev_t結構3.3檔案作業驅動程式內部以一個file結構來代表一個已開啟的裝置核心透過一個file_operations結構來存取驅動程式內部的作業函式(method)file結構包含一個f_op欄位,他是一個指向file_operations結構的指標file_ope

16、ration結構本身是由一系列“函式指標”所構成,這些指標分別指向驅動程式的各項作業函式。Ex:核心收到操作檔案的系統戶叫(ex:read(),依據系統呼叫指定的fd找出對應驅動程式的file結構,然後再循線從file_operations結構中找出對應於該系統呼叫的作業函式scull_read()Read()Scull_read()3.3檔案作業fops變數名稱用來表示file_operation結構(或是指向此結構的指標)在file_operations結構中的每一個欄位,都必須指向驅動程式中負責特定作業方法。對於驅動程式不支援的作業項目(method),其對應欄位就必須指向NULL,當核

17、心遇到NULL時會採取適當的預設行為。欄位的預設行為各不相同。每一種作業方法,其回傳值都有同樣的意義:0代表成功,負值為錯誤碼。3.3檔案作業struct module*owner;不同於file_operations結構裡的其它欄位,此欄位不是函式指標,而是一個指向結構所屬模組的指標。驅動程式本身用不到此欄位,因為他是供核心用來維護模組的用量計次(usage count)。loff_t(*llseek)(struct file*,loff_t,int);/seek作業方法的作用,是改變檔案目前的讀寫點位置;如果成功,則傳回新位置(正值),傳回值loff_t是一個至少64-bits“long

18、offset”,即使是在32-bits平台上也是如此。ssize_t(*read)(struct file*,char*,size_t,loff_t*);用於擷取出裝置上的資料。若將此欄位設定為NULL,則read()系統呼叫發生-EINVAL失敗。(Invalid argument,引數值無效),如果成功,則傳回一個非負數值,代表成功讀取的位元數。ssize_t(*write)(struct file*,const char*,size_t,loff_t*);將資料寫入裝置。若發生錯誤,則觸發write()系統呼叫的行程會收到-EINVAL。如果成功,write將傳回一個非負值,代表成功寫出

19、的位元數。int(*readdir)(struct file*,void*,filldir_t);對於裝置檔而言,此欄位必須是NULL,因為他是用來讀取目錄,而且只對檔案系統有意義。unsigned int(*poll)(struct file*,struct poll_table_struct*);poll作業方法是兩種系統呼叫的後台:poll()與select()-兩者都是用來探詢裝置在某特殊狀態下是法可讀或可寫,而且兩者都會等到目標裝置呈為可寫或可讀的狀態,才會返回。如果驅動程式沒定義自己poll作業方法,核心會預設目標裝置的性質為同時兼具可讀可寫,沒有特殊狀態。傳回值是一個代表裝置狀態

20、的位元遮罩(bit mask)。int(*mmap)(struct file*,struct vm_area_struct*);mmap用來將I/O memory對應到行程的位址空間。若驅動程式沒提供此方法,則mmap()系統呼叫將傳回-ENODEVint(*ioctl)(struct inode*,struct file*,unsigned int,unsigned long);每個裝置或多或少都有其特殊功能,標準的作業方法不見得能提供應用程式所需要的一切功能(ex:格式化),對於隨裝置而定的功能,應用程式可透過ioctl()系統呼叫來執行一系列裝置特有命令,而ioctl作業發法的任務,就是

21、實現在類特殊命令。int(*open)(struct inode*,struct file*);開啟,這必定是裝置檔操作程序的第一步,但是驅動程式並非一定要宣告一個對應方法不可。如果將此欄位指向NULL,開啟裝置的動作一定會成功,只不過驅動程式不會收到通知而已。int(*flush)(struct file*);這是行程在關閉其裝置檔之前的必要動作。flush應該執行任何還沒解決的作業程序。請不要將此方法與fsnc()系統呼叫聯想在一起。目前,只有NFS需要用到flush。如果讓flush指向NULL,不會有動作發生。int(*release)(struct inode*,struct fil

22、e*);在file結構被釋放之前,此方法會被呼叫。(並非,每次呼叫close()都會觸發release)int(*fsync)(struct inode*,struct dentry*,int);此為fsync()系統呼叫的實際後台,其作用是出清(flush)任何延滯的資料。如果驅動程式不提供此作業方法,系統呼叫會傳回-EINVALint(*fasync)(struct inode*,struct flie*,int);當裝置的FASYNC旗標出現變化時,核心就會呼叫驅動程式的fasync的作業方法。非同步通知(asynchronous notification)。int(*lock)(str

23、uct file*,int,struct file_lock*);Lock的作業方法用來實現檔案所訂的效果。對一般檔案而言,這項功能絕不可避免,但是幾乎沒有任何驅動程式時作此作業方法。ssize_t(*readv)(struct file*,const struct iovec*,unsigned long,loff_t*);ssize_t(*writev)(struct file*,const struct iovec*,unsigned long,loff_t*);以上兩者都是2.3版研發過程中才提出來的新作業發訪,分別用於實現“分散讀取(scatter read)”以及“累積寫出(gat

24、her write)”Ch_53.3檔案作業scull 驅動程式只實作最重要的幾項作業方法,而起GNU的標記語法來宣告初始話他自己的file_operations結構:scull/main.c:Line 551GNU C編譯器的優點:驅動程式的移植性較高程式碼精簡,容易理解在某些情況下,能得到相當程度的效能提升struct file_operations scull_fops=llseek:scull_llseek,read:scull_read,write:scull_write,ioctl:scull_ioctl,open:scull_open,release:scull_release,

25、;3.4file 結構File與一般應用程式常用的FILE毫無關係。File定義在C函式庫內,不可能出現在核心程式碼理,而file結構也絕不會出現在user-space應用程式理。struct file代表一個已開啟的檔案(open file),對於系統上每一個已開啟的檔案,在kernel-space裡都有一個對應的struct file。每一個file結構都是在核心收到open()系統呼叫時自動建立的,任何作用在檔案的作業方法與核心函式,都會收到該檔案的file結構。close()系統struct file不同於磁碟檔案在這裡file是結構,而filp是指向file結構的指標file=str

26、uct;filp=pointer3.4file 結構file結構的重要欄位:mode_t f_mode;代表檔案的“存取模式”,可能的存取方式包括“可讀”(FMODE_READ),”可寫”(FMODE_WRITE)或“可讀可寫”(FMODE_READ|FMODE_WRITE)。ioctl作業方法或許會想要檢查此欄位來確定讀寫權限,但是read,write則不需要執行權限檢查,因為核心在收到read(),write()系統呼叫時,就已經幫你檢查過了。loff_t f_pos;檔案目前的讀寫位置。loff_t是一個64bit值。若驅動程式想知道檔案目前的讀寫點位置,可以讀取此值,但是絕對不應該直接

27、修改此值。read,write作業方法應該透過它們從自己第三引數收到的指標(loff_t*)來更新讀寫點位置,而不是直接修改filp-f_pos.unsigned short f_flags;各項“檔案旗標”的組合,像是O_RDONLY,O_NONBLOCK與O_SYNC等等。驅動程式在進行非推延作業(nonblocking operation)時,會需要檢查這些旗標。(所有的旗標都定義在)struct file_operations*f_op;f_op為指向file_operation結構的指標。每當需要提調(dispatch)任何檔案作業時,核心會讀取此指標,找出對應的作業方法。核心決不會

28、快取filp-f_op的值f_op指向不同的作業方法,此技巧使得主編號相同的一系列檔案,可以表現出多種不同的行為模式,核心容許這種替換檔案作業方法的能力,相當於物件導向的“method overrideing”技術void*private_data;Private_data是相當有用的資源,可供我們保存生命其跨越多次系統呼叫的狀態資訊。驅動程式可以自由決定要如何運用此指標,甚至完全忽略,但必須記得在release method裡釋放掉此指標所占用的記憶體。3.4file 結構struct dentry*f_dentry;檔案所屬的“目錄項”結構(directory entry,通常簡稱為den

29、try)。目錄項的處理城係已經被最佳化了,所以通常不理會dentry結構,頂多也只是透過file-f_dentry-d_inode來存取inode結構而已。file結構實際的欄位超過以上所述。事實上驅動程式決不會自己填寫file結構,而只會存取其他地方產生的結構。3.5 open&releaseOpen作業方法提供驅動程式一個機會,為後續的作業進行任何必要的初始化準備工作。此外,open通常還會遞增目標裝置的“用量計次,以免模組檔案關閉之前被謝載。Release作業方法負責遞減用量計次(usage count)。MOD_INC_USE_COUNT:將目前模組使用次數加一MOD_DEC_USE_

30、COUNT:將目前模組使用次數減一3.5.1 open作業方法大部份驅動程式的open作業方法,應該執行下列動作:遞增用量計次檢查裝置特有的錯誤(ex:數據機佔線,沒放光碟片)如果目標裝置是第一次被開啟,則應該進行初始化程序辨認次編號,並更新f_op指標配置並裝填任何要放在filp-private_data的資料結構開啟的第一步是是要看目標裝置的次編號為何?i_rdevless main.c3.5.1 open作業方法核心不需要裝置的次編號,所以驅動程式可對次編號自由賦予任何意義。實務上,次編號常被用來區別“同類裝置的不同個體”或“同一個裝置的不同操作模式”。Ex:/dev/st0(次編號0)

31、與/dev/st1(次編號1)分別代表不同的SCSI磁碟機,但是/dev/nst0(次編號)128其實與/dev/st0是同一台機器,只不過操作模式不同而已(/dev/nst0被關閉之後不自動回帶)使用者可用自己慣用的名稱來代表常用裝置,也就是將常用裝置連結到另依各比較人性化的檔名。Ex:用/dev/cdrom代表/dev/hdc,用/dev/modem代表/dev/ttyS13.5.1 open作業方法scull驅動程式對於次編號的運用方式:高半位元組(bit7bit4)代表裝置的類型(個性)低半位元組(bit3bit0)用於辨別同類裝置的不同個體以scull為例:scull0與scullp

32、ipe0有不同的高半位元組-不同類型scull0與scull1則有不同的低半位元組-同類裝置在scull裡定義了TYPE與NUM巨集,分別用於從次編號取得裝置類型與個體編號。#define TYPE(dev)(MINOR(dev)4)/*高四位元高四位元*/#define NUM(dev)(MINOR(dev)&0 xf)/*低四位元低四位元*/對於每種裝置類型,scull分別定義一個專屬的file_operations結構,並使open將filp-f_op指向這些結構。以下示範scull驅動程式如何製作多個fops:struct file_operations*scull_fop_array

33、=&scull_fops,/*type 0*/&scull_priv_fops,/*type 1*/&scull_pipe_fops,/*type 2*/&scull_sngl_fops,/*type 3*/&scull_user_fops,/*type 4*/&scull_wusr_fops/*type 5*/;#define SCULL_MAX_TYPE 5/*scull_open依據依據TYPE(dev)來選擇來選擇/*要使用要使用fop_array裡的那一組裡的那一組fopsint scull_open(struct inode*inode,struct file*filp)int t

34、ype=TYPE(inode-i_rdev);略略 if(type SCULL_MAX_TYPE)return-ENODEV;filp-f_op=scull_fop_arraytype;略略一般而言,驅動程式不必呼叫自己的作業方法,因為fops主要是讓核心挑選適當的作業方法。但是,如果驅動程式必須在自己的open作業方法裡辨認裝置類型,或許會想要在修改了fops指標之後,刻意呼叫fops-open()一次。以下是scull_open()的實際作法:/scull/main.c:Line238int scull_open(struct inode*inode,struct file*filp)Sc

35、ull_Dev*dev;/*裝置資訊裝置資訊*/int num=NUM(inode-i_rdev);int type=TYPE(inode-i_rdev);/*如果如果private_data無效,那表示我們不是在使用無效,那表示我們不是在使用devfs /*所以可依據所以可依據type(次編號的高半位元次編號的高半位元)挑出新的挑出新的f_op if(!filp-private_data&type)if(type SCULL_MAX_TYPE)return-ENODEV;filp-f_op=scull_fop_arraytype;return filp-f_op-open(inode,fil

36、p);/*dispatch to specific open*/Scull_Dev是此驅動程式用來持有記憶區的資料結構scull_nr_devs 是可用裝置的數量scull_devices 實際指向Scull_Dev結構的指標陣列/*type 0,檢查裝置編號,檢查裝置編號(除非除非priate_data有效有效)*/dev=(Scull_Dev*)filp-private_data;if(!dev)if(num=scull_nr_devs)return-ENODEV;dev=&scull_devicesnum;filp-private_data=dev;/*供其他方法使用供其他方法使用*/*

37、在放心休息之前的必要動作在放心休息之前的必要動作*/MOD_INC_USE_COUNT;/*Before we maybe sleep*/*如果開啟模式是如果開啟模式是write-only,就將目標裝置的長度減到,就將目標裝置的長度減到0*/if(filp-f_flags&O_ACCMODE)=O_WRONLY)if(down_interruptible(&dev-sem)MOD_DEC_USE_COUNT;return-ERESTARTSYS;scull_trim(dev);/*忽略錯誤忽略錯誤*/up(&dev-sem);return 0;/*成功成功*/3.5.1 open作業方法這段程

38、式看起來似乎有點空洞?未實作任何處理硬體的動作,並未計算它目前被開啟了多少次?scull的本質被設計成通用,持續性的裝置核心能透過file_operations結構owner欄位來維護模組的用量計次,為何要再作一次同樣動作?為了與舊版核心相容為何要再write_only模式時將記憶區長度歸零?open是唯一會影響目標裝置的動作,除了write only之外的其他存取模式,open不必作任何事。根據設計理念,當scull存有大量資料時,若被較少量的資料蓋寫,則最後會只剩較短的記憶區,讓多的資料消失。3.5.2 release作業方法Release作業方法扮演的腳色正好與open相反。有時函式名稱

39、會是device_close(),而非device_release()。不管名稱如何,主要工作流程都一樣:釋放open配置給filp-private_data的任何東西在最後一次關閉時,將目標裝置關機遞減用量計次Int scull_release(struct inode*inode,struct file*filp)MOD_DEC_USE_COUNT;return 0;3.5.2 release作業方法如果再open有用遞增用量計次,則release就必須遞減之。因為只要用量計次沒歸零,核心就會拒絕卸載模組。有時候,一個檔案被開啟與被關閉的次數不一定相同,如何能夠確定用量計次一定正確?dup

40、()和fork()系統呼叫會直接建立以開檔案的副本,而不會啟動驅動程式的open,但這些副本在程式結束時卻會觸發close()系統呼叫ANS:並非每次close()系統呼叫都會觸發release作業方法。close()只有再真正需要釋放裝置上的資料時,才會去呼叫release這就是為何取名release而不是close的原因其它類型的裝置有各自的關閉函式,因為scull_open()已經依據裝置類型,讓filp-f_op指向該類裝置的作業方法。3.6 的記憶體用法規劃本節只專注scull的記憶體配置策略,而不涉及真實驅動程式所需的硬體管理計技巧(Ch8&9)scull裝置是一個長度可隨資料量而

41、變的記憶區寫入的資料越多,長度資然增加如果用較短的資料去蓋寫它,長度也會真著縮減不限制“裝置”區的大小在結構上,每個scull裝置都是一個“指標鏈結串列”(a linked list of pointers),每個指標分別指向一個Scull_Dev結構(最多可代表四百萬位元組)。這個陣列含有1000個指標 配額集(quantum set)每個指標指向一個4000bytes大小的區域 配額(quantum)3.6 的記憶體用法規劃配額與配額集應該佔用多少記憶量才合適?是操作策略(policy)上的問題,與機制(machanism)無關。“擬定合理的預設值,同時提供使用者修改的彈性”Scull容許

42、使用者以多種方式來修改配額量:編譯期:修改scull.h內的SCULL_QUANTUM與SCULL_QSET 常數值。模組載入期:scull_quantum與scull_qset變數值。執行期:使用ioctl()修改目前設定值與預設值。如何決定預設值?“半滿配額與配額集所浪費掉的記憶量”與“如果配額量太小,以致於需要釋放原有記憶體,重新配置,重建串鏈所造成的效率損耗”兩者之間找出平衡點。kmalloc()的內部設計也應該列入考量Scull_DevnextdataScull_DevnextdataScull_Devnextdata配額集配額集個別配額個別配額scull scull 裝置裝置裝置裝

43、置的記憶佈局的記憶佈局的記憶佈局的記憶佈局3.6 的記憶體用法規劃Scull用來保存裝置資訊的資料結構:Scull_trim()函式負責釋放整各資料區:write-only模式scull_open()時,它就是以scull_trim()來清空舊資料。typedef struct Scull_Dev void*data;struct Scull_Dev*next;/*next listitem*/int quantum;/*the current quantum size*/int qset;/*the current array size*/unsigned long size;devfs_h

44、andle_t handle;/*only used if devfs is there*/unsigned int access_key;/*used by sculluid and scullpriv*/struct semaphore sem;/*mutual exclusion semaphore */Scull_Dev;int scull_trim(Scull_Dev*dev)/*遊走一遍整各串列遊走一遍整各串列*/*並釋放所遇到的任何配額與配額集並釋放所遇到的任何配額與配額集*/Scull_Dev*next,*dptr;int qset=dev-qset;/*dev is not-

45、null*/int i;for(dptr=dev;dptr;dptr=next)/*all the list items*/if(dptr-data)for(i=0;i datai)kfree(dptr-datai);kfree(dptr-data);dptr-data=NULL;next=dptr-next;if(dptr!=dev)kfree(dptr);/*all of them but the first*/dev-size=0;dev-quantum=scull_quantum;dev-qset=scull_qset;dev-next=NULL;return 0;3.7相競狀況A與B

46、都開啟了同一個scull裝置來寫入資料,並同時將自己的資料寫到scull記憶區的末端相競狀況(race condition),同時競爭相同資源。單一CPU&SMP系統上Linux 核心提供了多項機制來迴避權狀機制(struct semaphore)驅動程式必須依照此程式介面來使用在scull,各裝置獨自配置一個權狀並儲存在Scull_Dev結構裡設定權狀初值的函式是sema_init()權狀初值必須為1表示目前在開放狀態取得權狀的功能稱為P=down_interruptible()&down()釋放權狀的功能稱為V=up(&sem)3.7相競狀況權狀的使用:受權狀保護的資料,必須被明確定義不能

47、同時使用兩個試圖取得權狀的函式(ex:down_interruptible()&down(),否則會造成死結(deadlock)3.8read&writefilp檔案指標buff引數指向user-space的緩衝區count要被傳輸的資料量offp是一個指向“long offset type”的物件指標,代表使用者正在存取的檔案位置ssize_t read(struct file*filp,char*buff,size_t count,loff_t*offp);ssize_t write(struct file*filp,const char*buff,size_t count,loff_t*

48、offp);3.8read&write“跨空間”資料傳輸(在kernel-space與user-space之間傳遞資料)scull的read作業方法,需有可將整段資料寫到user-sapce的能力。write作業方法,需要能從kernel-space讀取一整段資料的能力。由以下兩個核心函式(具有檢查功能)提供:預防動作必須注意(user-space有換出功能-swapped out)unsigned long copy_to_user(void*to,const void*from,unsigned long count);unsigned long copy_from_user(void*t

49、o,const void*from,unsigned long count);ssize_t dev_read(struct file*filestruct file*file,char*bufchar*buf,size_t countsize_t count,loff_t loff_t*ppos*ppos);struct filef_countf_flagsf_modef_pos.Buffer(in the Kernel-space)Buffer(in the user-spaceorLibc)Copy_to_user()Read Read 作業方法作業方法作業方法作業方法的引數之應用的引數

50、之應用的引數之應用的引數之應用核心空間核心空間(不可置換不可置換)使用者空間使用者空間(可被置換可被置換)3.8.1read 作業方法若傳回值等於當初傳給read()系統呼叫的count引數,那表示當初要求傳輸的資料已經全數傳輸成功了。若傳回值大於0,但小於count,則表示只順利傳輸了部份資料,原因多與目標裝置有關,通常應用程式會再嘗試重新讀取。若傳回值為0。代表已經遇到檔案尾端(EOF)。若傳回值為-1(不可能有其他負數),則代表發生了某種錯誤。3.8.2write 作業方法若傳回值等於count,表示要求的資料量已經全數被寫入裝置。若傳回值為正數值,但小於count,表示只有部份資料被寫

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 大学资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁