【问题标题】:Simple character driver crash简单的字符驱动程序崩溃
【发布时间】:2012-03-29 09:54:03
【问题描述】:

我正在制作一个简单的字符驱动程序,假设写入我的字符设备“/dev/coffee_bean”,当读取时,它应该显示字符串“Hi There!”在控制台中。我通过“cat /dev/coffee_bean”从设备读取,而不是我的系统崩溃并重置。贝娄是我的源代码。感谢您的帮助。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h>
MODULE_LICENSE("Dual BSD/GPL");

#define DEVICE_NAME "coffee_grinds"
#define COUNT 4
#define FIRST_MINOR 0
#define CONST_QUANTUM 4000
#define CONST_QSET 4000

int test;

module_param(test, int, S_IRUGO);

struct my_char_structure{
    struct cdev my_cdev;
    struct semaphore sem;
    unsigned int access_key;
    unsigned long size;
};

static dev_t dev_num;

int dev_open(struct inode *in_node, struct file *filp){
    struct my_char_structure *my_dev;

    my_dev = container_of(in_node->i_cdev, struct my_char_structure, my_cdev);
    filp->private_data = my_dev;
    return 0;
}

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

ssize_t dev_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp){
    struct my_char_structure *my_dev = filp->private_data;
    ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
    char *my_string;
    int counting;
    printk(KERN_ALERT "Write was accessed, Lol");
    if (down_interruptible(&my_dev->sem))
        return -ERESTARTSYS;
    my_string = kmalloc(count,GFP_KERNEL);
    counting = copy_from_user(my_string,buff,count);
    printk(KERN_ALERT "You wrote %s",my_string);
    kfree(my_string);
    up(&my_dev->sem);

    printk(KERN_ALERT "We wrote %d bytes",counting);
        return retval;
    // Here is some experimental code
}

    ssize_t dev_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){
        struct my_char_structure *my_dev = filp->private_data;
        ssize_t retval = 0;
        char *my_string;

        printk(KERN_ALERT "Read was accessed Lol");

        if (down_interruptible(&my_dev->sem))
            return -ERESTARTSYS;
        my_string = "Hi there!";
        copy_to_user(buff,my_string,10);
        up(&my_dev->sem);
        return retval;

    }

struct file_operations fops = {
    .owner  = THIS_MODULE,
    .read   = dev_read,
    .write  = dev_write,
    .open   = dev_open,
    .release= dev_release,
};

int start_mod(void){
    //Because we are dealing with a fictitious device, I want
    //the driver to create my two devices with arbitrarly 
    //assigned major numbers.
    static struct my_char_structure Dev;
    static 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)
        printk(KERN_ALERT "There was an error %d.",err);
    printk(KERN_ALERT " insmod to major number %d",MAJOR(dev_num));

    return 0;   
}

void end_mod(void){

    unregister_chrdev_region(dev_num, COUNT);

}

module_init(start_mod);
module_exit(end_mod);

【问题讨论】:

    标签: c linux kernel driver device


    【解决方案1】:

    仅看代码无法判断。您可以帮自己检查错误。在所有条件可能失败的地方,您可以使用 KERN_ERR 打印错误,您可以添加 goto OUT(其中 OUT:return -1 ) 从而将崩溃的可能性降到最低。这绝对可以告诉你哪里出错了。另外先创建只写函数并检查它是否正常工作,然后开始制作 dev_read 函数。

    【讨论】:

      【解决方案2】:

      查看您现在发布的完整代码,我没有看到任何明显的崩溃原因。您正在做的事情是在其他驱动程序中完成的。

      只是一些观察。

      几乎没有错误检查。这会咬你一口,因为下一件事的成功执行通常取决于前一件事的成功执行作为前提。

      此外,当您确实调用了 read 函数而没有任何崩溃时,您会发现它不会产生任何东西,因为您返回 0 并且不移动偏移量!大多数程序会将零返回解释为文件结尾。

      您必须遵守传入的缓冲区大小,否则您将破坏用户空间。 cat 程序可能不会执行 read(fd, buf, 5);(请注意,5 小于您要复制到用户空间的 10 个字节),但有些可能。

      顺便说一句,copy_to_usercopy_from_user 是你必须测试失败的函数,并将-EFAULT 返回到用户空间,告诉调用应用程序它在错误区域中传递。

      要调试崩溃,有两种传统方法。一种是添加更多 printk 语句。在没有分支且打印没有缓冲的代码块中,如果一个打印语句在崩溃之前产生输出而另一个没有,则崩溃发生在它们之间。

      另一种技术是解释崩溃转储:机器寄存器、指令指针周围的字节、调用跟踪等。如果您从崩溃中获得这些信息,通常可以通过查看机器来确定崩溃的位置代码和寄存器的值,你可以猜到C变量和数据结构在做什么。

      祝你好运。

      【讨论】:

      • 谢谢 Kaz,我已经调试了我的程序,崩溃是由于我为字符串分配内存的方式。设备不再崩溃,而是我收到来自 copy_to_user() 函数的分段错误。我怀疑我犯了这个错误,因为我没有使用传递的计数变量。感谢您花时间查看我的代码!!!
      • 但是你在写程序中有分配;你说它在阅读时崩溃。写例程中的主要问题是kmalloc 在它可以返回多大的块方面非常有限。它返回物理上连续的页面(这是一种宝贵的资源)。但是read 的参数可能非常大。 IE。一般而言,使用来自用户空间的值作为kmalloc 大小(而不是来自知道它在做什么的root 特权服务器程序)不是一个好主意。即使内存很大,也要准备好返回 null。
      • 好吧,我只想打印出像“Hi There”这样的小东西,并用 kmalloc 分配字节,从而阻止它崩溃。现在我不是在寻找一个功能齐全的驱动程序来实现写入。我只是想先阅读工作。这是我编写的第一个实现读/写的驱动程序。我两周前才开始学习。
      【解决方案3】:

      在到达dev_read 之前可能会出错。您在控制台上看到您的KERN_ALERT 消息了吗?

      显然,您的源代码还不止这些,因为模块已初始化,字符设备已注册,还有诸如 open 例程之类的其他功能。是什么让您认为错误存在于 dev_read 只是因为从设备读取会使机器崩溃?

      sizeof(my_string)sizeof(char *),即 4 或 8。您正在获取指针的大小。如果您使用的是 64 位内核,则当您调试得足够好以达到那么远时,您最多会得到 Hi there 而没有 !。 :)

      即很明显,您可能会从 C 基础知识的教程中受益,例如数组和指针之间的区别。

      【讨论】:

      • 哦,是的,我在获取指针大小之前犯了这个错误,我忘记了。我将添加其余的源代码。
      猜你喜欢
      • 1970-01-01
      • 2012-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多