资源描述
1 uC/OS-II是一个简洁、易用的基于优先级的嵌入式【抢占式】多任务实时内核。2 任务是一个无返回的无穷循环。uc/os-ii 总是运行进入就绪状态的【最高优先级】的任务。3 因为 uc/os-ii 总是运行进入就绪状态的最高优先级的任务。所以,确定哪个任务优先级最高,下面该哪个任务运行,这个工作就是由【调度器(scheduler )】来完成的。4 【任务级】的调度是由函数OSSched() 完成的,而【中断级】的调度是由函数OSIntExt() 完成。对于OSSched() ,它内部调用的是【OS_TASK_SW()】 完成实际的调度;OSIntExt() 内部调用的是【OSCtxSw() 】实现调度。5 任务切换其实很简单,由如下2 步完成:(1)将被挂起任务的处理器寄存器推入自己的【任务堆栈】。(2)然后将进入就绪状态的最高优先级的任务的寄存器值从堆栈中恢复到【寄存器】中。6 任务的 5 种状态。【睡眠态 (task dormat) 】:任务驻留于程序空间(rom 或 ram)中,暂时没交给ucos-ii 处理。【就绪态( task ready )】:任务一旦建立,这个任务就进入了就绪态。【运行态( task running )】:调用OSStart ()可以启动多任务。OSStart ()函数只能调用一次,一旦调用,系统将运行进入就绪态并且优先级最高的任务。【等待状态( task waiting )】:正在运行的任务,通过延迟函数或pend (挂起)相关函数后,将进入等待状态。【中断状态(ISR running )】:正在运行的任务是可以被中断的,除非该任务将中断关闭或者ucos-ii 将中断关闭。7【 不可剥夺型】内核要求每个任务自我放弃CPU 的所有权。不可剥夺型调度法也称作合作型多任务,各个任务彼此合作共享一个 CPU。8当系统响应时间很重要时,要使用【可剥夺型】内核。最高优先级的任务一旦就绪,总能得到CPU 的控制权。9使用可剥夺型内核时,应用程序不应直接使用不可重入型函数。调用不可重入型函数时,要满足互斥条件,这一点可以用【 互斥型信号量】来实现。10【可重入型】函数可以被一个以上的任务调用,而不必担心数据的破坏。11可重入型函数任何时候都可以被中断,一段时间以后又可以运行,而相应数据不会丢失。可重入型函数或者只使用【局部变量】,即变量保存在CPU 寄存器中或堆栈中。如果使用全局变量,则要对全局变量予以【保护】。12每个任务都有其优先级。任务越重要,赋予的优先级应【越高】。13 C/OS -初始化是通过调用系统函数【OSIint() 】实现的,完成C/OS-所有的变量和数据结构的初始化。名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 1 页,共 17 页 - - - - - - - - - 14多任务的启动是用户通过调用【OSStart() 】实现的。然而,启动C/OS-之前,用户至少要建立一个应用【任务】。15. C/OS -的参数配置文件名为【OS_CFG.H 】。16 删除任务 ,是说任务将返回并处于【休眠状态】,并不是说任务的代码被删除了,只是任务的代码不再被?C/OS- 调用。17 ?C/OS- 要求用户提供【定时中断】来实现延时与超时控制等功能。18定时中断也叫做【时钟节拍】,它应该每秒发生10 至 100 次。19. 时钟节拍的实际频率是由用户的应用程序决定的。时钟节拍的频率越高,系统的负荷就【越重】。20 ?C/OS-II 中的信号量由两部分组成:一个是信号量的【计数值】,它是一个16 位的无符号整数(0 到 65,535 之间);另一个是由等待该信号量的任务组成的【等待任务表】。用户要在OS_CFG.H中将 OS_SEM_EN开关量常数置成【1 】,这样 ?C/OS-II才能支持信号量。21. ?C/OS-II中表示当前已经创建的任务数全局变量名为:【OSTaskCtr 】。21. ?C/OS-II中表示当前内核运行的标记全局变量名为:【OSRunning 】。22在使用 OSTaskCreate创建任务时,若需要TaskData作伪参数传递给任务Task,并从任务Task 中获得传入的字符参数值,请在下面【】填上合适的代码。char TaskData=A; OSTaskCreate(Task, 【 (void *)& TaskData 】 , &TaskStk0TASK_STK_SIZE - 1, 1); void Task (void *pdata) char value = 【*(char *)pdata 】 ; for (;) OSSemPend(RandomSem, 0, &err); y = (int) (*(char *)pdata - A); OSSemPost(RandomSem); PC_DispChar(10, 25, value, DISP_FGND_WHITE + DISP_BGND_BLUE); OSTimeDly(1); 名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 2 页,共 17 页 - - - - - - - - - 23. 在 ?C/OS-II 在任务 Task1 中使用邮箱函数OSMboxPost ()发送字符;而在Task2 中接收 OSMboxPost ()字符 , 请在下面【】填上合适的代码。void Task1 (void *data) char txmsg; INT8U err; txmsg = A; for (;) OSMboxPost(TxMbox, 【 (void *)&txmsg 】 ); /* Send message to Task2*/ OSMboxPend(AckMbox, 0, &err); txmsg+; if (txmsg = Z) txmsg = A; void Task5 (void *data) char *rxmsg; INT8U err; data = data; for (;) 名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 3 页,共 17 页 - - - - - - - - - rxmsg = 【 (char *) 】 OSMboxPend(TxMbox, 0, &err); PC_DispChar(70, 18, *rxmsg, DISP_FGND_YELLOW + DISP_BGND_BLUE); OSMboxPost(AckMbox, (void *)1); 24. 在 Task1 中使用消息队列OSQPend ()函数接收消息“Hello World!”,而在 Task2 中使用消息队列OSQPost ()函数发送消息 “Hello World!”, 请在下面【】填上合适的代码. void Task1 (void *pdata) char *msg; INT8U err; pdata = pdata; for (;) msg = 【(char *) 】OSQPend(MsgQueue, 0, &err); PC_DispStr(70, 13, msg, DISP_FGND_YELLOW + DISP_BGND_BLUE); OSTimeDlyHMSM(0, 0, 0, 100); void Task2 (void *pdata) char msg20; pdata = pdata; strcpy(&msg0, Hello World!); for (;) 名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 4 页,共 17 页 - - - - - - - - - OSQPost(MsgQueue, 【(void *) 】&msg0); OSTimeDlyHMSM(0, 0, 0, 500); 1. 举例说明 ?COS-II 可移植型数据类型的定义方式答:因为不同的微处理器有不同的字长,?C/OS-II 的移植文件包括很多类型定义以确保可移植性。?COS-II 不使用 C 语言中的short,int,long等数据类型的定义,因为它们与处理器类型有关,隐含着不可移植性。?C/OS-II 代之以移植性强的整数数据类型,这样,既直观又可移植(该数据类型不依赖于编译),举例如下 : typedef unsigned char BOOLEAN; typedef unsigned char INT8U; typedef signed char INT8S; typedef unsigned int INT16U; typedef signed int INT16S; typedef unsigned long INT32U; typedef signed long INT32S; 2. ?COS-II如何定义全局变量? 答:众所周知 ,全局变量应该是得到内存分配且可以被其他模块通过C 语言中 extern 关键字调用的变量。因此,必须在 .C 和 .H 文件中定义。这种重复的定义很容易导致错误。?COS-II 采用的方法只需用在头文件中定义一次。uC/OS_II.H 头文件中包括以下定义全局宏定义: #ifdef OS_GLOBALS #define OS_EXT #else #define OS_EXT extern #endif OS_EXT INT32U OSIdleCtr; 名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 5 页,共 17 页 - - - - - - - - - 同时, uCOS_II.H有中以下定义:#define OS_GLOBALS #include “includes.h”当编译器处理uCOS_II.C时,它使得头文件变成如下所示,因为OS_EXT 被设置为空。INT32U OSIdleCtr; 这样编译器就会将这些全局变量分配在内存中。当编译器处理其他.C 文件时,头文件变成了如下的样子,因为OS_GLOBAL没有定义,所以OS_EXT 被定义为extern 。extern INT32U OSIdleCtr; 在这种情况下,不产生内存分配,而任何 .C 文件都可以使用这些变量。这样的就只需在 .H 文件中定义一次就可以了。3. OS_ENTER_CRITICAL() 和 OS_EXIT_CRITICAL()的含义及作用? 答:OS_ENTER_CRITICAL() :关中断;OS_EXIT_CRITICAL():开中断。关中断和开中断是为了保护临界段代码。用户的应用代码可以使用这两个宏来开中断和关中断。很明显,关中断会影响中断延迟,所以要特别小心。用户还可以用信号量来保护临界段代码。4基于 PC 的服务中如何测量PC_DisplayChar()的执行时间?答:测量 PC_DisplayChar()的执行时间的代码如下:INT16U time; / 定义时间变量PC_ElapsedInit(); /时钟初始化PC_ElapsedStart(); /开始计时PC_DispChar(40, 24, A, DISP_FGND_WHITE); /执行代码time = PC_ElapsedStop(); /结束计时5 uC/OS-II 的任务框架答:名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 6 页,共 17 页 - - - - - - - - - void task_xxx(void *pData) /* 该任务的初始化工作*/ /* 进入该任务的死循环*/ while(1) 每个用户的任务都必须符合事件驱动的编程模型,即uC/OS-II 的应用程序都必须是“ 事件驱动的编程模型” 。一个任务首先等待一个事件的发生,事件可以是系统中断发出的,也可以是其它任务发出的,又可以是任务自身等待的时间片。当一个事件发生了,任务再作相应处理,处理结束后又开始等待下一个事件的发生。如此周而复始的任务处理模型就是“ 事件驱动的编程模型” 。事件驱动模型也涵盖了中断驱动模型,uC/OS-II事件归根结底来自三个方面:(1)中断服务函数发送的事件(2)系统延时时间到所引起的(3)其它任务发送的事件。6 与共享资源打交道时,使之满足互斥条件最一般的方法包括哪些?答:包括:关中断、使用测试并置位指令、禁止做任务切换、利用信号量等。7. 描述利用C/OS_ 宏调用关中断和开中断处理共享数据的示意性代码程序。答:OS_ENTER_CRITICAL(); /*在这里处理共享数据*/ OS_EXIT_CRITICAL(); 8. 信号量的典型应用包括哪些?名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 7 页,共 17 页 - - - - - - - - - 答:信号量 (Semaphores) 是一种约定机制,在多任务内核中的典型应用包括:(1) 控制共享资源的使用权(满足互斥条件);(2) 标志某事件的发生(3) 使两个任务的行为同步9对信号量只能实施哪三种操作?答:一般地说,对信号量只能实施三种操作:(1) 初始化 (INITIALIZE) ,也可称作建立(CREATE) ;(2) 等信号 (WAIT) 也可称作挂起(PEND) ;(3) 给信号 (SIGNAL) 或发信号 (POST) 。10.给出 C/OS -中如何用信号量处理共享数据的示意代码?答:通过获得信号量处理共享数据的示意代码如下:OS_EVENT *SharedDataSem; void Function (void) INT8U err; OSSemPend(SharedDataSem, 0, &err); /*共享数据的处理在此进行,(中断是开着的 )*/ OSSemPost(SharedDataSem); 11.给出初始化和启动C/OS-的示意代码。答:名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 8 页,共 17 页 - - - - - - - - - void main (void) OSInit(); /* 初始化 uC/OS-II */ . 通过调用 OSTaskCreate()或 OSTaskCreateExt()创建至少一个任务; . OSStart(); /* 开始多任务调度!OSStart() 永远不会返回*/ 12 描述建立任务OSTaskCreate()的函数原型。答:建立任务 OSTaskCreate()的函数原型为:INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio) 其中, task: 任务代码的指针; pdata: 当任务开始执行时传递给任务的参数的指针; ptos: 分配给任务的堆栈的栈顶指针; prio: 分配给任务的优先级。13任务可以是一个无限的循环,也可以是在一次执行完毕后被删除掉。请给出示意代码结构。答:?C/OS- 描述的任务示意代码必须是以下两种结构之一: void YourTask (void *pdata) for (;) /* 用户代码*/ 名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 9 页,共 17 页 - - - - - - - - - 调用?C/OS- 的服务例程之一: OSMboxPend(); OSQPend(); OSSemPend(); OSTaskDel(OS_PRIO_SELF); OSTaskSuspend(OS_PRIO_SELF); OSTimeDly(); OSTimeDlyHMSM(); /* 用户代码*/ 或void YourTask (void *pdata) /* 用户代码*/ OSTaskDel(OS_PRIO_SELF); 14 C/OS -任务管理提供哪些服务?答:C/OS-任务管理提供的服务包括:(1)建立任务:OSTaskCreate()或 OSTaskCreateExt();(2)删除任务:OSTaskDel() ;(3)请求删除任务:OSTaskDelReq(); (4)改变任务的优先级:OSTaskChangePrio();(5)挂起任务:OSTaskSuspend();名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 10 页,共 17 页 - - - - - - - - - (6)恢复任务:OSTaskResume();(7)获得有关任务的信息:OSTaskQuery()。15 C/OS -时间任务管理提供哪些服务?答:C/OS-时间任务管理提供的服务包括:(1) 任务延时函数:OSTimeDly() (2) 按时分秒延时函数:OSTimeDlyHMSM() (3) 让处在延时期的任务结束延时:OSTimeDlyResume() (4) 设置系统时间:OSTimeGet() (5) 获得系统时间:OSTimeSet() 16. C/OS -提供的数据共享和任务通讯的方法包括哪些?答:C/OS-提供的数据共享和任务通讯的方法包括五种方法:(1) 利用宏 OS_ENTER_CRITICAL()和 OS_EXIT_CRITICAL()来关闭中断和打开中断。(2) 利用函数 OSSchedLock()和 OSSchekUnlock()对?C/OS-II中的任务调度函数上锁和开锁。(3) 信号量。(4) 邮箱。(5) 消息队列。17对于事件控制块进行的一些通用包括哪些操作?答:对于事件控制块进行的一些通用操作包括: (1) 初始化一个事件控制块: OSEventWaitListInit(); (2) 使一个任务进入就绪态:OSEventTaskRdy();(3) 使一个任务进入等待该事件的状态:OSEventTaskWait();(4) 因为等待超时而使一个任务进入就绪态:OSEventTO() 。18 C/OS -信号量提供哪些服务?答:C/OS-信号量提供的服务包括:名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 11 页,共 17 页 - - - - - - - - - (1) 建立一个信号量:OSSemCreate();(2) 等待一个信号量:OSSemPend();(3)发送一个信号量:OSSemPost();(4)无等待地请求一个信号量:OSSemAccept();(5)查询一个信号量的当前状态:OSSemQuery()。19 C/OS -邮箱提供哪些服务?答:C/OS-邮箱提供的服务包括:(1)建立一个邮箱:OSMboxCreate();(2)等待一个邮箱中的消息:OSMboxPend();(3)发送一个消息到邮箱中:OSMboxPost();(4)无等待地从邮箱中得到一个消息:OSMboxAccept();(5)查询一个邮箱的状态:OSMboxQuery() 20 C/OS -消息队列提供哪些服务?答:C/OS-消息队列提供的服务包括:(1)建立一个消息队列:OSQCreate() ;(2)等待一个消息队列中的消息:OSQPend() ;(3)向消息队列发送一个消息(FIFO ): OSQPost() ;(4)向消息队列发送一个消息(LIFO ): OSQPostFront();(5)无等待地从一个消息队列中取得消息:OSQAccept() ;(6)清空一个消息队列:OSQFlush() ;(7)查询一个消息队列的状态:OSQQuery() 。21 C/OS -内存管理提供哪些服务?答:C/OS-内存管理提供的服务包括:(1) 建立一个内存分区:OSMemCreate();名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 12 页,共 17 页 - - - - - - - - - (2) 分配一个内存块:OSMemGet();(3) 释放一个内存块:OSMemPut();(4) 查询一个内存分区的状态:OSMemQuery()。22移植 ?C/OS- 时,要使 ?C/OS- 正常运行,处理器必须满足哪些基本要求?答:要使 ?C/OS- 正常运行,处理器必须满足以下要求:(1) 处理器的 C 编译器能产生可重入代码。(2) 用 C 语言就可以打开和关闭中断。(3) 处理器支持中断,并且能产生定时中断(通常在 10 至 100Hz 之间 )。(4) 处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。(5) 处理器有将堆栈指针和其它CPU 寄存器读出和存储到堆栈或内存中的指令。四论述题1. 论述 C/OS-控制下的任务状态转换图答:C/OS-控制下的任务状态转换图如下图所示。在任一给定的时刻,任务的状态一定是在这五种状态之一。(1) 睡眠态( DORMANT ):指任务驻留在程序空间之中,还没有交给C/OS-管理。一个任务可以通过调用OSTaskDel() 返回到睡眠态,或通过调用该函数让另一个任务进入睡眠态。(2) 就绪态 (READY): 当任务一旦建立,这个任务就进入就绪态准备运行。把任务交给C/OS-是通过调用下述两个函数之一:OSTaskCreate()或 OSTaskCreateExt()。(3) 运行态 (RUN): 调用 OSStart() 可以启动多任务。OSStart() 函数运行进入就绪态的优先级最高的任务。(4) 等待状态 (WAITING): 正在运行的任务可以通过调用两个函数之一将自身延迟一段时间,这两个函数是OSTimeDly() 或OSTimeDlyHMSM()。这个任务于是进入等待状态,等待这段时间过去,下一个优先级最高的、并进入了就绪态的任务立刻被赋予了CPU的控制权。正在运行的任务期待某一事件的发生时也要等待,手段是调用以下3 个函数之一:OSSemPend() ,OSMboxPend(),或OSQPend() 。调用后任务进入了等待状态(WAITING )。(5) 中断状态 (ISR): 正在运行的任务是可以被中断的,除非该任务将中断关了,或者C/OS-将中断关了。被中断了的任务就进入了中断服务态(ISR)。2. 论述 C/OS-的核心数据结构任务控制块(OS_TCBs) 名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 13 页,共 17 页 - - - - - - - - - 答:任务控制块 (OS_TCBs) 是 C/OS-的核心数据结构,当任务的CPU 使用权被剥夺时,C/OS-用它来保存该任务的状态。当任务重新得到CPU 使用权时,任务控制块能确保任务从当时被中断的那一点丝毫不差地继续执行。OS_TCBs 全部驻留在RAM 中。一旦任务建立了,任务控制块OS_TCBs 将被赋值。 ?C/OS-II 任务控制块数据结构定义如下:typedef struct os_tcb OS_STK *OSTCBStkPtr; #if OS_TASK_CREATE_EXT_EN void *OSTCBExtPtr; OS_STK *OSTCBStkBottom; INT32U OSTCBStkSize; INT16U OSTCBOpt; INT16U OSTCBId; #endif struct os_tcb *OSTCBNext; struct os_tcb *OSTCBPrev; #if (OS_Q_EN & (OS_MAX_QS = 2) | OS_MBOX_EN | OS_SEM_EN OS_EVENT *OSTCBEventPtr; #endif #if (OS_Q_EN & (OS_MAX_QS = 2) | OS_MBOX_EN void *OSTCBMsg; #endif INT16U OSTCBDly; INT8U OSTCBStat; INT8U OSTCBPrio; INT8U OSTCBX; 名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 14 页,共 17 页 - - - - - - - - - INT8U OSTCBY; INT8U OSTCBBitX; INT8U OSTCBBitY; #if OS_TASK_DEL_EN BOOLEAN OSTCBDelReq; #endif OS_TCB; 其中:OSTCBStkPtr是指向当前任务栈顶的指针。OSTCBExtPtr 指向用户定义的任务控制块扩展。OSTCBStkBottom是指向任务栈底的指针。OSTCBStkSize存有栈中可容纳的指针元数目而不是用字节(Byte)表示的栈容量总数。OSTCBId 用于存储任务的识别码。OSTCBNext和 OSTCBPrev用于任务控制块OS_TCBs的双重链接。OSTCBEventPtr是指向事件控制块的指针。OSTCBMsg是指向传给任务的消息的指针。OSTCBStat是任务的状态字。OSTCBPrio是任务优先级。OSTCBDelReq是一个布尔量,用于表示该任务是否需要删除。OSTCBX, OSTCBY, OSTCBBitX和 OSTCBBitY用于加速任务进入就绪态的过程或进入等待事件发生状态的过程。3论述事件控制块ECB 数据结构答:?C/OS-II通过 uCOS_II.H 中定义的OS_EVENT数据结构来维护一个事件控制块的所有信息。该事件控制块ECB 数据结构的定义如下:typedef struct 名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 15 页,共 17 页 - - - - - - - - - void *OSEventPtr; /* 指向消息或者消息队列的指针*/ INT8U OSEventTblOS_EVENT_TBL_SIZE; /* 等待任务列表*/ INT16U OSEventCnt; /* 计数器 (当事件是信号量时) */ INT8U OSEventType; /* 时间类型*/ INT8U OSEventGrp; /* 等待任务所在的组*/ OS_EVENT; 其中:OSEventPtr指针 : 只有在所定义的事件是邮箱或者消息队列时才使用。当所定义的事件是邮箱时,它指向一个消息,而当所定义的事件是消息队列时,它指向一个数据结构. OSEventTbl 和 OSEventGrp: 两者包含的是系统中处于就绪状态的任务。OSEventCnt :当事件是一个信号量时,用于信号量的计数器。OSEventType :定义了事件的具体类型。4论述 ?C/OS-II 内存的管理的内存控制块数据结构答:为了便于内存的管理,在?C/OS-II中使用内存控制块(memory control blocks)的数据结构来跟踪每一个内存分区,系统中的每个内存分区都有它自己的内存控制块。内存控制块的数据结构定义如下:typedef struct void *OSMemAddr; void *OSMemFreeList; INT32U OSMemBlkSize; INT32U OSMemNBlks; INT32U OSMemNFree; OS_MEM; 其中:OSMemAddr是指向内存分区起始地址的指针。名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 16 页,共 17 页 - - - - - - - - - OSMemFreeList是指向下一个空闲内存控制块或者下一个空闲的内存块的指针。OSMemBlkSize是内存分区中内存块的大小,是用户建立该内存分区时指定的。OSMemNBlks是内存分区中总的内存块数量,也是用户建立该内存分区时指定的。OSMemNFree是内存分区中当前可以得空闲内存块数量。名师归纳总结 精品学习资料 - - - - - - - - - - - - - - -精心整理归纳 精选学习资料 - - - - - - - - - - - - - - - 第 17 页,共 17 页 - - - - - - - - -
展开阅读全文