Linux 了解文件操作的 loff_t *offp

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9713112/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:12:18  来源:igfitidea点击:

Understanding loff_t *offp for file_operations

clinuxlinux-kernelkernellinux-device-driver

提问by Dr.Knowitall

I'm designing a device driver that simply reads and writes to a character buffer. My question is however regarding the two functions in the file_operationsstructure readand write. I don't truly understand what loff_t *offpreally is. I know that for both the read and write operations that *offpis the file offset meaning the current reading/writing position of the file, however I'm not even sure what it means to write or read to/from a device file.

我正在设计一个简单地读取和写入字符缓冲区的设备驱动程序。然而,我的问题是关于file_operations结构readwrite. 我真的不明白什么是loff_t *offp真正的。我知道对于读取和写入操作,*offp文件偏移量意味着文件的当前读取/写入位置,但是我什至不确定向/从设备文件写入或读取意味着什么。

From what I gathered, and this is how I am writing and reading from my device is that I create a structure which represents my device which I call my_char_structwhich is shown bellow.

从我收集到的,这就是我从我的设备上写和读的方式,我创建了一个代表我称之为我的设备的结构,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;
};

This is a static structure that is initialized and pointed to when my driver is insmodas such.

这是一个静态结构,当我的驱动程序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);

When my device is open, I just make a pointer for the file open to point to that static structure that I've set up during module_init(start_mod)as such ...

当我的设备打开时,我只是为打开的文件创建一个指针,指向我在此期间设置的那个静态结构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;
}

What my read and write methods do is modify that initial structure Dev, that I've pointed to with my open files. Whatever I copy_to_userfrom my structure is what the user considers been written to the device and whatever I copy_from_userthe user thinks they're writing. But beyond changing my initial structure Dev, the idea of file position or offset doesn't make sense unless it refers to a pointer to buffered memory within the kernel for some arbitrary structure or type. Thats the only interpretation that I have for the file offset ... is this correct? Is that what the loff_t *offphere refers to?

我的读写方法所做的是修改我在打开的文件中指向的初始结构 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)

(given my understanding is correct) When some file_operation such as read/write is called and I hadn't set *offppersonally, what is loff_t *offp initially set to?

(鉴于我的理解是正确的)当调用某些文件操作(例如读/写)而我没有*offp亲自设置时,loff_t *offp 最初设置为什么?

If in the last file_operation offp = some_arbitrary_address(because I told it so), is that what the offp would be set to when this operation is called again?

如果在最后一个 file_operation offp = some_arbitrary_address(因为我是这么说的),那么当再次调用这个操作时,offp 会被设置成什么?

What happens if I have other file_opens operations running, will it set to what the last file_operation left it as, or will it keep a tab of which file_open operation it used and replace *offp to what the file_open had it at?

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

The concept of a char device is too abstract for me when it seems that the device itself doesn't even store the information like a file should, but rather its the driver that saves the information. I hope I've explained my fogginess and I'll clear up anything that I seem ambiguous about.

字符设备的概念对我来说太抽象了,因为它似乎设备本身甚至不像文件那样存储信息,而是保存信息的驱动程序。我希望我已经解释了我的迷茫,我会澄清任何我似乎模棱两可的事情。

采纳答案by torek

"loff_t" is a "long offset", i.e., a seek position that unifies the crazy diversity of off_t, off64_t, and so on, so that drivers can just use loff_t and not worry about it.

“参数loff_t”是一个“长偏移量”,即寻求立场相结合的疯狂多样性off_toff64_t等,使驾驶者可以只使用参数loff_t,而不是担心。

The pointer itself, at the time you get into the driver, points to the offset provided by the user (assuming it's user code doing the driver access—technically the kernel can provide its own, but the user case is the one to think about) via lseekor llseekor lseek64, etc., and then by ordinary read and write operations. Consider the case of a regular on-disk file: when you first openthe file, you (as a user) get the kernel to provide a data structure that keeps track of your current position in the file, so that if you reador writesome bytes, the next reador writepicks up from where you left off.

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

Furthermore, if you dupthe file descriptor, or do the equivalent by (e.g.) forkand execin terms of running a sequence of commands, that seek-position is shared by all the inheriting processes. Hence, at the shell prompt, the command:

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

(prog1; prog2; prog3) > outputfile

creates an output file, then dups the descriptor to the three programs, so that output that prog2writes goes into the file immediately after the output from prog1, and output from prog3follows the other two—all because all three separate processes share the same underlying kernel data structure with the same internal loff_t.

创建一个输出文件,然后dup将描述符prog2写入三个程序,以便写入的输出在从 的输出之后立即进入文件prog1,而从prog3的输出跟随其他两个——所有这一切都是因为所有三个独立的进程共享相同的底层内核数据结构具有相同的内部loff_t.

The same applies to device driver files. When your read and write functions are called, you receive the "current offset" as provided by the user, and you can (and should) update it as needed ... assuming there is any need (e.g., you want to provide users with the appearance of a regular file, including the fact that seek offsets move as you read and write). If the device has some logical application of the seek offset, you can use that here.

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

Of course, there's a lot more to device drivers, which is why there are entire book-chapters on this stuff (q.v.). :-)

当然,设备驱动程序还有很多,这就是为什么有关于这些东西的整本书章节 (qv)。:-)

回答by clearlight

Torek's answeris excellent. Just adding a bit extra detail/context... From an earlier Linux kernel (2.6.28), here is an example of offset in use in a system call... it copies the offset from user space to a temporary variable before getting into the kernel driver-invocation mechanism, and then copies it back out to the user file. This is how the offset the driver sees is decoupled from the user view of it, and facilitates the situations where offset is NULL in the system call, so no SEGVIO occurs.

托雷克的回答非常好。只是添加一点额外的细节/上下文......从早期的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);
}