当前位置: 首页 > news >正文

Linux驱动第一期1-10-驱动基础总结

1.环境的安装

FTP-FileZilla软件                    

windows与ubuntu通信

窗口终端或者SSH MobaXterm    

USB查看rk3568

adb  

 windows与rk3568通信

vscode  

在linux下进行c文件写

交叉编译器  

自己装了一个  /opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc -v

RKdev工具

烧写rk3568的镜像程序

2.GIT的使用

忘记了,哈哈哈,下次再学

重点:

编译驱动方法:

1.驱动编译成内核 :    在kernel/devices/下面各个设备驱动下编译

2.驱动编译成内核模块ko

        绝对路径使用交叉编译器
/opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc -v

Linux驱动的运行方式:

1.运行驱动编译进内核

        Linux系统启动后会自动运行程序

2.运行驱动编译成模块

        在Linux系统启动以后使用“insmod”命令加载驱动模块

3.实验1:驱动程序helloworld

        驱动编译成内核模块     运行驱动编译成模块

#include <linux/module.h>
#include <linux/init.h>static int helloworld_init(void)
{printk("hello world\n");return 0;
}static void helloworld_exit(void)
{printk("goodbye world\n");
}module_init(helloworld_init);
module_exit(helloworld_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("qiuiuiu");
MODULE_VERSION("V1.0");
MODULE_DESCRIPTION("A simple hello world module")
ARCH := arm64
CROSS_COMPILE := aarch64-rockchip1031-linux-gnu-    #这个环境变量要在外面声明
export ARCH CROSS_COMPILE                            #声明路径
KDIR := /home/alientek/rk3568_linux5.10_sdk/kernel    #内核的路径
PWD := $(shell pwd)
obj-m += helloworld.o                        #名字不要打错
all:make -C $(KDIR) M=$(PWD) modules    #注意前面是个制表符TABclean:make -C $(KDIR) M=$(PWD) clean    #注意前面是个制表符TAB

驱动编译成内核模块

有了Makefile以后,输入“make”命令就可以编译helloworld驱动模块,helloworld.ko

运行驱动编译成模块

insmod helloworld.ko”命令可以加载驱动

3.实验2:驱动模块传参

        驱动编译成内核模块     运行驱动编译成模块

#include <linux/init.h>
#include <linux/module.h>
static int number;//定义int类型变量number
static char *name;//定义char类型变量name
static int para[8];//定义int类型的数组
static char str1[10];//定义char类型字符串str1
static int n_para;//定义int类型的用来记录module_param_array函数传递数组元素个数的变量n_para
module_param(number, int, S_IRUGO);//传递int类型的参数number,S_IRUGO表示权限为可读
module_param(name, charp, S_IRUGO);//传递char类型变量name
module_param_array(para , int , &n_para , S_IRUGO);//传递int类型的数组变量para
module_param_string(str, str1 ,sizeof(str1), S_IRUGO);//传递字符串类型的变量str1
static int __init parameter_init(void)//驱动入口函数
{static int i;printk(KERN_EMERG "%d\n",number);printk(KERN_EMERG "%s\n",name);                                                                                                                                                          for(i = 0; i < n_para; i++){printk(KERN_EMERG "para[%d] : %d \n", i, para[i]);}printk(KERN_EMERG "%s\n",str1);return 0;
}
static void __exit parameter_exit(void)//驱动出口函数
{printk(KERN_EMERG "parameter_exit\n");
}
module_init(parameter_init);//注册入口函数
module_exit(parameter_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意GPL开源协议
MODULE_AUTHOR("topeet"); //作者信息
ARCH := arm64
CROSS_COMPILE := aarch64-rockchip1031-linux-gnu-
export ARCH CROSS_COMPILE
KDIR := /home/alientek/rk3568_linux5.10_sdk/kernel
PWD ?= $(shell pwd)
obj-m += a.o
obj-m += b.o
all:make -C $(KDIR) M=$(PWD) modules
clean:make -C $(KDIR) M=$(PWD) clean

驱动编译成内核模块

有了Makefile以后,输入“make”命令就可以编译helloworld驱动模块,helloworld.ko

运行驱动编译成模块

insmod parameter.ko number=100 name="topeet" para=0,1,2,3,4,5,6,7 str="itop"
命令可以加载驱动

rmmod parameter.ko
驱动的卸载

4.实验3:内核模块符号

         驱动编译成内核模块     运行驱动编译成模块

编写Linux下的内核模块符号导出实例代码,总共有两个驱动程序,

第一个驱动文件名为mathmodule.c,用来定义参数num和函数add(a,b),

第二个驱动文件名为hello.c,会引用mathmodule.c驱动程序中的参数num和数学函数add(a,b),并将相应的参数值和函数返回值打印到串口终端上

mathmodule.c代码

#include <linux/init.h>
#include <linux/module.h>
int num = 10;//定义参数num
EXPORT_SYMBOL(num);//导出参数numint add(int a, int b)//定义数学函数add(),用来实现加法
{return a + b;
}
EXPORT_SYMBOL(add);//导出数学函数add()static int __init math_init(void)//驱动入口函数
{printk("math_moudle init\n");return 0;
}static void __exit math_exit(void)//驱动出口函数
{printk("math_module exit\n");
}module_init(math_init);//注册入口函数
module_exit(math_exit);//注册出口函数MODULE_LICENSE("GPL");//同意GPL开源协议
MODULE_AUTHOR("topeet");//作者信息

hello.c代码

#include <linux/init.h>
#include <linux/module.h>
extern int num;//导入int类型变量num
extern int add(int a, int b);//导入函数add
static int __init hello_init(void)//驱动入口函数
{static int sum;printk("num = %d\n", num);//打印num值sum = add(3, 4);//使用add函数进行3+4的运算                                                                                                                                                                          printk("sum = %d\n", sum);//打印add函数的运算值return 0;
}static void __exit hello_exit(void)//驱动出口函数
{printk("Goodbye hello module\n");
}module_init(hello_init);//注册入口函数
module_exit(hello_exit);//注册出口函数MODULE_LICENSE("GPL");//同意GPL开源协议
MODULE_AUTHOR("topeet");//作者信息

mathmodule.c和hello.c的同一目录下创建Makefile文件

export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m := mathmodule.o
obj-m += hello.oKDIR :=/home/topeet/Linux/linux_sdk/kernel    #这里是你的内核目录                                                                                                                            
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules    #make操作
clean:make -C $(KDIR) M=$(PWD) clean    #make clean操作

驱动编译成内核模块

编译完后会生成hello.ko和mathmodule.ko目标文件

运行驱动编译成模块

由于 hello.ko依赖于mathmodule.ko,所以mathmodule.ko需要先加载,分别使用以下命令进行模块的加载(加载顺序不能变)

insmod mathmodule.koinsmod  hello.ko

由于hello.ko文件使用了mathmodule.ko导出的符号,所以要先卸载hello.ko,卸载完成之后再卸载mathmodule.ko。

5.实验4:menuconfig实验

        驱动编译成内核    运行驱动编译进内核

make ARCH=arm64 menuconfig

三个文件  .c  Makefile  Kconfig

输入“cd drivers/char”进入到 drivers/char 目录下,然后输入“mkdir hello”建立hello文件夹,并输入“cd hello”进入hello文件夹

hello.c

#include <linux/module.h>
#include <linux/kernel.h>static int __init helloworld_init(void)    //驱动入口函数
{printk(KERN_EMERG "helloworld_init\r\n");//注意:内核打印用printk而不是printfreturn 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");    //同意GPL开源协议
MODULE_AUTHOR("topeet");    //作者信息

输入“touch Kconfig”命令创建Kconfig文件

Kconfig

config HELLO
tristate "hello world"
helphello hello

touch Makefile”命令创建Makefile文件

Makefile

obj-$(CONFIG_HELLO)+=helloworld.o

修改上一级目录的Kconfig文件和Makefile文件,也就是driver/char目录

Kconfig

obj-y += hello/

Makefile

source “drivers/char/hello/Kconfig”

打开menuconfig图形化配置工具,  用 * 来表示.驱动编译进Linux内核,保存到.config文件

编译源码    

驱动编译成内核   会生成对应的.o文件

make savedefconfig
cp defconfig arch/arm64/configs/rockchip_linux_defconfig 
cd ../
./build.sh kernel

运行驱动编译进内核

将编译好的内核镜像烧写到开发板上后,在开发板系统启动的时候也可以成功看到加载helloworld驱动

6.编译进内核的驱动系统是如何运行的?

          驱动编译成内核    运行驱动编译进内核

    驱动编译成内核         KBUILD_CFLAGS_KERNEL

module_init的具体内容由MODULE宏定义来决定,MODULE宏定义在内核源码的顶层Makefile中,

KBUILD_CFLAGS_KERNEL和KBUILD_CFLAGS_MODULE两个宏

#ifndef MODULE
#define module_init(x)  __initcall(x);
#define module_exit(x)  __exitcall(x);#else /* MODULE */..........
#define module_init(initfn)                 \static inline initcall_t __maybe_unused __inittest(void)        \{ return initfn; }                  \int init_module(void) __copy(initfn) __attribute__((alias(#initfn)));
..........#endif

KBUILD_CFLAGS_KERNEL没有定义就选择了上面那个

#define module_init(x)  __initcall(x);
#define module_exit(x)  __exitcall(x);

宏定义展开到最后的initcall_t是一个函数指针,它的原型如下所示:

typedef int (*initcall_t)(void);

这个函数指针会被放置在.initcall6.init段,

“include/linux/module.h”文件

“include/linux/init.h”文件
include/linux/init.h 文件

include/asm-generic/vmlinux.lds.h 链接脚本(linker script)

do_initcalls函数,该函数定义在内核源码的init/main.c目录  启动执行

内核源码中除了module_init,还有其他的宏定义接口用来完成初始化模块并注册模块的功能,他们的原型都是define_initcall,只是相应的优先级不同

#define pure_initcall(fn)		__define_initcall(fn, 0)#define core_initcall(fn)		__define_initcall(fn, 1)
#define core_initcall_sync(fn)		__define_initcall(fn, 1s)
#define postcore_initcall(fn)		__define_initcall(fn, 2)
#define postcore_initcall_sync(fn)	__define_initcall(fn, 2s)
#define arch_initcall(fn)		__define_initcall(fn, 3)
#define arch_initcall_sync(fn)		__define_initcall(fn, 3s)
#define subsys_initcall(fn)		__define_initcall(fn, 4)
#define subsys_initcall_sync(fn)	__define_initcall(fn, 4s)
#define fs_initcall(fn)			__define_initcall(fn, 5)
#define fs_initcall_sync(fn)		__define_initcall(fn, 5s)
#define rootfs_initcall(fn)		__define_initcall(fn, rootfs)
#define device_initcall(fn)		__define_initcall(fn, 6)
#define device_initcall_sync(fn)	__define_initcall(fn, 6s)
#define late_initcall(fn)		__define_initcall(fn, 7)
#define late_initcall_sync(fn)		__define_initcall(fn, 7s)

include/asm-generic/vmlinux.lds.h 链接脚本(linker script)  定义初始化调用函数的布局和顺序

#define INIT_CALLS_LEVEL(level)						\__initcall##level##_start = .;				\KEEP(*(.initcall##level##.init))			\__initcall##level##s_start = .;				\KEEP(*(.initcall##level##s.init))			\
#define INIT_CALLS							\__initcall_start = .;					\KEEP(*(.initcallearly.init))				\INIT_CALLS_LEVEL(0)					\INIT_CALLS_LEVEL(1)					\INIT_CALLS_LEVEL(2)					\INIT_CALLS_LEVEL(3)					\INIT_CALLS_LEVEL(4)					\INIT_CALLS_LEVEL(5)					\INIT_CALLS_LEVEL(rootfs)				\INIT_CALLS_LEVEL(6)					\INIT_CALLS_LEVEL(7)					\__initcall_end = .;

总结

1.在使用module_init(hello_world)时,hello_world()函数指针会被放置在.initcall6.init段处。

2.内核启动时,会执行do_initcall()函数,该函数根据指针数组initcall_levels[6]找到_initcall6_start,

3.在include/asm-generic/vmlinux.lds.h文件中可以查到_initcall6_start对应.initcall6.init段的起始地址。

4.然后,依次取出该段中的函数指针,并执行这些函数

7.实验5: 让驱动快一点被加载实验

          驱动编译成内核    运行驱动编译进内核

四个文件  .c  .c  Makefile  Kconfig

helloworld_a.c代码

#include <linux/module.h>
#include <linux/kernel.h>static int __init helloworld_init(void) //驱动入口函数
{printk(KERN_EMERG "This is helloworld A\r\n");//注意:内核打印用 printk 而不是 printfreturn 0;
}static void __exit helloworld_exit(void) //驱动出口函数
{printk(KERN_EMERG "helloworld_exit\r\n");
}arch_initcall(helloworld_init); //注册入口函数
module_exit(helloworld_exit); //注册出口函数
MODULE_LICENSE("GPL v2"); //同意 GPL 开源协议
MODULE_AUTHOR("topeet"); //作者信息

helloworld_b.c代码

#include <linux/module.h>
#include <linux/kernel.h>static int __init helloworld_init(void) //驱动入口函数
{printk(KERN_EMERG "This is helloworld B\r\n");//注意:内核打印用 printk 而不是 printfreturn 0;
}static void __exit helloworld_exit(void) //驱动出口函数
{printk(KERN_EMERG "helloworld_exit\r\n");
}device_initcall(helloworld_init); //注册入口函数
module_exit(helloworld_exit); //注册出口函数
MODULE_LICENSE("GPL v2"); //同意 GPL 开源协议
MODULE_AUTHOR("topeet"); //作者信息

arch_initcall的优先级为3,device_initcall的优先级为6,所以helloworld_a.c驱动会比helloworld_b.c驱动先加载

输入“touch Kconfig”命令创建Kconfig文件

Kconfig

config HELLO
tristate "hello world"
helphello hello

touch Makefile”命令创建Makefile文件   objs: 这是一个固定的后缀,意思是“...的目标文件列表”

Makefile  

obj-$(CONFIG_HELLO)+=helloworld.o

修改上一级目录的Kconfig文件和Makefile文件,也就是driver/char目录

Kconfig

obj-y += hello/

Makefile

source “drivers/char/hello/Kconfig”

打开menuconfig图形化配置工具,  用 * 来表示.驱动编译进Linux内核,保存到.config文件

编译源码:    

驱动编译成内核   会生成对应的.o文件

make savedefconfig
cp defconfig arch/arm64/configs/rockchip_linux_defconfig 
cd ../
./build.sh kernel

运行驱动编译进内核

将编译好的内核镜像烧写到开发板上后,

dmesg | grep helloworld     

可以看到驱动A的加载时间为内核启动后的0.52秒左右,驱动B的加载时间为内核启动后的1.78秒左右

8.实验6:驱动代码使用Makefile的宏

                驱动编译成内核模块ko        运行驱动编译成模块

    驱动编译成内核         KBUILD_CFLAGS_MODULE
例如:  KBUILD_CFLAGS_MODULE += -DDEBUG表示将-DDEBUG编译选项添加到模块的编译命令中,定义了一个名为DEBUG的宏

KBUILD_CFLAGS_MODULE += -DDEBUG=3    和宏#define DDEBUG 3   一样的意思

l两个文件: .c     Makefile

define.c代码

#include <linux/module.h>
#include <linux/kernel.h>static int __init helloworld_init(void) //驱动入口函数
{
#ifndef DEBUGprintk("helloworld a!\n");
#elseprintk("helloworld b!\n");
#endifreturn 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"); //同意 GPL 开源协议
MODULE_AUTHOR("topeet"); //作者信息

Makefile代码

KBUILD_CFLAGS_MODULE += -DDEBUG
export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m +=define.o    #此处要和你的驱动源文件同名
KDIR :=/home/topeet/Linux/linux_sdk/kernel    #这里是你的内核目录                                                                                                                            
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules    #make操作
clean:make -C $(KDIR) M=$(PWD) clean    #make clean操作

驱动编译成内核模块

有了Makefile以后,输入“make”命令,形成define.ko目标文件

运行驱动编译成模块

开发板启动之后,使用以下命令进行驱动模块的加载

insmod define.ko命令可以加载驱动


最后,使用rmmod define.ko命令对该内核进行卸载

9.实验7:finit_module  内核是如何运行ko文件的_insmod

         驱动编译成内核模块ko        运行驱动编译成模块

insmod的源码为       busybox-1.34.1/modutils/insmod.c

bb_init_module函数,正是通过该函数加载的内核模块。      该函数定义在busybox1.34.1/modutils/modutils.c文件中

helloworld.c代码   添加了dump_stack()函数,用来在内核中输出当前调用堆栈信息,在这里使用该函数的目的是验证在app中的finit_module系统调用是否生效

#include <linux/module.h>
#include <linux/kernel.h>static int __init helloworld_init(void) //驱动入口函数
{dump_stack();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"); //同意 GPL 开源协议
MODULE_AUTHOR("topeet"); //作者信息

Makefile 代码

export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += helloworld.o    #此处要和你的驱动源文件同名
KDIR :=/home/topeet/Linux/linux_sdk/kernel    #这里是你的内核目录                                                                                                                            
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules    #make操作
clean:make -C $(KDIR) M=$(PWD) clean    #make clean操作

编写测试 APP    新目录    程序app.c代码   用finit_module系统调用加载模块到内核insmod

#include <stdio.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <unistd.h>#define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags)int main(int argc, char **argv)
{int fd;     // 文件描述符int ret;    // 返回值fd = open(argv[1], O_RDONLY | O_CLOEXEC);    // 打开文件,以只读方式打开并设置O_CLOEXEC标志if (fd < 0) {    // 打开文件失败printf("open error\n");return -1;}ret = finit_module(fd, "", 0);    // 调用finit_module系统调用加载模块return ret;    // 返回加载结果
}

驱动编译成内核模块

有了Makefile以后,输入“make”命令,形成helloworld.ko目标文件


编译应用程序
aarch64-linux-gnu-gcc app.c -o app
 

运行驱动编译成模块

helloworld.ko驱动文件和app可执行文件,所以需要先将两个要用到的文件拷贝到开发板上

开发板启动之后,使用以下命令加载helloworld.ko驱动

./app helloworld.ko


dump_stack函数会打印调用的一系列函数,这些函数的具体调用已经在进阶第一章讲解过了,这里的重点为红色框中的倒数第二行,调用了finit_module函数加载了helloworld驱动

lsmod  查看驱动的加载情况


最后,使用rmmod helloworld.ko命令对该内核进行卸载

10.实验8:init_module  实现insmod命令

驱动编译成内核模块ko        运行驱动编译成模块

insmod的源码为       busybox-1.34.1/modutils/insmod.c

bb_init_module函数,正是通过该函数加载的内核模块。      该函数定义在busybox1.34.1/modutils/modutils.c文件中

helloworld.c代码

#include <linux/module.h>
#include <linux/kernel.h>static int __init helloworld_init(void) //驱动入口函数
{dump_stack();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"); //同意 GPL 开源协议
MODULE_AUTHOR("topeet"); //作者信息

Makefile 文件内容
 

export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += helloworld.o    #此处要和你的驱动源文件同名
KDIR :=/home/topeet/Linux/linux_sdk/kernel    #这里是你的内核目录                                                                                                                            
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules    #make操作
clean:make -C $(KDIR) M=$(PWD) clean    #make clean操作

应用程序app.c代码  新目录    使用init_module系统调用加载模块到内核    insmod

#include <stdio.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)int main(int argc, char **argv)
{int fd;                  // 文件描述符int ret;                 // 返回值struct stat statbuf;     // 存储文件信息的结构体size_t image_size;       // 文件大小char *image;             // 文件内容缓冲区fd = open(argv[1], O_RDONLY | __O_CLOEXEC);  // 以只读方式打开文件if (fd < 0){printf("open error \n");  // 打开文件失败return -1;}fstat(fd, &statbuf);       // 获取文件信息image_size = statbuf.st_size;  // 获取文件大小image = malloc(image_size);  // 为文件内容分配内存空间read(fd, image, image_size);  // 读取文件内容到缓冲区ret = init_module(image, image_size, "");  // 调用系统调用初始化内核模块if (ret < 0){printf("error \n");  // 内核模块初始化失败}else{printf("ok \n");  // 内核模块初始化成功}free(image);  // 释放内存空间return ret;  // 返回结果
}

驱动编译成内核模块

有了Makefile以后,输入“make”命令,形成helloworld.ko目标文件


编译应用程序  译完成以后会生成一个app的可执行程序a.out
aarch64-linux-gnu-gcc myinsmod.c

运行驱动编译成模块

helloworld.ko驱动文件和a.out可执行文件,需要先将两个要用到的文件拷贝到开发板上

开发板启动之后,使用以下命令加载helloworld.ko驱动

./a.out helloworld.ko


dump_stack函数会打印调用的一系列函数,这些函数的具体调用已经在进阶第一章讲解过了,这里的重点为红色框中的倒数第二行,调用了finit_module函数加载了helloworld驱动

lsmod  查看驱动的加载情况


最后,使用rmmod helloworld.ko命令对该内核进行卸载

11.实验9:向系统中添加一个系统调用

        驱动编译成内核    运行驱动编译进内核

系统调用(system call)是操作系统提供给应用程序的编程接口  syscall函数是一个系统调用的包装函数

SYSCALL_DEFINE3(init_module, void __user *, umodunsigned long, len, const char __user *, uargs)

等价于__se_sys_init_module函数即可

__NR_init_module              include/uapi/asm-generic/unistd.h文件


文件   在实验4基础上  .c  Makefile  Kconfig  应用程序app的insmod

helloworld.c

#include <linux/kernel.h>
#include <linux/syscalls.h>
SYSCALL_DEFINE0(helloworld){printk("This is helloworld syscall\n");return 0;
}

添加系统调用号    Linux源码kernel/include/uapi/asm-generic/unistd.h文件中添加系统调用号

注意改名字

Kconfig

config HELLO
tristate "hello world"
helphello hello

Makefile

obj-$(CONFIG_HELLO)+=helloworld.o

修改上一级目录的Kconfig文件和Makefile文件,也就是driver/char目录

Kconfig

obj-y += hello/

Makefile

source “drivers/char/hello/Kconfig”

打开menuconfig图形化配置工具,  用 * 来表示.驱动编译进Linux内核,保存到.config文件

编译源码    

驱动编译成内核   会生成对应的.o文件

make savedefconfig
cp defconfig arch/arm64/configs/rockchip_linux_defconfig 
cd ../
./build.sh kernel

编译好的内核镜像boot.img,烧写内核镜像,启动开发板

应用程序syscall.c代码   新目录    

SYSCALL_DEFINE3(init_module, void __user *, umod,unsigned long, len, const char __user *, uargs)
{int err;struct load_info info = { };err = may_init_module();if (err)return err;pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",umod, len, uargs);err = copy_module_from_user(umod, len, &info);if (err)return err;return load_module(&info, uargs, 0);
}

进行应用程序编译,因为测试APP是要在开发板上运行的,所以需要aarch64-linux-gnu-gcc来编译,输入以下命令,编译完成以后会生成一个app的可执行程序

aarch64-linux-gnu-gcc syscall.c -o syscall

编译好的syscall程序拷贝到开发板上,然后运行程序

./syscall

在运行应用程序之后,打印“This is helloworld syscall” 成功调用自定义的系统调用,至此,向系统中添加一个系统调用实验结束

12.内核运行ko文件总结

要加载内核模块,通常需要使用insmod命令或类似工具触发系统调用,然后在内核中执行相应的系统调用函数(如sys_finit_module或sys_init_module)来加载模块。在加载过程中,会调用load_module函数,进而执行模块的初始化函数。这些过程构成了加载内核模块的流程

http://www.dtcms.com/a/502974.html

相关文章:

  • 我的WordPress网站梅林固件做网站
  • 分库分表:基础介绍
  • 使用css `focus-visible` 改善用户体验
  • AI人工智能-深度学习的基本原理-第二周(小白)
  • 【2070】数字对调
  • 【AI智能体】Coze 提取对标账号短视频生成视频文案实战详解
  • IOT项目——ESP系列
  • 【成长纪实】Dart 与 ArkTS 函数与类的对比学习:从 Flutter 到 HarmonyOS
  • 基于 JETSON+FPGA+GMSL+AI 车载视频采集与存储系统设计(二)系统测试
  • Flutter Event Loop
  • LeetCode 1287.有序数组中出现次数超过25%的元素
  • 递归-50.Pow(x,n)-力扣(LeetCode)
  • Flutter 并发编程全解:从零掌握 Isolate
  • 跨网络互联技术(①Singbox Core-Flutter-Android)
  • 移动端网站的重要性做一款推荐类的网站
  • 用div做网站中间部分做网站是干啥的
  • 【思维导图SimpleMind Pro】SimpleMind Pro——轻量级思维导图软件完全指南:轻量专业,让思维高效可视化
  • jlink烧入软件的使用
  • 03-流程控制语句-教程
  • 【mqtt参数上云和小程序开发】【第5期】hcsr04超声波测距模块调试完成
  • 【STM32笔记】:P03 ISP 一键下载电路详解
  • 江苏省网站建设哪家好php做的购物网站
  • 常见的位运算的总结
  • CSRF(跨站请求伪造)攻击详解:原理、途径与防范
  • 【C++】使用双指针算法习题
  • 全能型 AI 模型新标杆 Qwen2.5-Omni
  • 建设银行佛山分行网站wordpress插件 评分
  • 大数据计算引擎-从源码看Spark AQE对于倾斜的处理
  • 前端基础知识---Ajax
  • 数据结构——多维数组的存储