• 内核模块实践实验报告


    内核模块编程学习报告

    一、内核模块的概念

        内核模块是Linux内核向外部提供的一个插口,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),简称为模块。Linux内核之所以提供模块机制,是因为它本身是一个单内核(monolithic kernel)。单内核的最大优点是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。
        模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功能。
        总之,模块是一个为内核(从某种意义上来说,内核也是一个模块)或其他内核模块提供使用功能的代码块。Linux模块可以通过静态或动态地加载到内核空间,静态加载是指在内核启动过程中加载;动态加载是指在内核运行的过程中随时加载。
        一个模块被加载到内核中时,它就成为内核代码的一部分,与其他内核代码地位是一样的。模块加载如系统时,系统修改内核中的符号表,将新加载的模块提供的资源和符号加到内核符号表中,这样使模块间可进行通信。
    

    二、内核模块的基本结构

    1)  模块加载函数(一般需要)
        在用insmod或modprobe命令加载模块时,该函数被执行。完成模块的初始化工作。
        Linux内核的模块加载函数一般用__init标识声明,模块加载函数必须以module_init(函数名)的形式被指定。该函数返回整型值,如果执行成功,则返回0,初始化失败时则返回错误编码,Linux内核当中的错误编码是负值,在<linux/errno.h>中定义。
        在Linux中,标识__init的函数在连接时放在.init.text这个区段,而且在.initcall.init中保留一份函数指针,初始化的时候内核会根据这些指针调用初始化函数,初始化结束后释放这些init区段(包括前两者)。
    2)  模块卸载函数(一般需要)
    在用rmmod或modprobe命令卸载模块时,该函数被执行。完成与加载相反的工作。
    模块的卸载函数和模块加载函数实现相反的功能,主要包括:
    ①若模块加载函数注册了XXX,则模块卸载函数注销XXX
    ②若模块加载函数动态分配了内存,则模块卸载函数释放这些内存
    ③若模块加载函数申请了硬件资源,则模块卸载函数释放这些硬件资源
    ④若模块加载函数开启了硬件资源,则模块卸载函数一定要关闭这些资源
    3)  模块许可证声明(必须)
    如果不声明,则在模块加载时会收到内核被污染的警告,一般应遵循GPL协议。
    4)  模块参数(可选)
    模块在被加载时传递给模块的值,本身应该是模块内部的全局变量。
    5)  模块导出符号(可选)
    使用模块导出符号,方便其它模块依赖于该模块,并使用模块中的变量和函数等。
    在Linux2.6的内核中,/proc/kallsyms文件对应着符号表,它记录了符号和符号对应的内存地址。对于模块而言,使用下面的宏可以导出符号。
    6)  模块信息(可选)
    模块信息则是指模块的作者信息等。
    

    三、编写内核模块的基本步骤

    • 1.根据自己的需求编写内核模块源代码
    • 2.将源代码进行编译,生成.ko文件。在编译内核模块时需要用到Makefile,如下:
      obj-m :=文件名.o
      PWD := $(shell pwd)
      KDIR:=/lib/modules/3.0.0-17-generic/build
      all:
      make -C $(KDIR) M=$(PWD) modules
      clean:
      make -C $(KDIR) M=$(PWD) clean
      • obj-m:这个变量是指定要编译的模块
      • KDIR:这是我们正在运行的操作系统内核编译目录,也就是编译模块需要的环境
      • PWD:这是当前工作路径,$(shell )是make的一个内置函数,用来执行shell命令
    • 注意:要将Makefile文件与四个内核模块源代码放在同一个文件夹中。

    四、内核模块编程

    • 1、最简单的内核模块——输出姓名与学号的myname.c
      ①首先编写输出姓名与学号的module1.c文件。代码如下:
      #include<linux/init.h>
      #include<linux/module.h>
      MODULE_LICENSE("Dual BSD/GPL"); //声明许可
      static char *name="wangjianqiao";
      static int num=20135316;
      static int __init name_init(void)
      {
      printk(KERN_ALERT "name :%s ",name); //输出姓名
      printk(KERN_ALERT "num :%d ",num); //输出学号
      return 0;
      }
      static void __exit name_exit(void)
      {
      printk(KERN_INFO"Name module exit ");
      }
      module_init(name_init);
      module_exit(name_exit);
      module_param(num,int,S_IRUGO); //可传入参数给num
      module_param(name,charp,S_IRUGO); //可传入参数给name
      //作者等信息声明
      MODULE_AUTHOR("wangjianqiao");
      MODULE_VERSION("v1.0");
      MODULE_DESCRIPTION("A simple module for testing printk and module params");
      ②编写Makefile文件,代码如下:
      obj-m :=module1.o
      PWD := $(shell pwd)
      KDIR:=/lib/modules/3.0.0-17-generic/build
      all:
      make -C $(KDIR) M=$(PWD) modules
      clean:
      make -C $(KDIR) M=$(PWD) clean
      ③make之后生成.ko文件
      ④使用命令:sudo insmod module1.ko加载模块。

    • 2、显示进程信息的模块设计。
      ①首先编写输出进程信息的module2.c文件,代码如下:
      #include<linux/kernel.h>
      #include<linux/module.h>
      #include<linux/init.h>
      #include<linux/sched.h>
      static struct task_struck *pcurrent;
      int print_current_task_info(void);
      static int __init print_init(void)
      {
      printk(KERN_INFO "prit current task info ");
      print_current_task_info();
      return 0;
      }
      static void __exit print_exit(void)
      {
      printk(KERN_INFO "Finished ");
      }
      int print_current_task_info(void)
      {
      pcurrent = get_current();
      printk(KERN_INFO "Task state: %ld ",current->state);
      printk(KERN_INFO "pid : %d ",current->pid);
      printk(KERN_INFO "tgid : %d ",current->tgid);
      printk(KERN_INFO "prio : %d ",current->prio);
      return 0;
      }
      module_init(print_init);
      module_exit(print_exit);
      ②编写Makefile文件,代码如下:
      obj-m :=module2.o
      PWD := $(shell pwd)
      KDIR:=/lib/modules/3.0.0-17-generic/build
      all:
      make -C $(KDIR) M=$(PWD) modules
      clean:
      make -C $(KDIR) M=$(PWD) clean
      ③make之后生成.ko文件
      ④使用命令:sudo insmod module2.ko加载模块。

    • 3、proc文件模块
      ①首先编写输出进程信息的module3.c文件,代码如下:
      #include <linux/module.h>
      #include <linux/init.h>
      #include <linux/kernel.h>
      #include <linux/fs.h> // for basic filesystem
      #include <linux/proc_fs.h> // for the proc filesystem
      #include <linux/seq_file.h> // for sequence files
      #include <linux/jiffies.h> // for jiffies
      #include <linux/slab.h> // for kzalloc, kfree
      #include <linux/uaccess.h> // for copy_from_user
      #define BUF_SIZE 128
      static char *str = NULL;
      static int jif_show(struct seq_file *m, void *v)
      {
      char buf[BUF_SIZE];
      int ret = 0;
      ret = sprintf(buf, "current kernel time is %llu ", (unsigned long long) get_jiffies_64());
      ret += sprintf(buf + ret, "str is %s ", str);
      seq_printf(m, "%s", buf);
      return 0; //!! must be 0, or will show nothing T.T
      }
      static ssize_t jif_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
      {
      //分配临时缓冲区
      char *tmp = kzalloc((count+1), GFP_KERNEL);
      if (!tmp)
      return -ENOMEM;
      //将用户态write的字符串拷贝到内核空间
      //copy_to|from_user(to,from,cnt)
      if (copy_from_user(tmp, buffer, count)) {
      kfree(tmp);
      return -EFAULT;
      }
      //将str的旧空间释放,然后将tmp赋值给str
      kfree(str);
      str = tmp;
      return count;
      }
      static int jif_open(struct inode *inode, struct file file)
      {
      return single_open(file, jif_show, NULL);
      }
      static const struct file_operations jif_fops =
      {
      .owner = THIS_MODULE,
      .open = jif_open,
      .read = seq_read,
      .write = jif_write,
      .llseek = seq_lseek,
      .release = single_release,
      };
      static int __init jif_init(void)
      {
      struct proc_dir_entry
      jif_file;

        	jif_file = proc_create("jif", 0, NULL, &jif_fops);
        	if (NULL == jif_file)
        	{
        	    return -ENOMEM;
        	}
        	return 0;
        }
        static void __exit jif_exit(void)
        {
        	remove_proc_entry("jif", NULL);
        	kfree(str);
        }
        module_init(jif_init);
        module_exit(jif_exit);
        MODULE_AUTHOR("aran");
        MODULE_LICENSE("GPL");
        ②编写Makefile文件,代码如下:
            obj-m :=module3.o
            PWD := $(shell pwd)
            KDIR:=/lib/modules/3.0.0-17-generic/build
            all:
            make -C $(KDIR) M=$(PWD) modules
            clean:
            make -C $(KDIR) M=$(PWD) clean
        ③make之后生成.ko文件
        ④使用命令:sudo insmod module3.ko加载模块。
      
    • 4、proc文件与进程信息模块结合
      ①首先编写输出进程信息的module4.c文件,代码如下:
      #include <linux/module.h>
      #include <linux/init.h>
      #include <linux/kernel.h>
      #include <linux/fs.h> // for basic filesystem
      #include <linux/proc_fs.h> // for the proc filesystem
      #include <linux/seq_file.h> // for sequence files
      #include <linux/jiffies.h> // for jiffies
      #include <linux/slab.h> // for kzalloc, kfree
      #include <linux/uaccess.h> // for copy_from_user
      static struct task_struct *pcurrent;
      int print_current_task_info(void);
      static char *str = NULL;
      static int jif_show(struct seq_file *m, void *v)
      {
      seq_printf(m, "current kernel time is %llu ", (unsigned long long) get_jiffies_64());
      seq_printf(m,KERN_INFO "ORINT CURRENT TASK INFO ");
      seq_printf(m,"pid tgid prio state ");
      for_each_process(pcurrent)
      {
      seq_printf(m,"%d ",pcurrent->pid);
      seq_printf(m,"%d ",pcurrent->tgid);
      seq_printf(m,"%d ",pcurrent->prio);
      seq_printf(m,"%ld ",pcurrent->state);
      }
      seq_printf(m, "str is %s ", str);
      return 0;
      }
      static ssize_t jif_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
      {
      char *tmp = kzalloc((count+1), GFP_KERNEL);
      if (!tmp)
      return -ENOMEM;
      //将用户态write的字符串拷贝到内核空间
      //copy_to|from_user(to,from,cnt)
      if (copy_from_user(tmp, buffer, count)) {
      kfree(tmp);
      return -EFAULT;
      }
      //将str的旧空间释放,然后将tmp赋值给str
      kfree(str);
      str = tmp;
      return count;
      }
      static int jif_open(struct inode *inode, struct file file)
      {
      return single_open(file, jif_show, NULL);
      }
      static const struct file_operations jif_fops =
      {
      .owner = THIS_MODULE,
      .open = jif_open,
      .read = seq_read,
      .write = jif_write,
      .llseek = seq_lseek,
      .release = single_release,
      };
      static int __init jif_init(void)
      {
      struct proc_dir_entry
      jif_file;

            jif_file = proc_create("test", 0, NULL, &jif_fops);  
            if (NULL == jif_file)  
            {  
                return -ENOMEM;  
            }  
          
            return 0;  
        }   
        static void __exit jif_exit(void)  
        { 
        printk("******************************************
      ");
            remove_proc_entry("test", NULL);  
            kfree(str);
        printk(KERN_INFO"Finished
      ");
        }  
        module_init(jif_init);  
        module_exit(jif_exit);  
        MODULE_AUTHOR("wangjianqiao");
        MODULE_LICENSE("GPL");  
        ②编写Makefile文件,代码如下:
            obj-m :=module4.o
            PWD := $(shell pwd)
            KDIR:=/lib/modules/3.0.0-17-generic/build
            all:
            make -C $(KDIR) M=$(PWD) modules
            clean:
            make -C $(KDIR) M=$(PWD) clean
        ③make之后生成.ko文件
        ④使用命令:sudo insmod module4.ko加载模块。
  • 相关阅读:
    盒模型(框模型)
    边框
    尺寸及溢出处理
    HTML标签分类
    尺寸单位和颜色的取值
    选择器的优先级
    C++ 代码模板
    LC 425. Word Squares 【lock,hard】
    LC 660. Remove 9 【lock, hard】
    LC 759. Employee Free Time 【lock, hard】
  • 原文地址:https://www.cnblogs.com/20135316wjq/p/5521608.html
Copyright © 2020-2023  润新知