第02章 建立和运行模块
时间:2010-12-21 来源:purple_river
源码代码:
(1) 驱动模块不能使用lib(内核本身应该可以使用C库吧),
只能使用 内核导出的函数.--- cat /proc/kallsyms
有个宏,可以让你的符号进入全局不符号表kallsyms中.
这个宏叫做: 从这个文件中看到的: drivers\usb\core\message.c
EXPORT_SYMBOL_GPL(usb_interrupt_msg);
EXPORT_SYMBOL(usb_control_msg);
(2) 以双下划线(__)开始的函数名: 这样标志的函数名通常是一个低层的接口组件, 应当小心使用. 本质上讲, 双下划线告诉程序员:" 如果你调用这个函数, 确信你知道你在做什么."
(3) 内核代码不能做浮点算术: 使能浮点将要求内核在每次进出内核空间的时候保存和恢复浮点处理器的状态 -- 至少, 在某些体系上. 在这种情况下, 内核代码真的没有必要包含浮点,额外的负担不值得.
(4) printk输出的查看方法:
1) # dmesg
[21209.273006] Hello, world
[21216.776618] Goodbye, cruel world
2) # cat /var/log/kern.log
3) # cat /var/log/syslog
4) Ubuntu 10.04上系统管理工具有一个: 系统日志查看器
可以查看各种log日志,包括内核输出的信息。
另外:开一个窗口 # cat /proc/kmsg
这个方法不好,有时可以看到输出,有时看不到,没弄明白.
(5)其实 KERN_ALERT 就是串 "<1>" ,下面代码可以测试:
printk("<0>" "Hello, world \n KERN_ALERT=" KERN_ALERT"\n");
(6) 驱动实际上,在进程的上下文中运行的,内核代码可以通过使用 current 来使用进程特定的信息:
#include <linux/sched.h>
printk(KERN_INFO "The process is \"%s\" (pid %i)\n", current->comm, current->pid); (7)insmod: 它依赖一个在 kernel/module.c 中定义的系统调用: sys_init_module()
函数 sys_init_module 分配内核内存来存放模块...
(8)modprobe 工具: 自动安装依赖的模块.
(9)一旦 insmod 之后,/sys/module/就会创建一个目录: /sys/module/hello
# ls /sys/module/hello/ -l
drwxr-xr-x 2 root root 0 2010-12-19 11:26 holders
-r--r--r-- 1 root root 4096 2010-12-19 11:26 initstate
drwxr-xr-x 2 root root 0 2010-12-19 11:26 notes
-r--r--r-- 1 root root 4096 2010-12-19 11:26 refcnt
drwxr-xr-x 2 root root 0 2010-12-19 11:26 sections
-r--r--r-- 1 root root 4096 2010-12-19 11:26 srcversion
查看一下这个文件: srcversion
原来和 # modinfo hello.ko
filename: hello.ko
license: Dual BSD/GPL
srcversion: 73EA635C082B50A3AAEA1E3
depends:
vermagic: 2.6.32-16-generic SMP mod_unload modversions 586
中的 srcversion 完全相同!
include/linux/version.h 中存放内核版本信息:
#define LINUX_VERSION_CODE 132632
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
看来这个目录: include/linux/ 太重要了,很多关键头文件都在这里啊!
值得再提一下: cat /sys/module/hello/sections/__mcount_loc
0xf8312124
模块中的 __mcount_loc 段,被加载到 地址 0xf8312124 处.
/proc/modules 中存放当前加载的模块,以及加载地址.
(10) 模块参数
安装模块时候,可以这样: insmod hellop howmany=10 whom="Mom"
这样声明即可:
static char *whom = "world";
static int howmany = 1;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);
看来由 insmod 为这些参数赋值啊. 模块参数支持许多类型:
bool
invbool 反布尔型
charp 一个字符指针值. 内存为用户提供的字串分配, 指针因此设置.
int
long
short
uint
ulong
ushort
输入参数注意:
1)串必须用 双引号包围,且不能有空格!
2)“=”前后不能留有空格
(11) 查看一下 kernel/module.c | sys_init_module() asmlinkage long sys_init_module(
void __user *umod, unsigned long len, const char __user *uargs)
谁来调用sys_init_module() ? 搜索发现只有一处: kernel/sys_ni.c,形如:
cond_syscall(sys_init_module);
全是 cond_syscall()....很像是 系统sysycall调用相关...
#define cond_syscall(x) asm(".weak\t" #x "\n" #x "\t=\tsys_ni_syscall")
展开看看: cond_syscall(sys_init_module);
===> asm(".weak\t" #x "\n" #x "\t=\tsys_ni_syscall")
===> .weak sys_init_module
sys_init_module = sys_ni_syscall 宏定义中技巧: 一个#,表示后面的参数变为串
不懂 .weak 伪指令啥意思: 看样子很像汇编的编译指示符,查看 as.info: " 7.116 `.weak NAMES' "
因此,cond_syscall(sys_init_module);翻译成下面的宏汇编指令序列: .weak sys_init_module
sys_init_module =sys_ni_syscall
如果符号sys_init_module不存在,则创建该符号,并且把地址定义为 sys_ni_syscall
在我们这里,实际上,sys_init_module是存在的,不要创建.
回头看看,这个宏的名字: cond_syscall(),就是 Contidtional Syscall!
总之,cond_syscall(X)就是确保X这个符号存在,如果不存在,就创建,并且地址等于sys_ni_syscall, 那么 sys_ni_syscall 是何许人也? Non-implemented system calls,在 kernel/sys_ni.c 中实现,该函数直接返回 -ENOSYS,就是处理未定义的系统调用的.
在 hello.c 中,做了测试代码.学习 .weak 的用法.
说到底,就是学会了这句话的用法:
asm(".weak test_m\n test_m = my_con_call");
意思是,我首先定义了符号my_con_call,这是个函数指针.
然后,我创建一个符号test_m,这个符号的地址等于 my_con_call
终于体会了什么是别名--- alias !
kernel/scall32-o32.S | syscalltable 中有下面片段:
...
sys sys_ni_syscall 0 /* was create_module */
sys sys_init_module 5
sys sys_delete_module 1
...
看到了吧,sys_init_module 原来是个系统调用啊!
|
只能使用 内核导出的函数.--- cat /proc/kallsyms
有个宏,可以让你的符号进入全局不符号表kallsyms中.
这个宏叫做: 从这个文件中看到的: drivers\usb\core\message.c
EXPORT_SYMBOL_GPL(usb_interrupt_msg);
EXPORT_SYMBOL(usb_control_msg);
(2) 以双下划线(__)开始的函数名: 这样标志的函数名通常是一个低层的接口组件, 应当小心使用. 本质上讲, 双下划线告诉程序员:" 如果你调用这个函数, 确信你知道你在做什么."
(3) 内核代码不能做浮点算术: 使能浮点将要求内核在每次进出内核空间的时候保存和恢复浮点处理器的状态 -- 至少, 在某些体系上. 在这种情况下, 内核代码真的没有必要包含浮点,额外的负担不值得.
(4) printk输出的查看方法:
1) # dmesg
[21209.273006] Hello, world
[21216.776618] Goodbye, cruel world
2) # cat /var/log/kern.log
3) # cat /var/log/syslog
4) Ubuntu 10.04上系统管理工具有一个: 系统日志查看器
可以查看各种log日志,包括内核输出的信息。
另外:开一个窗口 # cat /proc/kmsg
这个方法不好,有时可以看到输出,有时看不到,没弄明白.
(5)其实 KERN_ALERT 就是串 "<1>" ,下面代码可以测试:
printk("<0>" "Hello, world \n KERN_ALERT=" KERN_ALERT"\n");
(6) 驱动实际上,在进程的上下文中运行的,内核代码可以通过使用 current 来使用进程特定的信息:
#include <linux/sched.h>
printk(KERN_INFO "The process is \"%s\" (pid %i)\n", current->comm, current->pid); (7)insmod: 它依赖一个在 kernel/module.c 中定义的系统调用: sys_init_module()
函数 sys_init_module 分配内核内存来存放模块...
(8)modprobe 工具: 自动安装依赖的模块.
(9)一旦 insmod 之后,/sys/module/就会创建一个目录: /sys/module/hello
# ls /sys/module/hello/ -l
drwxr-xr-x 2 root root 0 2010-12-19 11:26 holders
-r--r--r-- 1 root root 4096 2010-12-19 11:26 initstate
drwxr-xr-x 2 root root 0 2010-12-19 11:26 notes
-r--r--r-- 1 root root 4096 2010-12-19 11:26 refcnt
drwxr-xr-x 2 root root 0 2010-12-19 11:26 sections
-r--r--r-- 1 root root 4096 2010-12-19 11:26 srcversion
查看一下这个文件: srcversion
原来和 # modinfo hello.ko
filename: hello.ko
license: Dual BSD/GPL
srcversion: 73EA635C082B50A3AAEA1E3
depends:
vermagic: 2.6.32-16-generic SMP mod_unload modversions 586
中的 srcversion 完全相同!
include/linux/version.h 中存放内核版本信息:
#define LINUX_VERSION_CODE 132632
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
看来这个目录: include/linux/ 太重要了,很多关键头文件都在这里啊!
值得再提一下: cat /sys/module/hello/sections/__mcount_loc
0xf8312124
模块中的 __mcount_loc 段,被加载到 地址 0xf8312124 处.
/proc/modules 中存放当前加载的模块,以及加载地址.
(10) 模块参数
安装模块时候,可以这样: insmod hellop howmany=10 whom="Mom"
这样声明即可:
static char *whom = "world";
static int howmany = 1;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);
看来由 insmod 为这些参数赋值啊. 模块参数支持许多类型:
bool
invbool 反布尔型
charp 一个字符指针值. 内存为用户提供的字串分配, 指针因此设置.
int
long
short
uint
ulong
ushort
输入参数注意:
1)串必须用 双引号包围,且不能有空格!
2)“=”前后不能留有空格
(11) 查看一下 kernel/module.c | sys_init_module() asmlinkage long sys_init_module(
void __user *umod, unsigned long len, const char __user *uargs)
谁来调用sys_init_module() ? 搜索发现只有一处: kernel/sys_ni.c,形如:
cond_syscall(sys_init_module);
全是 cond_syscall()....很像是 系统sysycall调用相关...
#define cond_syscall(x) asm(".weak\t" #x "\n" #x "\t=\tsys_ni_syscall")
展开看看: cond_syscall(sys_init_module);
===> asm(".weak\t" #x "\n" #x "\t=\tsys_ni_syscall")
===> .weak sys_init_module
sys_init_module = sys_ni_syscall 宏定义中技巧: 一个#,表示后面的参数变为串
不懂 .weak 伪指令啥意思: 看样子很像汇编的编译指示符,查看 as.info: " 7.116 `.weak NAMES' "
因此,cond_syscall(sys_init_module);翻译成下面的宏汇编指令序列: .weak sys_init_module
sys_init_module =sys_ni_syscall
如果符号sys_init_module不存在,则创建该符号,并且把地址定义为 sys_ni_syscall
在我们这里,实际上,sys_init_module是存在的,不要创建.
回头看看,这个宏的名字: cond_syscall(),就是 Contidtional Syscall!
总之,cond_syscall(X)就是确保X这个符号存在,如果不存在,就创建,并且地址等于sys_ni_syscall, 那么 sys_ni_syscall 是何许人也? Non-implemented system calls,在 kernel/sys_ni.c 中实现,该函数直接返回 -ENOSYS,就是处理未定义的系统调用的.
在 hello.c 中,做了测试代码.学习 .weak 的用法.
说到底,就是学会了这句话的用法:
asm(".weak test_m\n test_m = my_con_call");
意思是,我首先定义了符号my_con_call,这是个函数指针.
然后,我创建一个符号test_m,这个符号的地址等于 my_con_call
终于体会了什么是别名--- alias !
kernel/scall32-o32.S | syscalltable 中有下面片段:
...
sys sys_ni_syscall 0 /* was create_module */
sys sys_init_module 5
sys sys_delete_module 1
...
看到了吧,sys_init_module 原来是个系统调用啊!
相关阅读 更多 +