初探linux内核编程,参数传递以及模块间函数调用
一.前言
我们一起从3个小例子来体验一下linux内核编程。如下:
1. 内核编程之hello world
2. 模块参数传递
3. 模块间函数调用
二.准备工作
首先,在你的linux系统上面安装linux头文件,debian系列:
1 $:sudo apt-get install linux-headers-`uname -r`
安装后,在你的 /lib/modules/ 目录下有你刚刚安装的头文件版本号对应的目录。头文件夹下面还有个 build 文件夹,里面的Makefile文件是等会要编译内核模块用的。 如图,这是我机器上面的:
注意: 安装的头文件版本一定要和你的系统版本一样 ,不然你自己编写的模块不能插入到本机的内核。如果你apt-get安装头文件时,没有对应的头文件,或者你的源里面放不稳定版本的源后,依然没有对应的头文件,你可以到 这里 搜索需要的deb包来安装。再或者下载跟本机对应的内核源码来构建环境。
三.内核编程之hello world
我们先来了解下内核模块文件的入口和出口。它由 2个宏 来注册入口和出口,分别是:
1 module_init(x); 2 module_exit(x);
这2个宏在头文件目录的 include/linux/module.h 。宏里面的x代表注册到内核的入口和出口函数。通俗一点讲就是模块初始化和模块卸载时调用的函数。
初始化函数的形式:int my_init(void);
退出函数的形式:void my_exit(void);
另外还有一些宏:
MODULE_LICENSE(_license):模块的许可证。
MODULE_AUTHOR(_author):模块的作者。
MODULE_VERSION(_version):模块版本
MODULE_DESCRIPTION(_description):模块的描述。
还有一些就不一一举例了。
现在,我来看最简单的hello world例子:
文件名: kernel_hello.c
1 #include <linux/init.h>2 #include <linux/module.h>3 #include <linux/kernel.h>4 5 /* 以下4个宏分别是许可证,作者,模块描述,模块版本 */6 MODULE_LICENSE("Dual BSD/GPL");7 MODULE_AUTHOR("yuuyuu");8 MODULE_DESCRIPTION("kernel module hello");9 MODULE_VERSION("1.0"); 10 11 /* 入口函数 */ 12 static int hello_init(void) 13 { 14 printk(KERN_ALERT "hello_init() start\n"); 15 16 return 0; 17 } 18 19 /* 退出函数 */ 20 static void hello_exit(void) 21 { 22 printk(KERN_ALERT "hello_exit() start\n"); 23 } 24 25 /* 注册到内核 */ 26 module_init(hello_init); 27 module_exit(hello_exit);
上面的 printk() 函数时内核自己实现的输出函数,KERN_ALERT时输出信息的级别!
然后再写一个很简单的Makefile文件:
1 KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build
2 PWD := $(shell pwd)
3
4 obj-m := kernel_hello.o
5
6 default:
7 $(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules
KERNAL_DIR :你刚刚安装头文件的目录
PWD :代表当前你编写模块文件的目录。
obj-m :模块的依赖目标文件。另外,内核的Makefile文件:obj-m代表将目标文件编译成模块,obj-y代表编译到内核,ojb-n代表不编译。
-C :执行make的时候,把工作目录切换到-C后面指定的参数目录,这里即头文件目录下的build目录。
M :这个M时内核Makefile里面的一个变量。作用时回到当前目录继续读取Makefile,这里的就是读完build目录下的Makefile之后再回到我们的这个目录,
读取我们刚刚编写的那个Makefile。
最后的modules是代表编译模块。
现在我们来编译下,在我们编写Makefile的目录下执行make,会看到生成了模块文件 kernel_hello.ko
查看模块信息:sudo modinfo kernel_hello.ko
可以看到刚刚那几个宏插入的模块信息。
现在我们一口气执行4个动作:插入模块,查看内核已插入的模块,卸载模块,查看dmesg信息:
可以看到,模块在初始化和退出时都打印了函数里面的信息。
四.模块参数传递
模块的参数传递也是一个宏,在头文件目录的 include/linux/moduleparam.h :
1 module_param(name, type, perm)
name :模块中的变量名,也是用户可指定参数名。
type :byte,short,ushot,int,uint,long,ulong,charp,bool这些
perm :模块的权限控制。跟linux文件权限控制一样的。
文件名: kernel_hello_param.c
1 #include <linux/init.h>2 #include <linux/module.h>3 #include <linux/kernel.h>4 5 /* 以下4个宏分别是许可证,作者,模块描述,模块版本 */6 MODULE_LICENSE("Dual BSD/GPL");7 MODULE_AUTHOR("yuuyuu");8 MODULE_DESCRIPTION("kernel module hello");9 MODULE_VERSION("1.0"); 10 11 static char *msg; 12 module_param(msg, charp, 0644); 13 14 /* 入口函数 */ 15 static int hello_init(void) 16 { 17 printk(KERN_ALERT "hello_init() start\n"); 18 printk(KERN_ALERT "%s\n", msg); 19 20 return 0; 21 } 22 23 /* 退出函数 */ 24 static void hello_exit(void) 25 { 26 printk(KERN_ALERT "hello_exit() start\n"); 27 } 28 29 /* 注册到内核 */ 30 module_init(hello_init); 31 module_exit(hello_exit);
比上一个文件,就增加了11,12,18行。注意第12行的 charp ,是内核的字符指针。
编译后,传参插入,dmesg查看信息:
插入的参数msg跟在模块后面即可 。
五.模块间函数调用
模块的函数 导出到符号表 才可以供其他函数使用,需要用到宏:
1 EXPORT_SYMBOL(sym)
该宏在 include/linux/export.h 里面。
既然模块间函数调用,我们要编写2个模块。
文件一: kernel_fun.h
1 #ifndef KERNEL_FUN_H 2 #define KERNEL_FUN_H 3 4 void fun(void); 5 6 #endif
文件二,要导出的模块文件: kernel_fun.c
1 #include <linux/init.h>2 #include <linux/module.h>3 #include <linux/kernel.h>4 #include <linux/export.h>5 6 #include "kernel_fun.h"7 8 /* 以下4个宏分别是许可证,作者,模块描述,模块版本 */9 MODULE_LICENSE("Dual BSD/GPL"); 10 MODULE_AUTHOR("yuuyuu"); 11 MODULE_DESCRIPTION("kernel module hello"); 12 MODULE_VERSION("1.0"); 13 14 /* 入口函数 */ 15 static int fun_init(void) 16 { 17 printk(KERN_ALERT "fun_init() start\n"); 18 19 return 0; 20 } 21 22 void fun() 23 { 24 printk(KERN_ALERT "fun() is called\n"); 25 } 26 27 /* 退出函数 */ 28 static void fun_exit(void) 29 { 30 printk(KERN_ALERT "fun_exit() start\n"); 31 } 32 33 /* 注册到内核 */ 34 module_init(fun_init); 35 module_exit(fun_exit); 36 37 /* 导出符号表 */ 38 EXPORT_SYMBOL(fun);
最后一行就是导出到符号表。
文件三,要调用模块文件二的函数: kernel_mod.c
1 #include <linux/init.h>2 #include <linux/module.h>3 #include <linux/kernel.h>4 #include <linux/export.h>5 6 #include "kernel_fun.h"7 8 /* 以下4个宏分别是许可证,作者,模块描述,模块版本 */9 MODULE_LICENSE("Dual BSD/GPL"); 10 MODULE_AUTHOR("yuuyuu"); 11 MODULE_DESCRIPTION("kernel module hello"); 12 MODULE_VERSION("1.0"); 13 14 /* 入口函数 */ 15 static int mod_init(void) 16 { 17 printk(KERN_ALERT "mod_init() start\n"); 18 19 /* 调用fun */ 20 fun(); 21 return 0; 22 } 23 24 /* 退出函数 */ 25 static void mod_exit(void) 26 { 27 printk(KERN_ALERT "mod_exit() start\n"); 28 } 29 30 /* 注册到内核 */ 31 module_init(mod_init); 32 module_exit(mod_exit);
第20行即是调用其他模块的函数 。
这里要编译2个模块,对应的Makefile文件:
1 KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build
2 PWD := $(shell pwd)
3
4 obj-m := kernel_mod.o kernel_fun.o
5
6 default:
7 $(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules
编译好这2个模块后,我们现在来验证。注意,因为kernel_mod依赖kernel_fun,所以我要 先插入kernel_fun 模块。
卸载模块的时候,我们要 先卸载kernel_mod ,原因同上。
依次插入kernel_fun,查看它的符号表,然后插入kernel_mod,查看dmesg:
可以看到kernel_fun的fun()被kernle_mod调用了 。
初探linux内核编程,参数传递以及模块间函数调用相关推荐
- Linux内核编程01:模块机制
模块机制是Linux内核非常重要的一个功能,如果从C语言的模块化编程角度来看,Linux内核其实就是由大大小小.层次不同的模块构成的.有的模块比较小,一个C源文件就实现了:有的模块比较复杂,由多个源文 ...
- linux内核_Linux驱动编程的本质就是Linux内核编程
由于Linux驱动编程的本质属于Linux内核编程,因此我们非常有必要熟悉Linux内核以及Linux内核的特点. 这篇文章将会帮助读者打下Linux驱动编程的基础知识. 本篇文章分为如下三个小节进行 ...
- Linux 内核编程指南
Linux 内核编程指南 PeterJay Salzman MichaelBurian OriPomerantz Copyright© 2001 Peter Jay Salzman 2007−05 ...
- linux内核编程_内核线程kthread_run
linux内核编程_内核线程kthread_run 1. 简述: 2. 使用示例: 3. 详述: 1. 简述: 头文件: include/linux/kthread.h 数据类型: struct ta ...
- 【华为云技术分享】Linux内核编程环境 (1)
在上一期中,我们介绍了Linux内核的源码结构,这一期我们介绍Linux内核编程环境,首先介绍的是Linux内核的编译方法. 一.Linux内核编译方法 本期中我们以Linux 4.19.94版内核来 ...
- 解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】
转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的 ...
- linux内核编程(hello world示例程序)
linux内核编程(hello world) Linux可加载内核模块是 Linux 内核的最重要创新之一.它们提供了可伸缩的.动态的内核.其它开发者可以不用重新编译整个内核便可以开发内核层的程序,极 ...
- Linux 内核编程风格
Linux 内核编程风格 (518 个字於此篇帖子) (已阅读: 1177 次) 这篇短小的文章是对Linux内核编程风格的建议.编程风格非常的个性化,而且,我并不想将我的观点强加给任何人,但是为了变 ...
- linux内核编程(一)
这些天在学习linux内核编程,就在这里小小的show以下. 首先编写如下的linux代码.并命名为hello.c 这里你应该注意亮点: 第一.linux内核编程,不同于普通的用户态下的编程:有一个入 ...
最新文章
- 区块链有哪些技术特征
- 替换libc中的内存分配函数
- php一些单选、复选框的默认选择方法(示例)
- python基础19 -------面向对象终结篇(介绍python对象中各种内置命令)
- java中的getnumber怎么用_java安全编码指南之:Number操作
- html百分比实现边框而不挤出
- c语言获取ip地址,如何用C语言获得本机IP地址
- 知虾:2022 Shopee开店图文指南,手把手教你入驻
- Django下载服务器文件到本地
- ROS安装教程(ubuntu16.04安装ROS Kinetic)
- jpg格式的矢量化arcgis_arcgis将jpg矢量化,带坐标导出TIFF格式,再导入cad的方法...
- 当女程序员遇到了问题......太真实了
- 苹果商店数据分析报告
- 工作流(Workflow) -- 实现简单工作流程
- Revit API: Dimension 尺寸标注
- HTML旅游网页设计制作 DW旅游网站官网滚动网页 DIV旅游风景介绍网页设计与实现
- 大学物理第一章 质点运动学详解
- TWaver使用中间点画折线方法
- 21考研:一研为定,定为研一
- 推荐系统- NCF(Neural Collaborative Filtering)的推荐模型与python实现