驱动程序中的文件操作

时间:2013-07-15 20:50:39

标签: driver linux-device-driver

我试图弄清楚驱动程序中的文件操作是如何工作的。我知道有几个文件操作但这些操作的函数是用几个参数调用的,而操作本身是在没有任何参数的情况下定义的。

所以,如果我有这个 -

static const struct file_operations proc_myled_operations = { 
     .open = proc_myled_open, 
     .read = seq_read, 
     .write = proc_myled_write, 
     .llseek = seq_lseek, 
     .release = single_release 
 };

现在我知道内核级驱动程序只能作为来自用户应用程序的文件进行访问。这是一个嵌入式系统,所以我有一些LED可以通过写入它们的存储器映射寄存器来打开。

因此,当我通过使用fopen打开此文件并使用fputs写入它来转动我可以执行的指令时,将执行.write或“proc_myled_write”调用。但是如果.write被映射为“proc_myled_write并且这个函数有这样的参数 -

static ssize_t proc_myled_write(struct file *file, const char __user * buf, 
size_t count, loff_t * ppos)

争论会怎样?使用这些参数没有函数调用上述函数。我在几个司机中看过这个。我刚用过这个,因为这是一个简单的例子。文件操作如何映射到这些函数?例如,用户空间中的“写入”如何跟踪驱动程序中的写入?

谢谢。

2 个答案:

答案 0 :(得分:5)

当你说“上述函数没有函数调用这些参数时,我不确定你的意思。”

这些功能的原型在declaration for struct file_operations itself

中定义

以下是结构声明中的前几行:

struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ...

虽然声明中没有命名参数,但您可以清楚地看到write()函数声明了4个与您在问题中提到的类型相匹配的参数。

当您将函数分配给其相应的字段(proc_myled_operations.write = proc_myled_write)时,您只需将指针传递给模块中声明和定义的write函数。函数指针本身不需要参数。


好的,所以你真正的问题是:“用户空间系统调用最终如何调用模块中的write函数?”好问题!我建议编辑你的问题,以便为将来的读者提供更清晰的信息。

好吧,让我们看看我是否可以按照文章记录。我发现this document为我提供了查看write()系统调用代码的起始位置。它非常非常旧,但是,嘿,并非内核中的所有内容都发生了变化!我们在fs/read_write.cwrite()系统调用声明中开始我们的旅程:

SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
            size_t, count)

它使用文件描述符fd来获取注册角色驱动程序时创建的struct file。然后它获取文件中的当前位置并调用vfs_write()

ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)

正是在这个函数中,见the following line

ret = file->f_op->write(file, buf, count, pos);

有它!

为了减轻对file->f_op类型的疑虑,我们会看一下the definition of struct file并看the following definition for the f_op field

    const struct file_operations    *f_op;

因此,当您注册驱动程序时,它必须是您传入的struct file_operations。呼!

如果您感到好奇,希望所有这些链接都会向您展示如何跟踪其他系统调用的跟踪。

答案 1 :(得分:1)

@Maverick,在内核中编写原型或签名是 ssize_t(* write)(struct file *,const char __user *,size_t,loff_t *)。在用户空间应用程序中,您将发出打开写入系统调用来打开/关闭LED。在用户空间中写入系统调用签名是 int write(int fd,const char * buf,size_t count)。因此,当您从用户空间调用write时,传递的fd(文件描述符)到达虚拟文件系统(vfs),维护打开文件描述符表(OFDT)的链接列表 ,因此根据 fd ,OFDT有一个指针 filp(文件指针),它指向为 ex:device node“/ dev / xxx”打开的文件或任何其他文件。其余的 buf和count 是从用户空间传递到内核空间的相同参数。最后 loff_t * fpos 如果您想要查找文件 ex:在打开的文件上使用lseek或fseek ,如果要搜索文件指针( loff_t fpos )位置相应地改变。希望我清除你的怀疑: - )