范例中以一个经典的hello驱动程序为例
$vim hello.c创建源码文件
程序有几个重要的部分
a. 引用两个基本的头文件linux/init.h、linux/module.h;
b. MODULE_LICENSE("Dual BSD/GPL")
此申明是告诉内核,该模块采用自由许可证,没此申明则内核在装载该模块时会产生抱怨,就好像内核在说,你(此模块)遵循GNU GPL规定,有问题的话后果自负;
c. module_init(func_init),insmod加载内核时,会调用func_init初始化函数
module_exit(func_exit),rmmod卸载内核时,会调用func_exit退出函数
程序代码如下
///////////////////////////////////////////////START/////////////////////////////////////////////
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
//定义模块的版本
#define MODULEVERSION "1:1.0"
MODULE_LICENSE("Dual BSD/GPL");//申明此模块遵循GPL规定
MODULE_AUTHOR("jayson_jang");//申明此模块的作者
MODULE_DESCRIPTION("hello test");//申明对此模块的描述
MODULE_VERSION(MODULEVERSION);//申明此模块的版本
MODULE_ALIAS("hello_test.ko");//申明此模块的别名
//MODULE_DEVICE_TABLE("ALL", "bycle");
/*
模块支持一下数据类型
bool invbool charp int long short uint ulong ushort
*/
//变量申明
static char *whom = "world";
static int howmany = 1;
static bool boolflag=1;
static char name[2][16]={"name1", "name2"};
static int num = 2;
static int major=0, minor=0;
//static invbool invboolflag=1;
//将申明的变量传入到此模块,S_IRUGO代表所有人可读此变量
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);//charp代表是char指针类型
module_param(boolflag, bool, S_IRUGO);
module_param_array(name, charp, &num, S_IRUGO);
module_param(major, int, S_IRUGO);
module_param(minor, int, S_IRUGO);
void func1(void)
{
printk("func1\r\n");
}
EXPORT_SYMBOL_GPL(func1);//导出func1,遵循GPL的其他模块可调用此函数
//模块初始化函数,__init代表此模块初始化完后,此函数将被简单的丢弃
static int __init hello_init(void)
{
int err=0, i=0;
dev_t devid=0;
//printk打印字符,KERN_ALERT代表将打印输出到控制台和日志文件中
printk(KERN_ALERT "Hello,world\n");
//current->comm代表此进程名,current->pid代表此进行id号
//current定义在linux/sched.h头文件中,可直接使用
printk(KERN_ALERT "The process is \"%s\", pid is %d\n", current->comm, current->pid);
//根据输入的howmany变量打印howmany次whom
for(i=0; i<howmany; i++)
printk(KERN_ALERT "are you ok,%s\n", whom);
printk(KERN_ALERT "boolflag = %d\n", boolflag);
//printk(KERN_ALERT "invboolflag = %d\n", invbool(boolflag));
//打印输入的二维字符数组
for(i=0; i<2; i++)
printk(KERN_ALERT "age[%d]:%s\n", i, name[i]);
if(err)
goto err_exit;
if(major) //如果定义了主设备号
{
devid = MKDEV(major, minor); //MKDEV生成设备号
register_chrdev_region(devid, 1, "hello");//注册设备号,并命名为hello
}
else
{
alloc_chrdev_region(&devid, minor, 1, "hello");//动态分配设备号
printk(KERN_ALERT "minor:%d\r\n", minor);
major = MAJOR(devid);//MAJOR获取主设备号,MINOR获取次设备号
printk(KERN_ALERT "major:%d\r\n", major);
}
return 0;
err_exit:
return EBADTYPE;
}
//模块卸载函数,__exit代表此函数仅仅用于模块卸载,如此模块不允许卸载则丢弃
static void __exit hello_exit(void)
{
dev_t devid=0;
printk(KERN_ALERT "Goodbye,cruel world\n");
printk(KERN_ALERT "The process is \"%s\", pid is %d\n", current->comm, current->pid);
//printk(KERN_ALERT "UTS_RELEASE: %s \n", UTS_RELEASE);
printk("printk1\n");//没有KERN_ALERT则为默认级别,结尾有加\n,则会打印
printk("printk2");//没有KERN_ALERT则为默认级别,结尾没有加\n,则不会打印
devid = MKDEV(major, minor);
unregister_chrdev_region(devid, 1);//释放设备号
}
module_init(hello_init); //模块初始化函数入口
module_exit(hello_exit);//模块卸载函数入口
///////////////////////////////////////////////END/////////////////////////////////////////////
$vim Makefile创建Makefile编译文件,源码如下
///////////////////////////////////////////////START/////////////////////////////////////////////
#Makefile 2.6
objs = hello.o
ifneq ($(KERNELRELEASE),)
mymodule-objs:=${objs}
obj-m:=${objs}
else
PWD:=$(shell pwd)
KVER?=$(shell uname -r)
KDIR:=/usr/src/linux-${KVER} //安装的内核源码树的位置
#KDIR:=/usr/src/linux-headers-2.6.32-21-generic/
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
@rm -rf *.com *.o *.mod.c *.ko .tmp_versions modules.order Module.symvers
endif
///////////////////////////////////////////////END/////////////////////////////////////////////
$make
生成hello.mod.c hello.o Module.symvers hello.ko hello.mod.o modules.order
$tail -f /var/log/message //新打开一个字符终端输入此命令用于查看模块加载、卸载打印
$sudo insmod hello.ko //加载内核,会看到如下打印
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.208007] Hello,world
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.208021] The process is "insmod", pid is 2357
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.208022] are you ok,world
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.208024] boolflag = 1
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.208025] age[0]:name1
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.208026] age[1]:name2
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.208029] minor:0
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.208030] major:249
$sudo rmmod hello.ko //卸载内核,会看到如下打印
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.236285] Goodbye,cruel world
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.236303] The process is "rmmod", pid is 2358
Apr 15 23:27:16 jayson-desktop kernel: [ 4322.236308] printk
本文通过一个经典的hello驱动程序实例,详细介绍了Linux内核驱动的开发过程,包括引用基本头文件、模块许可证声明、初始化与退出函数、参数声明以及设备注册。在加载和卸载模块时,内核会调用相应的初始化和退出函数,并通过printk打印相关信息。
1618

被折叠的 条评论
为什么被折叠?



