zoukankan      html  css  js  c++  java
  • Linux课程实践二:编译模块实现内核数据操控

    一、内核模块原理

    1. Linux内核增加功能

    Linux内核整体结构很庞大,包含了很多的组件,现在有两种方法将需要的功能包含进内核当中:
    - 静态加载:将所有的功能都编译进Linux内核。
    - 动态加载:将需要的功能编译成模块,在需要的时候动态地添加。
    
    • 第一种方法优点在于不会有版本不兼容的问题,不需要进行严格的版本检查;但是生成的内核会很大,要在现有的内核中添加新的功能,要编译整个内核。
    • 第二种方法有点在于模块本身不编译进内核,从而控制了内核的大小,模块一旦被加载,将和其它的部分完全一样。可能会有内核与模块版本不兼容的问题,导致内核崩溃,会造成内存的利用率比较低。

    2. 内核模块概述

    • 之所以提供模块机制,就是为了弥补Linux这个单内核可扩展性和可维护性相对较差的缺陷。
    • 一个模块被加载到内核中时,就成为内核代码的一部分。模块加载入系统时,系统修改内核中的符号表,将新加载的模块提供的资源和符号添加到内核符号表中,以便模块间的通信。

    二、实现内核模块

    1. 编写一个空模块

    (1)模块许可证声明

    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
     
    MODULE_LICENSE("GPL");
    
    • 内核支持GPL兼容代码,因此如果把许可证设置为其它非GPL兼容的(如,“Proprietary”[专利]),某些特定的内核功能在模块中不可用。
    • 如果不声明,则在模块加载时会收到内核被污染的警告,一般应遵循GPL协议。

    (2)模块加载函数

    static int __init XXX_init(void)
    {
             return 0;
    }
    moudle_init(XXX_init);
    
    • 在用insmod命令加载模块时,该函数被执行,完成模块的初始化工作。
    • 命名:Linux内核的模块加载函数一般用 __init 标识声明,模块加载函数必须以 XXX_init (函数名)的形式被指定。
    • 返回值:该函数返回整型值,如果执行成功,则返回0,初始化失败时则返回错误编码,Linux内核当中的错误编码是负值,在<linux/errno.h>中定义。

    (3)模块卸载函数

    static void __exit XXX_exit(void)
    {
    }
    moudle_exit(XXX_exit);
    
    • 在用rmmod命令卸载模块时,该函数被执行,完成与加载相反的工作。
    • 命名:Linux内核的模块加载函数一般用 __exit 标识声明,模块加载函数必须以 XXX_exit (函数名)的形式被指定。
    • 主要包括:
      • 若模块加载函数注册了XXX,则模块卸载函数注销XXX
      • 若模块加载函数动态分配了内存,则模块卸载函数释放这些内存
      • 若模块加载函数申请了硬件资源,则模块卸载函数释放这些硬件资源
      • 若模块加载函数开启了硬件资源,则模块卸载函数一定要关闭这些资源

    (4)模块参数

    static 类型 名称 = 赋值;
    
    • 模块在被加载时传递给模块的值,本身应该是模块内部的全局变量。

    2. 模块操作

    (1)编译模块

    • 编写makefile:

        obj-m:=XXX.o
        CURRENT_PATH:=$(shell pwd)
        LINUX_KERNEL_PATH:=/usr/src/linux-headers-4.4.0-21-generic	//这里写编译机器上的路径和版本
        all:
                make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
        clean:
                make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
      
        //make -C $(LINUX_KERNEL_PATH) 指明跳转到内核源码目录下读取那里的Makefile
        //M=$(CURRENT_PATH) 表明返回到当前目录继续执行当前的Makefile
      
    • 将其放在编写的模块.c文件同级目录下,使用make命令编译,生成XXX.ko模块文件。

    (2)加载模块

    sudo insmod XXX.ko
    
    • 此时已经加载了模块。

    (3)查看与测试

    查看已经安装好的模块:
    lsmod
    

    • 也可以通过查看/proc/modules文件的内容。实际上,lsmod读命令就是通过查看/proc/modules的内容来显示模块信息的。
    显示linux内核的记录信息:
    dmesg
    
    显示模块信息:
    modinfo XXX.ko
    

    (4)卸载模块

    sudo rmmod XXX
    
    • 这时用dmesg看内核信息,就会看到写在module_exit()中的输出。

    3. 实现功能(参考学姐代码)

    (1)输出当前进程信息

    • 代码:

    • 编译:

    • 查看内核信息,验证信息:

    (2)读取进程链表

    • 代码:

    • 从第一个PCB(叫做init_task)开始,顺着next指针读了一圈:


    参考资料

    参考资料1:Linux内核模块简介
    参考资料2:编写属于你的第一个Linux内核模块

  • 相关阅读:
    GUID概念
    某猿的饭局
    SVN切分支步骤
    OSX:设置用户默认浏览器
    值得推荐的android开发框架简单介绍
    用实力让情怀落地!阅兵前线指挥车同款电视TCL&#160;H8800受捧
    Excel查询序列所相应的值-vLoopup函数,求比例分子改变但分母不变
    CSS3制作W3cplus的关注面板
    Spring MVC框架实例
    @property 和@synthesize
  • 原文地址:https://www.cnblogs.com/hyq20135317/p/5517041.html
Copyright © 2011-2022 走看看