ldd3-2 构造和运行模块:Hello World模块笔记

实验环境:

  1. 按照之前的搭建方法,已经在Ubuntu 5.04版本上构建了linux原始的2.6.10版本内核树;
  2. GCC是用的安装镜像自带的版本;
  3. 一切准备就绪后对虚拟机做了快照,防止内核损坏;
  4. 因为Ubuntu 5.04虚拟机下编程很麻烦,所以编码和调试都不在虚拟机下运行了:

    1. 编辑在windows下运行,然后把代码文件通过Xftp传输到虚拟机里:

    2. 调试的话通过Xshell:

  5. 笔记基本是按照书上小结的标题来的,每个标题能做实验的就做实验,理论性的就小结一下,不易过多记忆,因为没代码实践,理论也理解不了,比如并发等等。

?
?

先没有操作看完了这章,因为涉及了好多额外内容,所以信息量还是比较大,所以先整体看一遍,第二遍则写代码再看一遍。

?
?

编写DEMO代码:

Hello World模块,随便建个目录ldd3-trainning,然后写个程序hello.c:

因为内核运行时是不依赖C库的,所以这里用了printk()

printk:处于内核的公用符号表中,待会到内核符号表那节再说吧

消息优先级:具有默认优先级的消息可能不会输出在控制台上,所以要显示地指定高优先级KERN_ALERT

static:因为这种函数在特定文件之外没有其他意义。因如果一个模块函数实在要对其他内核部分可见,则必须显式被导出。

?
?

编译模块:

如果想了解内核是如何构建的,阅读Documentation/kbuild目录下的文件。

----------------下面编写Makefile,一个Makefile可以管理多个实验代码,只要往里面添加就可以了

这里我拷贝随书代码的Makefile来解释:

?
?

obj-m表示 有一个模块hello.ko要从hello.o来构建,如果hello.o只由hello.c来生成则不需要写其他东西了,如果hello.o也是由file1.o file2.o生成,则要再写行:

module-objs := file1.o file2.o

?
?

好了,编译一下:

报错了,缺少个void

改正后继续编译

还是没有生成模块目标文件.ko,看错误信息,它的意思是没有hellop.c然后终止了,把makefile文件里的删了看看:

再次编译:

好了,成功生成!在构造模块之后,就是将模块装入内核。先运行一下在分析:

首先切换到超级用户下:

然后我们加载一下:

为什么没有显示hello world,根据这个答案它说控制台console和终端terminal是有区别的,代码里通过提高消息优先级为KERN_ALEART让消息输出到console上,但是当前系统console的信息不会输出到terminal上,所以

Edit /proc/sys/kernel/printk to set up the lowest priority to print in console。

书上说到调试技术那节会详细讲,这里就不要深究打印了,内核消息的传递机制将在第4章中详细讨论。用dmesg来查看console信息:

然后卸载模块rmmod hello:

或者:

可见,编写模块并不困难,难点在于理解设备并最大化其性能。

?
?

insmode:

  1. 依赖于kernel/module.c中的一个系统调用,sys_init_module给模块分配内核内存(第8章vmalloc)
  2. 系统调用再将模块正文赋值到这个内存区域;
  3. 通过内核符号表解析模块中的内核引用,最后调用模块的初始化函数;
  4. insmod可以接受一些命令行选项,并且可以在模块链接到内核之前给模块中的整型或者字符串类型变量赋值;

modprobe: 也用来将模块装到内核中

  1. 考虑是否引用了一些当前内核不存在的符号,如果有,会在当前模块搜索路径中查找定义了这些符号的其他模块(需要导出符号表),如果找到了,会同时把这些模块也装载到内核;如果此时调用insmod,则在系统日志文件中记录 unresolved symbols;
  2. 补充:只能从标准的已经安装模块目录中搜索需要装入的模块;

lsmod:

  1. 提供其他模块是不是在使用某个特定模块
  2. 读取 /proc/modules 虚拟文件来获悉
  3. 已经装载模块的信息也可以在 /sys/module 下找到

?
?

核心模块和应用程序的对比

内核:内核和C库文件是两回事,C库是供应用程序使用的。和内核相关的任何内容都在我们安装并配置好的内核源码树的头文件中声明,大多数内核相关头文件保存在include/linux和include/asm目录中:

上面其他子目录中保存的是和特定内核子系统相关的头文件。

核心模块和应用程序的差别:

  1. 应用程序就是从头到尾执行一个单个任务;

    而模块却是和内核说:"我在这儿,我初始化好了,我能做这些工作。"

  2. 应用程序退出时可以不管资源的释放或者其他的清除工作,

    但是模块的退出函数必须仔细撤销初始化函数所做的一切,否则,在系统重新引导之前某些东东就会残留在系统里;

  3. 可卸载模块化驱动程序编程当中最鲜明的特色,有助于缩短模块的开发周期;
  4. 模块仅仅被链接到内核,而不存在任何可链接的函数库;
  5. 应用程序开发过程中的段错误是无害的,并且总是可以使用调试器跟踪到源代码中的问题所在,

    而在一个内核错误即使不影响整个系统,也至少会杀死当前进程。(什么意思?不懂)

  6. 运行模式(内核空间+用户空间)模块化代码在内核空间中运行,用于扩展内核的功能。通常来讲,一个驱动程序要执行先前讲述过的两类任务:模块中某些函数作为系统调用的一部分执行,其他负责中断处理。
  7. 内核中的并发:的确,大部分应用程序出了多线程外都是顺序执行的。但是,内核代码是被被动调用的,所以自然很多人会索取资源:同一时刻,可能会有许多事情正在发生。这里的情况还是很重要的,等以后编程时在理解。
  8. 其他一些细节:

    1. 内核具有非常小的栈,所以声明大的自动变量不是一个好主意,如果我们需要打的结构,应该调用时动态分配;

    ?
    ?


当前进程

内核执行的大多数操作还是和某个特定的进程相关。

获得当前进程:访问定义在 include/asm-i386/current.h 中的全局项current

Jump To Definition可以看到task_struct定义在linux/sched.h,可以在window菜单下看到路径:

current指针指向当前正在运行的进程。在open等系统调用执行过程中,当前进程指的是调用这些系统调用的进程。我们来查看当前进程所执行文件的基本名称和PID看看,还在hello.c里面添加:

编译运行:

查看控制台:

看到了当前进程所执行的程序文件的基本名称base name:

?
?

来看下如何在模块中使用函数指针和函数调用的,从而为运行中的内核增加新的功能。

我的理解是通过赋值内核的函数指针,从而把我们实现的函数体让内核通过函数指针实现函数调用:下图使用visio绘制,书上也有

?
?

内核符号表(cat /proc/kallsyms | grep hello):

公共内核符号表(所有的全局内核项目:函数和变量的地址):insmod使用公共内核符号表来解析模块中未定义的符号。

模块层叠技术:

模块符号的可见性:模块符号需要显式导出EXPORT SYMBOL(hello_symbol);

实验验证:

编译出错:

错误说元素错了,修改下:

编译还是报错:

难道符号只能是函数名或者变量名吗?于是我改成了:

编译后,依然报相同的错误,那我改成:

编译出错:

没辙了,不能瞎猜了,到stackoverflow上搜索EXPORT_SYMBOL,搜到一个问题:那我自己定义一个函数,然后导出看看:

编译出错提示:

好像说fun没有声明,好吧,把fun定义放到导出之前:

这次终于编译通过了:

看来只能导出我自己定义的函数啊!还必须在导出之前声明。等等,我加个static看看:

还是可以通过,看来是模块初始化函数和模块退出函数不可以导出啊!不对,我把导出放到后面试试:

好吧,成功了!初始化函数和模块退出函数也可以导出没有区别!不知道为什么文件里的全局变量不可以导出为符号?

我们可以在其他模块中声明外部函数:extern int fun(void); 来为调用它做准备。

加载模块,查看内核公共符号表:

把全局变量hello_symbol设置为static在编译了看看:

好了这次hello_symbol还是有的啊,不过这次注意到前面那个符号d是小写,凡是显式导出的就是大写,不显式导出的就是小写,总之都存在,我们再导出这个全局变量看看:

编译加载后看看:

看到了,d变成大写的D了,确实啊!

对了,书上说符号EXPORT_SYMBOL(hello_symbol);会被扩展为一个特殊变量的声明,而该变量将在模块可执行文件的特殊部分(一个"ELF段")中保存,在装载时,内核通过这个段来寻找模块导出的变量:readelf -a hello.ko | grep hello_symbol

然后取消导出看看:

看到确实被扩展成了一个特殊变量 __crc__hello_symbol,其他几个我就不认识了。

?
?

【预备知识】

内核环境

内核代码

头文件:

include/linux/module.h

include/linux/init.h

?
?

以下声明习惯放在源文件的最后:

MODULE_LICENSE("GPL");

MODULE_AUTHOR();

MODULE_DESCRIPTION();

MODULE_VERSION();

MODULE_ALIAS(); ch11

MODULE_DEVICE_TABLE(); ch12

?
?

【初始化和关闭】

初始化函数

__init

软件抽象:也是可注册的设备

模块装载器

设备类型

设施:一个设备的多个功能

注册:注册多个设施完成一个设备的注册

内核符号表

注册模块提供的设施

【清除函数】

【模块装载竞争】

【模块参数】

?
?

时间: 2024-09-29 09:26:06

ldd3-2 构造和运行模块:Hello World模块笔记的相关文章

ldd3-2 构造和运行模块:环境搭建

编程环境搭建: 因为ubuntu 12.04的内核版本已经是3.x,而目前一些讲解内核驱动的书都是2.6.x. 嵌入式开发的版本一般都是基于3.14移植的,因为嵌入式是跑在开发板上的,所以开发驱动没有问题.但是教材的例子一般都是基于PC机的2.6.x版本,虽然内核内部接口相对稳定,但是我也不太清楚.至于低版本的内核驱动是否直接运行在高版本的Ubuntu上,我也不是太了解这里面的对应关系.为了排除无关的干扰,决定虚拟机安装个Ubuntu 10.04 32位,它对应的内核版本是2.6.32-21,这

LINUX设备驱动程序笔记(二)构造和运行模块

         <一>:设置测试系统 首先准备好一个内核源码树,构造一个新内核,然后安装到自己的系统中.           <二>:HelloWorld模块 #include <linux/init.h> //定义了驱动的初始化和退出相关的函数 #include <linux/module.h> //定义了内核模块相关的函数.变量及宏 MODULE_LICENSE("Dual BSD/GPL"); //该宏告诉内核,该模块采用自由许可

VBA中四种自动运行的宏以及模块的含义

在Excel的“标准模块”中可以创建4种自动运行的宏,它们分别是Auto_Open(打开工作 簿时自动运行), Auto_Close, Auto_Activate,  Auto_Deactivate.这些自动运行的宏是为了与Excel5和95兼容而保留下来的.现在可以使用工作簿的Open,Close,Activate,Deactivate 事件来代替它们. 那么,什么是“标准模块”呢?其实就是我们在VBE中插入的那个模块,微软称它为“标准模块”.而其余的三类:Microsoft Excel对象,

Apache Ranger 1.1.0源码导入IDEA并运行调试security-admin web模块

前言 Apache Ranger是什么,它是一个为Hadoop平台提供了全面的数据安全访问控制及监控的集中式管理框架,Apache顶级项目.不废话了,其实本篇没那么高大上,就是一步步教你如何将Ranger源码导入到IDEA,并运行调试其web模块. 导入源码 第一步当然是下载源码,这里选用了最新版1.1.0 git clone https://github.com/apache/ranger.git git checkout release-ranger-1.1.0 编译,这里选择编译全部,当然

day5模块学习 -- os模块学习

python基础之模块之os模块 os模块 os模块的作用: os,语义为操作系统,所以肯定就是操作系统相关的功能了,可以处理文件和目录这些我们日常手动需要做的操作,就比如说:显示当前目录下所有文件/删除某个文件/获取文件大小-- 另外,os模块不受平台限制,也就是说:当我们要在linux中显示当前命令时就要用到pwd命令,而Windows中cmd命令行下就要用到这个,额...我擦,我还真不知道,(甭管怎么着,肯定不是pwd),这时候我们使用python中os模块的os.path.abspath

python下通过os模块和shutil模块进行文件处理方式

python下通过os模块和shutil模块进行文件处理方式 得到当前工作目录路径:os.getcwd() 获取指定目录下的所有文件和目录名:os.listdir(dir) 删除文件:os.remove(file) 删除多个目录:os.removedirs(r"/home") 检测路径是否为文件:os.path.isfile(path) 检测路径是否为目录:os.path.isdir(path) 判断是否为绝对路径:os.path.isabs(path) 检测路径是否存在:os.pat

Python基础(12)_python模块之sys模块、logging模块、序列化json模块、pickle模块、shelve模块

5.sys模块 sys.argv 命令行参数List,第一个元素是程序本身路径 sys.exit(n) 退出程序,正常退出时exit(0) sys.version 获取Python解释程序的版本信息 sys.maxint 最大的Int值 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 sys.platform 返回操作系统平台名称 5.1 使用sys.argv进行登录判断,跳过 i/o阻塞 #使用sys.argv进行登录判断,跳过 i/o阻塞 import s

python基础知识8——模块1——自定义模块和第三方开源模块

模块的认识 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块. 如:os 是系统相关的模块:file是文件操作相关的模块 模块分为三种: 自定义模块 内置模块 第三方开源模块 自定义模块 1.定义模块 2.导入模块 Python之所以应用越来越广泛,在一定程度上也

模块之使用模块

你已经学习了如何在你的程序中定义一次函数而重用代码.如果你想要在其他程序中重用很多函数,那么你该如何编写程序呢?你可能已经猜到了,答案是使用模块.模块基本上就是一个包含了所有你定义的函数和变量的文件.为了在其他程序中重用模块,模块的文件名必须以.py为扩展名.模块可以从其他程序 输入 以便利用它的功能.这也是我们使用Python标准库的方法.首先,我们将学习如何使用标准库模块.使用sys模块~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 例8.1 使用sys模块 #!/usr

Python模块 实现过渡性模块重载

本文是在阅读Python 学习手册后  感觉比较不错的一个实现模块重载的一个模块,该模块可以实现对已经加载在运行程序中的模块实现重新加载,并且该模块可以递归的实现对要重新加载的模块内所引用的其它模块的重新加载. """ reloadall.py: transitively reload nested modules """ import types from imp import reload def status(module): print