標籤:printk linux
基於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
Linux核心printk實現