设备驱动程序在用户应用程序重复使用后崩溃

时间:2018-06-05 09:28:26

标签: linux linux-kernel linux-device-driver

这是我自己的简单设备驱动程序,它使用工作队列在用户应用程序上生成异步中断。每次在向用户应用程序提供异步通知后调用它时,工作队列会自行重新安排(延迟)。 在询问驱动程序中断并等待之后,用户应用程序进入。要正确处理退出,当使用ctrl + c终止用户应用程序时,调用handle会告诉驱动程序取消预定任务,然后关闭驱动程序并退出。

问题是,如果我重复运行用户应用程序超过3次,系统会冻结,并且syslog条目没有错误。 以下是驱动程序的代码

#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/kernel.h>
#include<linux/slab.h>
#include<linux/uaccess.h>
#include<linux/stat.h>
#include"ioctrl.h"
#include<linux/cdev.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include<linux/timer.h>
#include<linux/workqueue.h>

#define DEVICE_NAME "myCharDevice"
#define MODULE_NAME "myCharDriver"
#define CLASS_NAME "myCharClass"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("YASH BHATT");
MODULE_VERSION(".01");

static char *bufferMemory;
static int bufferPointer;
static int bufferSize = 15;
static dev_t myChrDevid;
static struct cdev *myChrDevCdev;
static struct class *pmyCharClass;
static struct device *pmyCharDevice;
static wait_queue_head_t waitQueue; 
static int exit_now = 0;
static struct delayed_work *MyTask;
static int in_sleep = 0;
static volatile int wake_up = 5;
int majorNumber = 0;

static int charDriverOpen(struct inode *inodep, struct file *filep);
static int charDriverClose(struct inode *inodep, struct file *filep);
static ssize_t charDriverWrite(struct file *filep, const char *buffer, size_t len, loff_t *offset);
static ssize_t charDriverRead(struct file *filep, char *buffer, size_t len, loff_t *offset);
static int charDriverEntry(void);
static void charDriverExit(void);
static long charDriverCtrl(struct file *filep, unsigned int command, unsigned long argument);
static int myfasync(int fd, struct file *fp, int on);
static void send_signal_timerfn(struct work_struct *work);

static struct file_operations fops = 
{
    .open = charDriverOpen,
    .release = charDriverClose,
    .read = charDriverRead,
    .write = charDriverWrite,
    .unlocked_ioctl = charDriverCtrl,
    .fasync = myfasync,
};
static struct fasync_struct *fasyncQueue;

static int __init charDriverEntry()
{
    int returnValue;

    returnValue = alloc_chrdev_region(&myChrDevid, 0, 1, DEVICE_NAME); 
    majorNumber = MAJOR(myChrDevid);    
    if (returnValue < 0)
    {
        printk(KERN_ALERT "ERROR : can not aquire major number! error %d",returnValue);
        return -1;
    }
    printk(KERN_INFO "Aquired Major Number! : %d\n", MAJOR(myChrDevid));

    myChrDevCdev = cdev_alloc();
    if (IS_ERR(myChrDevCdev))
    {
        printk(KERN_ALERT "Failed to allocate space for CharDev struct\n");
        unregister_chrdev_region(myChrDevid, 1);
        return -1;
    }
    cdev_init(myChrDevCdev,&fops);
    myChrDevCdev->owner = THIS_MODULE;
    pmyCharClass = class_create(THIS_MODULE,CLASS_NAME);
    if (IS_ERR(pmyCharClass))
    {
        printk(KERN_ALERT "Failed to Register Class\n");
        cdev_del(myChrDevCdev);
        kfree(myChrDevCdev);
        unregister_chrdev_region(myChrDevid, 1);
        return -1;
    }
    printk(KERN_INFO "Class created!\n");

    pmyCharDevice = device_create(pmyCharClass, NULL, MKDEV(majorNumber,0),NULL,DEVICE_NAME);
    if (IS_ERR(pmyCharDevice))
    {
        printk(KERN_ALERT "Failed to Register Class\n");
        class_unregister(pmyCharClass);
        class_destroy(pmyCharClass);
        cdev_del(myChrDevCdev);
        kfree(myChrDevCdev);
        unregister_chrdev_region(myChrDevid, 1);
        return -1;
    }
    printk(KERN_INFO "Device created!\n");

    returnValue = cdev_add(myChrDevCdev, myChrDevid, 1);
    if (returnValue < 0)
    {
        printk(KERN_ALERT "Failed to add chdev \n");
        return -1;
    }

    printk(KERN_INFO "Now We will create the attribute entry in sysfs\n");
    init_waitqueue_head(&waitQueue);
    MyTask = kmalloc(sizeof(struct delayed_work), GFP_KERNEL);
    INIT_DELAYED_WORK(MyTask, send_signal_timerfn);
    return 0;
}

static void __exit charDriverExit()
{
    device_destroy(pmyCharClass, MKDEV(majorNumber,0));
    class_unregister(pmyCharClass);
    class_destroy(pmyCharClass);
    //unregister_chrdev(majorNumber,DEVICE_NAME);
    cdev_del(myChrDevCdev);
    unregister_chrdev_region(myChrDevid, 1);
    kfree(myChrDevCdev);
    printk(KERN_INFO "Unmounting module done !\n");
}

static int charDriverOpen(struct inode *inodep, struct file *filep)
{
    if (bufferSize <= 0)
    {
        bufferSize = 15;
    }
    printk(KERN_INFO "INFO : CHARATER DRIVER OPENED\n");
    bufferMemory = kmalloc(bufferSize,GFP_KERNEL);
    bufferPointer = 0;
    wake_up = 5;
    in_sleep = 0;
    //add_timer(&timer);
    printk(KERN_INFO "timer setup \n");
    return 0;   
}

static int charDriverClose(struct inode *inodep, struct file *filep)
{
    //del_timer(&timer);
    kfree(bufferMemory);
    printk(KERN_INFO "INFO : CHARACTER DRIVER CLOSED\n");
    return 0;
}

static ssize_t charDriverWrite(struct file *filep, const char *buffer, size_t len, loff_t *offset)
{
    printk("dummy write\n");
}

static ssize_t charDriverRead(struct file *filep, char *buffer, size_t len, loff_t *offset)
{
    printk("Dummy read");
}
static long charDriverCtrl(struct file *filep, unsigned int command, unsigned long argument)
{
    int returnVal;
    bufferSizeStruct sizeStruct;
    printk(KERN_INFO "INFO: IOCONTROL called\n");
    switch(command)
    {
        case GO_TO_SLEEP:
            printk("Going to sleep \n");
            in_sleep = 1;
            printk("value of wake_up before going to sleep %d\n",wake_up);
            //returnVal = wait_event_interruptible(waitQueue, !wake_up);
            wait_event(waitQueue, !wake_up);
            returnVal = 0xA5A5;
            printk("I have woken up returnVal = %d!\n",returnVal);
            //del_timer(&timer);
            wake_up = 5;
            break;
        case ADD_TO_QUEUE:
            printk("Adding work to queue\n");
            schedule_delayed_work(MyTask, 100);
            exit_now = 0;
            break;
        case REMOVE_TASK_FROM_QUEUE:
            exit_now = 1;
            cancel_delayed_work_sync(MyTask);
            printk("Removing task from queue\n");
            break;
        default:
            printk(KERN_WARNING "WARNING: Invalid IOCTRL ARGUMENT!\n");
            return -1;
    }
    printk("outside the switch already \n");
    return 0;
}

static int myfasync(int fd, struct file *fp, int on)
{
    return fasync_helper(fd, fp, 1, &fasyncQueue);  
}

static void send_signal_timerfn(struct work_struct *work)
{   


    printk(KERN_INFO "timer expired \n");
    kill_fasync(&fasyncQueue, SIGIO, POLL_OUT);
    if (in_sleep == 1)
    {
        printk(KERN_INFO "in the timer callback with sleep on wake_up : %d\n", wake_up);
        wake_up--;
        if (!wake_up)
            {
                printk(KERN_INFO "now going to wake my self up\n");
                wake_up(&waitQueue);
            }
    }
    if (!exit_now)
        schedule_delayed_work(MyTask, 100);
}

module_init(charDriverEntry);
module_exit(charDriverExit);
module_param(bufferSize, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(bufferSize, "Buffer Memory Size [15]");

用户申请如下:

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include"ioctrl.h"
#include<sys/ioctl.h>
#include<signal.h>

bufferSizeStruct sizeStruct;
int fp;

void my_notifier(int signo, siginfo_t *sigInfo, void *data)
{
    printf("Signal received from the driver expected %d got %d \n",SIGIO,signo);
}

void my_signal(int p)
{
    ioctl(fp, REMOVE_TASK_FROM_QUEUE );
    close(fp);
    exit(0);
}
int main()
{
    struct sigaction signalInfo;
    int flagInfo;

    signalInfo.sa_sigaction = my_notifier;
    signalInfo.sa_flags = SA_SIGINFO;
    sigemptyset(&signalInfo.sa_mask);

    sigaction(SIGIO, &signalInfo, NULL);
    signal(SIGINT, my_signal);

    int i;
    char c[5];
    fp = open("/dev/myCharDevice",O_RDWR);
    if (fp<0)
        printf("Failed to open\n");

    /*New we will own the device so that we can get the signal from the device*/

    fcntl(fp, F_SETOWN, getpid());
    flagInfo = fcntl(fp, F_GETFL);
    fcntl(fp, F_SETFL, flagInfo|FASYNC);
    sleep(5);
    printf("Scheduling work queue\n");
    ioctl(fp, ADD_TO_QUEUE );
    //printf("Sending the drive to sleep for a while \n");
    //ioctl(fp, GO_TO_SLEEP);
    while(1);
    return 0;
}   

我想我忘了禁用某些东西而且正在加载内核。 当我与计时器合作时,我遇到了同样的问题,即使我在每个出口都执行了del_timer()。我在这里错过了什么? 谢谢

1 个答案:

答案 0 :(得分:0)

看起来像一个内核错误。我在Linux 4.15上运行了相同的程序,并且运行正常,但在4.13.43上崩溃了