《Linux内核原理与设计》第十周作业
教材17、19、20章学习及收获
1、在Linux以及所有unix系统中,设备被分为以下三种:块设备(blkdev)以块为单位寻址,通过块设备节点来访问;字符设备(cdev),不可寻址,通过字符设备节点访问;网络设备:对网络的访问,通过物理适配器和协议进行访问。
2、可装载内核模块:单独的二进制镜像,允许内核在运行时动态插入或删除代码。如果编译到内核中,入口点init存放在内核映像中,并且退出函数不会被包含和调用。
3、导出符号表:只有被导出后的内核函数,才可以被模块调用,代码被配置为模块的时候必须确保接口全部被导出。导出的内核符号表被看做导出的内核接口,内核API。导出指令:EXPORT_SYMBOL()
内核模块相关操作:
make modules_install //把随内核编译出来的模块安装到合适的目录/lib/modules/version/kernel下
depmod //产生内核依赖关系信息
depmod -A // 只为新模块生成依赖信息(速度更快)
insmod module.ko //以root身份运行命令
modprobe module [module parameters] //modprobe提供了模块依赖性分析、错误智能检查、错误报告以及许多其他功能和选项,需以root身份运行,强烈建议使用
rmmod module.ko //以root身份运行命令
modprobe -r module // 同模块的加载类似,modprobe命令也会卸载给定模块所依赖的相关模块
EXPORT_SYMBOL(函数名) //接在要导出的函数后面即可
EXPORT_SYMBOL_GPL(函数名) //和EXPORT_SYMBOL一样,区别在于只对标记为GPL协议的模块可见
内核模块导出符号表 示例:
编写一个导出函数的模块 module_A: test_module_A.c
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
MODULE_LICENSE("Dual BSD/GPL");
static int test_export_A_init(void)
{
/* 进入内核模块 */
printk(KERN_ALERT "*************************\\n");
printk(KERN_ALERT "ENTRY test_export_A!\\n");
printk(KERN_ALERT "*************************\\n");
printk(KERN_ALERT "\\n\\n\\n\\n\\n");
return 0;
}
static void test_export_A_exit(void)
{
/* 退出内核模块 */
printk(KERN_ALERT "*************************\\n");
printk(KERN_ALERT "EXIT test_export_A!\\n");
printk(KERN_ALERT "*************************\\n");
printk(KERN_ALERT "\\n\\n\\n\\n\\n");
}
/* 要导出的函数 */
int export_add10(int param)
{
printk(KERN_ALERT "param from other module is : %d\\n", param);
return param + 10;
}
EXPORT_SYMBOL(export_add10);
module_init(test_export_A_init);
module_exit(test_export_A_exit);
4、在安装和卸载多个模块时,要按照先安装模块A,再安装模块B;先卸载模块B,再卸载模块A的顺序进行。
5、统一设备模型的最初动机是为了实现智能的电源管理,linux 内核为了实现智能电源管理,需要建立表示系统中所有设备拓扑关系的树结构,这样在关闭电源时,可以从树的节点开始关闭。统一设备模型的核心部分就是 kobject,使得各个物理设备能够以树结构的形式组织起来。kset是kobject对象的集合体,可以所有相关的kobject置于一个kset之中,比如所有“块设备”可以放在一个表示块设备的kset中。
5、kset,ktype和sysfs都是和kobject息息相关的。
6、在编码可移植性代码时,使用不透明的数据结构不要假设该类型的长度,因为它们在不同的体系结构中可能会发生改变;长度确定的类型只能在内核空间使用, 用户空间无法使用. 用户空间有对应的变量类型, 名称前多了2个下划线。这个在敲代码时很容易出错,本周的linux实验在编译内核模块时我只敲了一个下划线,结果报错。
7、在协调数据对齐时,虽然调整结构体中元素的顺序可以减少填充的字节, 从而降低内存的消耗。但是对于内核中已有的那些结构, 千万不能随便调整其元素顺序, 因为内核中很多现存的方法都是通过元素在结构体中位置偏移来获取元素的。
8、使用 rmb() wmb() 等内存屏障来确保处理器的执行顺序。
9、起初编写的程序时常会有出现bug或者不完善的地方,这时可以通过提交补丁来改善。生成BUG或者改善代码的补丁有2种方法:
- 用diff命令创建补丁
生成patch
diff -urN linux-old/ linux-new/ > my-patch
// 比对整个内核代码文件夹
OR
diff -u linux-old/some/file linux-new/some/file > my-patch
// 比对某个文件
应用patch 应用了patch之后, linux-old 和 linux-new 中的代码就一样了
cd linux-old
patch -p1 < ../my-patch
// 这个命令是进入linux内核代码根目录内执行的
diffstat -p1 my-patch
//diffstat工具,列出补丁所引起的变更的统计(加入或移去的代码行)
- 用git命令创建补丁
提交修改的或新增的代码
git commit -a
// 提交所有修改的代码
OR
git commit linux-src/some/file.c
// 提交某个修改的代码
OR
git add linux-src/some/new-file.c
// 把新增的文件加入版本库
git commit -a
// 提交新增的文件
生成patch
git format-patch -N
//N是正整数, 这条命令生成最后N次提交产生的补丁
OR
git format-patch -1
//最后1次提交产生的补丁
应用patch
和第一种方法一样
总结:
在学习了Linux内核设计与实现这本书后,想要利用Linux系统去做一些实践,比如敲些代码等,那么加入linux社区是很有必要的,因为可以在社区中获取内核最新的消息,而且可以及时和社区内有经验的内核开发者交流经验。同时也是提交代码和讨论代码的地方, 了解社区的规则, 融入社区环境之中, 才能更好的学习内核, 体会内核开发的乐趣和成就感。
问题:
数据对齐是增强可移植性的一个重要方面,意思是数据结构的内存地址可以被4整除。char 类型只占 1个字节, 它的地址不一定能被4整除, 那么转换为 4个字节或者8个字节的 usigned long之后,为什么会导致 unsigned long 出现数据不对齐的现象?