了解file_operations的loff_t * offp

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了了解file_operations的loff_t * offp相关的知识,希望对你有一定的参考价值。

我正在设计一个只读取和写入字符缓冲区的设备驱动程序。然而,我的问题是关于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将其保留为什么,还是会保留一个tab使用哪个file_open操作并将* offp替换为file_open所拥有的操作?

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

答案

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

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

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

(prog1; prog2; prog3) > outputfile

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

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

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

另一答案

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);
}

以上是关于了解file_operations的loff_t * offp的主要内容,如果未能解决你的问题,请参考以下文章

帧缓冲字符设备接口

003_linux驱动之_file_operations函数

file_operations poll function

csharp file_operations的

python file_operation

Linux驱动开发file_operations结构体