博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
内核模块编程---hello world
阅读量:4184 次
发布时间:2019-05-26

本文共 2938 字,大约阅读时间需要 9 分钟。

什么是内核模块?

其全称为动态可加载内核模块。之所以提供模块机制,是因为Linux是“单块内核”的操作系统,单内核的最大优点是效率高,因为所有的内容都集中在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。Linux内核是模块化组成的,它允许我们方便地在运行时动态地向内核中插入或从中删除代码。

模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时加载,被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。

特点:

装入的内核模块和其他内核部分完全一样,具有相同的访问权限,因此,差的内核模块会导致系统崩溃;模块编程和内核版本密切相连,内核版本和模块版本的不兼容,也可能导致系统崩溃;这些代码被一并组合在一个单独的二进制镜像中,这样基本内核镜像就尽可能的小,而且灵活性很好否则,在需要添加新硬件或者升级设备驱动时,必须重新构建内核。

用户层编程和内核模块编程的区别

选项 应用程序 内核模块程序
使用函数 libc 库 内核函数
运行空间 用户空间 内核空间
运行权限 普通用户 超级用户
入口函数 libc 库 内核函数
运行空间 main() module_init
出口函数 exit() module_exit
编译 gcc makefile
链接 gcc insmod
运行 直接运行 insmod
入口函数 libc 库 内核函数
调试 gdb kdbug 、 kdb 、 kgdb

下面编写简单的内核模块

还是从最经典的hello world写起。

/*************************************************************************        > File Name: hello.c        > Author: Tanswer_        > Mail: 98duxm@gmail.com        > Created Time: 2017年02月27日 星期一 10时53分58秒 ************************************************************************/#include 
#include
#include
MODULE_LICENSE("Dual BSD/GPL");static int hello_init(void){ printk("hello world\n"); return 0;}static void hello_exit(void){ printk("Goodbye world\n");}module_init(hello_init);module_exit(hello_exit);

简单来看看代码

语法和我们平时写的C代码差异比较大,我也还没有适应,有机会再继续写。必要的两个函数:加载函数和卸载函数,分别还有另一种写法。printk是内核态下的打印函数。还可以加模块的声明与描述,比如:MODULE_AUTHOR("Tanswer");//作者。我们详细看一下头文件,在编译前需要配一下环境。

头文件

#include 
所有模块都要使用的 #include
包含了常用的内核函数 #include
包含了宏_init和_exit,它们允许释放内核占用的内存内核头文件的位置:/usr/src/kernels/用户层头文件的位置:/usr/include

我进入/usr/src/后发现里面是空的,并没有源码树,所以执行 #yum install kernel-devel 安装源码树。安装完成后,我发现我的uname -r 和 源码树目录显示的内核版本号不一致

这里写图片描述

这就导致了我写的Makefile出现了问题,见下面代码。

Makefile

obj-m := hello.oCURRENT_PATH:=$(shell pwd)KERNEL_NUM:= $(shell uname -r)#KERNEL_PATH:=/usr/src/kernels/$(KERNEL_NUM)KERNEL_PATH:=/usr/src/kernels/3.10.0-514.6.2.el7.x86_64all:#        make -C $(KERNEL_PATH) M=$(CURRENT_DIR) modules        make -C $(KERNEL_PATH) M=$(CURRENT_PATH) modulesclean:        make -C $(KERNEL_PATH) M=$(CURRENT_PATH) clean

第一次我写的是注释掉的那句,结果make 的时候显示路径名不正确,上面的原因我还不知道,懂得请赐教。所以我直接将路径名写上去了,就是现在的那句。我再次make,看下图:

这里写图片描述

又出现了两个错误,我逐个解决,上面那个错误,提示说please install libelf-dev or elfutils-libelf-devel 那我就乖乖听话装上它,然后我再次make:

这里写图片描述

上面那个错误已经解决,剩下这个以前用makefile的时候也没遇到过,我google了一下,发现有提这个问题的,但是没有解决方法。我没办法,有点失望就随手点开Makefile来看,然后……我就发现了一个小错误。。。Makefile代码中 -C 后指定的是 Linux 内核源代码的目录,而 M= 后指定的是 hello.c 和 Makefile 所在的目录,但是我第一次写成了CURRENT_DIR,就是我注释掉的那句,可明明我定义的变量是CURRENT_PATH,然后我就改过来了。再次make,终于终于终于

这里写图片描述

编译正确。

这里写图片描述

补充(转):

.ko 是kernel object 的缩写,是Linux 2.6内核使用的动态连接文件,在Linux系统启动时加载内核模块

链接和运行

1.insmod hello.ko  加载模块2.dmesg            查看结果3.rmmod            卸载模块

补充:常用的模块编程命令

1.lsmod命令查看系统中加载了的所有模块以及模块间的依赖关系

2.cat /proc/modules来查看加载模块信息
3.内核中已加载模块的信息也存在于 /sys/module 目录下,加载 hello.ko 后,内核中将包含 /sys/module/hello 目录,该目录下又包含一个 refcnt 文件和一个 sections 目录,在 /sys/module/hello 目录下运行 tree -a 可以看到他们之间的关系
4.使用 modinfo < 模块名 > 命令可以获得模块的信息,包括模块的作者,模块的说明,某块所支持的参数以及 vermagic

这里写图片描述

你可能感兴趣的文章
RabbitMQ消息确认(发送确认,接收确认)
查看>>
一篇笔记整理JVM工作原理
查看>>
activemq、rabbitmq、kafka原理和比较
查看>>
秒杀系统设计思路和实现方法
查看>>
Redis常见面试题
查看>>
JDK重要包和Java学习方法论
查看>>
网络通讯中的三次握手与四次挥手原理详解
查看>>
GitHub 开源神器:图片秒变文件
查看>>
openstack ice resize 详解(三)
查看>>
事务与锁(转)
查看>>
Namenode HA原理详解(脑裂)
查看>>
Differences between VMware FT and HA(转)
查看>>
Cloud Prizefight: OpenStack vs. VMware(转)
查看>>
亚马逊Auto Scaling
查看>>
openstack-instance-high-availability-Evacuate
查看>>
evacuate-instance-automatically
查看>>
pycharm常用设置(keymap设置及eclipse常用快捷键总结)
查看>>
关于在openstack的环境变量.bashrc自定自己简化命令
查看>>
Openstack Heat Project介绍(转)
查看>>
How to Perform an Upgrade from Icehouse to Juno(ice升级到juno)
查看>>