a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 151|回复: 1

[红旗认证] 红旗认证辅导:驱动程序基本框架

[复制链接]
发表于 2012-8-4 12:16:06 | 显示全部楼层 |阅读模式
前言* q7 a: A  _  O/ n4 O
  不管是Windows还是Linux,驱动程序都扮演着重要的角色。应用程序只能通过驱动程序才能同硬件设备或系统内核通讯。Linux内核对不同的系统定义了标准的接口(API),应用程序就是通过这些标准的接口来操作内核和硬件。驱动可以被编译的内核中(build-in),也可以做为内核模块(Module)存在于内核的外面,需要的时候动态插入到内核中运行。
3 T$ D* O4 H" f/ ~  就像你学习操作系统概念时所了解的那样,Linux内核也分为几个大的部分:进程管理、内存管理、文件系统、设备控制、网络系统等。* h0 o% v5 X0 M4 N. K8 W
  这里就不对Linux系统内核的各个部分做过多的介绍了,在后面的学习中你就会逐渐地对这些概念有个更深入的了解。其实Linux内核的精髓远不止这些,对于一个Linux内核的爱好者或开发者来说,最好详细的浏览内核源代码,订阅Linux内核相关的邮件列表,或是登陆Linux开发社区。更多的信息,请登陆Linux内核官方网站:http://www.kernel.org
; U; \; U9 Q% V' u9 D" Q8 \  一个简单的驱动3 \- J: E' w* ^* _7 h$ J5 R
  下面我们来编写第一个驱动程序,它很简单,在运行时会输出‘Hello World’消息。
; l% q: \) L; V' W) \9 }# O  // hello.c
4 d2 z9 d$ p9 i) M  #include
* I. ~" z1 D# E8 O5 U4 r" Y  #include
: q" s% w. M5 e9 G  #include - b. l/ V5 |9 I/ Q; q( F# q
  static int __init hello_init(void)/ U. ^7 ]# I" t# M& u* v
  {8 _) k9 f) m" |/ E
  printk(KERN_ALERT "Hello World!\n");
0 ^* M) r& w( `6 Q  return 0;! H1 o$ m/ [, Z2 @
  }
4 }% c. M6 k7 j8 l4 y# u6 U$ ~  static void __exit hello_exit(void)
! M7 ]3 K7 Q5 \6 _  {
9 d3 y; R1 v- i& ^1 V$ z  printk(KERN_ALERT "Goodbye World!\n");3 D; ~: O3 c1 f7 g
  }
. A& N; f* r! T& L  module_init(hello_init);7 Z' a& ^5 `* J' M7 i
  module_exit(hello_exit);7 j- w) o: n: d
  MODULE_LICENSE("GPL");( b( I9 z$ {7 C1 ?
  这就是一个简单的驱动程序,它什么也没做,仅仅是输出一些信息,不过对于我们来说这已经足够了。保存这个程序,命名为hello.c。在写一个Makefile文件用来编译它,Makefile和hello.c文件保存在同一个目录下。6 z3 L$ T8 R& F+ i  S% F
  ##Makefile2 w$ l" O) v4 `. m* k
  ifneq ($(KERNELRELEASE),)
! D' y# ?9 r- Z& |  MODULE_NAME = helloworld& s: n0 z; d$ {2 e+ D, I
  $(MODULE_NAME)-objs := hello.o& ~+ T! N! A; ]7 Q# S
  obj-m := $(MODULE_NAME).o, H' z& p! o' D1 {
  else
  Y  ~' M: i/ @# k% N! ^& N  KERNEL_DIR = /lib/modules/`uname -r`/build
. ?/ o% e7 i$ k  MODULEDIR := $(shell pwd)
7 I6 t, c* w: D7 e( l+ r  .PHONY: modules
5 [1 _1 k# _4 O* V* {# N' [  default: modules  O% L$ T1 V8 V0 q. J
  modules:0 e% I2 o, @# T5 A2 U1 N! f8 Z
  make -C $(KERNEL_DIR) M=$(MODULEDIR) modules
7 Z! Q9 X8 B5 u% c& m  clean distclean:
0 M- n0 `, q  V+ `2 f1 C: R, g  rm -f *.o *.mod.c .*.*.cmd *.ko
2 v. c; {, F" B3 ]; ]$ w/ z/ n5 ^; x  rm -rf .tmp_versions
% O  V5 n* B, \3 m% c  b) n" y6 T9 @  endif
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:16:07 | 显示全部楼层

红旗认证辅导:驱动程序基本框架

  编译并运行这个模块:
/ k$ w2 t- }, y1 ~  //需要root权限来运行
: v9 c3 y; N6 ^1 ~  make
# g/ C0 {, a& D  insmod helloworld.ko
* ?/ q/ d# \! j( w) I/ t$ A1 g  rmmod helloworld.ko
% Z( o. [2 N/ Q! P/ c. f5 }  尽管我们对它的一些细节还不够了解,它确实神奇的工作了,这个Hello World信息输出到了屏幕终端上(不是VT),www.britepic.org或者系统的Kenrel log里(/var/log/messages),你可以通过运行dmesg来看到这些信息。. F1 b7 C8 D0 c/ {: O: f9 R
  驱动基础
( f2 y4 }0 p9 S) ]  我们通过分析上面的代码来了解一个驱动程序的基本概念。( h9 C0 {3 E$ a" ]) n  T
  头文件
% H7 r3 Z# ^& z8 k1 @% i3 T4 t  就像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件:& a# B# m% L0 g' O+ J8 b/ a
  #include
/ W( I) a& j  R$ v7 a  #include 5 [8 G! l7 S: Q; E8 G! x6 Y
  #include " B6 |" g* E+ D
  init.h 定义了驱动的初始化和退出相关的函数,9 x+ K9 [  x3 {! Q0 n* ?
  kernel.h 定义了经常用到的函数原型及宏定义+ O4 ~7 k. H' ?: C3 B6 z
  module.h 定义了内核模块相关的函数、变量及宏。) n/ O3 y3 L/ f% N# D, E
  初始化
5 i- F. Y, N- _. P) S  任何一个驱动都去需要提供一个初始化函数,当驱动加载到内核中时,这个初始化函数就会被自动执行,初始化的函数原型定义如下:
( \" }# k0 t/ H  typedef int (*initcall_t)(void);
3 g0 E: q% T/ l) Q) A  驱动程序是通过module_init宏来声明初始化函数的:3 d# `5 L3 D9 ^7 V
  static int __init hello_init(void)
1 {  M' O9 l" N! H  {; Q2 m$ P2 v7 i! S
  printk(KERN_ALERT "Hello World!\n");8 m$ e  J3 f, i  ~2 W
  return 0;7 |3 M9 k! ]' C0 q( Y' m3 j5 G; |
  }
# G7 m% z3 Q, X5 T5 J7 Q* r  module_init(hello_init);" v7 t; W, l  a& A
  __init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到(.init.text)段,这样当函数初始化完成后这个区域可以被清除掉以节约系统内存。Kenrel启动时看到的消息“Freeing unused kernel memory: xxxk freed”同它有关。
1 f. i: i: q) h( O; J* q1 \9 C  初始化函数是有返回值的,只有在初始化成功是才返回0,否则返回错误码(errno)。- ~$ y6 g7 `! c! F# P
  卸载8 H1 S. C6 z. H; X0 G7 i
  如果驱动程序编译成模块(动态加载)模式,那么它需要一个清理函数。当移除一个内核模块时这个函数被调用执行清理工作。清理函数的函数原型定义为:
  u0 M7 U, X% @& @  typedef void (*exitcall_t)(void);' G5 ~0 j9 t9 S: A
  驱动程序是通过module_exit宏来声明清理函数的:
8 ~6 j' w, S9 E# x9 {  static void __exit hello_exit(void)/ \& j' Y$ x4 k2 F2 T/ z; h! w  o+ P
  {& Z9 j3 R8 A, i
  printk(KERN_ALERT "Goodbye World!\n");; Q8 _2 C. B2 t7 K
  }$ F! C, E9 O0 I0 x
  module_exit(hello_exit);
' a  F6 K  z# x  同__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数,因为编译进内核的模块不需要做清理工作。显然,__init和__exit对动态加载的模块是无效的。
- T4 @3 S, q7 j* s+ u  版权信息. [" P' U- E: J: ~0 H
  Linux内核是按照GPL发布的,同样Linux的驱动程序也要提供版权信息,否则当加载到内核中是系统会给出警告信息。Hello World例子中的版权信息是GPL。
+ F8 d$ Q, H% }0 Q! D- j  MODULE_LICENSE("GPL");+ I7 L) M4 |+ v+ A8 {
  后续4 G6 A0 M6 R0 U7 F2 T4 R+ |" b
  这里你了解了一个驱动程序的基本框架,所有的驱动都会包含这些内容。这里我们没有对Linux 驱动程序的编译系统做详细的介绍,因为它相对C应用程序的编译有些复杂。Linux2.6内核采用Kbuild系统做编译,下一章你会了解到Kbuild的详细内容。
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-6 15:26 , Processed in 0.181565 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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