如何让 CLion 识别 linux/init.h 和其他 linux 内核头文件?

问题描述 投票:0回答:1

我正在尝试使用 CLion 进行一些 Linux 内核编程。我观察到一些标题,如

<linux/kernel.h>
<linux/module.h>
被正确识别,但其他一些标题,如
<linux/init.h>
则未被正确识别。

为了更清楚,这里有一个示例程序:

1 #include <linux/init.h>
2 #include <linux/kernel.h>
3
4 int init_module(void) {
5     printk("initializing");
6     return 0;
7 }

这里,我的第 1 行和第 5 行以红色突出显示,并带有典型的“未找到文件”/“调用未声明的函数”错误,但第 2 行正确地未突出显示,并且如果我尝试编写链接到该库的内容完全没有问题。

我尝试为我的内核安装最新的标头,即

sudo apt install linux-headers-$(uname -r)
,但它似乎不起作用。我能做什么?

编辑

输出

locate init.h | grep linux/init.h

(some thimeshift things...)
/usr/src/linux-headers-5.4.0-187/include/linux/init.h
/usr/src/linux-headers-5.4.0-189/include/linux/init.h
/usr/src/linux-headers-5.4.0-74/include/linux/init.h

...这很好奇,因为我的内核版本是5.4.0-190,并且

sudo apt-get install linux-headers-$(uname -r)
的输出如下(手动翻译成英文):

...
linux-headers-5.4.0-190-generic is already at the most recent version (5.4.0-190.210).
...

编辑

添加 CMake 文件有点有效

include_directories(/usr/src/linux-headers-5.4.0-187/include/)
,但这只是一个“补丁”,而不是解决方案,所以我将问题悬而未决,希望得到一些更“明确”的答案

c linux linux-kernel clion kernel-module
1个回答
0
投票

事情没那么简单,有两个主要原因:

  1. CLion 想要使用 CMake,因此您必须编写一个自定义

    CMakeLists.txt
    来查找内核标头并调用
    make
    来调用内核 Makefile 并使用自定义命令构建模块。这并不容易,因为内核 Makefile 相当复杂,需要正确定义许多变量。

  2. 正如您似乎已经发现的那样,通常当前内核的内核头位于

    /usr/src/linux-headers-$(uname -r)/
    。但是,
    .h
    C 头文件位于不同的目录中。在 headers 目录中,我知道至少有 4 个不同的包含目录:
    include
    include/generated
    arch/XXX/include
    arch/XXX/include/generated
    。这些都需要包括在内。

使用 CLion 构建内核模块的 CMakeLists.txt

鉴于上述情况,构建内核模块的

CMakeLists.txt
应该执行以下操作:

  1. 查找当前的内核版本(使用
    uname -r
    命令)。
  2. /usr/src/linux-headers-$(uname -r)
    找到内核头文件。我们将此路径保存在
    KDIR
    变量中。
  3. 指定要构建的架构。我们将为此设置一个
    ARCH
    变量。没有简单的方法可以自动执行此操作,因为像
    uname -m
    这样的命令行工具会输出错误的名称(例如
    x86_64
    而不是
    x86
    )。
  4. 使用
    include_directories()
    包含适当的目录。
  5. 生成
    Kbuild
    (或
    Makefile
    )文件来构建模块。
  6. 使用
    make
    运行自定义
    custom_command()
    命令来构建模块。
  7. 最后,声明一个
    dummy_target
    目标用于依赖性检查。

最终结果如下:

cmake_minimum_required(VERSION 3.28)
project(mymod C)

set(CMAKE_C_STANDARD 99)
add_definitions(-D__KERNEL__ -DMODULE)

# Find running kernel release
execute_process(
        COMMAND uname -r
        OUTPUT_VARIABLE KERNEL_RELEASE
        OUTPUT_STRIP_TRAILING_WHITESPACE
)

set(KDIR "/usr/src/linux-headers-${KERNEL_RELEASE}")
set(ARCH "x86")

# Check that the headers are installed
if (NOT EXISTS "${KDIR}/include/linux/module.h")
    message(FATAL_ERROR "Kernel headers dir not found: ${KDIR}")
endif()

# Check that the headers are for the specified arch
if (NOT EXISTS "${KDIR}/arch/${ARCH}/include")
    message(FATAL_ERROR "Arch-specific include dir not found: ${KDIR}/arch/${ARCH}/include")
endif()

message(NOTICE "Building for arch: ${ARCH}")
message(NOTICE "Kernel release: ${KERNEL_RELEASE}")
message(NOTICE "Kernel headers: ${KDIR}")

# Include generic kernel headers
include_directories("${KDIR}/include")
include_directories("${KDIR}/include/generated")

# Include arch-specific kernel headers
include_directories("${KDIR}/arch/${ARCH}/include")
include_directories("${KDIR}/arch/${ARCH}/include/generated")

# Set some useful variables
set(MODULE_NAME mymod)
set(SRC_FILE "${MODULE_NAME}.c")
set(OBJ_FILE "${MODULE_NAME}.o")
set(KO_FILE "${MODULE_NAME}.ko")

# Generate Kbuild file in the source directory
FILE(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/Kbuild "obj-m := ${OBJ_FILE}")

# Custom `make` command used to build the module
add_custom_command(
        OUTPUT ${KO_FILE}
        COMMAND $(MAKE) -C ${KDIR} modules ARCH=${ARCH} M=${CMAKE_CURRENT_BINARY_DIR} src=${CMAKE_CURRENT_SOURCE_DIR}
        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
        DEPENDS ${SRC_FILE}
        VERBATIM
)

add_custom_target(mymod ALL DEPENDS ${KO_FILE})
add_library(dummy_target "${SRC_FILE}")

上面的

CMakeLists.txt
可以很好地构建以下
mymod.c
内核模块:

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

int __init myinit(void) {
    pr_info("Hello World!\n");
    return 0;
}

void __exit myexit(void) {
    pr_info("Goodbye World!\n");
}

module_init(myinit);
module_exit(myexit);
MODULE_VERSION("0.1");
MODULE_AUTHOR("Marco Bonelli");
MODULE_DESCRIPTION("Test module");
MODULE_LICENSE("GPL 2.0");

注意

MODULE_LICENSE()
是强制性的,如果没有指定许可证,内核Makefile将拒绝构建模块!

最终项目在 CLion 中应如下所示:

clion-screenshot

注意

Kbuild
文件是由
CMakeLists.txt
自动生成的。您需要写入的唯一两个文件是
CMakeLists.txt
mymod.c

唯一的问题(我不确定如何解决)是,正如您从上面的屏幕截图中看到的那样,CLion 似乎认为它还应该考虑用户空间包含诸如

/usr/include
/usr/x86_64-linux-gnu/include
等目录。这些绝对应该被考虑用于内核模块,但我不确定如何使用
CMakeLists.txt
禁用/删除它们。

© www.soinside.com 2019 - 2024. All rights reserved.