file_operations 操作表(file_operations table)
當你開啟一個檔案或裝置並呼叫 read() 或 write() 時,核心怎麼知道要執行哪段程式碼?不同裝置對同一個呼叫需要完全不同的行為。file_operations 操作表就是驅動程式的答案卡:一個塞滿函式指標的結構,每個操作一個,告訴核心「當有人讀我,呼叫這個函式;當有人寫我,呼叫那個」。這就是同一個通用系統呼叫 read() 如何抵達正確的、特定於驅動程式的處理常式。
具體而言,struct file_operations 有像 .read、.write、.open、.release、.unlocked_ioctl、.mmap、.llseek 等成員,每個都是指向具固定簽章函式的指標。驅動程式只填入它支援的操作,其餘留為 NULL;一個 NULL 欄位表示那個操作不被支援(核心會回傳錯誤或合理的預設)。當使用者空間呼叫 read(fd, ...) 時,核心查出該描述符背後的 struct file,順著它找到 file_operations 表,並呼叫 table->read(...)。這就是單純的函式指標分派——核心版本的介面或虛擬函式表(vtable),以 C 親手寫成。.owner = THIS_MODULE 欄位讓核心在檔案開啟期間把你的模組釘在記憶體中,使它不會在某個進行中的呼叫底下被卸載。
它之所以重要,是因為 file_operations 表正是「一切皆檔案」的統一模型與裝置專屬程式碼相接的縫,也是你為任何字元驅動程式寫的第一個東西。一個微妙的正確性要點:這些處理常式在呼叫進來的那個行程的情境中執行、(多數 fops)可以睡眠,而且可能同時被許多行程進入——所以處理常式碰到的任何共享狀態都需要自己的鎖。把某個操作設為 NULL 是刻意的選擇,不是偷懶:那是你說「這個裝置不做尋找」的方式。
static ssize_t my_read(struct file *f, char __user *buf, size_t len, loff_t *off) { ... } static const struct file_operations fops = { .owner = THIS_MODULE, .read = my_read, // read() 會落到這裡 // .write 留為 NULL -> 寫入會回傳錯誤 };
一張函式指標表把每個系統呼叫對應到驅動程式的處理常式;NULL 欄位表示該操作不被支援。
同一裝置的處理常式可能並行執行(多個開啟者),所以共享狀態需要加鎖。設定 .owner = THIS_MODULE,使模組不會在檔案開啟期間被卸載。