Linux内核printk实现

基于MTK 6595分析,内核版本3.10.5

1 Printk函数分析

内核为Printk维护一个环形缓冲区,其大小为:

#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)

大小可以通过CONFIG_LOG_BUF_SHIFT去控制

1.1函数原型如下:

asmlinkage int printk(const char *fmt, ...)

{

va_listargs;

intr;

va_start(args,fmt);

r= vprintk_emit(0, -1, NULL, 0, fmt, args);

va_end(args);

returnr;

}

asmlinkage int vprintk_emit(int facility,int level,

const char *dict, size_t dictlen,

const char *fmt, va_list args)

{

staticint recursion_bug;

staticchar textbuf[LOG_LINE_MAX];

char*text = textbuf;

size_ttext_len;

enumlog_flags lflags = 0;

unsignedlong flags;

intthis_cpu;

intprinted_len = 0;

int in_irq_disable, in_non_preempt;

in_irq_disable = irqs_disabled();//是否为中断上下文

in_non_preempt = in_atomic();//是否为原子上下文

vscnprintf(text,sizeof(textbuf), fmt, args);

memset(text,0x0, sizeof(textbuf));

boot_delay_msec(level);

/* This stops the holder of console_sem just where we want him */

local_irq_save(flags);

//禁止本地中断,因为printk可以在任何环境中使用,而下面又要获取logbug_lock去保护环形缓冲区,所以需要禁止本地中断,防止死锁.

this_cpu= smp_processor_id();

/*

* Ouch, printk recursed into itself!

*/

lockdep_off();

raw_spin_lock(&logbuf_lock);//禁止本地中断

logbuf_cpu= this_cpu ;

//把log保存到text中

text_len= vscnprintf(text, sizeof(textbuf), fmt, args);

/*mark and strip a trailing newline */

if(text_len && text[text_len-1] == ‘\n‘) {

text_len--;

lflags|= LOG_NEWLINE;

}

/*strip kernel syslog prefix and extract log level or control flags */

if(facility == 0) {

intkern_level = printk_get_level(text);//获取信息level

if(level == -1)

level= default_message_loglevel;

#ifdef CONFIG_PRINTK_PROCESS_INFO

if (in_irq_disable)

__raw_get_cpu_var(printk_state) = ‘-‘;//如果为中断上下文,在log中加”-“标志

#ifdef CONFIG_MT_PRINTK_UART_CONSOLE

else if (printk_disable_uart == 0)

__raw_get_cpu_var(printk_state) = ‘.‘; //如果为非中断上下文,在log中加”.”标志

#endif

else

__raw_get_cpu_var(printk_state) = ‘ ‘;

#endif

if(!(lflags & LOG_NEWLINE)) {

………………………………………….

}else {

boolstored = false;

if(!stored)

log_store(facility,level, lflags, 0,//把log保存在环形缓冲区中,这个重点分析

dict, dictlen, text, text_len);

}

printed_len+= text_len;

/*

* Try to acquire and then immediately releasethe console semaphore.

* The release will print out buffers and wakeup /dev/kmsg and syslog()

* users.

*

* The console_trylock_for_printk() functionwill release ‘logbuf_lock‘

* regardless of whether it actually gets theconsole semaphore or not.

*/

if(console_trylock_for_printk(this_cpu))//尝试获取控制台信号量,

console_unlock();//输出信息到控制台

lockdep_on();

out_restore_irqs:

local_irq_restore(flags);//恢复本地cpu
中断

returnprinted_len;

}

1.2 log_store 分析

Log_store函数主要想环形缓冲区中保存log信息,环形缓冲区组织如下:

(struct log +log_data) ---(struct log +log_data)—(struct log + log_data)……..

也就是说每行Log都是以struct log开头,然后接着存放log 数据

structlog {

u64 ts_nsec;             /*timestamp in nanoseconds *//时间标志

u16 len;             /*length of entire record *///总长度

u16 text_len;            /*length of text buffer */ //log 数据长度

u16 dict_len;             /*length of dictionary buffer */

u8 facility;                  /*syslog facility */

u8 flags:5;                  /*internal record flags */

u8 level:3;                  /*syslog level */ //调试级

};

另外内核还定义了四个全局变量来管理缓冲区

/* index and sequence number of the first record stored in thebuffer */

/*static*/ u64 log_first_seq; //指向当前可读的struct log
索引号

/*static*/ u32 log_first_idx; //指向环形缓冲区可以读的位置

/* index and sequence number of the next record to store in thebuffer */

/*static*/ u64 log_next_seq;//指向当前可写的struct log
索引号

/*static*/ u32 log_next_idx; //指向环形缓冲区可写的位置

static void log_store(int facility, int level,

enum log_flags flags, u64 ts_nsec,

const char *dict, u16 dict_len,

const char *text, u16 text_len)

{

struct log *msg;

u32 size, pad_len;

int this_cpu = smp_processor_id();

char state =__raw_get_cpu_var(printk_state);

/*printk prefix {*/

char tbuf[50];

unsigned tlen;

if (console_suspended == 0) {

//这里给Log添加前缀:如

// [  121.939790].(0)[124:bat_thread_kthr]

121.939790: 当前时间

.         : 处于非中断上下文

(0)          : 在cpu 0上执行

124:bat_thread_kthr :在124号进程进程调用了printk,bat_thread_kthr为线程名

tlen = snprintf(tbuf, sizeof(tbuf),"%c(%x)[%d:%s]",

state, this_cpu,current->pid, current->comm);

} else {

tlen = snprintf(tbuf, sizeof(tbuf),"%c%x)", state, this_cpu);

}

/*printk prefix }*/

/* number of ‘\0‘ padding bytes to next message*/

size = sizeof(struct log) + text_len +tlen +dict_len;

pad_len = (-size) & (LOG_ALIGN - 1);

size += pad_len;

while (log_first_seq < log_next_seq) {

u32 free;

if (log_next_idx > log_first_idx)

free = max(log_buf_len -log_next_idx, log_first_idx);//选择更大的free空间

else

free = log_first_idx -log_next_idx;
//log_next_idx和log_first_idx直接的内存空间

if (free > size + sizeof(structlog)) //log_next_idx后的内存不足

break;

/* drop old messages until we haveenough contiuous space */

log_first_idx =log_next(log_first_idx);//把log_first_idx移到下一个,此时旧log会被覆盖

log_first_seq++;

}

//这种情况下需要重置log_next_idx

if (log_next_idx + size + sizeof(struct log)>= log_buf_len) {

/*

* This message + an additional empty headerdoes not fit

* at the end of the buffer. Add an emptyheader with len == 0

* to signify a wrap around.

*/

memset(log_buf + log_next_idx, 0,sizeof(struct log));

log_next_idx = 0;

}

/* fill message */ //填充信息

msg = (struct log *)(log_buf + log_next_idx);

//memcpy(log_text(msg), text, text_len);

memcpy(log_text(msg), tbuf, tlen);

memcpy(log_text(msg) + tlen, text, text_len);

text_len += tlen;

msg->text_len = text_len;

memcpy(log_dict(msg), dict, dict_len);

msg->dict_len = dict_len;

msg->facility = facility;

msg->level = level & 7;

msg->flags = flags & 0x1f;

if (ts_nsec > 0)

msg->ts_nsec = ts_nsec;

else

msg->ts_nsec = local_clock();//获取时间信息

memset(log_dict(msg) + dict_len, 0, pad_len);

msg->len = sizeof(struct log) + text_len +dict_len + pad_len;

/* insert message */

log_next_idx += msg->len;//指向下一个可以写位置

log_next_seq++;//sep相应加1

}

1.3 console_unlock分析

Console_unlock函数主要把环形缓存区的buf输出到串口,并唤醒需要读取Log的线程

2 用户空间获取kernel  log

在printk中实现了/dev/kmsg和do_syslog系统调用,从而用户空间可以通过syslog 或klogctl .

获取内核log

Kmsg.c中通过调用do_syslog函数读取log,从而实现/proc/kmsg函数

3. printk.c提供的其他函数

int __printk_ratelimit(const char *func) //每个5s之内,只能打印10个msg

bool kmsg_dump_get_line(struct kmsg_dumper*dumper, bool syslog,

char*line, size_t size, size_t *len)//buf中获取一个record,也称为一个行log

bool kmsg_dump_get_buffer(structkmsg_dumper *dumper, bool syslog,

char *buf, size_t size, size_t *len)//从buf中获取size字节的log

时间: 2024-07-31 05:00:06

Linux内核printk实现的相关文章

linux 内核 printk 使用

在linux 中,内核打印语句 printk() 会将内核信息输出到内核信息缓冲区中.内核信息缓冲区是一个环形 缓冲区(ring buffer),因此,如果插入的信息过多,就会将之前的信息冲刷掉. printk() 定义了8个消息级别,分为级别0 ~ 7,级别越低(数值越大)的消息越不重要,第0级是紧急事件, 第7级是调试级,在内核中 include/linux/printk.h 中的定义如下: #define KERN_EMERG "<0>" /* system is u

linux内核打印数据到串口控制台,printk数据不打印问题

linux内核打印数据到串口控制台问题 原文来源:http://i.cnblogs.com/EditPosts.aspx?opt=1 1.查看当前控制台的打印级别 cat /proc/sys/kernel/printk 4    4    1    7 其中第一个“4”表示内核打印函数printk的打印级别,只有级别比他高的信息才能在控制台上打印出来,既 0-3级别的信息 2.修改打印 echo "新的打印级别  4    1    7" >/proc/sys/kernel/pr

linux内核编译与开发

一.Linux内核简介linux kernel map: linux 系统体系结构: linux kernel体系结构: arm有7种工作模式,x86也实现了4个不同级别RING0-RING3,RING0级别最高, 这样linux用户代码运行在RING3下,内核运行在RING0,这样系统本身就得到了 充分的保护 用户空间(用户模式)转到内核空间(系统模式)方法: ·系统调用 ·硬件中断 linux kernel 体系结构: 虚拟文件系统VFS: VFS(虚拟文件系统)隐藏各种文件系统的具体细节,

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线

如何切入 Linux 内核源代码

Makefile不是Make Love 从前在学校,混了四年,没有学到任何东西,每天就是逃课,上网,玩游戏,睡觉.毕业的时候,人家跟我说Makefile我完全不知,但是一说Make Love我就来劲了,现在想来依然觉得丢人. 毫不夸张地说,Kconfig和Makefile是我们浏览内核代码时最为依仗的两个文件.基本上,Linux内核中每一个目录下边都会有一个 Kconfig文件和一个Makefile文件.对于一个希望能够在Linux内核的汪洋代码里看到一丝曙光的人来说,将它们放在怎么重要的地位都

Linux内核剖析 之 进程简介

1.概念 1.1  什么是进程? 进程是程序执行的一个实例,可以看作充分描述程序已经执行到何种程度的数据结构的汇集. 从内核观点看,进程的目的就是担当分配系统资源(CPU时间,内存等)的实体. 我们熟悉的fork()库函数,它有两种用法: (1).一个父进程希望复制自己,使父子进程执行不同的代码段,常用于网络服务程序. (2).一个进程要执行一个不同的程序,fork()后立即exec(),如shell. 1.2  什么是线程? 有时候,一个进程希望有多个执行流,如一款麻将游戏,三个由电脑控制的人

Linux内核设计基础(十)之内核开发与总结

(1)Linux层次结构: (2)Linux内核组成: 主要由进程调度(SCHED).内存管理(MM).虚拟文件系统(VFS).网络接口(NET)和进程间通信(IPC)等5个子系统组成. (3)与Unix的差异: Linux支持动态加载内核模块 支持对称多处理(SMP)机制 Linux内核可以抢占 Linux内核并不区分线程和其他的一般进程 Linux提供具有设备类的面向对象的设备模型.热插拔事件,以及用户空间的设备文件系统(sysfs) (4)内核开发的特点: 内核编程时既不能访问C库也不能访

《linux 内核分析》 第二周 实验

王一 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 本次课的核心是通过中断机制完成进程的调度 ,在本次课程中__init my_start_kernel作为入口函数,定义0号进程的tPCB结构体,通过复制来制造其他进程的tPCB数据结构,中断时间函数被 my_timer_handler周期性的调用来修改my_need_sched 的值,而0号进程一直在检测my_need_sched 的

Linux Kernel - Debug Guide (Linux内核调试指南 )

http://blog.csdn.net/blizmax6/article/details/6747601 linux内核调试指南 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 ***第一部分:基础知识*** 总纲:内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 原理理解的陷阱 建立调试环境 发行版的选择和安装 安装交叉编译工具 bin工具集的使用 qemu的使用 initrd.img的原理与制作 x86虚拟调试环境的建立 arm虚拟调试环境的建立 arm开发板调试环