第一个简单的内核编程实验:hello.c

  内核编程入门,就以最为简单的hello.c为例。
        环境:Redhat   9.0,内核版本2.4.20-8。
 
        虽然现在2.6.x的内核很早就就发布了,但是毕竟很多公司还在使用2.4.x的内核。作为新手,从2.4.x的内核入手是可行的。原因有如下几条:
        (1)2.4.x比较成熟。可能你遇到的绝大多数问题,网上都有解决方案。在这个过程中,你可以节省大量的时间,同时还可以对比网上的解决方案,加深认识,总结解决问题的方法,调整自己的学习方法和思路。
        (2)事物的发展总不可能是一蹴而就的。了解发展的历程,对深入理解问题有很大的好处。所以在2.4.x的内核的基础上学习2.6.x的内核,就能够体会到2.6.x的内核在哪些方面要出色,或者为什么要采取这种改进技术。相信理论清晰了,即时2.6.x的内核也会容易上手。
 
        下面总结了第一个内核程序hello.c的学习过程。
 
(一)第一阶段:尽量简单
 


#define   MODULE
#include   <linux/module.h>

int   init_module(void)
{
                printk( "Hello   World!/n ");
                return   0;
}

void   cleanup_module(void)
{
                printk( "Goodbye!/n ");
}
 

执行,出现错误一:

[root@lqm   drivers]#   gcc   -c   hello.c
[root@lqm   drivers]#   insmod   hello.o
hello.o:   kernel-module   version   mismatch
                hello.o   was   compiled   for   kernel   version   2.4.20
                while   this   kernel   is   version   2.4.20-8.
 

        这是因为内核源代码版本和编译器版本不一致造成的。

(1)编译器版本/usr/include/linux/version.h

#define   UTS_RELEASE   "2.4.20 "
#define   LINUX_VERSION_CODE   132116
#define   KERNEL_VERSION(a,b,c)   (((a)   < <   16)   +   ((b)   < <   8)   +   (c))
 

(2)内核源代码版本/usr/src/linux-2.4.20-8/include/linux/version.h

/usr/src/linux-2.4.20-8/include/linux
[root@lqm   linux]#   cat   version.h
#include   <linux/rhconfig.h>
#if   defined(__module__smp)
#define   UTS_RELEASE   "2.4.20-8smp "
#elif   defined(__module__BOOT)
#define   UTS_RELEASE   "2.4.20-8BOOT "
#elif   defined(__module__bigmem)
#define   UTS_RELEASE   "2.4.20-8bigmem "
#else
#define   UTS_RELEASE   "2.4.20-8 "
#endif
#define   LINUX_VERSION_CODE   132116
#define   KERNEL_VERSION(a,b,c)   (((a)   < <   16)   +   ((b)   < <   8)   +   (c))
 

      可以采取修改编译器版本号与内核源代码版本号一致的办法来解决这个问题,即修改/usr/include/linux/version.h中   #define   UTS_RELEASE   "2.4.20 "
 


为   #define   UTS_RELEASE   "2.4.20-8 "
 


执行,出现错误二:

[root@lqm   drivers]#   gcc   -c   hello.c
[root@lqm   drivers]#   insmod   hello.o
Warning:   loading   hello.o   will   taint   the   kernel:   no   license
    See   http://www.tux.org/lkml/#export-tainted   for   information   about   tainted   modules
Module   hello   loaded,   with   warnings
[root@lqm   drivers]#   tail   -n   1   /var/log/messages
Jan   30   12:02:08   lqm   kernel:   Hello  
 

          也就是说出现了no   license的警告。GNU的软件需要有GPL,所以修改源代码如下:



#define   MODULE
#include   <linux/module.h>

MODULE_LICENSE( "GPL ");

int   init_module(void)
{
                printk( "Hello   World!/n ");
                return   0;
}

void   cleanup_module(void)
{
                printk( "Goodbye!/n ");
}
 

        这时没有错误了。写了一个脚本,测试流程自动化:

#!/bin/bash

gcc   -c   hello.c
sleep   1

insmod   hello.o   &&   echo   -e   "Instal   module   -   hello.o/n "
sleep   1
tail   -n   1   /var/log/messages
lsmod   |   grep   "hello "   &&   echo   -e   "Module   hello   has   instaled/n "

rmmod   hello   &&   echo   -e   "Remove   module   -   hello/n "
sleep   1
tail   -n   1   /var/log/messages
lsmod   |   grep   "hello "   ||   echo   "Module   hello   has   removed "
 

执行结果如下:

[root@lqm   hello]#   ./run

Instal   module   -   hello.o  

Jan   30   13:31:29   lqm   kernel:   Hello   World!  

hello   748   0   (unused)  

Module   hello   has   instaled  

Remove   module   -   hello  

Jan   30   13:31:30   lqm   kernel:   Goodbye!  

Module   hello   has   removed
 

(二)第二阶段:完善,深入一点



#ifndef   __KERNEL__
                #define   __KERNEL__
#endif
#ifndef   MODULE
                #define   MODULE
#endif

#include   <linux/kernel.h>  
#include   <linux/module.h>

MODULE_LICENSE( "GPL ");

static   int   init_module(void)
{
                printk( "Hello,   world!/n ");

                return   0;
}

static   void   cleanup_module(void)
{
                printk( "Goodbye!/n ");
}
 

写Makefile文件如下:

#   Kernel   Programming
#   Shandong   University,   Linqingmin

#   The   path   of   kernel   source   code
INCLUDEDIR   =   /usr/src/linux-2.4.20-8/include/

#   Compiler
CC   =   gcc

#   Options
CFLAGS   =   -D__KERNEL__   -DMODULE   -O   -Wall   -I$(INCLUDEDIR)

#   Target
OBJS   =   hello.o

all:   $(OBJS)

$(OBJS):   hello.c   /usr/include/linux/version.h
                $(CC)   $(CFLAGS)   -c   $ <

install:
                insmod   $(OBJS)

uninstall:
                rmmod   hello

.PHONY:   clean
clean:
                rm   -f   *.o
 

        写Makefile时应该注意,不要用空格来代替 <TAB> 。否则会出现错误:missing   separator.   Stop.

修改执行脚本run:

#!/bin/bash

#   The   first   step
make   &&   make   install   &&   echo   -e   "Instal   module   -   hello.o/n "
sleep   1
tail   -n   1   /var/log/messages
lsmod   |   grep   "hello "   &&   echo   -e   "Module   hello   has   instaled/n "

#   The   second   step
make   uninstall   &&   echo   -e   "Remove   module   -   hello/n "
sleep   1
tail   -n   1   /var/log/messages
lsmod   |   grep   "hello "   ||   echo   "Module   hello   has   removed "

#   The   last   step
make   clean
 

执行结果如下:

[root@lqm   hello]#   ./run
gcc   -D__KERNEL__   -DMODULE   -O   -Wall   -I/usr/src/linux-2.4.20-8/include/   -c   hello.c
hello.c:18:   warning:   `init_module '   defined   but   not   used
hello.c:25:   warning:   `cleanup_module '   defined   but   not   used
insmod   hello.o
Instal   module   -   hello.o

Jan   31   13:40:23   lqm   kernel:   Hello,  
hello   728   0   (unused)
Module   hello   has   instaled

rmmod   hello
Remove   module   -   hello

Jan   31   13:40:24   lqm   kernel:  
Module   hello   has   removed
rm   -f   *.o 
 
(三)第三阶段:总结

1、一个内核模块至少应该包括两个函数:

(1)init_module:模块插入内核时调用

(2)cleanup_module:模块移除时调用

        这个简单的程序就是只实现了这两个函数,而且只做了打印信息的工作,没有使用价值。典型情况下,init_module为内核中的某些东西注册一个句柄,相当于模块初始化的工作。cleanup_module则是撤销模块前期的处理工作,使模块得以安全卸载。

2、insmod实现动态加载模块。在当前OS上,动态加载模块以测试硬件等,避免了繁琐的工作。但是,在这种情况下,会出现版本不匹配的情况。另外,要分清楚内核源代码路径和编译器路径的不同,知道在编译时该指定那个路径。第二阶段开始出现过几个错误都是因为默认的路径是编译器路径,而不是内核源代码路径。体会内核模块化带来的好处!

3、应用Make工具来管理项目。即使小,也要训练。在2.4内核和2.6内核下,Makefile的编写会有所不同。只是语法形式的不同,先深入掌握一种,另一种注意一下应该可以避免犯错误。

        继续努力!

2007/02/01补记



 
#ifndef   __KERNEL__
                #define   __KERNEL__
#endif
#ifndef   MODULE
                #define   MODULE
#endif

#include   <linux/kernel.h>  
#include   <linux/module.h>
#include   <linux/init.h>  

MODULE_LICENSE( "GPL ");  

static   int   __init   hello_init(void)
{
                printk( " <1> Hello,   world!/n ");

                return   0;
}

static   void   __exit   hello_exit(void)
{
                printk( " <1> Goodbye!/n ");
}

module_init(hello_init);
module_exit(hello_exit);
 

 

#   Kernel   Programming
#   Shandong   University,   Linqingmin

#   The   path   of   kernel   source   code
KERNELDIR   =   /usr/src/linux-2.4.20-8

#   Compiler
CC   =   gcc

#   Options
CFLAGS   =   -D__KERNEL__   -DMODULE   -I$(KERNELDIR)/include   -O   -Wall

ifdef   CONFIG_SMP
                CFLAGS   +=   -D__SMP__   -DSMP
endif

#   OBJS
OBJS   =   hello.c

#   Target
TARGET   =   hello.o

$(TARGET):   hello.c
                $(CC)   $(CFLAGS)   -c   $ <

install:
                insmod   $(TARGET)

uninstall:
                rmmod   hello

.PHONY:   clean
clean:
                rm   -f   *.o
 

 

#!/bin/bash

#   The   first   step
make   &&   make   install   &&   echo   -e   "Instal   module   -   hello.o/n "
sleep   1
lsmod   |   grep   "hello "   &&   echo   -e   "Module   hello   has   instaled/n "

#   The   second   step
make   uninstall   &&   echo   -e   "Remove   module   -   hello/n "
sleep   1
lsmod   |   grep   "hello "   ||   echo   "Module   hello   has   removed "

#   The   last   step
make   clean
 

执行结果:

gcc   -D__KERNEL__   -DMODULE   -I/usr/src/linux-2.4.20-8/include   -O   -Wall   -c   hello.c
insmod   hello.o
Hello,World!
Instal   module   -   hello.o

hello   776   0   (unused)
Module   hello   has   instaled

rmmod   hello
Goodbye!
Remove   module   -   hello

Module   hello   has   removed
rm   -f   *.o
 

说明:

        这个版本采取了显式的初始化和清除函数,显然无论是调试的便利还是编程风格,都比前几个版本要好。这种机制具体描述如下:如果你将模块初始化函数命名为my_init(而不是init_module),将清除函数命名为my_exit,则可以使用下面两行来进行标记(通常在源文件的末尾)

module_init(my_init);

module_exit(my_exit);

注意必须包含头文件 <linux/init.h> 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值