到此我們已經快速瀏覽了這些成員, 我們開始在真實的scull 函數中使用它們.
3.5.1. open 方法
open 方法提供給 driver來做任何的初始化來準備後續的操作. 在大部分 driver中, open 應當進行下面的工作:
檢查設備特定的錯誤(例如設備沒準備好, 或者類似的硬件錯誤
如果它第一次打開, 初始化設備
如果需要的話, 更新f_op pointer.
分配並填充要放進filp->private_data 的任何資料結構
但是, 事情的第一步常常是確定打開哪個設備. 記住open 方法的原型是:
int (*open)(struct inode *inode, struct file *filp);
inode 參數有我們需要的信息,以它的i_cdev 成員的形式, 裡面包含我們之前建立的cdev 結構. 唯一的問題是通常我們不想要cdev 結構本身, 我們需要的是包含cdev 結構的scull_dev 結構. C 語言使程序員玩弄各種技巧來做這種轉換; 但是, 這種技巧編程是易出錯的, 並且導致別人難於閱讀和理解代碼. 幸運的是, 在這種情況下, kernal hacker 已經為我們實現了這個技巧, 以container_of 巨集的形式, 在<linux/kernel.h> 中定義:
container_of(pointer, container_type, container_field);
這個巨集使用一個指向container_field 類型的成員的pointer, 它在一個container_type 類型的結構中, 並且return一個pointer指向包含結構. 在scull_open, 這個巨集用來找到適當的設備結構:
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
一旦它找到scull_dev 結構, scull 在file structure的private_data 成員中存儲一個它的pointer, 為以後更易存取.
識別打開的設備的另外的方法是查看存儲在inode 結構的minor number. 如果你使用register_chrdev 註冊你的設備, 你必須使用這個技術. 確認使用iminor 從inode 結構中獲取次編號, 並且確定它對應一個你的 driver真正準備好處理的設備.
scull_open 的代碼(稍微簡化過)是:
int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
/* now trim to 0 the length of the device if open was write-only */
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
{
scull_trim(dev); /* ignore errors */
}
return 0; /* success */
}
代碼看來相當稀疏, 因為在調用open 時它沒有做任何特別的設備處理. 它不需要, 因為scull 設備設計為全局的和永久的. 特別地, 沒有如"在第一次打開時初始化設備"等動作, 因為我們不為scull 保持打開 counter.
唯一在設備上的真實操作是當設備為寫而打開時將它截取為長度為0. 這樣做是因為, 在設計上, 用一個短的文件覆蓋一個scull 設備導致一個短的設備數據區. 這類似於為寫而打開一個常規文件, 將其截短為0. 如果設備為讀而打開, 這個操作什麼都不做.
在我們查看其他scull 特性的代碼時將看到一個真實的初始化如何起作用的.
3.5.2. release 方法
release 方法的角色是open 的反面. 有時你會發現方法的實現稱為device_close, 而不是device_release. 任一方式, 設備方法應當進行下面的任務:
釋放open 分配在filp->private_data 中的任何東西
在最後的close 關閉設備
scull的基本形式沒有硬件去關閉,因此需要的代碼是最少的: [ 12 ]
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}
你可能想知道當一個設備文件關閉次數超過它被打開的次數會發生什麼. 畢竟, dup 和fork system calls不調用open 來創建打開文件的拷貝; 每個拷貝接著在程序終止時被關閉. 例如,大部分程序不打開它們的stdin 文件(或設備), 但是它們都以關閉它結束. driver如何知道一個打開的設備文件已經真正被關閉?
答案簡單: 不是每個close system call引起調用release 方法. 只有真正釋放設備資料結構的system call會調用這個方法-- 因此得名. 內核維持一個file structure被使用多少次的 counter. fork 和dup 都不創建新文件(只有open 這樣); 它們只遞增正存在的結構中的 counter. closesystem call僅在file structure counter掉到0 時執行release 方法, 這在結構被銷毀時發生. release 方法和close system call之間的這種關係保證了你的 driver一次open 只看到一次release.
注意, flush 方法在每次應用程序調用close 時都被調用. 但是, 很少 driver實現flush, 因為常常在close 時沒有什麼要做, 除非調用release.
如你會想到的, 前面的討論即便是應用程序沒有明顯地關閉它打開的文件也適用: 內核在進程exit 時自動關閉了任何文件, 通過在內部使用close system call.
[ 12 ]其他風味的設備由不同的函數關閉,因為scull_open為每個設備替換了不同的filp->f_op.我們在介紹每種風味時再討論它們.
沒有留言:
張貼留言