Linux kernel printk 占位符 %p 和 %px

1. %p

Linux 内核里的 printk 可以打印指针地址,这和 printf() 是一样的:

void *p = 0x1234;
printk("p = %p\n", p);

这里是想打印所指向的地址。实际运行起来后,看到的输出却是这样的:

p = (____ptrval____)

或者是这样的(并不是真实的地址):

p = 000000008a425fc2

通过查询代码和文档,内核打印这样实现目的,是为了避免内核地址的泄漏。

而显示内核地址,很可能仅仅是出于开发和调试的目的。对于研发过程中的这种需求,从文档中可以看到,可以在 bootargs 里加上参数 no_hash_pointers 来解决。

即使是这样,内核里也有非常明确的提醒,除非是调试,一定要避免使用这个选项。

[    0.000000] **********************************************************
[    0.000000] **   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE   **
[    0.000000] **                                                      **
[    0.000000] ** This system shows unhashed kernel memory addresses   **
[    0.000000] ** via the console, logs, and other interfaces. This    **
[    0.000000] ** might reduce the security of your system.            **
[    0.000000] **                                                      **
[    0.000000] ** If you see this message and you are not debugging    **
[    0.000000] ** the kernel, report this immediately to your system   **
[    0.000000] ** administrator!                                       **
[    0.000000] **                                                      **
[    0.000000] **   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE   **
[    0.000000] **********************************************************

2. %px

当然,如果实在是想打印内核地址,可以用 %px 占位符,比如:

void *p = 0x1234;
printk("p = %px\n", p);

同样的,按文档里的使用约定,这种用法是不合理的,应该尽量避免。

3. %p 的扩展

lib/vsprintf.c 里的 pointer() 函数可以看到,%p 后面还可以跟更多占位符,来实现多种内核数据类型的格式化,比如字符串、MAC 地址、IP 地址、UUID、struct clk 名字、fourcc 等等。因为功能太多了,确实不容易记忆,所以在需要使用的时候,参考 pointer() 函数里详细的注释说明,就可以了。

参考资料

  • lib/vsprintf.c
  • Documentation/core-api/printk-formats.rst

Read More: