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

  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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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虚拟文件)