构建内核模块时设置包含路径

Setting Include Paths When Building Kernel Modules

我正在尝试为 Linux 编译内核模块。我有以下文件:testuio.cMakefile。当我键入 make all 时,出现以下错误:

$ make all
make -C /lib/modules/`uname -r`/build  M=/srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
  CC [M]  /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o
In file included from /usr/include/unistd.h:25,
                 from /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.c:13:
/usr/include/features.h:424:12: fatal error: sys/cdefs.h: No such file or directory
 #  include <sys/cdefs.h>
            ^~~~~~~~~~~~~
compilation terminated.
make[3]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/scripts/Makefile.build:271: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory/testuio.o] Error 1
make[2]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:1665: /srv/dev-disk-by-label-tboWolfRaid/home/alex/ma/source/kernel_modules/memory] Error 2
make[1]: *** [/usr/src/linux-headers-5.4.0-0.bpo.2-common/Makefile:179: sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-0.bpo.2-amd64'
make: *** [Makefile:19: all] Error 2

没错,/usr/include/sys/下没有这个文件。我不明白的是为什么在 /usr/include/x86_64-linux-gnu/sys 下有这样一个文件的地方找不到它。

以下是gcc -xc -E -v -输出的一部分:

#include <...> search starts here:
 .
 /usr/lib/gcc/x86_64-linux-gnu/8/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include

我的 Makefile 包含:

# (1) consult https://www.kernel.org/doc/Documentation/kbuild/modules.txt to build a kbuild-compatible Makefile
# sections of special interest: 3.1 (shared makefile)
# (2) specifics about the Makefile under https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt
# sections of special interest 3.7 (compilation flags)

ifneq ($(KERNELRELEASE),)
# kbuild part of the makefile (could pull this out into a file named Kbuild, this variant is more robust though)
obj-m += testuio.o
CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}
#ccflags-y = -I/usr/include/aarch64-linux-gnu -I/usr/include # see (2) section 3.7 

else
# "normal" makefile
KDIR ?= /lib/modules/`uname -r`/build # ?= sets KDIR only if it has no value already

all:
        make -C $(KDIR) M=$(PWD) modules
install:
        make -C $(KDIR) M=$(PWD) modules_install
clean:
        make -C $(KDIR) M=$(PWD) clean
endif

作为参考,我的 testuio.c 包含:

// In order for a device to be compatible with this UIO platform device driver
// it needs to use "generic-uio" in its compatible property
// This is a kernel driver.

#include <linux/module.h> // included for module_* macros
#include <linux/device.h> // included for devm_kzalloc
#include <linux/mman.h> // included for mmap
// #include <linux/stat.h> // included for fstat (for being able to read out of files)

#include <linux/platform_device.h> // included for struct platform_device
#include <linux/uio_driver.h> // included for struct uio_info

#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf

#define EOPENUIOFD 1
#define EMEMMAP 2
#define UIO_SIZE_FILE "/sys/class/uio/uio0/maps/map0/size"

MODULE_LICENSE("");
MODULE_AUTHOR("Alexander Pastor");
MODULE_VERSION("0.0.1");
// MODULE_DEVICE_TABLE(???);

typedef struct testuio_dev {
    struct uio_info* info;
} testuio_dev;

// TODO: change s.t. interupts can be handled correctly
// HINT: might not be necessary tho
static int testuio_irq_handler(int irq, struct uio_info* info) 
{
    // if (IRQ is not caused by my hardware)
    if (true)
        return IRQ_NONE;

    /* Disable interrupt */
    // Perform some register access to silence the IRQ line
    return IRQ_HANDLED;
}

static int testuio_probe(struct platform_device* pdev) 
{
    struct testuio_dev* dev;
    struct resource* res;
    int irq;

    // collect handles and information from platform device
    // devm_kzalloc => managed (free upon detaching device from system) kernel zero-initialized memory allocation
    // gfp flags => get free page flags
    dev = devm_kzalloc(&pdev->dev, (sizeof(struct testuio_dev)), GFP_KERNEL);
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    irq = platform_get_irq(pdev, 0);

    // basic uio info struct initialization (required!)
    dev->info->name = "testuio";
    dev->info->version = "0.0.1";

    // memory region initialization
    dev->info->mem[0].name = "dummy_mem";
    dev->info->mem[0].addr = res->start;
    dev->info->mem[0].size = resource_size(res);
    dev->info->mem[0].memtype = UIO_MEM_PHYS; 
    // other memory types include:
    //  - UIO_MEM_LOGICAL allocated by kmalloc
    //  - UIO_MEM_VIRTUAL allocated by vmalloc


    // interrupt initialization
    dev->info->irq = irq;
    dev->info->irq_flags = 0;
    dev->info->handler = &testuio_irq_handler;

    if(uio_register_device(&pdev->dev, info)) 
    {
        iounmap(dev->info->mem[0].internal_addr);
        return -ENODEV;
    } else {
        return 0;
    }
}

int main(int argc, char** argv) 
{
    int uio_fd; // UIO file descriptor
    unsigned int uio_size; // memory size of UIO
    FILE* size_fp; // pointer to the file containing memory size
    void* base_address; // start address of mapped memory

    uio_fd = open(/dev/uio, O_RDWR);

    if(uio_fd == -1)
        return EOPENUIOFD;

    size_fp = fopen(UIO_SIZE_FILE, O_RDONLY);
    fscanf(size_fp, "0x%08x", &uio_size); // 0x%08x expects unsigned int; %p expects void*

    // Whenever the user space program reads or writes in the virtual address range
    // it is accessing the device w/o the need for a system call. This improves performance.
    base_address = mmap(NULL, // NULL => kernel chooses page-aligned address at which to create mapping
                    uio_size, // length of memory mapping
                    PROT_READ | PROT_WRITE, // flags: grant read + write access 
                    MAP_SHARED_VALIDATE, // updates to mapping are visible to other processes 
                                         // (+validates given flags, available since Linux 4.15)
                    uio_fd, 
                    0);

    if (uio_fd == MAP_FAILED)
        return EMEMMAP;

    // --- BEGIN APPLICATION CODE 
    // ??? Is this even the right spot for the application code
    // see also 
    // https://docplayer.net/37414164-Linux-user-space-device-drivers-john-linn-based-on-3-14-linux-kernel.html

    // helpful: 
    // TODO: read in content from file as done in link above using fstat
    int testvals[8] = {0xDEADBEEF, 7, 12, 13, 31, 42, -63, -65535}
    memcpy(base_address, testvals, sizeof(testvals));

    for(int i=0; i<8; i++) 
    {
        printk(KERN_INFO "The value at address %p is %d", 
                base_address+i*sizeof(int), 
                (int)*(base_address+i*sizeof(int)));
    }

    //! interrupt stuff
    // read() returns the number of interrupt events.
    // It allows blocking and non-blocking modes with this being blocking mode
    int pending = 0;
    int reennable = 1; 
    read(uio_fd, (void*)&pending, sizeof(int));
    //! device specific processing
    // acking the interrupt in the device
    write(uio_fd, (void*)&reenable, sizeof(int)); 
    // --- END APPLICATON CODE

    //! undo virtual address mapping
    munmap(base_address, uio_size);
    return 0;
}

您似乎在代码中混淆了内核和用户space 内容。然而这一切都是错误的:

CFLAGS_testuio.o = -I/usr/include/x86_64-linux-gnu
ccflags-m = -I/usr/include/x86_64-linux-gnu # see (2) section 3.7
ccflags-y += ${ccflags-m}

当你拼接一些用户时-space包含你的内核标志的路径。

还有所有这些:

#include <unistd.h> // included for read and write syscalls
#include <fcntl.h> // included for file creation flags
#include <stdio.h> // included for FILE* type, fscanf

不属于内核 module.The main 功能也必须消失。

我的假设是您需要一个内核模块和一个用户space 应用程序来测试它。不要把他们两个混在一起。将这些东西分开。