【linux驅動筆記】linux模塊機制淺析
1. 模塊module
操作系統分微內核和宏內核,微內核優點,可以使操作系統僅作很少的事,其它事情如網絡處理等都作為應用程序來實現,微內核精簡的同時,必然帶來性能的下降。而linux的宏內核設計,保證了設計性能,但linux作為一個通用操作系統,必然會兼容很多硬件,而linux本身的宏內核設計,導致了如果同時兼容所有的硬件,其編譯代碼將龐大無比,為了解決這個問題,linux效仿微內核,采用了模塊這一天才思想。當內核配置make menuconfig時,可以選擇M,將驅動作為模塊來加載,其生成的文件后綴為*.ko. 即kernel object,內核對象。采用模塊設計,解決了上述問題,但是本質上來說,模塊加載進內核后,還是運行在內核態,所以驅動的bug有可能引起系統崩潰,這是在驅動設計時需要特別注意的。
1.1. 模塊實例
#include <linux/init.h>
#include <linux/module.h>
static int hellokernel_init(void)
{
printk("Hello driver!\n");
return 0;
}
static void hellokernel_exit(void)
{
printk("Bye driver!\n");
}
module_init(hellokernel_init);
module_exit(hellokernel_exit);
上面都的module_init,module_exit都干了什么,當加載模塊insmod 和卸載模塊rmmod時又干了什么,下面將說明。
1.2. module_init,module_exit
\include\linux\Init.h 中有:
#ifndef MODULE
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)
#define module_exit(x) __exitcall(x);
#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn
#else
/* Each module must use one module_init(). */
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
/* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
static inline exitcall_t __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
#endif
從上述處理可以看出,當以非模塊編譯時,module_init等通過如下路徑被執行,其被靜態編譯進內核。
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn
而上述用__define_initcall(fn, id)定義的函數都是在linux內核啟動時被執行的。
start_kernel→rest_init→kernel_init→kernel_init_freeable→do_basic_setup→do_initcalls
而如果是模塊編譯時,為動態編譯
int init_module(void) __attribute__((alias(#initfn)));
這段代碼的作用是給我們的加載函數定義一個別名,別名就是init_module
1.3. 加載模塊
將hello.ko拷貝到/home/tang/wk-tzp/prj/nfs/fs/ramdisk_fs/lib/modules/3.10.53的目錄或者其子目錄下,
必須拷貝到對應的版本號如3.10.53下,否則會出問題。
insmod hello.ko
insmod是個應用程序,在嵌入式中,這些應用包括ls,pwd等都來自于大名鼎鼎的busybox。
因此需要閱讀busybox的源代碼,才能知道insmod究竟干了什么。
在busybox-1.22.1\modutils的insmod.c中:有:
insmod_main→bb_init_module
→
…
try_to_mmap_module //通過mmap函數將 xxx.ko文件內容映射到內存中。
…
init_module(image, image_size, options) //是宏syscall(__NR_init_module, mod, len, opts),調用linux系統調用
// image:指向***.ko文件內容開始,image_size為***.ko模塊文件大小,options為參數
…
在arch/arm/include/asm/unistd.h中,有#define __NR_init_module (__NR_SYSCALL_BASE+128)
在linux/arch/arm/kernel/calls.S有:
/* 125 */ CALL(sys_mprotect)
CALL(sys_sigprocmask)
CALL(sys_ni_syscall) /* was sys_create_module */
CALL(sys_init_module)
CALL(sys_delete_module)
可見,__NR_init_module 對應 sys_init_module,所以insmod最終調用sys_init_module
在Module.c (src\kernel) 中有:
SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)
在Syscalls.h (src\include\linux) 中有:#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINEx(x, sname, ...) \
SYSCALL_METADATA(sname, x, __VA_ARGS__) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
而#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
由上述可見,SYSCALL_DEFINE3(init_module…定義了 sys_init_module函數。
繼續調用:
SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)
→load_module→do_init_module→do_one_initcall(mod->init)→ret = fn();即執行了module_init(hello_init)中定義的函數
hello_init。
1.4. 卸載模塊
rmmod hello, 不能用 rmmod hello.ko
仍然從應用層開始,rmmod是由工具箱busybox實現的,
rmmod.c (busybox-1.22.1\modutils): int rmmod_main(int argc UNUSED_PARAM, char **argv)
→bb_delete_module // 宏定義為syscall(__NR_delete_module, mod, flags),執行linux系統調用sys_delete_module
同insmod討論的一樣,最后會執行如下的語句
在Module.c (src\kernel) 中有:
SYSCALL_DEFINE2(delete_module, const char __user *, name_user, unsigned int, flags)
→mod->exit(); //即module_exit(exit_func)定義的函數exit_func
1.5. 細節
src\Makefile:make modules : 目標是modules, 編譯好后需要安裝在制作的文件系統
make modules_install INSTALL_MOD_PATH=~/fs/ramdisk_fs
THIS_MODULE: http://www.rzrgm.cn/ziziwu/archive/2012/07/06/2578283.html
insmod/modprobe rmmod lsmod modinfo
insmod hellokernel.ko irq=100 pstr=china fish=1,2,3
echo 200 > /sys/module/hellokernel/parameters/irq
echo 10,20,30 > /sys/module/hellokernel/parameters/fish
權限問題:
非0:在/sys/module/模塊名/paramters/文件
通過修改這個文件完成對變量的內容修改
問題:會占用內存的資源
權限為0:就不會有一個文件存在,只能在模塊 加載的時候才能修改
1.6. 函數集合
insmod/modprobe
rmmod
lsmod
modinfo
1.7. 文獻
http://www.rzrgm.cn/fanzhidongyzby/p/3730131.html
歡迎轉載,轉載時需保留作者信息,謝謝。
博客園地址:http://www.rzrgm.cn/embedded-tzp
Csdn博客地址:http://blog.csdn.net/xiayulewa
posted on 2015-05-16 16:08 embedded_linux 閱讀(1136) 評論(0) 收藏 舉報
浙公網安備 33010602011771號