Грешка при сегментиране в драйвера за устройство на Linux

Следният код хвърля грешка при сегментиране, когато се опитвам да чета от устройството (напр.: cat /dev/device_name). Въпреки това, ако премахна извикванията към down_interruptible и up от методите klg_read и klg_write и ги преместя съответно към 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");

person Rohit Gurunath    schedule 28.07.2015    source източник
comment
Когато възникне грешка в модула на ядрото, това трябва да бъде съобщение в системния журнал (dmesg), което описва тази грешка. Обикновено стекът за повиквания също се доставя с това съобщение. Добавете този стек към вашата публикация с въпроси. Също така опитайте да изградите своя модул с ccflags := -Wall опции на компилатора. Трябва да има поне едно предупреждение за kcdev->owner = AUTHOR; ред (правилното е kcdev->owner = THIS_MODULE;).   -  person Tsyvarev    schedule 28.07.2015
comment
Какво имахте предвид с това filp->private_data = &klgdev; ?!   -  person 0andriy    schedule 28.07.2015
comment
Частта filp-›private_data = &klgdev беше само експериментална и нямаше конкретна употреба за същата. Кодът може много добре да се справи и без този ред. Благодаря!   -  person Rohit Gurunath    schedule 28.07.2015


Отговори (1)


Както @Tsyvarev забеляза просто като погледна вашия код, вие не инициализирате правилно полето собственик на вашия драйвер и файлови операции:

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

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

Поради това всеки път, когато издадете отвореното системно извикване на вашия драйвер, ще получите нещо подобно:

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;
    ...
};

Има и някои други предупреждения във вашия код. Може да искате да разгледате и тях.

person dragosht    schedule 28.07.2015
comment
Благодаря ти! Промяната на собственика на THIS_MODULE проработи. - person Rohit Gurunath; 28.07.2015