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

标签 linux linux-kernel linux-device-driver

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

问题是,如果我重复运行用户应用程序 3 次以上,系统会卡住,系统日志条目上没有错误。 下面是驱动程序的代码

#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() 。我在这里错过了什么? 谢谢

最佳答案

看起来像是内核错误。我在 Linux 4.15 上运行相同的程序,它工作正常但在 4.13.43 上崩溃

关于linux - 设备驱动程序在被用户应用程序重复使用后崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50696704/

相关文章:

c - 为什么 vmalloc 返回的地址不能转换为物理地址

c# - .Net Core Machine Key 替代 webfarm

linux - 解析json数据时文本编码错误

python - 从 Linux 中的 acpi 事件执行 PyQt 应用程序

linux - Beagleboard xm rev C 上未检测到 SMSC9514

将 GPIO 引脚控制在 150ns 的容差范围内

windows - 从 Linux 调用 Windows 批处理文件

linux-kernel - 编译 Linux 内核错误 xt_CONNMARK.h

c - Linux 平台驱动程序和普通设备驱动程序有什么区别?

linux-kernel - Linux 内核模块在卸载期间挂起