10.linux驱动之helloworld - 指南

1.printk打印问题

        驱动调试时,需要打印log,内核调试,打印使用printk打印

        当使用ssh这种登录方式时,无法将printk的内容输出到终端上。

核心原因

  1. SSH 终端并非系统 “物理控制台”:Linux 的 “控制台” 默认指物理显示器(tty1-tty6),而 SSH 连接属于伪终端(pty)。内核的 printk 默认仅向物理控制台输出,不会主动转发到伪终端。
  2. 日志级别匹配问题:即使 SSH 终端能接收日志,若 printk 的日志级别≥当前控制台日志级别(如你截图中的 4),仍不会在终端打印,仅存入内核环形缓冲区。
  3. syslog 服务未转发日志:若系统未运行 klogd、syslogd 或 rsyslogd 等日志服务,内核日志无法从环形缓冲区转发到 SSH 终端对应的输出流。

2.降低控制台日志打印级别

        printk 有 8 个日志级别(数值 0~7,数值越小级别越高),控制台也有对应的日志级别(可通过cat /proc/sys/kernel/printk查看,第一个数值即为当前控制台日志级别)。当printk 的日志级别数值 < 控制台日志级别数值时,会在控制台打印。

        首先确认,是否有printk打印:

dmesg

        如图所示:

        内核环形缓冲区中,有printk打印的信息。

  /proc/sys/kernel/printk,该文件四个数值分别对应控制台日志级别、默认消息日志级别、最低控制台日志级别、默认控制台日志级别

        查看当前控制台日志级别:

cat /proc/sys/kernel/printk

        修改控制台日志级别:

echo 7 > /proc/sys/kernel/printk

3.驱动调试方法

1. 编写驱动调试源文件

        编写最简单的:

#include 
#include 
static int __init helloworld_init(void)
{
	printk(KERN_EMERG "helloworld_init\r\n");
	return 0;
}
static void __exit helloworld_exit(void)
{
	printk(KERN_EMERG "helloworld_exit\r\n");
}
module_init(helloworld_init);
module_exit(helloworld_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("carrylee");

2. 编写makefile

#已经编译过的内核源码路径
KERNEL_DIR = /home/uisrc/uisrc-lab-xlnx/sources/kernel
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
#当前路径
CURRENT_DIR = $(shell pwd)
MODULE = helloworld
all :
#进入并调用内核源码目录中Makefile的规则, 将当前的目录中的源码编译成模块
	make -C $(KERNEL_DIR) M=$(CURRENT_DIR) modules
	rm -rf *.symvers *.order *.o *.mod.o *.mod.c
ifneq ($(APP), )
	$(CROSS_COMPILE)gcc $(APP).c -o $(APP)
endif
clean :
	make -C $(KERNEL_DIR) M=$(CURRENT_DIR) clean
	rm $(APP)
#指定编译哪个文件
obj-m += $(MODULE).o

3. 编译

        将源文件拷贝到home目录下。

        make

        生成.ko文件

4. 加载到目标电路板

        将生成的ko文件拷贝出来。

        通过ssh 网络连接电路板,通过TFTP将文件扔进去。

5. 验证

5.1. 加载内核
sudo insmod helloworld.ko

5.2. 移除模块
sudo rmmod helloworld

5.3. 确保模块被移除
lsmod | grep helloworld

4.注册字符设备

#include 
#include 
#include            // 文件操作相关头文件
#include          // 字符设备相关头文件
#include      // 用户空间与内核空间数据拷贝
#define DEVICE_NAME "helloworld_dev"  // 设备名称
#define MAJOR_NUM 0             // 主设备号(0表示由内核自动分配)
#define MINOR_NUM 0             // 次设备号
#define DEVICE_COUNT 1          // 设备数量
static dev_t dev_num;           // 设备号(主设备号+次设备号)
static struct cdev helloworld_cdev;  // 字符设备结构体
// 设备打开操作
static int helloworld_open(struct inode *inode, struct file *file) {
    printk(KERN_EMERG "helloworld device opened\r\n");
    return 0;
}
// 设备关闭操作
static int helloworld_release(struct inode *inode, struct file *file) {
    printk(KERN_EMERG "helloworld device released\r\n");
    return 0;
}
// 文件操作结构体
static struct file_operations helloworld_fops = {
    .owner = THIS_MODULE,
    .open = helloworld_open,
    .release = helloworld_release,
};
static int __init helloworld_init(void) {
    int ret;
    // 1. 分配设备号
    if (MAJOR_NUM == 0) {
        ret = alloc_chrdev_region(&dev_num, MINOR_NUM, DEVICE_COUNT, DEVICE_NAME);
    } else {
        dev_num = MKDEV(MAJOR_NUM, MINOR_NUM);
        ret = register_chrdev_region(dev_num, DEVICE_COUNT, DEVICE_NAME);
    }
    if (ret < 0) {
        printk(KERN_ERR "Failed to allocate device number\r\n");
        return ret;
    }
    printk(KERN_EMERG "helloworld_init: major=%d, minor=%d\r\n",
           MAJOR(dev_num), MINOR(dev_num));
    // 2. 初始化字符设备并注册
    cdev_init(&helloworld_cdev, &helloworld_fops);
    helloworld_cdev.owner = THIS_MODULE;
    ret = cdev_add(&helloworld_cdev, dev_num, DEVICE_COUNT);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add character device\r\n");
        unregister_chrdev_region(dev_num, DEVICE_COUNT);
        return ret;
    }
    return 0;
}
static void __exit helloworld_exit(void) {
    // 注销字符设备并释放设备号
    cdev_del(&helloworld_cdev);
    unregister_chrdev_region(dev_num, DEVICE_COUNT);
    printk(KERN_EMERG "helloworld_exit\r\n");
}
module_init(helloworld_init);
module_exit(helloworld_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("carrylee");

        如图所示,创建成功,将主设备号(241)和次设备号(0)打印了出来。

        然后执行:

sudo mknod /dev/helloworld_dev c 241 0

        可以看到,设备安装成功。

posted @ 2025-12-12 20:50  clnchanpin  阅读(24)  评论(0)    收藏  举报