Linux kernel 内核恐慌:试图在微小的tty驱动程序上写/读

Linux kernel 内核恐慌:试图在微小的tty驱动程序上写/读,linux-kernel,device-driver,tty,Linux Kernel,Device Driver,Tty,我是Linux编程的初学者,在练习时尝试了一些设备驱动程序示例。 下面的代码(一个精简版的tiny_tty.c)使用insmod完全加载,我可以在/proc/tty/drivers中看到它,/proc/modules和设备节点都是在/dev下创建的。当我尝试像echo“abcd”>/dev/ttyms0这样写入设备文件时(我希望这很好)或者读起来像cat/dev/ttyms0,内核会在屏幕上显示一个调用跟踪。我使用的是Ubuntu下的内核3.5.0。不幸的是,我无法捕获跟踪,因为当它恐慌时,我别

我是Linux编程的初学者,在练习时尝试了一些设备驱动程序示例。 下面的代码(一个精简版的
tiny_tty.c
)使用insmod完全加载,我可以在
/proc/tty/drivers
中看到它,
/proc/modules
和设备节点都是在/dev下创建的。当我尝试像
echo“abcd”>/dev/ttyms0
这样写入设备文件时(我希望这很好)或者读起来像
cat/dev/ttyms0
,内核会在屏幕上显示一个调用跟踪。我使用的是Ubuntu下的内核3.5.0。不幸的是,我无法捕获跟踪,因为当它恐慌时,我别无选择,只能使用电源按钮重新启动。我认为计时器存在一些问题,因为跟踪显示顶部有一行代码:
“*内核bug位于/build/buildd/linux-3.5.0/kernel/timer.c:901*
”,然后是调用跟踪,然后是
“*EIP位于添加计时器+0x18/0x20*”
下面是完整的代码。任何指导都是非常值得期待的

2013年5月10日:我尝试在open函数中初始化计时器,下面是“内核死机-不同步:发生中断死机时发生致命异常,切换回文本控制台”的调用跟踪:

update_sd_lb_stats+0xcd/0x4b0
查找最忙的组+0x2e/0x420
排队_实体+0xcb/0x510
负载平衡+0x7e/0x5e0
重新平衡\u域+0xed/0x150
__do_softirq+0xdb/0x180
本地\u bh\u启用\u ip+0x90/0x90
将\复制到\用户0x41/0x60
sys_gettimeofday+0x2a/0x70
sysenter\u do\u call0x12/0x20
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括/*kmalloc()*/
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
模块许可证(“GPL”);
#定义MS_TTY_MAJOR 250//测试值
#定义MS_TTY_NUM_DEV 2
#定义每个字符的延迟时间HZ*2/*2秒*/
#定义微小数据字符“t”
静态整数;
//静态int minor_num=0;
//静态int num_tty_dev=2;
/*下面的结构是设备特定字段的包装*/
结构ms\u tty\u序列{
struct tty_struct*tty;/*指向此设备的tty的指针*/
int open_count;/*此端口已打开的次数*/
结构信号量sem;/*锁定此结构*/
结构定时器列表*定时器;
};
静态结构ms_tty_串行*ms_tty_表[ms_tty_NUM_DEV];/*最初全部为空*/
静态无效ms_tty_计时器(无符号长计时器数据)
{
结构ms_tty_serial*ms_ptr=(结构ms_tty_serial*)定时器数据;
结构tty_结构*tty;
字符数据[1]={TINY_data_CHARACTER};
int data_size=1;
如果(!ms_ptr)
返回;
tty=ms_ptr->tty;
tty->低延迟=1;
/*将数据发送到tty层供用户读取。这不会
*除非设置了tty->low_latency,否则实际推送数据*/
tty缓冲区请求室(tty,数据大小);
tty_插入_翻转_字符串(tty、数据、数据大小);
tty\U翻转\U缓冲\U推送(tty);
/*再次提交计时器*/
ms\u ptr->timer->expires=jiffies+DELAY\u TIME;
添加定时器(ms\u ptr->timer);
}
////定义open函数////
静态int tty_ms_open(struct tty_struct*tty_this,struct file*file_this)
{
printk(内核警报“tty\U ms驱动程序:已打开…\n”);
结构ms_tty_serial*ms_ptr;
结构定时器列表*定时器;
整数指数;
/*在出现故障时初始化指针*/
tty\u this->driver\u data=NULL;
/*获取与此tty指针关联的串行对象*/
index=tty\u此->索引;
ms_ptr=ms_tty_表[索引];
如果(ms_ptr==NULL){
/*首次访问此设备时,请创建它*/
ms_ptr=kmalloc(sizeof(*ms_ptr),GFP_内核);
如果(!ms_ptr)
return-ENOMEM;
//init_MUTEX(&ms_ptr->sem);/*不适用于此内核版本3.5.0*/
#ifndef init_MUTEX/*sema_init将用于内核2.6.37及更高版本*/
sema_init(&ms_ptr->sem,1);
#否则
初始化互斥(ms_ptr->sem);
#恩迪夫
ms_ptr->open_count=0;
ms_ptr->timer=NULL;
ms_tty_table[索引]=ms_ptr;
}
向下(&ms_ptr->sem);
/*在tty结构中保存我们的结构*/
tty_this->driver_data=ms_ptr;
ms_ptr->tty=tty_此;
ms_ptr->filp=file_this;//要尝试
++ms\u ptr->打开\u计数;
如果(ms_ptr->打开_计数==1){
/*这是第一次打开此端口*/
/*这里是否需要进行任何硬件初始化*/
/*创建计时器并提交它*/
如果(!ms_ptr->计时器){
timer=kmalloc(sizeof(*timer),GFP_内核);
如果(!计时器){
向上(&ms_ptr->sem);
return-ENOMEM;
}
ms_ptr->timer=定时器;
}
初始化计时器(ms_ptr->timer);//要尝试
ms_ptr->timer->data=(无符号长)ms_ptr;
ms\u ptr->timer->expires=jiffies+DELAY\u TIME;
ms_ptr->timer->function=ms_tty_定时器;
添加定时器(ms\u ptr->timer);
}
向上(&ms_ptr->sem);
返回0;
}
////定义关闭函数////
静态无效do_关闭(结构ms_tty_序列*ms_ptr)
{
向下(&ms_ptr->sem);
如果(!ms\u ptr->打开\u计数){
/*港口从未开放*/
转到出口;
}
--ms\u ptr->打开\u计数;
如果(ms\u ptr->打开\u计数计时器);
}
出口:
向上(&ms_ptr->sem);
}
静态void tty\u ms\u close(struct tty\u struct*tty\u this,struct file*file\u this)
{
printk(内核警报“tty\U ms驱动程序:关闭…\n”);
struct ms_tty_serial*ms_ptr=tty_this->driver_data;
如果(ms_ptr)
do_关闭(ms_ptr);
}
////定义写函数////
静态整数tt
update_sd_lb_stats+0xcd/0x4b0
find_busiest_group+0x2e/0x420
enqueue_entity+0xcb/0x510
load_balance+0x7e/0x5e0
rebalance_domains+0xed/0x150
__do_softirq+0xdb/0x180
local_bh_enable_ip+0x90/0x90
<IRQ>
copy_to_user0x41/0x60
sys_gettimeofday+0x2a/0x70
sysenter_do_call0x12/0x20



  #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/kdev_t.h>
    #include <linux/wait.h>
    #include <linux/errno.h>
    #include <linux/slab.h>     /* kmalloc() */
    #include <linux/tty_driver.h>
    #include <linux/tty.h>
    #include <linux/tty_flip.h>
    #include <linux/mutex.h>
    #include <linux/serial.h>
    #include <linux/sched.h>
    #include <asm/uaccess.h>
    #include <asm/termios.h>


    MODULE_LICENSE("GPL");

    #define MS_TTY_MAJOR 250    //test value
    #define MS_TTY_NUM_DEV 2
    #define DELAY_TIME      HZ * 2      /* 2 seconds per character */
    #define TINY_DATA_CHARACTER 't'

    static int major_num;
    //static int minor_num=0;
    //static int num_tty_dev=2;

    /* Below structure is a wrapper for device specific fields */
    struct ms_tty_serial {
        struct tty_struct   *tty;       /* pointer to the tty for this device */
        int         open_count; /* number of times this port has been opened */
        struct semaphore    sem;        /* locks this structure */
        struct timer_list   *timer;

    };

    static struct ms_tty_serial *ms_tty_table[MS_TTY_NUM_DEV];  /* initially all NULL */

    static void ms_tty_timer(unsigned long timer_data)
    {
        struct ms_tty_serial *ms_ptr = (struct ms_tty_serial *)timer_data;
        struct tty_struct *tty;
        char data[1] = {TINY_DATA_CHARACTER};


    int data_size = 1;

        if (!ms_ptr)
            return;

        tty = ms_ptr->tty;
        tty->low_latency=1;
        /* send the data to the tty layer for users to read.  This doesn't
         * actually push the data through unless tty->low_latency is set */
        tty_buffer_request_room (tty, data_size);
            tty_insert_flip_string(tty, data, data_size);
            tty_flip_buffer_push(tty);

        /* resubmit the timer again */
        ms_ptr->timer->expires = jiffies + DELAY_TIME;
        add_timer(ms_ptr->timer);
    }



    //// Define the open function ////

   static int tty_ms_open(struct tty_struct *tty_this, struct file *file_this)
{

    printk(KERN_ALERT "tty_ms driver: OPENED ...\n");
    struct ms_tty_serial *ms_ptr;
    struct timer_list *timer;
    int index;

    /* initialize the pointer in case something fails */
    tty_this->driver_data = NULL;

    /* get the serial object associated with this tty pointer */
    index = tty_this->index;
    ms_ptr = ms_tty_table[index];
    if (ms_ptr == NULL) {
        /* first time accessing this device, create it */
        ms_ptr = kmalloc(sizeof(*ms_ptr), GFP_KERNEL);
        if (!ms_ptr)
            return -ENOMEM;

//      init_MUTEX(&ms_ptr->sem);       /* didn't work for this kernel version 3.5.0 */

        #ifndef init_MUTEX      /* sema_init is to be used for kernel 2.6.37 and above */
        sema_init(&ms_ptr->sem,1);
        #else
        init_MUTEX(&ms_ptr->sem);
        #endif

        ms_ptr->open_count = 0;
        ms_ptr->timer = NULL;

        ms_tty_table[index] = ms_ptr;
    }

    down(&ms_ptr->sem);

    /* save our structure within the tty structure */
    tty_this->driver_data = ms_ptr;
    ms_ptr->tty = tty_this;
    ms_ptr->filp = file_this;       // to be tried
    ++ms_ptr->open_count;
    if (ms_ptr->open_count == 1) {
        /* this is the first time this port is opened */
        /* do any hardware initialization needed here */

        /* create timer and submit it */
        if (!ms_ptr->timer) {
            timer = kmalloc(sizeof(*timer), GFP_KERNEL);
            if (!timer) {
                up(&ms_ptr->sem);
                return -ENOMEM;
            }
            ms_ptr->timer = timer;
        }

        init_timer (ms_ptr->timer);     // to be tried
        ms_ptr->timer->data = (unsigned long )ms_ptr;
        ms_ptr->timer->expires = jiffies + DELAY_TIME;
        ms_ptr->timer->function = ms_tty_timer;
        add_timer(ms_ptr->timer);
    }

    up(&ms_ptr->sem);
    return 0;
}

    //// Define the close function ////

    static void do_close(struct ms_tty_serial *ms_ptr)
    {
        down(&ms_ptr->sem);

        if (!ms_ptr->open_count) {
            /* port was never opened */
            goto exit;
        }

        --ms_ptr->open_count;
        if (ms_ptr->open_count <= 0) {
            /* The port is being closed by the last user. */
            /* Do any hardware specific stuff here */

            /* shut down our timer */
            del_timer(ms_ptr->timer);
        }
    exit:
        up(&ms_ptr->sem);
    }

    static void tty_ms_close(struct tty_struct *tty_this, struct file *file_this)
    {

        printk(KERN_ALERT "tty_ms driver: CLOSING ...\n");
        struct ms_tty_serial *ms_ptr = tty_this->driver_data;

        if (ms_ptr)
            do_close(ms_ptr);
    }


    //// Define the write function ////

    static int tty_ms_write(struct tty_struct *tty_this, const unsigned char *tty_buff, int count)
    {

        printk(KERN_ALERT "tty_ms driver: WRITING ...\n");
    struct ms_tty_serial *ms_ptr = tty_this->driver_data;
        int i;
        int retval = -EINVAL;

        if (!ms_ptr)
            return -ENODEV;

        down(&ms_ptr->sem);

        if (!ms_ptr->open_count)
            /* port was not opened */
            {
            up(&ms_ptr->sem);
            return retval;
            }
        /* fake sending the data out a hardware port by
         * writing it to the kernel debug log.
         */
        printk(KERN_DEBUG "%s - ", __FUNCTION__);
        for (i = 0; i < count; ++i)
            printk("%02x ", tty_buff[i]);
        printk("\n");
        return 0;

    }

    // Define the operations for tty driver in tty_operations struct //

    static struct tty_operations tty_ms_ops = {
    .open = tty_ms_open,
    .close = tty_ms_close,
    .write = tty_ms_write,
    //.set_termios = tty_ms_set_termios,
    };

    ///////////////////////////////////////// Module Initialization Starts ////////////////////////////////////

    static struct tty_driver *tty_ms_driver;

    static int tty_ms_init(void)
    {
    //  static int result;
        static int retval,iter;

    // Allocate the tty_driver struct for this driver //

        tty_ms_driver = alloc_tty_driver(MS_TTY_NUM_DEV);
        if (!tty_ms_driver)
        return -ENOMEM;         // Error NO Memory , allocation failed
        else
        printk(KERN_INFO "tty_driver structure allocated..!!");     //debug line

    // Initialize the allocated tty_driver structure //
        tty_ms_driver->magic=TTY_DRIVER_MAGIC;
        tty_ms_driver->owner = THIS_MODULE;
        tty_ms_driver->driver_name = "tty_ms";
        tty_ms_driver->name = "ttyms";
        tty_ms_driver->major = MS_TTY_MAJOR,
        tty_ms_driver->type = TTY_DRIVER_TYPE_SERIAL,
        tty_ms_driver->subtype = SERIAL_TYPE_NORMAL,
        tty_ms_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,
        tty_ms_driver->init_termios = tty_std_termios;
        tty_ms_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
        tty_set_operations(tty_ms_driver, &tty_ms_ops);
        printk(KERN_INFO "allocated tty_driver structure -INITIALIZED.");   //debug line
    //// Register this driver with the tty core ////

        retval = tty_register_driver(tty_ms_driver);
        if (retval) {
        printk(KERN_ERR "failed to register tty_ms driver\n tty registration returned %d", retval);
        put_tty_driver(tty_ms_driver);
        return retval;
        }

    //// Register the tty devices(nodes) with the tty core ////

        for (iter = 0; iter < MS_TTY_NUM_DEV ; ++iter)
        tty_register_device(tty_ms_driver, iter, NULL);
        return 0;       // All initializations done

    }           // init func ends

    ///////////////////////////////////////// Module Initialization Ends ////////////////////////////////////

    ///////////////////////////////////////// Module cleanup Starts ////////////////////////////////////

    static void tty_ms_terminate(void)
    {
        static int iter;
        struct ms_tty_serial *tty_ser;
        printk(KERN_ALERT "tty_ms driver: Unloading...\n");

        for(iter=1;iter<=MS_TTY_NUM_DEV;iter++)
         tty_unregister_device(tty_ms_driver,iter);   //unregister all the devices, from tty layer 
        tty_unregister_driver(tty_ms_driver);       // Now unregister the driver from tty layer

        /* shut down all of the timers and free the memory */
        for (iter = 0; iter < MS_TTY_NUM_DEV; ++iter) {
            tty_ser = ms_tty_table[iter];
            if (tty_ser) {
                /* close the port */
                while (tty_ser->open_count)
                    do_close(tty_ser);

                /* shut down our timer and free the memory */
                del_timer(tty_ser->timer);
                kfree(tty_ser->timer);
                kfree(tty_ser);
                ms_tty_table[iter] = NULL;
            }
        }   

         dev_t devno=MKDEV(major_num,0);  // wrap major/minor numbers in a dev_t structure , to pass for deassigning.
         unregister_chrdev_region(devno,MS_TTY_NUM_DEV); 

    }

    ///////////////////////////////////////// Module cleanup ends ////////////////////////////////////

    module_init(tty_ms_init);
    module_exit(tty_ms_terminate);