




已阅读5页,还剩23页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
操作系统中的TCP/IP软件结构,TCP/IP协议的细节探究,对于大多数计算机专家来说,TCP/IP协议的细节,以及实现它们的软件的结构仍然是一个谜。从表面上看,是否理解TCP/IP的内部细节并不重要,但是,使用TCP/IP的程序员们都知道,如果能够完全了解协议是如何运作的,他们将会编写出更健壮的程序代码。譬如说,一个了解TCP紧急数据处理过程的程序员可以在他们的应用程序中增加更多的功能,而那些不了解此细节的程序员则无法做到。即使了解一些简单的概念,诸如TCP是如何对数据进行缓冲的,也会对程序员在设计、实施和调试应用程序时有所帮助。举个例子,如果程序员不了解输出缓存、报文段传送、输入缓存和TCP推数据操作之间的关系,那么他就无法成功地编制出利用TCP协议的程序。研究TCP输入输出的细节,能够使程序员形成一个概念模型,用于解释各部分是如何交互作用的,并有助于理解如何使用底层机制。,TCP/IP协议和操作系统,在绝大多数运行TCP/IP软件的计算机中都利用一个操作系统来管理资源,比如管理某些外部设备。操作系统提供对并行处理的支持。即使在单处理器的计算机上,操作系统也能够通过迅速切换CPU的服务对象使用户感觉多个程序在同时执行。另外,操作系统管理着主内存,其中存放了正在执行的程序,同时它还管理着存放文件系统的二级(非易失)存储器。TCP/IP软件一般位于操作系统内部,并且可以被在机器上运行的所有应用程序所共享,操作系统的一些概念与TCP/IP实现,进程的概念进程的优先级进程的同步通信进程间通信设备驱动程序和输入、输出程序网络的输入和中断向高层协议传递分组IP协议与传输协议之间的数据报传递向应用程序的传递操作输出时的信息流从TCP经过IP到网络输出UDP输出处理,进程的概念,在操作系统中有几个抽象概念,这些概念在理解TCP/IP协议的实现时要用到。其中最重要应该是“进程”或者说“控制线程”。从概念上讲,一个进程就是一次计算过程,并且它独立于其他计算过程。一个操作系统提供创建新进程和结束现有进程的机制。在我们将要使用的范例系统中,程序调用函数create创建一个新进程。create函数返回一个整型的进程标识号。在对进程操作时,利用进程标识号来引用该进程。procid=create(arguments);/*createanewprocess*/进程一旦被创建,就独立于它的创建者而工作。若要结束一个进程,则需要在程序中调用kill,并将create函数返回的进程标识号作为参数,传递给kill。kill(procid);/*terminateaprocess*/,进程的概念,常规(顺序执行)程序根据单一的控制线程,逐步执行属于某个“程序”的代码。与常规程序不同,进程并不局限于任何特定的代码段或数据。操作系统允许两个或更多的进程执行同一段代码。例如,虽然操作系统的代码只有一份,但两个进程可以并发地执行操作系统的代码。实际上,两个或更多的进程并发执行某个过程中的特定代码段是完全可能的。由于进程是独立执行的,因此它们执行的速度有快有慢。在特定情况下,进程有时会因为执行某些操作而导致被“阻塞”或被“挂起”。例如,如果一个进程试图从键盘上读入一个字符,则它可能要等待用户按下键盘上的一个键。为了避免此类正在等待的进程占用CPU,操作系统阻塞该进程而允许其他进程继续执行。稍后,当操作系统接收到一个键盘敲击事件后,它将允许这个等待键盘输入的进程“恢复”执行。,进程的概念,TCP/IP软件的实现方案中使用了多个并发执行的进程。利用进程可将软件划分成更小的,更容易管理的模块,而不用编写一个能够处理全部可能事件序列的程序。我们将看到,使用进程可以简化设计,并使程序代码更容易理解和修改。进程在处理存在于许多协议中的超时和重发机制时尤其有用。使用单一的程序解决多个协议中的超时问题将导致程序复杂化,因为超时有可能重叠。例如,假设我们试图编写一个能够管理所有TCP/IP协议中的定时器的程序。某个高层协议,例如TCP,可能会产生一个报文段,并将其封装在数据报中,然后发送该数据报,并启动定时器。同时,IP必须为此数据报选择路由,并将其传送到网络接口。最后,一个诸如ARP的低层协议将被唤醒,它可能会重复多次地循环执行传送请求、设置定时器、处理定时器溢出等动作,此时的重发请求与TCP的定时器无关。在一个单一的程序中,当某个协议的定时器溢出,而程序却正在执行另一个协议的代码时,其处理过程是很困难的。如果系统对每一个要求超时机制的协议使用单独的进程来处理,那么进程只需处理与该协议有关的超时事件,因而使进程的代码更容易理解,不易出错。,进程的优先级,我们曾说过,所有的进程并发执行,但这样说法过于简单化。事实上,设计软件的程序员给每一个进程都分配有一个“优先级”。操作系统在给各进程分配CPU使用权时参照各进程的优先级。我们采用的优先级分配策略简单易懂:CPU将由没有被阻塞的进程赋予最高优先级。如果多个进程具有相同的优先级别,那么CPU将在这几个进程之间快速切换。协议软件中的优先级策略是非常有用的,因为它允许程序员赋予某进程在执行时优先于其他进程的权力。例如,现有一个普通的应用程序和一个协议软件,该协议软件必须从硬件中接收抵达的分组。设计者可以给实现该协议的进程以高优先级,迫使它先于应用程序的进程执行。由于操作系统处理了所有的进程调度细节,进程本身可以不包含任何有关调度的代码,进程的同步通信,如果每个进程都是一个独立的计算过程,那么数据是怎样从一个进程传递到另一个进程的呢?答案是:操作系统必须提供允许进程之间相互通信的机制。我们使用以下三种机制:计数信号量(semaphore)、端口和消息传递。通常,计数信号量就是通用的进程同步机制。操作系统提供一个函数screate,如有必要,调用它以创建信号量。screate返回一个信号量标识号,在此之后的有关该信号量的操作中必须用到这个信号量标识号。semid=screate(initcount);/*createsemaphore,specifyingcount*/每个信号量包含一个计数用的整数,调用者在创建信号量时赋予该整数一个初始值。一旦信号量产生,进程可以通过调用操作系统的wait和signal函数来管理信号量。当进程调用wait时,操作系统对信号量的计数值减1,如果计数值变为负值,则进程被阻塞。当进程调用signal函数时,操作系统将信号量的计数值加1,如果有任何一个进程因该信号量而被阻塞的话,此时这个进程将被重新激活。,进程的同步通信,尽管wait和signal从字面上看来可能使人产生误解,但实际上它们可以帮助解决几个重要的进程同步问题。最重要的是,它们可以用来提供“互斥”机制。互斥意味着在某段时间内,只允许一个进程执行一段特定代码。由于多个进程可以并发地执行同一段代码,因此互斥机制非常重要。要理解互斥机制的必要性,只需考虑一下如果两个向链表中增加新表项的进程同时执行将会带来什么后果。如果两个进程并发执行,它们可能在链表的同一个地方开始,并试图插入新表项。根据每个进程占用CPU时间长度的不同,第一个进程执行一段时间,然后切换到另一个进程继续执行,接着又一次切换到第一个进程,以此类推,其结果是,一个进程可能会推翻另一个进程的工作(使其中一个新表项完全丢失),或者可能会产生一个含有不正确的指针的错误链表。为了防止进程之间相互干扰,所有允许多个进程并发执行的协议软件必须使用信号量来实现互斥操作。要做到这一点,程序员必须为所有应受保护的代码段提供一个信号量,其初始值均为1。,进程的同步通信,s=screate(1);/*createmutualexclusionsemaphore*/然后,程序员用wait和signal语句将关键代码段围起来,如下所示:wait(s);/*beforecodetobeprotected*/.criticalcode.signal(s);/*aftercodetobeprotected*/第一个进程执行到wait(s)时,将信号量s的计数值减至0,并继续执行(由于计数器此刻不为负值)。如果进程结束,则会执行signal(s),使s的计数值回到1。然而,如果第一个进程正在使用关键代码时,第二个进程调用wait(s),计数器值变为负值,因此第二个进程将被阻塞。类似地,如果此时碰巧还有第三个进程执行wait(s),计数器仍然为负值,则第三个进程也将被阻塞。当第一个进程使用关键代码完毕后,它将调用signal(s),给计数器加1,唤醒第二个进程。第二个进程执行关键代码,此时第三个进程仍然在等待。当第二个进程结束并执行了signal(s)后,第三个进程才能够开始使用关键代码。其中心思想是任何时候只能有一个进程执行关键代码,所有其他试图执行相同关键代码的进程都将被信号量阻塞。,进程的同步通信,除了提供互斥机制以外,本书的范例中还利用信号量来保持队列访问的同步。由于队列的容量有限,因此队列的同步也是必要的。假设一个队列空间可以容纳N个数据项,有多个并发进程要将它们生成的数据置入该队列中。与此同时我们假设另外一组进程将从队列中提取数据并处理它们(一般来讲,是多个进程插入,一个进程提取)。通常往队列中插入数据的进程被称为“生产者”,而从队列中提取数据的进程被称为“消费者”。例如,这些数据可能是由一组用户应用程序产生的IP数据报,而一个IP进程提取这些数据报,选择路由并将它们送往相应的目的站。如果应用程序产生数据报的速度比IP进程处理这些数据报的速度快,那么队列最终将被装满。在队列全满的情况下,任何试图插入新元素的生产者必须被阻塞,以等待消费者从队列中取出一个数据项,使队列腾出一个空间。同样,如果消费者的执行迅速,它可能会将队列中的所有数据全部取出,那么此时它必须被阻塞,以等待另一个数据的到来。在访问一个长度为N的队列时,需要两个信号量来调度生产者和消费者。这两个信号量初始化如下:s1=screate(N);/*countsspaceinqueue*/s2=screate(0);/*countsitemsinqueue*/信号量被初始化以后,生产者和消费者利用它们来协调同步。一个生产者的执行如下:wait(s1);/*waitforspace*/.insertiteminnextavailableslot.signal(s2);/*signalitemavailable*/而消费者执行:wait(s2);/*waitforiteminqueue*/.extractoldestitemfromqueue.signal(s1);/*signalspaceavailable*/信号量保证了当队列全满时生产者进程被阻塞,而当队列全空时,消费者进程被阻塞。除此之外,生产者和消费者可以正常运行。,进程间通信:端口(port),在操作系统中,“端口”(port)这个抽象模型把进程的数据集中传送到一点处理。我们把端口看成是一个有限的消息队列,该消息队列有两个控制访问的信号量。程序调用pcreate函数生成一个端口,其入口参数指定队列的大小。pcreate返回一个端口标识号,用于引用该端口。portid=pcreate(size);/*createaportspecifyingsize*/生成一个端口后,进程可以调用过程psend和preceive来置入和取出端口中的数据项。psend过程向端口发送消息。psend(portid,message);/*sendamessagetoaport*/psend需要两个入口参数:一个端口标识号和一个要发送的单字消息(在TCP/IP代码中,消息经常是一个指针,指向某一分组)。preceive从端口中提取一个消息。message=preceive(port);/*extractnextmessagefromport*/,进程间通信:端口(port),就像我们建议的那样,在我们的实现方案中使用了信号量机制,如果端口全满,则调用psend的进程将被阻塞,而如果端口全空,则调用preceive的进程将被阻塞。一旦某个进程由于调用psend过程而被阻塞,那么它将保持阻塞状态,直至另一个进程调用了preceive为止。反之亦然。因此,在设计用到端口的进程体系时,程序员必须保证该体系不会使进程永远被阻塞(这正如同要求程序员在编制顺序执行软件时,必须小心地避免死循环一样)。除了避免由于进程间的交互作用而使进程永远被阻塞之外,对某些设计的要求也极为严格。它们要求某些进程无论在什么情况下都不应当被阻塞,即使是很短暂的阻塞时间也不行。如果这些特定的进程被阻塞,系统就无法正常工作。例如,在一个网络系统中,可能会需要网络传入进程永远不被阻塞,以避免某些应用程序在停止接收传入分组的情况下会导致整个系统瘫痪。在这种情况下,进程需要做出判断,如果调用psend过程会不会导致进程被阻塞。如果会,进程将采取变更方案(例如:丢弃该分组)。为了使进程能够判断调用psend是否会导致阻塞,系统提供了一个函数:pcount,它允许进程检测一个端口是否已满。n=pcount(portid);/*findoutwhetheraportisfull*/进程调用pcount,将待检测的端口标识作为入口参数。pcount返回该端口内现有的数据项计数。如果返回值为0,则端口为空。如果返回值与端口的大小相等,则端口全满。,进程间通信:消息传送,进程还可以通过消息传送来实现相互通信以及传送同步。消息传送允许一个进程直接将消息发送给另外一个进程。进程通过调用send函数将消息发送给其他进程。send函数以一个进程标识号和一个消息作为入口参数,并将给定的消息发送到指定的进程。send(msg,pid);/*sendintegermsgtoprocesspid*/一个进程调用receive来等待某个消息的到达。message=receive();/*waitformsgandreturnit*/在系统中,调用receive的进程将被阻塞,直至某个消息到达,而调用send函数的进程总是继续运行。如果在两次连续的send调用之间没有进程调用receive函数以接收消息,那么第二次调用send函数时将返回SYSERR,而该消息将无法发送。构造一个良好的系统,以保证消息不会丢失的工作由程序员来完成。,进程间通信:消息传送,为了协助消息交换的同步,程序可以调用函数recvclr,该函数删除所有正在等待被接收的消息,但并不阻塞进程。message=recvclr();/*clearmessagebuffer*/由于通常协议规定了等待确认的最长时间,因此在程序中经常会使用消息传送函数recvtim,该函数是receive的翻版,但它允许调用者指明等待的最长时间。如果消息在此上限时间内到达,recvtim将其返回给调用者。否则,revtim返回一个特定代码TIMEOUT。message=recvtim(50);/*wait5seconds(50tenthsofa*/*second)foramesgandreturnit*/,设备驱动程序和输入、输出程序,网络接口硬件将网络上传送来的分组送入计算机内存中,并通知操作系统一个分组已经到达。通常,网络接口使用“中断”机制来完成这一任务。一个中断使CPU暂时将正常处理挂起,跳转到被称为“设备驱动程序”(devicedriver)的代码段执行。此时由设备驱动程序管理操作的所有细节。例如,它要重置硬件中断,并且(可能)使网络接口硬件继续工作,准备接收下一个分组。设备驱动软件同时通知协议软件已有一个分组到达,并要求它马上处理。当设备驱动程序完成了这些繁琐的工作后,它将从中断中返回,CPU继续从中断发生处往下执行。因此,我们可以认为中断暂时“借用”了一下CPU来处理I/O事务。与许多操作系统相同,当一个分组到达时,Xinu操作系统安排网络接口设备中断处理器的正常工作。由设备驱动程序代码处理该中断,并清理设备,使它可以继续接收下一个分组。设备驱动程序同时还要为发送或接收该分组的程序提供一个方便的端口。实际上,它允许进程等待传入的分组(此时被阻塞)。从进程的观点来看,设备驱动程序隐藏在常规I/O接口背后,使传入分组的捕获更加容易。例如,要向以太网的一个接口发送一个帧(一个分组),程序调用如下所示:write(device,buff,len);/*writeoneEthernetpacket*/其中device是设备描述符,指定某个以太网接口设备,buffer给出将要传送的帧所处的缓冲区地址,len是以八位组(octet)计数的帧长度。,网络的输入和中断,既然我们已经了解了由操作系统提供的各种设施,不妨来研究一下TCP/IP范例软件的整体结构。回想一下,在操作系统中包含有设备驱动软件,该软件用来与硬件I/O设备通信,并处理中断。这些程序代码隐含在被称为“设备”的概念模型里,系统中包含了多个这种“设备”,其中每一个“设备”都对应于一个与机器直接相连的网络(许多主机只有一个网络接口,而网关或路由器具有多个网络接口)。为了适应分组到达的随机性,系统必须具有能够从任何网络接口中读取分组的能力。可以有多种方式解决等待随机接口的问题。一些操作系统使用计算机的“软件中断”(softwareinterrupt)机制。当一个分组到达时,产生一个硬件中断(hardwareinterrupt),设备驱动程序接收分组,重置接口设备。在中断返回之前,设备驱动程序会通知硬件安排下一个较低优先级的中断。当此次硬件中断结束后,低优先级中断会继续执行,“就好像是另外一个硬件设备产生了中断一样”。这种“软件中断”将CPU的正常处理挂起,使CPU跳到处理软件中断的代码段执行。因此,在某些系统中,所有的输入处理过程均以一系列中断的形式完成。这个思想正式体现在UNIX系统采用的机制中,被称为“流水线”(STREAMS)机制。软件中断是有效的,但它需要一些硬件,而这些硬件并不是每个计算机都具有的。为了使我们的协议软件具有普遍适用性,我们没有采纳软件中断机制,所有代码设计都只是依赖于常规的中断机制。,网络的输入和中断,即使使用常规硬件中断的操作系统也有许多方法可以解决多接口问题。一些操作系统使用的机制是:允许单个进程在多个输入设备上阻塞,当其中某个输入设备上有分组到达时,该进程立即被唤醒。另外一些操作系统则为每个接口各提供一个进程,允许该进程在与其相对应的接口上阻塞,而当该接口有分组到达时,对应的进程被唤醒。为了使我们的设计具有高效率,我们使用了图2.1所示的组织形式。分组从网络接口硬件出发,经过操作系统中的设备驱动程序,到达与该设备相关的一个输入队列中以太网的中断响应程序通过输入分组的分组类型字段,判断该分组应用的是什么协议。例如,如果一个以太网分组的分组类型值为080016,则分组携带的是一个IP数据报。对于那些不能自识别帧类型的网络,系统设计者要么选用某个链路层的协议,以判别分组中的内容,要么认为分组类型为“预定类型”(priori)。前者的例子为IEEE802.2链路层协议,后者的例子为SLIP协议。,向高层协议传递分组,由于输入操作发生在中断期间,此时设备驱动程序代码不能调用任何过程来进一步处理输入分组,它必须立即从中断中返回,因此中断过程并不直接调用IP。再者,由于系统使用一个独立的进程实现IP,因此设备驱动程序也不可能直接调用IP。事实上,系统应用了前面所述的队列以及消息传送原语,以实现进程之间通信的同步。当一个携带IP数据报的分组到达后,中断过程必须将该分组置入队列中,并调用send函数通知IP进程已有一个数据报到达。如果此时IP进程手头没有分组可处理,则调用receive函数等待下一个数据报的到达。每一个网络设备都有一个相对应的输入队列,而惟一的IP进程从所有的队列中提取数据报进行处理。图2.2说明了这一概念。网络设备驱动程序和实现IP的进程之间的通信是利用一组队列完成的。当一个数据报到达时,网络输入进程将其放入队列中,并发送一个消息给IP进程,IP协议与传输协议之间的数据报传递,一旦IP进程接受了一个传入的数据报,它必须决定对将其发往何处做进一步处理。如果数据报中的内容是一个TCP报文段,则必须将其交付给TCP模块。如果它携带的是用户数据报(UDP),则必须将其交付给UDP模块,以此类推。今后,我们将详细介绍每一个模块的内部结构。而此刻,我们只关心这些进程的结构。,将传入的数据报发送给TCP,由于TCP比较复杂,因而在许多设计方案中,由一个独立的进程来处理传入的TCP报文段。由于IP和TCP作为各自独立的进程执行,因而IP和TCP必须利用进程间的通信机制来通信。它们应用前面介绍过的端口机制。IP调用psend将报文段存放在端口内,而TCP调用preceive来读取端口中的报文段。我们将会看到,其他一些向TCP发送报文的进程也要利用这个端口。一旦TCP接收到一个报文段,就利用TCP协议端口号来寻找该报文段所属的连接。如果报文段中含有数据,TCP将把数据添加到与该连接相关的一个缓冲区中,并给发送方返回一个确认。如果输入的报文段中含有对发送出去的数据的确认,TCP输入进程还必须与TCP定时器管理进程通信,取消超时重发事件,将传入的数据报发送给UDP,处理传入UDP数据报的进程结构与处理TCP进程采用的结构差别很大。由于UDP比TCP要简单得多,UDP模块不作为独立进程存在。事实上,它是由一些常规过程组成,IP进程通过调用来处理传入的UDP数据报。这些过程检查UDP目的站协议端口号,根据该端口号为UDP数据报选择一个操作系统队列(端口)。IP进程把UDP数据报存放在相应的端口中,使应用程序可从这些端口中提取数据报。数据报在高层协议软件间的流动。IP进程向TCP进程发送传入报文段,却直接将UDP数据报放入各个端口中,应用程序可以从这些端口中访问该数据报,向应用程序的传递操作,UDP根据协议端口号,将传入的用户数据报多路分解,将它们分别放入各自相应的操作系统队列中。同时,TCP分离出传入数据流,将这些数据存放在缓冲区。当一个应用程序希望接收UDP数据报或是TCP流的数据时,它必须访问UDP端口或TCP缓冲区。具体细节比较复杂,理解的一个简单概念:由于每个应用程序都作为一个独立的进程执行,它必须利用由系统提供的通信原语,才能与实现协议的进程协调。例如,一个应用程序必须调用操作系统的preceive函数,来读取UDP数据报。当然,应用程序与操作系统内的进程之间的交互作用,要比操作系统内部的两个进程之间的交互作用复杂得多。对于传入的TCP数据来说,应用程序并没有利用preceive函数。事实上,系统利用了信号量机制来控制对TCP缓冲区中数据的访问。希望从传入数据流中读取数据的应用程序将对控制缓冲区的信号量调用wait,而当TCP进程要将数据放入缓冲区时,它调用signal。,输出时的信息流,产生输出分组的原因不外乎以下两者之一。某个应用程序将数据传递给一个高层协议软件,高层协议软件为此而向某个低层协议发送一个报文(或数据报),最终引起网络上的数据传输。操作系统中的协议软件要传送一些信息(例如,一个确认或是一个对回送请求的响应)。在这两种情况下,到达底层硬件上的数据帧都必须经过某个网络接口发送出去。为了有助于将分组的传输动作从那些实现了应用程序或协议的进程的运行中分离出来,系统为每一个网络接口设立了一个独立的输出队列。图2.4描述了这种设计思想。网络输出和存储输出分组队列。使用队列可将进程与网络传送分隔开对应于输出设备的输出队列是整体设计思想的重要组成部分。有了这些队列,进程生成一个分组后,就可以将其存放在队列中等待输出,而进程继续往下执行,毋需等待发送该分组。同时,硬件也可以并行地连续发送分组。当一个分组到达底层时,如果硬件正处于空闲状态(队列中没有分组存在),那么执行输出任务的进程将该分组放入队列中,同时调用一个设备驱动程序来启动硬件。当输出操作完成后,硬件向CPU发出中断请求。此时设备驱动程序从中断处理部分开始工作,将刚被发送出去的分组从队列中删除。如果队列中还有其他等待发送的分组,那么中断处理程序将重启硬件,以继续发送下一个分组。最后中断处理程序从中断中返回,使其他常规处理得以继续执行。因此,从IP进程的角度来看,分组传送是在后台自动进行的。只要队列里还有分组,硬件就会连续不断地传送它们。只有当IP进程将一个分组放入空队列中时才需要启动硬件。当然,输出队列的容量是有限的,如果系统生成分组的速度比网络硬件传送分组的速度快,那么队列将会溢出。我们假设这种情况是罕见的。但如果确实出现了这种情况,生成分组的队列必须做出选择:要么丢弃分组,要么被阻塞,直到硬件完成传送一个分组的任务,队列腾出新的空间为止。,从TCP经过IP到网络输出,与TCP的输入一样,TCP的输出也是很复杂的。输出时必须先建立连接,所传送的数据必须放在报文段中,在对方发来确认之前,报文段必须不断重发。一旦某个报文段被装入一个数据报中后,就可被交付给IP,由IP来处理选路和传送。系统软件利用了两个TCP进程来处理这一复杂过程。第一个进程被称作tcpout,由它来处理大部分的报文分段和数据传输细节。另一个进程是tcptimer,它管理着一个定时器,并为超时重发事件定时,当某个报文段不得不重发时,它将通知tcpout。tcpout进程利用一个端口使来自多个进程的输入同步。由于TCP是面向数据流的,因而它允许应用程序一次发送几个
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年焊工考试过关检测试卷含答案详解(研优卷)
- 现代物流管理与运营优化策略
- 学生阅读指导方法与案例分析
- 企业工伤事故调查报告模板范本
- 高中历史 第一单元 古代中国经济的基本结构与特点 第1课 农业的主要耕作方式和土地制度说课稿 北师大版必修2
- 2023年度粮油食品检验人员检测卷(原创题)附答案详解
- 高效率康奈尔笔记模板及使用方法
- 2024广播电视编辑记者题库检测试题打印含完整答案详解【典优】
- 环境保护法规及企业遵守要点解读
- 2024-2025学年度自考专业(公共关系)全真模拟模拟题附答案详解(典型题)
- 马凡综合征课件
- 党章党规党纪知识测试题及答案
- 医院人事管理制度岗前培训
- 车险合作协议补充协议
- 高尔夫tpi教学课件
- 2025至2030年中国软包电池行业市场供需规模及投资前景预测报告
- 老年共病管理中国专家共识(2023)课件
- 2025年新高考2卷(新课标Ⅱ卷)语文试卷
- 外卖危害知多少
- DB31/T 968.1-2016全过程信用管理要求第1部分:数据清单编制指南
- 钢材代储协议书
评论
0/150
提交评论