前言
首先,保证我们具有一定的C语言基础和单片机基础,然后,可以根据B站的视频可以快速上手Linux的学习,这个过程当中除了亲自上手使用或者编写模块驱动代码时,一定要学习到能够独立学习Linux源码以及驱动开发的独立学习能力。
肯定在最开始是记不住里面所有的Linux现有的模块API,那么至少需要知道其中那些会经常用到的API的所属的模块有哪些,至少形成编写代码时会知道Linux里有哪些类型模块(最好了解有Linux内核源码的大致框架图),同时,再进行使用AI快速帮助我们了解或者理解这个模块里的API的特性以及其使用场景(在Linux源码里可找到对应的头文件以及源文件,使用sourceinsight可以快速查找,一般在/linux-5.4/include/linux里的头文件里有介绍)。
后续,在经常使用当中进行逐步深刻记忆(就像学习单片机外设的过程一样)。学习旺哥的Linux开发方法后,可以再通过学习正电原子的补充Linux底层汇编裸机知识,同时再通过学习100ask的Linux应用层方面,形成正式的Linux学习框架,搭建起一个系统的认知,后续就是补充完成以及持续精进了(大部分情况是需要自行学习Linux驱动开发)。
注:(如有侵权,请联系删除)
(1)参考【搞linux的旺仔】【ubuntu系统环境搭建】:windows wsl安装ubuntu_哔哩哔哩_bilibili
(2)注意学习过程中一定要连续性的学习,否则会很快的遗忘。
(3)uboot也内容稍微大一些,可以单独学习。
(4)如果只想使用adb口,在adb shell进入后安装ko文件后使用dmesg可查看打印信息。
嵌入式Linux开发:C代码初步实战
/home/zky/ProjectsHub/Linux_Projects/pingdichan/linux-5.4/drivers/misc
在该目录下编写一个文件使用模块注册函数,注册一个打印模块在启动Linux可见
注意:
顶行证书
文件注释
Linux必要的头文件
注册/退出函数
模块注册/退出函数
模块作者 描述 协议类型
tab缩进(8个空格宽)
以及Linux代码风格
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* my first linux code
*
* Copyright (C) 2010 xxxx Electronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/*
* 设备树描述:
* pkey:pkey {
* compatible = "wangzai,powerkey";
* poweroff-gpios = <&pio PG 14 GPIO_ACTIVE_HIGH>;
* };
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
static int __init fcode_init(void)
{
pr_info("===================>%s running <=================\n", __func__);
return 0;
}
static void __exit fcode_exit(void)
{
pr_info("===================>%s exited <=================\n", __func__);
}
module_init(fcode_init);
module_exit(fcode_exit);
MODULE_AUTHOR("zky");
MODULE_DESCRIPTION("my first linux code");
MODULE_LICENSE("GPL");
写完后
bash
../../scripts/checkpatch.pl -f my_linux_c.c
vim Makefile
obj-$(CONFIG_FCODE) += my_linux_c.o
vim kconfig
config FCODE
tristate "my first linux code"
default n
help
This driver for my first linux code.
export ARCH=riscv
export CROSS_COMPILE=/home/zky/ProjectsHub/Linux_Projects/pingdichan/riscv64-wangzai-linux-gnu-gcc/bin/riscv64-unknown-linux-gnu-
make wangzai_d1s_rtl8723ds_defconfig
make menuconfig
vim .config
make savedefconfig
cp defconfig arch/riscv/configs/wangzai_d1s_rtl8723ds_defconfig
cp arch/riscv/boot/Image /home/zky/ProjectsHub/Linux_Projects/pingdichan/bootcard
cp arch/riscv/boot/dts/sunxi/wangzai_d1s.dts /home/zky/ProjectsHub/Linux_Projects/pingdichan/bootcard
ls /dev/sd*
sudo umount /dev/sdb1
sudo ./mkbootcard /dev/sdb
一、动态模块加载
MakeFile
CROSS_PREFIX := /home/zky/ProjectsHub/Linux_Projects/pingdichan/riscv64-wangzai-linux-gnu-gcc/bin/riscv64-unknown-linux-gnu-
KERNEL_DIR := /home/zky/ProjectsHub/Linux_Projects/pingdichan/linux-5.4/
arch := riscv
obj-m := ko.o
cc := $(CROSS_PREFIX)gcc
ld := $(CROSS_PREFIX)ld
.PHONY:all
all:
make ARCH=$(arch) CC=$(cc) LD=$(ld) -C $(KERNEL_DIR) M=$(shell pwd) modules V=1
.PHONY:clean
clean:
make ARCH=$(arch) CC=$(cc) LD=$(ld) -C $(KERNEL_DIR) M=$(shell pwd) clean
ko.c
cpp
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* my first linux code
*
* Copyright (C) 2010 xxxx Electronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/*
* 设备树描述:
* pkey:pkey {
* compatible = "wangzai,powerkey";
* poweroff-gpios = <&pio PG 14 GPIO_ACTIVE_HIGH>;
* };
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
static int __init ko_init(void)
{
pr_info("===================>%s running <=================\n", __func__);
return 0;
}
static void __exit ko_exit(void)
{
pr_info("===================>%s exited <=================\n", __func__);
}
module_init(ko_init);
module_exit(ko_exit);
MODULE_AUTHOR("zky");
MODULE_DESCRIPTION("my first linux code");
MODULE_LICENSE("GPL");
首先内核必须编译过,再编译模块代码
或是使用menuconfig 按下m,可以将内核代码单独编译为.ko模块代码


二、idr
idr.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* Idr test code.
*
* Copyright (C) 2025 zky
*
* This module for test idr.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/idr.h>
static int __init idr_test_init(void)
{
char *s1 = "parm1", *s2 = "parm2", *s3 = "parm3", *st = "parm_tmp";
int id1, id2, id3, idt;
struct idr idr;
char *ret_s;
/*
* Init idr
*/
idr_init(&idr);
/*
* Add element to idr
*/
id1 = idr_alloc(&idr, s1, 0, 10, GFP_KERNEL);
id2 = idr_alloc(&idr, s2, 0, 10, GFP_KERNEL);
id3 = idr_alloc(&idr, s3, 0, 10, GFP_KERNEL);
/*
* Find idr element
*/
ret_s = idr_find(&idr, id1);
pr_info("id1 [%d] = %s\n", id1, ret_s);
ret_s = idr_find(&idr, id2);
pr_info("id2 [%d] = %s\n", id2, ret_s);
ret_s = idr_find(&idr, id3);
pr_info("id3 [%d] = %s\n", id3, ret_s);
/*
* Delete idr element.
*/
idr_remove(&idr, id2);
/*
* Replace the idr parameter.
*/
idr_replace(&idr, st, id1);
/*
* Check if the idr is empty.
*/
if (idr_is_empty(&idr))
pr_info("idr empty\n");
else
pr_info("idr not empty\n");
/*
* Compile each parameter of the idr in a loop.
*/
idr_for_each_entry(&idr, ret_s, idt)
pr_info("id [%d] = %s\n", idt, ret_s);
/*
* Free idr
*/
idr_destroy(&idr);
return 0;
}
static void __exit idr_test_exit(void)
{
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("This is idr test code");
module_init(idr_test_init);
module_exit(idr_test_exit);

三、链表操作
linklist.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* link list test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/slab.h>
/*
* 定义一个链表结构体
*/
struct element {
struct list_head list;
#define NAME_LEN 64
char name[NAME_LEN];
};
/*
*申请链表元素
*/
static struct element *alloc_ele(char *name)
{
struct element *ele;
ele = kmalloc(sizeof(struct element), GFP_KERNEL);
if (!ele)
return NULL;
strcpy(ele->name, name);
return ele;
}
static void free_ele(struct element *ele)
{
kfree(ele);
}
static int __init link_list_init(void)
{
struct element hele;
struct element *ele, *tmp_ele;
struct element *ele1, *ele2, *ele3;
/*
* 初始化链表头
*/
INIT_LIST_HEAD(&hele.list);
/*
* 申请链表
*/
ele1 = alloc_ele("p1");
if (!ele1)
return -ENOMEM;
/*
* 将链表添加到链表链中
*/
list_add_tail(&ele1->list, &hele.list);
ele2 = alloc_ele("p2");
if (!ele2) {
free_ele(ele1);
return -ENOMEM;
}
list_add_tail(&ele2->list, &hele.list);
ele3 = alloc_ele("p3");
if (!ele3) {
free_ele(ele2);
free_ele(ele1);
return -ENOMEM;
}
list_add_tail(&ele3->list, &hele.list);
/*
* 遍历删除链表中特定元素
* 这里使用list_for_each_entry_safe()而非list_for_each_entry()原因是
* 由于在循环链表中要删除元素,而list_for_each_entry()不支持此种操作
*/
pr_info("start delete ele\n");
list_for_each_entry_safe(ele, tmp_ele, &hele.list, list) {
pr_info(" ===========> ele->name = %s\n", ele->name);
if (!strcmp(ele->name, "p3")) {
list_del(&ele->list);
free_ele(ele);
}
}
pr_info("start get ele\n");
list_for_each_entry_safe(ele, tmp_ele, &hele.list, list) {
pr_info(" ===========> ele->name = %s\n", ele->name);
list_del(&ele->list);
free_ele(ele);
}
if (list_empty(&hele.list))
pr_info(" ===========> link list is empty\n");
else
pr_info(" ===========> link list not empty\n");
return 0;
}
static void __exit link_list_exit(void)
{
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is link list test");
module_init(link_list_init);
module_exit(link_list_exit);
四、rbtree(红黑树)
rbtree.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* rbtree test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/slab.h>
/*
* 定义一个红黑树结构体
*/
struct par {
u32 id;
char *name;
struct rb_node node;
};
/*
* 通过id查找对应的结构体
*/
static struct par *rbtree_find_by_id(struct rb_root *root, u32 id)
{
struct rb_node *node;
struct par *par;
node = root->rb_node;
while (node) {
par = rb_entry(node, struct par, node);
if (par->id < id)
node = node->rb_right;
else if (par->id > id)
node = node->rb_left;
else
return par;
}
return NULL;
}
/*
* 往红黑树中插入元素
*/
static void rbtree_insert(struct rb_root *root, struct par *par)
{
struct rb_node **new_node, *parent_node = NULL;
struct par *curr_par;
new_node = &(root->rb_node);
while (*new_node) {
parent_node = *new_node;
curr_par = rb_entry(parent_node, struct par,
node);
if (curr_par->id > par->id) {
new_node = &((*new_node)->rb_left);
} else if (curr_par->id < par->id) {
new_node = &((*new_node)->rb_right);
} else {
pr_info("%u already in tree\n", par->id);
return;
}
}
rb_link_node(&par->node, parent_node, new_node);
rb_insert_color(&par->node, root);
}
/*
* 删除红黑树中对应的元素
*/
static void rbtree_delete(struct rb_root *root, struct par *par)
{
rb_erase(&par->node, root);
}
/*
* 删除红黑树中所有元素
*/
static void rbtree_delete_all(struct rb_root *root)
{
struct rb_node *node, *next;
struct par *curr_par;
for (node = rb_first(root);
next = node ? rb_next(node) : NULL, node != NULL;
node = next) {
curr_par = rb_entry(node, struct par, node);
rbtree_delete(root, curr_par);
}
}
static int __init rbtree_init(void)
{
struct rb_root root = {};
struct par par1 = {
.id = 1,
.name = "one",
};
struct par par2 = {
.id = 2,
.name = "two",
};
struct par par3 = {
.id = 3,
.name = "three",
};
rbtree_insert(&root, &par1);
rbtree_insert(&root, &par2);
rbtree_insert(&root, &par3);
pr_info("find name = %s\n", rbtree_find_by_id(&root, 1)->name);
pr_info("find name = %s\n", rbtree_find_by_id(&root, 2)->name);
pr_info("find name = %s\n", rbtree_find_by_id(&root, 3)->name);
rbtree_delete_all(&root);
return 0;
}
static void __exit rbtree_exit(void)
{
pr_info("%s\n", __func__);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is rbtree test");
module_init(rbtree_init);
module_exit(rbtree_exit);
Makefile(后续使用时注意更改文件名称)
bash
CROSS_PREFIX := /home/zky/ProjectsHub/Linux_Projects/pingdichan/riscv64-wangzai-linux-gnu-gcc/bin/riscv64-unknown-linux-gnu-
KERNEL_DIR := /home/zky/ProjectsHub/Linux_Projects/pingdichan/linux-5.4/
arch := riscv
obj-m := rbtree.o
cc := $(CROSS_PREFIX)gcc
ld := $(CROSS_PREFIX)ld
.PHONY:all
all:
make ARCH=$(arch) CC=$(cc) LD=$(ld) -C $(KERNEL_DIR) M=$(shell pwd) modules V=1
.PHONY:clean
clean:
make ARCH=$(arch) CC=$(cc) LD=$(ld) -C $(KERNEL_DIR) M=$(shell pwd) clean
env.sh(后续使用时注意更改文件名称)
bash
#!/bin/bash
export ARCH=riscv
export CROSS_COMPILE=/home/zky/ProjectsHub/Linux_Projects/pingdichan/riscv64-wangzai-linux-gnu-gcc/bin/riscv64-unknown-linux-gnu-
echo "ARCH: $ARCH"
echo "CROSS_COMPILE: $CROSS_COMPILE"
# /home/zky/ProjectsHub/Linux_Projects/pingdichan/linux-5.4/scripts/checkpatch.pl -f linklist.c
# 定义checkpatch.pl路径(方便维护)
CHECKPATCH_PATH="/home/zky/ProjectsHub/Linux_Projects/pingdichan/linux-5.4/scripts/checkpatch.pl"
# 要检查的文件
CHECK_FILE="rbtree.c"
echo -e "\n========== 开始检查 $CHECK_FILE 代码规范 =========="
"$CHECKPATCH_PATH" -f "$CHECK_FILE"
# 检查执行结果,给出友好提示
if [ $? -eq 0 ]; then
echo -e "========== 完成检查 $CHECK_FILE 代码规范 =========="
echo -e "\n✅ $CHECK_FILE 代码规范检查通过!"
else
echo -e "========== 完成检查 $CHECK_FILE 代码规范 =========="
echo -e "\n⚠️ $CHECK_FILE 代码规范检查发现问题,请根据提示修改!"
# 非0退出码,但不终止脚本(可选,根据你的需求调整)
# exit 1
fi
使用方法:
bash
# 使用ubuntu终端
. ./ex.sh
make
adb devices
adb push ./rbtree.ko /root
# 后面使用板子的串口终端
root
cd /root
ls
lsmod
insmod rbtree.ko
rmmod rbtree.ko

注:空行不能有tab;变量定义和执行代码间需要空行隔开;
五、bitmap
bitmap.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* bitmap test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
/*
* 定义bitmap
*/
#define BITMAP_LEN 128
static DECLARE_BITMAP(test_bitmap1, BITMAP_LEN);
static DECLARE_BITMAP(test_bitmap2, BITMAP_LEN);
static int __init bitmap_test_init(void)
{
int i, len, start, align_mask;
for (i = 0; i < 10; i++) {
/*
* 设置特定的bit
*/
bitmap_set(test_bitmap1, i, 1);
}
/*
* 检查bitmap是否为全1
*/
if (bitmap_full(test_bitmap1, BITMAP_LEN))
pr_info("bitmap full\n");
else
pr_info("bitmap not full\n");
/*
* 将所有bit设为0
*/
bitmap_zero(test_bitmap1, BITMAP_LEN);
/*
* 将所有bit设为1
*/
bitmap_fill(test_bitmap2, BITMAP_LEN);
/*
* 将test_bitmap1与test_bitmap2的每个bit进行与操作
*/
bitmap_and(test_bitmap1, test_bitmap1, test_bitmap2, BITMAP_LEN);
/*
* 检查test_bitmap1的所有bit是否为0
*/
if (bitmap_empty(test_bitmap1, BITMAP_LEN))
pr_info("bitmap empty\n");
else
pr_info("bitmap not empty\n");
/*
* 将所有bit设为0
*/
bitmap_zero(test_bitmap1, BITMAP_LEN);
/*
* 将所有bit设为1
*/
bitmap_fill(test_bitmap2, BITMAP_LEN);
/*
* 将test_bitmap1与test_bitmap2的每个bit进行或操作
*/
bitmap_or(test_bitmap1, test_bitmap1, test_bitmap2, BITMAP_LEN);
/*
* 检查test_bitmap1的所有bit是否为0
*/
if (bitmap_full(test_bitmap1, BITMAP_LEN))
pr_info("bitmap full\n");
else
pr_info("bitmap not full\n");
/*
* 将所有bit设为0
*/
bitmap_zero(test_bitmap1, BITMAP_LEN);
for (i = 0; i < 10; i++) {
/*
* 设置特定的bit
*/
bitmap_set(test_bitmap1, i, 1);
}
/*
* bitmap_find_next_zero_area - find a contiguous aligned zero area
* @map: The address to base the search on
* @size: The bitmap size in bits
* @start: The bitnumber to start searching at
* @nr: The number of zeroed bits we're looking for
* @align_mask: Alignment mask for zero area
*
* The @align_mask should be one less than a power of 2; the effect is that
* the bit offset of all zero areas this function finds is multiples of that
* power of 2. A @align_mask of 0 means no alignment is required.
*/
/*
* 从start的bit处查找不需要对齐的连续2个为0的区域标签
*/
len = 2;
start = 0;
align_mask = 0;
i = bitmap_find_next_zero_area(test_bitmap1, BITMAP_LEN,
start, len, align_mask);
pr_info("idx = %d\n", i);
return 0;
}
static void __exit bitmap_test_exit(void)
{
pr_info("%s\n", __func__);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is bitmap test");
module_init(bitmap_test_init);
module_exit(bitmap_test_exit);

六、kfifo
kfifo.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* kfifo test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/kfifo.h>
#define KFIFO_SIZE 256
static int __init kfifo_test_init(void)
{
char buffer[8], val[2];
struct kfifo kfifo;
int ret = 0;
/*
* 申请一个深度为KFIFO_SIZE的kfifo
*/
ret = kfifo_alloc(&kfifo, KFIFO_SIZE, GFP_KERNEL);
if (ret) {
pr_info("Kfifo mallo fail\n");
return -EINVAL;
}
/*
* 往kfifo中压入元素
*/
buffer[0] = 1;
buffer[1] = 2;
kfifo_in(&kfifo, buffer, 2);
buffer[0] = 3;
buffer[1] = 4;
kfifo_in(&kfifo, buffer, 2);
pr_info("currtent kfifo len = %d\n", kfifo_len(&kfifo));
/*
* 获取kfifo的元素
*/
if (kfifo_out(&kfifo, &val, 2) != 2) {
ret = -EINVAL;
goto fail;
}
pr_info("first get val = %d, %d\n", val[0], val[1]);
/*
* 判断kfifo是否为空
*/
if (kfifo_is_empty(&kfifo))
pr_info("current kfifio is empty\n");
else
/*
* 如果kfifo不为空,则获取kfifo的长度
*/
pr_info("current kfifo len = %d\n", kfifo_len(&kfifo));
if (kfifo_out(&kfifo, &val, 2) != 2) {
ret = -EINVAL;
goto fail;
}
pr_info("second get val = %d, %d\n", val[0], val[1]);
if (kfifo_is_empty(&kfifo))
pr_info("current kfifio is empty\n");
else
pr_info("current kfifo len = %d\n", kfifo_len(&kfifo));
fail:
/*
* 释放kfifo
*/
kfifo_free(&kfifo);
return 0;
}
static void __exit kfifo_test_exit(void)
{
pr_info("%s\n", __func__);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is kfifo test");
module_init(kfifo_test_init);
module_exit(kfifo_test_exit);

七、栈回溯(调试方面)
noinline 防止内联优化
dump_stack.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* dump stack test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
static noinline void function_1(void)
{
dump_stack();
}
static noinline void function_2(void)
{
function_1();
}
static noinline void function_3(void)
{
function_2();
}
static int __init dump_stack_test_init(void)
{
function_3();
return 0;
}
static void __exit dump_stack_test_exit(void)
{
}
MODULE_AUTHOR("Wangzai <1587636487@qq.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is dump stack test");
module_init(dump_stack_test_init);
module_exit(dump_stack_test_exit);

dmesg


由于编译环境上下文的问题会出现下面的警告,但是能正常安装使用.ko文件,不要管这个警告了

八、锁
8.1 互斥锁
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* mutex test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/delay.h>
static struct task_struct *thread1, *thread2;
static DEFINE_MUTEX(mutexlock);
static int shared_resource;
/*
* 线程处理函数,thread1和thread2共用同一个线程处理函数。
*/
static int thread_handler(void *arg)
{
pr_info("thread running\n");
while (!kthread_should_stop()) {
/*
* 互斥锁,如果没抢到锁,就会被调度走。
*/
mutex_lock(&mutexlock);
shared_resource = 0;
if (shared_resource == 1)
pr_info("shared_resource verfiy to 1\n");
shared_resource = 1;
if (shared_resource == 0)
pr_info("shared_resource verfiy to 0\n");
mutex_unlock(&mutexlock);
}
return 0;
}
static int __init mutex_test_init(void)
{
/*
* 申请两个线程,实现两个线程共同操作同一个变量。
*/
thread1 = kthread_run(thread_handler, (void *)1, "thread1");
if (IS_ERR(thread1)) {
pr_info("thread1 create fail\n");
return -1;
}
pr_info("thread1 create success\n");
thread2 = kthread_run(thread_handler, (void *)2, "thread2");
if (IS_ERR(thread2)) {
kthread_stop(thread1);
pr_info("thread2 create fail\n");
return -1;
}
pr_info("thread2 create success\n");
return 0;
}
static void __exit mutex_test_exit(void)
{
kthread_stop(thread1);
kthread_stop(thread2);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is mutex test");
module_init(mutex_test_init);
module_exit(mutex_test_exit);

8.2 自旋锁
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* spinlock test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/delay.h>
static struct task_struct *thread1, *thread2;
static DEFINE_SPINLOCK(spinlock);
static int shared_resource;
/*
* 线程处理函数,thread1和thread2共用同一个线程处理函数。
*/
static int thread_handler(void *arg)
{
unsigned long flags;
pr_info("thread running\n");
while (!kthread_should_stop()) {
/*
* 自旋锁,如果没抢到锁,就会陷入自旋。
*/
spin_lock_irqsave(&spinlock, flags);
shared_resource = 0;
if (shared_resource == 1)
pr_info("shared_resource verfiy to 1\n");
spin_unlock_irqrestore(&spinlock, flags);
spin_lock_irqsave(&spinlock, flags);
shared_resource = 1;
if (shared_resource == 0)
pr_info("shared_resource verfiy to 0\n");
spin_unlock_irqrestore(&spinlock, flags);
}
return 0;
}
static int __init spinlock_test_init(void)
{
/*
* 申请两个线程,实现两个线程共同操作同一个变量。
*/
thread1 = kthread_run(thread_handler, (void *)1, "thread1");
if (IS_ERR(thread1)) {
pr_info("thread1 create fail\n");
return -1;
}
thread2 = kthread_run(thread_handler, (void *)2, "thread2");
if (IS_ERR(thread2)) {
kthread_stop(thread1);
pr_info("thread2 create fail\n");
return -1;
}
return 0;
}
static void __exit spinlock_test_exit(void)
{
kthread_stop(thread1);
kthread_stop(thread2);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is spinlock test");
module_init(spinlock_test_init);
module_exit(spinlock_test_exit);

8.3 信号量
semaphore.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* semaphore test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/kthread.h>
#include <linux/delay.h>
static struct task_struct *thread1, *thread2;
static struct semaphore sem;
static int shared_resource;
/*
* 线程处理函数,thread1和thread2共用同一个线程处理函数。
*/
static int thread_handler(void *arg)
{
pr_info("thread running\n");
while (!kthread_should_stop()) {
/*
* 尝试获取信号量。
*/
if (down_interruptible(&sem)) {
pr_err("Interrupted while waiting for semaphore\n");
return -ERESTARTSYS;
}
shared_resource = 0;
if (shared_resource == 1)
pr_info("shared_resource verfiy to 1\n");
shared_resource = 1;
if (shared_resource == 0)
pr_info("shared_resource verfiy to 0\n");
/*
* 释放信号量。
*/
up(&sem);
}
return 0;
}
static int __init semaphore_test_init(void)
{
/*
* 初始化信号量,并设置信号量计数器的初始值为1。
*/
sema_init(&sem, 1);
/*
* 申请两个线程,实现两个线程共同操作同一个变量。
*/
thread1 = kthread_run(thread_handler, (void *)1, "thread1");
if (IS_ERR(thread1)) {
pr_info("thread1 create fail\n");
return -1;
}
thread2 = kthread_run(thread_handler, (void *)2, "thread2");
if (IS_ERR(thread2)) {
kthread_stop(thread1);
pr_info("thread2 create fail\n");
return -1;
}
return 0;
}
static void __exit semaphore_test_exit(void)
{
kthread_stop(thread1);
kthread_stop(thread2);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is semaphore test");
module_init(semaphore_test_init);
module_exit(semaphore_test_exit);
rwsem.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* rwsem test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/rwsem.h>
#include <linux/delay.h>
static struct rw_semaphore rwsem;
struct task_struct *threads[4];
static int shared_data;
/*
* 读线程函数。
*/
static int read_handler(void *arg)
{
pr_info("read thread running\n");
while (!kthread_should_stop()) {
/*
* 获取读锁。
*/
down_read(&rwsem);
pr_info("read shared_data = %d\n", shared_data);
/*
* 释放读锁。
*/
up_read(&rwsem);
}
return 0;
}
/*
* 写线程函数。
*/
static int write_handler(void *arg)
{
pr_info("write thread running\n");
while (!kthread_should_stop()) {
/*
* 获取写锁。
*/
down_write(&rwsem);
shared_data += 1;
pr_info("write shared_data = %d\n", shared_data);
/*
* 释放写锁。
*/
up_write(&rwsem);
}
return 0;
}
static int __init rwsem_test_init(void)
{
int i, j;
/*
* 初始化读写信号量。
*/
init_rwsem(&rwsem);
/*
* 创建2个写线程。
*/
for (i = 0; i < 2; i++) {
threads[i] = kthread_run(write_handler, NULL, "write_thread");
if (IS_ERR(threads[i]))
goto ERR;
}
/*
* 创建2个读者线程。
*/
for (i = 2; i < 4; i++) {
threads[i] = kthread_run(read_handler, NULL, "read_thread");
if (IS_ERR(threads[i]))
goto ERR;
}
return 0;
ERR:
/*
* 处理申请线程失败。
*/
for (j = 0; j < i; j++)
kthread_stop(threads[j]);
return -1;
}
static void __exit rwsem_test_exit(void)
{
for (int i = 0; i < 4; i++)
kthread_stop(threads[i]);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is rwsem test");
module_init(rwsem_test_init);
module_exit(rwsem_test_exit);
8.4 顺序锁
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* seqlock test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/seqlock.h>
#include <linux/delay.h>
static seqlock_t seqlock;
struct task_struct *threads[4];
static int shared_data;
/*
* 读线程函数。
*/
static int read_handler(void *arg)
{
unsigned int seq;
int val;
pr_info("read thread running\n");
while (!kthread_should_stop()) {
do {
seq = read_seqbegin(&seqlock);
val = shared_data;
} while (read_seqretry(&seqlock, seq));
pr_info("read shared_data = %d\n", shared_data);
}
return 0;
}
/*
* 写线程函数。
*/
static int write_handler(void *arg)
{
pr_info("write thread running\n");
while (!kthread_should_stop()) {
/*
* 获取写锁。
*/
write_seqlock(&seqlock);
shared_data += 1;
pr_info("write shared_data = %d\n", shared_data);
/*
* 释放写锁。
*/
write_sequnlock(&seqlock);
}
return 0;
}
static int __init seqlock_test_init(void)
{
int i, j;
/*
* 初始化seqlock。
*/
seqlock_init(&seqlock);
/*
* 创建2个写线程。
*/
for (i = 0; i < 2; i++) {
threads[i] = kthread_run(write_handler, NULL, "write_thread");
if (IS_ERR(threads[i]))
goto ERR;
}
/*
* 创建2个读者线程。
*/
for (i = 2; i < 4; i++) {
threads[i] = kthread_run(read_handler, NULL, "read_thread");
if (IS_ERR(threads[i]))
goto ERR;
}
return 0;
ERR:
/*
* 处理申请线程失败。
*/
for (j = 0; j < i; j++)
kthread_stop(threads[j]);
return -1;
}
static void __exit seqlock_test_exit(void)
{
int i;
for (i = 0; i < 4; i++)
kthread_stop(threads[i]);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is seqlock test");
module_init(seqlock_test_init);
module_exit(seqlock_test_exit);

8.5 原子操作
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* atomic test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/atomic.h>
#include <linux/kthread.h>
#include <linux/delay.h>
static atomic_t counter = ATOMIC_INIT(0); // 初始化原子变量
static int __init atomic_test_init(void)
{
/*
* 原子设置counter的值为10。
*/
atomic_set(&counter, 10);
pr_info("counter val = %d\n", atomic_read(&counter));
/*
* 原子将counter加1。
*/
atomic_inc(&counter);
pr_info("counter val = %d\n", atomic_read(&counter));
/*
* 原子将counter加5。
*/
atomic_add(5, &counter);
pr_info("counter val = %d\n", atomic_read(&counter));
/*
* 原子将counter减2。
*/
atomic_sub(2, &counter);
pr_info("counter val = %d\n", atomic_read(&counter));
return 0;
}
static void __exit atomic_test_exit(void)
{
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is atomic test");
module_init(atomic_test_init);
module_exit(atomic_test_exit);
九、线程操作
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* kthread test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
int val = 1;
/*
* 线程处理函数
*/
static int kernel_thread(void *arg)
{
int *val = (int *)arg;
while (!kthread_should_stop()) {
msleep(1000);
pr_info("test_threads_handler: arg = %d\n", *val);
}
return 0;
}
static int __init kthread_test_init(void)
{
struct task_struct *thread;
/*
* 创建内核线程
*/
thread = kthread_create(kernel_thread, &val, "test_thread");
if (IS_ERR(thread)) {
pr_info("Failed to create waiting thread.\n");
return PTR_ERR(thread);
}
/*
* 唤醒内核线程
*/
wake_up_process(thread);
msleep(5000);
/*
* 停止内核线程
*/
kthread_stop(thread);
return 0;
}
static void __exit kthread_test_exit(void)
{
pr_info("%s\n", __func__);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is create kthread test");
module_init(kthread_test_init);
module_exit(kthread_test_exit);

十、工作队列Wait Queue
工作队列部分类似于线程池的某些方面
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* workqueue test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
struct work_struct work1;
struct work_struct work2;
static struct workqueue_struct *work_queue;
static void work_func1(struct work_struct *work)
{
pr_info("my_work_func 1 exec\n");
}
static void work_func2(struct work_struct *work)
{
pr_info("my_work_func 2 exec\n");
}
static int __init workqueue_test_init(void)
{
/*
* 创建一个工作队列
*/
work_queue = create_workqueue("wangzai-workqueue");
/*
* 初始化work
*/
INIT_WORK(&work1, work_func1);
INIT_WORK(&work2, work_func2);
/*
* 将work调度运行
*/
queue_work(work_queue, &work1);
queue_work(work_queue, &work2);
return 0;
}
static void __exit workqueue_test_exit(void)
{
destroy_workqueue(work_queue);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is workqueue test");
module_init(workqueue_test_init);
module_exit(workqueue_test_exit);
十一、tasklet
Linux tasklet 是内核中用于处理中断的下半部任务的轻量级延迟执行机制,基于软中断实现。
Linux中断分为上半部和下半部
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* tasklet test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include <linux/kernel.h>
#include<linux/interrupt.h>
static struct tasklet_struct tasklet;
/*
* tasklet处理函数
*/
static void tasklet_handler(unsigned long data)
{
pr_info("%s running, data = %ld\n", __func__, data);
}
static int __init tasklet_test_init(void)
{
unsigned long data = 12;
/*
* 初始化tasklet
*/
tasklet_init(&tasklet, tasklet_handler, data);
/*
* 使能tasklet
*/
tasklet_enable(&tasklet);
/*
* 调度tasklet
*/
tasklet_schedule(&tasklet);
/*
* 失能tasklet
*/
tasklet_disable(&tasklet);
/*
* 调度tasklet
*/
tasklet_schedule(&tasklet);
return 0;
}
static void __exit tasklet_test_exit(void)
{
tasklet_kill(&tasklet);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is tasklet test");
module_init(tasklet_test_init);
module_exit(tasklet_test_exit);

十二、等待队列
详细执行步骤:
-
模块初始化(waitqueue_test_init):
- 初始化等待队列头
wq; - 创建内核线程
waitqueue_test_thread,线程开始执行。
- 初始化等待队列头
-
内核线程执行:
- 定义等待节点
wait→ 调用prepare_to_wait挂到wq,设为可中断休眠; - 打印
kernel thread start wait→ 调用schedule(),线程休眠。
- 定义等待节点
-
主线程继续执行:
- 调用
msleep(3000),主线程休眠 3 秒; - 3 秒后打印
main process wake up kernel thread→ 调用wake_up_interruptible(&wq)唤醒线程。
- 调用
-
内核线程被唤醒:
- 调度器将线程设为运行状态,线程从
schedule()(之前在这里休眠,现在唤醒继续)处继续执行; - 调用
finish_wait清理等待节点 → 打印kernel thread wake up→ 线程返回 0,退出。
- 调度器将线程设为运行状态,线程从
-
主线程收尾:
- 调用
kthread_stop停止线程(此时线程已退出)→ 模块初始化完成。
- 调用
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* waitqueue test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
wait_queue_head_t wq;
/*
* 线程处理函数
*/
static int waitqueue_test_thread(void *arg)
{
/*
* 初始化wait
*/
DEFINE_WAIT(wait);
/*
* 等待前准备
*/
prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
pr_info("kernel thread start wait\n");
/*
* 利用schedule()将任务调度走
*/
schedule();
finish_wait(&wq, &wait);
pr_info("kernel thread wake up\n");
return 0;
}
static int __init waitqueue_test_init(void)
{
struct task_struct *thread;
/*
* 初始化waitqueue
*/
init_waitqueue_head(&wq);
/*
* 创建内核线程
*/
thread = kthread_run(waitqueue_test_thread, NULL, "waitqueue_test_thread");
if (IS_ERR(thread)) {
pr_info("Failed to create waiting thread.\n");
return PTR_ERR(thread);
}
msleep(3000);
pr_info("main process wake up kernel thread\n");
/*
* 唤醒等待的waitqueue
*/
wake_up_interruptible(&wq);
/*
* 停止内核线程
*/
kthread_stop(thread);
return 0;
}
static void __exit waitqueue_test_exit(void)
{
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is waitqueue test");
module_init(waitqueue_test_init);
module_exit(waitqueue_test_exit);


十三、完成量complete
在 Linux 中,完成量可以看成是一种与信号量相关的数据结构,在某些情况下,完成量可以替代
信号量的使用。
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* complete test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
static struct completion completion;
/*
* 线程处理函数
*/
static int complete_test_thread(void *arg)
{
pr_info("kernel thread running\n");
msleep(3000);
pr_info("kernel threads complete\n");
/*
* 利用complete()通知主线程完成
*/
complete(&completion);
return 0;
}
static int __init complete_test_init(void)
{
struct task_struct *thread;
init_completion(&completion);
/*
* 创建线程
*/
thread = kthread_run(complete_test_thread, NULL, "complete_test_thread");
if (IS_ERR(thread)) {
pr_info("Failed to create waiting thread.\n");
return PTR_ERR(thread);
}
pr_info("Main thread: Waiting for completion\n");
/*
* 利用complete机制等待其他线程完成
*/
wait_for_completion(&completion);
if (completion_done(&completion))
pr_info("Main thread: Detected completion done\n");
return 0;
}
static void __exit complete_test_exit(void)
{
pr_info("%s\n", __func__);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is complete test");
module_init(complete_test_init);
module_exit(complete_test_exit);

十四、通知链notifier chain
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* notifier chain test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/notifier.h>
#include <linux/ktime.h>
static BLOCKING_NOTIFIER_HEAD(test_notifier_head);
#define ACTION_A 1
#define ACTION_B 2
/*
* 内核通知链回调函数
*/
static int test_nb1_notifier_call(struct notifier_block *self,
unsigned long action, void *data)
{
if (action == ACTION_A) {
pr_info("%s :action = ACTION_A, data = %d\n", __func__,
*(unsigned int *)data);
} else if (action == ACTION_B) {
pr_info("%s :action = ACTION_B, data = %d\n", __func__,
*(unsigned int *)data);
} else
pr_info("%s :unkown action\n", __func__);
return NOTIFY_DONE;
}
/*
* 内核通知链结构体
*/
static struct notifier_block test_nb1 = {
/*
* 通知链函数指针
*/
.notifier_call = test_nb1_notifier_call,
/*
* 通知链优先级
*/
.priority = INT_MIN + 1,
};
static int test_nb2_notifier_call(struct notifier_block *self,
unsigned long action, void *data)
{
if (action == ACTION_A) {
pr_info("%s :action = ACTION_A, data = %d\n", __func__,
*(unsigned int *)data);
} else if (action == ACTION_B) {
pr_info("%s :action = ACTION_B, data = %d\n", __func__,
*(unsigned int *)data);
} else
pr_info("%s :unkown action\n", __func__);
return NOTIFY_DONE;
}
static struct notifier_block test_nb2 = {
.notifier_call = test_nb2_notifier_call,
.priority = INT_MIN + 2,
};
static int __init notifier_chain_test_init(void)
{
unsigned long action;
unsigned int data;
/*
* 注册内核通知链
*/
pr_info("notifier chain register\n");
blocking_notifier_chain_register(&test_notifier_head, &test_nb1);
blocking_notifier_chain_register(&test_notifier_head, &test_nb2);
/*
* 调用内核通知链
*/
pr_info("notifier chain call acton = ACTION_A\n");
action = ACTION_A;
data = 10;
blocking_notifier_call_chain(&test_notifier_head, action, &data);
pr_info("notifier chain call acton = ACTION_B\n");
action = ACTION_B;
data = 1;
blocking_notifier_call_chain(&test_notifier_head, action, &data);
return 0;
}
static void __exit notifier_chain_test_exit(void)
{
/*
* 注销内核通知链
*/
blocking_notifier_chain_unregister(&test_notifier_head, &test_nb1);
blocking_notifier_chain_unregister(&test_notifier_head, &test_nb2);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is notifier chain test");
module_init(notifier_chain_test_init);
module_exit(notifier_chain_test_exit);

十五、定时器
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* timer test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
static struct timer_list timer;
/*
* 定时器回调函数
*/
void timer_callback(struct timer_list *timer)
{
pr_info("timer expired!\n");
/*
* 重新启动定时器
*/
mod_timer(timer, jiffies + msecs_to_jiffies(1000));
}
static int __init timer_test_init(void)
{
/*
* 初始化定时器
*/
timer_setup(&timer, timer_callback, 0);
/*
* 启动定时器,1秒后到期
*/
mod_timer(&timer, jiffies + msecs_to_jiffies(1000));
return 0;
}
static void __exit timer_test_exit(void)
{
del_timer(&timer);
pr_info("%s\n", __func__);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is timer test");
module_init(timer_test_init);
module_exit(timer_test_exit);

十六、字符设备char
app.c
cpp
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <stdint.h>
#include <sys/mman.h>
int main(int argc, char *argv[])
{
int devfd, val, size = 4096;
unsigned int ioctl_val = 0;
char *src = "hello world\n";
char dst[128];
devfd = open("/dev/chrdev_dev", O_RDWR);
if (devfd < 0) {
printf("open /dev/chrdev fail\n");
return -1;
}
write(devfd, src, strlen(src));
read(devfd, dst, strlen(src));
ioctl_val = 1;
ioctl(devfd, 1111, &ioctl_val);
ioctl_val = 3;
ioctl(devfd, 2222, &ioctl_val);
close(devfd);
printf("dst = %s\n", dst);
return 0;
}
chrdev.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* chrdev test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define CHARDEV_DEV_CNT (1)
#define CHARDEV_DEV_NAME "chrdev_dev"
#define CHARDEV_CLASS_NAME "chrdev_class"
#define CHARDEV_CDEV_NAME "chrdev_chrdev"
#define MALLOC_LEN (128)
#define CHRDEV_DBG pr_info("func = %s, line = %d\n", __func__, __LINE__)
struct chrdev_dev {
dev_t devid;
struct cdev *cdev;
struct class *class;
struct device *dev;
};
static struct chrdev_dev chrdev;
/*
* 字符设备open()函数
*/
static int chrdev_open(struct inode *inode_p, struct file *file_p)
{
char *mem = NULL;
CHRDEV_DBG;
/*
* 申请一段内存并保存到文件的私有数据中。
*/
mem = kmalloc(MALLOC_LEN, GFP_KERNEL);
if (!mem)
return -1;
file_p->private_data = mem;
return 0;
}
/*
* 字符设备release()函数,即关闭函数
*/
static int chrdev_release(struct inode *inode_p, struct file *file_p)
{
char *mem = (char *)file_p->private_data;
CHRDEV_DBG;
/*
* 通过kfree()函数释放在chrdev_open()函数中申请的内存。
*/
kfree(mem);
return 0;
}
/*
* 字符设备读函数
*/
static ssize_t chrdev_read(struct file *file_p, char __user *buf, size_t size,
loff_t *ppos)
{
char *mem = (char *)file_p->private_data;
int rc;
CHRDEV_DBG;
/*
* 通过copy_to_user()函数将内核空间的数据(mem)拷贝到应用程序空间(buf)。
*/
rc = copy_to_user(buf, mem, size);
if (rc)
return -1;
return size;
}
/*
* 字符设备写函数
*/
static ssize_t chrdev_write(struct file *file_p, const char __user *buf,
size_t size, loff_t *ppos)
{
char *mem = (char *)file_p->private_data;
int rc;
CHRDEV_DBG;
/*
* 通过copy_from_user()函数将应用程序空间的数据(buf拷贝到内核空间(mem)。
*/
rc = copy_from_user(mem, buf, size);
if (rc)
return -1;
return size;
}
/*
* 字符设备iotctl()函数,通过cmd传递命令,通过arg传递数据
*/
static long chrdev_unlocked_ioctl(struct file *file_p, unsigned int cmd,
unsigned long arg)
{
unsigned int val = 0;
int rc;
CHRDEV_DBG;
rc = copy_from_user((void *)&val, (const char __user *)arg, sizeof(val));
if (rc)
return -1;
pr_info("cmd = %d, val = %d\n", cmd, val);
return 0;
}
/*
* 文件操作结构体
*/
static const struct file_operations chrdev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = chrdev_unlocked_ioctl,
.read = chrdev_read,
.write = chrdev_write,
.open = chrdev_open,
.release = chrdev_release,
};
static int __init chrdev_test_init(void)
{
int major = 0, minor = 0;
/*
* 注册一个字符设备。
* 本质是创建一组与设备号绑定的文件读写接口。
*
* 第一个参数为传入的major,当major = 0时,自动获取major。
*/
major = register_chrdev(0, CHARDEV_CDEV_NAME, &chrdev_fops);
if (!major)
return -1;
chrdev.devid = MKDEV(major, minor);
/*
* 创建一个class。
*/
chrdev.class = class_create(THIS_MODULE, CHARDEV_CLASS_NAME);
if (IS_ERR(chrdev.class))
goto UNREG_CHRDEV;
/*
* 创建一个class下面的设备,并通过设备号与字符设备绑定,这个设备是 chrdev_dev
*/
chrdev.dev = device_create(chrdev.class, NULL, chrdev.devid,
NULL, CHARDEV_DEV_NAME);
if (!chrdev.dev)
goto CLASS_DESTORY;
return 0;
CLASS_DESTORY:
class_destroy(chrdev.class);
UNREG_CHRDEV:
unregister_chrdev(major, CHARDEV_CDEV_NAME);
return -1;
}
static void __exit chrdev_test_exit(void)
{
/*
* 释放设备。
*/
device_destroy(chrdev.class, MAJOR(chrdev.devid));
/*
* 释放class。
*/
class_destroy(chrdev.class);
/*
* 释放字符设备。
*/
unregister_chrdev(MAJOR(chrdev.devid), CHARDEV_CDEV_NAME);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is chrdev test");
module_init(chrdev_test_init);
module_exit(chrdev_test_exit);



/home/zky/ProjectsHub/Linux_Projects/pingdichan/riscv64-wangzai-linux-gnu-gcc/bin/riscv64-unknown-linux-gnu-gcc app.c -o app
需要单独编译app,再在板子里执行
十七、杂设备
/home/zky/ProjectsHub/Linux_Projects/pingdichan/linux-5.4/include/linux/miscdevice.h
杂设备是简化封装后的字符设备
app.c
cpp
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <stdint.h>
#include <sys/mman.h>
int main(int argc, char *argv[])
{
char *str = "hello world\n";
char buf[4096];
int devfd;
devfd = open("/dev/my_miscdevice", O_RDWR);
if (devfd < 0) {
printf("open /dev/my_miscdevice fail\n");
return -1;
}
write(devfd, str, strlen(str));
read(devfd, buf, strlen(str));
close(devfd);
printf("buf = %s\n", buf);
return 0;
}
miscdevice.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* miscdevice test
*
* Copyright (C) 2025 Wangzai 《搞linux的旺仔》
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#define MISCDEVICE_DEV_NAME "my_miscdevice"
#define MALLOC_LEN (4096)
/*
* 杂设备open()函数
*/
static int miscdevice_open(struct inode *inode, struct file *filp)
{
char *buf = NULL;
/*
* 申请一段内存并保存到文件的私有数据中。
*/
buf = kmalloc(MALLOC_LEN, GFP_KERNEL);
if (!buf)
return -ENOMEM;
filp->private_data = buf;
return 0;
}
/*
* 杂设备release()函数,即关闭函数
*/
static int miscdevice_release(struct inode *inode, struct file *filp)
{
char *buf = (char *)filp->private_data;
kfree(buf);
return 0;
}
/*
* 杂设备读函数
*/
static ssize_t miscdevice_read(struct file *filp, char __user *ptr, size_t len,
loff_t *off)
{
char *buf = (char *)filp->private_data;
/*
* 通过copy_to_user()函数将内核空间的数据(buf)拷贝到应用程序空间(ptr)。
*/
if (copy_to_user(ptr, buf, len))
return -EFAULT;
return len;
}
/*
* 杂设备写函数
*/
static ssize_t miscdevice_write(struct file *filp, const char __user *ptr,
size_t len, loff_t *off)
{
char *buf = (char *)filp->private_data;
/*
* 通过copy_from_user()函数将应用程序空间的数据(ptr拷贝到内核空间(buf)。
*/
if (copy_from_user(buf, ptr, len))
return -EFAULT;
return len;
}
/*
* 杂设备iotctl()函数,通过cmd传递命令,通过arg传递数据
*/
static long miscdevice_ioctl(struct file *filp,
unsigned int cmd,
unsigned long arg)
{
return 0;
}
/*
* 文件操作结构体
*/
static const struct file_operations miscdevice_fops = {
.owner = THIS_MODULE,
.open = miscdevice_open,
.release = miscdevice_release,
.read = miscdevice_read,
.write = miscdevice_write,
.unlocked_ioctl = miscdevice_ioctl,
};
/*
* 杂设备结构体
*/
static struct miscdevice my_miscdevice = {
.name = MISCDEVICE_DEV_NAME,
.minor = MISC_DYNAMIC_MINOR,
.fops = &miscdevice_fops,
};
static int __init miscdevice_test_init(void)
{
/*
* 注册杂设备
*/
return misc_register(&my_miscdevice);
}
static void __exit miscdevice_test_exit(void)
{
/*
* 注销杂设备
*/
misc_deregister(&my_miscdevice);
}
MODULE_AUTHOR("Wangzai <1587636487@qq.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is miscdevice test");
module_init(miscdevice_test_init);
module_exit(miscdevice_test_exit);

十八、固件加载
load_firmware.c
cpp
// SPDX-License-Identifier: GPL-2.0
/*
* load firmware test
*
* Copyright (C) 2025 zky
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
/*
* 在根文件系统的/lib/firmware/下要放一个名字为LOAD_IMAGE_NAME所定义的文件
*/
#define LOAD_IMAGE_NAME "test.firmware"
static struct miscdevice load_firmware_misc_dev = {
.name = "load_firmware_test",
.minor = MISC_DYNAMIC_MINOR,
};
static int __init load_firmware_test_init(void)
{
const struct firmware *fw;
int ret;
/*
* 注册一个杂设备
*/
ret = misc_register(&load_firmware_misc_dev);
if (ret)
return ret;
/*
* 利用request_firmware()加载文件系统下的固件
*/
ret = request_firmware(&fw, LOAD_IMAGE_NAME, load_firmware_misc_dev.this_device);
if (ret) {
misc_deregister(&load_firmware_misc_dev);
return ret;
}
pr_info("loading firmware = %s\n", fw->data);
/*
* 释放固件
*/
release_firmware(fw);
return 0;
}
static void __exit load_firmware_test_exit(void)
{
/*
* 注销杂设备
*/
misc_deregister(&load_firmware_misc_dev);
}
MODULE_AUTHOR("zky");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is load firmware test");
module_init(load_firmware_test_init);
module_exit(load_firmware_test_exit);

十九、platform驱动/设备
待更新