【问题标题】:Unable to spot the error in echo kernel module无法发现 echo 内核模块中的错误
【发布时间】:2013-03-25 20:15:40
【问题描述】:

我正在尝试编写一个非常简单的 echo Linux 驱动程序。

驱动程序从命令行获取最多 250 个字符,并将其写入虚拟设备“mydev”。这再次从设备读回。前端和驱动代码贴在下面供参考。

问题是我能写但不能读。编译或分段错误没有错误。但是没有打印驱动程序读取的 printk 中的任何消息。我对正在发生的事情感到困惑。我可以在这里找到一些线索吗?

为了更清楚,我只是分享代码副本:

mydriver.c :

    #include <linux/module.h>
    #include <linux/fs.h>
    #include <asm/uaccess.h>

    MODULE_LICENSE("GPL");

    static int major;
    static char kbuf[250];

    static int dv_open(struct inode *inode, struct file *filp)
    {
        return 0; 
    }

    static int dv_close(struct inode *inode, struct file *filp)
    {
        return 0; 
    }

    static ssize_t dv_read(struct file *filp, char __user *buf, 
                    size_t sz, loff_t *fpos)
    {
        int r;
        int L;
        printk("READ:Entering\n");

        L = strlen(kbuf);
        r = copy_to_user(buf, kbuf, L);

        printk("READ:Ends\n");

        return L; 
    }
    static ssize_t dv_write(struct file *filp, const char __user *buf, 
                    size_t sz, loff_t *fpos)
    {
        int r, wr_sz;

        printk("WRITE:Entering\n");
        memset(kbuf,'\0', 250);
        if ( sz <= 250 ) {
           wr_sz = sz;
        } else {
           wr_sz = 250;
        }
        r = copy_from_user(kbuf, buf, wr_sz);

        printk("WRITE:Rx buf = %s\n", kbuf);

        return 0;

    }
    static struct file_operations dv_fops = {
        .open     = dv_open,
        .release  = dv_close,
        .read     = dv_read,
        .write    = dv_write,
        .owner    = THIS_MODULE,
    };

    int init_module(void)
    {
        major = register_chrdev(0, "dvdev", &dv_fops);
        if ( major < 0 ) {
           printk("Error in registering driver\n");
           return -1;
        }
        else printk("Success. major = %d\n", major);
        return 0;
    }

    void cleanup_module(void)
    {
        unregister_chrdev(major, "dvdev");
    }

myuserapp.c

    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>

    static char buf[250];
    static char * wbuf;
    int main(int argc, char **argv)
    {
        int fd;
        int option;
        int nbr = 0, len;

        if ( argc != 2 ) {
           printf("usage: front <devName>\n");
           return -1;
        }
        fd = open("mydev", O_RDONLY | O_WRONLY);
        if ( fd < 0 ) {
           printf("Error opening file. %s does not exist\n", argv[1]);
           return -2;
        }

        wbuf = argv[1];
        len = strlen(wbuf);
        nbr = write(fd, wbuf, len); 
        printf("USR: Buf written = %s, nbr = %d\n", wbuf ,nbr);


        nbr = read(fd, buf, 250);
        printf("USR RD: %s", buf);

        close(fd);
        return 0;
    }

【问题讨论】:

  • 您为什么以只读和只写方式打开文件?你的意思是读/写吗? (O_RDWR)
  • 好吧,我被困在 Windows 计算机上,或者我会尝试一下,但可能就是这样。 RDWR == 2,但 (RD | WR) == (0 | 1) == 1 == 错误。所以我猜用“O_RDONLY | O_WRONLY”打开一个文件只打开它。
  • 我猜 O_RDONLY 和 O_WRONLY 是单独的位,所以它会像 O_RDONLY | O_WRONLY = 10|01 = 11 。 Read 和 write 的位都设置了...
  • 您是否创建了设备节点?您确实在内核日志中看到了major 设备ID 打印?顺便说一句,如果您有像openclose 这样的存根,您可以在file_operations 中将它们保留为NULL。如果您还没有制作设备节点,那么我可以看到您的症状。 mknod mydev c &lt;major&gt; 0 其中 来自您的内核日志。你为什么用'C'标记这个?我不认为这是必要的。另见chapter 3 of ldd
  • 好吧,我意识到我对位的困惑。我将检查这一点,并将结果发布在所有信息上。感谢大家宝贵的时间

标签: c linux linux-kernel driver


【解决方案1】:

您的代码至少有一个错误:

 fd = open("mydev", O_RDONLY | O_WRONLY);

这是一个不恰当的open() 电话。
man page for open() 指定:

应用程序应在 oflag 的值中准确指定以下前三个值(文件访问模式)之一:

O_RDONLY 以只读方式打开。
O_WRONLY 仅供写作使用。
O_RDWR 打开用于读取和写入。如果将此标志应用于 FIFO,则结果未定义。

您有一个包含两个值的表达式,而不是只指定一个。

我猜 O_RDONLY 和 O_WRONLY 是单独的位,所以它会像 O_RDONLY | O_WRONLY = 10|01 = 11 。 Read 和 write 的位都设置了。

位值无关紧要,因为不允许组合这些值。
您似乎忽略了排除后缀“ONLY”。
RDONLY 表示“允许读取和禁止写入”。
WRONLY 表示“允许写入和禁止读取”。
“O_RDONLY | O_WRONLY”是一个逻辑矛盾。
如果你想允许读写,那么你必须指定O_RDWR。

并且 Mark Stevens 提供了正确的值和布尔运算来证明你的不正确的表达式不等同于 O_RDWR。

【讨论】:

  • Mark Stevens 第一次评论时确实如此。但是,如果是他的问题,那他就不应该写东西吗? fd 只是用户空间的一个数字;所以内核必须是错误的并返回一个文件句柄。另外,我认为至少部分取决于驱动程序的open() 来实现这一点,目前他在那里什么也没做,但值得检查。
  • 传统上,O_RDONLY 为 0,O_WRONLY 为 1,O_RDWR 为 2。如果这些是实际值,O_RDONLY | O_WRONLY == O_WRONLY
  • @artlesnoise -- 我的意思是,无论这些值如何,用户的 open() 调用都不符合手册页。即使这些值就像 OP 认为的那样,这仍然是一个矛盾的表达。手册页没有列出不正确的访问规范的错误号,可能是因为布尔结果不是无效的(在这种情况下无论如何)。
  • 好的。我说还是值得一试;我明白这是错误的。乔纳森提出了一个很好的观点,我假设它们是“1”和“2”而不是“0”和“1”。 Linux VFS 必须强制执行此操作,因为他的驱动程序不是。让我们看看这是否能解决他的问题。
  • @sawdust -- 事实上,即使进程从未进入驱动程序中的读取函数,我也得到了读取的返回值 -1!
【解决方案2】:

sawdust 给出了正确的答案,但您的代码中还有另一个问题。

如果您向设备写入 250 个字节,则缓冲区不会以空值结尾。那么,strlen在读取的时候会越过它,导致意想不到的结果。

【讨论】:

    【解决方案3】:

    对你的 write 函数的每次调用都会导致在缓冲区上调用 memset。这是你无法检索消息的根本原因。

    【讨论】:

      猜你喜欢
      • 2020-07-13
      • 1970-01-01
      • 1970-01-01
      • 2014-06-28
      • 1970-01-01
      • 2013-10-15
      • 2012-11-19
      • 1970-01-01
      • 2015-07-31
      相关资源
      最近更新 更多