• 【linux】内核-模块(驱动)命令原理


    模块的基本描述

      Linux kernel由诸多模块组成,这些模块可以直接与硬件交互,我们也叫它为硬件模块。诸多模块以模块化的方式存在于kernel中。在编译kernel时,可以将需要的模块加入到核心中,也可以将各个子模块编译成各自的单独的模块(模块以ko为扩展名),在需要的时候再分别载入。

    写一个简单的模块程序

    编写模块代码hello.c

    #include <linux/init.h>
    #include <linux/module.h>
    
    MODULE_AUTHOR("voipman");
    MODULE_LICENSE("Dual BSD/GPL");
    
    static int hello_init(void)
    {
        printk(KERN_ERR"Module, hello world.
    ");
        return 0x0;
    }
    
    static void hello_exit(void)
    {
        printk(KERN_ERR"Module, hello exit.
    ");
        return;
    }
    module_init(hello_init);
    module_exit(hello_exit);

    编写Makefile文件

    CURR_PATH   = $(shell pwd)
    KERNEL_PATH = /usr/src/kernels/3.10.0-1160.el7.x86_64
    BUILD_PATH  = $(CURR_PATH)/build
    obj-m := hello.o
    default:
        make -C $(KERNEL_PATH) M=$(CURR_PATH) modules
    .PHONY:clean
    clean:
        @$(RM) *.o *.ko *.mod.* Module.*

    编写Makefile文件时,需要设定内核源码的地址KERNEL_PATH,需要linux系统首先安装kernel-devel包,如下

    yum install kernel-devel

    安装完毕内核开发包后,检查内核代码的路径/usr/src/kernels/3.10.0-1160.el7.x86_64/

    编译demo模块 make,会生成hello.ko的模块文件。

    到此,模块文件已经准备ok,实验这些模块操的命令。

    管理模块的各个命令说明

    insmod

          加载hello.ko模块,会触发系统调用finit_module,通过系统调用自动调用module_init函数,执行回调函数hello_init,输出对应初始化信息。

    [root@localhost ldd]# strace insmod hello.ko
    ...
    stat("/home/w/ldd", {st_mode=S_IFDIR|0775, st_size=235, ...}) = 0
    stat("/home/w/ldd/hello.ko", {st_mode=S_IFREG|0644, st_size=101648, ...}) = 0
    open("/home/w/ldd/hello.ko", O_RDONLY|O_CLOEXEC) = 3
    read(3, "177ELF21", 6)               = 6
    lseek(3, 0, SEEK_SET)                   = 0
    fstat(3, {st_mode=S_IFREG|0644, st_size=101648, ...}) = 0
    mmap(NULL, 101648, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f9f4b253000
    finit_module(3, "", 0)                  = 0
    munmap(0x7f9f4b253000, 101648)          = 0
    close(3)                                = 0
    exit_group(0)                           = ?

    从系统调用发现,insmod加载模块时,会调用系统调用 finit_module 实现模块的初始化,从而执行内核代码的init_module函数。

    CentOS加载模块的系统调用号是313

    #define __NR_finit_module 313

    几个主要的内核函数执行顺序如下

    finit_module --> load_module --> do_init_module 

    在do_init_module中会执行模块初始化函数module_init的函数hello_init,如下

    static noinline int do_init_module(struct module *mod)
    {
        // ...
        /* Start the module */
        if (mod->init != NULL)
            ret = do_one_initcall(mod->init);
    
          // ...

    查看模块的执行信息

    [root@localhost ldd]# cat /proc/kmsg
    <3>[28204.904444] Module, hello world.

     加载模块后,会在系统目录下产生模块的目录

    [root@localhost ldd]# ls /sys/module/hello/
    coresize     initsize     notes/       rhelversion  srcversion   uevent
    holders/     initstate    refcnt       sections/    taint
    [root@localhost ldd]# cat /sys/module/hello/initstate
    live

    另外,加载模块,需要加载着明确的指定模块的依赖关系。如 模块A依赖模块B,加载模块A之前需要先加载模块B。

      

    insmod modB
    insmod modA

    lsmod

      列出加载的模块,找出上面开发的hello.ko模块信息

    [root@localhost ldd]# lsmod |grep hello
    hello                  12496  0

     这个12496代码模块的大小,可以从文件中读取

    [root@localhost ldd]# cat /sys/module/hello/coresize
    12496

    rmmod

      移除模块时,系统会调用系统调用delete_module,更新系统调用如下

    [root@localhost ldd]# strace rmmod hello.ko
    ....
    open("/sys/module/hello/refcnt", O_RDONLY|O_CLOEXEC) = 3
    read(3, "0
    ", 31)                      = 2
    read(3, "", 29)                         = 0
    close(3)                                = 0
    delete_module("hello", O_NONBLOCK)      = 0
    exit_group(0)                           = ?
    +++ exited with 0 +++

    在内核代码中调用module_exit函数,执行回调函数hello_exit,输出对应的退出信息。

    [root@localhost ldd]# rmmod hello.ko
    [root@localhost ldd]# cat /proc/kmsg
    <3>[28947.565658] Module, hello exit.

    删除模块的系统调用号176

    #define __NR_delete_module 176

    几个主要的内核函数执行顺序如下

    delete_module --> free_module 

    其中delete_module系统调用函数如下,会调用模块的退出函数mod->exit()

    SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
            unsigned int, flags)
    {
        struct module *mod;
        
        // ...
        /* Final destruction now no one is using it. */
        if (mod->exit != NULL)
           mod->exit();
        // ...  
        free_module(mod);
        // ... 
    
    }

    modprobe

      加载模块,检查模块依赖的方式加载模块

       -r  删除模块

      modules.dep

    depmod

      分析kernel下的模块,将要载入的模块的相互依赖保存到依赖文件(modules.dep)

    参考材料:

    https://github.com/gityf/ldd

    Done.

  • 相关阅读:
    java 正则表达式
    jqGrid初次使用遇到的问题及解决方法
    JavaScript设计模式 -- 读书笔记
    CSS 7阶层叠水平
    高性能的JavaScript -- 读书笔记
    javaWeb学习笔记
    eclipse内存溢出报错:java.lang.OutOfMemoryError:Java heap space.
    解决eclipse插件svn不显示svn信息和显示的信息为数字的问题
    JDK环境变量配置
    Maven3.0.3的环境变量配置
  • 原文地址:https://www.cnblogs.com/voipman/p/6216233.html
Copyright © 2020-2023  润新知