嵌入式操作系统-wince编程实验chapter_第1页
嵌入式操作系统-wince编程实验chapter_第2页
嵌入式操作系统-wince编程实验chapter_第3页
嵌入式操作系统-wince编程实验chapter_第4页
嵌入式操作系统-wince编程实验chapter_第5页
免费预览已结束,剩余24页可下载查看

付费下载

下载本文档

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

文档简介

程与线程的描述这个章节将详细介绍WindowsCE系统中的进程(process)和线程(thread),并对WindowsCE操作系统所使用的调度策略进行分析。进程是资源分配的基本单位,而线程是调度的基本单位。这一章的程序代码主要节选于[CEROOT]PRIVATE\WINCEOS\COREOS\NK\KERNEL\目录的schedule.c、 以及 schedule.c、kernel.h的几个,其目的在于了解程序在开发执行时,对系统资进程概(text)、用户数据段(usersegment)以及系统数据段(systemsegment)共同组成一个执行环境,负责处理器、内存和等资源的分配和回收。进程是计算机系统资源(指令执行和处理器状态的改变)(变量的生成和初始化)和系统控(PCB(ProcessControlBlock)的生成和删除)。独立性是指各个进程的地地址空间的结构划分,如程序代码段、数据段和段划分。令和数据的集合,这些指令和程序代码在磁盘上成为可执行映像进程和程序的关进程和程序的区程序可以在设备(如:磁盘)上长期保存,而进程则是建立进程后产生,结束进程后WindowsCE的进程不同于Windows98或WindowsNT,最大差别在于WindowsCE最多只可以支持32个进程在系统中同时运行,系统启动的时候,将至少自动启动四个进程,一个是NK.exe,用来提供操作系统中kernel的服务,FILESYS.EXE它用来提供对GUI系统的支持,第四个是DEVICE.EXE,它用来加载和管理外围的驱动程序。他们占据虚拟地址的前四个slots,一个slot有32MB空间,详见资料部分的介绍,目前执行的进程将会对应到第一个slot (slot0)。大部分的WindowsCE系统,也会同时建立EXEPLORER.EXE进程﹔如果WindowsCE系统正在与个人计算机相连,则会启动REPLLOG.EXE和PAPISRV.EXE,他们用来管理个人计算机和WindowsCE系统之间的连接服务。所以使用者可以启动的进程最多大概有24个或稍微多一点但是对一般的使用来说这是足够的。Windows98WindowsNT系统,WindowsCE系统不支持一些功WindowsCE系统不支持许多进程和与线程相关的函数。WindowsCE(environment)Win32WindowsCE系统中并不存在。与Windows98或WindowsNT的进程相比较,WindowsCE进程包含比较少的状态信息。由于WindowsCE不支持驱动程序及工作 Directory)的概念,所以每个进程不需要保存这些信息。WindowsCE也不需要PCB中不需要有关于环境变量的部分。WindowsCE不支持句柄继承,所以也不需要告诉进程这些相关的信息。由于以上种种原因,WindowsCE进程的结构相对地简单很多。作对象(HANDLEhProc)。下面将简单介绍一个程序结构的主要部分:um:BYTE(ID)pProxList:存放proxy的队列,LPPROXY结构的dwVMBase:DWORD类别,记录进程在内存所占区域中的址pTh(详见线程介绍部分)pTh表示当前进BasePtr:LPVOID类别,指向载入.EXEame:LPWSTRPfnEH:进程例外处理器,PEXCEPTION_ROUTINEpmodResource:PMODULE类别,MODULENK/INC/kernel.h中所oe:openexe_tWindowsCE中所定义的程序结构。structProcess/*00:IDofthisprocess[ie:it'sslotnumber]/*01:IDofprocesscurrentlyDebugActiveProcess'ingthisprocess/*02:Didthecreatorwanttodebugchildprocesses?/*03:leveloftrustofthisexe#defineOFFSET_TRUSTLVL 3//offsetofthebTrustLevelmemberinProcessstructure /*04:listofproxiestothreadsblockedonthisprocess*/ /*08:handleforthisprocess,neededonlyforSC_GetProcFromPtr /*0C:baseofprocess'smemorysection,or0ifnotinuse*/ /*10:firstthreadinthisprocess*/ACCESSKEYaky; /*14:defaultaddressspacekeyforprocess'sthreads*/ /*18:Basepointerofexeload*/ /*1C:handleofthreaddebuggingthisprocess,ifany /*20:nameofprocess /*24:TLSinusebitmask(first32slots)*/ /*28:TLSinusebitmask(second32slots)*/PEXCEPTION_ROUTINEpfnEH; /*2C:processexceptionhandler*/ /*30:Debugzonepointer*/ /*34primarythreadinthis/*38:modulethatcontainstheresources/*3C:Pointertonamesforstdio/*48:Pointertocommandline/*4C:numberofpendingdyingthreads/*50:Pointertoexecutablefilehandle/*??:structurecontainingexeheader/*??:o32arraypointerforexe/*??:extendpdata/*??:highestpriorityofallthreadsoftheprocess/*??:thisprocesscannotbedebugged/*padding};/*Process进程的同步};/*ProcessWindowsCE在NK\INC\schedule.hevent(相当于触发器,可以用event的出现)、mutexsemaphore三种同步对象。而在NK\kernel.c中定义了这些同步对象的系统呼叫,可以用于进程或者线程的具体关于semaphore、mutex、event的机制不再介绍,这跟在Windows2000中的semaphore、mutex、event有一定的相同之处,读者可以参考Windows2000API函数。eventdnnsignaledalednonsignledindowsCE2.0开始,eventI有:_Createventevent传回相应的句柄﹔_teven_EventCloseeeventHANDLECreateEventBOOLbManualReset,BOOLLPTSTRBOOLSetEvent(HANDLEhEvent BOOLPulseEventHANDLEhEvent BOOLResetEvent(HANDLEhEvent //手动恢复nonsignal//手动恢复nonsignalmutexWindows2000对应semaphoreAPI有:SC_CreateSemaphore、SC_ReleaseSemaphore、SC_SemCloseHandlesemaphoresemaphore,以及关闭semaphoreWindows2000相关的等待函DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds)event(Block)event发出信号 //event //时间到,但等待的event没有发出信号 DWORDWaitForMultipleObjects(DWORDnCount,CONSTHANDLE*lpHandles,BOOLbWaitAll,DWORDdwMilliseconds);event,WindowsCEeventbWaitAll只能为FALSE,当任何一个event发信号时,函数就返回。其回传值与前一函数基本相同,只是如果是event发出信号而返回时,回传值为WAIT_OBJECT_0加上同时发出信号事件的句柄(handle)中最小的注标(即lpHandles发出信号事件中最小的注标)。线程概从60年代进程的概念提出以来,进程就一直是操作系立运行的基本单80立运行的基本单位——(thread),目的是用来减少程序同时执行时所需要的线程的概念线程是进程的一个实体,是CPU调度和分配的基本单位,除了一些在运行中必要的资源(例如:程序计数器,一些缓存器和堆栈),线程基本上不拥有系统(WindowsCE中是主线程)。下面将简单地比较线程源的基本单位。将原本进程的两个属性分开(为资源分配与调度的基本并行性:引入线程,不仅在进程之间可以并行执行,而且在一个进(如:缓存器)较少,所以线程之各项系统负荷远远小于进程所需。引入线程的优点在同一个进程内的线程共享进程所拥有的资源(包括内存和等),所以线程能独立执行所以能充分利用和发挥处理器与设备的并行工作能力WindowsCENK/INC/kernel.hwInfo信息SLEESLEEMontecarlokernel.h#define 2位(bit#define //1#define //1#define4//1#defineSLEE5//1#define6//1#define7//1#define8//1#define9//1#define//1#define//1#define//1#ifdef //1#define//1#define115各种信一个线程属于某个进程,一个进可能同时有多个线程,调度以线程为基3.2所3.2程线程调度信(bCPrio)(dwQuantum)信息,系统可以决定让线preempted后,它所剩下的时间信息也要保留。3.3线程时间信CPUuser-modekernel-mode时间,每个时间中断,3.4中列出了一些比较重要的时间信息:3.4kernel-modeuser-mode一些有关计数的信wCount用于停滞线程队列的计数,wCount2用于睡眠队列的计数。 /*nonceforblockinglists*/ /*nonceforSleepList*/ 有关WindowsCE调WindowsCEpreeemptivetime-slicedround-robin来进行调度,系统可能要在同的线程之间切换,所以切换时必须保存线程的上下文(context) /*thread'scpucontextinformationWindowsCEThreadstructstructThread/*00:variousinfoaboutthread,seeabove/*02:threadsuspendcount/*03:stateofwaitingloop/*04:listofproxiestothreadsblockedonthisthread/*08:nextthreadinthisprocess/*0C:pointertocurrentprocess/*10:pointertoownerprocess/*14:keysusedbythreadtoaccessmemory&handles/*18:currentapicallinfo/*1C:Originalstackbase/*20:Sizeoftheoriginalthreadstack/*24:tlspointer/*28:sleepcount,also tonwaitmult/*2c:TLSforsecurestack/*30:TLSfornon-securestack/*34:firstproxythisthreadisblockedon/*38:lasterror/*3C:Handletothisthread,neededbyNextThread/*40:basepriority/*41:currpriority/*42:nonceforblockinglists/*44:previousthreadinthisprocess/*48:pointertothreaddebugstructure,ifany/*4c/*50:timethreadiscreated/*58:cleaneventforunqueueingblockinglists/*5c:threadPCatcreation,usedtogetthreadname/*60:thread'scpucontextinformation/*??:next thread,if /*??:uprunpointer(circular)/*??:downrunpointer(circular)/*??:upsleeppointer(nullterminated)/*??:downsleeppointer(nullterminated)/*??:listofcrits(criticalsection)andmutexesforinversionnextnextrunnablethreadonrunq,ifrunnable /*??:backpointerif orrunnable};/*Thread/*??:threadquantum/*??:quantumleft/*??:proxyfromlastcriticalsectionblock,incasestolenback*//*??:pendingproxiesforqueueing/*??:returnvaluefrompendedwait/*??:timeoutvalueofwaitoperation/*??:pendingtimeout/*??:nonceforSleepList/*??:pendingsuspendcount/*??:recursivelevelindebugmessage/*??:Lastcrittaken,clearedbynextthread/*??:elapsedkerneltime/*??:elapsedusertime在schedule.h和schedule.c中还有一些重要的数据结构,这些数据结构在调 schedule.h中有关于线程时间的数据结构typedefstructTHREADTIMEstructTHREADTIMEHANDLEFILETIMEFILETIMEFILETIME/*kernel-mode时间FILETIME/*user-mode时间}THREADTIME,有关临界区的数据结构typedefstructCRIT{LPCRITICAL_SECTIONlpcs;LPPROXYLPPROXYLPCRITpPrev;BYTEbListed;BYTEbListedPrio;BYTEiOwnerProc;BYTEbPad;structCRIT*pPrevOwned;structCRIT*pNextOwned;structCRIT*pUpOwned;structCRIT*pDownOwned;LPCRITpNext;}

/*Pointertoacritical_sectionstructure/*previouseventinlist/*Isthisonsomeone'sownerlist/*Indexoftheownerprocess/*Prevcrit/mutex(forprioinversion)/*Nextcrit/mutexsectionowned(forprioinversion)/*NextCRITinlist有关可执行线程队列的数据结构typedefstruct/*可执行线程的队列/*目前正在执行的线程}pRunnable(doubly-linkedlist)组成的。PRIORITY_LEVELS_HASHSIZE的值是256,所以,pHashTable256entryentry各指向一个优先权等级大小不同的有关线程等待队列的数据结构/*/*下一个线程最长要被唤醒的时间/*指向睡眠队列的指针typedefstructsleeper_t{PTHREADpth;WORDwCount2;WORDwDirection;DWORD}结束线程(voidRemoveThread(RTs*pRTs)函数)相关结构typedefstructRTsPTHREADDWORDdwBase,DWORD//如果在fiberPPROCESSLPTHRDDBGHANDLEPTHREADCLEANEVENTCLEANEVENTCLEANEVENTLPDWORD}WindowsCEWindowsCE是一个preemptive的多任务操作系统,它采用以优先权顺序为主的时间片段循环方法(time-slicedround-robin)。一般来说线程是调度的基本单位,线程可执行一个固定的时间片段,在WindowsCE系统中有许多队列,分别 行的进程(如果有的话)则例外。可执行的线程处于一个可执行队列之中(对每一种优先级,都对应一个可执行队列,最多可有256个可执行队列)。其它队列关键(time-critical)的话,线程调度策略会把它排到相同优先级的执行队列的末继续执行。在WindowsCE系统中,一般设定的时间片段大小为100ms(当然这个时间片段可以藉由OEM厂商所开发的不同硬件来设置)。也就是说,WindowsCE以优先权顺序选择线程来执行,拥有最高优先级的线程将在低优先级的线程前面执行。基本调度情况如图3.1所示。3.1WindowsCE线程调度的时线程状态切换方式如下:线程执行完后,线就绪态转入睡眠态,或者线睡眠态转换到就绪态,分别藉由执行RemoveThread()、RunqDequeue()、NextThread()来呼叫MakeRun(),在这个函数中会呼叫相应的程序代码。当可执行队列前面插入了一个线程时,MakeRun()会进行判断,一个线程要目前执MakeRun()函数,时间片段的分配与中断相关,KCNextThread(void)函数中,将判断时Reschedule来判断是否关闭电源,还是执行当前线程相关的下一个线OEMIdleCPU闲置。如下面程序代码所示(NK\KERNEL\x86的fault.c中):NakedNaked{asm[KData].bPowerOff,0FFhshortrsd10[KData].bPowerOff,0////是–wordptr([KData].bResched),1shortrsd11wordptr([KData].bResched),dwordptr([KData].dwKCRes),1shortrsd12dwordptr([KData].dwKCRes),dwordptr([KData].dwKCRes),shorteax,[RunList.pth]eax,eaxshortrsd50eax,edishort//////.TSSring0堆栈指针,使其////(eax)=ptrtothreadstructure edi,eax esi,(THREAD)[eax].hTh hCurThd, PtrCurThd, ecx, [KData].lpvTls, edi, eax, eax, eax, CR0, edx,offsetg_aGlobalDescriptorTable+KGDT_PCR ecx,FS_LIMIT+1 wordptr[edx+2], ecx, byteptr[edx+4], byteptr[edx+7], ecx, [MainTSS].Esp0, //如果没有线程可以执行,呼叫OEMldle关闭掉CPUrsd50:cli wordptr([KData].bResched),

//(Program(esi)=(thread//////////(ecx)=threadlocalstorage//设置TLS(ThreadLocalStorage)//(ecx)=ptrtoNK_PCR//设置FS基址 //设置FS//设置FS//(ecx)=ptrtoendofcontextsave//edi}}shortDoReschedule}}shortDoReschedulebyteptr([KData].bResched),Windows98WindowsNT相比,WindowsCE为线程分配时间的方法有WindowsNT中,一个进程建立的时候将同时建立一个与优先级有关的类别(class),线程将从建立它的父进的优先级类别(priorityclass)中WindowsCEWindowsNT系统的优先级类别,所以所有的进程都WindowsCE(RUNSTATE_RUNNING—正在执行)、可执行态,睡眠态(等待态)。其中后面两种状态可以细分如下:可执行态 停滞态 WindowsCENK\INC\schedule.h中,关于优先级的定义如#define #define 8256NK\inc\schedule.h(hashvalue)对应到相应的优先级,一个优32。#define #definePRIORITY_LEVELS_HASHSCALE3.5被停滞的线程是指那些等待某种系统资源或同步对象的线程还没有用完自己所preemptd外,如果一个线程具TREAIORITICL优先级,这个有TREAD_PRIORIT_TIME_CRITICL优先级的线程将使所有的线程等待,直到他执行结束。所以这个优先级一般是被保留,用来在设备驱动中断时使用。级(priorityinversion),这样可以使低优先级的线程得以执行完并且释放它所占THREAD_PRIORITY_NORMAL,我们可以藉由SetThreadPriority(HANDLEhThread,intnPriority)intGetThreadPriority(HANDLEhThread)来得知它的优先级。跟调度有关的函数简shedule.cMakeRunIfNeeded(HANDLE//关闭profiling//关闭profiling机制//呼叫MakeRun重新调度//线程现正处在睡眠态,将它从睡眠队列中移除//开启profiling机制//检查线程的状态是否为即将进入可运行状态VoidMakeRunIfNeeded(HANDLE{PTHREADpth;if((pth=HandleToThread(hth))&&{if {pth->wCount++;}}MakeRun(PTHREADpth是优先级最高的线程,pth插入到可运行队列的最前面,并判断是否需要重新修改调度策略。否pthpth插入到合适的地方插入。VOIDMakeRun(PTHREADVOIDMakeRun(PTHREADpth{DWORDprio,prio2;PTHREADpth2,pth3;if(!pth- {prio=prio2=if(!(pth2=RunList.pRunnable)||(prio<{//没有可执行的线程或者pth自己就pth->pPrevSleepRun=最高优先权的线程if(pth->pNextSleepRun=//pth插入runnable队列最前面{并更新hashtablepth2->pPrevSleepRun=}pth->pUpRun=pth->pDownRun=RunList.pHashThread[prio2]=RunList.pRunnable=if(!RunList.pth||(prio<//检查是否需要重新调//没有其它正在执行的线程或者目前线程pth优先权是最高}else//设定重新调度//若有其它优先权高于pth的线if(!(pth2=找出队列中正确的位置将pth插入{//pth相同优先权的执行队RunList.pHashThread[prio2]=空while(!(pth2=RunList.pHashThread[--//找到前0hashtableentry;}if(prio<{//pth的优先权是否高于pth2DEBUGCHK(prio/PRIORITY_LEVELS_HASHSCALE==pth->pPrevSleepRun=pth2-pth->pNextSleepRun=//pth线程插在执行队列中pth->pUpRun=pth->pDownRun=之前pth->pPrevSleepRun->pNextSleepRun=pth2->pPrevSleepRun}else//pth优先权pth2,找到适当while((pth3=pth2->pNextSleepRun)置将pth插入执行队列(prio//pth//pth0//pth的优先权介于pth2pth3//pthpth2pth2//找到执行队列中优先权不高于pth2=DEBUGCHK(!pth3||(prio<GET_CPRIO(pth3)));DEBUGCHK(GET_CPRIO(pth2)<=prio);if(prio=={pth->pUpRun=pth2-pth->pUpRun->pDownRun=pth2->pUpRun=pth;pth->pDownRun=pth2;pth->pPrevSleepRun=pth->pNextSleepRun=}elseif(pth->pNextSleepRun={pth3->pPrevSleepRun=}pth->pPrevSleepRun=pth2->pNextSleepRun=pth->pUpRun=pth->pDownRun=}}}}elseDEBUGCHK(!((pth->wInfo>> T_SHIFT)&1));SET_RUNSTATE(pth,RUNSTATE_BLOCKED);}}可执行队列中删除,并让下一个可执行的线程运行(MakeRun)。voidRunqDequeue(PTHREADvoidRunqDequeue(PTHREADpth,DWORD{PTHREADpDown,DWORDprio=DEBUGCHK(!GET_SLEEDEBUGCHK(!pth-//checkifthereisahangingtailoftheif(pDown=pth-//检查pth之后是否有其它线{着让pth唤醒DEBUGCHK(!GET_NEEDSLEEP(pDown)&&GET_SLEEDEBUGCHK(cprio<=GET_CPRIODEBUGCHK(!pDown- pDown->wCountpDown->wCount2CLEAR_SLEEpDown->pUpSleep=pth->pDownSleep=//don'tworryaboutitifNEEDRUNflagisif(GET_RUNSTATE(pDown)!={//setupproxyforcleanupifnotpureif(pDown->lpce){//notsetifpurelypDown->lpce->base=pDown-pDown->lpce->size=(DWORD)pDown-pDown->lpProxy=}elseif(pDown->lpProxy)//mustbeaninterruptevent-specialcaseLPPROXYpprox=pDown-LPEVENTlpe=(LPEVENT)pprox-DEBUGCHK(pprox->bType==DEBUGCHK(pprox->pQDown==DEBUGCHK(pprox->pQPrev==DEBUGCHK(pprox->dwRetVal==lpe->pProxList=pprox->pQDown=pDown->lpProxy=}//replacepth'sslotifofsameif(cprio==GET_CPRIO//pthpDown的优先权是同{级的if(pth==pth-//pth相同优先权的等级且{执行状态的线程只有pth自己pDown->pUpRun=pDown->pDownRun=个}else//fixupthe//还有其pth相同优先权等if(pDown->pUpRun=pth-且处于可执行状态的线pDown->pUpRun->pDownRun=//pDown取代pthif(pDown->pDownRun=pth-table中相同优先权等级的队列pDown->pDownRun->pUpRun=的位置}//fixupnextif(pDown->pNextSleepRun=pth-pDown->pNextSleepRun->pPrevSleepRun=//pDown取代pth在执行队中的位//fixupprevnode,updatepRunnableifif(pDown->pPrevSleepRun=pth-{pDown->pPrevSleepRun->pNextSleepRun=}elseif(RunList.pRunnable==pth)RunList.pRunnable=//若是pth为正在执行的线程}pDown取代pth成为正在执行if(RunList.pHashThread[prio]==线程RunList.pHashThread[prio]=//pthhashtable中优先级为prio的队列的第一个,则须更新hashtable的值,SET_RUNSTATE(pDown,pDown取代之//设定pDown为可执行状态}//notofthesamepriority,justcall//mightwanttosaveaninstructionortwo//handlingthelogichere(don'thaveto//pDownpth为不同优//suspend/pRunnable,等级,只要呼叫MakeRunMakeRunpDown在执行队列中位置}}pDown=pth-pNext=pth-//没有其它线程等着让pth唤醒if(RunList.pHashThread[prio]==pth)pth从执行队列及hashRunList.pHashThread[prio]=((pDown!=pth)?pDown中移除,并对队列及hash(pNext&&(GET_CPRIO(pNext)/PRIORITY_LEVELS_HASHSCALE做适当更新(WORD)prio))?pNext:}if(pDown==//hashtable中优先权等{相同的队列中pth一个线if(!pth-{DEBUGCHK(RunList.pRunnable==if(RunList.pRunnable=pNext->pPrevSleepRun=}elseif(pth->pPrevSleepRun->pNextSleepRun=pNext->pPrevSleepRun=pth-}//hashtable中优先权等}else相同的队列中还有其它线程//pthhashtable中优先权pDown->pUpRun=pth-级与pth相同的队列中移除pth->pUpRun->pDownRun=//pth从执行队列中移除if(pth-{pth->pPrevSleepRun->pNextSleepRun=pDown->pPrevSleepRun=pth-goto}elseif(pth==RunList.pRunnable)RunList.pRunnable=if{pNext->pPrevSleepRun=pDown->pNextSleepRun=}}}}}SleepqDequeue(PTHREADvoidSleepqDequeue(PTHREAD{PTHREADvoidSleepqDequeue(PTHREAD{PTHREADDEBUGCHK(pth&& //pth的睡眠计数加1if(pth2=pth-pth等着其它线程唤醒它{DEBUGCHK(pth!=DEBUGCHK(!pth->pNextSleepRun&&!pth-if(pth2->pDownSleep=pth-//更新UpSleepDownSleep指针{pth从队列中移除pth2->pDownSleep->pUpSleep=pth->pDownSleep=}pth->pUpSleep=}elseif(pth2=pth->pDownSleep)//有其它线程等pth唤醒DEBUGCHK(!pth2->pNextSleepRun&&!pth2-//更新pNextSleepRunif(pth2->pNextSleepRun=pth-pPreySleepRun指标,将pth从睡{队列中移除pth2->pNextSleepRun->pPrevSleepRun=}if(pth2->pPrevSleepRun=pth-{pth2->pPrevSleepRun->pNextSleepRun=}elseDEBUGCHK(pth==SleepList=}//在睡眠队列中在心pth之前另有一pth2->pUpSleep=pth->pDownSleep=线程//////pth//pth//pNextSleepRunpPreySleepRunpth从睡眠队}elseif(pth2=pth->pPrevSleepRun)if(pth2->pNextSleepRun=pth-{pth2->pNextSleepRun->pPrevSleepRun=}}elseDEBUGCHK(pth==//updateSleepListanddwReschedTimedwReschedTime=dwPrevReschedTime+((RunList.pth&&RunList.pth->dwQuantum)?RunList.pth->dwQuantLeft:0x7fffffff);if(SleepList=pth-{SleepList->pPrevSleepRun=if((int)(dwReschedTime-SleepList->dwWakeupTime)>{dwReschedTime=SleepList-}}} }ThreadSleep(DWOR

温馨提示

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

最新文档

评论

0/150

提交评论