1、内核模块的编写
2、内核模块的编译
* 静态编译 * 动态编译
3、模块加载和卸载命令
4、内核模块传参
make menuconfig //调用了Kconfig文件
make uImage //调用了Makefile
make modules //调用了Makefile
基本概念
目录结构
系统启动时需要的文件
制作文件系统工具:BusyBox
NFS挂载
root=/dev/nfs nfsroot=/home/edu/rootfs rw ip=10.9.21.100:10.9.21.50:10.9.21.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,115200
制作根文件系统镜像包
利用Ext4工具包mkfs_ext4.tar.gz
用make_ext4fs
制作ext4文件系统镜像
tar xvf mkfs_ext4.tar.gz
cp make_ext4fs /bin/make_ext4fs
sudo chmod 777 $HOME/rootfs/ -R
制作ext4镜像
make_ext4fs -s -l 314572800 -a root -L linux gtk.img /home/edu/rootfs
生成镜像包后,将镜像包拷贝到eMMC中,然后从eMMC中加载根文件系统。
setenv bootargs root=/dev/mmcblk0p8 rw rootfstype=ext4 init=/linucrc lcd=wy070ml tp=gslx680
saveenv
从内核源码的drivers目录中的驱动C文件中找到共同的函数:
分析模块初始化宏的调用逻辑
module_init(X)
#define module_init(x) __initcall(x) //在iclude/linux/init.h中申明
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall("6",fn,6)
而__define_initcall(level,fn,id)
的定义为:
#define __define_initcall(level,fn,id)\
static initcall_t _initcall_##fn##id __used\
__attribute__((__section__(".initcall" level ".init"))) = fn
所以:
__define_initcall("6",fn,6)就等于
static initcall_t _initcall_fn6 __used __attribute__((__section__(".initcall" 6 ".init"))) = fn
结论:
模块初始化宏module_init(fn)作用就是将fn这个函数地址,编译的时候统一放到init段中,在内核启动的时候或模块加载的时候被执行!
同理:模块卸载函数module_exit(fn)的作用就是将fn这个函数地址,编译的时候统一放到init段中,在模块被移除的时候被调用执行。
/*linux/init.h*/
/*linux/module.h*/
//内核模块所必须的三个要素:
typedef int(*initcall_t)(void);
typedef void(*exitcall_t)(void);
MODULE_LICENSE("GPL")
可有可无的声明:
MODULE_LICENSE()一般必须得有!!!
7 /*
98 * The following license idents are currently accepted as indicating free
99 * software modules
100 *
101 * "GPL" [GNU Public License v2 or later]
102 * "GPL v2" [GNU Public License v2]
103 * "GPL and additional rights" [GNU Public License v2 rights and more]
104 * "Dual BSD/GPL" [GNU Public License v2
105 * or BSD license choice]
106 * "Dual MIT/GPL" [GNU Public License v2
107 * or MIT license choice]
108 * "Dual MPL/GPL" [GNU Public License v2
109 * or Mozilla license choice]
110 *
111 * The following other idents are available
112 *
113 * "Proprietary" [Non free products]
114 *
115 * There are dual licensed components, but when running with Linux it is the
116 * GPL that is relevant so this is a non issue. Similarly LGPL linked with GPL
117 * is a GPL combined work.
118 *
119 * This exists for several reasons
120 * 1. So modinfo can show license info for users wanting to vet their setup
121 * is free
122 * 2. So the community can ignore bug reports including proprietary modules
123 * 3. So vendors can do likewise based on their own policies
124 */
#define MODULE_LICENSE(_license) MODULE_INFO(license,_license)
/*Author(s),use"Name "or just"Name",formultiple
*authors use multipleMODULE_AUTHOR()statements/lines.
*/
#define MODULE_AUTHOR(_author) MODULE_INFO(author,_author)
/* What your module does. */
#define MODULE_DESCRIPTION(_description) MODULE_INFO(description,_description)
检查其调用逻辑:
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info) //./module.h:92:
#define __MODULE_INFO(tag, name, info)\
static const char __module_cat(name,__LINE__)[]\
__used __attribute__((section(".modinfo"), unused, aligned(1)))\
= __stringify(tag) "=" info
结论:
作者传入的信息最后都被存入到一个数组中去了。
#include
#include
static int __init demo_init(void)
{
printk("---%s---%s---%d---\n",__FILE__,__func__,__LINE___);
return 0;
}
static void __exit demo_exit(void)
{
printk("---%s---%s---%d---\n",__FILE__,__func__,__LINE___);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
make uImage
生成内核镜像,模块便加入到镜像文件中去了)内部编译:
外部编译:
kernel-XXX/Documentation/kbuild/modules.txt
make
之后,就可以看到.ko格式的模块文件了。insmod mod_name.ko
就可以加载进内核,执行rmmod mod_name
就可以卸载模块了。dmesg
命令,可以查看模块的加载卸载情况。#Makefile文件
# 1.首先定义内核源码路径
#KDIR:=/lib/modules/$(shell uname-r)/build #x86架构
KDIR:=/home/edu/SAMBA_SHARE/BK2101/Driver/03_kernel/kernel-3.4.39 #armx架构
# 2. 然后定义自己定义的模块源码路径
PWD:=$(shell pwd)
# 3.添加自己模块的目标文件名
obj-m += demo.o
# 4.添加编译目标
modules:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
/include/linux/
下;体系结构相关的位于/arch/[arm/x86]/include/
下。/include/linux/moduleparam.h
下。/*module_param-typesafe helperfora module/cmdline parameter
*@name:the variable to alter,and exposed parameter name.
*@type:the type of the parameter
*@perm:visibility in sys fs; 在sys目录下创建文件时用的文件权限。
*
*@value becomes the module parameter,or(prefixed byKBUILD_MODNAMEand a
*".")the kernel commandline parameter.Note that-is changed to _,so
*the user can use"foo-bar=1"evenforvariable"foo_bar".
*
*@perm is 0 if the the variable is not to appear in sysfs,or 0444
*for world-readable,0644 for root-writable,etc.Note thatifit
*is writable,you may need to use kparam_block_sysfs_write()around
*accesses(esp.charp,which can be kfreed when it changes).
*
*The @type is simply pasted to refer to a param_ops_##type and a
*param_check_##type:forconvenience many standard types are providedbut
*you can create your own by defining those variables.
*
*Standard types are:
*byte,short,ushort,int,uint,long,ulong
*charp:a character pointer
*bool:a bool,values0/1,y/n,Y/N.
*invbool:the above,only sense-reversed(N=true).
*/
#define module_param(name,type,perm)\
module_param_named(name,name,type,perm)
举例说明:
#include
#include
int num=10;
char *strp = "default";
module_param(num, int, 0644);
module_param(strp, charp, 0644);
static int __init demo_init(void)
{
printk(KERN_INFO"---num=%d---\n", num);
printk(KERN_INFO"---strp=%s---\n", strp);
printk(KERN_INFO"---%s---%s---%d--\n", __FILE__, __func__, __LINE__ );
return 0;
}
static void __exit demo_exit(void)
{
printk(KERN_INFO"---%s---%s---%d--\n", __FILE__, __func__, __LINE__ );
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
insmod demo.ko num=21 strp="leon"
/**
*module_param_string - a char array parameter
*@name: the name of the parameter //参数名
*@string: the string variable //字符串变量的名字
*@len: the maximum length of the string,incl.terminator
*@perm: visibility in sys fs.
*
*This actually copies the string when it's set(unlike type charp).
*@len is usually just sizeof(string).
*/
//一般我们将参数名name(用在命令行中给)和变量名string(用在代码中)都设置为一样;
//其涵义就是name从命令行中获取参数值,而后回来赋值给string
#define module_param_string(name,string,len,perm)
//insmod demo.ko name="I am a string!"
//回到代码中后,string = name;
/**
*module_param_array-a parameter which is an arrayofsome type
*@name: the name of the array variable
*@type: the type,aspermodule_param()
*@nump: optional pointer filled in with the number written //数组元素个数
*@perm: visibility in sys fs
*
*Input and output areascomma-separated values. Commas inside values
*don't workproperly(eg.an arrayofcharp).
*
*ARRAY_SIZE(@name) is used to determine the number of elements in the
*array,so the definition must be visible.
*/
#define module_param_array(name,type,nump,perm)
//insmod demo.ko name=val1,val2,...
符号表文件:
//fn为需要导出的函数名,之后其它模块便可以调用fn了
#include
EXPORT_SYMBOL(fn);
EXPORT_SYMBOL_GPL(fn);
注意:
Module.symvers
文件里;printk(打印级别"",...);
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
打印级别和控制台相关:
/proc/sys/kernel/printk
文件指示了当前系统的打印级别配置,即凡是比控制台打印级别高的(数字上小的打印级别高)都可以显示,否则只能通过dmesg
命令查看了:
控制台打印级别 默认级别 系统支持的最高打印级别 最低打印级别
7 7 1 7
lsmod -查看内核加载的模块
insmod -加载模块
rmmod -卸载模块
modprob