2015-07-28 3 views
2

Следующий код генерирует ошибку сегментации при попытке чтения с устройства (например: cat/dev/device_name). Однако, если я удалю вызовы down_interruptible и перейду из методов klg_read и klg_write и переместим их в klg_open и klg_close соответственно, код будет работать нормально. Я экспериментирую с драйверами устройств и хочу понять, почему возникает эта ошибка сегментации. Спасибо!Ошибка сегментации в Linux Драйвер устройства

#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"); 
+1

При возникновении ошибки в модуле ядра это должно быть сообщение в системном журнале ('dmesg'), в котором описывается эта ошибка. Обычно это сообщение также содержит стек вызовов. Добавьте этот стек в свой вопрос. Кроме того, попробуйте создать свой модуль с параметрами компилятора 'ccflags: = -Wall'. Это должно быть как минимум одно предупреждение о 'kcdev-> owner = AUTHOR;' line (правильный - 'kcdev-> owner = THIS_MODULE;'). – Tsyvarev

+0

Что вы имели в виду под этим 'filp-> private_data = & klgdev;'?! – 0andriy

+0

Часть filp-> private_data = & klgdev была просто экспериментальной, и для нее не было конкретного использования. Код может очень хорошо обойтись без этой линии. Спасибо! –

ответ

2

Как @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; 
    ... 
}; 

Есть также некоторые другие предупреждения в вашем коде. Вы можете также взглянуть на них.

+0

Спасибо! Сработало изменение владельца на THIS_MODULE. –