字元装置驱动程式_第1页
字元装置驱动程式_第2页
字元装置驱动程式_第3页
字元装置驱动程式_第4页
字元装置驱动程式_第5页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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

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

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

4、誤碼) Scullwuid允許開始多次,限同一使用者。(推延,等待),Ch_5.2.5,Ch_5.6,3.2-主編號與次編號,主編號(major number)(0255) 代表裝置所配合的驅動程式 當核心收到open()系統呼叫時,就是依據“主編號”來選擇驅動程式 次編號(minor number)(0255) 驅動程式以次編號來辨認同類裝置的個體 核心本身用不到,只有驅動程式自己才知道次編號的意義 當使用者要存取字元裝置時,必須透過檔案系統裡的“代表名稱”特殊檔(special file) 、裝置檔(device file) 、 檔案系統樹的節點(node),集中在/dev/目錄下。 裝置

5、類型: “c”代表char driver的特殊檔 “b”代表block driver的裝置檔,3.2-主編號與次編號, ls al /dev/ |less,brw-rw- 1 root disk 66, 72 Apr 11 2002 sdak8 brw-rw- 1 root disk 66, 73 Apr 11 2002 sdak9 crw-r-r- 1 root root 253, 1 Mar 1 22:58 scull1 crw-r-r- 1 root root 253, 2 Mar 1 22:58 scull2 crw-r-r- 1 root root 253, 3 Mar 1 22:5

6、8 scull3 brw-rw- 1 root disk 8, 0 Apr 11 2002 sda crw-rw- 1 root uucp 154, 18 Apr 11 2002 ttySR18,3.2-主編號與次編號,檔案系統製作裝置節點的命令是mknod,必須有特權身分(root)才能使用此工具。至少需要四個引數 mknod /dev/ant c 252 0 像任何儲存在磁碟上的普通檔案一樣,mknod所產生的裝置節點會被保存下來,除非刻意刪除它們。用一般的rm命令即可辦到 rm /dev/ant,3.2.1-隨機取得主編號,大部份常見的裝置幾乎都有固定的主編號,可在核心源碼樹的Docum

7、entation/devices.txt檔案內找到一份“裝置-主編號”對照表。 less /usr/src/linux-2.4.20/Documentation/devices.txt “實驗性或自家使用”的主編號: 6063 、 120127、240.254 “隨機索取主編號”呼叫register_chrdev() 定義在 less /usr/src/linux-2.4.20/include/linux/fs.h,extern int register_chrdev(unsigned int, const char *, struct file_operations *); extern i

8、nt unregister_chrdev(unsigned int, const char *);,3.2.1-隨機取得主編號,在呼叫register_chrdev()時: major引數給0: =回傳值為0 then group=staff else group=wheel fi # invoke insmod with all arguments we got # and use a pathname, as newer modutils dont look in . by default /sbin/insmod -f ./$module.o $* | exit 1 major=cat

9、/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-3 mknod /dev/$device0 c $major 0 mknod /dev/$device1 c $major 1 mknod /dev/$device2 c $major 2 mknod /

10、dev/$device3 c $major 3 ln -sf $device0 /dev/$device chgrp $group /dev/$device0-3 chmod $mode /dev/$device0-3,#!/bin/sh module=scull“ device=scull # invoke rmmod with all arguments we got /sbin/rmmod $module $* | exit 1 # Remove stale nodes rm -f /dev/$device /dev/$device0-3 rm -f /dev/$devicepriv r

11、m -f /dev/$devicepipe /dev/$devicepipe0-3 rm -f /dev/$devicesingle rm -f /dev/$deviceuid rm -f /dev/$devicewuid,scull_load 不同於file_operations結構裡的其它欄位,此欄位不是函式指標,而是一個指向結構所屬模組的指標。驅動程式本身用不到此欄位,因為他是供核心用來維護模組的用量計次(usage count)。 loff_t (*llseek) (struct file *, loff_t, int); /seek作業方法的作用,是改變檔案目前的讀寫點位置;如果成功

12、,則傳回新位置(正值),傳回值loff_t是一個至少64-bits “long 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 *); 將資料寫入裝置。若

13、發生錯誤,則觸發write()系統呼叫的行程會收到-EINVAL。如果成功,write將傳回一個非負值,代表成功寫出的位元數。 int (*readdir) (struct file *,void *,filldir_t); 對於裝置檔而言,此欄位必須是NULL,因為他是用來讀取目錄,而且只對檔案系統有意義。 unsigned int (*poll) (struct file *, struct poll_table_struct *); poll作業方法是兩種系統呼叫的後台:poll()與select()-兩者都是用來探詢裝置在某特殊狀態下是法可讀或可寫,而且兩者都會等到目標裝置呈為可寫或可

14、讀的狀態,才會返回。如果驅動程式沒定義自己poll作業方法,核心會預設目標裝置的性質為同時兼具可讀可寫,沒有特殊狀態。傳回值是一個代表裝置狀態的位元遮罩(bit mask)。 int (*mmap) (struct file *, struct vm_area_struct *); mmap用來將I/O memory對應到行程的位址空間。若驅動程式沒提供此方法,則mmap()系統呼叫將傳回-ENODEV,int (*ioctl) (struct inode *, struct file *, unsigned int,unsigned long); 每個裝置或多或少都有其特殊功能,標準的作業方

15、法不見得能提供應用程式所需要的一切功能(ex:格式化),對於隨裝置而定的功能,應用程式可透過ioctl()系統呼叫來執行一系列裝置特有命令,而ioctl作業發法的任務,就是實現在類特殊命令。 int (*open) (struct inode *, struct file *); 開啟,這必定是裝置檔操作程序的第一步,但是驅動程式並非一定要宣告一個對應方法不可。如果將此欄位指向NULL,開啟裝置的動作一定會成功,只不過驅動程式不會收到通知而已。 int (*flush) (struct file *); 這是行程在關閉其裝置檔之前的必要動作。flush應該執行任何還沒解決的作業程序。請不要將此

16、方法與fsnc()系統呼叫聯想在一起。目前,只有NFS需要用到flush。如果讓flush指向NULL,不會有動作發生。 int (*release) (struct inode *, struct file *); 在file結構被釋放之前,此方法會被呼叫。(並非,每次呼叫close( )都會觸發release),int (*fsync) (struct inode *, struct dentry *, int); 此為fsync( )系統呼叫的實際後台,其作用是出清(flush)任何延滯的資料。如果驅動程式不提供此作業方法,系統呼叫會傳回-EINVAL int (*fasync) (st

17、ruct inode *, struct flie *, int); 當裝置的FASYNC旗標出現變化時,核心就會呼叫驅動程式的fasync的作業方法。非同步通知(asynchronous notification)。 int (*lock) (struct file *, int, struct file_lock *); Lock的作業方法用來實現檔案所訂的效果。對一般檔案而言,這項功能絕不可避免,但是幾乎沒有任何驅動程式時作此作業方法。 ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t

18、 *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); 以上兩者都是2.3版研發過程中才提出來的新作業發訪,分別用於實現“分散讀取(scatter read)”以及“累積寫出(gather write)”,Ch_5,3.3檔案作業,scull 驅動程式只實作最重要的幾項作業方法,而起GNU的標記語法來宣告初始話他自己的file_operations結構:scull/main.c:Line 551 GNU C編譯器的優點: 驅動程式的移植性較高 程式碼精簡,容易理解 在某些情況下

19、,能得到相當程度的效能提升,struct file_operations scull_fops = llseek: scull_llseek, read: scull_read, write: scull_write, ioctl: scull_ioctl, open: scull_open, release: scull_release,;,3.4file 結構,File與一般應用程式常用的FILE毫無關係。File定義在C函式庫內,不可能出現在核心程式碼理,而file結構也絕不會出現在user-space應用程式理。 struct file代表一個已開啟的檔案(open file),對於系

20、統上每一個已開啟的檔案,在kernel-space裡都有一個對應的struct file。每一個file結構都是在核心收到open()系統呼叫時自動建立的,任何作用在檔案的作業方法與核心函式,都會收到該檔案的file結構。close()系統 struct file不同於磁碟檔案 在這裡file是結構,而filp是指向file結構的指標 file = struct ; filp = pointer,3.4file 結構,file結構的重要欄位: mode_t f_mode; 代表檔案的“存取模式”,可能的存取方式包括“可讀”(FMODE_READ),”可寫”(FMODE_WRITE)或“可讀可寫

21、”(FMODE_READ | FMODE_WRITE)。ioctl作業方法或許會想要檢查此欄位來確定讀寫權限,但是read,write則不需要執行權限檢查,因為核心在收到read(),write()系統呼叫時,就已經幫你檢查過了。 loff_t f_pos; 檔案目前的讀寫位置。loff_t是一個64bit值。若驅動程式想知道檔案目前的讀寫點位置,可以讀取此值,但是絕對不應該直接修改此值。read, write作業方法應該透過它們從自己第三引數收到的指標(loff_t *)來更新讀寫點位置,而不是直接修改filp-f_pos.,unsigned short f_flags; 各項“檔案旗標”的

22、組合,像是O_RDONLY, O_NONBLOCK與O_SYNC等等。驅動程式在進行非推延作業(nonblocking operation)時,會需要檢查這些旗標。(所有的旗標都定義在) struct file_operations *f_op; f_op為指向file_operation結構的指標。每當需要提調(dispatch)任何檔案作業時,核心會讀取此指標,找出對應的作業方法。核心決不會快取filp-f_op的值f_op指向不同的作業方法,此技巧使得主編號相同的一系列檔案,可以表現出多種不同的行為模式,核心容許這種替換檔案作業方法的能力,相當於物件導向的“method override

23、ing”技術 void *private_data; Private_data是相當有用的資源,可供我們保存生命其跨越多次系統呼叫的狀態資訊。驅動程式可以自由決定要如何運用此指標,甚至完全忽略,但必須記得在release method裡釋放掉此指標所占用的記憶體。,3.4file 結構,struct dentry *f_dentry; 檔案所屬的“目錄項”結構(directory entry,通常簡稱為dentry) 。目錄項的處理城係已經被最佳化了,所以通常不理會dentry結構,頂多也只是透過file-f_dentry-d_inode來存取inode結構而已。 file結構實際的欄位超過以

24、上所述。事實上驅動程式決不會自己填寫file結構,而只會存取其他地方產生的結構。,3.5 open /* 裝置資訊 */ int num = NUM(inode-i_rdev); int type = TYPE(inode-i_rdev); /*如果private_data無效,那表示我們不是在使用devfs /*所以可依據type(次編號的高半位元)挑出新的f_op if (!filp-private_data /* dispatch to specific open */ ,Scull_Dev是此驅動程式用來持有記憶區的資料結構 scull_nr_devs 是可用裝置的數量 scull_d

25、evices 實際指向Scull_Dev結構的指標陣列,/*type 0,檢查裝置編號(除非priate_data有效)*/ dev = (Scull_Dev *)filp-private_data; if (!dev) if (num = scull_nr_devs) return -ENODEV; dev = /*成功*/ ,3.5.1 open作業方法,這段程式看起來似乎有點空洞? 未實作任何處理硬體的動作, 並未計算它目前被開啟了多少次? scull的本質被設計成通用,持續性的裝置 核心能透過file_operations結構owner欄位來維護模組的用量計次,為何要再作一次同樣動作?

26、 為了與舊版核心相容 為何要再write_only模式時將記憶區長度歸零? open是唯一會影響目標裝置的動作,除了write only之外的其他存取模式,open不必作任何事。 根據設計理念,當scull存有大量資料時,若被較少量的資料蓋寫,則最後會只剩較短的記憶區,讓多的資料消失。,3.5.2 release作業方法,Release作業方法扮演的腳色正好與open相反。有時函式名稱會是device_close(),而非device_release()。不管名稱如何,主要工作流程都一樣: 釋放open配置給filp-private_data的任何東西 在最後一次關閉時,將目標裝置關機 遞減用

27、量計次 ,Int scull_release (struct inode *inode, struct file *filp) MOD_DEC_USE_COUNT; return 0; ,3.5.2 release作業方法,如果再open有用遞增用量計次,則release就必須遞減之。因為只要用量計次沒歸零,核心就會拒絕卸載模組。 有時候,一個檔案被開啟與被關閉的次數不一定相同,如何能夠確定用量計次一定正確? dup( )和fork( )系統呼叫會直接建立以開檔案的副本,而不會啟動驅動程式的open,但這些副本在程式結束時卻會觸發close( )系統呼叫 ANS:並非每次close( )系統呼

28、叫都會觸發release作業方法。close( )只有再真正需要釋放裝置上的資料時,才會去呼叫release這就是為何取名release而不是close的原因 其它類型的裝置有各自的關閉函式,因為scull_open( )已經依據裝置類型,讓filp-f_op指向該類裝置的作業方法。,3.6 的記憶體用法規劃,本節只專注scull的記憶體配置策略,而不涉及真實驅動程式所需的硬體管理計技巧(Ch8 struct Scull_Dev *next; /* next listitem */ int quantum; /* the current quantum size */ int qset; /*

29、 the current array size */ unsigned long size; devfs_handle_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

30、_Dev *next, *dptr; int qset = dev-qset; /* dev is not-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

31、-size = 0; dev-quantum = scull_quantum; dev-qset = scull_qset; dev-next = NULL; return 0; ,3.7相競狀況,A與B都開啟了同一個scull裝置來寫入資料,並同時將自己的資料寫到scull記憶區的末端 相競狀況(race condition),同時競爭相同資源。 單一CPU ssize_t write(struct file *filp, const char *buff, size_t count, loff_t *offp);,3.8read unsigned long copy_from_user(void *to, const void *from, unsigned long count );,ssize_t dev_read( struct file *file, char *buf, size_t

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论