Presentation is loading. Please wait.

Presentation is loading. Please wait.

— 嵌入式系统软件开发技术 嵌入式系统 2006年6月23日.

Similar presentations


Presentation on theme: "— 嵌入式系统软件开发技术 嵌入式系统 2006年6月23日."— Presentation transcript:

1 — 嵌入式系统软件开发技术 嵌入式系统 2006年6月23日

2 主要内容 版级支持包 BSP 嵌入式系统初始化以及BSP的设计 Linux系统驱动程序开发 嵌入式联网

3 BSP的概念 BSP全称“板级支持包”(Board Support Packages),说的简单一点,就是一段启动代码,和计算机主板的BIOS差不多,但提供的功能区别就相差很大 在Windows CE中,BSP是驱动程序、OEM适应层(OEM Adaptation Layers,OAL)、硬件抽象层(HAL)以及启动设备和使外设正常工作所需BIOS文件的集合。

4 BSP和BIOS区别 BIOS主要是负责在电脑开启时检测、初始化系统设备(设置栈指针,中断分配,内存初始化..)、装入操作系统并调度操作系统向硬件发出的指令。 BSP是和操作系统绑在一起运行,尽管BSP的开始部分和BIOS所做的工作类似,但是 BSP还包含和系统有关的基本驱动 BIOS程序是用户不能更改,编译编程的,只能对参数进行修改设置,但是程序员还可以编程修改BSP,在BSP中任意添加一些和系统无关的驱动或程序,甚至可以把上层开发的统统放到BSP中

5 不同系统中的BSP 一个嵌入式操作系统针对不同的CPU,会有不同的BSP 即使同一种CPU,由于外设的一点差别BSP相应的部分也不一样

6 BSP的特点与功能 硬件相关性 操作系统相关性
不同的操作系统具有各自的软件层次结构, 因此,不同的操作系统具有特定的硬件接口形式

7 BSP的设计与实现 为实现上述两部分功能,设计一个完整的BSP需要完成两部分工作: 设计初始化过程,完成嵌入式系统的初始化;
设计硬件相关的设备驱动,完成操作系统及应用程序对具体硬件的操作。

8 嵌入式系统初始化以及BSP的功能 嵌入式系统的初始化过程是一个同时包括硬件初始化和软件初始化的过程;而操作系统启动以前的初始化操作是BSP的主要功能之一 初始化过程总可以抽象为三个主要环境,按照自底向上、从硬件到软件的次序依次为: 片级初始化 板级初始化 系统级初始化

9 初始化过程 片级初始化: 主要完成CPU的初始化 片级初始化把CPU从上电时的缺省状态逐步设置成为系统所要求的工作状态
这是一个纯硬件的初始化过程

10 初始化过程(续1) 板级初始化: 完成CPU以外的其他硬件设备的初始化
同时还要设置某些软件的数据结构和参数,为随后的系统级初始化和应用程序的运行建立硬件和软件环境 这是一个同时包含软硬件两部分在内的初始化过程

11 初始化过程(续2) 系统级初始化: 这是一个以软件初始化为主的过程,主要进行操作系统初始化
BSP将控制转交给操作系统,由操作系统进行余下的初始化操作: 包括加载和初始化与硬件无关的设备驱动程序 建立系统内存区 加载并初始化其他系统软件模块(如网络系统、文件系统等) 最后,操作系统创建应用程序环境并将控制转交给应用程序的入口

12 硬件相关的设备驱动程序 BSP另一个主要功能是硬件相关的设备驱动
与初始化过程相反,硬件相关的设备驱动程序的初始化和使用通常是一个从高层到底层的过程 尽管BSP中包含硬件相关的设备驱动程序,但是这些设备驱动程序通常不直接由BSP使用 而是在系统初始化过程中由BSP把它们与操作系统中通用的设备驱动程序关联起来,并在随后的应用中由通用的设备驱动程序调用,实现对硬件设备的操作。

13 BSP开发的前提和步骤 开发的前提 : BSP开发的一般步骤如下: 熟悉硬件方面:使用CPU等
熟悉工具方面:电表,示波器,逻辑分析仪,硬件仿真器,仿真调试环境等 语言方面:汇编语言,C语言 BSP开发的一般步骤如下: 硬件主板研制,测试 操作系统的选定,BSP编程 上层应用程序的开发

14 编写BSP函数 BSP对板卡中每个芯片的操作都通过多个函数来完成
对每个芯片来说,都应当有初始化函数和状态读取函数

15 设计实现BSP的一般方法 BSP的开发需要具备一定的硬件知识 要求掌握操作系统所定义的BSP接口 两种快捷方法
“自顶向下”地设计硬件相关的驱动程序

16 BSP设计方法的不足与改进 目前BSP的设计与实现主要是针对某些特定的文件进行修改
直接修改相关文件容易造成代码的不一致性,增加软件设计上的隐形错误,从而增加系统调试和代码维护的难度 解决这个问题的一个可行办法是:设计实现一种具有图形界面的BSP开发设计向导,由该向导指导设计者逐步完成BSP的设计和开发,并最终由向导生成相应的BSP文件,而不再由设计人员直接对源文件进行修改。

17 Linux设备驱动程序及开发

18 Linux设备驱动程序概述 Linux设备驱动程序是处理或操作硬件控制器的软件,被集成在内核中,是常驻内存的低级硬件处理程序的共享库,设备驱动程序是系统对设备的抽象管理与控制。 Linux允许设备驱动程序作为内核可加载模块实现,即除了可以在系统启动时进行注册外,还可以在启动后进行加载注册。

19 Linux驱动程序开发 建立嵌入式Linux平台,移植和编写驱动程序往往是最具挑战的工作
驱动程序的开发周期一般较长,对产品的面世时间有着重要影响 驱动程序质量的好坏,直接关系到系统工作效能和稳定性,对项目的成败起着关键作用

20 设备驱动程序主要功能 设备驱动程序主要完成如下功能: 检测设备和初始化设备 使设备投入运行和退出服务 从设备接收数据并提交给内核
从内核接收数据送到设备 检测和处理设备错误

21 Linux设备驱动程序分类 Linux中所有设备被抽象出来,都看成文件 设备的读写和普通文件一样 Linux系统的设备分为如下三类:
字符设备(char device) 块设备(block device) 网络设备(network device) 字符设备是指存取时没有缓存的设备 块设备的读写都有缓存来支持,且块设备必须能够随机存取(random access) 网络设备在Linux里做专门的处理

22 Linux设备驱动程序分类 网络设备在Linux里做专门的处理
Linux的网络系统主要是基于BSD unix的socket 机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递 系统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持

23 Linux设备驱动程序分类 典型的字符设备包括鼠标,键盘,串行口等 块设备主要包括硬盘、软盘设备、CD-ROM等
一个文件系统要安装进入操作系统必须在块设备上

24 Linux驱动程序介绍 嵌入式Linux驱动已经支持的设备门类齐全,已成为其相对其他嵌入式操作系统的一大优势 工业控制常用的串口,并口
人机输入设备如鼠标、键盘,触摸屏 彩色、黑白液晶显示输出 网络的完善支持,包括tcp/ip,udp,firewall,WLAN,ip forwarding,ipsec,vpn Usb的全面支持,包括usb硬盘、u盘,usb摄像头 支持丰富的文件系统,包括FAT32,NTFS

25 嵌入式设备框图

26 驱动程序的功用 1、驱动程序直接操控硬件 收发通讯数据 读写存储介质,比如flash或硬盘 操作输出设备和执行机构,例如打印,开关门禁等

27 驱动程序的功用(续) 2、驱动程序提供软件访问硬件的机制 应用软件通过驱动程序安全高效的访问硬件 驱动程序文件可以方便的提供访问权限控制
驱动程序作为一个隔离的中间层软件,将底 层细节隐藏起来,提高了软件的可移植性

28 访问Linux设备驱动的方法 设备提供dev文件系统节点和proc文 件系统节点 应用程序通过dev文件节点访问驱动 程序
询设备驱动的信息

29 驱动程序位置 驱动程序位于drivers目录下 通常驱动程序占kernel代码的50%
Linux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。 在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。 在2.0.xx到2.2.xx的变动里,驱动程序的编写做了一些改变,但是从2.0.xx的驱动到2.2.xx的移植只需做少量的工作。

30 Linux驱动程序的特点 嵌入式Linux驱动程序需求多样 嵌入式设备硬件各异
嵌入式计算平台往往资源有限,比如处理速度、存储器容量、总线带宽、电池容量等 通常要求短的开发周期、压力大 开发驱动程序需要丰富的专业知识,包括 硬件和软件知识

31 嵌入式Linux驱动程序特点 嵌入式系统硬件更新速度加快 国际上大的嵌入式芯片提供商如Intel、
Samsung、Freescale、TI、ST每年都有大量新品推出 新的芯片功能总是需要相应的驱动程序支持

32 Linux驱动开发流程 熟悉设备的特性 确定设备驱动程序类别 编写测试用例 搜集可重用的代码 编写自己的驱动程序代码 编码、调试、测试

33 Linux驱动程序的开发环境 本机编译调试 主机+目标机 开发环境配置简单 无需网络环境 适用于配置较高的x86机器
主机可以自由选择Linux或Windows+Cygwin 主机和目标机通过网络共享文件系统 内核崩溃不会影响主机

34 Linux驱动程序的开发环境(续) 主机+目标机环境包括 主机运行的工具链∶cross gcc + glibc + gdb,
如果是windows主机还要有cygwin仿真环境 主机运行远程服务,常用的有tftp用来传送内 核映像、initrd,NFS用来共享文件系统 目标机运行ssh或telnet等远程登陆服务,用来 调试驱动程序

35 Linux驱动程序的加载方式 驱动程序直接编译入内核 驱动程序以模块形式存储在文件系 统里,需要时动态载入内核
驱动程序在内核启动时就已经在内存中 可以保留专用存储器空间 驱动程序以模块形式存储在文件系 统里,需要时动态载入内核 驱动程序按需加载,不用时节省内存 驱动程序相对独立于内核,升级灵活

36 Linux驱动程序模块加载

37 Linux驱动程序开发的任务 规划硬件资源的使用 分离硬件相关和硬件无关的代码 划分驱动程序的抽象层次 移植驱动程序到新的平台

38 Linux驱动程序开发的任务 规划硬件资源的使用 CPU时间片分配 中断处理 系统存储器空间映射

39 设备存储器的映射

40 Linux驱动程序开发的任务 分离硬件相关和硬件无关的代码 划分驱动程序的抽象层次

41 Linux驱动程序开发的任务 移植驱动程序到新的平台

42 GPL对驱动程序开发的影响 实现非GPL授权的方法——模块形 式动态加载 驱动程序可以以私有产权形式进行 商业授权

43 设备驱动程序的代码 驱动程序的注册与注销 设备的打开与释放 设备的读写操作 设备的控制操作 register_chrdev()
register_blkdev() 设备的打开与释放 open() release() 设备的读写操作 read() write() 设备的控制操作 ioctl()

44 设备驱动的加载 使用模块的方式动态加载驱动 将驱动静态编译到内核里面 int func_init(void) Makefile:
insmod xx.o lsmod rmmod xx.o 将驱动静态编译到内核里面 int __init func_init(void) 启动时自动加载

45 Linux驱动程序模块加载

46 编写驱动程序的一些基本概念 无论是什么操作系统的驱动程序,都有一些通用的概念 操作系统提供给驱动程序的支持也大致相同
以下简单介绍网络设备驱动程序的一些基本要求

47 编写网络驱动程序 发送和接收 这是一个网络设备最基本的功能
发送和接收 这是一个网络设备最基本的功能 如一块网卡所做的无非就是收发工作。所以驱动程序里要告诉系统发送函数在哪里,系统在有数据要发送时就会调用发送程序。 驱动程序由于是直接操纵硬件的,所以网络硬件有数据收到,最先能得到这个数据的也就是驱动程序,它负责把这些原始数据进行必要的处理,然后送给系统。 这里,操作系统必须要提供两个机制: 找到驱动程序的发送函数 驱动程序把收到的数据送给系统

48 编写驱动程序 读写 几乎所有设备都有输入和输出。每个驱动程序要负责本设备的读写操作。
操作系统不需要知道对设备的具体读写操作怎样进行,这些都由驱动程序屏蔽掉了 操作系统定义好一些读写接口,由驱动程序完成具体的功能 在驱动程序初始化时,需要把具有这种接口的读写函数注册进操作系统

49 编写驱动程序 中断 中断在现代计算机结构中有重要的地位 操作系统必须提供驱动程序响应中断的能力 一般是把一个中断处理程序注册到系统中去
操作系统在硬件中断发生后调用驱动程序的处理程序 Linux支持中断的共享,即多个设备共享一个中断

50 编写驱动程序 时钟 在实现驱动程序时,很多地方会用到时钟。如某些协议里的超时处理,没有中断机制的硬件的轮询等。
操作系统应为驱动程序提供定时机制 一般是在预定的时间过了以后回调注册的时钟函数

51 内核模块 模块是内核的一部分,但是并没有被编译到内核里去。它们被分别编译和连接成目标文件。
用命令insmod插入一个模块到内核中,用命令rmmod卸载一个模块 在Linux内核中,以下内容一般编译成模块: 大多数的驱动程序。包括SCSI设备,CD-ROM,网络设备,不常用的字符设备,如打印机,watchdog等。 大多数文件系统,理论上除了根文件系统不能是模块,其他文件系统都可以是模块。 一些内核支持的不常用的可执行文件格式,如binfmt_misc。

52 kmod 和高级模块化 Linux 提供了对模块自动加载和卸载的支持
按需加载的例子 :ALSA(Advanced Linux Sound Architecture)声卡驱动程序组的实现

53 常用的系统支持 内存申请和释放 中断 时钟 I/O 中断打开关闭 输出信息 注册驱动程序

54 内存申请和释放 include/linux/kernel.h里声明了kmalloc()和kfree()。用于在内核模式下申请和释放内存。
与用户模式下的malloc()不同,kmalloc()申请空间有大小限制。长度是2的整次方。可以申请的最大长度也有限制。另外kmalloc()有priority参数 Kfree()释放的内存必须是kmalloc()申请的

55 申请中断和释放中断 request_irq()、free_irq() 是驱动程序申请中断和释放中断的调用。
在include/linux/sched.h里声明

56 时钟 时钟的处理类似中断,也是登记一个时间处理函数,在预定的时间过后,系统会调用这个函数。
在include/linux/timer.h里声明 使用时钟,先声明一个timer_list结构,调用init_timer对它进行初始化。Time_list结构里expires是标明这个时钟的周期,单位采用jiffies的单位。

57 I/O I/O端口的存取使用: 在include/adm/io.h里定义
inline unsigned int inb(unsigned short port); inline unsigned int inb_p(unsigned short port); inline void outb(char value, unsigned short port); inline void outb_p(char value, unsigned short port); 在include/adm/io.h里定义

58 中断打开关闭 系统提供给驱动程序开放和关闭响应中断的能力 是在include/asm/system.h
#define cli() __asm__ __volatile__ ("cli"::) #define sti() __asm__ __volatile__ ("sti"::)

59 输出信息 驱动程序要输出信息使用printk() include/linux/kernel.h里声明

60 注册驱动程序 如果使用模块(module)方式加载驱动程序,需要在模块初始化时把设备注册到系统设备表里去 不再使用时,把设备从系统中卸除
定义在drivers/net/net_init.h里的两个函数完成这个工作 Int register_netdev(struct device *dev); void unregister_netdev(struct device *dev);

61 网络驱动程序的结构 所有的Linux网络驱动程序遵循通用的接口 设计时采用的是面向对象的方法
一个设备就是一个对象(device 结构),它内部有自己的数据和方法 每一个设备的方法被调用时的第一个参数都是这个设备对象本身。这样这个方法就可以存取自身的数据(类似面向对象程序设计时的this引用) 一个网络设备最基本的方法有初始化、发送和接收

62 网络驱动程序的结构(续) 初始化程序完成硬件的初始化、device中变量的初始化和系统资源的申请
发送程序是在驱动程序的上层协议层有数据要发送时自动调用的。一般驱动程序中不对发送数据进行缓存,而是直接使用硬件的发送功能把数据发送出去 接收数据一般是通过硬件中断来通知的。在中断处理程序里,把硬件帧信息填入一个skbuff结构中,然后调用netif_rx( )传递给上层处理

63 网络驱动程序的基本方法 初始化(initialize) 打开(open) 关闭(stop ) 发送(hard_start_xmit)
接收(reception) 硬件帧头(hard_header) 地址解析(xarp) 参数设置和统计数据

64 网络驱动程序的基本方法--初始化 驱动程序必须有一个初始化方法 在把驱动程序载入系统的时候会调用这个初始化程序。它做以下几方面的工作:
检测设备:在初始化程序里你可以根据硬件的特征检查硬件是否存在,然后决定是否启动这个驱动程序。 配置和初始化硬件:在初始化程序里可以完成对硬件资源的配置,比如即插即用的硬件就可以在这个时候进行配置(Linux内核对PnP功能没有很好的支持,可以在驱动程序里完成这个功能)。 配置或协商好硬件占用的资源以后,就可以向系统申请这些资源。有些资源是可以和别的设备共享的,如中断。有些是不能共享的,如IO、DMA。 初始化device结构中的变量 让硬件正式开始工作

65 网络驱动程序的基本方法—打开 打开(open) open这个方法在网络设备驱动程序里是在网络设备被激活的时候被调用(即设备状态由down-->up) 实际上很多在initialize中的工作可以放到这里来做。比如资源的申请、硬件的激活。 如果dev->open返回非0(error),则硬件的状态还是down open方法另一个作用是如果驱动程序做为一个模块被装入,则要防止模块卸载时设备处于打开状态。 在open方法里要调用MOD_INC_USE_COUNT宏

66 网络驱动程序的基本方法—关闭 关闭(stop) close方法做和open相反的工作。可以释放某些资源以减少系统负担。
close是在设备状态由up转为down时被调用的 如果是做为模块装入的驱动程序,close里 应该调用MOD_DEC_USE_COUNT,减少设备被引用的次数,以使驱动程序可以被卸载。 close方法必须返回成功(0==success)

67 网络驱动程序的基本方法—发送 发送(hard_start_xmit) 所有的网络设备驱动程序都必须有这个发送方法。
在系统调用驱动程序的xmit时,发送的数据放在一个sk_buff结构中。 一般的驱动程序把数据传给硬件发出去。也有一些特殊的设备比如loopback把数据组成一个接收数据再回送给系统,或者 dummy设备直接丢弃数据。 如果发送成功,hard_start_xmit方法里释放sk_buff,返回0(发送成功) 如果设备暂时无法处理,比如硬件忙,则返回1。这时如果dev->tbusy置为非0,则系统认为硬件忙,要等到dev->tbusy置0以后才会再次发送。tbusy的置0任务一般由中断完成。

68 网络驱动程序的基本方法—发送(续) 硬件在发送结束后产生中断,这时可以把tbusy置0,然后用mark_bh()调用通知系统可以再次发送。
在发送不成功的情况下,也可以不置dev->tbusy为非0,这样系统会不断尝试重发。 如果hard_start_xmit发送不成功,则不要释放sk_buff。 传送下来的sk_buff中的数据已经包含硬件需要的帧头。所以在发送方法里不需要再填充硬件帧头,数据可以直接提交给硬件发送。sk_buff是被锁住的(locked), 确保其他程序不会存取它。

69 网络驱动程序的基本方法—接收 接收(reception) 驱动程序并不存在一个接收方法。有数据收到应该是驱动程序来通知系统的。
一般设备收到数据后都会产生一个中断,在中断处理程序中驱动程序申请一块sk_buff(skb),从硬件读出数据放置到申请好的缓冲区里。接下来填充sk_buff中的一些信息。skb->dev = dev,判断收到帧的协议类型,填入skb->protocol(多协 议的支持)。 把指针skb->mac.raw指向硬件数据然后丢弃硬件帧头(skb_pull)。

70 网络驱动程序的基本方法—接收(续) 还要 设置skb->pkt_type,标明第二层(链路层)数据类型。可以是以下类型: PACKET_BROADCAST : 链路层广播 PACKET_MULTICAST : 链路层组播 PACKET_SELF : 发给自己的帧 PACKET_OTHERHOST : 发给别人的帧(监听模式时会有这种帧) 最后调用netif_rx()把数据传送给协议层。netif_rx()里数据放入处理队列然后返回,真正的处理是在中断返回以后,这样可以减少中断时间。 调用netif_rx()以后,驱动程序就不能再存取数据缓冲区skb。

71 网络驱动程序的基本方法—硬件帧头 硬件一般都会在上层数据发送之前加上自己的硬件帧头,比如以太网(Ethernet)就有14字节的帧头。这个帧头是加在上层ip、ipx等数据包的前面的。 驱动程序提供一个hard_header方法,协议层(ip、ipx、arp等)在发送数据之前会调用这段程序。 硬件帧头的长度必须填在dev-hard_header_len,这样协议层回在数据之前保留好硬件帧头的空间。这样hard_header程序只要调用skb_push然后正确填入硬件帧头就可以了。

72 网络驱动程序的基本方法—硬件帧头(续1) 在协议层调用hard_header时,传送的参数包括(2.0.xx):
数据的sk_buff device指针 Protocol 目的地址(daddr) 源地址(saddr) 数据长度(len) 数据长度不要使用sk_buff中的参数,因为调用hard_header时数据可能还没完全组织好 saddr是NULL的话是使用缺省地址(default) daddr是NULL表明协议层不知道硬件目的地址 如果hard_header完全填好了硬件帧头,则返回添加的字节数。

73 网络驱动程序的基本方法—硬件帧头(续2) 如果硬件帧头中的信息还不完全(比如daddr为NULL,但是帧头中需要目的硬件地址。典型的情况是以太网需要地址解析(arp)),则返回负字节数。hard_header返回负数的情况下,协议层会做进一步的build header的工作。 目前Linux系统里就是做arp (如果hard_header返回正,dev->arp=1,表明不需要做arp,返回负,dev->arp=0,做arp)。 对hard_header的调用在每个协议层的处理程序里。如ip_output。

74 网络驱动程序的基本方法—地址解析 地址解析(xarp)
有些网络有硬件地址(比如Ethernet),并且在发送硬件帧时需要知道目的硬件地址。这样就需要上层协议地址(ip、ipx)和硬件地址的对应。这个对应是通过地址 解析完成的。 需要做arp的的设备在发送之前会调用驱动程序的rebuild_header方法。调用的主要参数包括: 指向硬件帧头的指针 协议层地址 如果驱动程序能够解析硬件地址,就返回1,如果不能,返回0。 对rebuild_header的调用在net/core/dev.c的do_dev_queue_xmit()里。

75 网络驱动程序的基本方法—参数设置和统计数据
参数设置和统计数据 在驱动程序里还提供一些方法供系统对设备的参数进行设置和读取信息。 一般只有超级用户(root)权限才能对设备参数进行设置。 设置方法有: dev->set_mac_address() 当用户调用ioctl类型为SIOCSIFHWADDR时是要设置这个设备的mac地址。一般对mac地址的设置没有太大意义的。

76 网络驱动程序的基本方法—参数设置和统计数据(续)
dev->set_config() 当用户调用ioctl时类型为SIOCSIFMAP时,系统会调用驱动程序的set_config方法。用户会传递一个ifmap结构包含需要的I/O、中断等参数。 dev->do_ioctl() 如果用户调用ioctl时类型在SIOCDEVPRIVATE和SIOCDEVPRIVATE+15之间,系统会调用驱动程序的这个方法。一般是设置设备的专用数据。 读取信息也是通过ioctl调用进行。 除次之外驱动程序还可以提供一个dev->get_stats方法,返回一个enet_statistics结构,包含发送接收的统计信息。 ioctl的处理在net/core/dev.c的dev_ioctl()和dev_ifsioc()里

77 网络驱动程序中用到的数据结构 最重要的是网络设备的数据结构。它定义在include/linux/netdevice.h sk_buff
Linux网络各层之间的数据传送都是通过sk_buff

78 编写Linux网络驱动程序中需要注意的问题
中断共享 硬件发送忙时的处理 流量控制(flow control) 调试

79 中断共享 Linux系统运行几个设备共享同一个中断。需要共享的话,在申请的时候指明共享方式。
系统提供的request_irq()调用的定义: int request_irq(unsigned int irq, void (*handler)(int irq, void *dev_id, struct pt_regs *regs), unsigned long irqflags, const char * devname, void *dev_id); 如果共享中断,irqflags设置SA_SHIRQ属性,这样就允许别的设备申请同一个中断。需要注意所有用到这个中断的设备在调用request_irq()都必须设置这个属性。系统在回调每个中断处理程序时,可以用dev_id这个参数找到相应的设备。一 般dev_id就设为device结构本身。系统处理共享中断是用各自的dev_id参数依次调用每一个中断处理程序。

80 硬件发送忙时的处理 主CPU的处理能力一般比网络发送要快,所以经常会遇到系统有数据要发,但上一包数据网络设备还没发送完。因为在Linux里网络设备驱动程序一般不做数据缓存,不能发送的数据都是通知系统发送不成功,所以必须要有一个机制在硬件不忙时及时通知系统接着发送下面的数据。 一般对发送忙的处理在前面设备的发送方法(hard_start_xmit)里已经描述过,即如果发送忙,置tbusy为1。处理完发送数据后,在发送结束中断里清tbusy,同时用mark_bh()调用通知系统继续发送。 但在具体实现驱动程序时发现,这样的处理系统好象并不能及时地知道硬件已经空闲了,即在mark_bh()以后,系统要等一段时间才会接着发送。造成发送效率很低。 实现时不把tbusy置1,让系统始终认为硬件空闲,但是报告发送不成功。系统会一直尝试重发。这样处理就运行正常了。

81 流量控制 网络数据的发送和接收都需要流量控制。这些控制是在系统里实现的,不需要驱动程序做工作。
每个设备数据结构里都有一个参数dev->tx_queue_len,这个参数 标明发送时最多缓存的数据包。在Linux系统里以太网设备(10/100Mbps) tx_queue_len一般设置为100,串行线路(异步串口)为10。 实际上,设置了dev->tx_queue_len并不是为缓存这些数据申请了空间。这个参数只是在收到协议层的数据包时判断发送队列里的数据是不是到了tx_queue_len的限度,以决定这一包数据加不加进发送队列。发送时另一个方面的流控是更高层协议的发送窗口(TCP协议里就有发送窗口)。 达到了窗口大小,高层协议就不会再发送数据。 接收流控也分两个层次。netif_rx()缓存的数据包有限制。另外高层协议也会有一个最大的等待处理的数据量。 发送和接收流控处理在net/core/dev.c的do_dev_queue_xmit()和netif_rx() 中。

82 调试 很多Linux的驱动程序都是编译进内核的,形成一个大的内核文件。但对调试来说,这是相当麻烦的。调试驱动程序可以用module方式加载。
支持模块方式的驱动程序必须提供两个函数:int init_module(void)和void cleanup_module(void)。 init_module()在加载此模块时调用,在这个函数里可以register_netdev()注册设备。init_module()返回0表示成功,返回负表示失败。 cleanup_module()在驱动程序被卸载时调用,清除占用的资源,调用unregister_netdev()。 模块可以动态地加载、卸载。在2.0.xx版本里,还有kerneld自动加载模块,但是2.2.xx中已经取消了kerneld。手工加载使用insmod命令,卸载用rmmod命令,看内核中的模块用lsmod命令。 编译驱动程序用gcc,主要命令行参数-DKERNEL -DMODULE。并且作为模块加载的驱动程序,只编译成obj形式(加-c参数)。编译好的目标文件放在/lib/modules/2.x.xx/misc下,在启动文件里用insmod加载

83 Linux驱动程序可利用资源 互联网上有很多驱动程序资源: www.kernel.org www.linuxdevice.org
sourceforge.net

84 驱动源代码

85 驱动源代码

86 驱动源代码

87 驱动源代码

88 驱动源代码

89 编译驱动程序

90 应用程序

91 编译应用程序

92 背景知识: Linux设备管理

93 主要内容 概述 驱动程序基础 中断处理 辅助函数 设备驱动程序 模块编程基础 字符设备 块设备 网络设备

94 概述 输入输出子系统: 下层:设备驱动程序 上层:设备无关部分——VFS in Linux? Unix和Linux的设备管理方法: VFS

95 设备管理总体结构示意 用户程序 系统调用接口 文件系统 高速缓存 字符设备 块设备 驱动程序 硬件设备

96 输入输出系统层次结构 I/O请求 I/O应答 用户进程 设备无关软件 设备驱动程序 设备服务子程序 中断处理程序 硬件
命名、保护、阻塞、缓冲、分配 建立设备寄存器、检测状态 I/O结束时,唤醒设备服务子程序 执行I/O操作 用户进程 设备无关软件 设备驱动程序 设备服务子程序 中断处理程序 硬件

97 驱动程序基础——I/O空间 Linux中的三种地址空间: 常见总线 ISA VESA EISA PCI
CPU Untranslated Address CPU Translated Address Bus Address:一般PC机中是一组寄存器 命令more /proc/ioports 常见总线 ISA VESA EISA PCI

98 驱动程序基础——命名空间 并行设备:lp 软盘:fd SCSI盘:sd IDE硬盘:hda1, hda2, hdb等 网络设备:ethn, slipn, pppn等 在写驱动程序的时候,需要给函数名加上选择的前缀来避免任何混淆。如:foo_read(),foo_write()等。

99 驱动程序基础——内存分配 函数kmalloc() 内存以2的幂大小的块分配 有一个优先级参数
宏kfree()和函数kfree_s() kfree()调用kfree_s(),和free()一样工作 可以直接调用kfree_s(),但是需要知道释放内存块的大小

100 驱动程序基础——设备分类 字符设备:不使用缓冲区,顺序读写 foo_read() & foo_write()
块设备:需要使用缓冲区,随机读写 策略规程 网络设备 采用了特殊的处理方法。Struct device

101 驱动程序基础——设备号 主设备号&次设备号 主设备号相同的设备使用相同的驱动程序 次设备号区分具体设备的实例
命令:ls –l /dev/had*

102 驱动程序基础——中断vs轮询 工作机制的区别
编程上的区别: UNIX的系统调用:执行模式的改变 内核模式下的进程访问进程原来所在的用户空间的存储:get_fs_*()和memcpy_fromfs()读用户空间,put_fs_*()和memcpy_tofs()写入用户空间内存。在进程运行时调用,不需要考虑地址的问题。 在中断发生时,这些宏不能使用。因为它们可能覆盖其他运行着的进程的随机空间。必须提供临时空间存放信息。 对于块设备,由cache缓冲机制自动提供;字符设备需要驱动程序分配。

103 驱动程序基础——DMA方式 用于传送大规模的数据
PC机上的ISA DMA控制器 8条DMA通道。每条通道联系着一个16位地址寄存器和16位计数器 DMA直接访问物理内存 DMA通道不能被共享。一些设备拥有固定的DMA通道。 Struct dma_chan结构: 每个通道拥有一个此结构 两个域:指向该通道拥有者的字符串指针 指示该通道是否已分配的标志

104 驱动程序基础——睡眠唤醒机制 TASK_INTERRUPTIBLE TASK_UNITERUPTIBLE, 决定于睡眠是否能够被系统调用一类的事情打断。一般来说,如果设备比较慢、可以被无限阻塞,包括终端、网络设备或伪设备,睡眠应该是可中断的 _sleep_on() Struct wait_queue {struct task_struct *task; struct wait_queue *next; };

105 驱动程序基础——设备文件 设备管理的“上半部分” Struct file结构 include/linux/fs.h
增加一个设备时需要用mknod命令为该设备创建一个inode

106 驱动程序基础——file_operations
lseek():转到所需的偏移。 struct inode *inode 指向此设备inode结构的指针。 Struct file *file 指向此设备的文件结构的指针。 Off_t offset 要转移到的相对origin指示的基准的偏移地址。 Int origin 0 = 采用相对于绝对地址0(开始)的偏移量。 1 = 采用相对于当前位置的偏移量。 2 = 采用相对于末尾的偏移量。 Lseek()在出错是返回出错码 –errno,否则返回lseek操作以后的绝对地址(>=0)。

107 read()和write() struct inode. inode:指向代表要访问的设备的特殊文件的指针。 sturct file
read()和write() struct inode *inode:指向代表要访问的设备的特殊文件的指针。 sturct file *file:指向该设备的文件结构的指针。 Char *buf:一个读写的字符缓冲区。位于用户空间内存中,可以用get_fs*(),put_fs*()和memcpy*fs()访问。 Int count:缓冲区中读或写的字符的计数。它是buf的大小,也是知道怎样到达buf的末尾的手段,因为buf是没有保证以NULL结尾的。

108 Select() struct inode *inode:指向该设备的inode结构的指针。
Struct file *file:指向设备的文件结构的指针。 Int sel_type:可以执行的选择类型 SEL_IN read SEL_OUT write SEL_EX exception Select_table *wait   如果设备没有准备好,调用select_wait(),并且返回0。 如果设备准备好,返回1。

109 ioctl()函数:处理ioctl调用。
结构:首先差错检查,然后用一个大的switch语句来处理所有可能的ioct。 参数: Struct inode *inode Struct file *file Unsigned int cmd :ioctl命令。一般用于做case语句的switch参数。 Unsigned int arg 这是此命令的参数,由用户定义。 返回: 出错返回-error。 其他情况下返回由用户定义。

110 mmap()函数 Struct inode *inode Struct file *file
Unsigned long addr 需要映射进入的主存开始地址。 Size_t len 需要映射的存储空间长度。 Int prot 下面中的一个: PROT_READ 可以读的区域。 PROT_WRITE 可写的区域 PROT_EXEC 可执行的区域 PROT_NONE 不可访问的区域 Unsigned long off 需要映射的文件偏移地址。这个地址将被映射到addr。

111 Struct inode *inode 指向此设备的inode结构的指针。
open()和release()函数 Struct inode *inode 指向此设备的inode结构的指针。 Struct file *file 指向此设备的文件结构的指针。 Open()在设备特殊文件打开时调用。是用来保证一致性的策略机制。 Release()只在进程关闭它打开的最后一个文件描述子的时候调用?

112 init()函数 内核第一次启动时调用: 在正确的位置调用init(): 字符设备drivers/char/mem
init()函数 内核第一次启动时调用: 在正确的位置调用init(): 字符设备drivers/char/mem.c中的chr_dev_init() 把file_operation注册到VFS中: 对于字符设备register_chrdev() 打印关于设备的信息,并且报告找到的硬件 printk()

113 中断处理 文件/proc/interrupts 函数request_irq() 函数free_irq() 睡眠与唤醒 中断共享
ISR的上部和下部(bottomhalf)

114 辅助函数——请求调度 Static void add_request(struct blk_dev_struct *dev,
struct request * req) Static void end_request(struct blk_dev_struct *dev, struct request * req) static void make_request(int major, int rw, struct buffer_head *bh) void ll_rw_block(int rw, int nr, struct buffer_head *bh[]) make_request()调用add_request() ll_rw_block()对请求队列排序,只能通过buffer cache调用。 电梯算法: 读在写前 低次设备号请求在高次设备号前 低块号在高块号前 drivers/block/ll_rw_block.c

115 辅助函数——定时器管理 void add_timer(struct timer_list * timer)
void del_timer(struct timer_list * timer) void init_timer(struct timer_list * timer) struct timer_list

116 辅助函数——中断管理 extern int request_irq(unsigned int irq, void (*handler)(int, void*, struct pt_regs *), unsigned long flags, const char *device, void *dev_id); void free_irq(unsigned int irq) cli() sti()

117 辅助函数——端口读写 void out*() unsigned char byte, unsigned port
unsigned in*() 参数:unsigned port *: b, w, l 用in*()来清空某些状态值?

118 辅助函数——内存管理&设备号 kmalloc() kfree() kfree_s()
设备号由主、次设备号拼接而成。 #define MAJOR(dev) ((dev)>>8) #define MINOR(dev) ((dev)&0xff)

119 辅助函数——设备的注册和注销 int register_chrdev(unsigned int major, const char *name, struct file_operations *fops) int register_blkdev(unsigned int major, const char *name, struct file_operations *fops) int unregister_chrdev(unsigned int major, const char *name) int unregister_blkdev(unsigned int major, const char *name) #include <linux/fs.h> #include <linux/errno.h>

120 辅助函数——内存空间转换 inline void memcpy_tofs(void * to, const void * from,
unsigned long n) inline void memcpy_fromfs(void * to, const void * from, unsigned long n) #include <asm/segment.h> #define get_user(ptr) \ ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))) #define put_user(x,ptr) \ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))) #include <asm/segment.h> 在用户空间和内核空间拷贝内存 属于隐含的I/O操作,不要再临界代码中使用,因为它可能冲破cli()和sti()的保护

121 辅助函数——缓冲区管理 int size); void brelse(struct buffer_head *buf);
fs.c struct buffer_head *getblk(kdev_t dev, int block, int size); void brelse(struct buffer_head *buf); void ll_rw_block(int rw, int nr, struct buffer_head*bh[]);

122 辅助函数——其他 int printk(const char* fmt, ...) #include <linux/kernel.h> 在内核中打印信息,是printf的内核版本 可能导致隐含的I/O操作。不要在cli()保护的代码段中使用,因为它可能导致开中断。 一些与进程相关的系统调用

123 模块编程基础 模块的基本概念: module在需要的时可通过符号表(symbol table)使用核心资源。
可以动态的加载到内核中成为kernel的一部分;加载后可以访问内核的数据结构; 用户空间的程序或进程可以通过某个模块和内核交互。 module在需要的时可通过符号表(symbol table)使用核心资源。 而且module一般需要调用核心的资源,所以必须注意module的版本和核心的版本的相配问题。一般在module的装入过程中检查module的版本信息。

124 模块之间的函数调用 内核可以使用其它模块或内核的函数,也可以export一些函数供其他模块或内核使用。 模块栈:如果模块A使用了模块B的函数,那么B必须在A之前加载,否则加载A的命令不成功。 模块可以使用的函数: 自身定义; 其他module提供; 内核提供 命令ksyms –a:列出已经加载的模块的函数或变量。 Symbol table:记录module导出的函数或变量。 所有声明为global的函数或变量都意味着被导出,可以被其他模块使用。

125 模块编程基础——常用命令 lsmod 把现在 kernel 中已经安装的modules 列出来
insmod 把某个 module 安装到 kernel 中。 rmmod 把某个没在用的 module 从kerne中卸载。 depmod 制造 module dependency file,以告诉将来的 insmod 要去哪儿找modules 来安装。这个 dependency file放在 /lib/modules/[当前kernel版本]/modules.dep。

126 模块基础——装入 Insmod命令 内核kerneld守护进程(daemon)自动装入。 守护进程:在超级用户下运行的一个用户进程,与核心建立一个IPC通道。 Kerneld调用insmod和rmmond来装入和移出模块。kerneld装入的module,一般放在/lib/modules/kernel-version目录下。 insmod 命令调用sys_get_kernel_sys()系统调用收集核心中所有符号来解决module中资源引用问题。

127 模块基础——装入 符号表的记录有两个域:符号的名字(symbol name)和符号的值(一般是符号的地址)。核心提供的符号表在module 链表最尾module中。 insmod调用sys_create_module(),为新module分配一个module数据结构,挂在module_list头上,置新module 状态为UNINITIALIZED。 当初始化module时,insmod调用sys_init_module()系统调用,将module的初始化和清除函数作为参数传递。修改核心的符号表,同时系统需要修改新module依赖的所有module中的相关指针。

128 模块基础——卸载 Rmmod命令 Kerneld进程自动卸载 自动卸载的机制为:每隔一定的时间,kerneld 调用sys_delete_module()系统调用,将它装入的且不在被使用的module从系统中卸载。它遍历module_list,检查被它装入(AUTOCLEAN)并且不用(VISITED标志)的模块。

129 内核模块必须有两个函数: int init_module():为内核中的某些东西注册一个句柄,或者把内核中的程序提换成它自己的代码(通常是进行一些工作以后再调用原来工作的代码)。 void clean_module():模块要求撤销init_module进行的所有修改,使得模块可以被安全的卸载。 在insmod和rmmod命令中使用这两个函数。 Use count:记录使用本模块的进程数或模块数。 MOD_INC_USE_COUNT:增加use count MOD_DEC_USE_COUNT:减少use count MOD_IN_USE:检查use count是否是0

130 模块基础——数据结构 struct module{ struct module *next;
struct module_ref *ref; /* 所有引用该模块的模块,也用链表连接*/ struct symbol_table *symtab; /*符号表*/ const char *name; /*模块的名字,存放在module结构后面的64个字节里 */ int size; /* size of module in pages */ void* addr; /* address of module */ int state; /*三种状态:未初始化,运行,删除*/ void (*cleanup)(void); /* cleanup routine */ };

131 struct symbol_table { int size; /* 包括string table的总长度 */ int n_symbols; int n_refs; struct internal_symbol symbol[0]; struct module_ref ref[0]; }; 后面定义的是两个零大小的数组声明,便于动态分配空间。 symbol包含一组字符串指针,指向真正的符号字符串表

132 string table的内容是该模块导出的函数名和变量名
struct internal_symbol { /*符号信息*/ void *addr; const char *name; /*指向string table*/ }; struct module_ref { /*引用信息*/ struct module *module; struct module_ref *next; string table的内容是该模块导出的函数名和变量名

133

134 模块基础——系统调用 Sys_create_module 为模块分配空间,将模块链入系统的模块链中 Sys_init_module
初始化模块,修正指针使模块正常工作 Sys_delete_module 从系统模块链中删除模块,释放内存空间 Sys_get_kernel_syms 将系统的所有符号表全部取出到用户空间

135

136 ①insmod先调用系统调用sys_get_kernel_syms,将当前加到系统中的模块和核心的符号表全部输出到kernel_sym结构中,为后面使用。这个结构的内容在insmod用户进程空间。
②将Mymodule目标文件读进insmod用户进程空间,成为一个映像。 ③根据第一步得到的信息,将Mymodule映像中的地址没有确定的函数和变量一一修正过来。 ④调用系统调用sys_create_module、sys_init_module,将Mymodule链入到系统中去。

137 内核模块的编译 需要用-c选项进行编译。所有的内核模块都必须包含特定的标志:
   __KERNEL__:这个标志告诉头文件此代码将在内核模块中运行,而不是作为用户进程。     MODULE:这个标志告诉头文件要给出适当的内核模块的定义。 LINUX:从技术上讲,这个标志不是必要的。用于比较正规的内核模块,在多个操作系统上编译,这个标志将会使你感到方便。它可以允许你在独立于操作系统的部分进行常规的编译。 例: gcc -Wall -DMODULE -D__KERNEL__ -DLINUX hello.c -D表示加入标志

138 用户空间设备驱动程序 有时不需要真正的驱动程序: 没有两个以上的应用程序使用设备 并且不需要响应中断 例:vgalib库 早期的鼠标转换
没有多个进程访问 不管理资源 例:vgalib库 早期的鼠标转换

139 设备驱动程序框架——接口 Linux设备驱动程序与外界的接口
1.设备驱动程序与操作系统内核的接口,通过file_operations(include/linux/fs.h)完成。 2.驱动程序与系统引导的接口,初始化设备。 3.驱动程序与设备的接口。与具体设备相关。

140 驱动程序框架——功能 驱动程序的注册与注销 设备的打开与释放 设备的读写操作 设备的控制操作 设备的中断和轮询处理

141 chr_dev_init()/blk_dev_init()
初始化函数的调用关系 系统转入核心,调用函数start_kernel()。它调用kernel_thread (init, NULL, 0),创建init进程进行系统配置(其中包括所有设备的初始化工作)。 Kernel_thread() init() setup() sys_setup() device_setup() chr_dev_init()/blk_dev_init()

142 注册与注销 注册和注销函数: register_*dev() unregister_*dev() //include/linux/fs.h
所谓注册就是在内核的chrdevs或blkdevs中添加一项。 struct device_struct { const char * name; struct file_operations * fops; }; 相同主设备号的fops元素内容相同。

143 打开与释放 打开设备: open() 检查与设备有关的错误,如未准备好。 如果是首次打开,则初始化设备。
确定次设备号,根据需要可更新设备的f_op。 如果需要,分配且设置文件中的private_data。 递增设备使用的计数器。 如果只允许一个进程使用设备,则需要设忙标志。

144 释放设备:release() 递减设备使用的计数器 释放设备文件中的私有数据所占空间 如果是独占设备,则要清除忙标志,使其他进程可以使用 如果是最后一个释放,则关闭设备

145 设备的读写操作 字符设备: foo_read()和foo_write() 块设备
block_read和block_write()——策略规程,不需要在驱动程序中实现。 通过缓冲区读写,只在数据不在缓冲区时才真正执行数据传输,通过request_fn()完成。 struct blk_dev_struct struct request

146 设备的控制 ioctl() 一般做法是: 首先差错检查, 返回:
然后用一个大的switch语句(可能是内嵌的)来处理所有可能的ioctl命令。 返回: 出错返回-erro 其他情况由用户定义

147 字符设备驱动程序 数据结构 注册与注销 轮询和中断 对应驱动程序的“三个接口”。

148 添加一个简单的字符设备 确定主设备号 编写file_operations中的函数以及中断处理函数。 编写初始化函数foo_init()
在chr_dev_init()中添加调用和返回初始化函数的代码。 修改drivers/char/Makefile; 假设我们把所以必要的函数写foo.c中,则在“L_OBJS := tty_io.o n_tty.o console.o \”行把“foo.o”加到其中。

149 将该设备私有的*.c,*.h复制到目录drivers/char下。
用命令:make clean;make dep;make zImage重新编译内核。 用mknod命令在目录/dev下建立相应主设备号的用于读写的特殊文件 mknod命令:建立设备特殊文件 格式: mknod 文件名 类型 主设备号 次设备号 类型:c或b,代表字符设备或块设备

150 块设备驱动程序 数据结构 注册与注销 读写请求及其处理 增加一个块设备的方法和字符设备差不多。
块设备不需要编写file_operations结构里的read和write函数,但是也需要read和write在request中调用。 需要有请求处理函数,以及中断处理函数。

151 嵌入式网络技术

152 TCP/IP协议简介 TCP/IP协议是一套把因特网上的各种系统互连起来的协议组,保证因特网上数据的准确快速传输
参考开放系统互连(OSI)模型,TCP/IP通常采用一种简化的四层模型 : 应用层 传输层 网络层 链路层

153 TCP协议的实现 TCP协议是面向连接的、端对端的可靠通信协议。 它采用了许多机制来保证可靠传输,应用于嵌入式系统显得过于复杂
TCP协议数据传输可分为三个阶段:建立连接、传输数据和断开连接。 它的实现过程可以用状态机来描述。

154 TCP连接建立 建立连接有两种方法,即主动打开和被动打开 服务器端是一种被动打开,它一直在侦听连接请求
而客户端是一种主动打开,它发送连接请求以建立连接。

155 TCP连接断开 断开连接有两种方式:一是主动断开连接;另一是被动断开连接
被动断开连接的处理较为简单,但标准TCP协议的主动断开连接的状态机部分过于复杂。 经过实验发现,在需要主动断开连接的时候,发送一个Fin数据报;接收到对Fin数据报的确认后,再发送一个Reset数据报,即可顺利完成一次主动断开连接。 标准的TCP协议使用慢启动的滑动窗口机制

156 IP协议 IP协议是TCP/IP的基础 为不同网络的主机之间发送数据报的操作序列提供无连接服务
通过在数据报前添加IP协议头,使每个数据报具有寻址能力。 嵌入式系统只把IP作为传输工具,进行简化以完成主要的操作

157 其它协议 ARP协议为32位IP地址到对应的48位以太网地址之间提供动态映射。 嵌入式系统中仅响应ARP请求,发送ARP回答包。
请求者广播出包含ARP请求的以太帧、目的以太网地址为全1的广播地址。本机收到后,由目的IP地址发现自己是目的主机,发送一个ARP回答。 ICMP通过IP协议传输其报文。IP协议是无连接的,它无法将报文和错误信息传到最初的主机,ICMP将状态信息和错误信息发送到发报文的主机。

158 嵌入式TCP/IP协议栈应用前提 连接在互联网上的电子装置不一定比单独使用的电子装置具有更高的智能功能,但连通意味着提高智能化管理水平

159 嵌入式TCP/IP协议栈适用的范围 在商业系统,利用它来进行远程监视服务很有意思
万维网服务器还可以对一个电子装置进行远程配置

160 存在的局限 首先嵌入式栈是建在专用的软插座API之上的 提供可靠传输,占用了大量的资源 体积太大,许多场合不适用

161 实施的方法 采用软件的方式 硬件方法 DSP方法 采用iReady的芯片或芯核,虽然使用的是4位微处理器也能够和互联网实现直接连接
也可以选用DSP为基础的TCP/IP协议栈

162 采用方法选择 根据所设计的电子装置的实际情况来决定 使用硬件功能如何? 打算如何来实现和互联网的连接? 需要发送和接收那些种类的信息?
将软件或硬件芯片整合到所设计的电子装置中去,有没有困难? 增加一个协议栈,是否需要对所设计的电子装置进行重大更改等等

163 构件化协议框架概述 TCP/IP协议是发展至今最成功的通信协议 路由器则是Internet的基石
目前的路由器大部分都支持多个协议,即多协议路由器 目前的通讯设备逐步向着多CPU发展 协议构件化框架NSPCF(Networks Sub-Protocol Component Frame),应用构件技术将通讯协议划分为子构件,完成高性能路由器上的高效通讯 构件技术把单个的应用程序分割成多个独立的构件,某一块构件可独立的被新的构件更新、替换

164 NSPCF网络子协议构件框架组成 传统网络协议和服务设计模式(面对过程)越来越不能满足网络增长和发展的需求

165 NSPCF模型 需求分析 程序员 构件库 协议模块化 构件开发平台 协议构件 构件操作系统 协议运行,提供网络服务

166 NSPCF特征 NSPCF具有传统CBSD具有的一切特征,包括封装性、复用性、扩展性,同时提供一组扩展特性,诸如系统安全、动态加载、跨平台、分布式、开放性 NSPCF框架继承于CBSD,基本执行模块是与对象等价的构件 NSPCF的复用模型是要建立一种基于代码和二进制级的复用模型 在NPCF框架中,有三种类型的成员public、private、protected 通过设计一个安全进程SP(security process)对系统安全性进行管理 跨平台性

167 构件跨平台实现 构件库 构件系统 CTE DIC DDC

168 网络协议构件化拆分 对于传统的按层次划分的网络协议,我们必须先进行模块化,然后才能构件化,组装成协议构件 协议模块化的目的在于控制协议的粒度
构件的粒度越小,协议划分得越细,协议构件越多;构件粒度越大,协议划分得越粗,协议构件就越少 按层次构件化、按功能构件化、按使用对象构件化

169 协议构件的组装 协议1 构件A1 协议2(v1) 构件A2 构件A3 协议2(v2) 构件B1 构件B2 构件b1 构件b2

170 按层次构件化

171 按功能构件化

172 按使用对象构件化 还可以按照协议构件的使用对象对协议进行构件化。
对于某一个网络协议,使用对象可以分为多种,有服务端使用,有客户端使用;有给同一层协议使用的,有给上一层协议使用的;有给一般用户使用的,也有给超级用户使用的,这些使用对象的特性,都可以用来对协议进行构件化。 应用程序在使用网络协议时,有可能只是使用到了协议的某一个模块,例如客户端模块。这时候,就可以提供协议构件中的客户端构件给应用程序。

173 协议构件库设计 所有的协议构件都被统一放置在构件库PCB(Protocol Component Base)中进行统一管理
协议构件库是针对网络通讯需求特点开发的、不断完善改进的、用来对构件化的协议进行使用、管理和维护的软件构件库 管理和维护涉及一系列问题: 构件的有效分类、快捷检索和准确理解机制; 用户权限控制能力; 构件库体系的可定制性与库间互操作性技术问题。 协议构件库是一个由构件数据库、数据库管理系统、网络通信模块等组成的一个完整的体系。

174 构件库结构

175 构件索引 基于层次的构件索引 基于功能的构件索引 基于使用对象的构件索引
在协议构件化时,按照OSI参考模型的层次,对每层的协议进行构件化。构件化的协议放在不同的层次中。 基于功能的构件索引 在协议构件化时,按照协议不同组成部分的功能进行划分和构件化。此时,构件化的协议没有层次的要求,但是被构件化的协议应该可以划分出独立的功能模块。 基于使用对象的构件索引 在协议构件化时,按照服务、协议,对每层的协议进行构件化。构件化的协议放在不同的层次中。

176 构件分类 基本构件。基本构件是整个协议构件库的根本,协议和服务最终由基本构件组装而成,因此,在构件库中的实际上是基本构件。
协议构件。协议构件是一种虚拟的构件。即在构件库中实际不存在这种构件,但是为了更方便快捷的检索,建立索引,将常用的协议看成是一种构件。 服务构件。与协议构件一样,它也是一种虚拟的构件,同样,也是对常用的服务建立索引表。

177 协议组装 协议构件本身是无法单独为通信设备提供服务的,因此需要一种机制,能够将这些被拆分的对象进行组合,以提供通信服务。
针对网络构件化之后,协议栈的动态重构的情况,通过建立构件化协议组装模型来达到协议组装重用和定制通信服务的目的。 构件化协议组装模型从各种协议构件中选择符合需求的模块,按照协议层次重新组装使用。 采用构件化和封装方法后,在对协议进行拆分后产生的构件,将在某一时刻在重新组成协议栈、协议和服务。在进行这个重组时,采用组装模型来完成这个工作

178 协议组装模型作用 组装模型与操作系统和构件库的通信,通信设备端与构件库端的通信。 对服务对象性能参数的跟踪、选择和协商。
对服务对象的组装工作。 组装完成的服务对象的检测和初始化。 服务对象的管理工作。

179 组装模型体系结构 用户:用户说明需要的目标服务 通信器:负责与用户和其他模块的通信和交流
组装器:负责将各个构件组装成对应的协议,协调构件关系 测试器:对组装好的服务对象使用典型测试数据进行测试 监控器:负责监控和记录构件执行的各项参数,在适当的时候反馈给构件库 管理器:负责控制和维护不同的通信服务


Download ppt "— 嵌入式系统软件开发技术 嵌入式系统 2006年6月23日."

Similar presentations


Ads by Google