Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6231
  • 博文数量: 1
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2015-03-06 09:24
文章分类
文章存档

2015年(1)

我的朋友
最近访客

分类: 嵌入式

2015-03-06 09:37:48


Linux设备驱动程序(第三版)—随书笔记

第二章 建立和运行模块(上)

作者:VianoWu

时间:2014.6.25整理

关键词:

1.module_init( )  module_exit( )  insmod 

2.控制台优先等级(与代码调试信息) syslog kern.log messages debug

 

源码:linux 2.6.39  modutils

 

一、module_init() 和 module_exit()

原书示例:

#include

#include

MODULE_LICENSE(“Dual BSD/GPL”);

 

Static int hello_init(void)

{

       printk(“KERN_ALERT” helloworld\n);

       return 0;

}

Static void hello_exit(void)

{
       printk(“KERN_ALERT”Goodbye,cruel world\n);

}

 

Module_init(hello_init);

Module_exit(hello_exit);

 

*知识扩展:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 

1.     module_init( ) 与 init_module( )的关系

\linux-2.6.39\include\linux\init.h

第294行

/* 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)));

       由以上源码可知:module_init( )这个宏的参数是作为init_module( )函数的别名。即如果不是采用默认的init_module( )函数声明的初始化函数,可以通过module_init( )这个宏来代替。

       module_exit( )与exit_module( )同理。

 

2.module_init( )的具体实现

(1)若module_init()编译进内核:

\linux-2.6.39\include\linux\Init.h

第258行

/**

       * module_init() - driver initialization entry point

 * @x: function to be run at kernel boot time or module insertion

 *

 * module_init() will either be called during do_initcalls() (if

 * builtin) or at module insertion time (if a module).  There can only

 * be one per module.

 */

#define  module_init(x)  __initcall(x);

 

第212行

       #define  __initcall(fn)  device_initcall(fn)

 

第207行

       #define device_initcall(fn)  __define_initcall("6",fn,6)  //编译进内核的定义

188

/*

 * A "pure" initcall has no dependencies on anything else, and purely

 * initializes variables that couldn't be statically initialized.

 *

 * This only exists for built-in code, not for modules.

 */

 

第167行

       /* initcalls are now grouped by functionality into separate

 * subsections. Ordering inside the subsections is determined

 * by link order.

 * For backwards compatibility, initcall() puts the call in

 * the device init subsection.

 *

 * The `id' arg to __define_initcall() is needed so that multiple initcalls

 * can point at the same handler without causing duplicate-symbol build errors.

 */

#define __define_initcall(level,fn,id) \

              static initcall_t __initcall_##fn##id __used \

              __attribute__((__section__(".initcall" level ".init"))) = fn

      

       结合我之前文章《深入linux设备驱动程序内核机制--随笔--第一章 内核模块(上)http://blog.chinaunix.net/uid-26975993-id-4296257.html中,“三、EXPORT_SYMBOL的内核实现”的知识扩展部分“1.__CRC_SYMBOL(sym,sec)

中的“(1)关于“#stringification”可得:

       __define_initcall(6XXX6)等价于 __initcallXXX6 __used __attribute__ ((__section__(“.initcall6.init”))) = XXX;

       即如果模块编译进入内核中,模块的初始化函数名最终放置在一个“.initcall6.init”节中,由初始化函数调用。

 

关于linux启动调用模块初始化的整个过程:

       Linux\Init\Main.c

       456asmlinkage void __init start_kernel(void)  ->  624rest_init();

347static noinline void __init_refok rest_init(void)  ->  357kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

776static int __init kernel_init(void * unused)  -> 801do_basic_setup();

710static void __init do_basic_setup(void)  ->  718do_initcalls();

695static void __init do_initcalls(void)  ->  699for (fn = __early_initcall_end; fn < __initcall_end; fn++)

 

Linux-2.6.39\include\asm-generic\vmlinux.lds.h:

       627

#define INITCALLS                                                \

              *(.initcallearly.init)                                    \

              VMLINUX_SYMBOL(__early_initcall_end) = .;               \

             *(.initcall0.init)                                         \

             *(.initcall0s.init)                                       \

             *(.initcall1.init)                                         \

             *(.initcall1s.init)                                       \

             *(.initcall2.init)                                         \

             *(.initcall2s.init)                                       \

             *(.initcall3.init)                                         \

             *(.initcall3s.init)                                       \

             *(.initcall4.init)                                         \

             *(.initcall4s.init)                                       \

             *(.initcall5.init)                                         \

             *(.initcall5s.init)                                       \

              *(.initcallrootfs.init)                                         \

             *(.initcall6.init)                                         \

             *(.initcall6s.init)                                       \

             *(.initcall7.init)                                         \

             *(.initcall7s.init)

       其中:VMLINUX_SYMBOL 在第56 #define VMLINUX_SYMBOLsymsym

       646

              define INIT_CALLS                                                \

                     MLINUX_SYMBOL(__initcall_start) = .;                  \

                     NITCALLS                                        \

                     MLINUX_SYMBOL(__initcall_end) = .;

可得:__initcall_start/__initcall_end 都采用了当前地址值。

 

(2)若模块采用insmod方式:

Modutils

Insmod的实现:http://blog.csdn.net/wuhui_gdnt/article/details/5316531

 

3.module_exit( )的实现

\linux-2.6.39\include\linux\Init.h

268

/**

 * module_exit() - driver exit entry point

 * @x: function to be run when driver is removed

 *

 * module_exit() will wrap the driver clean-up code

 * with cleanup_module() when used with rmmod when

 * the driver is a module.  If the driver is statically

 * compiled into the kernel, module_exit() has no effect.

 * There can only be one per module.

 */

#define module_exit(x)   __exitcall(x);

       如果驱动编译进内核,则module_exit( )无作用。否则将由rmmod调用cleanup_module( )函数执行相关动作。

      

214

       #define __exitcall(fn) \

       static exitcall_t __exitcall_##fn __exit_call = fn

 

300

/* 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)));

 

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

1.printk

printk函数在linux内核中定义并且对模块可用;在insmod加载模块之后,模块被连接到内核并且可存取内核的公用符号。字符串KERN_ALERT是消息的优先级。

 

*知识扩展:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 

1.KERN_ALERT 消息优先级

(1)消息优先级

\linux-2.6.39\include\linux\printk.h

7

#define KERN_EMERG       "<0>"     /* system is unusable                   */系统用不了了

#define KERN_ALERT  "<1>"     /* action must be taken immediately     */动作马上执行

#define KERN_CRIT     "<2>"     /* critical conditions                    */临界状态

#define KERN_ERR      "<3>"     /* error conditions                */错误状态

#define KERN_WARNING   "<4>"     /* warning conditions                  */警告状态

#define KERN_NOTICE       "<5>"     /* normal but significant condition       */正常但有意义状态

#define KERN_INFO    "<6>"     /* informational                   */报告

#define KERN_DEBUG       "<7>"     /* debug-level messages               */调试信息

 

(2)控制台优先等级配置

\linux-2.6.39\include\linux\printk.h

27

#define console_loglevel (console_printk[0])

#define default_message_loglevel (console_printk[1])

#define minimum_console_loglevel (console_printk[2])

#define default_console_loglevel (console_printk[3])

 

\linux-2.6.39\kernel\printk.c

58

/* We show everything that is MORE important than this.. */

#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */

#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */

64

int console_printk[4] = {

       DEFAULT_CONSOLE_LOGLEVEL,  /* console_loglevel */

       DEFAULT_MESSAGE_LOGLEVEL,  /* default_message_loglevel */

       MINIMUM_CONSOLE_LOGLEVEL, /* minimum_console_loglevel */

       DEFAULT_CONSOLE_LOGLEVEL,  /* default_console_loglevel */

};

 

 

  在控制台输入以下命令:

#vim  /proc/sys/kernel/printk

4     //console_loglevel                 控制台日志等级:打印优先等级高于该值的消息至控制台

4     //default_message_loglevel    默认的消息优先级

1     //minimum_console_loglevel 控制台日志等级的最小值(最高优先级)

7     //defaut_console_loglevel      控制台日志的默认等级

 

proc/sys/kernel/目录下:

printk_delay :               printk消息之间的延迟毫秒数

printk_ratelimit:            printk消息之间的最小时间间隔

printgk_ratelimit_burst: 消息数量

 

(3)代码优化

在调试代码的时候,可以通过以下命令修改消息等级,查看警告信息。

#echo 5 > /proc/sys/kernel/prinitk  //将控制台等级调低,可以打印出警告信息。

/var/log 目录下的四个文件:Syslog  kern.log  messages  debug可以分别得到相应的输出信息。其中,syslogkern.log可以查看到所有的打印信息。Messages记录测试代码的456级别的打印信息,debug记录了测试代码的7级别打印信息。

       命令行输入dmesg (一个显示内核缓冲区系统控制信息的工具),打印内核开机信息和测试代码的所有打印信息。

       本次测试源码:

Printkk_test.c

16 #include

 17 #include

 18 #include

 19

 20 static int printk_level_init(void)

 21 {

 22     printk("printk level init\n");

 23     printk(KERN_EMERG"0 LEVEL\n");

 24     printk(KERN_ALERT"1 LEVEL\n");

 25     printk(KERN_CRIT"2 LEVEL\n");

 26     printk(KERN_ERR"3 LEVEL\n");

 27     printk(KERN_WARNING"4 LEVEL\n");

 28     printk(KERN_NOTICE"5 LEVEL\n");

 29     printk(KERN_INFO"6 LEVEL\n");

 30     printk(KERN_DEBUG"7 LEVEL\n");

 31     return 0;

 32 }

 33

 34 static void printk_level_exit(void)

 35 {

 36     printk("printk level exit\n");

 37     printk(KERN_EMERG"0 LEVEL\n");

 38     printk(KERN_ALERT"1 LEVEL\n");

 39     printk(KERN_CRIT"2 LEVEL\n");

 40     printk(KERN_ERR"3 LEVEL\n");

 41     printk(KERN_WARNING"4 LEVEL\n");

 42     printk(KERN_NOTICE"5 LEVEL\n");

 43     printk(KERN_INFO"6 LEVEL\n");

 44     printk(KERN_DEBUG"7 LEVEL\n");

 45 }

 46

 47 module_init(printk_level_init);

 48 module_exit(printk_level_exit);

 49

 50 MODULE_LICENSE("GPL");

 51 MODULE_AUTHOR("VIANOWU");

 

Makefile

1ifneq ($(KERNELRELEASE),)

  2

  3 obj-m := printk_test.o

  4

  5 else

  6

  7 #KDIR := /home/mier/05-Linux_Source/Linux_Kernel/linux-2.6.39  //第一路径

  8 #KDIR := /usr/src/linux-headers-2.6.32-21                                      //第二路径

  9 KDIR := /lib/modules/2.6.32-21-generic/build                                 //第三路径

 10

 11 all:

 12     make -C $(KDIR) M=$(PWD) modules

 13 clean:

 14     rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order

 15

 16 endif

 

       在命令行输入cat /var/log/syslog |tail cat /var/log/syslog |tail查看Syslogkern.log输出所有的打印信息。得到相同的结果,如下所示:

Jun 25 09:41:10 comtop-desktop kernel: [174188.360876] printk level init

Jun 25 09:41:10 comtop-desktop kernel: [174188.360887] 0 LEVEL

Jun 25 09:41:10 comtop-desktop kernel: [174188.360919] 1 LEVEL

Jun 25 09:41:10 comtop-desktop kernel: [174188.360931] 2 LEVEL

Jun 25 09:41:10 comtop-desktop kernel: [174188.360939] 3 LEVEL

Jun 25 09:41:10 comtop-desktop kernel: [174188.360948] 4 LEVEL

Jun 25 09:41:10 comtop-desktop kernel: [174188.360956] 5 LEVEL

Jun 25 09:41:10 comtop-desktop kernel: [174188.360964] 6 LEVEL

Jun 25 09:41:10 comtop-desktop kernel: [174188.360972] 7 LEVEL

       在命令行输入cat /var/log/messages |tail查看messages所记录的打印信息为:

Jun 25 09:41:10 comtop-desktop kernel: [174188.360876] printk level init

Jun 25 09:41:10 comtop-desktop kernel: [174188.360948] 4 LEVEL

Jun 25 09:41:10 comtop-desktop kernel: [174188.360956] 5 LEVEL

Jun 25 09:41:10 comtop-desktop kernel: [174188.360964] 6 LEVEL

       在命令行输入 cat /var/log/debug |tail 查看debug记录信息:

Jun 25 09:41:10 comtop-desktop kernel: [174188.360972] 7 LEVEL

 

(4)测试代码编译常见问题分析

4.1)测试代码环境:

#cat /proc/version

Linux version 2.6.32-21-generic (buildd@rothera) (gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) ) #32-Ubuntu SMP Fri Apr 16 08:10:02 UTC 2010

 

4.2)测试编译源码选择:

KDIR := /home/mier/05-Linux_Source/Linux_Kernel/linux-2.6.39  //第一路径

KDIR := /usr/src/linux-headers-2.6.32-21                                       //第二路径

KDIR := /lib/modules/2.6.32-21-generic/build                                 //第三路径

 

4.3)执行现象:

4.3.1)路径一:

# make

make -C /home/mier/05-Linux_Source/Linux_Kernel/linux-2.6.39 M=/test/printk_level modules

make[1]: Entering directory `/home/mier/05-Linux_Source/Linux_Kernel/linux-2.6.39'

  CC [M]  /test/printk_level/printk_test.o

  Building modules, stage 2.

  MODPOST 1 modules

  CC      /test/printk_level/printk_test.mod.o

  LD [M]  /test/printk_level/printk_test.ko

make[1]: Leaving directory `/home/mier/05-Linux_Source/Linux_Kernel/linux-2.6.39

 

# insmod printk_test.ko

insmod: error inserting 'printk_test.ko': -1 Invalid module format

 

错误原因分析:

模块版本与内核版本不一致;

#modinfo  printk_test.ko

filename:       printk_test.ko

author:         VIANOWU

license:        GPL

depends:       

vermagic:       2.6.39 mod_unload 686

 

4.3.2)路径二:

#make

make -C /usr/src/linux-headers-2.6.32-21 M=/test/printk_level modules

make[1]: Entering directory `/usr/src/linux-headers-2.6.32-21'

 

  WARNING: Symbol version dump /usr/src/linux-headers-2.6.32-21/Module.symvers

           is missing; modules will have no dependencies and modversions.

 

  CC [M]  /test/printk_level/printk_test.o

In file included from include/linux/gfp.h:4,

                 from include/linux/kmod.h:22,

                 from include/linux/module.h:13,

                 from /test/printk_level/printk_test.c:17:

include/linux/mmzone.h:18:26: error: linux/bounds.h: No such file or directory

include/linux/mmzone.h:258:5: warning: "MAX_NR_ZONES" is not defined

include/linux/mmzone.h:260:7: warning: "MAX_NR_ZONES" is not defined

include/linux/mmzone.h:262:7: warning: "MAX_NR_ZONES" is not defined

In file included from include/linux/gfp.h:4,

                 from include/linux/kmod.h:22,

                 from include/linux/module.h:13,

                 from /test/printk_level/printk_test.c:17:

include/linux/mmzone.h:300: error: ‘MAX_NR_ZONES’ undeclared here (not in a function)错误

 

错误原因分析:

缺少/lib/modules/’uname -r’/bulid/中的modules.symvers

 

4.3.3)路径三:

# make

make -C /lib/modules/2.6.32-21-generic/build M=/test/printk_level modules

make[1]: Entering directory `/usr/src/linux-headers-2.6.32-21-generic'

  CC [M]  /test/printk_level/printk_test.o

  Building modules, stage 2.

  MODPOST 1 modules

  CC      /test/printk_level/printk_test.mod.o

  LD [M]  /test/printk_level/printk_test.ko

make[1]: Leaving directory `/usr/src/linux-headers-2.6.32-21-generic'

 

#insmod printk_test.ko

# rmmod printk_test.ko

 

4.4)几个目录

/boot/ config-2.6.32-21-generic  这个运行系统的内核配置.Config

/lib/modules/2.6.32-21-generic/build  内核模块相关链接libc

 

(5)关于内核调试办法将独立整理。(包括syslog与syslogd、klogd  dmesg  /var/log目录下的文件)

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


阅读(1230) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:没有了

给主人留下些什么吧!~~