首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Char驱动程序Linux: file_operations读写的正确实现是什么?需要做什么偏移检查?

Char驱动程序Linux: file_operations读写的正确实现是什么?需要做什么偏移检查?
EN

Stack Overflow用户
提问于 2020-02-29 19:53:29
回答 1查看 1.4K关注 0票数 0

我正试着读和写给一个司机。当我使用C程序打开设备文件并进行读写时,它会产生SEG错误。当我在设备文件中使用cat时,它会进入无限循环。

1)我遗漏了什么,在file_operations中读写的正确实现是什么?

2)在原型读写中我知道:read(struct file *fp, char *ch, size_t count, loff_t *lofft)计数指的是读/写请求的字节数。但是,使用的最后一个参数偏移量是什么,需要对偏移量进行哪些检查?

3)对于像cat /dev/chardriver这样的多个读取调用,每次读取时是否会增加偏移量?就像count=100的第一次读取呼叫偏移量从1到100一样,在下一次读取呼叫中,偏移量会从101偏移吗?还是会从任何随机数中消失?

这是我的代码:

代码语言:javascript
复制
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/uaccess.h>

    static int device_open(struct inode *, struct file *);
    static int device_release(struct inode *, struct file *);
    static ssize_t device_read(struct file *, char *, size_t, loff_t *);
    static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
    char kernelbuff[1024];

    MODULE_LICENSE("GPL");

    struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .open = device_open,
    .release = device_release
    };


    int device_open(struct inode *inode, struct file *fp)
    {
    printk("device_open called");
    return 0;
    }

    static int device_release(struct inode *inode, struct file *fp)
    {
    printk("device_release called");
    return 0;
    }

    static ssize_t device_read(struct file *fp, char *ch, size_t sz, loff_t *lofft)
    {
    printk("device_read called");      
    copy_to_user(ch, kernelbuff, 1024);
    return sz;
    }

    static ssize_t device_write(struct file *fp, const char *ch, size_t sz, loff_t *lofft)
    {
    printk("device_write called");
    copy_from_user(kernelbuff, ch, 50);
    return 1024;
    }

    static int hello_init(void)
    {
      printk("basicchardriver: module initialized");
      register_chrdev(500, "chr_device", &fops);
      return 0;
    }

    static void hello_exit(void)
    {
      printk("basicchardriver: module exited");
      unregister_chrdev(500, "chr_device");
    }

    module_init(hello_init);
    module_exit(hello_exit);
 }

测试:

代码语言:javascript
复制
sudo mknod -m 666 /dev/chardev c 500 0
echo "Hello World" >> /dev/chardev    ===> Works fine
cat /dev/chardev     ===> Goes to infinite loop

如果我使用C程序调用驱动程序,就会产生SEG错误:

代码语言:javascript
复制
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

int main()
{
  int fd;
  char buff[500];

  fd = open("/dev/chardev", O_RDWR);
  write(fd, "Hello World", 13);

  read(fd, buff, 500);
  printf("Reading data from kernel: \t");
  puts(buff);

  return 0;
}

raj@raj-VirtualBox:~/device-driver/chardriver/read-write$ ./a.out
Reading data from kernel: Hello World
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-02-29 20:32:29

我想我的问题得到了正确的答案:(专家们可以随意添加您自己的答案,/modify)

下面是正确的阅读方式:

代码语言:javascript
复制
static ssize_t device_read(struct file *fp, char *ch, size_t sz, loff_t *lofft)
{
        printk("device_read called");
        if (*lofft > 1024 || sz > 1024) 
        {
          return -EFBIF; // return 0 also works
        }
        if ((*lofft+sz) > 1024)
        {
           sz = 1024 - *lofft;
        }

        copy_to_user(ch, kernelbuff + *lofft, sz);
        *lofft+=sz;
        return sz;
}

(编写操作代码如下)。

偏移量的答案可以从这里引用:Understanding loff_t *offp for file_operations

关于抵消的一些重要问题:

  1. 因此,是的,每个连续读取调用的偏移量需要在read函数中进行调整。下一次读取偏移量应从count_of_no_of_bytes_read_in_last_function_call
  2. Read + last_offset开始,如果偏移量超过内核缓冲区大小,则应返回0。

编辑:读取所需的有效检查可以在此链接中引用(由@Tsyvarev建议):Error checking in a '.read' function in kernel module

编辑:添加改进版本的写函数

代码语言:javascript
复制
static ssize_t device_write(struct file *fp, const char *ch, size_t sz, loff_t *lofft)
{
        printk("device_write called");
        if (((*lofft) > sizeof(kernelbuff)) || (sz > sizeof(kernelbuff)))
        {
        printk("Error: Allocating more than kernel buffer size"); // pr_err( ) can also be used as pointed by @KamilCuk
        return -EFBIG;
        }
        if ((*lofft + sz) > sizeof(kernelbuff))
        {
        printk("Error: Allocating more than kernel buffer size");
        return -EFBIG;
        }

        copy_from_user(kernelbuff + *lofft, ch, sz);
        *lofft+=sz;
        return sz;
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60469058

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档