Linux 设备驱动程序中的分段错误

Segmentation Fault in Linux Device Driver

当我尝试从设备(例如:cat /dev/device_name)读取时,以下代码会引发分段错误。但是,如果我从 klg_read 和 klg_write 方法中删除对 down_interruptible 及以上的调用,并将它们分别移至 klg_open 和 klg_close,则代码有效美好的。我正在试验设备驱动程序,想了解为什么会出现此分段错误。谢谢!

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/interrupt.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
#include<asm/io.h>
#include<linux/semaphore.h>
#include<linux/spinlock.h>
#include<linux/sched.h>
#include<linux/device.h>



#define DEVICE_NAME "klg_app"
#define AUTHOR "morpheus15"
#define LICENSE "GPL"
#define CLASS_NAME "klg_class"
#define FOR(i,x,n) for(i=x;i<n;i++)
#define BUF_MAX_SIZE 10
#define FIRST_MINOR_NUM 0
#define MINOR_DEVICE_COUNT 1

typedef struct
{
  char data[BUF_MAX_SIZE];
}klg_device_t;


static klg_device_t klgdev;
static int ret,major;
static dev_t dev_num;
static struct cdev *kcdev = NULL;
static struct class *klg_class = NULL;
static struct semaphore semm;

static int klg_open(struct inode *klg_inode,struct file *filp)
{
  filp->private_data = &klgdev;
  /*if(down_interruptible(&semm))
    {
      printk(KERN_INFO "could not open device %s\n",DEVICE_NAME);
      return -ERESTARTSYS;
    }
    */
  printk(KERN_INFO "device %s called open()\n",DEVICE_NAME);
  return 0;
}

static int klg_close(struct inode *klg_inode,struct file *filp)
{
  //up(&semm);
  printk(KERN_INFO "device %s called close()\n",DEVICE_NAME);
  return 0;
}

static ssize_t klg_read(struct file *filp,char __user *buf,size_t count,loff_t *pos)
{
  printk(KERN_INFO "device %s called read()\n",DEVICE_NAME);
  klg_device_t *klgp = filp->private_data;
   if (down_interruptible(&semm))
    {
      printk(KERN_INFO "device %s already in use. could not open for reading\n",DEVICE_NAME);
      return -ERESTARTSYS;
    }

  int len = strlen(klgp->data);
  if (!len || (*pos) > len)
    {
       up(&semm);
      return 0;
    }


  if (*pos + count > len)
    {
      count = len - *pos;
    }

  ret = copy_to_user(buf,klgp->data,count);
  if (ret)
    {
      printk(KERN_INFO "copy_to_user() failed for device %s, exit with error %d\n",DEVICE_NAME,ret);
      up(&semm);
      return -EFAULT;
    }

  (*pos) += count;
   up(&semm);
  return count;

}

static ssize_t klg_write(struct file *filp,char __user *buf,size_t count,loff_t *pos)
{


  printk(KERN_INFO "device %s called write()\n",DEVICE_NAME);
  klg_device_t *klgp = filp->private_data;
   if (down_interruptible(&semm))
    {
      printk(KERN_INFO "device %s already in use. Could not open for writing\n",DEVICE_NAME);
      return -ERESTARTSYS;
    }


  memset(klgp->data,0,BUF_MAX_SIZE);

  if (count  > BUF_MAX_SIZE)
    {
      count = BUF_MAX_SIZE;
    }

  ret = copy_from_user(klgp->data,buf,count);
  if (ret)
    {
      printk(KERN_INFO "copy_from_user() failed for device %s, exit with error %d\n",DEVICE_NAME,ret);
        up(&semm);
      return -EFAULT;
    }

  up(&semm);
  return count;


}


static struct file_operations fops = 
  {
    .owner = AUTHOR,
    .open = klg_open,
    .release = klg_close,
    .read = klg_read,
    .write = klg_write
  };



 static void initialize_constructs(void)
{
  sema_init(&semm,1);
}

static int __init klg_init(void)
{
  ret = alloc_chrdev_region(&dev_num,FIRST_MINOR_NUM,MINOR_DEVICE_COUNT,DEVICE_NAME);
  if (ret)
    {
      printk(KERN_INFO "could not alloc_chrdev_region for device %s\n",DEVICE_NAME);
      return -1;
    }
  major = MAJOR(dev_num);
  printk(KERN_INFO "registered device %s with major number %d\n",DEVICE_NAME,major);


  // add the device to /sys/class/CLASS_NAME
  klg_class = class_create(DEVICE_NAME,CLASS_NAME);
  if (!klg_class)
    {
      printk(KERN_INFO "could not add device %s to /sys/class/%s\n",CLASS_NAME);
      unregister_chrdev_region(dev_num,MINOR_DEVICE_COUNT);
      return -2;
    }

  // create an entry in /dev for the device file
  if (!device_create(klg_class,NULL,dev_num,NULL,DEVICE_NAME))
    {
      printk(KERN_INFO "could not create entry /dev/%s for device %s\n",DEVICE_NAME,DEVICE_NAME);
      class_destroy(klg_class);
      unregister_chrdev_region(dev_num,MINOR_DEVICE_COUNT);
      return -2;
    }

  // create the cdev struct for the character device
  kcdev = cdev_alloc();
  kcdev->owner = AUTHOR;
  kcdev->ops = &fops;
  ret = cdev_add(kcdev,dev_num,MINOR_DEVICE_COUNT);
  if (ret)
    {
      printk(KERN_INFO "could not allocate a char dev struct for device %s\n",DEVICE_NAME);
      device_destroy(klg_class,dev_num);
      class_destroy(klg_class);
      unregister_chrdev_region(dev_num,MINOR_DEVICE_COUNT);
      return -1;
    }


  initialize_constructs();
  printk(KERN_INFO "successfully created a character device driver for device %s\n",DEVICE_NAME);
  return 0;
}

static void __exit klg_exit(void)
{
  // destroy in the reverse order
  cdev_del(kcdev);
  device_destroy(klg_class,dev_num);
  class_destroy(klg_class);
  unregister_chrdev_region(dev_num,MINOR_DEVICE_COUNT);
  printk(KERN_INFO "successfully unregistered character device %s and all its components\n",DEVICE_NAME);
}


module_init(klg_init);
module_exit(klg_exit);
MODULE_AUTHOR(AUTHOR);
MODULE_LICENSE(LICENSE);
MODULE_DESCRIPTION("this is a sample application");

正如@Tsyvarev 通过查看您的代码观察到的那样,您没有正确初始化驱动程序和文件操作的所有者字段:

warning: assignment from incompatible pointer type [enabled by default]
kcdev->owner = AUTHOR;

warning: initialization from incompatible pointer type [enabled by default]
 .owner = AUTHOR,

因此,每次您在驱动程序上发出 open 系统调用时,您都会得到如下信息:

BUG: unable to handle kernel paging request at 75ae1601
IP: [<x109b89f>] try_module_get+0x1f/0x80
*pde = 00000000
Oops: 0000 [#1] SMP
...

只需将其更改为:

kcdev->owner = THIS_MODULE;

和:

static struct file_operations fops = 
{
    .owner = THIS_MODULE;
    ...
};

您的代码中还有其他一些警告。您可能也想看看这些。