如何打印特定内存地址的值?

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)));
  1. 在没有转换的情况下将整数转换为指针会产生编译器警告,因此初始化应该是:

    void *Unique_Id = (void *)0x5C000234;
    
  2. 内核使用虚拟地址访问内存位置。数字 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);
    
  3. 重新映射的 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 位整数类型。)

  4. 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() 的一次调用来一次性打印所有内容。 )

  5. 内核的打印格式字符串处理有特殊的扩展,其中用于打印指针值的 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

  6. 内核有一个 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_NONEDUMP_PREFIX_ADDRESSDUMP_PREFIX_OFFSET)、rowsize(每行输出打印的字节数)、groupsize(每行打印的字节数)之一space-separated 组),buf(指向要转储的数据的指针),len(要转储的数据的长度),ascii(如果为真,则在十六进制输出后包括 ASCII)。