zoukankan      html  css  js  c++  java
  • 内核模块

    内核模块

     内核模块是Linux内核向外部提供的一个插口,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),我们简称为模块。Linux内核之所以提供模块机制,是因为它本身是一个单内核(monolithic kernel)。单内核的最大优点是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。很多驱动程序都以模块的形式存在,用户可以有选择的加载需要的驱动程序。

    一、 什么是模块

      模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功能。

    二、 编写一个简单的模块

      模块和内核都在内核空间运行,模块编程在一定意义上说就是内核编程。因为内核版本的每次变化,其中的某些函数名也会相应地发生变化,因此模块编程与内核版本密切相关。以下例子针对2.6内核:

      tasklet.c文件

    复制代码
     1 #include <linux/module.h>
     2 #include <linux/init.h>
     3 #include <linux/kernel.h>
     4 #include <linux/interrupt.h>  
     5 
     6 static struct tasklet_struct my_tasklet;  
     7 
     8 static void tasklet_handler (unsigned long data)
     9 {
    10         printk(KERN_ALERT "tasklet_handler is running.
    ");
    11 }  
    12 
    13 static int __init test_init(void)
    14 {
    15         tasklet_init(&my_tasklet, tasklet_handler, 0);
    16         tasklet_schedule(&my_tasklet);
    17         return 0;
    18 }  
    19 
    20 static void __exit test_exit(void)
    21 {
    22         tasklet_kill(&my_tasklet);
    23         printk(KERN_ALERT "test_exit running.
    ");
    24 }
    25 MODULE_LICENSE("GPL");  
    26 
    27 module_init(test_init);
    28 module_exit(test_exit);
    复制代码

      1.头文件说明:所有模块都要使用头文件module.h,此文件必须包含进来;头文件kernel.h包含了常用的内核函数;头文件init.h包含了宏_init和_exit,它们允许释放内核占用的内存。interrupt.h文件时编写tasklet(关于tasklet的说明可以参考linux中断与异常)必须要用的头文件。

      2.test_init是模块的初始化函数,它必需包含诸如要编译的代码、初始化数据结构等内容,其中的tasklet_init初始化我们自己创建的tasklet,tasklet_schedule函数则是将我们初始化后的tasklet挂到tasklet链表中,并激活软中断来执行我们的tasklet。

      3.test_exit是模块的退出和清理函数。此处可以做所有终止该内核模块时相关的清理工作。

      4.函数module_init()和module_exit()是模块编程中最基本也是必须的两个函数。module_init()向内核注册模块,并调用注册做模块的初始化工作的函数,而module_exit()注销模块并注册模块注销时执行的清理函数

      5.第10行使用了printk()函数,该函数是由内核定义的,功能与C库中的printf()类似,它把要打印的信息输出到终端或系统日志。字符串前面的KERN_ALERT是输出的级别,表示立即在终端输出,可以调用dmesg命令来查看输出。

      有了上面的各个组成,一个模块就算编写好了。

    三、 编译模块

      编译模块不想编译普通的c文件,直接敲几个命令就可以了,编译模块必须用Makefile文件,而且Makefile文件必须按一定的规则编写,这个和平时编写的Makefile文件不一样。编写内核模块的Makefile文件内容几乎相同的。下面是模版:

    复制代码
    obj-m += tasklet.o        # 产生tasklet 模块的目标文件
    CURRENT_PATH := $(shell pwd)   #模块所在的当前路径
    LINUX_KERNEL := $(shell uname -r)    #Linux内核源代码的当前版本
    LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL) #Linux内核源代码的绝对路径
    all:
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules   #编译模块了
    clean:
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean    #清理
    复制代码

      除了第一行要把tasklet换成编写的目标模块的.c文件名外,其它都是一样的。

      一切都准备好之后,直接make一下就ok了。

    四、 运行模块

      运行模块也不能像运行普通程序一样,直接./tasklet来运行,必须通过专门的命令来运行。

      使用$insmod tasklet.ko命令直接将我们的模块插入到内核中,模块被插入内核时就会调用我们注册的初始化函数test_init,用dmesg命令查看输出,可以看到tasklet_handler is running.消息被打印了。

      使用$lsmod命令来查看系统中的模块:

      使用$rmmod tasklet来卸载模块,卸载模块的同时会执行注册函数test_exit,用dmesg可以查看打印消息。

      

     
     
    分类: linux内核
  • 相关阅读:
    RPC服务和HTTP服务对比
    常用工具地址
    maven教程
    【springboot】知识点总结
    [JZOJ4272] [NOIP2015模拟10.28B组] 序章-弗兰德的秘密 解题报告(树形DP)
    [NOIP2015模拟10.22] 最大子矩阵 解题报告(单调栈)
    [NOIP2015模拟10.27] 挑竹签 解题报告(拓扑排序)
    [NOIP2015模拟10.27] [JZOJ4270] 魔道研究 解题报告(动态开点+权值线段树上二分)
    [NOIP2015模拟10.22] 最小代价 解题报告 (最小生成树)
    BZOJ4479 [JSOI2013] 吃货jyy 解题报告(三进制状态压缩+欧拉回路)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3393321.html
Copyright © 2011-2022 走看看