06、printk执行过程
📢通过查看linux-source/kernel/printk/printk.c
中的源码可知,printk
函数一路通过vprintk_func
、vprintk_default
、vprintk_emit
、vprintk_store
、log_output
调用到log_store
函数。
一、printk
其中args
:fmt
的值就是我们printk
代入的参数
C
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
r = vprintk(fmt, args); //调用vprintk()
va_end(args);
return r;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
二、vprintk
- 打印信息放到临时缓冲区
printk_buf[]
- 从临时缓冲区
printk_buf[]
复制到循环缓冲区log_buf[]
每次拷贝前都要检查打印级别,若没有打印级别,便插入默认值default_message_loglevel
- 最后检查是否有注册的控制台,若有,便调用
release_console_sem()
C
asmlinkage int vprintk_emit(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args)
{
int printed_len;
bool in_sched = false;
unsigned long flags;
if (level == LOGLEVEL_SCHED) {
level = LOGLEVEL_DEFAULT;
in_sched = true;
}
boot_delay_msec(level);
printk_delay();
/* This stops the holder of console_sem just where we want him */
logbuf_lock_irqsave(flags);
printed_len = vprintk_store(facility, level, dict, dictlen, fmt, args);
logbuf_unlock_irqrestore(flags);
/* If called from the scheduler, we can not call up(). */
if (!in_sched) {
/*
* Disable preemption to avoid being preempted while holding
* console_sem which would prevent anyone from printing to
* console
*/
preempt_disable();
/*
* Try to acquire and then immediately release the console
* semaphore. The release will print out buffers and wake up
* /dev/kmsg and syslog() users.
*/
if (console_trylock_spinning())
console_unlock();
preempt_enable();
}
return printed_len;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
C
/* Must be called under logbuf_lock. */
int vprintk_store(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args)
{
static char textbuf[LOG_LINE_MAX];
char *text = textbuf;
size_t text_len;
enum log_flags lflags = 0;
/*
* The printf needs to come first; we need the syslog
* prefix which might be passed-in as a parameter.
*/
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) {
int kern_level;
while ((kern_level = printk_get_level(text)) != 0) {
switch (kern_level) {
case '0' ... '7':
if (level == LOGLEVEL_DEFAULT)
level = kern_level - '0';
/* fallthrough */
case 'd': /* KERN_DEFAULT */
lflags |= LOG_PREFIX;
break;
case 'c': /* KERN_CONT */
lflags |= LOG_CONT;
}
text_len -= 2;
text += 2;
}
}
#ifdef CONFIG_EARLY_PRINTK_DIRECT
printascii(text);
#endif
if (level == LOGLEVEL_DEFAULT)
level = default_message_loglevel;
if (dict)
lflags |= LOG_PREFIX|LOG_NEWLINE;
return log_output(facility, level, lflags,
dict, dictlen, text, text_len);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
- 其中打印等级"<
0
"至 "<7
"到底是什么?printk
的打印级别在include/linux/kernel.h
中:
C
#define KERN_EMERG "<0>" // 系统崩溃
#define KERN_ALERT "<1>" //必须紧急处理
#define KERN_CRIT "<2>" // 临界条件,严重的硬软件错误
#define KERN_ERR "<3>" // 报告错误
#define KERN_WARNING "<4>" //警告
#define KERN_NOTICE "<5>" //普通但还是须注意
#define KERN_INFO "<6>" // 信息
#define KERN_DEBUG "<7>" // 调试信息
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
printk()
如何加入这些前缀值?比如:printk
打印级别0
,可以输入printk(KERN_EMERG "abc");
或者printk( "<0>abc");
当printk()
里没有打印级别前缀,比如printk("abc ")
,便会加入默认值default_message_loglevel
- 那么默认值
default_message_loglevel
到底又是定义的哪个级别?
C
#define MINIMUM_CONSOLE_LOGLEVEL 1 //打印级别"<1>"
#define DEFAULT_CONSOLE_LOGLEVEL 7 //打印级别"<7>"
#define DEFAULT_MESSAGE_LOGLEVEL 4 //打印级别"<4>"
int console_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL, //=打印级别"<7>"
DEFAULT_MESSAGE_LOGLEVEL, // =打印级别"<4>"
MINIMUM_CONSOLE_LOGLEVEL, // =打印级别"<1>"
DEFAULT_CONSOLE_LOGLEVEL,
};
#define console_loglevel (console_printk[0]) //信息打印最大值, console_printk[1]=7
#define default_message_loglevel (console_printk[1]) //信息打印默认值, console_printk[1]=4
#define minimum_console_loglevel (console_printk[2]) //信息打印最小值, console_printk[2]=1
#define default_console_loglevel (console_printk[3])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
显然默认值default_message_loglevel
为打印级别"<4>
":当默认值default_message_loglevel
大于console_loglevel
时,表示控制台不会打印信息而最小值minimum_console_loglevel
,是用来判断是否大于console_loglevel
三、log_store
C
static size_t log_output(int facility, int level, enum log_flags lflags, const char *dict, size_t dictlen, char *text, size_t text_len)
{
/*
* If an earlier line was buffered, and we're a continuation
* write from the same process, try to add it to the buffer.
*/
if (cont.len) {
if (cont.owner == current && (lflags & LOG_CONT)) {
if (cont_add(facility, level, lflags, text, text_len))
return text_len;
}
/* Otherwise, make sure it's flushed */
cont_flush();
}
/* Skip empty continuation lines that couldn't be added - they just flush */
if (!text_len && (lflags & LOG_CONT))
return 0;
/* If it doesn't end in a newline, try to buffer the current line */
if (!(lflags & LOG_NEWLINE)) {
if (cont_add(facility, level, lflags, text, text_len))
return text_len;
}
/* Store it in the record log */
return log_store(facility, level, lflags, 0, dict, dictlen, text, text_len);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
C
/* insert record into the buffer, discard old ones, update heads */
static int 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 printk_log *msg;
u32 size, pad_len;
u16 trunc_msg_len = 0;
/* number of '\0' padding bytes to next message */
size = msg_used_size(text_len, dict_len, &pad_len);
if (log_make_free_space(size)) {
/* truncate the message if it is too long for empty buffer */
size = truncate_msg(&text_len, &trunc_msg_len,
&dict_len, &pad_len);
/* survive when the log buffer is too small for trunc_msg */
if (log_make_free_space(size))
return 0;
}
if (log_next_idx + size + sizeof(struct printk_log) > log_buf_len) {
/*
* This message + an additional empty header does not fit
* at the end of the buffer. Add an empty header with len == 0
* to signify a wrap around.
*/
memset(log_buf + log_next_idx, 0, sizeof(struct printk_log));
log_next_idx = 0;
}
/* fill message */
msg = (struct printk_log *)(log_buf + log_next_idx);
memcpy(log_text(msg), text, text_len);
msg->text_len = text_len;
if (trunc_msg_len) {
memcpy(log_text(msg) + text_len, trunc_msg, trunc_msg_len);
msg->text_len += trunc_msg_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 = size;
/* insert message */
log_next_idx += msg->len;
log_next_seq++;
return msg->text_len;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
log_buf
可以看到其是一个全局变量,其大小是1 << CONFIG_LOG_BUF_SHIFT
,CONFIG_LOG_BUF_SHIFT
定义在init/Kconfig
中其值一般是14
,即log_buf
一般为16KB
。