了解file_operations的loff_t * offp

时间:2012-03-15 02:17:34

标签: c linux linux-kernel kernel linux-device-driver

我正在设计一个只读取和写入字符缓冲区的设备驱动程序。然而,我的问题是关于file_operations结构readwrite中的两个函数。我真的不明白loff_t *offp到底是什么。我知道对于读取和写入操作*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是什么,用户认为已经写入设备的内容以及用户认为他们正在写的任何我copy_from_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 * offp最初设置为什么?

如果在最后一个file_operation中,offp = some_arbitrary_address(因为我告诉过它),那么当再次调用此操作时,offp将被设置为什么?

如果我正在运行其他file_opens操作会发生什么,它会设置为最后一个file_operation将其保留的内容,还是会保留一个使用了file_open操作的选项卡,并将* offp替换为file_open所拥有的操作?

当设备本身甚至不存储文件应该存储的信息时,char设备的概念对我来说太抽象了,而是它保存信息的驱动程序。我希望我已经解释了我的模糊性,并且我会清除任何我看似模棱两可的东西。

2 个答案:

答案 0 :(得分:20)

“loff_t”是一个“长偏移”,即一个统一off_toff64_t等疯狂多样性的搜索位置,以便司机可以使用loff_t而不用担心关于它。

当你进入驱动程序时,指针本身指向用户提供的偏移量(假设它的用户代码执行驱动程序访问 - 技术上内核可以提供它自己的,但是用户案例是考虑一下)通过lseekllseeklseek64等,然后通过普通的读写操作。考虑常规磁盘文件的情况:当您第一次open文件时,您(作为用户)获取内核以提供跟踪文件中当前位置的数据结构,以便您readwrite一些字节,下一个readwrite从您离开的地方开始接收。

此外,如果您dup文件描述符,或者在运行一系列命令方面由(例如)forkexec执行等效操作,那么共享搜索位置通过所有继承过程。因此,在shell提示符下,命令:

(prog1; prog2; prog3) > outputfile

创建一个输出文件,然后dup描述三个程序,以便prog2写入的输出在prog1输出后立即进入文件,并从prog3跟随另外两个 - 所有因为所有三个独立的进程共享相同的底层内核数据结构,具有相同的内部loff_t

这同样适用于设备驱动程序文件。当您调用读写函数时,您会收到用户提供的“当前偏移量”,您可以(并且应该)根据需要更新它...假设有任何需要(例如,您希望为用户提供常规文件的外观,包括读取和写入时搜索偏移量移动的事实。如果设备有一些寻道偏移的逻辑应用,你可以在这里使用它。

当然,设备驱动程序还有很多,这就是为什么有关于这些内容(q.v.)的完整书籍章节的原因。 : - )

答案 1 :(得分: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);
}