如何打印特定内存地址的值?
How to printk value of specific memory adrress?
我想从内核中的特定内存地址打印 12byte。
以下是我的硬编码。
void *Unique_Id = 0x5C000234;
for(int i=0;i<12;i++){
printk("%02x ", *(uniqueId+i));
}
这是正确的吗?或者还有其他建议吗?
请多多指教
谢谢。
我认为既然你想打印字节,那么只需将 uniqueID + i 转换为 character/unsigned 字符指针
printk("%02x ", *((char *)(uniqueId+i)));
在没有转换的情况下将整数转换为指针会产生编译器警告,因此初始化应该是:
void *Unique_Id = (void *)0x5C000234;
内核使用虚拟地址访问内存位置。数字 0x5C000234
很可能是硬件 I/O 内存 space 中某些寄存器的 物理地址 。在访问它们的内容之前,需要将它们重新映射到内核的虚拟地址 space。对于硬件 I/O 内存,这是由 ioremap()
或 ioremap_*()
变体之一完成的。指向重新映射的 I/O 内存的指针应标记为 __iomem
:
void __iomem *Unique_Id = ioremap(0x5C000234, 12);
(第二个参数是映射的字节长度。)
如果映射内存失败,ioremap()
将 return NULL
,因此应该检查一下:
if (Unique_Id == NULL) {
/* Deal with the error */
当不再需要映射时,可以调用iounmap()
取消映射:
iounmap(Unique_Id);
重新映射的 I/O 内存的内容不应直接访问。有用于访问 8 位、16 位、32 位和(在 64 位系统上)64 位量的特定函数。它们是 readb()
和 writeb()
(8 位)、readw()
和 writew()
(16 位)、readl()
和 writel()
(32-位),以及 readq()
和 writeq()
(64 位)。所以打印 12 个连续字节的 for
循环可以写成:
for (int i = 0; i < 12; i++) {
printk("%02x ", readb(Unique_Id + i));
}
(请注意 void *
上的指针算法是 GCC 扩展,但受到 Linux 内核开发人员的祝福。)
还有 memcpy_fromio()
和 memcpy_toio()
函数用于在普通内核内存和 I/O 内存之间进行复制,因此可以将 12 字节的 ID 复制到 12 字节的数组中,使用memcpy_fromio()
:
u8 id[12];
memcpy_fromio(id, Unique_Id, 12);
for (int i = 0; i < 12; i++) {
printk("%02x ", id[i]);
}
(u8
是由 #include <linux/types.h>
定义的无符号 8 位整数类型。)
printk()
的正常用法是在格式字符串前加一个“log-level”宏,例如KERN_INFO
(依靠C字符串字面量拼接将其与格式字符串组合),并以 new-line 字符结束格式字符串:
for (int i = 0; i < 12; i++) {
printk(KERN_INFO "%02x\n", id[i]);
}
这会将内核日志中的 ID 分成 12 行。有一个特殊的 KERN_CONT
宏指示该消息是前一条消息的延续,因此组合 printk()
调用的正确方法如下:
printk(KERN_INFO "%02x ", id[0]);
for (int i = 1; i < 11; i++) {
printk(KERN_CONT "%02x ", id[i]);
}
printk(KERN_CONT, "%02x\n", id[11]);
(最好使用 snprintf()
在 char
的临时数组中构造一个字符串,并使用对 printk()
的一次调用来一次性打印所有内容。 )
内核的打印格式字符串处理有特殊的扩展,其中用于打印指针值的 p
说明符可以添加各种字符作为后缀以改变其行为。例如,12 字节的 ID 可以打印为:
u8 id[12];
memcpy_fromio(id, Unique_Id, 12);
printk(KERN_INFO "%12ph\n", id);
这适用于最多 64 个字节的十六进制字符串。
有关 printk
格式说明符的详细信息,请参阅 How to get printk format specifiers right。
内核有一个 print_hex_dump()
函数用于打印十六进制转储。 12字节的ID可以打印如下:
u8 id[12];
memcpy_fromio(id, Unique_Id, 12);
print_hex_dump(KERN_INFO, "Unique_Id: ", DUMP_PREFIX_NONE, 12, 1, id, 12, false);
(print_hex_dump()
的参数为level
(如KERN_INFO
),prefix_str
(每行输出前的字符串),prefix_type
( DUMP_PREFIX_NONE
、DUMP_PREFIX_ADDRESS
或 DUMP_PREFIX_OFFSET
)、rowsize
(每行输出打印的字节数)、groupsize
(每行打印的字节数)之一space-separated 组),buf
(指向要转储的数据的指针),len
(要转储的数据的长度),ascii
(如果为真,则在十六进制输出后包括 ASCII)。
我想从内核中的特定内存地址打印 12byte。
以下是我的硬编码。
void *Unique_Id = 0x5C000234;
for(int i=0;i<12;i++){
printk("%02x ", *(uniqueId+i));
}
这是正确的吗?或者还有其他建议吗?
请多多指教
谢谢。
我认为既然你想打印字节,那么只需将 uniqueID + i 转换为 character/unsigned 字符指针
printk("%02x ", *((char *)(uniqueId+i)));
在没有转换的情况下将整数转换为指针会产生编译器警告,因此初始化应该是:
void *Unique_Id = (void *)0x5C000234;
内核使用虚拟地址访问内存位置。数字
0x5C000234
很可能是硬件 I/O 内存 space 中某些寄存器的 物理地址 。在访问它们的内容之前,需要将它们重新映射到内核的虚拟地址 space。对于硬件 I/O 内存,这是由ioremap()
或ioremap_*()
变体之一完成的。指向重新映射的 I/O 内存的指针应标记为__iomem
:void __iomem *Unique_Id = ioremap(0x5C000234, 12);
(第二个参数是映射的字节长度。)
如果映射内存失败,ioremap()
将 returnNULL
,因此应该检查一下:if (Unique_Id == NULL) { /* Deal with the error */
当不再需要映射时,可以调用
iounmap()
取消映射:iounmap(Unique_Id);
重新映射的 I/O 内存的内容不应直接访问。有用于访问 8 位、16 位、32 位和(在 64 位系统上)64 位量的特定函数。它们是
readb()
和writeb()
(8 位)、readw()
和writew()
(16 位)、readl()
和writel()
(32-位),以及readq()
和writeq()
(64 位)。所以打印 12 个连续字节的for
循环可以写成:for (int i = 0; i < 12; i++) { printk("%02x ", readb(Unique_Id + i)); }
(请注意
void *
上的指针算法是 GCC 扩展,但受到 Linux 内核开发人员的祝福。)还有
memcpy_fromio()
和memcpy_toio()
函数用于在普通内核内存和 I/O 内存之间进行复制,因此可以将 12 字节的 ID 复制到 12 字节的数组中,使用memcpy_fromio()
:u8 id[12]; memcpy_fromio(id, Unique_Id, 12); for (int i = 0; i < 12; i++) { printk("%02x ", id[i]); }
(
u8
是由#include <linux/types.h>
定义的无符号 8 位整数类型。)printk()
的正常用法是在格式字符串前加一个“log-level”宏,例如KERN_INFO
(依靠C字符串字面量拼接将其与格式字符串组合),并以 new-line 字符结束格式字符串:for (int i = 0; i < 12; i++) { printk(KERN_INFO "%02x\n", id[i]); }
这会将内核日志中的 ID 分成 12 行。有一个特殊的
KERN_CONT
宏指示该消息是前一条消息的延续,因此组合printk()
调用的正确方法如下:printk(KERN_INFO "%02x ", id[0]); for (int i = 1; i < 11; i++) { printk(KERN_CONT "%02x ", id[i]); } printk(KERN_CONT, "%02x\n", id[11]);
(最好使用
snprintf()
在char
的临时数组中构造一个字符串,并使用对printk()
的一次调用来一次性打印所有内容。 )内核的打印格式字符串处理有特殊的扩展,其中用于打印指针值的
p
说明符可以添加各种字符作为后缀以改变其行为。例如,12 字节的 ID 可以打印为:u8 id[12]; memcpy_fromio(id, Unique_Id, 12); printk(KERN_INFO "%12ph\n", id);
这适用于最多 64 个字节的十六进制字符串。
有关
printk
格式说明符的详细信息,请参阅 How to get printk format specifiers right。内核有一个
print_hex_dump()
函数用于打印十六进制转储。 12字节的ID可以打印如下:u8 id[12]; memcpy_fromio(id, Unique_Id, 12); print_hex_dump(KERN_INFO, "Unique_Id: ", DUMP_PREFIX_NONE, 12, 1, id, 12, false);
(
print_hex_dump()
的参数为level
(如KERN_INFO
),prefix_str
(每行输出前的字符串),prefix_type
(DUMP_PREFIX_NONE
、DUMP_PREFIX_ADDRESS
或DUMP_PREFIX_OFFSET
)、rowsize
(每行输出打印的字节数)、groupsize
(每行打印的字节数)之一space-separated 组),buf
(指向要转储的数据的指针),len
(要转储的数据的长度),ascii
(如果为真,则在十六进制输出后包括 ASCII)。