嵌入式系统设计与应用 课件 第8-10章设备驱动程序设计-SQLite数据库_第1页
嵌入式系统设计与应用 课件 第8-10章设备驱动程序设计-SQLite数据库_第2页
嵌入式系统设计与应用 课件 第8-10章设备驱动程序设计-SQLite数据库_第3页
嵌入式系统设计与应用 课件 第8-10章设备驱动程序设计-SQLite数据库_第4页
嵌入式系统设计与应用 课件 第8-10章设备驱动程序设计-SQLite数据库_第5页
已阅读5页,还剩71页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第八章嵌入式Linux驱动程序本章含有多个微课视频,请择机观看第八章嵌入式Linux驱动程序(1)设备驱动开发概述驱动程序处理过程/设备驱动程序框架

(2)内核设备模型功能/Sysfs和实现机制/Platform总线/设备树(扩展)(3)字符设备驱动程序及其实例关键数据结构/驱动框架/实例/GPIO/

pinctrl框架(扩展)/I2C总线(4)块设备与网络设备驱动程序驱动程序框架/关键数据结构/设计方法/实例ARM-Linux驱动程序的4种经典方法1.mmap映射型设计方法(单片机工程师的最爱)2.传统file_operatiopns设计方法(盛久不衰,沿用至今)3.platform总线型设计方法(2.6版本主流)4.设备树(内核3.x版本后流行)

设备驱动程序开发概述PartOne8.1设备驱动程序是应用程序和硬件设备之间的一个软件层,它向下负责和硬件设备的交互,向上通过一个通用的接口挂接到文件系统上,从而使用户或应用程序可以无需考虑具体的硬件实现环节。由于设备驱动程序为应用程序屏蔽了硬件细节,在用户或者应用程序看来,硬件设备只是一个透明的设备文件,应用程序对该硬件进行操作就像是对普通的文件进行访问和控制硬件设备(如打开、关闭、读和写等)。Linux设备驱动程序概述

作为Linux内核的重要组成部分,设备驱动程序主要完成以下的功能:(1)对设备初始化和释放。(2)把数据从内核传送到硬件和从硬件读取数据。(3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据。(4)检测错误和处理中断。Linux设备驱动程序可以分为两个主要组成部分:(1)对子程序进行自动配置和初始化,检测驱动的硬件设备是否正常,能否正常工作。(2)设备服务子程序和中断服务子程序,这两者分别是驱动程序的上下两部分。驱动上部分即设备服务子程序的执行是系统调用的结果,并且伴随着用户态向核心态的演变,在此过程中还可以调用与进程运行环境有关的函数,比如sleep()函数。驱动程序的下半部分即中断服务子程序。Linux设备驱动程序分类

1.字符设备字符设备是传输数据以字符为单位进行的设备,字符设备驱动程序通常实现open、close、read和write等系统调用函数,常见的字符设备有键盘、串口、控制台等。通过文件系统节点可以访问字符设备,例如/dev/tty1和/dev/lp1。2.块设备所谓块设备是指对其信息的存取以“块”为单位。块设备和字符设备一样可以通过文件系统节点来访问。在大多数linux系统中,只能将块设备看作多个块进行访问,一个块设备通常是1024B数据。3.网络设备网络设备驱动通常是通过套接字(Socket)等接口来实现操作。任何网络事务处理都可以通过接口来完成和其他宿主机数据的交换。内核和网络设备驱动程序之间的通信与字符设备驱动程序和块设备驱动程序与内核的通信是完全不同的。8.1.1Linux设备驱动程序分类1.字符设备字符设备是传输数据以字符为单位进行的设备,字符设备驱动程序通常实现open、close、read和write等系统调用函数,常见的字符设备有键盘、串口、控制台等。通过文件系统节点可以访问字符设备,例如/dev/tty1和/dev/lp1。字符设备和普通文件系统之间唯一的区别是普通文件允许往复读写,而大多数字符设备驱动仅是数据通道,只能顺序读写。此外,字符设备驱动程序不需要缓冲且不以固定大小进行操作,它与用户进程之间直接相互传输数据。2.块设备所谓块设备是指对其信息的存取以“块”为单位。如常见的光盘、硬磁盘、软磁盘、磁带等,块长大小通常取512B、1024B或4096B等。块设备和字符设备一样可以通过文件系统节点来访问。在大多数linux系统中,只能将块设备看作多个块进行访问,一个块设备通常是1024B数据。块设备的特点是对设备的读写是以块为单位的,并且对设备的访问是随机的。块设备和字符设备的区别主要在于内核内部的管理上,其中应用程序对于字符设备的每个I/O操作都会直接传递给系统内核对应的驱动程序;而应用程序对于块设备的操作要经过系统的缓冲区管理间接地传递给驱动程序处理。

3.网络设备网络设备驱动通常是通过套接字(Socket)等接口来实现操作。任何网络事务处理都可以通过接口来完成和其他宿主机数据的交换。内核和网络设备驱动程序之间的通信与字符设备驱动程序和块设备驱动程序与内核的通信是完全不同的。8.1.2驱动程序的处理过程:块设备为例如果逻辑I/O层请求读取块设备的第j块,假设请求到来时驱动程序处于空闲状态,那么驱动程序立刻执行该请求,由于外设速度相比CPU要慢很多,因此进程会在该数据块缓存上阻塞,并调度新的进程运行。但是如果驱动程序同时正在处理另一个请求,那么就将请求挂在一个请求队列中,对应的请求进程也阻塞于所请求的数据块。当完成一个请求的处理时,设备控制器向系统发出一个中断信号。结束中断的处理方法是将设备控制器和通道的控制块均置为空闲状态,然后查看请求队列是否为空。如果为空则驱动程序返回,反之则继续处理下一个请求。如果传输错误,则向系统报告错误或者进行相应进程重复执行处理。对于故障中断,则向系统报告故障,由系统进一步处理。1.内存与I/O端口编写驱动程序大多数情况下其本质都是对内存和I/O端口的操作。(1)内存Linux通常有以下几种地址类型:用户虚拟地址物理地址总线地址内核逻辑地址内核虚拟地址(2)I/O端口有两个重要的内核调用可以保证驱动程序使用正确的端口,它们定义在include/linux/ioport.h中。int__check_region(structresource*,resource_size_t,resource_size_t);该函数的作用是查看系统I/O表,看是否有别的驱动程序占用某一段I/O口。structresource*__request_region(structresource*, resource_size_tstart, resource_size_tn, constchar*name,intflags);根据CPU系统结构的不同,CPU对I/O端口的编址方式通常有两种:第一种是I/O映射方式,如x86处理器为外设专门实现了一个单独的地址空间,称为I/O地址空间,CPU通过专门的I/O指令来访问这一空间的地址单元;第二种是内存映射方式,RSIC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间,外设I/O端口成为了内存的一部分,此时CPU访问I/O端口就像访问一个内存单元,不需要单独的I/O指令。这两种方式在硬件实现上的差异对软件来说是完全可见的。2.并发控制

在驱动程序中经常会出现多个进程同时访问相同的资源时可能会出现竞态(racecondition),即竞争资源状态,因此必须对共享资料进行并发控制。Linux内核中解决并发控制最常用的方法是自旋锁(spinlocks)和信号量(semaphores)。(1)自旋锁自旋锁是一个互斥现象的设备,它只能是两个值:locked(锁定)或unlocked(解锁)。它通常作为一个整型值的单位来实现。在任何时刻,自旋锁只能有一个保持者,也就是说在同一时刻只能有一个进程获得锁。(2)信号量信号量是一个结合一对函数的整型值,这对函数通常称为P操作和V操作。自旋锁和信号量有很多相似之处但又有些本质的不同。其相同之处主要有:首先它们对互斥来说都是非常有用的工具;其次在任何时刻最多只能有一个线程获得自旋锁或信号量。不同之处主要有:首先自旋锁可在不能睡眠的代码中使用,如在中断服务程序(ISR)中使用,而信号量不可以;其次自旋锁和信号量的实现机制不一样;最后通常自旋锁被用在多处理器系统。总体而言,自旋锁通常适合保持时间非常短的情况,它可以在任何上下文中使用,而信号量用于保持时间较长的情况,只能在进程上下文中使用。互斥锁(互斥量)互斥锁(Mutex,全称MutualExclusion)是一种同步原语,用于防止多个线程或进程同时访问共享资源,从而避免竞态条件和数据不一致的问题。互斥锁确保任何时候只有一个线程可以持有锁并访问资源,其他线程必须等待直到锁被释放。互斥锁的基本原理包括:在编程中,互斥锁通常通过特定的库或语言特性来实现。例如,在C语言中,可以使用POSIX线程库(pthread)提供的pthread_mutex_t类型来实现互斥锁。当一个线程想要访问共享资源时,它首先尝试锁定互斥锁。如果锁已经被其他线程持有,则线程将被阻塞,直到锁被释放。锁定(Lock)一旦线程成功锁定互斥锁,它就可以安全地访问共享资源。访问资源线程完成对共享资源的访问后,必须释放锁,以便其他线程可以访问资源。解锁(Unlock)如果线程在持有互斥锁的同时尝试再次锁定它,这可能导致死锁。为了避免这种情况,通常要求线程在尝试重新锁定之前先解锁。死锁避免自旋锁和互斥锁之间的区别自旋锁是忙等待(busy-waiting)的,而互斥锁是休眠等待(sleep-waiting)的(又叫做休眠锁)。01需要访问共享资源的进程或线程必须先获取互斥锁才能访问。如果互斥锁已经被另一个进程或线程占有,这个尝试获取的进程或线程将被阻塞,直到互斥锁被释放。互斥锁通常使用原子操作和系统调用的组合来实现。互斥锁的主要优点是防止忙等待,从而可以节省CPU周期并降低功耗。互斥锁是为共享资源提供互斥的锁。02自旋锁不会在锁已被占有时阻塞进程或线程,而是会在循环中重复检查锁的状态,直到它空闲为止。这比阻塞和解除阻塞进程或线程更有效,尤其是在锁只占用很短时间的情况下。但是,如果锁占用时间过长,自旋锁会浪费大量CPU时间,并且其他进程或线程可能会因缺乏CPU资源造成饥饿。自旋锁是一种使用忙等待来同步对共享资源访问的锁。3.阻塞与非阻塞在驱动程序的处理过程中我们提到了阻塞的概念,这里进行以下说明。阻塞(blocking)和非阻塞(nonblocking)是设备访问的两种不同模式,前者在I/O操作暂时不可进行时会让进程睡眠,而后者在I/O操作暂时不可进行时并不挂起进程,它或者放弃,或者不停地查询,直到可以进行操作为止。(1)阻塞与非阻塞操作阻塞操作是指在执行设备操作时,若不能获得资源则进程挂起,直到满足可操作的条件再进行操作。被挂起的进程进入睡眠状态,被从调度器的运行队列中移走,直到等待条件被满足。非阻塞操作是在不能进行设备操作时并不挂起,它会立即返回,使得应用程序可以快速查询状态。(2)异步通知异步通知是指一旦设备准备就绪,则该设备会主动通知应用程序,这样应用程序就不需要不断地查询设备状态,通常把异步通知称为信号驱动的异步I/O(SIGIO),这有点类似于硬件上的中断。中断处理Linux将中断分为两个部分:上半部分(tophalf)和下半部分(bottomhalf)。上半部分的功能是注册中断,下半部完成了中断处理程序的大部分工作中断的Bottom-half机制,包括了softirq(软中断)、tasklet(任务队列)、workqueue(工作队列)等,从2.3版本开始,Linux为实现下半部的机制主要引入了tasklet()和软中断。软中断是一组静态定义的下半部接口,可在所有处理器上同时执行-这就要求软中断执行的函数必须可重入。当软中断在访问临界区时需要用到同步机制,如自旋锁。软中断主要针对时间严格要求的下半部使用,如网络和SCSI。任务队列(tasklet)下面是tasklet的定义:structtasklet_struct{ structtasklet_struct*next;//指向下一个tasklet unsignedlongstate;//tasklet的状态 atomic_tcount;//计数,1表示禁止 void(*func)(unsignedlong);//处理函数指针 unsignedlongdata;//处理函数参数};在interrupt.h中可以看到tasklet的数据结构,其状态定义了两个位的含义:enum{TASKLET_STATE_SCHED,/*正在运行*/ TASKLET_STATE_RUN /*已被调度,准备运行*/};工作队列(workqueue)接口在linux2.5版本中引入,取代了任务队列接口。工作队列与tasklet的主要区别在于tasklet在软中断上下文中运行,代码必须是原子的;工作队列函数在一个内核线程上下文中运行,并且可以在延迟一段时间后才执行,因而具有更多的灵活性,并且工作队列可以使用信号量等能够sleep的函数。另外,工作队列的中断服务程序和tasklet非常类似,唯一不同就是它调用schedule_work()来调度下半部处理,而tasklet使用tasklet_schedule()函数来调度下半部处理。工作队列(workqueue)工作队列的queue核心代码如下所示。staticvoidinsert_work(structcpu_workqueue_struct*cwq,

structwork_struct*work,structlist_head*head,

unsignedintextra_flags){

structglobal_cwq*gcwq=cwq->gcwq;

set_work_cwq(work,cwq,extra_flags);

list_add_tail(&work->entry,head);

//head参数表示的是每cpu一个的全局队列

if(__need_more_worker(gcwq))//如果是诸如高优先级之类的工作或者当前已经没有//空闲的工作者了,那么唤醒一个工作者,系统起码要保持一个空闲的工作者进程以备用。

wake_up_worker(gcwq);}驱动程序在使用工作队列时的主要步骤是:(1)当驱动程序不使用默认的工作队列时,驱动程序可以创建一个新的工作队列。(2)当驱动程序需要延迟时,根据需要静态或者动态创建工作队列。(3)将工作队列任务插入工作队列。5.设备号用户进程与硬件的交流是通过设备文件进行的,硬件在系统中会被抽象成为一个设备文件,访问设备文件就相当于访问其所对应的硬件。每个设备文件都有其文件属性(c/b),表示是字符设备还是块设备。每个设备文件的设备号有两个:第一个是主设备号,标识驱动程序对应一类设备的标识;第二个是从设备号,用来区分使用共用的设备驱动程序的不同硬件设备。设备号在linux2.6内核中,主从设备被定义为一个dev_t类型的32位数,其中前12位表示主设备号,后20位表示从设备号。另外,在include/linux/kdev.h中定义了如下的几个宏来操作主从设备号。#defineMAJOR(dev) ((unsignedint)((dev)>>MINORBITS))#defineMINOR(dev) ((unsignedint)((dev)&MINORMASK))#defineMKDEV(ma,mi) (((ma)<<MINORBITS)|(mi))上述宏分别实现从32位dev_t类型数据中获得主设备号、从设备号及将主设备号和从设备号转换为dev_t类型数据的功能。设备驱动程序框架Linux的设备驱动程序可以分为以下部分。(1)驱动程序与内核的接口,这是通过关键数据结构file_operations来完成的。(2)驱动程序与系统引导的接口,这部分利用驱动程序对设备进行初始化。(3)驱动程序与设备的接口,描述了驱动程序如何与设备进行交互,这与具体设备密切相关。根据功能划分,设备驱动程序代码通常可分为以下几个部分:(1)驱动程序的注册与注销(2)设备的打开与释放:引用计数(3)设备的读写操作(4)设备的控制操作(5)设备的轮询和中断处理根据功能划分,设备驱动程序代码通常可分为以下几个部分:(1)驱动程序的注册与注销对于字符设备或者是块设备,关键的一步还要向内核注册该设备,linux操作系统也专门提供了相应的功能函数,如字符设备注册函数register_chrdev()、块设备注册函数register_blkdev()。在设备关闭时,要在内核中注销该设备,操作系统也相应的提供了注销设备的函数unregister_chrdev()、unregister_blkdev(),并释放设备号。在内核中使用一个数组chrdevs[]保存所有字符设备驱动程序的信息,在fs/char_dev.c中该数组的数据结构如下所示:staticstructchar_device_struct{ structchar_device_struct*next; unsignedintmajor; unsignedintbaseminor; intminorct; charname[64]; structcdev*cdev; }*chrdevs[CHRDEV_MAJOR_HASH_SIZE];字符设备驱动程序的注册其实就是将字符设备驱动程序插入到该数组中。Linux通过字符设备注册函数register_chrdev()来完成注册功能。其函数原型如下:int__register_chrdev(unsignedintmajor,unsignedintbaseminor, unsignedintcount,constchar*name, conststructfile_operations*fops)Linux通过字符设备注销函数unregister_chrdev来完成注销功能。其函数原型如下:void__unregister_chrdev(unsignedintmajor,unsignedintbaseminor,unsignedintcount,constchar*name)块设备比字符设备要复杂,但是块设备驱动程序也需要一个主设备号来标识。块设备驱动程序的注册时通过register_blkdev()函数实现的。其函数原型如下:int__register_blkdev(unsignedintmajor,constchar*name)(2)设备的打开与释放打开设备是由调用定义在incliude/linux/fs.h中的file_operations结构体中的open()函数完成的。open()函数主要完成的主要工作:增加设备的使用计数。检测设备是否异常,及时发现设备相关错误,防止设备有未知硬件问题。若是首次打开,首先完成设备初始化。读取设备次设备号。其函数原型如下:int(*open)(structinode*,structfile*);释放设备由release()完成,包括以下几件事情:释放open时系统为之分配的内存;释放所占用的资源,并进行检测,关闭设备,并递减设备使用计数。其函数原型如下:int(*release)(structinode*,structfile*);(3)设备的读写操作字符设备对数据的读写操作是由各自的read()函数和write()函数来完成的。对块设备的读写操作,由文件block_devices.C中定义的函数blk_read()和blk_write()完成。真正需要读写的时候由每个设备的request()函数根据其参数cmd与块设备进行数据交换。(4)设备的控制操作ioctl()函数就是驱动程序提供的控制函数。该函数的使用和具体设备密切相关。在linux内核版本2.6.35以前,ioctl()函数原型如下:int(*ioctl)(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg);在2.6.35以后,系统已经完全删除了structfile_operations中的ioctl函数指针,剩下unlocked_ioctl和compat_ioctl,取而代之的是unlocked_ioctl,主要改进就是不再需要上大内核锁(调用之前不再先调用lock_kernel()然后再unlock_kernel())。如下是unlocked_ioctl和compat_ioctl的原型:long(*unlocked_ioctl)(structfile*,unsignedint,unsignedlong);long(*compat_ioctl)(structfile*,unsignedint,unsignedlong);(5)设备的轮询和中断处理对于支持中断的设备,可以按照正常的中断方式进行。但是对于不支持中断的设备,过程就相对繁琐,在确定是否继续进行数据传输时都需要轮询设备的状态。驱动程序的加载

通常linux驱动程序可通过两种方式进行加载:一种是将驱动程序编译成模块形式进行动态加载,常用命令有insmod(加载)、rmmod(卸载)等;另一种是静态编译,即将驱动程序直接编辑放进内核。动态加载模块设计使Linux内核功能更容易扩展。而静态编译方法对于在要求硬件只是完成比较特定、专一的功能的一些嵌入式系统中,具有更高的效率。以网卡DM9000为例说明驱动程序的加载过程嵌入式系统设计与应用基于ARMCortex-A9和Linux第九章与第十章

嵌入式系统高级应用(Qt和SQLite)第九章与第十章嵌入式系统高级应用第九章、QT图形界面应用程序开发基础QT/QT和SQLite第十章、嵌入式数据库嵌入式数据库系统结构/SQLite关键技术VDBE/SQLite安装与接口测试第9章QT图形界面应用程序开发基础与普通GUI不同的是,嵌入式GUI的要求是轻量级的,如在嵌入式linux中使用的图形界面系统。同时嵌入式GUI还具有可定制,高可靠性,可裁减性等特点。嵌入式GUI的开发系统主要有XWindow、MiniGUI、OpenGL、Qt等。按不同的版本发行:Qt商业版:提供给商业软件开发。它们提供传统商业软件发行版并且提供在协议有效期内的免费升级和技术支持服务。Qt开源版:仅仅为了开发自由和开放源码软件,提供了和商业版本同样的功能。GNU通用公共许可证下,它是免费的。2009年3月发布的Qt4.5起,诺基亚为Qt增添开源LGPL授权选择。QtQt是一个跨平台应用程序和图形用户界面GUI开发框架。使用Qt只需一次性开发应用程序,无须重新编写源代码,便可跨不同桌面和嵌入式操作系统部署这些应用程序。信号与插槽机制信号和插槽用于两个对象之间的通信,信号和插槽(signal/slot)机制是Qt的核心特征,信号和插槽在Qt程序中,利用信号(signal)和插槽(slot)机制进行对象间的通信signal和slot机制真正实现了封装的概念,slot除了接收signal之外和其它的成员函数没有什么不同,而且signal和slot之间也不是一一对应的。事件处理的方式也是回调

当对象状态发生改变的时候,发出signal通知所有的slot接收signal,尽管它并不知道哪些函数定义了slot,而slot也同样不知道要接收怎样的signal下面举例来说明信号/插槽机制。

#include<QObject>classCounter:publicQObject{Q_OBJECTpublic:Counter(){m_value=0;}intvalue()const{returnm_value;}publicslots:voidsetValue(intvalue);signals:voidvalueChanged(intnewValue);private:intm_value;};在声明信号/插槽后,使用connect()函数将它们关联起来。Connect函数原型如下:boolQObject::connect(constQObject*sender,constchar*signal,constQObject*receiver,constchar*slot)[static]下面给出一个例子予以说明。Countera,b;QObject::connect(&a,&Counter::valueChanged,&b,&Counter::setValue);a.setValue(18);//a.value()==18,b.value()==18b.setValue(42);//a.value()==18,b.value()==42Counter类通过发射信号valueChanged来通知其他对象它的状态发生了变化,同时该类还具有一个插槽setValue,其他对象可以发信号给这个插槽。插槽setValue的定义如下:voidCounter::setValue(intvalue){if(value!=m_value){m_value=value;emitvalueChanged(value);}}当信号与插槽没有必要继续保持关联时,用户可以使用disconnect()函数来断开连接。其定义如下所示:boolQObject::disconnect(constQObject*sender,constchar*signal,constObject*receiver,constchar*slot)[static]这个函数断开发射者中的信号与接收者中的插槽函数之间的关联。9.4.1Helloworld程序新建一个helloworld项目,该项目使用的类信息中将基类选择为QDialog。项目构成如下图所示。图9-4helloworld项目构成在源文件Main.cpp中输入源码,如下:#include<qapplication.h>#include<qlabel.h>intmain(intargc,char**argv){QApplicationa(argc,argv);//创建了一个QApplication类的对象aQLabel*hello=newQLabel("Helloworld!",0);//创建了一个静态文本,将label设置为“Helloworld!”hello->show();//调用show()方法使窗口部件可见returna.exec();//exec()中qt接收并处理用户和系统的事件,并且把它们传递给适当的窗口部件}Qt数据库应用Qt中的QtSql模块提供了对数据库的支持,该模块中的众多类基本上可以分为三层,分别是驱动层、SQL接口层和用户接口层。除了QSqlQuery类外,Qt还提供了三种用于访问数据库的高层SQL模型,如表9-3所示。Qt中使用了这些模型来避免使用SQL语句,为用户提供了更简便的可视化数据库操作及数据显示模型,有效的减了开发工作量。(本节有微课)SQL模型第10章SQLite数据库嵌入式数据库系统是指支持移动计算或某种特定计算模式的数据库管理系统,它通常与操作系统和具体应用集成在一起,运行在智能型嵌入式设备或移动设备上。由于嵌入式数据库系统总是与移动计算相结合,所以通常情况下嵌入式数据库也被称为嵌入式移动数据库。嵌入式数据库系统具备了如下主要特点:(1)嵌入性(2)移植性(3)安全性(4)实时性(5)可靠性(6)主动性嵌入式数据库的分类方法很多,根据其嵌入的对象不同分为:面向软件的嵌入式数据库、面向设备的嵌入式数据库、内存数据库等。也可以根据其应用的不同分为:普通嵌入式数据库、嵌入式移动数据库、小型架构数据库等。在众多的数据库中,如何选择适用于嵌入式系统的数据库呢?嵌入式系统开发环境决定了其对数据库需求的要素:(1)体积较小(2)功能齐备(3)代码开源(4)性能可靠嵌入式数据库的系统结构嵌入式数据库通常与操作系统和具体应用集成在一起,而嵌入式操作系统种类繁多、系统特点不一,从而导致嵌入式数据库系统的应用环境也复杂多样。因此,嵌入式数据库没有一个比较固定的模式,其结构和采用的技术也因为不同的需求而多种多样。总的来说,嵌入式数据库系统体系结构可以分为外部接口和内部处理两大模块,嵌入式实时数据库除了具备

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论