内核模块-基本概念

简介: 内核模块-基本概念

Linux内核模块简介


众所周知,Linux系统已经成了应用最为广泛的操作系统。小到日常经常用到的电子设备,比如,智能手机、智能手表等,大到互联网公司的数据中心,都可以见到Linux的身影。Linux之所以如此成功,其中一个比较关键的因素就是她非常优秀的内核设计。这种设计可以使其适应各种场景的需求,加上天生开源的特质,想不火都难。


今天,本文所要介绍的就是Linux内核相关开发中一个比较基础的概念:Linux Kernel Module(LKM)。大家,可能都知道,Linux是一个跨平台的操作系统,市面上几乎所有的硬件平台,Linux应该都是支持的。即便,暂时不支持,也可以经过少许的适配,就可以使Linux系统完美的运行起来。那么,LKM在硬件平台的适配中就扮演着十分钟的角色,有数据表面,Linux内核源代码中有超过50%都是设备驱动相关的代码,而设备驱动一般都是以内核模块的形式存在。


Linux内核是宏内核,简单来说就是内核就是一个整体,可以理解成为一个可执行程序,内核包揽了所有的系统功能,比如,进程调度、内核管理、文件系统、设备管理等等。这样势必出现一个问题,那就是如果开启所有的功能,会使得内核变得十分的臃肿。而LKM,可以从一定程度上,解决这个问题。通过将内核功能模块实现为LKM,可以对内核进行精简,只保留最为核心的功能,然后,其他功能模块,可以随着后续需求,进行动态的加载。Linux内核通过LKM可以动态的管理内核功能,使内核功能就要U盘一样,支持热插拔,这样做不仅可以节约内核运行时的硬件要求,同时可以满足内核功能的实时扩展性。


Linux内核实现了一整套管理LKM的框架,用来加载、卸载、管理模块间的关系。LKM本质上就是一段可以被链接到内核空间的代码片段,其格式和内核一样,都是ELF格式,其为内核提供访问该模块的入口和出口,而后内核模块管理框架,就会自动的将其链接到内核空间,从而完成内核功能的扩展。


下面,就通过一个实例,简单的介绍一下LKM。


基本实例


LKM既然是内核代码片段,那么其运行的环境就是内核环境,而内核环境有其独特开发标准和开发库。Linux用户空间开发一般的都是基于libc库,比如大名鼎鼎的glibc。而内核环境不依赖于glibc,所以LKM不会依赖于libc开发,而是基于内核开发框架。下面以一个实例来说明一下,LKM的开发方式。


与大多数入门程序示例一样,这里我们也是实现一个打印“hello,world"的LKM。


#include <linux/init.h>                                                                                                                                                                                                                                                                 
  #include <linux/module.h>
  static int __init module_init_func(void)
  {
      printk("hello, world.\n");
      return 0;
  }
  static void __exit module_exit_func(void)
  {
      return;
  }
  MODULE_LICENSE("GPL v2");
  MODULE_VERSION("v0.1");
  MODULE_AUTHOR("lhl");
  MODULE_DESCRIPTION("LKM, helloworld.");
  module_init(module_init_func);
  module_exit(module_exit_func);


上面代码的基本含义如下:


  • init.h和module.h是编写内核模块必须包含的两个头文件,里面包含LKM基本的数据结构和主要的API。


  • module_init_func和module_exit_func分别对应LKM的入口和出口,而module_init和module_exit用于将这两个函数注册到内核中。


  • 宏MODULE_LICENSE用于声明内核模块所遵循的协议,一般有GPL、GPL v2、GPL and additional rights、Dual BSD/GPL等。


  • 宏MODULE_VERSION用于声明内核模块的版本信息。


  • 宏MODULE_AUTHOR用于声明LKM的作者信息。


  • 宏MODULE_DESCRIPTION用于声明LKM的基本功能描述。


内核模块的编译分为两种:单独编译和嵌入到内核代码中编译。一般情况下,LKM的编译选择单独编译,这种编译方式有两种优势:


  • 编译速度快;


  • 便于代码的维护。


单独编译需要提前构建内核编译环境,构建方式有两种:


  • 下载内核源码,编译完成后,执行make modules_install安装。


  • 如果是Ubuntu系统,可以使用sudo apt-get install linux-headers-$(uname -r)安装适配于当前内核的构建环境。


内核模块的构建需要编写Makefile文件,我们把上面的helloworld保存为hellword.c,相应的Makefile文件的内容如下:


obj-m:=helloworld.o
KERS :=/lib/modules/$(shell uname -r)/build                                                                                                                                                                                                                                             
all:
  make -C $(KERS) M=$(shell pwd) modules
clean:
  make -C $(KERS) M=$(shell pwd) clean


上述代码的基本含义如下:


  • obj-m:用于定义内核模块的二进制名称,注意,该文件名称与内核模块的c程序文件名进行对应。


  • KERS:用于声明内核编译环境的位置。


  • all:用于定义目标文件的编译规则,make -C (KERS)表示进入内核编译环境,M=(KERS)表示进入内核编译环境,M=(KERS)M=(shell pwd)指定当前LKM的路径,modules表示进行LKM的编译。


  • clean:与all目标类似,这里用于定义清理LKM编译过程中产生的文件,包括.ko文件。


好了,准备好了编译内核模块的所有文件,执行make,可以看到下面的编译过程。 $ make


make -C /lib/modules/5.3.0-45-generic/build M=/home/lhl/learn/LKM/example modules
make[1]: 进入目录“/usr/src/linux-headers-5.3.0-45-generic”
  CC [M]  /home/lhl/learn/LKM/example/helloworld.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/lhl/learn/LKM/example/helloworld.mod.o
  LD [M]  /home/lhl/learn/LKM/example/helloworld.ko
make[1]: 离开目录“/usr/src/linux-headers-5.3.0-45-generic”


helloworld.ko就是最终生成的内核模块,使用sudo insmod helloworold.ko,安装该内核模块,然后,执行dmesg,可以看到内核日志:


$ sudo insmode helloworld.ko
$ dmesg
......
[18813.852210] hello, world.
......
$ sudo rmmod helloworld.ko


通过sudo rmmod helloworld,可以卸载内核模块。


还可以通过,modinfo可以查看LKM的基本信息:


$ modinfo helloworld.ko
filename:       /home/lhl/learn/LKM/example/helloworld.ko
description:    LKM, helloworld.
author:         lhl
version:        v0.1
license:        GPL v2
srcversion:     44ADBB2239DA10943034DBE
depends:        
retpoline:      Y
name:           helloworld
vermagic:       5.3.0-45-generic SMP mod_unload 


总结


上面就是对于LKM的简单介绍,对于一个小白足可以完成一个内核模块,后续文章还会深入分析LKM的具体的实现机制。


相关文章
|
4月前
|
Linux 编译器 调度
【Linux】对共享库加载问题的深入理解——基本原理概述
【Linux】对共享库加载问题的深入理解——基本原理概述
|
安全 网络协议 Linux
Linux驱动开发 设备驱动的基本概念
Linux驱动开发 设备驱动的基本概念
|
6月前
|
消息中间件 Linux
Linux进程间通信(IPC)教程 Linux共享内存介绍:介绍POSIX共享内存的基本概念、用途和编程实践
Linux进程间通信(IPC)教程 Linux共享内存介绍:介绍POSIX共享内存的基本概念、用途和编程实践
139 2
|
6月前
|
Ubuntu Linux 芯片
Linux 驱动开发基础知识——设备树的语法驱动开发基础知识(九)
Linux 驱动开发基础知识——设备树的语法驱动开发基础知识(九)
254 1
Linux 驱动开发基础知识——设备树的语法驱动开发基础知识(九)
|
6月前
|
存储 缓存 监控
Linux 文件系统全面解析:从基本原理到实际应用
Linux 文件系统全面解析:从基本原理到实际应用
1010 0
|
6月前
|
Linux 芯片 开发者
Linux 驱动开发基础知识——内核对设备树的处理与使用(十)
Linux 驱动开发基础知识——内核对设备树的处理与使用(十)
869 0
Linux 驱动开发基础知识——内核对设备树的处理与使用(十)
|
消息中间件 Linux 调度
【Linux】进程间通信的有关基础概念
【Linux】进程间通信的有关基础概念
|
6月前
|
Linux API 芯片
Linux 系统的中断子系统基本框架(一)
Linux 系统的中断子系统基本框架(一)
110 0
|
存储 算法 网络协议
Linux内核之旅:揭秘关键的数据结构设计
Linux内核之旅:揭秘关键的数据结构设计
|
缓存 负载均衡 监控
Linux第八章之进程概念
计算机管理硬件1. 描述起来,用struct结构体2. 组织起来,用链表或其他高效的数据结构。
119 0