0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

内核模块的原理以及其模块编写

C语言专家集中营 2018-01-02 11:11 次阅读

HelloWorld内核

开始断断续续学习内核,大概半年了,多少开始对内核有点感悟了,但是对于这个庞然大物我显得很渺小,在枯燥的内核源码之中似乎没有一点点成功的喜悦,因此我选择学习内核模块编程,通过编写一些内核模块来体验那一点点小小的成就感吧!

什么是内核模块

内核模块是具有独立功能的程序。它可以被单独编译,但是不能单独运行,它的运行必须被链接到内核作为内核的一部分在内核空间中运行。
内核模块的原理以及其模块编写

最简单的内核模块

#include //所有模块都必须包含的头文件#include //一些宏定义,例如这里的KERN_INFOint init_module(void) { printk(KERN_INFO "Hello world 1.\n"); /* * 返回非0表示模块初始化失败,无法载入 */ return 0; }void cleanup_module(void) { printk(KERN_INFO "Goodbye world 1.\n"); } //一个模块至少需要两个函数,一个初始化函数这里是init_module在载入内核的时候调用, //一个结束函数,这里是cleannup_module在从内核中注销的时候调用

一个Makefile来编译这个内核模块

obj-m += hello-1.oall: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

注意:本文所有环节都是基于Centos6.5下测试OK,你可能在有的书上看见Makefile是这样写的

make -C /usr/src/linux-headers-$(shell uname -r) M=$(PWD) modules 其实/lib/modules/$(shell uname -r)/build 这个路径就是上面路径的一个软链接 [root@localhost 2.6.32-431.el6.x86_64]# ls -al build lrwxrwxrwx. 1 root root 44 Mar 16 05:26 build -> /usr/src/kernels/2.6.32-504.12.2.el6.x86_64/

编写好makefile文件后,使用make进行编译,编译完就出现一个.ko的文件,这个就是内核模块,需要载入运行

载入内核模块进行运行

载入内核模块的方法有很多比如: modprobe 和 insmod前者会分析模块的依赖关系,并且会去指定路径查找内核模块载入,而后者需要指定内核模块的绝对路径进行载入并且不解决模块的依赖关系。这里我们使用insmod来载入内核模块,使用rmmod卸载内核模块 [root@localhost kernel_module]# insmod hello-1.ko使用dmes查看内核模块的输出Hello world 1.卸载内核模块 [root@localhost kernel_module]# rmmod hello-1 dmesg查看输出Goodbye world 1.

内核模块编程和应用程序编程的异同

内核模块编程是不能去使用标准库(比如malloc free等)和一些第三方的库

内核模块编程是没有内存保护的,如果内存访问错误,就会出现oops错误

内核模块编程是没有main函数的,只有一个初始化函数,和一个提出函数

内核模块编程需要使用内核提供的头文件和API

内核模块编程的标准输出是输出到文件,而不是输出到屏幕

内核模块编程的debug是不能使用gdb来进行调试的。

内核模块进阶

内核模块的编程不仅仅是上面的一个HelloWorld,内核模块编程还有一些更高级的写法,下面会一一介绍:

去掉init_module/cleanup_module

在上面的HelloWorld模块中,你会发现初始化函数和退出函数好像是固定的名称,那么有没有办法自己自定义名称呢其实是可以的,你可以自己自定义名称,然后进行注册即可(注册其实就是做了一个函数指针的赋值而已)下面是自定义名称的写法:

//不需要固定内核模块的初始化函数的名字和结束的名字#include #include #include static int hello_2_init(void) { printk(KERN_INFO "Hello,world 2\n"); return 0; }static void hello_2_exit(void) { printk(KERN_INFO "Goodbye,world 2\n"); } //这两个函数来注册模块初始化和模块结束module_init(hello_2_init); module_exit(hello_2_exit);

__init/__initdata/__exit

在有的内核模块编程的书籍或者介绍内核模块编程的博客中,你或许会发现有这样的一些特殊关键字__init ,_initdata ,__exit等等,其实这些都是gcc的扩展属性:__init宏最常用的地方是驱动模块初始化函数的定义处,其目的是将驱动模块的初始化函数放入名叫.init.text的输入段。当内核启动完毕后,这个段中的内存会被释放掉供其他使用。__initdata宏用于数据定义,目的是将数据放入名叫.init.data的输入段。其它几个宏也类似。

模块描述信息

可以使用modinfo去查看一个模块的模块信息,下面是自己编写的模块和系统自带的模块的两个模块信息的对比

[root@localhost kernel_module]# modinfo hello-1.kofilename: hello-1.kosrcversion: 0D3956C127A907CC9E7114Fdepends: vermagic: 2.6.32-504.12.2.el6.x86_64 SMP mod_unload modversions [root@localhost kernel_module]# modinfo/lib/modules/2.6.32-431.el6.x86_64/kernel/fs/ext4/ext4.ko filename: /lib/modules/2.6.32-431.el6.x86_64/kernel/fs/ext4/ext4.kolicense: GPLdescription: Fourth Extended Filesystemauthor: Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others srcversion: 345EBDA2AEFF60FFED78864 depends: mbcache,jbd2 vermagic: 2.6.32-431.el6.x86_64 SMP mod_unload modversions

从上面的对比可知,自己编写的模块的模块信息很少,没有作者信息,没有许可证信息等等,其实这些都可以设置

#include #include #include #define DRIVER_AUTHOR "zyf"#define DRIVER_DESC "A sample driver"static int __init init_hello_4(void) { printk(KERN_INFO "Hello, world 4\n"); return 0; } static void __exit cleanup_hello_4(void) { printk(KERN_INFO "Goodbye, world 4\n"); } module_init(init_hello_4); module_exit(cleanup_hello_4); //模块的许可证 MODULE_LICENSE("GPL"); //模块的作者MODULE_AUTHOR(DRIVER_AUTHOR); //模块的描述MODULE_DESCRIPTION(DRIVER_DESC);

模块参数

在用户态编写程序的时候我们都应该清楚,是可以给程序传递参数的,那么同样内核模块同样也有这样的需求,下面的例子展示了如何去给内核模块传递参数:

#include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("ZYF");static short int myshort = 1;static int myint = 420;static long int mylong = 9999;static char *mystring = "blah";static int myintArray[2] = {-1,-1};static int arr_argc = 0;//需要使用module_param来对参数进行说明,指明这个参数的类型,权限等charp是字符指针//定义数组参数需要使用module_param_arraymodule_param(myshort,short,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); MODULE_PARM_DESC(myshort,"A short integer"); module_param(myint,int,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); MODULE_PARM_DESC(myint,"A integer"); module_param(mylong,long,S_IRUSR); MODULE_PARM_DESC(mylong,"A long integer"); module_param(mystring,charp,0000); MODULE_PARM_DESC(mystring,"A character string"); module_param_array(myintArray,int,&arr_argc,0000); MODULE_PARM_DESC(myintArray,"An array of integer");static int __init hello_5_init(void) { int i; printk(KERN_INFO "Hello, world 5\n=============\n"); printk(KERN_INFO "myshort is a short integer: %hd\n", myshort); printk(KERN_INFO "myint is an integer: %d\n", myint); printk(KERN_INFO "mylong is a long integer: %ld\n", mylong); printk(KERN_INFO "mystring is a string: %s\n", mystring); for (i = 0; i < (sizeof myintArray / sizeof (int)); i++)    {        printk(KERN_INFO "myintArray[%d] = %d\n", i, myintArray[i]);    }    printk(KERN_INFO "got %d arguments for myintArray.\n", arr_argc);        return 0; }static void __exit hello_5_exit(void) {    printk(KERN_INFO "Goodbye,world 5\n"); } module_init(hello_5_init); module_exit(hello_5_exit);/* 载入模块的时候,如果不指定参数就是上面的默认值,如果要指定参数的话 可以像下面这样来指定参数。 insmod hello-5.ko mystring="superc" myint=444 */

模块文件分割

在用户态写程序的时候,你会将一个大的程序分割成好几个文件,这样程序脉络就显的很清晰。在这里我们将初始化函数和退出函数分开在两个文件中编写。

start.c中#include /* We're doing kernel work */#include /* Specifically, a module */int init_module(void) { printk(KERN_INFO "Hello, world - this is the kernel speaking\n"); return 0; } stop.c中#include /* We're doing kernel work */#include /* Specifically, a module */void cleanup_module() { printk(KERN_INFO "Short is the life of a kernel module\n"); } 那么Makefile编译的时候需要设置成这样: obj-m += startstop.o startstop-objs := start.o stop.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • helloworld
    +关注

    关注

    0

    文章

    13

    浏览量

    4364
  • 内核模块
    +关注

    关注

    0

    文章

    10

    浏览量

    3086

原文标题:黑客内核:编写属于你的Hello world

文章出处:【微信号:C_Expert,微信公众号:C语言专家集中营】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    linux 了解内核模块的原理 《Rice linux 学习开发》

    内核模块是一种没有经过链接,不能独立运行的目标文件,是在内核空间中运行的程序。
    的头像 发表于 07-16 10:08 4657次阅读
    linux 了解<b class='flag-5'>内核模块</b>的原理 《Rice linux 学习开发》

    Linux 内核模块工作原理及内核模块编译案例

    一个内核模块至少包含两个函数,模块被加载时执行的初始化函数init_module()和模块被卸载时执行的结束函数cleanup_module()。
    发表于 09-23 09:39 2497次阅读
    Linux <b class='flag-5'>内核模块</b>工作原理及<b class='flag-5'>内核模块</b>编译案例

    详解Linux内核模块编写方法

    Linux 系统为应用程序提供了功能强大且容易扩展的 API,但在某些情况下,这还远远不够。与硬件交互或进行需要访问系统中特权信息的操作时,就需要一个内核模块
    的头像 发表于 05-11 08:55 3742次阅读

    Linux内核模块间通讯方法

    Linux内核模块间通讯方法非常的多,最便捷的方法莫过于函数或变量符号导出,然后直接调用。默认情况下,模块模块之间、模块内核之间的全局变
    发表于 06-07 16:23 2523次阅读
    Linux<b class='flag-5'>内核模块</b>间通讯方法

    Linux内核模块程序结构

    Linux设备驱动会以内核模块的形式出现,因此,学会编写Linux内核模块编程是学习Linux设备驱动的先决条件。一个Linux内核模块主要由如下几个部分组成: (1)
    发表于 05-27 09:36

    高效学习Linux内核——内核模块编译

    (description);三、Linux内核模块的编译首先为HelloWorld模块编写MakeFile文件该MakeFile文件应该与源码位于同一目录在Makefile中,在obj-m
    发表于 09-24 09:11

    什么是内核模块?如何编写一个简单的模块

    内核模块是Linux内核向外部提供的一个插口,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),我们简称为模块。Linux
    发表于 08-24 17:15 20次下载

    什么是 Linux 内核模块?

    lsmod 命令能够告诉你当前系统上加载了哪些内核模块以及关于使用它们的一些有趣的细节。
    的头像 发表于 08-09 17:01 3238次阅读

    嵌入式LINUX系统内核内核模块调试教程

    本文档的主要内容详细介绍的是嵌入式LINUX系统内核内核模块调试教程。
    发表于 11-06 17:32 21次下载
    嵌入式LINUX系统<b class='flag-5'>内核</b>和<b class='flag-5'>内核模块</b>调试教程

    如何在Petalinux创建Linux内核模块

    创建内核模块 Petalinux可以帮助工程师简化内核模块的创建工作。在petalinux工程目录下,使用命令“ petalinux-create -t modules --name
    的头像 发表于 03-02 11:10 4357次阅读

    Asterisk内核模块介绍

    主要阐述Asterisk内核模块的基础知识。
    发表于 03-17 13:47 7次下载

    嵌入式LINUX系统内核内核模块调试

    嵌入式LINUX系统内核内核模块调试(嵌入式开发和硬件开发)-嵌入式LINUX系统内核内核模块调试                 
    发表于 07-30 13:55 10次下载
    嵌入式LINUX系统<b class='flag-5'>内核</b>和<b class='flag-5'>内核模块</b>调试

    什么是内核模块签名?内核如何开启模块签名

    驱动可以直接编译进内核镜像,也可以单独编译成ko文件(内核模块),然后再进行加载。内核从3.7后开始支持模块签名,该功能使能以后,内核只允许
    发表于 08-08 16:14 1166次阅读

    Linux内核模块参数传递与sysfs文件系统

    函数传参的内核传参机制,编写内核程序时只要实现传参接口,用户在加载内核模块时即可传入指定参数,使得内核模块更加灵活。
    发表于 06-07 16:23 2115次阅读

    深入分析Linux kernel安全特性: 内核模块签名

    顾名思义,在开启该功能之后,内核在加载内核模块时,会对内核模块的签名进行检查。
    的头像 发表于 10-18 12:32 4730次阅读