设备驱动程序调试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 <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会发生什么情况呢?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容