一.前言                                 

我们一起从3个小例子来体验一下linux内核编程。如下:

1.内核编程之hello world

2.模块参数传递

3.模块间函数调用

二.准备工作                          

首先,在你的linux系统上面安装linux头文件,debian系列:

 $:sudo apt-get install linux-headers-`uname -r`

安装后,在你的/lib/modules/目录下有你刚刚安装的头文件版本号对应的目录。头文件夹下面还有个build文件夹,里面的Makefile文件是等会要编译内核模块用的。如图,这是我机器上面的:

注意:安装的头文件版本一定要和你的系统版本一样,不然你自己编写的模块不能插入到本机的内核。如果你apt-get安装头文件时,没有对应的头文件,或者你的源里面放不稳定版本的源后,依然没有对应的头文件,你可以到这里搜索需要的deb包来安装。再或者下载跟本机对应的内核源码来构建环境。

三.内核编程之hello world    

我们先来了解下内核模块文件的入口和出口。它由2个宏来注册入口和出口,分别是:

 module_init(x);
 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

 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>

 /* 以下4个宏分别是许可证,作者,模块描述,模块版本 */
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("yuuyuu");
 MODULE_DESCRIPTION("kernel module hello");
 MODULE_VERSION("1.0");

 /* 入口函数 */
 static int hello_init(void)
 {
     printk(KERN_ALERT "hello_init() start\n");

     ;
 }

 /* 退出函数 */
 static void hello_exit(void)
 {
     printk(KERN_ALERT "hello_exit() start\n");
 }

 /* 注册到内核 */
 module_init(hello_init);
 module_exit(hello_exit);

上面的printk()函数时内核自己实现的输出函数,KERN_ALERT时输出信息的级别!

然后再写一个很简单的Makefile文件:

 KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build
 PWD := $(shell pwd)

 obj-m := kernel_hello.o

 default:
     $(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:

 module_param(name, type, perm)

name:模块中的变量名,也是用户可指定参数名。

type:byte,short,ushot,int,uint,long,ulong,charp,bool这些

perm:模块的权限控制。跟linux文件权限控制一样的。

文件名:kernel_hello_param.c

 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>

 /* 以下4个宏分别是许可证,作者,模块描述,模块版本 */
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("yuuyuu");
 MODULE_DESCRIPTION("kernel module hello");
 MODULE_VERSION("1.0");

 static char *msg;
 module_param(msg, charp, );

 /* 入口函数 */
 static int hello_init(void)
 {
     printk(KERN_ALERT "hello_init() start\n");
     printk(KERN_ALERT "%s\n", msg);

     ;
 }

 /* 退出函数 */
 static void hello_exit(void)
 {
     printk(KERN_ALERT "hello_exit() start\n");
 }

 /* 注册到内核 */
 module_init(hello_init);
 module_exit(hello_exit);

比上一个文件,就增加了11,12,18行。注意第12行的charp,是内核的字符指针。

编译后,传参插入,dmesg查看信息:

插入的参数msg跟在模块后面即可。

五.模块间函数调用                

模块的函数导出到符号表才可以供其他函数使用,需要用到宏:

 EXPORT_SYMBOL(sym)

该宏在include/linux/export.h里面。

既然模块间函数调用,我们要编写2个模块。

文件一:kernel_fun.h

 #ifndef KERNEL_FUN_H
 #define KERNEL_FUN_H

 void fun(void);

 #endif

文件二,要导出的模块文件:kernel_fun.c

 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/export.h>

 #include "kernel_fun.h"

 /* 以下4个宏分别是许可证,作者,模块描述,模块版本 */
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("yuuyuu");
 MODULE_DESCRIPTION("kernel module hello");
 MODULE_VERSION("1.0");

 /* 入口函数 */
 static int fun_init(void)
 {
     printk(KERN_ALERT "fun_init() start\n");

     ;
 }

 void fun()
 {
     printk(KERN_ALERT "fun() is called\n");
 }

 /* 退出函数 */
 static void fun_exit(void)
 {
     printk(KERN_ALERT "fun_exit() start\n");
 }

 /* 注册到内核 */
 module_init(fun_init);
 module_exit(fun_exit);

 /* 导出符号表 */
 EXPORT_SYMBOL(fun);

最后一行就是导出到符号表。

文件三,要调用模块文件二的函数:kernel_mod.c

 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/export.h>

 #include "kernel_fun.h"

 /* 以下4个宏分别是许可证,作者,模块描述,模块版本 */
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("yuuyuu");
 MODULE_DESCRIPTION("kernel module hello");
 MODULE_VERSION("1.0");

 /* 入口函数 */
 static int mod_init(void)
 {
     printk(KERN_ALERT "mod_init() start\n");

     /* 调用fun */
     fun();
     ;
 }

 /* 退出函数 */
 static void mod_exit(void)
 {
     printk(KERN_ALERT "mod_exit() start\n");
 }

 /* 注册到内核 */
 module_init(mod_init);
 module_exit(mod_exit);

第20行即是调用其他模块的函数。

这里要编译2个模块,对应的Makefile文件:

 KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build
 PWD := $(shell pwd)

 obj-m := kernel_mod.o kernel_fun.o

 default:
     $(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内核编程,参数传递以及模块间函数调用的更多相关文章

  1. 小松之LINUX驱动学习笔记之模块间函数调用通讯

    1. 符号导出函数 EXPORT_SYMBOL() EXPORT_SYMBOL标签内定义的函数对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用. EXPORT_SYMBOL_GPL( ...

  2. 初探Linux内核中的内存管理

    Linux内核设计与实现之内存管理的读书笔记 初探Linux内核管理 内核本身不像用户空间那样奢侈的使用内存; 内核不支持简单快捷的内存分配机制, 用户空间支持? 这种简单快捷的内存分配机制是什么呢? ...

  3. 解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】

    转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的 ...

  4. Linux内核编程-0:来自内核的 HelloWorld

    Linux内核编程一直是我很想掌握的一个技能.如果问我为什么,我也说不上来. 也许是希望有一天自己的ID也出现在内核开发组的邮件列表里?或是内核发行文件的CREDITS文件上? 也许是吧.其实更多的, ...

  5. Linux内核编程规范与代码风格

    source: https://www.kernel.org/doc/html/latest/process/coding-style.html translated by trav, travmym ...

  6. linux内核编程入门 hello world

    注意: Makefile 文件的命名注意M需要大写,否则会报错. 在Makefile文件中make命令前应为tab制表符. 下文转载至:https://blog.csdn.net/bingqing07 ...

  7. Linux内核模块编程——Hello World模块

    Linux内核模块编程 编程环境 Ubuntu 16.04 LTS 什么是模块 内核模块的全称是动态可加载内核模块(Loadable Kernel Modul,KLM),可以动态载入内核,让它成为内核 ...

  8. 宋宝华: Linux内核编程广泛使用的前向声明(Forward Declaration)

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者:宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 前向声明 编程定律 先强调一点:在一切可 ...

  9. linux内核编程入门--系统调用监控文件访问

    参考的资料: hello world   https://www.cnblogs.com/bitor/p/9608725.html linux内核监控模块--系统调用的截获  https://www. ...

随机推荐

  1. velocity-tools-beta1.jar与velocity-tools.jar不兼容

    今天在升级了某些依赖jar的版本启动一项目后,velocity中的有些定义在common.vm中变量居然变成了null,没能include进来导致,而没升级的环境是ok的,经过反查,最后发现是将vel ...

  2. 【转载】GUID vs INT Debate

    I recently read a blog post on what was better using GUIDs or Integer values. This is been an age lo ...

  3. 验证:mysql AUTO_INCREMENT 默认值是1

    用mongodb时,有些字段需要做自增,而且是用二十进制字母表示(使用a-t对应0-19),做了一个_auto_increment字段用来保存,但是应该从0开始还是从1开始呢? 和mysql保持一致便 ...

  4. Code First :使用Entity. Framework编程(2) ----转发 收藏

    第二章:Code First概览 如果你使用第一.二版的EF框架工作过,你会回想起书中的业务案例:Break Away Geek Adventures, 简称BAGA.BAGA共享了很多像我们这样的奇 ...

  5. javascript之DOM

    文档对象模型(Document Object Model,DOM)是一种用于HTML和XML文档的编程接口.它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式.我们最为关心的是,DOM把 ...

  6. 微信 小程序 drawImage wx.canvasToTempFilePath wx.saveFile 获取设备宽高 尺寸问题

    以下问题测试环境为微信开发者0.10.102800,手机端iphone6,如有不对敬谢指出. 根据我的测试,context.drawImage,在开发者工具中并不能画出来,只有预览到手机中显示. wx ...

  7. ARCGIS server没有服务、silverlight不能调试、windows server2008安装时跳出“安装程序无法创建新的系统分区也无法定位现有的系统分区”的解决方案

    1.某个系统服务没开启 2.默认浏览器设置为IE.(IE内核有时候也不能调试) 3.BIOS里面的SATA设置为开启.

  8. 为Autodesk Viewer添加自定义工具条

    如果你参加过我们近期的活动,你就会频繁的听到我们现在正在做的Autodesk Viewer大模型浏览器,这是一个不需要下载任何插件,基于WebGL技术的浏览器,可以支持几十种数据格式.同时viewer ...

  9. 【读书笔记】iOS网络-测试与操纵网络流量

    一,观测网络流量. 观测网络流量的行为叫做嗅探或数据包分析. 1,嗅探硬件. 从iOS模拟器捕获数据包不需要做特别的硬件或网络配置.如果需要捕获这些数据包,那么可以使用嗅探软件来监听回送设备或是用于连 ...

  10. XML解析之SAX详解

    XML解析之SAX详解 本文属于作者原创 http://www.cnblogs.com/ldnh/ XML解析的五个步骤 1.打开文档 (void)parserDidStartDocument:(NS ...