zoukankan      html  css  js  c++  java
  • insmod[转]


    在Linux下,驱动程序是内核的一部分,运行在内核态下,你可以将驱动静态的和内核编译在一起,这样的缺点是内核会比较大,而且如果驱动出错,会导致整个系统崩溃;也可以以module的方式编译,在需要的时候动态的载入。如果你编译过内核,应该记得在make menuconfig中,选项前面是可以选择和的,就分别表示"编译到内核中"和"编译成模块"。
    .
    下面介绍下模块,一个简单的“helloworld module”如下所示:

    /*  hello-1.c - The simplest kernel module.
     */
    #include
      /* Needed by all modules */
    #include
      /* Needed for KERN_ALERT */
     
    int init_module(void)
    {
       printk("<1>Hello world 1.\n");
     
       // A non 0 return means init_module failed; module can't be loaded.
       return 0;
    }
     
    void cleanup_module(void)
    {
      printk(KERN_ALERT "Goodbye world 1.\n");
    }

    其中,init_module函数是加载模块时会被调用的,一般作一些初始化的工作;cleanup_module函数是卸载模块时会被调用的,做一些清理的工作。因为模块是运行在内核态的,你自然不能使用库函数,因此要打印信息,需要使用printk函数而不是printf函数。另外,你可以使用任意函数名(只要同内核函数名不冲突)来替换init_module和cleanup_module这两个函数名,但必须使用module_init(初始化函数名),module_exit(卸载时函数名)这两个宏来声明一下,也就是说,下面这个模块和上面的模块是等价的:

    /*  hello-1.c - The simplest kernel module.
     */
    #include
      /* Needed by all modules */
    #include
      /* Needed for KERN_ALERT */
     
    int helloworld(void)
    {
       printk("<1>Hello world 1.\n");
     
       // A non 0 return means init_module failed; module can't be loaded.
       return 0;
    }
     
    void goodbyeworld(void)
    {
      printk(KERN_ALERT "Goodbye world 1.\n");
    }
    module_init(helloworld);
    module_exit(goodbyeworld);

    .
    编译和加载一个模块也很容易:

    从上面的图可以看到,我们通过make编译生成一个模块文件".ko"之后,使用"insmod"命令来加载模块,那么“insmod”具体做了什么呢?
    .
    下面让我们来介绍下insmod这个工具:
    insmod是linux下加载模块的工具,路径一般是/sbin/insmod,当你调用这个工具后,它的工作基本如下:

    • 在用户空间打开待安装的module
    • 调用query_module()系统调用询问无法落实的符号在内核或其他模块中的地址
    • 链接操作,落实模块中的符号引用
    • 调用create_module()系统调用在内核中创建module数据结构,并申请所需的内核空间
    • 调用init_module()系统调用将链接好的module映像装入内核空间,然后调用模块中的init_module()函数(注意:这里面的两个init_module函数不一样,一个是系统调用,一个是你写在模块里面的函数

    .
    内核导出的符号清单可以由下面的命令来查看:

    more /proc/k[all]syms
    c0400000 T _text
    c0400000 T startup_32
    c040007b t default_entry
    c04000d0 T startup_32_smp
    c0400152 t checkCPUtype
    c04001d3 t is486
    c04001da t is386
    c0400247 t check_x87
    c040027a t setup_idt
    …
    c06adb25 T printk
    …

    .
    从已加载模块中卸载模块使用的是“rmmod”,"rmmod"所做工作如下:

    • 调用delete_module()系统调用释放模块的module结构,同时释放模块所占的内核空间
    • 调用模块中的cleanup_module()的函数

    .

    那么,一般情况下,驱动程序会在init_module()和cleanu_module()函数中做些什么呢?

     

    • init_module(): 向内核登记本模块中一些包含着函数指针的数据结构(file_operations)
    • cleanup_module(): 向内核撤销本模块提供的数据结构的登记,使内核在模块拆卸后不至于再企图访问这些数据结构

    .

    一个字符型驱动的注册函数如下:

    #include
     
    #inlcude
     
    int register_chrdev(unsigned int major, const char *name,
                    struct file_operations *fops);
    • major: 设备的主设备号,若为空则系统动态分配
    • name: 设备名
    • fops: 函数指针结构,各个调用的入口
    • 操作成功,设备名出现在/proc/devices文件

    至于怎么创建一个有设备名和设备号的文件,可以通过man mknod获取信息。
    .
    file_operantions的结构如下:

    Linux-2.6.27.25
    struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*fsync) (struct file *, struct dentry *, int datasync);
    ……
    }

    .
    此外,初始化部分还负责为设备驱动申请系统资源,如:内存,时钟,中断,I/O端口等。

  • 相关阅读:
    UVALive 7509 Dome and Steles
    HDU 5884 Sort
    Gym 101194H Great Cells
    HDU 5451 Best Solver
    HDU 5883 The Best Path
    HDU 5875 Function
    卡特兰数
    UVa 11729 Commando War 突击战
    UVa 11292 The Dragon of Loowater 勇者斗恶龙
    Spark Scala Flink版本对应关系
  • 原文地址:https://www.cnblogs.com/amaoxiaozhu/p/2950002.html
Copyright © 2011-2022 走看看