2004年07月30日

Driver porting: compiling external modules

This article is part of the LWN Porting Drivers to 2.6 series.

The 2.5 development series saw extensive changes to the kernel build mechanism and the complete replacement of the module loading code. One result of these changes is that compiling loadable modules has gotten a bit more complicated. In the 2.4 days, a makefile for an external module could be put together in just about any old way; typically a form like the following was used:

 KERNELDIR = /usr/src/linux CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O

 all: module.o

Real-world makefiles, of course, tended to be a bit more complicated, but the job of creating a loadable module was handled in a single, simple compilation step. All you really needed was a handy set of kernel headers to compile against.

With the 2.6 kernel, you still need those headers. You also, however, need a configured kernel source tree and a set of makefile rules describing how modules are built. There’s a few reasons for this:

  • The new module loader needs to have some extra symbols defined at compilation time. Among other things, it needs to have the KBUILD_BASENAME and KBUILD_MODNAME symbols defined.

  • All loadable modules now need to go through a linking step – even those which are built from a single source file. The link brings in init/vermagic.o from the kernel source tree; this object creates a special section in the loadable module describing the environment in which it was built. (后面的一偏文章会谈到,2.6的模块的版本识别。这种识别对模块的兼容性要求极高,有gcc版本、SMP与否、强占内核与否、kernel version)It includes the compiler version used, whether the kernel was built for SMP, whether kernel preemption is enabled, the architecture which was compiled for, and, of course, the kernel version. A difference in any of these parameters can render a module incompatible with a given running kernel; rather than fail in mysterious ways, the new module loader opts to detect these compatibilities and refuse to load the module.

    As of this writing (2.5.59), the “vermagic” scheme is fallible in that it assumes a match between the kernel’s vermagic.o file and the way the module is being built. That will normally be the case, but people who change compiler versions or perform some sort of compilation trickery could get burned.

  • The new symbol versioning scheme (“modversions”) requires a separate post-compile processing step and yet another linkable object to hold the symbol checksums.

One could certainly, with some effort, write a new, standalone makefile which would handle the above issues. But that solution, along with being a pain, is also brittle; as soon as the module build process changes again, the makefile will break. Eventually that process will stabilize, but, for a while, further changes are almost guaranteed.

So, now that you are convinced that you want to use the kernel build system for external modules, how is that to be done? The first step is to learn how kernel makefiles work in general; makefiles.txt from a recent kernel’s Documentation/kbuild directory is recommended reading. The makefile magic needed for a simple kernel module is minimal, however. In fact, for a single-file module, a single-line makefile will suffice:

 obj-m := module.o

(where module is replaced with the actual name of the resulting module, of course). The kernel build system, on seeing that declaration, will compile module.o from module.c, link it with vermagic.o, and leave the result in module.ko, which can then be loaded into the kernel.

A multi-file module is almost as easy:

 obj-m := module.o module-objs := file1.o file2.o 

In this case, file1.c and file2.c will be compiled, then linked into module.ko.

Of course, all this assumes that you can get the kernel build system to read and deal with your makefile. The magic command to make that happen is something like the following:

 make -C /path/to/source SUBDIRS=$PWD modules

Where /path/to/source is the path to the source directory for the (configured and built) target kernel. This command causes make to head over to the kernel source to find the top-level makefile; it then moves back to the original directory to build the module of interest.

Of course, typing that command could get tiresome after a while. A trick posted by Gerd Knorr can make things a little easier, though. By looking for a symbol defined by the kernel build process, a makefile can determine whether it has been read directly, or by way of the kernel build system. So the following will build a module against the source for the currently running kernel:

 ifneq ($(KERNELRELEASE),) obj-m := module.o

 else KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) // 这个是什么?

 default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules endif

Now a simple “make” will suffice. The makefile will be read twice; the first time it will simply invoke the kernel build system, while the actual work will get done in the second pass. A makefile written in this way is simple, and it should be robust with regard to kernel build changes.

Driver porting: hello world

This article is part of the LWN Porting Drivers to 2.6 series.

Your editor is currently in the middle of porting the example source from Linux Device Drivers, Second Edition to the 2.5 kernel. This work is, of course, just the beginning of the rather larger job of updating the whole book. This article is the first in what will, hopefully, be a series describing what is required to make this code work again. The series will thus, with luck, be useful as a guide to how to port drivers to the new kernel API.

The obvious place to start in this sort of exercise, of course, is the classic “hello world” program, which, in this context, is implemented as a kernel module. The 2.4 version of this module looked like:

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

 int init_module(void)  {  printk(KERN_INFO "Hello, world\n");  return 0;  }

 void cleanup_module(void)  {  printk(KERN_INFO "Goodbye cruel world\n");  }

One would not expect that something this simple and useless would require much in the way of changes, but, in fact, this module will not quite work in a 2.5 kernel. So what do we have to do to fix it up?

The first change is relatively insignificant; the first line:

 #define MODULE // 不需要了,理由如下:

is no longer necessary, since the kernel build system (which you really should use now, see the next article) defines it for you.

The biggest problem with this module, however, is that you have to explicitly declare your initialization and cleanup functions with module_init and module_exit, which are found in <linux/init.h>. You really should have done that for 2.4 as well, but you could get away without it as long as you used the names init_module and cleanup_module. You can still sort of get away with it (though you may have to ignore some compiler warnings), but the new module code broke this way of doing things once, and could do so again. It’s really time to bite the bullet and do things right.

With these changes, “hello world” now looks like:

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

 static int hello_init(void) { printk(KERN_ALERT "Hello, world\n"); return 0; }

 static void hello_exit(void) { printk(KERN_ALERT "Goodbye, cruel world\n"); }

 module_init(hello_init); module_exit(hello_exit);
2004年07月27日

显然,要同时满足上述三个目的,如果采用传统的 C/S 结构对MiniGUI-Threads 进行改造,应该不难实现。但前面提到的传统 C/S 结构的缺陷却无法避免。经过对 PDA 等嵌入式系统的分析,我们发现,某些 PDA 产品具有运行多个任务的能力,但同一时刻在屏幕上进行绘制的程序,一般不会超过两个。因此,只要确保将这两个进程的绘制相互隔离,就不需要采用复杂的 C/S 结构处理多个进程窗口之间的互相剪切。也就是说,在这种产品中,如果采用基于传统 C/S 结构的多窗口系统,实际是一种浪费。

/*    

 *   确实是种浪费,多窗口对于PDA或者手持设备来说根本没必要,

 *   如PALM、PPC都是单窗体撑满屏幕。PPC的后台则可以多任务运行。

 *   当然我设想是:多一个模态的MsgBox,提供系统警告信息,这样屏幕上

 *   就只有三个layer:Manager Layer, Current Process UI Layer, Message UI Layer

 *   他们按Z轴增续排列。

 */

有了上述认识,我们对 MiniGUI-Threads 进行了如下简化设计:

  • 每个进程维护自己的主窗口 Z 序,同一进程创建的主窗口之间互相剪切。也就是说,除这个进程只有一个线程,只有一个消息循环之外,它与原有的 MiniGUI 版本之间没有任何区别。每个进程在进行屏幕绘制时,不需要考虑其他进程。/* 省去多窗口的屏幕维护 */
  • 建立一个简单的客户/服务器体系,但确保最小化进程间的数据复制功能。因此,在服务器和客户之间传递的数据仅限于输入设备的输入数据,以及客户和服务器之间的某些请求和响应数据。 /* 这样的体系可以在uC/OS的内核帮助下,在Manager Layer/User Layer之间完成 */
  • 有一个服务器进程(mginit),它负责初始化一些输入设备,并且通过 UNIX Domain 套接字将输入设备的消息发送到前台的 MiniGUI-Lite 客户进程。
  • 服务器和客户被分别限定在屏幕的某两个不相交矩形内进行绘制,同一时刻,只能有一个客户及服务器进行屏幕绘制。其他客户可继续运行,但屏幕输入被屏蔽。服务器可以利用 API 接口将某个客户切换到前台。同时,服务器和客户之间采用信号和 System V 信号量进行同步。
  • 服务器还采用 System V IPC 机制提供一些资源的共享,包括位图、图标、鼠标、字体等等,以便减少实际内存的消耗。

从传统 C/S 窗口系统的角度看,MiniGUI-Lite 的这种设计,无法处理达到完整的多窗口支持,这的确是一个结构设计上的不足和缺陷。不过,这实际是 MiniGUI-Lite 不同于其他窗口系统的一个特征。因为处理每个进程之间的互相剪切问题,将导致客户和服务器之间的通讯量大大增加,但实际上在许多嵌入式系统当中这种处理是没有必要的。在类似 PDA 的嵌入式系统中,往往各个程序启动后,就独占屏幕进行绘制输出,其他程序根本就没有必要知道它现在的窗口被别的进程剪切了,因为它根本就没有机会输出到屏幕上。所以,在 MiniGUI-Lite 当中,当一个进程成为最顶层程序时,服务器会保证其输出正常,而当有新的程序成为最顶层程序时,服务器也会保证其他程序不能输出到屏幕上。但这些进程依然在正常执行着,不过,服务器只向最顶层的程序发送外部事件消息。

2004年07月26日
Who ever own a iPod  would have a panic that all the round devices of it is too expencive! not exceptive of me!    

   The wire control’s high cost forces me to buy a second hand one.  I also want to have a iPod’s case, a cool one i found is a Belkin Neoprene Sport Case which want’s me $24.99. Today i went to the BaiNaoHui to see see. A seller give me 198RMB. What’s the lowest price u can offer? i ask. 140RMB! that’s cheaper than that i see on net. When i want to another, 120RMB he says. Oh that’s yipe! I can not believe , let him to check agatin. That’s true, how he can sell it at this so lower price, He mumu to me: Don’t tell anybody, it’s his game!

   Oh, My God! that’s so ….! A case, just a case They want to grep how many money from it ! 120RMB is sure be higher than import price, how much is the import price? 140RMB, if i bought, they gain more than 20RMB from it. Just a case, u see. It’s so panic!