如何优雅地禁用无内核崩溃的中断线路? [英] How to gracefully disable the interrupt line without a kernel crash?

查看:353
本文介绍了如何优雅地禁用无内核崩溃的中断线路?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实现了一个程序,从keyboad读取和扫描code和放入微进程。该微进程放开的read()。因此,我的QT应用程序可以读取数据,如果发现L的扫描code,它触发回调到Qt的WebKit的。然而,当我做我的性格司机rmmod的。整个内核崩溃。什么是我的性格驱动程序的问题。

 的#include< Linux的/ init.h中>
#包括LT&; Linux的/ - module.h中GT; / **所有模块所需的** /
#包括LT&;的Linux / kernel.h> / **这是KERN_ALERT ** /
#包括LT&; Linux的/ fs.h文件> / **文件操作** /
#包括LT&; Linux的/ cdev.h> / **字符设备** /
#包括LT&; Linux的/ device.h中> / **为SYS设备登记为/ dev /和/ SYS /班** /
/ **的** copy_to_user /
#包括LT&; ASM / uaccess.h中>
/ **内核线程* /
#包括LT&; Linux的/ kthread.h>
/ **自旋锁这里必不可少** /
#包括LT&; Linux的/ spinlock.h>
#包括LT&; Linux的/ interrupt.h>
#包括LT&; ASM / io.h>/ **对于类登记工作,你需要GPL许可** /
MODULE_LICENSE(GPL);的#define NUMBER_OF_MINOR_DEVICE(0)/ **避免污染名字空间,让一切都静** /静态结构的cdev basicCdev;
静态结构类* basicDriverClass;
/ **创建的线程** /
静态结构的task_struct * basicCharThread = NULL;
静态wait_queue_head_t chrDriverQueue;
静态spinlock_t MLOCK;
静态INT read_unblock_flag = 0;
静态INT basicMajorNumber = 0;/ **只是为了调试,下半区(任务蕾)是否执行或不** /
静态CHAR my_tasklet_data [] =my_tasklet_function被称为;/ **扫描code,将通过读返回** /
静态unsigned char型扫描code;
静态无符号字符状态;静态无效my_tasklet_function(unsigned long类型的数据);DECLARE_TASKLET(my_tasklet,my_tasklet_function,(无符号长)及my_tasklet_data);/ **读原型,当读取功能上完成给驾驶员,这将调用** /
/ **声明类型是基于文件操作函数指针 - 读** /静态ssize_t供basicRead(结构文件*的filp,字符*缓冲区,为size_t长度,参数loff_t *偏移量);静态INT basicOspen(结构的inode的inode *,结构*文件的文件);/ **文件操作函数指针表** /
/ **有大量文件操作的** /静态结构的file_operations FOPS =
{
    .read = basicRead,
    .WRITE = NULL,
    。开= basicOspen,
    .release = NULL
};静态ssize_t供basicRead(结构文件*的filp,字符*缓冲区,为size_t长度,参数loff_t *偏移量)
{
    无符号长标志;
    味精烧焦[] =你好SJ_read \\ 0;    / **锁定** /
    spin_lock_irqsave(安培; MLOCK,旗);    printk的(KERN_ALERT称为\\ r \\ n读操作);    spin_unlock_irqrestore(安培; MLOCK,旗);    wait_event_interruptible(chrDriverQueue,read_unblock_flag!= 0);    / **设置回原来的睡眠** /
    read_unblock_flag = 0;    printk的(KERN_ALERT读睡眠\\ r \\ n出);    如果(signal_pending(电流))
    {
        printk的(KERN_ALERT信号挂起的错误\\ r \\ n);
        返回-1;
    }    spin_lock_irqsave(安培; MLOCK,旗);    如果(0 = copy_to_user(缓冲,和放大器;!扫描code,sizeof的(扫描code)))
    {
        printk的(KERN_ALERTCopy_to_user失败!\\ r \\ n);
    }    spin_unlock_irqrestore(安培; MLOCK,旗);    返回的sizeof(MSG);
}静态INT basicOspen(结构的inode的inode *,结构*文件的文件)
{    printk的(Kernel.Basic驱动程序现在开业!\\ r \\ n);    返回0;
}静态无效setup_cdev(结构的cdev * dev的,INT次要的,结构的file_operations * FOPS)
{
    INT ERR = -1;
    / **用mkdev调用创建一个设备号,即主要和次要号码组合** /
    INT devno =用mkdev(basicMajorNumber,未成年人);
    / ** Initiliaze性格与开发FOPS ** /
    cdev_init(DEV,FOPS);
    / **所有者和经营初始化** /
    dev亡>所有者= THIS_MODULE;
    dev亡> OPS = FOPS;
    / **字符设备添加到系统** /
    / **在这里,1意味着只有1次编号,你可以给2 2次设备,最后一个参数是招收未成年数的计数** /
    ERR = cdev_add(DEV,devno,1);    如果(ERR)
    {
        printk的(级别较为不能添加的cdev);
    }
}
/ **未使用的内核线程这里** /
静态INT basicCharThread_fn(void *的数据)
{
    无符号长J0,J1;
    INT延迟= 30 * HZ;
    无符号长标志;    J0 = jiffies的;
    J1 = J0 +延迟;    / **这个循环运行,直到jiffies的等于(旧的jiffies + 60Hz的延迟),这是总** 1分钟的延迟/
    而(time_before(jiffies的,J1))
    {
        // printk的(以下简称线程启动\\ r \\ n);
        时间表();
    }    spin_lock_irqsave(安培; MLOCK,旗);
    read_unblock_flag = 1;
    spin_unlock_irqrestore(安培; MLOCK,旗);
    wake_up_interruptible(安培; chrDriverQueue);
    printk的(这个过程被移出现在等待队列\\ r \\ n个);
    返回0;
}
/ *下半区功能 - 微进程* /
无效my_tasklet_function(unsigned long类型数据)
{
    无符号长标志;
    printk的(%S \\ n,(字符*)的数据);    / **扫描code 1C pressed ** /
    printk的(KERN_INFO扫描code%×%S。\\ n
           扫描code&放; 0x7F的,
           扫描code&放; 0x80的? 发布:pressed);    spin_lock_irqsave(安培; MLOCK,旗);
    read_unblock_flag = 1;
    spin_unlock_irqrestore(安培; MLOCK,旗);
    wake_up_interruptible(安培; chrDriverQueue);
    返回;
}
/ ***中断处理程序*** /
irqreturn_t irq_handler(INT IRQ,无效*的dev_id,结构pt_regs *暂存器)
{
    / *
    *读取键盘状态
    * /
    状态= INB(0x64);
    扫描code = INB(0x60的);
    / ** BH计划** /
    tasklet_schedule(安培; my_tasklet);    返回IRQ_HANDLED;
}/ **字符驱动负载 - 主入口函数 - MODULE_INIT() - 呼吁insmod的** /
静态INT chrDriverInit(无效)
{    INT结果= -1;
    的dev_t开发;    / **初始化内核线程** /
    CHAR kthreadBasic [] =kthreadBasic;
    printk的(欢迎!设备初始化现在..);    / ** INT alloc_chrdev_region(*的dev_t开发,无符号整型firstminor,无符号整型数,字符*姓名); ** /
    / **开发 - >的dev_t变量类型,这将让内核分配的主要号码。 ** /
    / **相同​​的名字会出现在/ proc /设备。 ** /    / **它注册字符设备** /
    / **主设备号将被动态分配这里** /
    / ** alloc_chrdev_region(安培; dev_num,FIRST_MINOR,COUNT,DEVICE_NAME); ** /
    结果= alloc_chrdev_region(安培;开发,0,NUMBER_OF_MINOR_DEVICE,pSeudoDrv);    如果(结果℃,)
    {
        printk的(错误在分配装置);
        返回-1;
    }    / **从这两如果我们避免了手动mknod命令来对创建/ dev /<驾驶GT; ** /
    / **创建类,然后创建的设备删除调用mknod的的依赖** /
    / **的一个好方法 - 用mknod的办法就是去pciated $ P $ ** /
    / ** mknod的办法就是 - 用mknod的/ dev /<&DRIVER_NAME GT; C< majorNumber> < minorNumber> * /
    / **驱动程序添加到/ -sys /班/ chardrv * /
    如果((basicDriverClass = class_create(THIS_MODULEchardrv))== NULL)// $ LS / SYS /班
    {
        unregister_chrdev_region(DEV,1);
        返回-1;
    }    / **驱动程序添加到/ dev / pSeudoDrv - 在这里** /
    如果(device_create(basicDriverClass,NULL,开发,NULL,pSeudoDrv)== NULL)// $ ls / dev命令/
    {
        class_destroy(basicDriverClass);
        unregister_chrdev_region(DEV,1);
        返回-1;
    }    / **让我们看看主要的数字是由内核分配的** /
    basicMajorNumber = MAJOR(DEV);
    printk的(内核分配的主设备号为%d .. \\ r \\ n,basicMajorNumber);    / **现在设置了的cdev ** /
    setup_cdev(安培; basicCdev,NUMBER_OF_MINOR_DEVICE,&安培; FOPS);    / ** Linux的线程是只是一个过程,就像是只有**处理/
    / **存储的名称字符串 - kthreadBasic ** /
    / **结构的task_struct * kthread_create(INT(*功能)(无效*数据),void *的数据为const char名[],...)** /
    basicCharThread = kthread_create(basicCharThread_fn,(无效*)NULL,kthreadBasic);
    / **要运行的过程中,我们需要传递线程ID叫wake_up_process,通过kthread_create获得** /
    / **停止线程,调用kthread_stop通过线程ID ** /
    如果(basicCharThread == NULL)
    {
        printk的(KERN_ALERT线程创建失败\\ r \\ n);
        返回-1;
    }    / ** inialize自旋锁** /
    spin_lock_init(安培; MLOCK);
    / **初始化等待队列** /
    init_waitqueue_head(安培; chrDriverQueue);    / **现在就开始执行线程** /
    / **残疾人线程这里 - 在不使用** /
    // wake_up_process(basicCharThread);
    / **免费IRQ - 如果已经注册** /
    free_irq(1,NULL);
    / **注册共享模式下的键盘IRQ,注意这是特定于X86架构** /
    返回的request_irq(1 / *键盘IRQ的PC上的数字* /
                       (irqreturn_t *)irq_handler,/ *我们的处理* /
                       IRQF_SHARED,test_keyboard_irq_handler
                       (无效*)(irq_handler));    返回0;}/ **司机退出 - 宏module_exit()..呼吁rmmod的** /静态无效chrDriverExit(无效)
{
    // free_irq(1,NULL);
    / **停止正在运行的线程** /
    tasklet_kill(安培; my_tasklet);
    // kthread_stop(basicCharThread);
    / **反向 - 摧毁mechansim - 它的创建方式** /
    printk的(释放的简单离散事件 - %S \\ r \\ n,__FUNCTION__);
    / **删除添加的字符驱动** /
    cdev_del(安培; basicCdev);
    / **摧毁创建的设备** /
    device_destroy(basicDriverClass,用mkdev(basicMajorNumber,0));
    / **摧毁创建的类** /
    class_destroy(basicDriverClass);
    / **注销CHR开发** /
    unregister_chrdev(basicMajorNumber,NUMBER_OF_MINOR_DEVICE);}/ **模块进入和退出*** /
宏module_init(chrDriverInit);
宏module_exit(chrDriverExit);


解决方案

什么是崩溃的消息?也许my_tasklet处于运行虽然说你终止它。您可以设置一些printk的调试味精的出口例程。

I have implemented a program that reads from the keyboad, and scans code and put it into the tasklet. The tasklet unblocks the read(). Thus, my QT-application can read the data and if it finds the scan code of l, it fires a callback to Qt-webkit. However, when I am doing rmmod of my character driver. The entire kernel crashes. What is the problem in my character driver.

#include <linux/init.h>
#include <linux/module.h> /** needed by all modules **/
#include <linux/kernel.h>  /** This is for KERN_ALERT **/
#include <linux/fs.h> /** for file operations **/
#include <linux/cdev.h>  /** character device **/
#include <linux/device.h>  /** for sys device registration in /dev/ and /sys/class **/
/** for copy_to_user **/
#include <asm/uaccess.h>
/** kernel thread */
#include <linux/kthread.h>
/** spin lock essential here **/
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/io.h>

/** For class registration to work, you need GPL license **/
MODULE_LICENSE("GPL");

#define NUMBER_OF_MINOR_DEVICE (0)

/** to avoid namespace pollution, make everything static **/

static struct cdev basicCdev;
static struct class *basicDriverClass;
/** Created thread **/
static struct task_struct *basicCharThread = NULL;
static wait_queue_head_t chrDriverQueue;
static spinlock_t mLock;
static int read_unblock_flag = 0;
static int  basicMajorNumber = 0;

/** Just for debugging, whether bottom half (tasklet) is executed or not **/
static char my_tasklet_data[]="my_tasklet_function was called";

/** scan code that will be return by read **/
static unsigned char scancode;
static unsigned char status;

static void my_tasklet_function( unsigned long data );

DECLARE_TASKLET( my_tasklet, my_tasklet_function,(unsigned long) &my_tasklet_data );

/** Prototype for read, this will be invoked when the read function is done on to the driver **/
/** The declaration type is file operations based function pointer - read **/

static ssize_t basicRead(struct file *filp, char *buffer, size_t length,loff_t *offset);

static int basicOspen(struct inode *inode, struct file *file);

/** File Operations function pointer table **/
/** There are plenty of file operations  **/

static struct file_operations fops =
{
    .read = basicRead,
    .write = NULL,
    .open = basicOspen,
    .release = NULL
};

static ssize_t basicRead(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
    unsigned long flags;
    char msg[] = "Hello SJ_read\0";

    /** lock it **/
    spin_lock_irqsave( &mLock, flags );

    printk(KERN_ALERT "The Read operation called\r\n");

    spin_unlock_irqrestore( &mLock, flags );

    wait_event_interruptible(chrDriverQueue, read_unblock_flag != 0);

    /** set it back to original for sleep **/
    read_unblock_flag = 0;

    printk(KERN_ALERT "Read out of sleep\r\n");

    if( signal_pending( current ) )
    {
        printk(KERN_ALERT "Signal pending error\r\n");
        return -1;
    }

    spin_lock_irqsave( &mLock, flags );

    if ( 0 != copy_to_user( buffer, &scancode, sizeof(scancode) ))
    {
        printk(KERN_ALERT "Copy_to_user failed !!\r\n");
    }

    spin_unlock_irqrestore( &mLock, flags );

    return sizeof(msg);
}

static int basicOspen(struct inode *inode, struct file *file)
{

    printk("Kernel.Basic Driver Opened now!!\r\n");

    return 0;
}

static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
    int err = -1;
    /** MKDEV call creates a device number i.e. combination of major and minor number **/
    int devno = MKDEV(basicMajorNumber, minor);
    /** Initiliaze character dev with fops **/
    cdev_init(dev, fops);
    /**owner and operations initialized **/
    dev->owner = THIS_MODULE;
    dev->ops = fops;
    /** add the character device to the system**/
    /** Here 1 means only 1 minor number, you can give 2 for 2 minor device, the last param is the count of minor number enrolled **/
    err = cdev_add (dev, devno, 1);

    if (err)
    {
        printk (KERN_NOTICE "Couldn't add cdev");
    }
}


/** Un-used Kernel Thread here **/
static int basicCharThread_fn (void *data)
{
    unsigned long j0,j1;
    int delay = 30*HZ;
    unsigned long flags;

    j0 = jiffies;
    j1 = j0 + delay;

    /** this loop will run till the jiffies is equal to the (old jiffies + 60Hz delay) , this is a 1 minute delay in total **/
    while (time_before(jiffies, j1))
    {
        //printk("The thread is started\r\n");
        schedule();
    }

    spin_lock_irqsave( &mLock, flags );
    read_unblock_flag = 1;
    spin_unlock_irqrestore( &mLock, flags );
    wake_up_interruptible( &chrDriverQueue );
    printk("The process is moved out of wait queue now\r\n");
    return 0;
}


/* Bottom Half Function - Tasklet */
void my_tasklet_function( unsigned long data )
{
    unsigned long flags;
    printk( "%s\n", (char *)data );

    /** Scan Code 1c Pressed **/
    printk(KERN_INFO "Scan Code %x %s.\n",
           scancode& 0x7F,
           scancode & 0x80 ? "Released" : "Pressed");

    spin_lock_irqsave( &mLock, flags );
    read_unblock_flag = 1;
    spin_unlock_irqrestore( &mLock, flags );
    wake_up_interruptible( &chrDriverQueue );
    return;
}


/*** Interrupt Handler ***/
irqreturn_t irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
    /*
    * Read keyboard status
    */
    status = inb(0x64);
    scancode = inb(0x60);
    /** BH scheduled **/
    tasklet_schedule( &my_tasklet );

    return IRQ_HANDLED;
}

/** character Driver load - Main Entry Function - Module_Init() - called on insmod **/
static int chrDriverInit(void)
{

    int result = -1;
    dev_t dev;

    /** Initialize the kernel thread **/
    char  kthreadBasic[]="kthreadBasic";
    printk("Welcome!! Device Init now..");

    /** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);  **/
    /** dev -> The dev_t variable type,which will get the major number that the kernel allocates.  **/
    /**The same name will appear in /proc/devices.  **/

    /** it is registering the character device **/
    /** a major number will be dynamically allocated here **/
    /**  alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); **/
    result = alloc_chrdev_region(&dev, 0, NUMBER_OF_MINOR_DEVICE, "pSeudoDrv");

    if( result < 0 )
    {
        printk("Error in allocating device");
        return -1;
    }

    /** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
    /**  creating class, and then device created removes the dependency of calling mknod  **/
    /** A good method - the mknod way is depreciated **/
    /** mknod way is -  mknod /dev/<driver_name> c <majorNumber> <minorNumber> */


    /** add the driver to /-sys/class/chardrv */
    if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL)    //$ls /sys/class
    {
        unregister_chrdev_region(dev, 1);
        return -1;
    }

    /** add the driver to /dev/pSeudoDrv -- here **/
    if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
    {
        class_destroy(basicDriverClass);
        unregister_chrdev_region(dev, 1);
        return -1;
    }

    /** let's see what major number was assigned by the Kernel **/
    basicMajorNumber = MAJOR(dev);
    printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );

    /** Now setup the cdev **/
    setup_cdev(&basicCdev,NUMBER_OF_MINOR_DEVICE, &fops);

    /** Linux thread is just another process, and is treated like that only **/
    /** string that stores the name - kthreadBasic**/
    /** struct task_struct *kthread_create(int (*function)(void *data),void *data const char name[], ...)**/
    basicCharThread = kthread_create(basicCharThread_fn,(void *)NULL,kthreadBasic);
    /** To run the process, we need to call wake_up_process by passing the thread-id, obtained by kthread_create **/
    /** To stop the thread, call kthread_stop pass the thread-id **/
    if( basicCharThread == NULL)
    {
        printk( KERN_ALERT "Thread creation failed\r\n");
        return -1;
    }

    /** inialize spin lock **/
    spin_lock_init( &mLock);
    /** Initialize wait queue **/
    init_waitqueue_head(&chrDriverQueue);

    /** Start executing the thread now **/
    /** Disabled thread here- not in use **/
    //wake_up_process(basicCharThread);
    /** Free IRQ - if already registered **/
    free_irq(1, NULL);
    /** register the keyboard IRQ on shared mode,note this is specific to X86 architecture **/
    return request_irq(1,   /* The number of the keyboard IRQ on PCs */
                       (irqreturn_t *)irq_handler,  /* our handler */
                       IRQF_SHARED, "test_keyboard_irq_handler",
                       (void *)(irq_handler));

    return 0;

}

/** Driver Exit - Module_Exit() .. called on rmmod **/

static void chrDriverExit(void)
{
    //free_irq(1, NULL);
    /** stop the running thread **/
    tasklet_kill( &my_tasklet );
    //kthread_stop(basicCharThread);
    /** A reverse - destroy mechansim -- the way it was created **/
    printk("Releasing Simple Devs -- %s\r\n",  __FUNCTION__);
    /** delete the character driver added **/
    cdev_del(&basicCdev);
    /** destroy the device created **/
    device_destroy(basicDriverClass, MKDEV(basicMajorNumber, 0));
    /** destroy the class created **/
    class_destroy(basicDriverClass);
    /** unregister the chr dev **/
    unregister_chrdev(basicMajorNumber, NUMBER_OF_MINOR_DEVICE);

}

/** Module Entry and Exit ***/
module_init(chrDriverInit);
module_exit(chrDriverExit);

解决方案

What's the crash message? Perhaps my_tasklet is in the run Q while you terminate it. you can set up some printk debug msg in the exit routine.

这篇关于如何优雅地禁用无内核崩溃的中断线路?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆