a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 89|回复: 0

[红旗认证] 驱动程序开发:驱动程序基本框架

[复制链接]
发表于 2012-8-4 12:16:06 | 显示全部楼层 |阅读模式
  前言3 J' g- P" y  |; f0 H
  不管是Windows还是Linux,驱动程序都扮演着重要的角色。应用程序只能通过驱动程序才能同硬件设备或系统内核通讯。Linux内核对不同的系统定义了标准的接口(API),应用程序就是通过这些标准的接口来操作内核和硬件。驱动可以被编译的内核中(build-in),也可以做为内核模块(Module)存在于内核的外面,需要的时候动态插入到内核中运行。
3 {9 i8 c2 I/ [0 D& W  就像你学习操作系统概念时所了解的那样,Linux内核也分为几个大的部分:进程管理、内存管理、文件系统、设备控制、网络系统等。0 {" L3 j9 v  b  o  B$ [
  这里就不对Linux系统内核的各个部分做过多的介绍了,在后面的学习中你就会逐渐地对这些概念有个更深入的了解。其实Linux内核的精髓远不止这些,对于一个Linux内核的爱好者或开发者来说,最好详细的浏览内核源代码,订阅Linux内核相关的邮件列表,或是登陆Linux开发社区。更多的信息,请登陆Linux内核官方网站:http://www.kernel.org
. V/ H8 G: y8 x7 |  V) S4 e1 y  一个简单的驱动
( o' ~- _) T% \5 T$ a2 y0 \! I  下面我们来编写第一个驱动程序,它很简单,在运行时会输出‘Hello World’消息。
- w# {; n0 T2 g! ~, J% Y  // hello.c
# Q: g9 n, y6 b9 N- ~  t  #include
  X! T6 o3 w% H' v: S# k$ @9 A% Y  #include + [+ U% x4 Z5 t1 H3 \6 Z* J
  #include
0 _% h5 j3 r# m  static int __init hello_init(void)( p# D. O, E: U* ~+ {; u2 M
  {8 _2 M. N; `6 k8 g( W; J! i
  printk(KERN_ALERT "Hello World!\n");: d2 v. `% L# ^5 S" E" n
  return 0;; X! z- s1 t1 I, r( A5 Z
  }
; u: E$ [0 y; c  static void __exit hello_exit(void)4 c- ?- E* C( s7 k# Z
  {
, ^1 G+ p3 R+ @. {0 p  printk(KERN_ALERT "Goodbye World!\n");
8 ]; U$ |/ Z: z+ {% D2 G  }
- E0 T4 P* Y4 K4 w$ y  module_init(hello_init);
0 \$ G- C7 ^; n, O% A5 e: O  module_exit(hello_exit);6 f4 L3 p: E- o! t5 E7 S+ ?
  MODULE_LICENSE("GPL");
; t& w. V% C$ `0 P+ o+ v  这就是一个简单的驱动程序,它什么也没做,仅仅是输出一些信息,不过对于我们来说这已经足够了。保存这个程序,命名为hello.c。在写一个Makefile文件用来编译它,Makefile和hello.c文件保存在同一个目录下。3 d) h: B' N7 n4 A$ {/ E& |+ h% V
  ##Makefile
1 ^- r2 W; k# o$ I  ifneq ($(KERNELRELEASE),)0 E  ?  v  K& b3 Q9 q6 ?8 X3 y
  MODULE_NAME = helloworld; k* F: l/ q. ~# K5 S% |
  $(MODULE_NAME)-objs := hello.o
) j2 q$ i0 ]/ D  obj-m := $(MODULE_NAME).o
9 X) j" P4 o4 y! @! y! e* D- `) W  else
' J$ F$ i* _  ?/ E, z  KERNEL_DIR = /lib/modules/`uname -r`/build1 B/ G  {# j. u6 Z4 {
  MODULEDIR := $(shell pwd)
: k! ?, P! z$ O5 N% T0 o  .PHONY: modules
7 s: c/ l1 n! l5 E0 L: E3 ^  default: modules6 \: g& |& d' g2 q0 P
  modules:  l% c9 `7 D4 N7 T& K+ N
  make -C $(KERNEL_DIR) M=$(MODULEDIR) modules, a$ p" j* a$ y6 f  j4 }
  clean distclean:
/ @! S/ w! L8 e* y, B2 ^  rm -f *.o *.mod.c .*.*.cmd *.ko
& N9 u" L( m0 z- p- Y  rm -rf .tmp_versions* r, b2 v& J& x4 F
  endif* K3 r" U+ d! S6 u% _
  编译并运行这个模块:! U4 F+ {7 K* H2 i
  //需要root权限来运行
* K# C5 K3 |& Q6 g0 P  make
) Q: x: w, p$ ?, T" `: L' C  insmod helloworld.ko& O$ J6 G# A( y# e+ {( ?6 U: W5 A' X
  rmmod helloworld.ko
; @; L# D+ |7 K; z" |. g  i- o  尽管我们对它的一些细节还不够了解,它确实神奇的工作了,这个Hello World信息输出到了屏幕终端上(不是VT),www.britepic.org或者系统的Kenrel log里(/var/log/messages),你可以通过运行dmesg来看到这些信息。4 K/ ~0 v. l! Z3 h
  驱动基础. p- `  y' s! E$ }* [
  我们通过分析上面的代码来了解一个驱动程序的基本概念。8 k6 }2 w6 D0 c/ n. F4 R. w
  头文件; o; Q' Y: m7 M3 D, W
  就像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件:4 p/ A7 x9 B; G
  #include
7 U- m# N8 M! p0 g5 m  #include
4 ~- W( a5 s1 H3 A* n1 w+ g  #include 1 a+ a/ e* {) [6 K) E( u
  init.h 定义了驱动的初始化和退出相关的函数,
, V3 B6 N  k6 _  kernel.h 定义了经常用到的函数原型及宏定义7 j8 r7 r. Q4 c, g% M
  module.h 定义了内核模块相关的函数、变量及宏。* k5 J) X/ ^. a: r# ]+ [+ ^6 j! [
  初始化2 V3 [4 {; d/ N8 E- V
  任何一个驱动都去需要提供一个初始化函数,当驱动加载到内核中时,这个初始化函数就会被自动执行,初始化的函数原型定义如下:
( c+ W. G& C! s! Q% F: T  typedef int (*initcall_t)(void);* C/ x  v! _$ u
  驱动程序是通过module_init宏来声明初始化函数的:
: H9 e. a/ P, e; N  static int __init hello_init(void), t0 ?8 Y# C  g; J8 ~; {+ @& ?0 b( j
  {% [4 f1 y/ [3 M3 W5 G  U! R
  printk(KERN_ALERT "Hello World!\n");
  M- j. r" ^9 U8 c# G  return 0;. s: [$ A+ y2 @5 F3 g9 E
  }
# x/ E* ~. c, ]: d2 P/ t  module_init(hello_init);
) P0 j. j2 B9 V1 v$ j' K  __init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到(.init.text)段,这样当函数初始化完成后这个区域可以被清除掉以节约系统内存。Kenrel启动时看到的消息“Freeing unused kernel memory: xxxk freed”同它有关。
9 L+ Y' X6 n: I6 K4 I  M% d  初始化函数是有返回值的,只有在初始化成功是才返回0,否则返回错误码(errno)。& K9 M. Z. f* ~4 y! V1 o3 u
  卸载
/ t' G5 J- N4 o, Y' V  如果驱动程序编译成模块(动态加载)模式,那么它需要一个清理函数。当移除一个内核模块时这个函数被调用执行清理工作。清理函数的函数原型定义为:
3 z: u* ~5 j: O0 u  z! r* z. I  typedef void (*exitcall_t)(void);
# v$ D& i1 \, J# P3 B5 W  驱动程序是通过module_exit宏来声明清理函数的:
7 E' U. m3 S( G4 e7 N( P  static void __exit hello_exit(void)
( B: C) v- i3 L$ E  {
' l) X( K# f4 w6 u5 U2 _  printk(KERN_ALERT "Goodbye World!\n");
6 O- j0 U+ `, D' w- Y2 R7 r  }4 j$ k. p1 c% M* L! U' @: A$ b6 F9 }
  module_exit(hello_exit);1 K; v- A+ W& g8 W& ^) Z
  同__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数,因为编译进内核的模块不需要做清理工作。显然,__init和__exit对动态加载的模块是无效的。' g: U: G9 O* g7 f- r/ C  Y  U4 V2 E4 z
  版权信息0 d' x) c4 d+ L
  Linux内核是按照GPL发布的,同样Linux的驱动程序也要提供版权信息,否则当加载到内核中是系统会给出警告信息。Hello World例子中的版权信息是GPL。9 x$ y- D3 @% `; u' a+ X# b) D7 F
  MODULE_LICENSE("GPL");! x4 W, ?2 l) p& y
  后续# ~0 E* [0 A: F) R; i
  这里你了解了一个驱动程序的基本框架,所有的驱动都会包含这些内容。这里我们没有对Linux 驱动程序的编译系统做详细的介绍,因为它相对C应用程序的编译有些复杂。Linux2.6内核采用Kbuild系统做编译,下一章你会了解到Kbuild的详细内容。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-4-29 19:30 , Processed in 0.193730 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表