第一個簡單的內核編程實驗: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> 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章