当前位置:文档之家› linux设备驱动开发实例.

linux设备驱动开发实例.

linux设备驱动开发实例.
linux设备驱动开发实例.

·1 基础知识

设备驱动的作用:任何一个计算机系统的运行都是系统中软硬件协作的结果,没有硬件的软件是空中楼阁,没有软件的硬件则是一堆废铁。硬件是底层基础,是所有软件得以运行的平台,代码最终会落实为硬件上的组合逻辑与时序逻辑;软件则实现了具体应用,它按照各种不同的业务需求而设计,满足了用户的需求。软件和硬件不应该相互渗透到对方的领域。为了尽快的完成设计,应用软件工程师不想也不必关心硬件,而硬件工程师也难掌握软件编程语言。例如,应用软件工程师在使用printf函数输出信息的时候,他不用知道具体底层是怎么实现将信息输出到显示屏或者串口上的。也就是说,应用软件工程师需要看到的是一个没有硬件的纯粹的软件世界,他不用知道底层的硬件原理,而是通过一些通用的接口函数就可以操作。那么这些接口函数是怎么提供给上层的软件工程师的呢,那这个艰巨的任务就落在了驱动工程师的头上。

驱动程序在Linux 内核里扮演着特殊的角色. 它们是截然不同的"黑盒子", 使硬件的特殊的一部分响应定义好的内部编程接口. 它们完全隐藏了设备工作的细节. 用户的活动通过一套标准化的调用来进行, 这些调用与特别的驱动是独立的; 设备驱动的角色就是将这些调用映射到作用于实际硬件的和设备相关的操作上. 这个编程接口是这样, 驱动可以与内核的其他部分分开建立, 并在需要的时候在运行时"插入". 这种模块化使得Linux 驱动易写, 以致于目前有几百个驱动可用.总而言之:驱动就是linux给用户操作硬件提供的一个接口,它是一个存在于应用程序和实际设备间的软件层。

由此可见,设备驱动充当了硬件和应用软件之间的纽带,它使得应用软件只需要调用系统的一些应用编程接口(API)就可以让硬件去完成相应的操作。在没有操作系统的情况下,我们可以根据硬件设备的特点利用汇编和C语言的混合编程来操作硬件。在有操作系统的情况下,设备驱动的结构则由相应的操作系统定义,然后驱动工程师必须按照相应的架构设计设备驱动,这样,设备驱动才能良好的整合到操作系统的内核中。

并不是所有的计算机系统都一定要运行操作系统,在许多情况下操作系统是不必要的。对于一些功能比较单一、控制并不复杂的系统,如公交车刷卡机、电冰箱、微波炉、简单的手机和小灵通等,并不需要多任务调度、文件系统、内存管理等复杂功能,用单任务架构完全可以很好的支持它们的工作。一个无限循环中夹杂对设备中断的检测或者对设备的轮询是这种系统中软件的典型架构。在这样的系统中,虽然不存在操作系统,但是设备驱动往往是必须存在的。一般情况下,对每一种设备驱动都会定义

那么如果计算机系统中包含了操作系统,那么设备驱动会变得怎样呢?首先我们要知道操作系统的功能。操作系统的主要功能:进程管理、内存管理、文件管理、设备控制和网络管理等。操作系统的存在它要求设备驱动附加更多的代码和功能,把单一的“驱动硬件设备行动”变成了操作系统内与硬件交互的模块,它对外呈现为操作系统的API。此时我们需要将设备驱动融入到内核中。为了实现这个融合,必须在所有的设备驱动中设计面向操作系统内核的接口,这样的接口由操作系统规定。我们的linux操作系统是一个开源的操作系统,Linux 的众多优良特性之一就是可以在运行时扩展由内核提供的特性的能力. 这意味着你可以在系统正在运行着的时候增加内核的功能(也可以去除).每块可以在运行时添加到内核的代码, 被称为一个模块. Linux 内核提供了对许多模块类型的支持, 包括但不限于设备驱动. 每个模块由目标代码组成( 没有连接成一个完整可执行文件), 可以动态连接到运行中的内核中, 通过insmod安装模块程序, 以及通过rmmod 移除模块.

Linux内核的划分如图所示:

计算机系统的硬件主要由CPU、存储器和外设组成。随着IC制造工艺的发展,目前,芯片的集成度越来越高,往往在CPU内部就集成了存储器和外设适配器。ARM、PowerPC、MIPS等处理器都集成了UART、IIC控制器、USB控制器、SDRAM控制器等,有的处理器还集成了片内RAM和Flash。

驱动针对的对象是存储器和外设(包括CPU内部集成的存储器和外设),而不是针对CPU核。Linux将存储器和外设分为3 种基本设备类型(字符设备、块设备、网络设备). 每个模块常常实现3 种设备类型中的 1 种, 因此可分类成字符模块, 块模块,网络模块.

3类设备如下:

字符设备:一个字符( char ) 设备是一种可以当作一个字节流来存取的设备( 如同一个文件); 它是必须以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等。这样的驱动常常至少实现open, close, read, 和write 系统调用. 它不经过系统的快速缓冲。

块设备:如同字符设备, 它可以用任意顺序进行访问,以块为单位进行操作,它会经过系统的快速缓冲。块设备通过位于/dev 目录的文件系统结点来存取. 一个块设备(例如一个磁盘)应该是可以驻有一个文件系统的. Linux允许应用程序读写一个块设备象一个字符设备一样-- 它允许一次传送任意数目的字节. 结果就是, 块和字符设备的区别仅仅在内核在内部管理数据的方式上,因此在内核/驱动的软件接口上有所不同.

网络设备:面向数据包的接收和发送而设计,它并不对应文件系统的节点。

除了网络设备以外,字符设备和块设备都被映射到linux文件系统的文件和目录,在linux 中,一切皆文件。通过文件系统的系统调用接口open/write/read/close等函数调用即可访问字符设备和块设备。所有的字符设备和块设备都被统一的呈现给用户。块设备比字符设备复

杂,在它上面会首先建立一个磁盘/Flash文件系统,如FAT、Ext3、YAFFS、JFFS等。这些文件系统规范了文件和目录在存储介质上的组织方式。应用程序可以使用linux的系统调用接口编程,也可以使用C库函数。linux设备驱动与整个软硬件系统的关系如图:

·2 linux内核模块编程

linux内核的整体结构非常庞大,其包含的组件也非常多,那么我们怎么把需要的部分都包含在内核中呢?一种方法时把所有需要的功能都编译到内核,利用make zImage可以实现,并会生成镜像文件,烧录到开发板上即可。但是这会导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除部分功能,将不得不重新编译内核,而这个过程是比较耗费时间的。那么有没有一种机制使得编译出的内核本身并不需要包含所有功能,而是在这些功能需要被使用的时候,其对应的代码可以被动态的加载到内核中呢?linux 提供了这种机制,称为模块(MODULE)。模块的特点:模块本身不被编译如内核镜像,从而控制了内核的大小;模块一旦被加载,它就和内核中的其他部分完全一样;模块可以被卸载,使用非常方便。

在进行内核模块编程之前,先要保证我们的开发板能够从nandflash启动进入命令行模式。如果不行需要重新烧写镜像。然后将linux-2.6.30.tar.bz2解压。并且确保交叉编译器为4.3.3版本。

解压出来的linux内核它的源代码目录结构如下:

arch:包含和硬件体系结构相关的代码,每种平台占一个相应的目录,如i386、ARM、PowerPC、MIPS等。

block:块设备驱动程序I/O调度。

crypto:常用加密和散列算法(如AES、SHA等),还有一些压缩和CRC校验算法。

Documentation:内核各部分的通用解释和注释。

drivers:设备驱动程序,不同的驱动占用一个子目录(如char、block、net、mtd、iic)fs:支持的各种文件系统,如EXT、FAT、NTFS、JFFS2、YAFFS等。

include:头文件,与系统相关的头文件被放置在include/linux子目录下。

init:内核初始化代码。

ipc:进程间通信的代码。

kernel:内核的最核心部分,包括进程调度、定时器等,而和平台相关的一部分代码放在了arch/…/kernel目录下。

lib:库文件代码。

mm:内存管理代码,和平台相关的一部分代码放在arch/…/mm目录下。

net:网络相关代码,实现了各种常见的网络协议。

scripts:包含用于配置内核的脚步文件。

security:主要包含SELinux模块。

sound:ALSA、OSS音频设备的驱动核心代码和常见设备驱动。

usr:实现了用于打包和压缩的cpio等。

linux内核主要由进程调度(SCHED)、内存管理(MM)、虚拟文件系统(VFS)、网络接口(NET)和进程间通信(IPC)等5个子系统组成。如图:

内核模块编程常用函数说明如下:

·指定模块的初始化和清理退出函数的宏定义:

#include

module_init(init_function); //指明模块的入口函数

module_exit(exit_function); //指明模块的退出函数

module_init和module_exit是两个宏。

module_init:指明模块的入口函数,当模块加载到内核时会自动调用init_function函数,该函数初始化成功,则返回0;失败,则返回错误编码。在linux内核中,错误编码是一个负值,在中定义,包含-ENODEV、-ENOMEM之类的符号值;

module_exit:指明模块的退出函数,当模块被去除时会自动调用exit_function函数,模块卸载函数要完成与模块加载函数相反的功能。如:若模块加载函数注册了XXX,则模块卸载函数应该注销XXX。若模块加载函数动态申请了内存,则模块卸载函数应释放该内存。若模块加载函数申请了硬件资源(中断、DMA通道、I/O端口和I/O内存等)的占用,则模块卸载函数应释放这些硬件资源。若模块加载函数开启了硬件,则卸载函数中一般要关闭硬件。init_function和exit_funciton的函数原型如下:

static int __init init_function(void); //静态表示只能在本源程序中调用

static void __exit exit_function(void);

__init:是一个给内核的暗示, 给定的函数只用于初始化操作. 所有标识为__init的函数在连接的时候都放在.init.text这个区段内。模块加载者在模块加载后会丢掉这个初始化函数, 使它的内存可做其他用途。

__exit:标识这个代码是只用于模块卸载操作。和__init一样,__exit也可以使对应函数在运行完成后自动回收内存。

·模块的打印函数:

#include

int printk(const char* fmt, …);

该函数类似于C语言程序中的printf函数。用于在内核模块中打印信息。常用的形式为:printk(“hello init\n”); 或printk(KERN_INFO “hello init\n”);其中KERN_INFO和后面的字符串之间没有逗号,只是用空格隔开。用printk,内核会根据日志级别,可能把消息打印到当前控制台上,这个控制台通常是一个字符模式的终端、一个串口打印机或是一个并口打印机。可选项如下:

#define KERN_EMERG 0 /*紧急事件消息,系统崩溃之前提示,表示系统不可用*/ #define KERN_ALERT 1 /*报告消息,表示必须立即采取措施*/

#define KERN_CRIT 2 /*临界条件,通常涉及严重的硬件或软件操作失败*/

#define KERN_ERR 3 /*错误条件,驱动程序常用KERN_ERR来报告硬件的错误*/ #define KERN_W ARNING 4 /*警告条件,对可能出现问题的情况进行警告*/

#define KERN_NOTICE 5 /*正常但又重要的条件,用于提醒*/

#define KERN_INFO 6 /*提示信息,如驱动程序启动时,打印硬件信息*/

#define KERN_DEBUG 7 /*调试级别的消息*/

·几个常见模块宏声明

MODULE_LICENSE(license); //遵循的协议,指明为GPL(通用公共协议)MODULE_AUTHOR(author); //表示作者

MODULE_DESCRIPTION(description); //具体描述

MODULE_VERSION(version_string); //版本信息

通常写法如下:

MODULE_LICENSE("GPL");

MODULE_AUTHOR("stars984@https://www.doczj.com/doc/bb18939033.html,");

MODULE_DESCRIPTION("this is my first module program");

MODULE_VERSION("1.0");

实例如下:

在linux下编写驱动模块程序有两种方式:

第一种:单独编译:

首先编写通用的驱动Makefile程序如下:

obj-m := hello.o //obj-m:一个makefile 符号, 内核建立系统用来决定当前目录下的哪个模块应当被建立。只需要更换这个地方就可以了,Makefile是通用的KDIR := /lib/modules/`uname -r`/build//内核所在目录,此时是在虚拟机上模拟。如果是想将模块在开发板上运行,需要修改该地址为你刚刚linux-2.6.30.tar.bz2安装的目录,如/opt/ linux-2.6.30.4。如果最后执行make的时候提示163的错误,则在linux-2.6.30.4目录下执行make modules即可。

PWD := $(shell pwd) //当前路径

all:

make -C $(KDIR) M=$(PWD) modules //-C表示去到内核目录中M=表示表示生成的模块*.ko在当前目录

clean:

rm -rf *.o .* .cmd *.ko *.mod.c .tmp_versions

解析make -C $(KDIR) M=$(PWD) modules:表示编译生成为模块。这个命令开始是改变它的目录到用-C选项提供的目录下(就是说, 你的内核源码目录). 它在那里会发现内核的顶层Makefile. 这个M=选项使Makefile 在试图建立模块目标前, 回到你的模块源码目录.

然后编写第一个hello.c模块程序如下:

#include //linux内核的头文件

#include //linux驱动模块的头文件

#include //linux模块入口函数和退出函数的头文件

MODULE_LICENSE("GPL"); //定义宏,表示所要遵循的协议

MODULE_AUTHOR("stars984@https://www.doczj.com/doc/bb18939033.html,"); //表示作者

static int __init hello_init(void) //入口函数

{

printk("hello_init\n");

return 0;

}

static void __exit hello_exit(void) //出口函数

{

printk("hello_exit\n");

}

module_init(hello_init); //内核注册函数

module_exit(hello_exit); //内核退出函数

因为我此时是在虚拟机上模拟,所以直接执行make。成功生成一个hello.ko。可以使用modinfo hello.ko获得模块的信息。然后执行insmod hello.ko安装模块,此时在执行dmesg | tail –n 10可以看到hello init。通过lsmod可以查看你安装的模块(其实执行lsmod命令,实际上读取并分析的是/proc/modules文件,它等价于cat /proc/modules,加载hello.ko成功之后,在内核的/sys/module/下回生成一个目录hello)。执行rmmod hello表示去除模块hello。再用dmesg | tail –n 10可以看到又有一条信息输出:hello exit。

如果是用开发板的内核(linux-2.6.30.tar.bz2),则修改Makefile为指定的linux-2.6.30.tar.bz2的解压路径。然后执行make,如果提示163错误,则到linux-2.6.30.4里面去执行make modules然后再回到源目录执行make,成功会生成一个hello.ko,然后利用超级终端,从nandflash启动,然后输入命令rz,然后点击菜单里面的发送,选择发送文件就可以将hello.ko发送到开发板上去。然后再开发板上运行insmod hello.ko,利用dmesg | tail –n 10查看信息。利用lsmod可以查看是否有你安装的模块,用rmmod可以去除模块。如果开发板从nandflash启动进入不了命令行,则重新烧写。用DNW连接开发板。1烧uboot、3烧linux kernel、6烧文件系统(root_qt_4.5_2.6.30.4_256MB_20100601.bin)、b烧LOGO。其中hello_init这个函数是在insmod的时候调用的,hello_exit是在rmmod的时候调用的。

第二种:加入到linux内核编译菜单三态(<*>编入内核编为模块<>不编译)

在linux内核中增加程序需要完成以下3项工作:

·将编写的源代码复制到linux内核源代码所在的响应目录。

·在目录的Kconfig文件中增加新源代码对应项目的编译配置选项。

·在目录的Makefile文件中增加对新源代码的编译条目。

1>.cp hello.c /opt/linux-2.6.30.4/drivers/char

2>.cd /opt/linux-2.6.30.4/drivers/char

3>.vi Kconfig-->添加如下代码:

config HELLO //这里的HELLO,后面在Makefile里面的时候要用CONFIG_HELLO tristate "hello world" //表示采用3态模式

depends on ARCH_S3C2440 //依赖于S3C2440的架构

4>.修改同目录下的Makefile--> vi Makefile-->添加如下代码:

obj-$(CONFIG_HELLO) += hello.o

5>.进到linux-2.6.30.4下执行make menuconfig,选择Deriver Drivers ---> Character devices ---> hello world(选择编译为模块)

6>.在linux-2.6.30.4目录下执行make modules(表示编译所有的模块。而make zImage 表示编译到内核里面去,并生成镜像文件),会在driver下的char里面生成hello.ko,然后进到drivers/char中,用ftp把hello.ko传到开发板,在开发板上运行insmod hello.ko,rmmod hello,lsmod等操作。

·指定模块参数的函数:

#include

module_param(variable, type, perm);

该函数用户创建模块参数,可以被用户在模块加载时传入。其中variable表示变量名称,type表示变量类型,可以是bool、charp、int、short等。perm表示权限,如S_IRUGO表示0444,即所有者,同组和其他人都是只读权限。

实例test.c:

#include

#include

#include

MODULE_LICENSE("GPL");

MODULE_AUTHOR("stars984@https://www.doczj.com/doc/bb18939033.html,");

static char* name = NULL; //定义char*类型变量

static int n = 0; //定义int类型变量

module_param(name, charp, 0444);

module_param(n, int, 0444);

static int __init func_init(void)

{

printk("---func init---\n");

printk("%s--->%d\n", name, n);

return 0;

}

static void __exit func_exit(void)

{

printk("%s--->%d\n", __FILE__, __LINE__);

printk("---func exit---\n");

}

module_init(func_init);

module_exit(func_exit);

此时执行make生成test.ko,然后执行insmod test.ko name=wangxiao n=1001则可以给内核传递参数。另外,这里用到了两个宏:__FILE__和__LINE__。其中__FILE__表示文件所在的完整路径和文件名,__LINE__表示所在的行号。

·全局变量current

#include

struct task_struct *current;

利用这个变量可以打印当前进程ID和产生该进程执行的命令名。

实例:printk(KERN_INFO "The process is \"%s\" (pid %i)\n", current->comm, current->pid);·导出符号

linux2.6的“/proc/kallsyms”文件对应着内核符号表,它记录了符号以及符号所在的内存地址。模块可以使用如下宏导出符号到内核符号表:

EXPORT_SYMBOL(符号名);

EXPORT_SYMBOL_GPL(符号名);

导出的符号将可以被其他的模块使用,使用前声明一下即可。EXPORT_SYMBOL_GPL()只适用于包含GPL许可的模块。

例如编写一个求加法和减法的函数如下:

#include

#include

MODULE_LICENSE("GPL");

int add(int a, int b)

{

return a + b;

}

int sub(int a, int b)

{

return a – b;

}

EXPORT_SYMBOL(add);

EXPORT_SYMBOL(sub);

此时修改hello.c如下:

#include

#include

#include

#include

#include

MODULE_LICENSE("GPL");

MODULE_AUTHOR("stars984@https://www.doczj.com/doc/bb18939033.html,");

extern int add(int a, int b); //引用其他模块中的函数

extern int sub(int a, int b);

static char* szName = NULL;

static int iNo = 0;

module_param(szName, charp, 0444);

module_param(iNo, int, 0444);

static int __init hello_init(void)

{

rintk(KERN_INFO "hello init\n");

printk(KERN_INFO "name : %s no : %d\n", szName, iNo);

printk("add=%d sub=%d\n", add(3, 4), sub(2, 4));

return 0;

}

static void __exit hello_exit(void)

{

printk(KERN_INFO "hello exit\n");

printk(KERN_INFO "%s %d\n", __FILE__, __LINE__);

printk(KERN_INFO "The process is \"%s\" (pid %i)\n", current->comm, current->pid);

}

module_init(hello_init);

module_exit(hello_exit);

另外,需要修改Makefile如下:

obj-m := hello.o func.o

KDIR := /lib/modules/`uname -r`/build

PWD := $(shell pwd)

all:

make -C $(KDIR) M=$(PWD) modules

clean:

rm -rf *.o *.cmd *.ko *.mod.c *.tmp_versions

执行步骤:

首先执行make将会生成func.ko和hello.ko。然后先执行insmod func.ko,然后再执行insmod hello.ko。此时再用dmesg就可以看到效果。最后取出模块的时候,应该先rmmod main 然后再rmmod func。

利用命令“cat /proc/kallsyms”可从“/proc/kallsyms”文件中看到add/sub的相关信息。

·3 字符设备驱动

在讲解字符设备驱动之前,我们先要会用软件去查看我们开发板上linux内核的源代码,因为linux内核的源代码大概有30000多个源文件,一个一个的看会比较麻烦,这里我们利用sourceInsight这个软件。用sourceInsight打开我们解压的linux-2.6.30.tar.bz2(sourceInsight 的使用方法:打开sourceInsight,在options里面选择Document Options,在弹出的Document Type里面选择C Source File,然后在旁边的File Filter里面添加“;*.inc;*.s”表示等下加载的时候将汇编的也加载进来,否则,你会发现加载之后没有汇编程序。然后选择project菜

单,选择New Project,然后输入项目名,点击“确定”。然后再New Project Setting里面选择browse,选择你的linux-2.6.30.4目录。然后点击ok,然后再弹出的Add and Remove Project Files里面选择Add All,然后勾选Recursively add lower sub-directories表示添加子目录,然后单击ok添加成功)。后面我们在字符驱动里面会在sourceInsight里面查找函数原型。

在linux中一切皆文件,一个进程占用4G的虚拟内存空间,其中3G给了用户,1G给了内核。我们写的应用程序通过系统调用接口(常见的API函数,如open、write、read等)可以与内核之间进行交互,内核里面有很多设备驱动程序,这些程序都是用模块的方式实现的,设备驱动程序可以让我们的硬件响应。所以上层的应用程序调用才会有相应的反应。

3.1 老版本字符驱动编写方式

在linux2.4内核里面,采用下面函数来注册一个字符设备:

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

其中major表示主设备号,name表示设备名,fops表示文件操作结构体指针,通常需要写在init函数里面,如:register_chrdev(def_major, DEV_NAME, &fops);

当不再使用这个字符设备,就要注销这个字符设备,可以采用如下的函数:int unregister_chrdev(unsigned int major, const char *name);

其中major表示主设备号,name表示设备名。通常这个函数需要写在exit函数里面,如:unregister_chrdev(def_major, DEV_NAME);

具体示例如下:

//old_char_dev.c

#include

#include

#include

#include

#define MAJOR_NO 300

#define CDEV_NAME "my_old_char_dev"

MODULE_LICENSE("GPL");

MODULE_AUTHOR("906010152@https://www.doczj.com/doc/bb18939033.html,");

MODULE_VERSION("1.0");

MODULE_DESCRIPTION("old char device");

int my_open(struct inode *inode, struct file *filp)

{

printk("**********open**********\n");

return 0;

}

int my_close(struct inode *inode, struct file *filp)

{

printk("**********close*********\n");

return 0;

}

ssize_t my_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

{

printk("**********read**********\n");

return 0;

}

ssize_t my_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp) {

printk("**********write*********\n");

return 0;

}

struct file_operations fops = {

.owner = THIS_MODULE,

.open = my_open,

.read = my_read,

.write = my_write,

.release = my_close

};

static int __init func_init(void)

{

printk("*******func init********\n");

register_chrdev(MAJOR_NO, CDEV_NAME, &fops);

return 0;

}

static void __exit func_exit(void)

{

printk("*******func exit********\n");

unregister_chrdev(MAJOR_NO, CDEV_NAME);

}

module_init(func_init);

module_exit(func_exit);

//上层测试程序:testfunc.c

#include

#include

#include

#include

#include

#include

#include

#define DEV_NAME "/dev/my_old_char_dev"

int main()

{

int fd = open(DEV_NAME, O_RDWR);

if(fd == -1)

{

perror("open error");

exit(-1);

}

write(fd, "hello", 6);

char buf[512] = {'\0'};

read(fd, buf, sizeof(buf));

close(fd);

return 0;

}

//通用Makefile程序:

obj-m := old_char_dev.o

KDIR := /opt/linux-2.6.30.4/

PWD := $(shell pwd)

all:

make -C $(KDIR) M=$(PWD) modules

clean:

rm -rf *.o *.ko

执行步骤:

首先执行make生成old_char_dev.ko,然后利用arm-linux-gcc –o testfunc testfunc.c生成可执行程序testfunc,利用arm-linux-striptestfunc消除多余的一些符号信息。最后利用超级终端将old_char_dev.ko和testfunc发到开发板上去。在开发板上先给testfunc添加可执行的权限:chmod +x testfunc,然后加载模块insmod old_char_dev.ko,做完这一步之后需要利用mknod /dev/my_old_char_dev c 300 0来建立字符设备的节点。执行./testfunc然后利用dmesg | tail –n 10就可以显示信息了。最后执行完了之后,利用rm /dev/my_old_char_dev删除设备节点,利用rmmod old_char_dev去除模块。

3.2 新版本字符驱动编写方式

在linux2.6内核的字符设备驱动编程里面,使用cdev结构体描述字符设备,其具体的内容如下:

struct cdev

{

struct kobject kobj; /* 内嵌的kobject对象,不常用*/

struct module *owner; /* 所属模块*/

struct file_operations *ops /* 文件操作结构体*/

dev_t dev; /* 设备号*/

unsigned int count; /* 个数*/

};

在cdev结构体的dev_t成员定义了设备号,是32位的整型。其中高12位是主设备号,低20位为次设备号。

我们通过ls –l /dev可以查看所有的设备。其中以c开头的表示字符设备、以b开头的表示块设备。并且在长格式显示的时候,我们可以看到设备的主设备号和次设备号。例如:crw-rw---- 1 root uucp 4, 64 2014-01-03 22:29 ttyS0

这里设备ttyS0中4就是主设备号,64就是次设备号。

如果已知设备号,可以使用下列宏从dev_t获得主设备号和次设备号:

int MAJOR(dev_t dev);

int MINOR(dev_t dev);

如果已知主设备号和次设备号,可以利用下面的宏来得到设备号(dev_t类型):dev_t MKDEV(unsigned int major, unsigned int minor) ;

在cdev结构体中还有另外一个非常重要的成员ops(struct file_operations*类型),struct file_operations结构体定义了字符设备驱动提供给虚拟文件系统的接口函数。我们在linux的上层应用程序开发中经常调用open/read/write/close等函数,其实在linux的内核中已经为我们实现了这些函数底层的操作。我们编写一个字符驱动,上层应用程序通过open、read、write、close等函数调用,然后底层的驱动模块程序由我们自己实现。此时,这个文件指针就发挥了至关重要的作用。可以在sourceInsight去查看该文件操作结构体的具体内容(fs.h)。其中该结构体里面常见的一些函数指针如下:

struct file_operations

{

struct module* owner; //拥有该结构的模块的指针,一般为THIS_MODULE

int (*open)(struct inode*, struct file*);//打开

loff_t (*llseek)(struct file*, loff_t, int); //用来修改文件当前的读写位置

ssize_t (*read)(struct file*, char __user*, size_t, loff_t*);//从设备中读取数据

ssize_t (*write)(struct file*, const char __user*, size_t, loff_t*);//向设备发送数据

int (*ioctl)(struct inode*, struct file*, unsigned int, unsigned long);//设备I/O控制命令

int (*release)(struct inode*, struct file*);//关闭

……

};

其中open对应上层应用程序里面的open函数调用,上层应用程序调用open函数打开设备文件时,设备驱动里面的open函数会被调用。驱动模块程序可以不实现这个函数,在这种情况下,设备的打开操作永远成功。

(llseek---lseek read---read write---write ioctl---ioctl release---close)

Linux2.6内核里面提供了一组函数用户操作cdev结构体,如下:

#include

void cdev_init(struct cdev* cdev, struct file_operations* ops);

int cdev_add(struct cdev* cdev, dev_t dev, unsigned int count);

void cdev_del(struct cdev* cdev);

函数cdev_init:初始化cdev成员,并建立cdev和file_operations文件操作结构体直接的连接。

函数cdev_add:向系统里面添加一个字符设备。对cdev_add的调用通常发生在字符设备驱动模块的init函数中。

函数cdev_del:从系统里面删除一个字符设备。对cdev_del的调用通常发生在字符设备驱动模块的exit函数中。

在调用cdev_add函数向系统注册字符设备之前,应该首先调用register_chrdev_region 或alloc_chrdev_region函数向系统注册或申请设备号。这两个函数的原型如下:int register_chrdev_region(dev_t dev, unsigned count, const char* name);

int alloc_chrdev_region(dev_t* dev, unsigned baseminor, unsigned count, const char* name);

函数register_chrdev_region用于已知主设备,从设备号不知,则注册count个(通常为1个)且设备名称为name的设备号(这里的name就是设备名,最后在/proc/devices下会显示你的设备号和设备名)。dev是设备号,它里面的次设备号通常为0。这样我们可以利用dev_t devno = MKDEV(MAJOR_NO, 0);的形式获得devno之后,再利用函数register_chrdev_region (devno, 1, “MY_DEV”);如果注册成功,该函数返回0.

函数alloc_chrdev_region用于主次设备号都未知的情况下,向系统申请count个(通常为1个)且设备名称为name的设备号,其中从设备号baseminor开始(通常为0),此时dev 为输出参数)。常见原型:alloc_chrdev_region(&devno, 0, 1, “MY_DEV”);

同理,再调用cdev_del函数从系统中注销一个字符设备之后,应该调用函数unregister_chrdev_region来释放原先申请的设备号。该函数原型如下:

void unregister_chrdev_region(dev_t dev, unsigned count);

其中dev为设备号,这里要利用宏MKDEV(major, minor)来获取。常见原型:unregister_chrdev_region(MKDEV(MAJOR_NO, 0), 1);

那么我们一个字符设备驱动应该如何写:

在linux系统中,字符设备驱动由如下几个部分组成:

·字符设备驱动模块加载与卸载函数:主要实现设备号的申请和cdev的注册以及最后的设备号的释放和cdev的注销。

·字符设备驱动的file_operations结构体中成员函数:这些函数是字符设备驱动与内核的接口,是用户空间对linux进行系统调用最终的落实者。

字符设备模块的源程序:

#include

#include

#include

#include

#include

#define MAJOR_NO 1314 /*预设的主设备号*/

#define DEV_NAME "char_dev_module" /* 设备名*/

static int def_major = MAJOR_NO;

struct cdev cdev; /*cdev结构体*/

/*打开函数*/

int my_open(struct inode *inode, struct file *filp)

{

printk("********open********\n");

return 0;

}

/*关闭函数*/

int my_close(struct inode *inode, struct file *filp)

{

printk("********close********\n");

return 0;

}

/*读函数*/

static ssize_t my_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)

{

printk("********read********\n");

return 0;

浅谈验收测试驱动开发

浅谈验收测试驱动开发 【摘要】软件行业已经发展了很多年,尽管新技术不断涌现,但是软件质量问题依然存在,最突出的两点就是较高的缺陷率和较差的可维护性。为了应对此类问题,驱动测试开发技术(ADD)应运而生,但是随着ADD技术的普及,它所隐藏的问题也浮出水面,最为人诟病的就是“不能满足客户需求”,因为测试人员只注重代码缺陷率而忽视了系统具体功能。本文阐述如何在ADD开发模式的基础上,结合验收测试驱动开发(ATDD)探讨如何开发适应于用户的系统。 【关键词】敏捷开发;验收测试驱动开发;软件工程 一、引言 极限编程方法理论中“测试驱动开发”是其一个重要组成部分,最早是由Kent Beck提出,并积极推广的一种软件开发方法。Kent Beck在他所著的《测试驱动开发》一书中指出“测试驱动开发”遵循“为明天编码,为今天设计”的观点。相比传统遵循“需求-设计-开发-测试”的软件开发流程而言,更强调测试优先,再通过编码和重构反复迭代最终构筑一个完整的软件系统。“测试驱动开发”在相当程度上了的确提高了开发人员的代码质量,而且在应对系统的可靠性也教之传统软件开发有着更大的优势,主要体现在客户需求变更时能灵活应对。然而软件问题中另一项“是否满足客户需求”确没有很好地解决。验收测试驱动开发(ATDD)针对这个问题,提出让客户参与到测试标准的制定,让软件满足客户需求。用ATDD 方法开发软件,开发人员更注重的是系统行为测试,而不是软件中每个模块,甚至每行代码的测试。构筑一个满足客户需求的软件系统,不仅仅是软件设计开发人员和测试人员靠个人能力能解决的,在此过程中需要客户参与进来,为打造可靠的软件提供有力的保障。 二、什么是ATDD 测试驱动开发(ADD)能够帮助开发人员开发出高质量的代码,保证开发人员所开发出的代码执行正确,但是这些执行正确的代码在很大程度上是针对的具体模块而不是整体的系统功能。在一定程度上不一定能够满足客户的需求。验收测试驱动开发(ATDD)是建立在TDD的基础上,TDD和ATDD既可以分开使用也可以配合使用,在帮助开发人员在提高软件质量的同时,也帮助开发人员开发出用户真正需要的软件系统。软件测试是软件工程的重要组成部分,在传统的软件开发当中,软件测试大概包括软件执行过程中是否存在BUG、系统中是否还存在其它缺陷以及系统是否与系统设计书保持一致几项内容,ATDD则在此基础上赋予了软件软件测试新的任务,即利用验收测试从系统功能的角度上驱动软件开发,解决软件不能满足客户需求或者是与客户设想相背离的问题。 总体而言验收测试驱动开发是包括客户在内的一个团体组织的活动,围绕着客户需求引入“用户故事”(user story)这种灵活的客户需求管理方式。客户和技术人员(包括设计、开发和测试)通过紧密的写作、有效的交流和沟通构筑可靠

(整理)嵌入式系统的以太网接口设计及linux内核网络设备驱动.

嵌入式系统的以太网接口设计及linux驱动 1 以太网概述 以太网(Ethernet)是当今局域网采用的最通用的通信协议标准。在以太网中,所有计算机被连接在一条电缆上,采用带冲突检测的载波侦听多路访问(CSMA/CD)方法,采用竞争机制和总线拓扑结构。基本上,以太网由共享传输媒体,如双绞线电缆或同轴电缆、多端口集线器、网桥或交换机构成。 按照OSI(Open System Interconnection Reference Model,开放式系统互联参考模型)7层参考模型,以太网定义的是物理层(PHY)和数据链路层(对应以太网的MAC层)的标准。 2 嵌入式处理器上扩展以太网接口 以太网接口控制器主要包括MAC乘PHY两部分,如图1所示为嵌入式处理器集成MAC层控制器。 MAC层控制器和PHY的连接是通过MII、RMII等接口实现的。在IEEE802的标准系列中,数据链路层包括LLC和MAC两个子层。其中MAC负责完成数据帧的封装、解封、发送和接受功能。PHY层的结构随着传输速率的不同而有一定的差异。对于1OBaseT等网络,从以太网PHY芯片输出的就是传输所需的差分信号。但是还需要一个网络隔离变压器组成图2的结构。网络隔离变压器可起到抑制共模干扰、隔离线路以及阻抗匹配等作用。 本文介绍一种新款网络接口芯片DM9000A,它可以很方便的实现与嵌入式CPU的接口,实现扩展以太网口的功能。DM9000A是中国台湾DAVICOM公司推出的一款高速以太网接口芯片,其基本特征是:集成10/100M物理层接口;内部带有16K字节SRAM用作接收发送的FIFO缓存;支持8/16bit两种主机工作模式:

浅谈测试驱动开发(TDD)

浅谈测试驱动开发(TDD) 李群https://www.doczj.com/doc/bb18939033.html, 测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了 代码,又保证了软件质量。本文从开发人员使用的角度,介绍了TDD 优势、原理、过程、 原则、测试技术、Tips 等方面。 背景 一个高效的软件开发过程对软件开发人员来说是至关重要的,决定着开发是痛苦的挣扎,还是不断进步的喜悦。国人对软件蓝领的不屑,对繁琐冗长的传统开发过程的不耐,使大多数开发人员无所适从。最近兴起的一些软件开发过程相关的技术,提供一些比较高效、实用的软件过程开发方法。其中比较基础、关键的一个技术就是测试驱动开发(Test-Driven Development)。虽然TDD光大于极限编程,但测试驱动开发完全可以单独应用。下面就从开发人员使用的角度进行介绍,使开发人员用最少的代价尽快理解、掌握、应用这种技术。下面分优势,原理,过程,原则,测试技术,Tips等方面进行讨论。 1. 优势 TDD的基本思路就是通过测试来推动整个开发的进行。而测试驱动开发技术并不只是单纯的测试工作。 需求向来就是软件开发过程中感觉最不好明确描述、易变的东西。这里说的需求不只是指用户的需求,还包括对代码的使用需求。很多开发人员最害怕的就是后期还要修改某个类或者函数的接口进行修改或者扩展,为什么会发生这样的事情就是因为这部分代码的使用需求没有很好的描述。测试驱动开发就是通过编写测试用例,先考虑代码的使用需求(包括功能、过程、接口等),而且这个描述是无二义的,可执行验证的。 通过编写这部分代码的测试用例,对其功能的分解、使用过程、接口都进行了设计。而且这种从使用角度对代码的设计通常更符合后期开发的需求。可测试的要求,对代码的内聚性的提高和复用都非常有益。因此测试驱动开发也是一种代码设计的过程。 开发人员通常对编写文档非常厌烦,但要使用、理解别人的代码时通常又希望能有文档进行指导。而测试驱动开发过程中产生的测试用例代码就是对代码的最好的解释。 快乐工作的基础就是对自己有信心,对自己的工作成果有信心。当前很多开发人员却经常在担心:“代码是否正确?”“辛苦编写的代码还有没有严重bug?”“修改的新代码对其他部分有没有影响?”。这种担心甚至导致某些代码应该修改却不敢修改的地步。测试驱动开发提供的测试集就可以作为你信心的来源。 当然测试驱动开发最重要的功能还在于保障代码的正确性,能够迅速发现、定位bug。而迅速发现、定位bug是很多开发人员的梦想。针对关键代码的测试集,以及不断完善的测试用例,为迅速发现、定位bug提供了条件。 我的一段功能非常复杂的代码使用TDD开发完成,真实环境应用中只发现几个bug,而且很

Linux设备驱动程序举例

Linux设备驱动程序设计实例2007-03-03 23:09 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,在与硬件设备之间 建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设 备进行打开(open)、关闭(close)、读写(read/write)等操作。通过分析和设计设 备驱动程序,可以深入理解Linux系统和进行系统开发。本文通过一个简单的例子 来说明设备驱动程序的设计。 1、程序清单 //MyDev.c 2000年2月7日编写 #ifndef __KERNEL__ #define __KERNEL__//按内核模块编译 #endif #ifndef MODULE #define MODULE//设备驱动程序模块编译 #endif #define DEVICE_NAME "MyDev" #define OPENSPK 1 #define CLOSESPK 2 //必要的头文件 #include //同kernel.h,最基本的内核模块头文件 #include //同module.h,最基本的内核模块头文件 #include //这里包含了进行正确性检查的宏 #include //文件系统所必需的头文件 #include //这里包含了内核空间与用户空间进行数据交换时的函数宏 #include //I/O访问 int my_major=0; //主设备号 static int Device_Open=0; static char Message[]="This is from device driver"; char *Message_Ptr; int my_open(struct inode *inode, struct file *file) {//每当应用程序用open打开设备时,此函数被调用 printk ("\ndevice_open(%p,%p)\n", inode, file); if (Device_Open) return -EBUSY;//同时只能由一个应用程序打开 Device_Open++; MOD_INC_USE_COUNT;//设备打开期间禁止卸载 return 0; } static void my_release(struct inode *inode, struct file *file)

linux驱动开发的经典书籍

linux驱动开发的经典书籍 结构、操作系统、体系结构、编译原理、计算机网络你全修过 我想大概可以分为4个阶段,水平从低到高 从安装使用=>linux常用命令=>linux系统编程=>内核开发阅读内核源码 其中学习linux常用命令时就要学会自己编译内核,优化系统,调整参数 安装和常用命令书太多了,找本稍微详细点的就ok,其间需要学会正则表达式 系统编程推荐《高级unix环境编程》,黑话叫APUE 还有《unix网络编程》 这时候大概还需要看资料理解elf文件格式,连接器和加载器,cmu的一本教材中文名为《深入理解计算机系统》比较好 内核开发阅读内核源码阶段,从写驱动入手逐渐深入linux内核开发 参考书如下《linux device drivers》,黑话叫ldd 《linux kernel development》,黑话叫lkd 《understading the linux kernel》,黑话叫utlk 《linux源码情景分析》 这四本书为搞内核的必读书籍 最后,第三阶段和第四阶段最重动手,空言无益,光看书也不罩,不动手那些东西理解不了 学习linux/unix编程方法的建议 建议学习路径: 首先先学学编辑器,vim, emacs什么的都行。 然后学make file文件,只要知道一点就行,这样就可以准备编程序了。 然后看看《C程序设计语言》K&R,这样呢,基本上就可以进行一般的编程了,顺便找本数据结构的书来看。 如果想学习UNIX/LINUX的编程,《APUE》绝对经典的教材,加深一下功底,学习《UNP》的第二卷。这样基本上系统方面的就可以掌握了。 然后再看Douglus E. Comer的《用TCP/IP进行网际互连》第一卷,学习一下网络的知识,再看《UNP》的第一卷,不仅学习网络编程,而且对系统编程的一些常用的技巧就很熟悉了,如果继续网络编程,建议看《TCP/IP进行网际互连》的第三卷,里面有很多关于应用

Linux网络设备驱动开发实验

实验三:Linux网络设备驱动开发实验 一、实验目的 读懂linux网络设备驱动程序例子,并且实际加载驱动程序,加载进操作系统以后,会随着上层应用程序的触发而执行相应动作,具体执行的动作可以通过代码进行改变。 ●读懂源码及makefile ●编译驱动程序 ●加载 ●多种形式触发动作 二、预备知识 熟悉linux驱动基本原理,能读懂简单的makefile。 三、实验预计时间 80-120分钟左右 四、驱动程序部分具体步骤 要求读懂一个最简单的驱动程序,在驱动程序的诸如“xxx_open”、“xxx_read”等标准接口里面加入打印语句。可参考多模式教学网上的驱动样例。 五、用于触发驱动动作的应用程序及命令 驱动程序就是以静态的标准接口库函数形式存在,网络设备驱动会受到两大类情况的触发,一种是linux里面的控制台里面的命令,另一种是套接口应用程序,首先要搞清都有哪些具体的命令和应用程序流程,应用程序参考多模式教学网的例子。 六、运行测试 提示:需要将驱动程序以dll加载进系统中,并且触发应用程序调用各种文件操作的接口函数,使得驱动有所动作,打印出相关信息。 1.编译驱动: cd /某某目录/vnetdev/ make clean make 2.加载驱动与打开网卡: insmod netdrv.ko

ifconfig vnet0 up 3.运行应用程序 ../raw 4.通过命令“修改网卡MTU”触发驱动执行动作: ifconfig vnet0 mtu 1222 5.显示内核打印: cat /var/log/messages 6.卸载: ifconfig vnet0 down rmmod netdrv.ko 7.修改代码中的某些函数中的打印信息,重新试验上述流程。 至此大家都应该真正理解和掌握了驱动程序-操作系统-应用程序的三者联动机制。 七、实验结果 由图可知能正常加载网卡驱动,并且能够打印调试信息。

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {

int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;

浅析测试驱动开发

浅析测试驱动开发 测试驱动开发是一种用于敏捷软件开发的开发过程,可以快速应对需求变化。它要求先设计和编写测试代码,然后编写功能代码通过所有测试,再重构以提高代码质量。文章将先介绍测试驱动开发的优点、使用环境,然后介绍开发过程,最后介绍相关工具。 标签:测试;TDD;敏捷开发 1 概述 1.1 定义 测试驱动开发(Test Driven Development,TDD)是由极限编程之父Kent Beck提出的一种面向对象的开发方法[1]。区别于传统的软件开发模式,测试先行将更重视测试在整个软件开发过程中的作用并促进项目的进行。它要求先完成测试代码,然后编写功能代码,并且功能代码要以通过测试代码为标准,然后对功能代码重构,重构之后再运行测试并要通过测试[2]。它的一个开发周期比较短,整个项目是多个周期的迭代。这种开发方式有效的提高了软件质量和开发效率[3]。目前,TDD已经被很多公司和开发团队接受并用于实践。 1.2 优点 由于测试先行,因此写代码前就应该有明确的需求,并体现在测试用例中。在交付前,测试用例可以用来描述功能需求并替代部分文档。并且以测试用例描述的需求不容易出现模糊不清的概念,因为测试结果只会是True或False。这解决了开发人员在开发时误解或由于沟通问题不完全理解需求文档而造成开发到一定程度后才发现代码与需求有偏差。但这还没有解决对客户的误解或与客户沟通不畅导致需求分析错误的问题。 功能代码编写完成后必须通过所有测试,这就保证了这部分代码是满足其功能要求的。通过确保每小部分代码的质量,可以较快的叠加成更复杂的功能且保证最后交付的软件与设计的要求是一致的。在对功能代码进行优化时,因为也要通过测试用例,所以能保证这部分代码的改动不会对调用它的其他模块有影响。 1.3 适用环境 尽管从理论上讲TDD可以在各种软件开发项目中使用,但是在某些项目上可能感觉不到比较明显的效率提升或质量提高。 TDD是面向对象的开发方式,如果项目不使用面向对象的设计和开发,则不适合使用TDD。

Jbehave 学习

Jbehave 学习 JBehave行为驱动开发(BDD)是一个框架。 BDD是测试驱动开发(TDD)和验收测试驱动开发(ATDD)的一种演进,目的是使新手和专家开发实践起来更加方便和直观。它改变了从被测试为基础到以行为为基础的词汇,将自己定位为一个设计理念。 一、BDD课题研究之测试思想和方法总结 此次研究的课题是BDD,主要涉及两个方面:测试的思想和方法、技术框架。这里做测试思想和方法的总结。 BDD是什么 全称: Behaviour Driven Development(行为驱动开发)。 BDD改变了我们对软件测试的认识。先前我对测试的认识是:从大的角度来讲,软件测试就是对一个软件系统从功能上进行确认测试和验证测试,从性能上进行压力测试和负载测试,以及对系统的配置测试和兼容性测试等,从类别上又有单元测试,集成测试,回归测试,所有的这些测试工作都有一个目的:交付一套高质量的软件系统。我们软件测试人员的工作就是:尽可能早的找出软件缺陷,并确保其得以修复。顺理成章的,在我们的思维中是:我们先拿到系统的既成品,然后开展测试工作,而BDD恰好颠覆了我们的思维。 回到BDD正题,BDD中有两个大的概念:测试先行和系统设计。 测试先行:BDD提倡我们在开发者的编码工作开展之前,先写测试用例,然后由测试来推动着开发的工作,具体解释为:在设计如何实现一个功能前,先考虑如何测试这个功能,测试的代码完成后,再编写功能实现代码,并且使得该测试用例通过,即完成了系统的一个功能模块。 系统设计:在系统功能实现代码编写之前,我们需要先编写测试代码,在我们的测试代码中实现对系统行为的描述,这个描述其实就是用一种接近自然语言的方式对系统进行详细的设计,并且使项目相关人员,即使是非技术人员也能很容易看懂。关于系统行为,举例说明:用户在一个特定的条件下对系统做请求,系统在该条件下做什么样的处理,这就是系统的一个行为。 总结一下BDD的概念:在项目之初,由客户、开发人员、测试人员一起通过充分的沟通对系统的行为进行设计,由测试人员以接近自然语言的方式编写可以描述系统行为的测试用例,然后由开发人员编写相关的实现代码,并确保该测试用例通过。循环这个过程实现整个系统的功能。

《Linux设备驱动开发详解:基于最新的Linux 4.0内核》19. Linux电源管理系统架构和驱动

以下电子书来源于宋宝华《Linux设备驱动开发详解:基于最新的Linux 4.0内核》第19章《Linux电源管理系统架构和驱动》 本章导读 Linux在消费电子领域的应用已经铺天盖地,而对于消费电子产品而言,省电是一个重要的议题。 本章将介绍Linux设备树(Device Tree)的起源、结构和因为设备树而引起的驱动和BSP 变更。 19.1节阐述了Linux电源管理的总体架构。 19.2~19.8节分别论述了CPUFreq、CPUIdle、CPU热插拔以及底层的基础设施Regulator、OPP以及电源管理的调试工具PowerTop。 19.9节讲解了系统Suspend to RAM的过程以及设备驱动如何提供对Suspend to RAM的支持。 19.10节讲解了设备驱动的Runtime suspend。 本章是相对《Linux设备驱动开发详解(第2版)》全新的一章内容,也是Linux设备驱动工程师必备的知识体系。

第十九章Linux电源管理系统架构和驱动 1.Linux电源管理全局架构 Linux电源管理非常复杂,牵扯到系统级的待机、频率电压变换、系统空闲时的处理以及每个设备驱动对于系统待机的支持和每个设备的运行时电源管理,可以说和系统中的每个设备驱动都息息相关。 对于消费电子产品来说,电源管理相当重要。因此,这部分工作往往在开发周期中占据相当大的比重,图19.1呈现了Linux内核电源管理的整体架构。大体可以归纳为如下几类: 1.CPU在运行时根据系统负载进行动态电压和频率变换的CPUFreq 2.CPU在系统空闲时根据空闲的情况进行低功耗模式的CPUIdle 3.多核系统下CPU的热插拔支持 4.系统和设备对于延迟的特别需求而提出申请的PM QoS,它会作用于CPUIdle的具体 策略 5.设备驱动针对系统Suspend to RAM/Disk的一系列入口函数 6.SoC进入suspend状态、SDRAM自刷新的入口 7.设备的runtime(运行时)动态电源管理,根据使用情况动态开关设备 8.底层的时钟、稳压器、频率/电压表(OPP模块完成)支撑,各驱动子系统都可能用 到 图19.1 Linux电源管理系统架构 2.CPUFreq驱动 CPUFreq子系统位于drivers/cpufreq目录,负责进行运行过程中CPU频率和电压的动态

Linux驱动工程师成长之路

本人此刻还不是什么驱动工程师,连入门都谈不上,但我坚信在未来的3-5年我肯定能成为我想像中的人,因为我马上就要进入这一行工作了。写下这个日志来记录我是怎么最后成为我想像中的人才的,呵呵。 《Linux驱动工程师》这个东西是我在大二的时候看到有一篇讲如何学习嵌入式的,点击这里下载PDF,里面讲到嵌入式分为四层:硬件,驱动,系统,应用程序;还说linux驱动最难然后工资也最高就冲着他这句话我就决定我大学毕业的时候要去做这个linux驱动工程师,随后我就先后买了51单片机,ARM7,ARM9还有一大堆的视频教程准备来进行学习。我还跟我旁边那个哈工大哥们说:“我们学校像我这样的人很少,你们学校呢?”他说:“太少了,不过我们学校都是做这种板子卖的人比较多!”。行,你们牛!即使是买了这些东西,从大二到现在都快毕业了但感觉还是没有入门。回想一下我都学过什么啊:1:自己在ARM9上写bootloader(主要锻炼了三方面的知识:C语言应该写了有近万行的代码,ARM9的外设的基本操作方法如UART,LCD,TOUCH,SD,USB,ETHERNET...,makefile);2:移植和学习linux驱动。下面我说一下我学习Linux驱动的一个思路这也是我在面试的时候自我介绍中最重要的部分;1:硬件知识学习Linux驱动首先得了解这个驱动对应的硬件的一些基本原理和操作方法比如LCD你得了解它的场同步,行同步,像素时钟,一个像素的表示模式,还有就是这个LCD是怎么把图像显示在屏幕上的。如果是USB,SD卡就得了解相关协议。可以通过spec(协议)、datasheet来了解,这就是传说中的Linux驱动开发三件宝之二,还有一个就是linux相关源码。2:了解linux驱动框架linux下的每一类驱动差不多都是一个比较完善的子系统,比如FLASH的驱动它就属于MTD子系统从上到下分为四层:设备节点层,设备层,原始设备层,最下面的与具体硬件相关的硬件驱动层,通常要我们自己来实现就是最下面这个与具体硬件相关那部分代码。3:了解这个驱动的数据流。这个过程与第二个过程紧密相关,如果了解了驱动的框架差不多这个过程也算了解了。比如flash.在/dev/目录下有对应flash的字符设备文件和块设备文件,用户对这些文件进行读、写、ioctl操作,其间通过层层的函数调用最终将调用到最下面的硬件驱动层对硬件进行操作。了解这个过程我相信在调试驱动的时候是很有帮助。3:分析与硬件相关通常需要我们实现的那部分源代码。4:三板子上将驱动调试出来。每次调试都会出问题,但我买的板子提供的资料比较全调试过程中遇到的问题都比较浅显,即使是浅显的问题也要把它记录下来。(这个是我上次在华为面试的时候,那个人问我你调试驱动遇到过什么问题吗?你是如何解决的。当时我学习还没有到调试驱动这一步,所以那次面试也惨败收场)。 好像说了这么多,还没有进入正题《工作的选择》。在年前去了龙芯,实习2.8K,转正3.5k,环境还是不错,经理很好,头儿也很帅都是中科院的硕士。不过去了两周我就没去了身边的人都不太理解,我也一度有过后悔的时候,从龙芯出来应该是1月6号,也就是从那个时候开始我就没有再找工作,转而学习linux驱动。一直到上周日。上周日的晚上我就开始投简历一开始要找linux驱动,在智联里面输入linux驱动出来500来个职位,点开一看没有一个自己符合要求的,差不多都要3-5年经验本科,有时候好不容易有个实习的关键字在里面,一看要求硕士,严重打击了我的信心,哎不管了随便投,最后又投了一下嵌入式关键字的职位。最后就瞎申请,看看职位要求差不多就申请。周一来了,这周一共来了6个面试,创下了我求职以来的历史新高。周一下午面了一家感觉还不错不过到现在也没有给我一个通知,估计当时我要了4500把他给要跑了,这家是做测量的不是Linux驱动,差不多是把ARM当单片机用。周二上午一家也是要招linux驱动面了估计不到二分钟,他

基于测试驱动开发的高校突发事件辅助决策系统.doc

基于测试驱动开发的高校突发事件辅助决策系统 基于测试驱动开发的高校突发事件辅助决策系统 摘耍:由于高校的特殊性,导致突发事件的机会更多、危害更大,因此如何利用历史数据对高校突发事件进行预警和辅助决策显得十分重要。在探讨高校突发事件辅助决策系统的基础上,将测试驱动开发的方法应用于系统开发,实验证明可以明确高校突发事件辅助决策系统的开发需求,加速开发进程,改进软件的质量。 关键词:高校突发事件;辅助决策系统;测试驱动开发 目前,对于高校突发事件危机管理方面的应用研究比较欠缺,很多研究只是基于初步调查的经验总结和感性判断。因此将相关的前沿理论应用到突发事件管理的研究中,建立完善的突发事件辅助决策系统,为高校的管理者提供理论和实践依据是众多专家探讨的关键问题。将测试驱动开发TDD (Test-Dri VenDevel opment)的方法应用于系统开发,实验证明可以明确高校突发事件辅助决策系统的开发需求,加速幵发进程,改进软件的质量。 一、系统功能分析 高校突发事件辅助决策系统主耍具有突发事件预警和突发事件辅助处理两大功能。突发事件预警是指从根本上防止突发事件的形成、爆发,是一种超前的管理。预警系统是对预警对象、预警指标进行分析,从而获取预警信息,以便评佔信息、评价突发事件严重程度、决定是否发出突发事件警报。突发事件辅助处理是根据预警系统对突发事件的早期预测结果作决策,实施处理计划,把已经发生和未发生而将耍发生的事件的影响,控制在最小范围。 二、系统模块设计

根据上述分析,高校突发事件辅助决策系统可以划分为以下模块: 1、预警指标体系设定子模块。由于传统的事件跟踪的预警方法有着诸多弊端,高校突发事件辅助决策系统采用预警指标的方法。预警指标是依据对预警对象(事件、个人)的情况建立一套有监测功能的预警指标体系,通过预警指标收集信息,分析判断突发事件的成因、规模、类型、发生频率、强度、影响后果及发展和变化规律,进行突发事件的预测。 2、预警信息分析子模块。突发事件预警分析子模块主要工作是收集预警征兆信息,进行分析,根据分析结果,发布警报信息和对策信息。通过对学生所在的外部环境的分析研究,掌握客观环境的发展趋势和动态,了解与突发事件发纶有关的微观动向,从而敏锐地察觉环境的各种变化,保证当环境出现不利的因素时,能及吋有效地采取措施,趋利避害。 3、突发事件辅助处理子模块。突发事件管理既强调突发事件出现和发生之后的及时干预,乂重视对突发事件的处理,突发事件管理的偶然和突发性使得处理突发事件的应急计划的制定显得十分重要。在突发事件的应急计划屮,包括应对突发事件的策略、干预突发事件的规则、解决突发事件的程度和方法等。 4、数据查询功能子模块。系统具备全面简便的查询功能,可以按照所填的信息进行查询,快速生成处理报告。系统自带统计分析功能,可以为部分大量表的结果提供描述性统计量,能够实现对不同年份、性质、程度等基本统计量进行比较,大大方便了辅助决策及报告工作。 5、数据导出功能。系统具备全面轻松的数据导出功能,方便深入的科学研究。可以将全部量表的数据导出,从而很方便地实现深入的研究及完成辅助决策功能。 三、TDD在高校突发事件辅助决策系统的应用 1、TDD的概念 测试驱动开发TDD是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码。测试代码确定要编写产品的具体需求。TDD的基本思想是通过测试来推动整个开发的进行,但是测试驭动开发不是单纯的测试工作,而是把需求分析、设计、质量控制量化的过程。

敏捷开发总结分析解析

Intro: 简单的说,敏捷开发是一种以人为核心、迭代、循序渐进的开发方法。在敏捷开发中,软件项目的构建被切分成多个子项目,各个子项目的成果都经过测试,具备集成和可运行的特征。换言之,就是把一个大项目分为多个相互联系,但也可独立运行的小项目,并分别完成,在此过程中软件一直处于可使用状态。敏捷开发是由一些业界专家针对一些企业现状提出了一些让软件开发团队具有快速工作、响应变化能力的价值观和原则,并于2001初成立了敏捷联盟。他们 正在通过亲身实践以及帮助他人实践,揭示更好的软件开发方法。 敏捷开发(agile development)概念从2004年初开始广为流行。Bailar非常支持这一理论,他采取了"敏捷方式"组建团队:Capital One的"敏捷团队" 包括3名业务人员、两名操作人员和5?7名IT人员,其中包括1个业务信息指导(实际上是业务部门和IT部门之间的"翻译者");另夕卜,还有一个由项目经理和至少80名开发人员组成的团队。这些开发人员都曾被Bailar送去参加过" 敏捷开发"的培训,具备相关的技能。 每个团队都有自己的敏捷指导(Bailar聘用了20个敏捷指导),他的工作是关注流程并提供建议和支持。最初提出的需求被归纳成一个目标、一堆记 录详细需要的卡片及一些供参考的原型和模板。在整个项目阶段,团队人员密切合作,开发有规律地停顿--在9周开发过程中停顿3?4次,以评估过程及决定需求变更是否必要。在Capital One大的IT项目会被拆分成多个子项目,安排给各"敏捷团队",这种方式在"敏捷开发"中叫"蜂巢式(swarming)",所有过程由一名项目经理控制。 为了检验这个系统的效果,Bailar将项目拆分,从旧的"瀑布式"开发转变为"并列式"开发,形成了"敏捷开发"所倡导的精干而灵活的开发团队,并将开发阶段分成30天一个周期,进行"冲刺"--每个冲刺始于一个启动会议,到下个冲刺前结束。 在Bailar将其与传统的开发方式做了对比后,他感到非常兴奋--"敏捷开发"使开发时间减少了30%-40%有时甚至接近50%提高了交付产品的质量"不过,有些需求不能用敏捷开发来处理。"Bailar承认,"敏捷开发"也有局限性,比如对那些不明确、优先权不清楚的需求或处于"较快、较便宜、较优" 的三角架构中却不能排列出三者优先级的需 求。此外,他觉得大型项目或有特 殊规则的需求的项目,更适宜采用传统的开发方式。尽管描述需求一直是件困难的事,但经过阵痛之后,需求处理流程会让CIO受益匪浅。 敏捷开发是由一些业界专家针对一些企业现状提出了一些让软件开发团队具有快速工作、响应变化能力的价值观和原则,并于2001初成立了敏捷联盟 他们正在通过亲身实践以及帮助他人实践,揭示更好的软件开发方法。通过这项工作,他们认为: 个体和交互胜过过程和工具 ?可以工作的软件胜过面面俱到的文档 客户合作胜过合同谈判

从零开始搭建Linux驱动开发环境

参考: 韦东山视频第10课第一节内核启动流程分析之编译体验 第11课第三节构建根文件系统之busybox 第11课第四节构建根文件系统之构建根文件系统韦东山书籍《嵌入式linux应用开发完全手册》 其他《linux设备驱动程序》第三版 平台: JZ2440、mini2440或TQ2440 交叉网线和miniUSB PC机(windows系统和Vmware下的ubuntu12.04) 一、交叉编译环境的选型 具体的安装交叉编译工具,网上很多资料都有,我的那篇《arm-linux- gcc交叉环境相关知识》也有介绍,这里我只是想提示大家:构建跟文件系统中所用到的lib库一定要是本系统Ubuntu中的交叉编译环境arm-linux- gcc中的。即如果电脑ubuntu中的交叉编译环境为arm-linux-

二、主机、开发板和虚拟机要三者互通 w IP v2.0》一文中有详细的操作步骤,不再赘述。 linux 2.6.22.6_jz2440.patch组合而来,具体操作: 1. 解压缩内核和其补丁包 tar xjvf linux-2.6.22.6.tar.bz2 # 解压内核 tar xjvf linux-2.6.22.6_jz2440.tar.bz2 # 解压补丁

cd linux_2.6.22.6 patch –p1 < ../linux-2.6.22.6_jz2440.patch 3. 配置 在内核目录下执行make 2410_defconfig生成配置菜单,至于怎么配置,《嵌入式linux应用开发完全手册》有详细介绍。 4. 生成uImage make uImage 四、移植busybox 在我们的根文件系统中的/bin和/sbin目录下有各种命令的应用程序,而这些程序在嵌入式系统中都是通过busybox来构建的,每一个命令实际上都是一个指向bu sybox的链接,busybox通过传入的参数来决定进行何种命令操作。 1)配置busybox 解压busybox-1.7.0,然后进入该目录,使用make menuconfig进行配置。这里我们这配置两项 一是在编译选项选择动态库编译,当然你也可以选择静态,不过那样构建的根文件系统会比动态编译的的大。 ->Busybox Settings ->Build Options

Linux网络设备驱动

嵌入式培训专家
Linux网络设备驱动
主讲:宋宝华
https://www.doczj.com/doc/bb18939033.html,

华清远见
今天的内容
vLinux网络设备驱动架构 vLinux网络设备驱动数据流程
? NON-NAPI模式数据接收流程 ? NAPI模式数据接收流程 ? 数据发送流程
vLinux网络协议栈的实现
? TCP/UDP/IP/MAC各层数据传递 ? 网络系统调用与socket

华清远见
Linux网络设备驱动架构

华清远见
net_device
struct net_device_ops { int (*ndo_open)(struct net_device *dev); int (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev); int (*ndo_set_mac_address)(struct net_device *dev, void *addr); int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); void (*ndo_tx_timeout) (struct net_device *dev); ... }
struct net_device { struct net_device_stats stats; const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; ... }
struct ethtool_ops { int (*get_settings)(struct net_device *, struct ethtool_cmd *); int (*set_settings)(struct net_device *, struct ethtool_cmd *); void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *); int (*get_regs_len)(struct net_device *); ... }

软件测试练习题

练习题 1.软件调试的目的是? A A. 找出错误所在并改正之 B. 排除存在错误的可能性 C. 对错误性质进行分类 D. 统计出错的次数 2.下列叙述中,哪一项是正确的 ...? D A.用黑盒法测试时,测试用例是根据程序内部逻辑设计的; B.测试是为了验证该软件已正确地实现了用户的要求; C.对面向对象程序来说,单元测试的最小单元是每条程序语句,即以分号结尾的程序; D.发现错误多的程序模块,残留在模块中的错误也多。 3.创建一个基于JUNIT的单元测试类,该类必须扩展? C A.TestSuite B. Assert C. TestCase D. JFCTestCase 4.以下对单元测试,不正确 ...的说法是? C A.单元测试的主要目的是针对编码过程中可能存在的各种错误; B.单元测试一般是由程序开发人员完成的 C.单元测试是一种不需要关注程序结构的测试; D.单元测试属于白盒测试的一种。 5.测试驱动开发的含义是? B A.先写程序后写测试的开发方法 B. 先写测试后写程序,即“测试先行” C. 用单元测试的方法写测试 D. 不需要测试的开发 6.用JUNIT断言一个方法输出的是指定字符串,应当用的断言方法是? C A.assertNotNull( ) B. assertSame() C. assertEquals() D. assertNotEquals() 7.TestCase是junit.framework中的一个? C A.方法 B. 接口 C. 类 D. 抽象类

8.TestSuite是JUNIT中用来? A A.集成多个测试用例 B. 做系统测试用的 C. 做自动化测试用的 D. 方法断言 9.对于测试程序的一些命名规则,以下说法正确 ..的一项是? C A.测试类的命名只要符合Java类的命名规则就可以了; B.测试类的命名一般要求以Test打头,后接类名称,如:TestPerson; C.测试类的命名一般要求以Test结尾,前接类名称,如:PersonTest; D.测试类中的方法都是以testXxx()形式出现。 10.以下不属于单元测试优点的一项是? D A.它是一种验证行为 B. 它是一种设计行为 C.它是一种编写文档的行为 D. 它是一种评估行为 数据驱动测试也称? C A.单元测试 B. 白盒测试 C. 黑盒测试 D. 确认测试 11.逻辑驱动测试也称? C A.单元测试 B. 灰盒测试 C. 白盒测试 D. 用户测试 12.以下不属于白盒测试的优点是? B A.增大代码的覆盖率 B.与软件的内部实现无关 C.提高代码的质量 D.发现代码中隐藏的问题 13.组装测试又称为? A A.集成测试 B. 系统测试 C. 回归测试 D. 确认测试 14.对于单元测试框架,除了用于Java的JUnit还有CppUnit、NUnit等,它们是? A A.C++单元测试框架、.NET单元测试框架 B. C语言单元测试框架、通用单元测试框架 C.C++单元测试框架、自动化单元测试框架 D. 自动化单元测试框架、.NET单元测试框架 15.对于JFCUnit,以下说法不正确 ...的是? D A. 它是JAVA GUI的测试框架 B. 它是JUnit的扩展,用于GUI的测试 C.编写JFCUnit的测试用例需要扩展JFCTestCase

相关主题
文本预览
相关文档 最新文档