我现在安装了gcc-12,执行make后报错:$ make make -C /lib/modules/6.8.0-65-generic/build M=/home/tp/kernel_code modules make[1]: 进入目录“/usr/src/linux-headers-6.8.0-65-generic” warning: the compiler differs from the one used to build the kernel The kernel was built by: x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0 You are using: gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0 CC [M] /home/tp/kernel_code/char_dev.o In file included from ./arch/x86/include/asm/mem_encrypt.h:15, from ./include/linux/mem_encrypt.h:17, from ./arch/x86/include/asm/processor-flags.h:6, from ./arch/x86/include/asm/processor.h:5, from ./arch/x86/include/asm/timex.h:5, from ./include/linux/timex.h:67, from ./include/linux/time32.h:13, from ./include/linux/time.h:60, from ./include/linux/stat.h:19, from ./include/linux/module.h:13, from /home/tp/kernel_code/char_dev.c:1: /home/tp/kernel_code/char_dev.c: In function ‘char_dev_init’: ./include/linux/init.h:188:22: error: passing argument 1 of ‘class_create’ from incompatible pointer type [-Werror=incompatible-pointer-types] 188 | #define THIS_MODULE (&__this_module) | ~^~~~~~~~~~~~~~~ | | | struct module * /home/tp/kernel_code/char_dev.c:83:31: note: in expansion of macro ‘THIS_MODULE’ 83 | char_class = class_create(THIS_MODULE, CLASS_NAME); | ^~~~~~~~~~~ In file included from ./include/linux/device.h:31, from ./include/linux/cdev.h:8, from /home/tp/kernel_code/char_dev.c:3: ./include/linux/device/class.h:228:54: note: expected ‘const char *’ but argument is of type ‘struct module *’ 228 | class * __must_check class_create(const char *name); | ~~~~~~~~~~^~ /home/tp/kernel_code/char_dev.c:83:18: error: too many arguments to function ‘class_create’ 83 | char_class = class_create(THIS_MODULE, CLASS_NAME); | ^~~~~~~~~~~~ ./include/linux/device/class.h:228:29: note: declared here 228 | struct class * __must_check class_create(const char *name); | ^~~~~~~~~~~~ cc1: some warnings being treated as errors make[3]: *** [scripts/Makefile.build:243:/home/tp/kernel_code/char_dev.o] 错误 1 make[2]: *** [/usr/src/linux-headers-6.8.0-65-generic/Makefile:1925:/home/tp/kernel_code] 错误 2 make[1]: *** [Makefile:240:__sub-make] 错误 2 make[1]: 离开目录“/usr/src/linux-headers-6.8.0-65-generic” make: *** [Makefile:5:all] 错误 2
时间: 2025-08-07 19:09:31 浏览: 9
<think>我们遇到了新的编译错误,这次是关于函数参数不匹配的问题。错误信息显示在调用`class_create`函数时,传递了不兼容的指针类型和过多的参数。
### 错误分析:
1. **第一个错误**:
```
error: passing argument 1 of ‘class_create’ from incompatible pointer type [-Werror=incompatible-pointer-types]
```
- 我们传递了`THIS_MODULE`(类型为`struct module *`)给`class_create`,但该函数期望的第一个参数是`const char *`。
2. **第二个错误**:
```
error: too many arguments to function ‘class_create’
```
- 我们传递了两个参数给`class_create`,但该函数只接受一个参数。
### 原因:
在较新的Linux内核版本中(5.11以后),`class_create`函数的原型发生了变化。旧版本的内核中,`class_create`函数原型是:
```c
struct class *class_create(struct module *owner, const char *name);
```
但在新版本中,它被简化为:
```c
struct class *class_create(const char *name);
```
### 解决方案:
修改代码,将`class_create`的调用方式改为只传递一个参数(类名),并移除`THIS_MODULE`。
### 修改后的代码示例:
```c
// 原来的调用(两个参数):
// char_class = class_create(THIS_MODULE, CLASS_NAME);
// 新调用(一个参数):
char_class = class_create(CLASS_NAME);
```
### 完整修复步骤:
1. 打开`char_dev.c`文件,找到调用`class_create`的地方(在`char_dev_init`函数中)。
2. 修改该行代码,去掉第一个参数(`THIS_MODULE`),只保留类名字符串。
3. 保存文件,重新编译。
### 示例代码片段(修复后):
```c
static int __init char_dev_init(void) {
int ret;
dev_t devno;
// 分配设备号
ret = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ERR "Failed to allocate char device region\n");
return ret;
}
major = MAJOR(devno);
// 创建字符设备结构
char_dev = cdev_alloc();
if (!char_dev) {
unregister_chrdev_region(devno, 1);
return -ENOMEM;
}
// 初始化字符设备
cdev_init(char_dev, &char_dev_fops);
char_dev->owner = THIS_MODULE;
// 添加字符设备到系统
ret = cdev_add(char_dev, devno, 1);
if (ret) {
printk(KERN_ERR "Failed to add char device\n");
cdev_del(char_dev);
unregister_chrdev_region(devno, 1);
return ret;
}
// 创建设备类(修复点:只传递一个参数)
char_class = class_create(CLASS_NAME);
if (IS_ERR(char_class)) {
cdev_del(char_dev);
unregister_chrdev_region(devno, 1);
return PTR_ERR(char_class);
}
// 创建设备节点
char_device = device_create(char_class, NULL, devno, NULL, DEVICE_NAME);
if (IS_ERR(char_device)) {
class_destroy(char_class);
cdev_del(char_dev);
unregister_chrdev_region(devno, 1);
return PTR_ERR(char_device);
}
printk(KERN_INFO "Char device registered with major number: %d\n", major);
return 0;
}
```
### 注意事项:
1. **内核版本兼容性**:如果希望代码在多个内核版本上工作,可以使用条件编译:
```c
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
char_class = class_create(THIS_MODULE, CLASS_NAME);
#else
char_class = class_create(CLASS_NAME);
#endif
```
2. **错误处理**:确保检查`class_create`的返回值(如示例中所示),因为它可能返回错误指针。
### 重新编译:
修改后,重新运行`make`命令,应该可以成功编译。
### 如果还有其他错误:
如果还有其他类似的函数参数不匹配错误,请检查以下常用函数的变化(在新内核中可能有类似改动):
- `device_create`:现在通常需要4个参数(旧版本需要5个)
```c
// 旧版本(5个参数):
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
// 新版本(4个参数):
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, const char *fmt, ...);
```
修改方法:去掉第四个参数(`drvdata`),直接传递设备名格式字符串和可变参数。
示例:
```c
// 旧调用:
// device_create(char_class, NULL, devno, NULL, DEVICE_NAME);
// 新调用(移除第四个参数):
device_create(char_class, NULL, devno, DEVICE_NAME);
```
### 最终Makefile参考(确保编译器设置正确):
```makefile
# 指定编译器为gcc-12
CC := gcc-12
obj-m := char_dev.o
KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) CC=$(CC) modules
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean
```
### 编译并加载模块:
```bash
make
sudo insmod char_dev.ko
dmesg | tail # 查看加载日志
```
§§相关问题§§:
1. Linux内核模块中字符设备驱动的完整实现步骤是什么?
2. 如何在内核模块中实现文件操作(file_operations)结构体?
3. 用户空间程序如何与字符设备驱动进行通信?
4. 如何解决Linux内核版本升级导致的驱动兼容性问题?</think># 解决内核模块编译错误:class_create函数参数不匹配
您遇到的错误是由于Linux内核API变化导致的,特别是在`class_create`函数的参数变化上。让我们详细分析并解决这个问题。
## 错误分析
### 关键错误信息
```text
error: passing argument 1 of ‘class_create’ from incompatible pointer type
error: too many arguments to function ‘class_create’
```
### 问题原因
1. **内核API变化**:
- 在较新的内核版本(5.11+)中,`class_create`函数签名发生了变化
- 旧版本:`struct class *class_create(struct module *owner, const char *name)`
- 新版本:`struct class *class_create(const char *name)`
2. **参数不匹配**:
- 您的代码使用了旧版API(传递两个参数)
- 内核头文件使用的是新版API(只需要一个参数)
3. **编译器严格模式**:
- `-Werror=incompatible-pointer-types` 将警告视为错误
- 这是内核编译的默认行为,确保代码质量
## 完整解决方案
### 1. 更新代码使用新版API
```c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#define DEVICE_NAME "char_dev"
#define CLASS_NAME "char_class"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Character Device Driver Example");
static int major;
static struct cdev *char_dev;
static struct class *char_class;
static struct device *char_device;
static int char_dev_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "Char device opened\n");
return 0;
}
static int char_dev_release(struct inode *inode, struct file *file) {
printk(KERN_INFO "Char device closed\n");
return 0;
}
static ssize_t char_dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
const char *msg = "Hello from kernel!\n";
size_t len = strlen(msg);
if (*f_pos > 0) return 0;
if (copy_to_user(buf, msg, len)) return -EFAULT;
*f_pos = len;
return len;
}
static struct file_operations char_dev_fops = {
.owner = THIS_MODULE,
.open = char_dev_open,
.release = char_dev_release,
.read = char_dev_read,
};
static int __init char_dev_init(void) {
int ret;
dev_t devno;
// 分配设备号
ret = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ERR "Failed to allocate char device region\n");
return ret;
}
major = MAJOR(devno);
// 创建字符设备结构
char_dev = cdev_alloc();
if (!char_dev) {
unregister_chrdev_region(devno, 1);
return -ENOMEM;
}
// 初始化字符设备
cdev_init(char_dev, &char_dev_fops);
char_dev->owner = THIS_MODULE;
// 添加字符设备到系统
ret = cdev_add(char_dev, devno, 1);
if (ret) {
printk(KERN_ERR "Failed to add char device\n");
cdev_del(char_dev);
unregister_chrdev_region(devno, 1);
return ret;
}
// 创建设备类 - 使用新版API(单个参数)
char_class = class_create(CLASS_NAME);
if (IS_ERR(char_class)) {
printk(KERN_ERR "Failed to create device class\n");
cdev_del(char_dev);
unregister_chrdev_region(devno, 1);
return PTR_ERR(char_class);
}
// 创建设备节点
char_device = device_create(char_class, NULL, devno, NULL, DEVICE_NAME);
if (IS_ERR(char_device)) {
printk(KERN_ERR "Failed to create device\n");
class_destroy(char_class);
cdev_del(char_dev);
unregister_chrdev_region(devno, 1);
return PTR_ERR(char_device);
}
printk(KERN_INFO "Char device registered with major number: %d\n", major);
return 0;
}
static void __exit char_dev_exit(void) {
dev_t devno = MKDEV(major, 0);
device_destroy(char_class, devno);
class_destroy(char_class);
cdev_del(char_dev);
unregister_chrdev_region(devno, 1);
printk(KERN_INFO "Char device unregistered\n");
}
module_init(char_dev_init);
module_exit(char_dev_exit);
```
### 2. 修改Makefile确保使用正确编译器
```makefile
# 确保使用gcc-12
CC := gcc-12
obj-m := char_dev.o
# 获取内核构建目录
KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
# 获取当前工作目录
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) CC=$(CC) modules
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean
```
### 3. 编译和测试模块
```bash
# 清理之前的构建
make clean
# 编译模块
make
# 加载模块
sudo insmod char_dev.ko
# 检查设备节点
ls -l /dev/char_dev
# 测试设备
echo "Test" | sudo tee /dev/char_dev
sudo cat /dev/char_dev
# 查看内核日志
dmesg | tail
# 卸载模块
sudo rmmod char_dev
```
## 内核API变化详解
### class_create函数变化
| 内核版本 | 函数签名 | 参数说明 |
|----------|----------|----------|
| < 5.11 | `struct class *class_create(struct module *owner, const char *name)` | `owner`: 拥有该类的模块<br>`name`: 类名称 |
| ≥ 5.11 | `struct class *class_create(const char *name)` | `name`: 类名称 |
### 其他相关API变化
1. **device_create**:
```c
// 旧版 (4个参数)
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
// 新版 (3个参数)
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, const char *fmt, ...);
```
2. **class_destroy**:
```c
// 保持不变
void class_destroy(struct class *cls);
```
## 跨内核版本兼容方案
如果您需要支持多个内核版本,可以使用条件编译:
```c
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,11,0)
// 旧版内核 (< 5.11)
char_class = class_create(THIS_MODULE, CLASS_NAME);
#else
// 新版内核 (≥ 5.11)
char_class = class_create(CLASS_NAME);
#endif
```
## 完整字符设备驱动开发流程
1. **设备号分配**:
- `alloc_chrdev_region()` - 动态分配设备号
- `register_chrdev_region()` - 静态注册设备号
2. **字符设备初始化**:
- `cdev_alloc()` - 分配cdev结构
- `cdev_init()` - 初始化cdev结构
- `cdev_add()` - 添加设备到系统
3. **设备类创建**:
- `class_create()` - 创建设备类
- `class_destroy()` - 销毁设备类
4. **设备节点创建**:
- `device_create()` - 创建设备节点
- `device_destroy()` - 销毁设备节点
5. **文件操作实现**:
- 实现`open`, `release`, `read`, `write`等操作
- 填充`file_operations`结构体
## 常见错误处理
1. **设备号分配失败**:
```c
if (alloc_chrdev_region(&dev, 0, count, name) < 0) {
pr_err("Device number allocation failed\n");
return -EBUSY;
}
```
2. **类创建失败**:
```c
if (IS_ERR(char_class)) {
ret = PTR_ERR(char_class);
pr_err("Class creation failed: %d\n", ret);
goto class_error;
}
```
3. **设备创建失败**:
```c
if (IS_ERR(char_device)) {
ret = PTR_ERR(char_device);
pr_err("Device creation failed: %d\n", ret);
goto device_error;
}
```
4. **资源释放顺序**:
```c
void cleanup_module(void) {
device_destroy(char_class, devno); // 先销毁设备
class_destroy(char_class); // 再销毁类
cdev_del(char_dev); // 删除字符设备
unregister_chrdev_region(devno, 1); // 最后释放设备号
}
```
阅读全文
相关推荐













