• 【linux驱动笔记】linux模块机制浅析


    1.   模块module

    操作系统分微内核和宏内核,微内核优点,可以使操作系统仅作很少的事,其它事情如网络处理等都作为应用程序来实现,微内核精简的同时,必然带来性能的下降。而linux的宏内核设计,保证了设计性能,但linux作为一个通用操作系统,必然会兼容很多硬件,而linux本身的宏内核设计,导致了如果同时兼容所有的硬件,其编译代码将庞大无比,为了解决这个问题,linux效仿微内核,采用了模块这一天才思想。当内核配置make menuconfig时,可以选择M,将驱动作为模块来加载,其生成的文件后缀为*.ko. kernel object,内核对象。采用模块设计,解决了上述问题,但是本质上来说,模块加载进内核后,还是运行在内核态,所以驱动的bug有可能引起系统崩溃,这是在驱动设计时需要特别注意的。

    1.1.  模块实例

    #include <linux/init.h>

    #include <linux/module.h>

     

    static int hellokernel_init(void)

    {

       

        printk("Hello driver! ");

        return 0;

    }

     

    static void hellokernel_exit(void)

    {

        printk("Bye driver! ");

    }

     

    module_init(hellokernel_init);

    module_exit(hellokernel_exit);

     

    上面都的module_initmodule_exit都干了什么,当加载模块insmod 和卸载模块rmmod时又干了什么,下面将说明。

     

    1.2.  module_init,module_exit

    includelinuxInit.h 中有:

    #ifndef MODULE

    #define module_init(x)  __initcall(x);

    #define __initcall(fn) device_initcall(fn)

    #define device_initcall(fn)   __define_initcall(fn, 6)

     

    #define module_exit(x)  __exitcall(x);

    #define __exitcall(fn)

      static exitcall_t __exitcall_##fn __exit_call = fn

    #else

      /* Each module must use one module_init(). */

    #define module_init(initfn)          

      static inline initcall_t __inittest(void)   

      { return initfn; }            

      int init_module(void) __attribute__((alias(#initfn)));

     

    /* This is only required if you want to be unloadable. */

    #define module_exit(exitfn)          

      static inline exitcall_t __exittest(void)   

      { return exitfn; }            

      void cleanup_module(void) __attribute__((alias(#exitfn)));

    #endif

     

     

    从上述处理可以看出,当以非模块编译时,module_init等通过如下路径被执行,其被静态编译进内核。

    #define __define_initcall(fn, id)

       static initcall_t __initcall_##fn##id __used

       __attribute__((__section__(".initcall" #id ".init"))) = fn

     

    而上述用__define_initcall(fn, id)定义的函数都是在linux内核启动时被执行的。

    start_kernelrest_initkernel_initkernel_init_freeabledo_basic_setupdo_initcalls

     

     

    而如果是模块编译时,为动态编译

    int init_module(void) __attribute__((alias(#initfn)));

    这段代码的作用是给我们的加载函数定义一个别名,别名就是init_module

     

     

    1.3.  加载模块

    hello.ko拷贝到/home/tang/wk-tzp/prj/nfs/fs/ramdisk_fs/lib/modules/3.10.53的目录或者其子目录下,

    必须拷贝到对应的版本号如3.10.53下,否则会出问题。

     

    insmod hello.ko

     

     

    insmod是个应用程序,在嵌入式中,这些应用包括lspwd等都来自于大名鼎鼎的busybox

    因此需要阅读busybox的源代码,才能知道insmod究竟干了什么。

    busybox-1.22.1modutilsinsmod.c中:有:

    insmod_mainbb_init_module

       

       

    try_to_mmap_module //通过mmap函数将 xxx.ko文件内容映射到内存中。

    init_module(image, image_size, options) //是宏syscall(__NR_init_module, mod, len, opts),调用linux系统调用

    // image:指向***.ko文件内容开始,image_size***.ko模块文件大小,options为参数

     

    arch/arm/include/asm/unistd.h中,有#define __NR_init_module     (__NR_SYSCALL_BASE+128)

    linux/arch/arm/kernel/calls.S有:

    /* 125 */  CALL(sys_mprotect)

           CALL(sys_sigprocmask)

           CALL(sys_ni_syscall)     /* was sys_create_module */

           CALL(sys_init_module)

           CALL(sys_delete_module)

    可见,__NR_init_module   对应 sys_init_module,所以insmod最终调用sys_init_module

    Module.c (srckernel) 中有:

    SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)

    Syscalls.h (srcincludelinux) 中有:#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)

    #define SYSCALL_DEFINEx(x, sname, ...)             

       SYSCALL_METADATA(sname, x, __VA_ARGS__)         

       __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

     

    #define __SYSCALL_DEFINEx(x, name, ...)              

        asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); 

     

    由上述可见,SYSCALL_DEFINE3(init_module…定义了 sys_init_module函数。

     

    继续调用:

    SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)

    load_moduledo_init_moduledo_one_initcall(mod->init)ret = fn();即执行了module_init(hello_init)中定义的函数

    hello_init

     

     

    1.4.  卸载模块

    rmmod hello, 不能用 rmmod hello.ko

     

    仍然从应用层开始,rmmod是由工具箱busybox实现的,

    rmmod.c (busybox-1.22.1modutils): int rmmod_main(int argc UNUSED_PARAM, char **argv)

    bb_delete_module // 宏定义为syscall(__NR_delete_module, mod, flags),执行linux系统调用sys_delete_module

     

    insmod讨论的一样,最后会执行如下的语句

    Module.c (srckernel) 中有:

    SYSCALL_DEFINE2(delete_module, const char __user *, name_user, unsigned int, flags)

    mod->exit(); //module_exit(exit_func)定义的函数exit_func

    1.5.  细节

    srcMakefilemake modules  : 目标是modules, 编译好后需要安装在制作的文件系统

    make  modules_install  INSTALL_MOD_PATH=~/fs/ramdisk_fs

     

     

    THIS_MODULE: http://www.cnblogs.com/ziziwu/archive/2012/07/06/2578283.html

     

    insmod/modprobe    rmmod   lsmod modinfo

           insmod  hellokernel.ko  irq=100 pstr=china fish=1,2,3

           echo 200 > /sys/module/hellokernel/parameters/irq   

           echo 10,20,30 > /sys/module/hellokernel/parameters/fish

    权限问题:

      0:在/sys/module/模块名/paramters/文件

      通过修改这个文件完成对变量的内容修改

      问题:会占用内存的资源

     

      权限为0:就不会有一个文件存在,只能在模块  加载的时候才能修改

     

    1.6.  函数集合

    insmod/modprobe   

    rmmod  

    lsmod

    modinfo

     

    1.7.  文献

    http://www.cnblogs.com/fanzhidongyzby/p/3730131.html

    欢迎转载,转载时需保留作者信息,谢谢。

    邮箱:tangzhongp@163.com

    博客园地址:http://www.cnblogs.com/embedded-tzp

    Csdn博客地址:http://blog.csdn.net/xiayulewa

  • 相关阅读:
    Python3安装turtle提示错误:Command "python setup.py egg_info" failed with error code 1
    慕学生鲜xadmin登录不成功解决办法
    mysql删除后ID不连续解决办法
    Mac 安装gevent
    ubuntu 下 docker的简单使用
    python 字典的一些简单操作
    列表推导式
    Serializer字段和选项
    添加包解决一些导包问题
    css 文本换行,文本空白符处理相关 属性:white-space word-wrap word-break
  • 原文地址:https://www.cnblogs.com/embedded-tzp/p/4507988.html
Copyright © 2020-2023  润新知