美文网首页
设备驱动程序调试printk讲解和使用,打印到proc虚拟文件

设备驱动程序调试printk讲解和使用,打印到proc虚拟文件

作者: sgy1993 | 来源:发表于2017-08-01 21:49 被阅读0次
    1. u-boot参数 console = ttySAC0
      UBOOT传入console=ttySAC0 console=tty1
    __setup("console=", console_setup);
    
    • console_setup
        add_preferred_console(name, idx, options);
    
    • add_preferred_console
        c = &console_cmdline[i];
        memcpy(c->name, name, sizeof(c->name));
    

    console_cmdline在哪里调用

    • register_console里使用
      linux-2.6.22\trunk\drivers\serial\s3c2410.c
        register_console(&s3c24xx_serial_console);
    
    printk
      r = vprintk(fmt, args);
        void release_console_sem(void)
          call_console_drivers(_con_start, _log_end);
            _call_console_drivers
              __call_console_drivers
                con->write(con, &LOG_BUF(start), end - start);
    
    
    打印有级别,默认的打印级别是4,可以用dmesg命令将log_buf的数据打印出来
    printk("abc");
    printk(KERNEL_WARING"abc");
    

    打印级别的相关解释

    # cat proc/sys/kernel/printk
    7       4       1       7
    
    #define KERN_EMERG  "<0>"   /* system is unusable           */
    #define KERN_ALERT  "<1>"   /* action must be taken immediately */
    #define KERN_CRIT   "<2>"   /* critical conditions          */
    #define KERN_ERR    "<3>"   /* error conditions         */
    #define KERN_WARNING    "<4>"   /* warning conditions           */
    #define KERN_NOTICE "<5>"   /* normal but significant condition */
    #define KERN_INFO   "<6>"   /* informational            */
    #define KERN_DEBUG  "<7>"   /* debug-level messages         */
    
    

    因此修改第一个数字 7,就可以控制printk的打印信息开放或者关闭,将其改的特别小,就可以屏蔽所有打印,将其改的特别大,就能放开所有打印
     u-boot可以设置loglevel=xx来更改参数,这样在启动内核时不用查看内核的打印信息,加快启动速度,以下是两种方法

    1..---------------------------------------------
    static int __init loglevel(char *str)
    {
        get_option(&str, &console_loglevel);
        return 1;
    }
    
    __setup("loglevel=", loglevel);
    2..------------------------------------------------
    __setup("debug", debug_kernel);
    __setup("quiet", quiet_kernel);
    

    打印到proc虚拟文件

    printk的信息一份存在缓冲区里(其实也是一个文件),一份打印到串口

    # ls -l /proc/kmsg
    -r--------    1 0        0               0 Jan  1 00:11 /proc/kmsg
    # 
    

    proc是一个虚拟文件系统

    # cat /etc/init.d/rcS 
    #!/bin/sh
    ifconfig eth0 192.168.0.90
    
    mount -a
    mkdir /dev/pts
    mount -t devpts devpts /dev/pts
    echo /sbin/mdev > /proc/sys/kernel/hotplug
    mdev -s
    if [ ! -e /etc/pointercal ] 
    then
    /bin/ts_cal.sh
    fi
    /bin/qpe.sh &
    # 
    

    mount -a挂接以下文件中所有的文件系统

    # cat /etc/fstab 
    # device     mount-point    type   options        dump  fsck order
    proc           /proc        proc   defaults        0     0
    tmpfs          /tmp         tmpfs  defaults        0     0
    sysfs          /sys         sysfs  defaults        0     0
    tmpfs          /dev         tmpfs  defaults        0     0
    # 
    

    怎么把printk信息抽出来

    仿照 my log_buf,/proc/mymsg
    

    步骤一:先创建设备文件

    static int __init mymsg_init(void)
    {
        myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
        return 0;
    }
    
    
    static void __exit mymsg_exit(void)
    {
        remove_proc_entry("mymsg", &proc_root);
    }
    
    • cat新创建的文件时,会调用myentry->proc_fops里的read函数读取数据,实现环形缓冲区的操作函数

    写:buf[w] = val; w = w + 1
    读:val=buf[r], r = r + 1
    满:先不写数据,判断一下下一次写的位置是不是等于读

    • 缓冲区是否已经空了,如果读指针==写指针
    static int mylogbuf_isempty()
    {
        return (mylogbuf_read == mylogbuf_read);
    }
    

    完全版的代码,export_sympol之后,就可以在别的文件里使用myprintk函数打印了,然后

    cat /proc/mymsg就可以查看使用myprintk打印的信息
    
    #include <linux/types.h>
    #include <linux/errno.h>
    #include <linux/time.h>
    #include <linux/kernel.h>
    #include <linux/kernel_stat.h>
    #include <linux/fs.h>
    #include <linux/tty.h>
    #include <linux/string.h>
    #include <linux/mman.h>
    #include <linux/proc_fs.h>
    #include <linux/ioport.h>
    #include <linux/mm.h>
    #include <linux/mmzone.h>
    #include <linux/pagemap.h>
    #include <linux/swap.h>
    #include <linux/slab.h>
    #include <linux/smp.h>
    #include <linux/signal.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/seq_file.h>
    #include <linux/times.h>
    #include <linux/profile.h>
    #include <linux/utsname.h>
    #include <linux/blkdev.h>
    #include <linux/hugetlb.h>
    #include <linux/jiffies.h>
    #include <linux/sysrq.h>
    #include <linux/vmalloc.h>
    #include <linux/crash_dump.h>
    #include <linux/pid_namespace.h>
    #include <asm/uaccess.h>
    #include <asm/pgtable.h>
    #include <asm/io.h>
    #include <asm/tlb.h>
    #include <asm/div64.h>
    
    #define LEN 7
    static int mylogbuf_read;
    static int mylogbuf_read_buffer;
    static DECLARE_WAIT_QUEUE_HEAD(mywaitq);
    static int mylogbuf_write;
    static char my_logbuf[LEN] = {0};
    static char tmp_buf[LEN] = {0};
    
    
    
    static int myentry_open(struct inode *inode, struct file *file)
    {
        mylogbuf_read_buffer = mylogbuf_read;
        return 0;
    }
    
    
    static int mylogbuf_isempty(void)
    {
        return (mylogbuf_read_buffer == mylogbuf_write);
    }
    
    static int mylogbuf_isfull(void)
    {
        return (((mylogbuf_write + 1) % LEN  ) == mylogbuf_read);
    }
    
    static void mylogbuf_putc(unsigned char c)
    {
    
        if (mylogbuf_isfull()) {
        /*
            满了的话,舍弃掉后面的一个数据,如果不舍弃,写的话,读指针会等于写指针,误判缓冲区为空
            所以将读指针往后挪一个,舍弃掉这个数据.
        */
            mylogbuf_read = (mylogbuf_read + 1) % LEN;
        }
        my_logbuf[mylogbuf_write] = c;
        mylogbuf_write = (mylogbuf_write  + 1) % LEN;
            printk("func:%s, line:%d, mylogbuf_write:%d, mylogbuf_read:%d\n",
        __func__, __LINE__, mylogbuf_write, mylogbuf_read);
    
        wake_up(&mywaitq);
    }
    static int mylogbuf_getc(unsigned char *p)
    {
    
        if (mylogbuf_isempty())
        {
            return 0;
        }
        *p = my_logbuf[mylogbuf_read];
            printk("func:%s, line:%d, mylogbuf_read:%d, read_buffer:%d\n",
        __func__, __LINE__, mylogbuf_read, mylogbuf_read_buffer);
    
    
        mylogbuf_read_buffer = (mylogbuf_read_buffer + 1) % LEN;
        return 1;
    }
    
    
    int myprintk(const char *fmt, ...)
    {
        va_list args;
        int i, j;
    
        va_start(args, fmt);
        /*直接打印进去logbuf不会有读写指针*/
        i=vsnprintf(tmp_buf, INT_MAX, fmt, args);
        va_end(args);
        for (j = 0; j < i; j++) {
            mylogbuf_putc(tmp_buf[j]);
        }
        return i;
    
    }
    static struct proc_dir_entry *myentry = NULL;
    
    static ssize_t myentry_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
    {
    
        unsigned char c;
        int i = 0;
        int error;
    
        if ((file->f_flags & O_NONBLOCK) && mylogbuf_isempty()) {
            return -EAGAIN;
        }
        error = wait_event_interruptible(mywaitq, !mylogbuf_isempty());//contion等于假的时候会休眠
        printk("func:%s, line:%d, size:%d\n", __func__, __LINE__, size);
        while (!error && mylogbuf_getc(&c) &&(i < size)) {
            
            error = __put_user(c, buf);
            buf++;
            i++;
        }
    
        if (!error)
            error = i;
            
        return error;
    }
    
    
    static struct file_operations myentry_proc_fops = {
        .open = myentry_open,
        .read = myentry_read,
    };
    static int __init mymsg_init(void)
    {
        myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
        if (myentry)
            myentry->proc_fops = &myentry_proc_fops;
        return 0;
    }
    
    
    static void __exit mymsg_exit(void)
    {
        remove_proc_entry("mymsg", &proc_root);
    }
    EXPORT_SYMBOL(myprintk);
    module_init(mymsg_init);
    module_exit(mymsg_exit);
    
    MODULE_LICENSE("GPL");
    
    • 这里附上我的自己的一些分析
      每次读的时候,将mylogbuf_read_buffer更改为mylogbuf_read(实验后发现这样做不行,会不停大打印,见后面的分析)
      每次 mylogbuf_getc 读取log_buf的时候,不能用mylogbuf_read,他变了的话(会一直读到和log_write指针相等),下次在获取数据就没有数据了.

    一次的读取过程中总是读到log_buf里面没有数据为止,所以mylogbuf_isempty里面也要用read_buffer,有没有写满仍然需要使用buf_read来标志.

    因为cat命令会循环执行 myentry_read 函数,所以不能这里面使用

        mylogbuf_read_buffer = mylogbuf_read;
    

    这样判断log_buf是不是空的时候,每次都不为空,尽管在mylogbuf_getc都将mylogbuf_read_buffer加1,第一次读取完,判断log为空成立时,又跑去执行myentry_read,所以赋值需要选在只执行一次的函数当中,比如open函数

        mylogbuf_read_buffer = (mylogbuf_read_buffer + 1) % LEN;
    

    如果我将LEN的长度缩小到7会怎么样
    myprintk("11111111\n");
    usbmouse.c总共打印9个字符,LEN = 7
    0 1 2 3 4 5 6
    log_write等于6的时候,这个时候程序已经判断环形缓冲区满了buf_read一开始为0,舍弃掉0数据,buf_read = 1,6号内存也已经将数据1填进去了.此时buf_write = 0.这时总共写了7个数据,在写两个数据之后,就会变成buf_write = 2(因为肯定写到1号内存,w然后加1),read_buf = 3,此时执行cat会发生什么情况呢?

    相关文章

      网友评论

          本文标题:设备驱动程序调试printk讲解和使用,打印到proc虚拟文件

          本文链接:https://www.haomeiwen.com/subject/cenrlxtx.html