2012-03-15 19 views
14

我正在設計一個簡單地讀取和寫入字符緩衝區的設備驅動程序。然而,我的問題是關於file_operations結構readwrite中的兩個功能。我真的不明白什麼是loff_t *offp。我知道*offp是文件偏移量,意味着文件的當前讀/寫位置,但我甚至不確定寫入或讀取/從設備文件意味着什麼。瞭解file_operations的loff_t * offp

從我收集的,這是我寫如何從我讀設備是我創造它代表我的設備我稱之爲my_char_struct其中顯示波紋管的結構。

struct my_char_structure{ 
    struct cdev my_cdev; 
    struct semaphore sem; 
    char *data; 
    ssize_t data_size; 
    unsigned int access_key; 
    unsigned long size; 
}; 

這是初始化,並指出當我的司機是insmod因爲這樣的靜態結構。

static dev_t dev_num; 
static struct my_char_structure Dev; 

int start_mod(void){ 
    //Because we are dealing with a fictitious device, I want 
    //the driver to create my two devices with arbitrarily 
    //assigned major numbers. 
    struct my_char_structure *my_dev = &Dev; 
    int err; 

    alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); 

    sema_init(&(my_dev->sem),1); 

    cdev_init(&(my_dev->my_cdev), &fops); 
    my_dev->my_cdev.owner = THIS_MODULE; 
    my_dev->my_cdev.ops = &fops;// fops is my file operations struct 

    err = cdev_add(&my_dev->my_cdev, dev_num, COUNT); 
    if(err<0) 
     printk(KERN_ALERT "There was an error %d.",err); 
    printk(KERN_ALERT " insmod to major number %d",MAJOR(dev_num)); 

    return 0; 
} 

module_init(start_mod); 

當我的設備是開放的,我只是做了該文件打開指向期間module_init(start_mod)因爲這樣,我已經設置了靜態結構的指針...

int dev_open(struct inode *in_node, struct file *filp){ 
    static struct my_char_structure *my_dev; 
    my_dev = container_of(in_node->i_cdev, struct my_char_structure, my_cdev); 
    printk(KERN_ALERT "The device number is %d",iminor(in_node)); 
    if(!my_dev) 
     printk(KERN_ALERT "something didn't work. my_dev not initialized."); 
    filp->private_data = my_dev; 
    return 0; 
} 

我什麼讀取和寫入方法的做法是修改初始結構Dev,這是我用我打開的文件指出的。無論我從我的結構中得到的copy_to_user是用戶認爲寫入設備的內容以及用戶認爲他們正在寫入的內容。但除了改變我的初始結構Dev之外,文件位置或偏移量的概念沒有意義,除非它指向內核中用於某些任意結構或類型的緩衝內存的指針。這是我對文件偏移量的唯一解釋......這是正確的嗎?這是指什麼loff_t *offp這裏指的是什麼?

write(struct file *filp, const char __user *buff, size_t count, loff_t *offp) 
read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 

(給我的理解是正確的)當一些file_operation如讀/寫叫,我還沒有設置*offp個人而言,什麼是參數loff_t *石油換食品計劃最初設定爲?

如果在上一次file_operation offp = some_arbitrary_address(因爲我這麼說),那麼當再次調用這個操作時,offp會被設置爲什麼?

如果我有其他file_opens操作正在運行,它會將其設置爲最後一個file_operation將其保留爲的內容,還是保留其使用的file_open操作的選項卡並將* offp替換爲file_open具有的內容?

字符設備的概念對我來說太抽象了,看起來設備本身並不像文件應該存儲信息,而是存儲信息的驅動程序。我希望我已經解釋了我的模糊感,並且我會澄清我似乎模糊不清的任何事情。

回答

19

「loff_t」是一個「長偏移量」,即統一了瘋狂多樣性off_t,off64_t等的查找位置,以便駕駛員可以使用loff_t而不用擔心。

指針本身在進入驅動程序時指向用戶提供的偏移量(假設它是用戶代碼執行驅動程序訪問 - 技術上內核可以提供它自己的,但用戶案例是唯一的想想)通過lseekllseeklseek64等等,然後通過普通的讀寫操作。考慮一下常規的磁盤文件:當你第一次使用open這個文件時,你(作爲一個用戶)讓內核提供一個數據結構來跟蹤你當前在文件中的位置,這樣如果你read或者write一些字節,下一個readwrite從你離開的地方開始。

此外,如果你dup文件描述符,或做通過(例如)forkexec等效在運行命令的序列而言,該尋找位置是由所有的繼承進程共享。因此,在shell提示,命令:

(prog1; prog2; prog3) > outputfile 

創建一個輸出文件,然後dup S中的描述符的三個方案,所以從prog1輸出之後立即輸出該prog2寫入進入該文件,並prog3的輸出跟在另外兩個之後 - 所有這三個獨立的進程共享相同的底層內核數據結構,並且內部採用相同的內部loff_t

這同樣適用於設備驅動程序文件。當您讀取和寫入函數被調用時,您將收到用戶提供的「當前偏移量」,並且您可以(也應該)根據需要更新它......假設有任何需要(例如,您要爲用戶提供常規文件的外觀,包括在您讀寫時查找偏移量的事實)。如果設備具有尋道偏移量的某種邏輯應用,那麼可以在這裏使用。

當然,還有更多的設備驅動程序,這就是爲什麼有這個東西(q.v.)整本書章節。 :-)

+1

所以,當我改變偏移量來偏移+ = bytes_read/write時,用戶指針被改變了,但它不會自動執行它?我認爲這爲我清除了一些東西。我正在閱讀其他人閱讀的linux設備驅動程序第3版手冊,其中的偏移指針引用了一些他們稱之爲文件位置的奇怪的內核抽象(缺少更好的單詞)。感謝您的幫助,這種清理工作:) – 2012-03-15 03:39:10

+2

是的。 (大概你的意思是'* offp + = nbytes'。)你正在改變的變量實際上是一個內核空間的東西,但它代表*用戶的查找偏移量。 (或者,在某些情況下,提供給「pread」或「pwrite」調用的偏移量,甚至是其他情況,但通常是用戶的'lseek'偏移量。)當您稱之爲「怪異的內核抽象」時,是什麼使'(prog1; prog2)>輸出'工作。順便說一句,在* BSD內核中,有一個名爲'uiomove'的函數可以自動爲您更新「用戶I/O偏移量」 Linux剛剛走另一條路。 – torek 2012-03-15 04:41:18

0

Torek's answer非常好。只需添加一些額外的細節/上下文......從較早的Linux內核(2.6.28)開始,這裏是一個在系統調用中使用的偏移量的示例...它在獲取之前將用戶空間的偏移量複製到臨時變量進入內核驅動程序調用機制,然後將其複製回用戶文件。這就是驅動程序所看到的偏移與用戶視圖的解耦方式,並且有助於在系統調用中將其偏移爲NULL的情況,因此不會出現SEGVIO。

SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count) 
{ 
    loff_t pos; 
    ssize_t ret; 

    if (offset) { 
     if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t)))) 
      return -EFAULT; 
     ret = do_sendfile(out_fd, in_fd, &pos, count, 0); 
     if (unlikely(put_user(pos, offset))) 
      return -EFAULT; 
     return ret; 
    } 

    return do_sendfile(out_fd, in_fd, NULL, count, 0); 
}