首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

Linux 核心 hexdump 實作分析:print_hex_dump 系列函式詳解

     編輯:關於C

核心工具函式位置

原始碼位於 kernel/lib/hexdump.c,提供標準化的二進位資料列印功能,廣泛用於驅動除錯。

核心轉換邏輯:位元組 → ASCII

const char hex_asc[] = "0123456789abcdef";
#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]

巧妙利用查表法,無分支、極高效。

底層實作:hex_dump_to_buffer()

void hex_dump_to_buffer(const void *buf, size_t len,
                        int rowsize, int groupsize,
                        char *linebuf, size_t linebuflen, bool ascii) {
    const u8 *ptr = buf;
    int j, lx = 0;
    int ascii_col;

    // 強制行寬 16 或 32
    if (rowsize != 16 && rowsize != 32) rowsize = 16;
    if (!len) goto nil;
    if (len > rowsize) len = rowsize;
    if (len % groupsize) groupsize = 1;

    // 預設 groupsize=1: 逐位元組轉 hex
    for (j = 0; j < len && lx + 3 <= linebuflen; ++j) {
        u8 ch = ptr[j];
        linebuf[lx++] = hex_asc_hi(ch);
        linebuf[lx++] = hex_asc_lo(ch);
        linebuf[lx++] = ' ';
    }
    if (j) lx--; // 移除尾部空格

    ascii_col = 3 * rowsize + 2; // ASCII 欄起始位置

    if (!ascii) goto nil;

    // 填充空格至 ASCII 欄
    while (lx < linebuflen - 1 && lx < ascii_col - 1)
        linebuf[lx++] = ' ';

    // 附加可列印字元,不可列印顯示 '.'
    for (j = 0; j < len && lx + 2 < linebuflen; ++j) {
        u8 ch = ptr[j];
        linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
    }

nil:
    linebuf[lx++] = '\0'; // 必須結束符,否則 printk 可能 panic
}

上層封裝:print_hex_dump()

void print_hex_dump(const char *level, const char *prefix,
                    int prefix_type, int rowsize, int groupsize,
                    const void *buf, size_t len, bool ascii) {
    const u8 *ptr = buf;
    int i, linelen, remaining = len;
    char linebuf[32*3 + 2 + 32 + 1]; // 足夠大緩衝區

    if (rowsize != 16 && rowsize != 32) rowsize = 16;

    for (i = 0; i < len; i += rowsize) {
        linelen = min(remaining, rowsize);
        remaining -= rowsize;

        hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
                           linebuf, sizeof(linebuf), ascii);

        switch (prefix_type) {
        case DUMP_PREFIX_ADDRESS:
            printk("%s%s%p: %s\n", level, prefix, ptr + i, linebuf);
            break;
        case DUMP_PREFIX_OFFSET:
            printk("%s%s%.8x: %s\n", level, prefix, i, linebuf);
            break;
        default:
            printk("%s%s%s\n", level, prefix, linebuf);
        }
    }
}

最常用介面:print_hex_dump_bytes()

void print_hex_dump_bytes(const char *prefix, int prefix_type,
                          const void *buf, size_t len) {
    print_hex_dump(KERN_DEBUG, prefix, prefix_type, 16, 1, buf, len, true);
}

預設參數:16 位元組/行、1 位元組分組、顯示 ASCII、DEBUG 等級。

輸出格式範例

DUMP_PREFIX_ADDRESS:
  mydrv 00000000f5a1b2c0: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO

DUMP_PREFIX_OFFSET:
  mydrv 00000000: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO

驅動開發實戰建議

  • 除錯封包標頭:print_hex_dump_bytes("rx_hdr: ", DUMP_PREFIX_OFFSET, skb->data, 32);
  • 記憶體池檢查:print_hex_dump(KERN_INFO, "pool: ", DUMP_PREFIX_ADDRESS, 32, 4, pool, 256, false); (4 位元組分組看 32-bit 值)
  • 避免在中斷上下文列印過大資料,可能導致丟失 log 或死機。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved