Linux驱动模块之helloworld驱动

例程代码路径:ELF 1开发板资料包\03-例程源码\03-2 驱动例程源码\01_helloworld\hello

本章节先写一个helloworld驱动,不涉及对硬件的操作,它的目的是展示驱动程序的基本结构和加载过程。

源码编写

(一)首先包含头文件

#include <linux/module.h>   // 包含模块相关函数的头文件

#include <linux/kernel.h>   // 包含内核相关函数的头文件

#include <linux/init.h>     // 包含初始化和清理函数的头文件

(二)驱动模块入口和出口函数

module_init(helloworld_init);  // 指定驱动程序的初始化函数

module_exit(helloworld_exit);  // 指定驱动程序的清理函数

module_init()的定义:

#define module_init(init_fn)          \

    static inline initcall_t __initcall_##init_fn##_wrapper(void) \

    {                                           \

        return init_fn;                         \

    }                                           \

    int init_module(void) __attribute__((alias(#init_fn)));

module_init()宏接受一个初始化函数作为参数,并将其定义为内联函数__initcall_##init_fn##_wrapper()。然后,它使用__attribute__((alias(#init_fn)))将init_module()函数与初始化函数进行关联。

在Linux内核加载模块时,会调用init_module()函数,它实际上是一个入口点函数。通过将module_init()定义为init_module()的别名,使得初始化函数成为模块加载时的入口点函数。

使用module_init()宏的目的是在驱动程序中指定初始化函数,以便在模块加载时调用该函数执行必要的初始化操作。

当使用insmod将模块加载进内核的时候,初始化函数的代码将会被执行。模块初始化代码只与内核模块管理子系统打交道,并不与应用程序直接交互。

module_exit()的定义:

#define module_exit(exit_fn)           \

    static inline exitcall_t __exitcall_##exit_fn##_wrapper(void) \

    {                                           \

        return exit_fn;                         \

    }                                           \

    void cleanup_module(void) __attribute__((alias(#exit_fn)));

module_exit()宏接受一个退出函数作为参数,并将其定义为内联函数__exitcall_##exit_fn##_wrapper()。然后,它使用__attribute__((alias(#exit_fn)))将cleanup_module()函数与退出函数进行关联。

在Linux内核卸载模块时,会调用cleanup_module()函数,它实际上是一个出口点函数。通过将module_exit()定义为cleanup_module()的别名,使得退出函数成为模块卸载时的出口点函数。

使用module_exit()宏的目的是在驱动程序中指定退出函数,以便在模块卸载时调用该函数执行必要的清理操作。

几点说明:

(1)模块退出没有返回值;

(2)__exit标记这段代码仅用于模块卸载;

(3)module_exit不是必须的。但是,没有module_exit定义的模块无法被卸载,如果需要支持模块卸载则必须有module_exit;

当使用当使用rmmod卸载模块时,退出函数的代码将被执行。模块退出代码只与内核模块管理子系统打交道,并不直接与应用程序交互。

(三)入口函数和出口函数实现

static int __init helloworld_init(void)

{

    printk(KERN_INFO "Hello, World!\n");  // 打印消息到内核日志

    return 0;

}



static void __exit helloworld_exit(void)

{

    printk(KERN_INFO "Goodbye, World!\n");  // 打印消息到内核日志

}

内核加载时输出Hello, World!,内核卸载时输出Goodbye, World!

(四)声明模块信息

MODULE_LICENSE("GPL");  // 指定模块的许可证信息

MODULE_AUTHOR("Your Name");  // 指定模块的作者信息

MODULE_DESCRIPTION("A simple Hello World driver");  // 指定模块的描述信息

hello.c示例源码

#include <linux/module.h>   // 包含模块相关函数的头文件

#include <linux/kernel.h>   // 包含内核相关函数的头文件

#include <linux/init.h>     // 包含初始化和清理函数的头文件



static int __init helloworld_init(void)

{

    printk(KERN_INFO "Hello, World!\n"); // 打印消息到内核日志

    return 0;

}



static void __exit helloworld_exit(void)

{

    printk(KERN_INFO "Goodbye, World!\n"); // 打印消息到内核日志

}



module_init(helloworld_init); // 指定驱动程序的初始化函数

module_exit(helloworld_exit); // 指定驱动程序的清理函数



MODULE_LICENSE("GPL"); // 指定模块的许可证信息

MODULE_AUTHOR("Your Name"); // 指定模块的作者信息

MODULE_DESCRIPTION("A simple Hello World driver"); // 指定模块的描述信息

编译

编译驱动分为两种形式,一种是编译现成模块,生成.ko文件然后通过手动加载;另一种是编译到内核,在内核启动时自动加载。一般在驱动调试阶段会编译成模块,这样便于调试,不用每次都重新编译整个内核,本章节以编译成模块进行讲解。

在hello.c同级目录下编写Makefile:

LINUX_KERNEL_PATH = /home/elf/work/linux-imx-imx_4.1.15_2.0.0_ga

CURRENT_PATH := $(shell pwd)

CROSS_COMPILE = arm-poky-linux-gnueabi-

CC = $(CROSS_COMPILE)gcc -march=armv7ve -mfpu=neon -mcpu=cortex-a7 --sysroot=/opt/fsl-imx-x11/4.1.15-2.0.0/sysroots/cortexa7hf-neon-poky-linux-gnueabi



obj-m :=hello.o



all:

        $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules

clean:

        $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules clean

.PHONY: modules clean

-C:参数后面加.config文件所在的文件夹;

M:参数后面是要编译的模块;

LINUX_KERNEL_PATH:为要依赖内核的目录;

CURRENT_PATH:目前模块的目录;

CROSS_COMPILE:编译器的前缀;

CC:编译器以及参数;

注意:LINUX_KERNEL_PATH的路径要以实际源码存放的路径为准

注意:复制粘贴过程中可能会出现格式问题。

添加后效果如下:

设置环境变量,编译:

 . /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/01_helloworld/hello$ make

将生成的hello.ko文件拷贝到开发板中测试。在开发板中执行insmod加载驱动,rmmod卸载驱动:

root@ELF1:~# insmod hello.ko

Hello, World!

root@ELF1:~# rmmod hello.ko

Goodbye, World!

可以看出加载时进入到了helloworld_init函数,并打印了Hello, World!,卸载函数时进入了helloworld_exit函数并打印了Goodbye, World!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值