6.5 定位設備
最後, 我們討論一下 llseek
方法, 對於某些設備來說, 該方法非常重要, 其實現非常簡單。
6.5.1 llseek實現
llseek
方法實現了 lseek
和 llseek
系統調用。 llseek
方法在設備的操作中缺失, 內核中的默認實現是通過修改 filp->f_pos
來定位當前的讀寫位置。 如果想要 lseek
系統調用能夠正常工作, read
和 write
方法必須更新它們的參數變量 offset
。
如果 seek
操作對應於設備的物理操作, 那麼需要提供你自己的 llseek
方法。 下面是 scull
中的實現:
loff_t scull_llseek(struct file *filp, loff_t off, int whence)
{
struct scull_dev *dev = filp->private_data;
loff_t newpos;
switch(whence) {
case 0: /* SEEK_SET */
newpos = off;
break;
case 1: /* SEEK_CUR */
newpos = filp->f_pos + off;
break;
case 2: /* SEEK_END */
newpos = dev->size + off;
break;
default: /* can't happen */
return -EINVAL;
}
if (newpos < 0) return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
這裏唯一特定於設備的操作是從設備中檢索文件長度。 在 scull
中, read
和 write
方法根據需要進行協作,如第3章所示。
上面的實現, 對於處理明確定義的數據區域的 scull
有意義, 但大多數設備提供數據流而不是數據區域(比如, 串行端口或鍵盤), 定位這些設備沒有意義。 因爲默認情況下是支持定位的, 所以不聲明 llseek
方法不能實現不定位設備。 必須在你的 open
實現中明確調用 nonseekable_open
告知內核不支持 llseek
方法。
int nonseekable_open(struct inode *inode; struct file *filp);
此調用標記給定的 filp
是不可定位的; 內核永遠不允許對這樣的文件進行 lseek
調用。 通過以這種方式標記文件,您還可以確保不會嘗試通過 pread
和 pwrite
系統調用來定位文件。
爲了完整性, 在你自己的 file_operations
結構體中使用特殊的輔助函數 no_llseek
來設置 llseek
方法, 該輔助函數定義在 <linux/fs.h>
。