用于在循环中将字符串数组合并为单个字符串的 C 函数,并在释放分配的内存后 return 字符串

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 内核扩展,并试图实现一个模拟 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() 函数中,我使用其中之一来检查是否支持 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";
}

而且它的效果和预期的一样。然而,一旦我开始将数组添加到组合中,事情就开始变得不稳定了。

我设置了一个包含每个标志字符串的字符数组:

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,
    …
}

然后我编写了一个函数,该函数应该迭代这些数组以检查是否存在某个特征,方法与我用于 FPU 检测的方法相同,但我没有做类似“cpuid_info() ->cpuid_features & CPUID_FEATURE_FPU” 对于每个标志,我希望 cpuid_info()->cpuid_features 从 feature_list 数组中获取每个标志,如果支持的话,将其复制到标志变量,将结果字符串移动到静态 ret 变量,这样我们就可以释放分配的内存和 return 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;
}

此函数随后被主 do_cpuinfo() 函数调用,该函数将信息打印到用户 space 中。为了节省 space 我在这里提供了一个仅处理“标志”字段的最小示例:

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 时得到的结果(显然完整的功能完好无损,而不是上面提供的最小示例):

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:

如您所见,标志字段似乎以某种非常简约的方式将标志字符串的片段组合在一起,而不是完整的标志字符串。

注意:您看到的space是四类特征(cpuid_features、cpuid_extfeatures、cpuid_leaf7_features和cpuid_leaf7_extfeatures)的结果在 XNU 所以我为每个类别做了一个像 get_cpu_flags() 的函数。因此,在我的主要代码中,snprintf 函数需要四组字符串,它们之间有一个 space ("flags\t\t\t: %s %s %s %s\n", cpuflags, cpuextflags, leaf7flags, leaf7extflags), 但它只打印出其中两个并且在 userspace.

中都没有正确打印一次

我假设问题出在 get_cpu_flags() 函数及其姊妹函数中,但我不确定到底是什么问题。如果我将“ret = flags”移出循环并在其后立即放置 _FREE(&feature_flags, M_TEMP),我会出现内核恐慌,这是堆栈跟踪:

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 和内核编程的业余爱好者,因为这让我着迷了很长时间。我对使用字符数组和内存分配还很陌生,因此非常感谢任何建议。我之前问过类似的问题,但有人向我指出我应该更具体并提供更广泛的示例。然后我也一直在与内核恐慌作斗争,但现在这似乎不是主要问题所以我删除了我的旧问题并根据我之前收到的提示和我从那时起的进展提出了这个新问题。我希望这次我能更好地提出我的问题,但如果没有,请告诉我,我会努力改进。

我的这些函数的完整源代码可以在这里找到: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 的情况下执行此操作,正如所建议的那样,但它总是导致内核恐慌,堆栈跟踪没有告诉我任何有用的信息,真的。所以要么我误解了,要么内核根本不允许。

我还想重复的一件事是我不希望 'get_cpu_flags' 执行 'snprintf' 位。我想要该函数做的是收集支持的 cpu 功能标志列表,将它们存储在一个字符串中,然后 return 该字符串现在应该包含支持的 cpu 标志列表当前 cpu。然后我用 'get_cpu_flags' 在 'procfs_docpuinfo' 中定义了一个 char* 变量,一旦被 'snprintf' 调用,应该打印出支持的标志列表。

为了更好的参考,这里是我完整的 'procfs_docpuinfo' 功能,目前看起来:

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' 函数:

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' 函数现在看起来像这样:

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 的输出现在如下所示:

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 提供正确答案,以及其他所有参与的人!我非常感谢您的所有投入!现在唯一剩下的就是在每个标志之间添加一个 space,但现在我对我正在做的事情有了更好的理解,这应该不是一个太大的挑战。再次感谢大家,祝大家平安。 <3

不需要为此任务分配内存:传递一个指向本地数组的指针及其大小并正确使用 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 = '[=11=]';
    /* 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;
}

很高兴你想学习 C,更高兴的是你正在学习内核编程。很棒的话题。让你的工作继续下去!

现在回答你的问题。我在您的代码中注意到以下 if-statement:

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

我认为一些问题也可能源于您使用静态字符数组 ret 的构造。我建议要么将“足够大”的 char 数组传递给函数并填充它,要么在函数中 malloc 一些东西,然后 return 指向它的指针,基本上假设调用者会处理它。但在这种情况下就是你。在那种情况下,您可以在使用字符串后简单地释放。

我认为使用 malloc 的 ad-hoc 实现看起来像这样:

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;
}

内存来自外部的实现可能如下所示:

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]);
    }
}

请注意,我正在 phone 上输入此内容:^) 无论如何,我希望我能帮助您了解这些见解,并举例说明如何将字符串数组中的字符串放在一起。

附录: 阅读之前的答案,我必须同意,在这种情况下,大约 300 字节,本地 char 数组就可以了,并且可以节省一些 malloc。我建议使用这种方法。