簡體   English   中英

C function 用於在循環中將字符串數組組合成單個字符串並在釋放分配的字符串后返回字符串 memory

[英]C function for combining an array of strings into a single string in a loop and return the string after freeing the allocated memory

我正在為 macOS 開發一個 procfs kernel 擴展,並試圖實現一個模擬 Linux 的 /proc/cpuinfo 的功能,類似於 FreeBSD 對其 linprocfs 所做的。 由於我正在努力學習,並且由於不是所有的 FreeBSD 代碼都可以簡單地復制到 XNU 並有望在 jar 之外工作,所以我正在從頭開始編寫此功能,使用 FreeBSD 和 NetBSD 的基於 linux 的procfs 特性作為參考。 無論如何...

在 Linux 下,$cat /proc/cpuinfo 顯示如下:

processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 25
model           : 33
model name      : AMD Ryzen 9 5950X 16-Core Processor
stepping        : 0
microcode       : 0xa201016
cpu MHz         : 2195.107
cache size      : 512 KB
physical id     : 0
siblings        : 32
core id         : 0
cpu cores       : 16
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 16
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_apicid aperfmperf pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local clzero irperf xsaveerptr wbnoinvd arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif umip pku ospke vaes vpclmulqdq rdpid overflow_recov succor smca
bugs            : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
bogomips        : 6787.02
TLB size        : 2560 4K pages
clflush size    : 64
cache_alignment : 64
address sizes   : 48 bits physical, 48 bits virtual
power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14]

我在 i386/cpuid.h 中使用 XNU 的 i386_cpu_info 結構來完成大部分工作,但到目前為止我還無法讓“標志”字段正確顯示。

XNU 的 i386/cpuid.h 定義每個功能標志如下:

#define CPUID_FEATURE_FPU       _Bit(0)   /* Floating point unit on-chip */
#define CPUID_FEATURE_VME       _Bit(1)   /* Virtual Mode Extension */
#define CPUID_FEATURE_DE        _Bit(2)   /* Debugging Extension */
#define CPUID_FEATURE_PSE       _Bit(3)   /* Page Size Extension */
#define CPUID_FEATURE_TSC       _Bit(4)   /* Time Stamp Counter */
...

而 i386_cpu_info 結構有一個字段應該對應於這些定義:

typedef struct i386_cpu_info {
    ...
    uint64_t        cpuid_features;
    ...
}

例如,在我的“主要”do_cpuinfo() function 中,我使用其中之一來檢查是否支持 FPU 功能,如下所示:

/*
 * Check if the FPU feature is present.
 */
char *fpu, *fpu_exception;

 /*
  * The cpuid_info() function sets up the i386_cpu_info structure and returns a pointer to the structure.
  */
if (cpuid_info()->cpuid_features & CPUID_FEATURE_FPU) {
    fpu = "yes";
    fpu_exception = "yes";
} else {
    fpu = "no";
    fpu_exception = "no";
}

它按預期工作。 然而,一旦我開始將 arrays 添加到組合中,事情就開始變得不穩定了。

我設置了一個 char 數組,其中包含每個標志的字符串:

const char *feature_flags[] = {
    /* 1 */  "fpu",
    /* 2 */  "vme",
    /* 3 */  "de",
    /* 4 */  "pse",
    /* 5 */  "tsc”,
    …
}

然后設置一個對應的 uint64_t 數組,其中包含 i386/cpuid.h 中定義的每個標志:

uint64_t feature_list[] = {
    /* 1 */  CPUID_FEATURE_FPU,
    /* 2 */  CPUID_FEATURE_VME,
    /* 3 */  CPUID_FEATURE_DE,
    /* 4 */  CPUID_FEATURE_PSE,
    /* 5 */  CPUID_FEATURE_TSC,
    …
}

然后我寫了一個 function,它應該迭代這些 arrays 以檢查是否存在一個特征,方法與我用於 FPU 檢測的方法相同,但不是為每個標志執行類似“cpuid_info()->cpuid_features & CPUID_FEATURE_FPU”的操作,我希望 cpuid_info()->cpuid_features 從 feature_list 數組中獲取每個標志,如果支持,將其復制到標志變量,將結果字符串移動到 static ret 變量,這樣我們就可以釋放分配的 memory 並返回 ret .

char *
get_cpu_flags(void)
{
    int i = 0;
    char *flags = NULL;
    static char *ret;

    /*
     * Allocate memory for our flag strings.
     */
    flags = _MALLOC(sizeof(feature_flags), M_TEMP, M_WAITOK);

    do {
        /* 
         * If the CPU supports a feature in the feature_list[],
         * move its corresponding flag from the feature_flags[]
         * into the buffer.
         */
        if ((cpuid_info()->cpuid_features & feature_list[i]) == feature_list[i]) {
            strlcat(flags, feature_flags[i], strlen(feature_flags[i]));
        //             |    |                |
        //             |    |                * The length of the string I want to amend to the ‘flags’ variable
        //             |    * The flag string in the array I want to amend to the ‘flags’ variable.
        //             * The variable I want the flag string to be amended to.
        }

        /*
         * Move the flag strings to a static variable before freeing the allocated memory
         * so we can free it before returning the resulting string.
         */
        ret = flags;

        /*
         * Add 1 to the counter for each iteration.
         */
        i++;

        /* 
         * If the counter exceeds the number of items in the array,
         * break the loop.
         */ 
        if (i > nitems(feature_flags)) {
            /*
             * Free the allocated memory before breaking the loop.
             */
            _FREE(&feature_flags, M_TEMP);
            break;
        } else {
            continue;
        }
    } while (i < nitems(feature_flags));

    return ret;
}

這個 function 然后被主 do_cpuinfo() function 調用,將信息打印到用戶空間。 為了節省空間,我在這里提供了一個僅處理“標志”字段的最小示例:

int
procfs_docpuinfo(__unused procfsnode_t *pnp, uio_t uio, __unused vfs_context_t ctx)
{
    vm_offset_t pageno, uva, kva;
    int len = 0, xlen = 0;
    off_t page_offset = 0;
    size_t buffer_size = 0;
    char *buffer;

    uint32_t max_cpus = *_processor_count;
    uint32_t cnt_cpus = 0;

    /*
     * Set up the variables required to move our data into userspace.
     */
    kva = VM_MIN_KERNEL_ADDRESS;                                        // kernel virtual address
    uva = uio_offset(uio);                                                                   // user virtual address
    pageno = trunc_page(uva);                                                         // page number
    page_offset = uva - pageno;                                                       // page offset
    buffer_size = sizeof(i386_cpu_info_t) + (LBFSZ * 2);                 // buffer size
    buffer = _MALLOC(buffer_size, M_TEMP, M_WAITOK);           // buffer

    char *flags = get_cpu_flags();

    do {
        if (cnt_cpus <= max_cpus) {
            /* 
             * len should snprintf our flags via uiomove.
             */
            len += snprintf(buffer, buffer_size, "flags\t\t\t: %s\n", flags);

            /*
             * Subtract the uva offset from len.
             */
            xlen = len - uva;
            xlen = imin(xlen, uio_resid(uio));

            /*
             * Copy our data into userspace.
             */
            uiomove(buffer, xlen, uio);

            /* 
             * Set len back to 0 before entering into the next loop.
             */
            if (len != 0) {
                len = 0;
            }

            /*
             * Update the CPU counter.
             */
            cnt_cpus++;

            /*
             * Continue unless the counter exceeds the
             * available processor count.
             */
            continue;
        } else if (cnt_cpus > max_cpus) {
            /*
             * If the counter exceeds the processor count,
             * free the associated memory and break the loop.
             */
            _FREE(&buffer, M_TEMP);
            break;
        }
    } while (cnt_cpus < max_cpus);

    return 0;
}

然而,這是我在 cpuinfo 上執行 cat 時得到的結果(完整的 function 顯然完好無損,而不是上面提供的最小示例):

processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 25
model           : 1
model name      : AMD Ryzen 9 5950X 16-Core Processor
microcode       : 186
stepping        : 0
cpu MHz         : 3393.62
cache size      : 512 KB
physical id     : 0
siblings        : 32
core id         : 0
cpu cores       : 16
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 16
wp              : yes
flags           : fpappclm syscalpre
bugs            :
bogomips        : 6786.62
TLB size        : 2560 4K pages
clflush_size    : 64
cache_alignment : 64
address sizes   : 48 bits physical, 48 bits virtual
power management:

如您所見,標志字段似乎以某種非常簡約的方式將標志字符串的片段組合在一起,而不是完整的標志字符串。

注意:您看到的空間是 XNU 中有四類功能(cpuid_features、cpuid_extfeatures、cpuid_leaf7_features 和 cpuid_leaf7_extfeatures)的結果,所以我為每個類別制作了一個 function,如 get_cpu_flags()。 因此,在我的主要代碼中,snprintf function 需要四組字符串,它們之間有一個空格(“flags\t\t\t: %s %s %s %s\n”, cpuflags, cpuextflags, leaf7flags, leaf7extflags) ,但它只打印出其中兩個,並且在用戶空間中都沒有正確打印出一次。

我認為問題出在 get_cpu_flags() function 及其姊妹函數中,但我不確定到底是什么問題。 如果我將 'ret = flags' 移出循環並將 _FREE(&feature_flags, M_TEMP) 放在它后面,我會得到 kernel 恐慌,這是堆棧跟蹤:

panic(cpu 18 caller 0xffffff8019a9b8c6): "address 0xffffff7fb66cd110 inside vm entry 0xffffff802abc21e0 [0xffffff7f9a410000:0xffffff8000000000), map 0xffffff802abb10f8"com.apple./System/Volumes/Data/SWE/macOS/BuildRoots/36806d33d2/
Library/Caches/com.apple.xbs/Sources/xnu/xnu-7195.141.19/osfmk/kern/kalloc.c:651
Backtrace (CPU 18), Frame : Return Address
0xffffffa0ef283690 : 0xffffff8019a8c26d mach_kernel : _handle_debugger_trap + 0x3fd
0xffffffa0ef2836e0 : 0xffffff8019bd3993 mach_kernel : _kdp_i386_trap + 0x143
0xffffffa0ef283720 : 0xffffff8019bc3f8a mach_kernel : _kernel_trap + 0x55a
0xffffffa0ef283770 : 0xffffff8019a30a2f mach_kernel : _return_from_trap + 0xff
0xffffffa0ef283790 : 0xffffff8019a8ba8d mach_kernel : _DebuggerTrapWithState + 0xad
0xffffffa0ef2838b0 : 0xffffff8019a8bd83 mach_kernel : _panic_trap_to_debugger + 0x273
0xffffffa0ef283920 : 0xffffff801a29c8da mach_kernel : _panic + 0x54
0xffffffa0ef283990 : 0xffffff8019a9b8c6 mach_kernel : _ipc_thread_port_unpin + 0x116
0xffffffa0ef2839c0 : 0xffffff8019a9bf33 mach_kernel : _kfree + 0x263
0xffffffa0ef283a10 : 0xffffff8019a9be3f mach_kernel : _kfree + 0x16f
0xffffffa0ef283a70 : 0xffffff7fb66c5713 com.stupid.filesystems.procfs : _get_cpu_flags + 0xe3
0xffffffa0ef283aa0 : 0xffffff7fb66c4f4f com.stupid.filesystems.procfs : _procfs_docpuinfo + 0x24f
0xffffffa0ef283d50 : 0xffffff7fb66ca4c0 com.stupid.filesystems.procfs : _procfs_vnop_read + 0xb0
0xffffffa0ef283d90 : 0xffffff8019d4449c mach_kernel : _utf8_normalizeOptCaseFoldAndMatchSubstring + 0x72c
0xffffffa0ef283e30 : 0xffffff801a04c3b8 mach_kernel : _read_nocancel + 0x328
0xffffffa0ef283ee0 : 0xffffff801a04c145 mach_kernel : _read_nocancel + 0xb5
0xffffffa0ef283f40 : 0xffffff801a13ed0e mach_kernel : _unix_syscall64 + 0x2ce
0xffffffa0ef283fa0 : 0xffffff8019a311f6 mach_kernel : _hndl_unix_scall64 + 0x16
Kernel Extensions in backtrace:
com.stupid.filesystems.procfs(1.0)[89308435-3658-3ED4-990A-F8AF63358857]com.apple.0xffffff7fb66c3000-com.apple.driver.0xffffff7fb66ccfff

鄭重聲明,我只是一個嘗試自己學習 C 和 kernel 編程的業余愛好者,因為它讓我着迷了很長時間。 我對使用字符 arrays 和 memory 分配還很陌生,因此非常感謝任何建議。 我之前問過類似的問題,但有人向我指出我應該更具體並提供更廣泛的示例。 然后我也專門與 kernel 恐慌作斗爭,但現在這似乎不是主要問題所以我刪除了我的舊問題並根據我之前收到的提示和我從那時起的進展提出了這個新問題。 我希望這次我能更好地提出我的問題,但如果沒有,請告訴我,我會努力改進。

我的這些功能的完整源代碼可以在這里找到: https://github.com/somestupidgirl/procfs_kext/blob/main/kext/procfs_cpu.c

編輯 1:

每個標志數組的 sizeof 給出以下結果:

 * sizeof(feature_flags)            = 480
 * sizeof(feature_ext_flags)        = 64
 * sizeof(leaf7_feature_flags)      = 368
 * sizeof(leaf7_feature_ext_flags)  = 96

正如建議的那樣,我一直在嘗試在沒有 malloc 的情況下執行此操作,但它總是導致 kernel 恐慌,堆棧跟蹤沒有告訴我任何有用的信息,真的。 所以要么我誤會了,要么 kernel 根本不允許。

我還想重復的一件事是我不希望“get_cpu_flags”執行“snprintf”位。 我想要 function 做的是收集支持的 cpu 功能標志列表,將它們存儲在一個字符串中,然后返回該字符串,該字符串現在應該包含當前 cpu 支持的 cpu 標志列表。 然后,我使用“get_cpu_flags”在“procfs_docpuinfo”中定義了一個 char* 變量,一旦被“snprintf”調用,它應該打印出支持的標志列表。

為了更好地參考,這是我目前完整的“procfs_docpuinfo”function:

int
procfs_docpuinfo(__unused procfsnode_t *pnp, uio_t uio, __unused vfs_context_t ctx)
{
    vm_offset_t pageno, uva, kva;
    int len = 0, xlen = 0;
    off_t page_offset = 0;
    size_t buffer_size = 0;
    char *buffer;

    /*
     * Overall processor count for the current CPU.
     *
     * Not to be conflated with cpu_cores (number of cores)
     * as these are not the same.
     */
    uint32_t max_cpus = *_processor_count;

    /*
     * Initialize the processor counter.
     * This should always begin at 0 and
     * add 1 for each loop according to the
     * number of processors present.
     */
    uint32_t cnt_cpus = 0;

    /*
     * The core id should always start at 0.
     */
    int core_id = 0;

    /*
     * Initialize the TSC frequency variables.
     */
    uint64_t freq = *_tscFreq;
    int fqmhz = 0, fqkhz = 0;

    /* 
     * Set the TSC frequency variables
     */
    if (freq != 0) {
        fqmhz = (freq + 4999) / 1000000;
        fqkhz = ((freq + 4999) / 10000) % 100;
    }

    /*
     * The apicid variable begins at 0 and get increased
     * by 2 for each loop until the number becomes greater
     * than max_cpus, in which case the loop resets the
     * variable to a value of 1 and then contines increasing
     * that number by 2 for each loop.
     */
    int apicid = 0, initial_apicid = 0;

    /* 
     * Here we can utilize the i386_cpu_info structure in i386/cpuid.h
     * to get the information we need. The cpuid_info() function sets up
     * the i386_cpu_info structure and returns a pointer to the structure.
     */
    char *vendor_id = _cpuid_info()->cpuid_vendor;
    uint8_t cpu_family = _cpuid_info()->cpuid_family;
    uint8_t model = _cpuid_info()->cpuid_model; // FIXME
    char *model_name = _cpuid_info()->cpuid_brand_string;
    uint32_t microcode = _cpuid_info()->cpuid_microcode_version; // FIXME
    uint32_t cache_size = _cpuid_info()->cpuid_cache_size;
    uint8_t stepping = _cpuid_info()->cpuid_stepping;
    uint32_t cpu_cores = _cpuid_info()->core_count;
    uint32_t cpuid_level = cpu_cores;
    uint32_t tlb_size = _cpuid_info()->cache_linesize * 40;
    uint32_t clflush_size = _cpuid_info()->cache_linesize;
    uint32_t cache_alignment = clflush_size;
    uint32_t addr_bits_phys = _cpuid_info()->cpuid_address_bits_physical;
    uint32_t addr_bits_virt = _cpuid_info()->cpuid_address_bits_virtual;

    /*
     * Check if the FPU feature is present.
     */
    char *fpu, *fpu_exception;
    if (_cpuid_info()->cpuid_features & CPUID_FEATURE_FPU) {
        fpu = "yes";
        fpu_exception = "yes";
    } else {
        fpu = "no";
        fpu_exception = "no";
    }

    /*
     * Get the CPU flags.
     */
    char *cpuflags, *cpuextflags, *leaf7flags, *leaf7extflags;

    cpuflags = get_cpu_flags();
    cpuextflags = get_cpu_ext_flags();
    leaf7flags = get_leaf7_flags();
    leaf7extflags = get_leaf7_ext_flags();

    /*
     * Check for CPU write protection.
     */
    char *wp;
    if (get_cr0() & CR0_WP) {
        wp = "yes";
    } else {
        wp = "no";
    }

    /* TODO */
    //char *bugs = get_cpu_bugs();
    char *bugs = "";
    //char *pm = get_cpu_pm();
    char *pm = "";


    /*
     * Set up the variables required to move our data into userspace.
     */
    kva = VM_MIN_KERNEL_ADDRESS;                                        // kernel virtual address
    uva = uio_offset(uio);                                              // user virtual address
    pageno = trunc_page(uva);                                           // page number
    page_offset = uva - pageno;                                         // page offset
    buffer_size = (LBFSZ * 4);                                          // buffer size
    buffer = _MALLOC(buffer_size, M_TEMP, M_WAITOK);                    // buffer

    do {
        if (cnt_cpus <= max_cpus) {
            /* 
             * The data which to copy over to userspace.
             */
            len += snprintf(buffer, buffer_size,
                "processor\t\t: %u\n"
                "vendor_id\t\t: %s\n"
                "cpu family\t\t: %u\n"
                "model\t\t\t: %u\n"
                "model name\t\t: %s\n"
                "microcode\t\t: %u\n"
                "stepping\t\t: %u\n"
                "cpu MHz\t\t\t: %d.%02d\n"
                "cache size\t\t: %d KB\n"
                "physical id\t\t: %u\n"
                "siblings\t\t: %u\n"
                "core id\t\t\t: %d\n"
                "cpu cores\t\t: %u\n"
                "apicid\t\t\t: %u\n"
                "initial apicid\t\t: %u\n"
                "fpu\t\t\t: %s\n"
                "fpu_exception\t\t: %s\n"
                "cpuid level\t\t: %u\n"
                "wp\t\t\t: %s\n"
                "flags\t\t\t: %s %s %s %s\n"
                "bugs\t\t\t: %s\n"
                "bogomips\t\t: %d.%02d\n"
                "TLB size\t\t: %u 4K pages\n"
                "clflush_size\t\t: %u\n"
                "cache_alignment\t\t: %d\n"
                "address sizes\t\t: %d bits physical, %d bits virtual\n"
                "power management\t: %s\n\n",
                cnt_cpus,               // processor
                vendor_id,              // vendor_id
                cpu_family,             // cpu family
                model,                  // model
                model_name,             // model name
                microcode,              // microcode
                stepping,               // stepping
                fqmhz, fqkhz,           // cpu MHz
                cache_size,             // cache size
                0,                      // physical id
                max_cpus,               // siblings
                core_id,                // core id
                cpu_cores,              // cpu cores
                apicid,                 // apicid
                initial_apicid,         // initial apicid
                fpu,                    // fpu
                fpu_exception,          // fpu exception
                cpuid_level,            // cpuid level
                wp,                     // wp
                cpuflags,               // flags
                cpuextflags,            // flags
                leaf7flags,             // flags
                leaf7extflags,          // flags
                bugs,                   // bugs
                fqmhz * 2, fqkhz,       // bogomips
                tlb_size,               // TLB size
                clflush_size,           // clflush_size
                cache_alignment,        // cache_alignment
                addr_bits_phys,         // address size physical
                addr_bits_virt,         // address size virtual
                pm                      // power management
            );

            /*
             * Subtract the uva offset from len.
             */
            xlen = len - uva;
            xlen = imin(xlen, uio_resid(uio));

            /*
             * Copy our data into userspace.
             */
            uiomove(buffer, xlen, uio);

            /* 
             * Set len back to 0 before entering into the next loop.
             */
            if (len != 0) {
                len = 0;
            }

            /*
             * Reset the max_cpus variable at the end of each loop.
             * Otherwise it tends to behave erratically.
             */
            if (max_cpus != *_processor_count) {
                max_cpus = *_processor_count;
            }

            /*
             * Increase by 2 for each loop.
             */
            apicid += 2;
            if (apicid >= max_cpus) {
                /* If the number exceeds max_cpus, reset to 1. */
                apicid = 1;
            }

            /*
             * The initial apicid is the same as apicid.
             */
            initial_apicid = apicid;

            /*
             * Update the CPU counter.
             */
            cnt_cpus++;

            /*
             * Update the core_id.
             */
            core_id++;

            /*
             * The core_id should never exceed the number of cores.
             * Start over if it does.
             */
            if (core_id > cpu_cores - 1) {
                core_id = 0;
            }

            /*
             * Continue unless the counter exceeds the
             * available processor count.
             */
            continue;
        } else if (cnt_cpus > max_cpus) {
            /*
             * If the counter exceeds the processor count,
             * free the associated memory and break the loop.
             */
            _FREE(&buffer, M_TEMP);
            break;
        }
    } while (cnt_cpus < max_cpus);

    return 0;
}

這是我稍微更新的“get_cpu_flags”function:

STATIC char *
get_cpu_flags(void)
{
    int i = 0;
    int size = 0;
    char *flags;
    static char *ret;

    size = sizeof(feature_flags);
    flags = _MALLOC(size, M_TEMP, M_WAITOK);

    do {
        /* 
         * If the CPU supports a feature in the feature_list[]...
         */
        if (_cpuid_info()->cpuid_features & feature_list[i]) {
            /*
             * ...amend its flag to 'flags'.
             */
            strlcat(flags, feature_flags[i], sizeof(flags));
        }

        ret = flags;

        /*
         * Add 1 to the counter for each iteration.
         */
        i++;

        /* 
         * If the counter exceeds the number of items in the array,
         * break the loop.
         */ 
        if (i > nitems(feature_flags)) {
            _FREE(&flags, M_TEMP);
            break;
        } else {
            continue;
        }
    } while (i < nitems(feature_flags));

    return ret;
}

在 cpuinfo 上執行 cat 的結果仍然與之前幾乎相同。 我仍在消化收到的所有回復,希望我能想出一個早日有效的解決方案,謝謝大家。 <3

編輯 2:

我終於弄明白了:'get_cpu_flags' function 現在看起來像這樣:

STATIC char *
get_cpu_flags(void)
{
    int i = 0;
    int size = (sizeof(feature_flags) * 2);
    char *flags[size];

    do {
        /* 
         * If the CPU supports a feature in the feature_list[]...
         */
        if (_cpuid_info()->cpuid_features & feature_list[i]) {
            /*
             * ...amend its flag to 'flags'.
             */
            strlcat(flags, feature_flags[i], sizeof(flags));
        }

        /*
         * Add 1 to the counter for each iteration.
         */
        i++;

    } while (i < nitems(feature_flags));

    return flags;
}

cpuinfo 文件中來自 cat 的 output 現在看起來像這樣:

processor           : 0
vendor_id           : AuthenticAMD
cpu family          : 25
model               : 1
model name          : AMD Ryzen 9 5950X 16-Core Processor
microcode           : 186
stepping            : 0
cpu MHz             : 3393.63
cache size          : 512 KB
physical id         : 0
siblings            : 32
core id             : 0
cpu cores           : 16
apicid              : 0
initial apicid      : 0
fpu                 : yes
fpu_exception       : yes
cpuid level         : 16
wp                  : yes
flags               : fpuvmedepsetscmsrpaemcecx8apicsepmtrrpgemcacmovpatpse36clfshmmxfxsrssesse2httsse3pclmulqdqmonssse3fmacx16sse4.1sse4.2movbepopcntaesxsaveosxsaveavx1.0f16crdrand
bugs                :
bogomips            : 6786.63
TLB size            : 2560 4K pages
clflush_size        : 64
cache_alignment     : 64
address sizes       : 48 bits physical, 48 bits virtual
power management    :

感謝 chqrlie 提供正確的答案,以及其他所有參與的人,我非常感謝您的所有意見。 現在唯一剩下的就是在每個標志之間添加一個空格。 但現在我對我在做什么有了更好的理解,這應該不是一個太大的挑戰。 再次感謝大家,祝大家平安。 <3

很高興你想學習 C,更高興的是你正在學習 kernel 編程。 很棒的話題。 繼續你的工作!

現在回答你的問題。 我注意到您的代碼中有以下 if 語句:

if (i > nitems(feature_flags)) {
    /*
     * Free the allocated memory before breaking the loop.
     */
    _FREE(&feature_flags, M_TEMP);
    break;
} else {
    continue;
}

據我了解,這絕不是真的,因為我達到的最高價值是nitems(feature_flags)

我還注意到您使用了 strlcat,它實際上只是附加到提供的字符串。 這可能會使分隔標志變得棘手。 您可以改為執行以下操作: offset += snprintf(flags + offset, len[i] + 1, " %s", feature_flags[i]); , 其中len[i] = strnlen(feature_flags[i], max_len); 注意 NUL 字符的+1

我認為一些問題也可能源於您使用 static 字符數組ret的構造。 我建議將“足夠大”的字符數組傳遞給 function 並在 function 中填充它或 malloc 並返回指向它的指針,基本上假設調用者會處理它。 但在這種情況下就是你。 在那種情況下,您可以在使用字符串后簡單地釋放。

我認為帶有 malloc 的臨時實現看起來像這樣:

char*
get_cpu_flags(void)
{
     // NOTE: This only works because feature_flags is an actual array. This not necessarily works for pointers
    int n = sizeof(feature_flags) / sizeof(feature_flags[0]);
    char *flags = _MALLOC(n, M_TEMP, M_WAITOK);
    
    int offset = 0;
    for (int i = 0; i < n; ++i) {
        if ((cpuid_info()->cpuid_features & feature_list[i]))
            offset += snprintf(flags + offset, some_upper_bound, " %s", feature_flags[i]);
    }

    return flags;
}

來自外部的 memory 的實現可能如下所示:

void
get_cpu_flags(char* array, size_t size)
{        
    int offset = 0;
    for (int i = 0; i < size; ++i) {
        if ((cpuid_info()->cpuid_features & feature_list[i]))
            offset += snprintf(flags + offset, some_upper_bound, " %s", feature_flags[i]);
    }
}

請注意,我是在手機上輸入此內容的:^) 無論如何,我希望我能幫助您了解這些見解以及如何將字符串數組中的字符串放在一起的示例。

附錄:閱讀之前的答案,我必須同意,在這種情況下,對於 ~300 字節,本地字符數組可以做到並且可以節省一些 malloc。我建議使用這種方法。

無需為此任務分配 memory:傳遞指向本地數組的指針及其大小並正確使用strlcat

strlcat(flags, feature_flags[i], size);

使用復制字符串長度的大小參數調用strlcat()是錯誤的,大小參數應該是strlcat截斷目標的目標數組的大小。

例如:

char *get_cpu_flags(char *dest, size_t size) {
    /* avoid patching dest if size is 0 */ 
    if (!size)
        return;
    /* initialize dest as an empty string */
    *dest = '\0';
    /* repeat these tests: */
    if (cpuid_info()->cpuid_features & [...]) {
        /* append flag name, truncating if necessary */
        strlcat(dest, " flagname", size);
        }
    }
    [...]
    /* return argument so the composed string can be passed
       to snprintf directly */
    return dest;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM