註冊裝置編號只是孵出驅動程式的第一步而已,在進一步研究驅動程式的實際內涵之前,必須先認識核心提供給字元驅動程式的API,也就是驅動程式會用到的資料結構。驅動程式的基本操作,多半涉及核心的三種重要的資料結構,分別是file_operations、file與inode。對這些結構的基本認識,是驅動程式設計者的基本素養。
檔案作業(file_operations)
到目前為止我們已取得了一組可供運用的裝置編號,但還沒將這些編號"連結"到驅動程式的任何作業功能。對於字元驅動程式而言,擔任此一連結工作的便是file_operations結構;此結構定義於
習慣上,指向file_operations結構的指標(或者該型別的變數),通常命名為fops(或者類似的名稱)。此結構裡的每個欄位,都必須指向驅動程式中實作該欄位所象徵的功能的函式,不然就得指向NULL,藉此表示驅動程式不支援該向操作功能。
file_operations結構所宣告的作業函式原型中,loff_t
參數:是一個long offset , 其長度最少有64bits,__user
參數:這是一種另類的註解,註明該指標是指向user-space位址,不可直接取值。
struct file_operations {
struct module *owner;
/*此欄位的作用,是避免模組仍在活動中時,被卸載出核心*/
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 *);
/*寫出作業,傳送資料到裝置上。正的回傳值代表已經成功寫出的位元組個數。*/
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
/*異步讀取作業;類似讀取作業,但是不一定能在函式返回之前傳輸完成所有要讀取的資料*/
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
/*異步寫出作業;類似寫出作業,但是不一定能在函式返回之前傳輸完成所有要寫出的資料*/
int (*readdir) (struct file *, void *, filldir_t);
/*讀取檔案系統上的目錄。對裝置檔而言,此指標應指向NULL。*/
unsigned int (*poll) (struct file *, struct poll_table_struct *);
/*查詢裝置的I/O狀態。*/
int (*ioctl) (struct inode *, struct file*, unsigned int,unsigned long);
/*執行"裝置專屬指令"(device-specific command)*/
int (*mmap) (struct file *, struct vm_area_struct *);
/*記憶體映射操作(memory mapping)*/
int (*open) (struct inode *, struct file *);
/*開啟檔案,作用在裝置檔上的第一項操作*/
int (*flush) (struct file *, fl_owner_t id);
/*完工作業。*/
int (*release) (struct inode *, struct file *);
/*釋放裝置檔*/
int (*fsync) (struct file *, int datasync);
/*出清留滯資料。*/
int (*aio_fsync) (struct kiocb *, int datasync);
/*fsync作業方法得異版本。
int (*fasync) (int, struct file *, int);
/*異步同步作業。*/
int (*lock) (struct file *, int, struct file_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 *);
/*這些方法實現發散/匯聚讀和寫操作. 應用程式偶爾需要做一個包含多個內存區的單個讀或
寫操作;這些系統調用允許它們這樣做而不必對數據進行額外拷貝. 如果這些函數指針為 NULL
, read 和 write 方法被調用( 可能多於一次 )*/
ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);
/*使用最少的拷貝從一個文件描述(FD)符搬移數據到另一個文件描述*/
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
/*sendpage 是 sendfile 的另一半; 它由內核調用來發送數據, 一次一頁,
到對應的文件. 設備驅動實際上不實現 sendpage。*/
unsigned long (*get_unmapped_area)(struct file *, unsigned long,
unsigned long, unsigned long, unsigned long);
/*這個方法的目的是在進程的地址空間找一個合適的位置來映射在底層設備上的內存段中.
這個任務通常由內存管理代碼進行; 這個方法存在為了使驅動能強制特殊設備可能有的任何的
對齊請求. 大部分驅動可以置這個方法為 NULL*/
int (*check_flags)(int);
/*這個方法允許模塊檢查傳遞給 fnctl(F_SETFL...) 調用的標誌.*/
int (*dir_notify)(struct file *, unsigned long);
/*這個方法在應用程式使用 fcntl 來請求目錄改變通知時調用.
只對文件系統有用; 驅動不需要實現 dir_notify.*/
}
file_operations結構是以下列方式初始化:
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = device_read,
.write = device_write,
.ioctl = device_ioctl,
.open = device_open,
.release = device_release
};
file結構
此結構定義在
struct file {
mode_t f_mode;
/*代表檔案的存取模式*/
loft_ f_pos;
/*目前的讀寫位置*/
unsigned int f_flags;
/*檔案旗標的組合像是O_RDONLY,O_NONBLOCK等*/
struct file_operations *f_op;
/*此指標指向一個file_operations結構*/
void *private_data;
/*驅動程式可自行運用此指標,典型用法是讓他指向一塊私有資料區,如果有用到這個,
release時要記得釋放此指標的記憶體非常好用的功能*/
struct dentry *f_dentry;
/*通常不在乎自己在哪個目錄項,但會有需要透過file->f_dentry->d_inode來存取inode結構*/
};
inode結構
核心內部使用inode來代表檔案,它不同於file結構,該結構是代表已開啟檔案的FD(File Descriptor),同一個檔案可以同時被開啟多次,在這種情況下,核心中會有多個代表FD的file結構,但是他們全都指向同一個inode結構。inode結構含有大量關於檔案的資訊,但是一般而言,驅動程式只對其中兩個欄位有興趣:
dev_t i_rdev;
此欄位含有實際的裝置編號
可用unsigned int imajor(struct inode* inode)
;
unsigned int iminor(struct inode* inode)
;來取得major和minor號碼
使用方法如下:
unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);
struct cdev *i_cdev;
struct cdev是核心內部用來表示字元裝置的結構。對於字元裝置的inode,此欄位含有一個指向該結構的指標。
```