




免费预览已结束,剩余22页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
操作系统课程设计说明书题 目:GeekOS操作系统的研究与实现系 别: 计算机科学与工程学院 目录 1 GeekOS概述311GeekOS系统源代码结构32 实验环境53 项目实现531 project053.11项目设计目的53.12项目设计要求53.13项目设计原理53.13项目具体实现63.14调试运行结果732 project183.21项目设计目的83.22项目设计要求83.23项目设计原理93.23项目具体实现103.24调试运行结果1133 project2123.31项目设计目的123.32项目设计要求123.33项目设计原理133.23项目具体实现153.24调试运行结果254 遇到问题及解决方法265 学习总结27参考文献271 GeekOS概述GeekOS是一个基于X86架构的PC上运行的微操作系统内核。由美国马理兰大学的教师开发,是一个用C语言开发的操作系统。主要用于操作系统课程设计,目的是使学生能够实际动手参与到一个操作系统的开发工作中。出于教学目的,这个系统内核设计简单,却又兼备实用性,它可以运行在真正的X86 PC硬件平台。作为一个课程设计平台,GeekOS由一个基本的操作系统内核作为基础,提供了操作系统与硬件之间的所有必备接口,实现了系统引导、实模式到保护模式的转换、中断调用及异常处理、基于段式的内存管理,FIFO进程调度算法以及内核进程、基本的输入输出(键盘作为输入设备、显示器作为输出设备),以及一个用于存放用户程序的只读文件系统PFAT。学生可以在Linux或Unix环境下对其进行功能扩充,且其针对进程、文件系统、存储管理等操作系统核心内容分别设计了7个难度逐渐增加的项目供学生选择 11GeekOS系统源代码结构 GeekOS操作系统源文件geekos-0.3.0.zip可以从下载。解压后的GeekOS目录结构如图1-3所示:在doc目录下文件hacking.pdf和index.htm是GeekOS系统的参考文档。Scripts目录下有startProject和removeEmptyConflicts两个脚本文件。GeekOS系统的源文件在src目录下,分为7个项目:Project0, Project1, Project2, Project3, Project4, Project5, Project6。每个项目的文件结构都类似,以Project0为例,结构如图1-4所示:在build文件夹中,包含系统编译后的可执行文件的文件、软盘镜像或是硬盘镜像、makefile项目管理文件。在inculde文件夹中有geekOS和libc两个子目录,在geekOS子目录中有kthread.h,keyboard.h等头文件,在libc中包含有geekOS支持的C语言标准函数string.H头文件。在scripts文件夹是项目编译时要用到的一些脚本文件。Src文件夹中存放系统内核源代码,用户修改geekOS系统时要修改的源代码如main.c都位于这个目录中。在User子目录中一般是用来存放用户的测试文件,在tools子目录中的代码是用来建立PFAT测试文件系统的。2 实验环境(1)硬件环境:本次课设是在虚拟机上安装Linux进行开发调试,具体安装使用方法如下: 、安装linux虚拟机 本次课设的虚拟机是运行在oracle VM Virtualbox上的,下载Linux镜像文件后,即可按提示即可安装。 、GeekOS:是一个基于X86架构的PC机上运行的微操作系统内核,由美国 马理兰大学的教师开发,是一个用C语言开发的操作系统, GeekOS主要 用于操作系统课程设计,目的是使学生能够实际动手参与到一个操作系统的 开发工作中。 GeekOS的使用:打开linux虚拟机,直接解压GeekOS压缩包就可使用, 无需安装。 、Bochs安装和使用:在Linux系统中需先解压软件包,然后再配置编译生成系统文件。(2)软件环境:标准C语言3 项目实现31 project03.11项目设计目的熟悉GeekOS的项目编译、调试和运行环境,掌握GeekOS运行工作过程。3.12项目设计要求(1)搭建GeekOS的编译和调试平台,掌握GeekOS的内核进程工作原理。(2)熟悉键盘操作函数,编程实现一个内核进程。该进程的功能是:接受键盘输入的字符并显示到屏幕上,当输入Ctrl+D时,结束进程的运行。3.13项目设计原理项目要求从键盘输入,因此要用到键盘处理函数 。GeekOS的键盘处理函数定义在keyboard.h与keyboard.c 文件中。键盘的初始化是在Main函数中调用Init_Keyboard进行的,Init_Keyboard主要功能是设置初始状态下存放键盘扫描码的缓冲区,并为键盘中断设置处理函数。而键盘中断处理过程是:首先从相应的I/O端口读取键盘扫描码,根据是否按下Shift键,分别在键值表中寻找扫描码对应的按键值,经过处理后将键值放入键盘缓冲区,最后通知系统重新调度进程。若用户进程需要从键盘输入信息,可调用Wait_For_Key()函数,进程调用该函数后,会阻塞进入按键操作的等待队列,直到按键操作结束,进程才会被唤醒。Start_Kernel_Thread函数主要功能就是建立一个内核线程。本项目主要要求设计一个函数对键盘的中断进行响应。这主要通过使用Geekos提供的键盘响应函数Wait_For_Key()进行键盘中断的响应及返回键值。该函数首先检查键盘缓冲区是否有按键,如果有,就读取一个键码,如果此时键盘缓冲区中没有按键,就将线程放入键盘事件等待队列。于是可分两步完成:1 编写函数,函数功能是:接受键盘输入的按键,并将键值显示到显示器,当输入Ctrl+D退出。2 在Main函数体内调用Start_User_Thread函数,将编写的函数地址传递给startFunc,建立一个内核进程。3.13项目具体实现编写的函数static void keyin(void) Keycode keycode;Print(n-Wait for your input,Ctrl+d to exit-n);while(1)keycode=Wait_For_Key(); /读取键盘按键状态if(!( (keycode & 0x0100) | (keycode & 0x8000) ) /处理非特殊按键的按下事件 int asciiCode = keycode & 0x03ff; /低8位为Ascii码if( (keycode & 0x4000)=0x4000 & asciiCode=d) /按下Ctrl键Print(n-Bye-n);Exit(1); elsePrint(%c,(asciiCode=r) ? n : asciiCode); (2) 首先注释Main()函数中TODO宏,并调用Start_Kernel_Thread函数,将步骤1编写的函数地址传递给参数,建立一个内核级进程 struct Kernel_Thread *thread; thread = Start_Kernel_Thread(&project0,0,PRIORITY_NORMAL,false);(3) 执行make depend及make命令,此时会在build目录下生成fd.img(4) 编写brochs 配置文件vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latestromimage: file=/usr/share/bochs/BIOS-bochs-latest, address=0xf0000megs: 8boot: afloppya: 1_44=fd.img, status=inserted#floppya: 1_44=fd_aug.img, status=insertedlog: ./bochs.outkeyboard_serial_delay: 200floppy_command_delay: 500vga_update_interval: 300000ips: 1000000mouse: enabled=0private_colormap: enabled=0i440fxsupport: enabled=03.14调试运行结果进入/os/ project0/build目录执行 make depend执行 make成功之后在build 目录下生成fd.img文件。启动bochs在build目录中执行bochs f bochsrc成功后,运行结果:32 project13.21项目设计目的熟悉ELF文件格式,了解GeekOS系统如何将ELF格式的用户可执行程序装入内存,建立内核进程并运行的实现技术。3.22项目设计要求(1)修改/geekos/elf.c文件:在函数Parse_ELF_Executable()中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息),并填充Exe_Format数据结构的域值。(2)掌握GeekOS在核心态运行用户程序的原理,为项目2的实现做准备。3.23项目设计原理ELF是UNIX系统实验室作为应用程序二进制接口而开发和发布的。有两个平行视图。连接程序视图 执行程序视图 ELF 头部ELF 头部 程序头部表(可选) 程序头部表 节区1 段 1 . 节区 n 段 2 . . . 节区头部表 节区头部表(可选) GeekOS中的用户程序全部在系统的编译阶段完成编译和连接,形成可执行文件,用户可执行文件保存在PFAT文件系统中。本项目要完成的就是在系统启动后,从PFAT文件系统将可执行文件装入内存,建立进程并运行得到相应的输出。在磁盘中的ELF文件的映像和在内存中执行程序镜像间的对应关系如下图所示而此过程主要由Spawner函数实现,其主要经过简要概述为:先调用Read_Fully函数将文件读入内存,后调用Parse_ELF_Executable函数分析ELF文件,最后调用Spawn_Program函数将可执行程序的代码段和数据段等装入内存,此后便可以开始运行一个内核级进程了。如下图所示:在本项目中,我们要完成Parse_ELF_Executable函数,此函数的作用为根据ELF文件格式,从exeFileData指向的内容中得到ELF文件头,继续分析可得到程序头和程序代码段等信息。3.23项目具体实现修改project/project1/src/geekos/elf.c文件:在函数Parse_ELF_Executable( )中添加代码,分析 ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文 件长度,代码段、数据段等信息),并填充Exe_Format数据结构中的域值1、elf.c:将ELF格式的可执行程序装入到内存,建立内核进程并运行.= elf.c = int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat) int i; elfHeader *head=(elfHeader*)exeFileData; programHeader *proHeader=(programHeader *)(exeFileData+head-phoff); KASSERT(exeFileData!=NULL); KASSERT(exeFileLengthhead-ehsize+head-phentsize*head-phnum); KASSERT(head-entry%4=0); exeFormat-numSegments=head-phnum; exeFormat-entryAddr=head-entry; for(i=0;iphnum;i+) exeFormat-segmentListi.offsetInFile=proHeader-offset; exeFormat-segmentListi.lengthInFile=proHeader-fileSize; exeFormat-segmentListi.startAddress=proHeader-vaddr; exeFormat-segmentListi.sizeInMemory=proHeader-memSize; exeFormat-segmentLtFlags=proHeader-flags; proHeader+;return 0;=2、编译,成功后生成两个镜像文件:fd.img和diskc.img。其中,Diskc.img为模拟器能引导的操作系统镜像 。 3、编写相应的bochs配置文件 由于生成了diskc.img,因此配置文件需加上以下内容config_interface: textconfigmegs: 8vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latestromimage: file=$BXSHARE/BIOS-bochs-latestfloppya: 1_44=./fd.img, status=insertedata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14ata1: enabled=0, ioaddr1=0x170, ioaddr2=0x370, irq=15#ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11#ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9ata0-master: type=disk, path=diskc.img, mode=flat, cylinders=40, heads=8, spt=64#ata0-slave: type=cdrom, path=/dev/cdrom, status=insertedboot: aips: 1000000log:./bochs.outvga_update_interval: 300000keyboard_serial_delay: 250keyboard_paste_delay: 100000private_colormap: enabled=0 3.24调试运行结果进入/os/ project1/build目录执行 make depend执行 make成功之后在build 目录下生成fd.img、diskc.img文件。启动bochs在build目录中执行成功后,运行结果:33 project23.31项目设计目的扩充GeekOS操作系统内核,使得系统能够支持用户级进程的动态创建和执行。3.32项目设计要求开始本项目前需要阅读/src/geekos目录中的entry.c、lowlevel.asm、kthread.c、userseg.c,其中在userseg.c中主要关注Destroy_User_Context()和Load_User_Program()两个函数。本项目要求用户对以下几个文件进行修改:1)“src/GeekOS/user.c”文件中的函数Spawn(),其功能是生成一个新的用户级进程;2)“src/GeekOS/user.c”文件中的函数Switch_To_User_Context(),调度程序在执行一个新的进程前调用该函数以切换用户地址空间;3)“src/GeekOS/elf.c”文件中的函数Parse_ELF_Executable()。该函数的实现要求和项目1相同。4)“src/GeekOS/userseg.c”文件中主要是实现一些为实现对“src/GeekOS/user.c”中高层操作支持的函数。 Destroy_User_Context()函数的功能是释放用户态进程占用的内存资源。 Load_User_Program()函数的功能通过加载可执行文件镜像创建新进程的User_Context结构。 Copy_From_User()和Copy_To_User()函数的功能是在用户地址空间和内核地址空间之间复制数据,在分段存储器管理模式下,只要段有效,调用memcpy函数就可以实现这两个函数的功能。 Switch_To_Address_Space()函数的功能是通过将进程的LDT装入到LDT寄存器来激活用户的地址空间;5)“src/GeekOS/kthread.c”文件中的Start_User_Thread函数和Setup_User_Thread函数。 Setup_User_Thread()函数的功能是为进程初始化内核堆栈,堆栈中是为进程首次进入用户态运行时设置处理器状态要使用的数据。 Start_User_Thread()是一个高层操作,该函数使用User_Context对象开始一个新进程。6)“src/GeekOS/kthread.c”文件中主要是实现用户程序要求内核进行服务的一些系统调用函数定义。要求用户实现的有Sys_Exit()函数、Sys_PrintString()函数、Sys_GetKey()、Sys_SetAttr()、Sys_GetCursor()、Sys_PutCursor()、Sys_Spawn()函数、Sys_Wait()函数和Sys_GetPID( )函数。7)在main.c文件中改写生成第一个用户态进程的函数调用:Spawn_Init_Process(void) 。3.33项目设计原理Geekos的初始系统不支持用户态进程,但提供了用户态进程上下文接口和实现用户态进程需要用到的数据结构。所以,用户态进程及相关函数都要开发者实现。在Geekos中为了区分用户态进程和内核进程,在Kernel_Thread结构体中设置了一个字段userContext,指向用户态进程上下文。对于内核进程来说,这个指针为空,而用户态进程都拥有自己的用户上下文。因此在Geekos中要判断一个进程是内核进程还是用户态进程,只要通过userContext字段是否为空来判断就可以了。GeekOS的进程结构图如10-1所示:图10.1 用户态进程结构每个用户态进程都拥有属于自己的内存段空间,如:代码段、数据段、堆栈段等,每个段有一个段描述符(segment descriptor),并且每个进程有一个段描述符表(Local Descriptor Table),用于保存该进程的所有段描述符。操作系统中还设置一个全局描述符表(GDT,Global Descriptor Table),用于记录了系统中所有进程的ldt描述符。GDT、LDT和User_Context的关系如图10-2所示图10-2 GDT、LDT和User_Context的关系根据exeFileData得出程序头表的偏移量程序流程图:用户程序编译和连接后形成可执行文件保存在PFAT文件系统从PFAT文件系统读入可执行文件装入内存缓冲区对装入内存中的可执行文件进行分析创建一个用户进程,并加入准备运行队列用户进程运行结束,调用Exit函数退出初始化User_Context结构,并将可执行程序的程序段和数据段载入内存GeekOS的用户级进程创建过程可以描述如下:(1)Spawn函数导入用户程序并初始化:调用Load_User_Program进行User_Context的初始化及用户级进程空间的分配及用户程序各段的装入;(2)Spawn函数调用Start_User_Thread(),初始化一个用户态进程,包括初始化进程Kernel_Thread结构以及调用Setup_User_Thread初始化用户级进程内核堆栈;(3)最后Spawn函数退出,这时用户级进程已被添加至系统运行进程队列,可以被调度了。3.23项目具体实现1、添加代码= user.c =/产生一个进程(用户态)int Spawn(const char *program, const char *command, struct Kernel_Thread *pThread) /TODO(Spawn a process by reading an executable from a filesystem); int rc; /标记各函数的返回值,为0则表示成功,否则失败 char *exeFileData = 0;/保存在内存缓冲中的用户程序可执行文件 ulong_t exeFileLength;/可执行文件的长度 struct User_Context *userContext = 0;/指向User_Conetxt的指针 struct Kernel_Thread *process = 0;/指向Kernel_Thread *pThread的指针 struct Exe_Format exeFormat;/调用Parse_ELF_Executable函数得到的可执行文件信息 if (rc = Read_Fully(program, (void*) &exeFileData, &exeFileLength) != 0 ) /调用Read_Fully函数将名为program的可执行文件全部读入内存缓冲区 Print(Failed to Read File %s!n, program); goto fail; if(rc = Parse_ELF_Executable(exeFileData, exeFileLength, &exeFormat) != 0 ) /调用Parse_ELF_Executable函数分析ELF格式文件 Print(Failed to Parse ELF File!n); goto fail; if(rc = Load_User_Program(exeFileData, exeFileLength, &exeFormat, command, &userContext) != 0) /调用Load_User_Program将可执行程序的程序段和数据段装入内存 Print(Failed to Load User Program!n); goto fail; /在堆分配方式下释放内存并再次初始化exeFileData Free(exeFileData); exeFileData = 0;/* 开始用户进程,调用Start_User_Thread函数创建一个进程并使其进入准备运行队列*/ process = Start_User_Thread(userContext, false); if (process != 0) /不是核心级进程(即为用户级进程) KASSERT(process-refCount = 2);/* 返回核心进程的指针 */ *pThread = process; rc = process-pid;/记录当前进程的ID else/超出内存 project2includegeekoserrno.h rc = ENOMEM; return rc;fail: /如果新进程创建失败则注销User_Context对象 if (exeFileData != 0) Free(exeFileData);/释放内存 if (userContext != 0) Destroy_User_Context(userContext);/销毁进程对象 return rc;-/切换至用户上下文void Switch_To_User_Context(struct Kernel_Thread* kthread, struct Interrupt_State* state) /TODO(Switch to a new user address space, if necessary); static struct User_Context* s_currentUserContext; /* last user context used */ /extern int userDebug; struct User_Context* userContext = kthread-userContext;/指向User_Conetxt的指针,并初始化为准备切换的进程 KASSERT(!Interrupts_Enabled(); if (userContext = 0) /userContext为0表示此进程为核心态进程就不用切换地址空间 return; if (userContext != s_currentUserContext) ulong_t esp0; /if (userDebug) Print(A%pn, kthread); Switch_To_Address_Space(userContext);/为用户态进程时则切换地址空间 esp0 = (ulong_t) kthread-stackPage) + PAGE_SIZE; /if (userDebug) / Print(S%lxn, esp0);/* 新进程的核心栈. */ Set_Kernel_Stack_Pointer(esp0);/设置内核堆栈指针/* New user context is active */ s_currentUserContext = userContext; = elf.c =同 project1= userseg.c =/需在此文件各函数前增加一个函数,此函数的功能是按给定的大小创建一个用户级进程上下文,具体实现如下:/函数功能:按给定的大小创建一个用户级进程上下文static struct User_Context* Create_User_Context(ulong_t size) struct User_Context * UserContext; size = Round_Up_To_Page(size); UserContext = (struct User_Context *)Malloc(sizeof(struct User_Context); /为用户态进程 if (UserContext != 0) UserContext-memory = Malloc(size); /为核心态进程 else goto fail; /内存为空 if (0 = UserContext-memory) goto fail; memset(UserContext-memory, 0, size); UserContext-size = size; /以下为用户态进程创建LDT(段描述符表) /新建一个LDT描述符 UserContext-ldtDescriptor = Allocate_Segment_Descriptor(); if (0 = UserContext-ldtDescriptor) goto fail; /初始化段描述符 Init_LDT_Descriptor(UserContext-ldtDescriptor, UserContext-ldt, NUM_USER_LDT_ENTRIES); /新建一个LDT选择子 UserContext-ldtSelector = Selector(KERNEL_PRIVILEGE, true, Get_Descriptor_Index(UserContext-ldtDescriptor); /新建一个文本段描述符 Init_Code_Segment_Descriptor( &UserContext-ldt0, (ulong_t) UserContext-memory, size / PAGE_SIZE, USER_PRIVILEGE ); /新建一个数据段 Init_Data_Segment_Descriptor( &UserContext-ldt1, (ulong_t) UserContext-memory, size / PAGE_SIZE, USER_PRIVILEGE ); /新建数据段和文本段选择子 UserContext-csSelector = Selector(USER_PRIVILEGE, false, 0); UserContext-dsSelector = Selector(USER_PRIVILEGE, false, 1); /将引用数清0 UserContext-refCount = 0; return UserContext;fail: if (UserContext != 0) if (UserContext-memory != 0) Free(UserContext-memory); Free(UserContext); return 0;-/摧毁用户上下文void Destroy_User_Context(struct User_Context* userContext) /TODO(Destroy a User_Context); /释放占用的LDT Free_Segment_Descriptor(userContext-ldtDescriptor); userContext-ldtDescriptor=0; /释放内存空间 Free(userContext-memory); userContext-memory=0; /释放userContext本身占用的内存 Free(userContext); userContext=0;-int Load_User_Program(char *exeFileData, ulong_t exeFileLength,struct Exe_Format *exeFormat, const char *command, struct User_Context *pUserContext) /TODO(Load a user executable into a user memory space using segmentation); int i; ulong_t maxva = 0;/要分配的最大内存空间 unsigned numArgs;/进程数目 ulong_t argBlockSize;/参数块的大小 ulong_t size, argBlockAddr;/参数块地址 struct User_Context *userContext = 0; /计算用户态进程所需的最大内存空间 for (i = 0; i numSegments; +i) /elf.h struct Exe_Segment *segment = &exeFormat-segmentListi; ulong_t topva = segment-startAddress + segment-sizeInMemory; /* FIXME: range check */ if (topva maxva) maxva = topva; Get_Argument_Block_Size(command, &numArgs, &argBlockSize);/获取参数块信息 size = Round_Up_To_Page(maxva) + DEFAULT_USER_STACK_SIZE;/用户进程大小=参数块总大小 + 进程堆栈大小(8192) argBlockAddr = size; size += argBlockSize; userContext = Create_User_Context(size);/按相应大小创建一个进程 if (userContext = 0)/如果为核心态进程 return -1; for (i = 0; i numSegments; +i) struct Exe_Segment *segment = &exeFormat-segmentListi; /根据段信息将用户程序中的各段内容复制到分配的用户内存空间 memcpy(userContext-memory + segment-startAddress, exeFileData + segment-offsetInFile,segment-lengthInFile); /格式化参数块 Format_Argument_Block(userContext-memory + argBlockAddr, numArgs, argBlockAddr, command); /初始化数据段,堆栈段及代码段信息 userContext-entryAddr = exeFormat-entryAddr; userContext-argBlockAddr = argBlockAddr; userContext-stackPointerAddr = argBlockAddr; /将初始化完毕的User_Context赋给*pUserContext *pUserContext = userContext; return 0;/成功-/将用户态的进程复制到内核缓冲区bool Copy_From_User(void* destInKernel, ulong_t srcInUser, ulong_t bufSize) /TODO(Copy memory from user buffer to kernel buffer); struct User_Context * UserContext = g_currentThread-userContext; /-: check if memory if validated if (!Validate_User_Memory(UserContext,srcInUser, bufSize) return false; /-:user-kernel memcpy(destInKernel, UserContext-memory + srcInUser, bufSize); return true;-/将内核态的进程复制到用户态bool Copy_To_User(ulong_t destInUser, void* srcInKernel, ulong_t bufSize) /TODO(Copy memory from kernel buffer to user buffer) struct User_Context * UserContext = g_currentThread-userContext; /-: check if memory if validated if (!Validate_User_Memory(UserContext, destInUser, bufSize) return false; /-:kernel-user memcpy(UserContext-memory + destInUser, srcInKernel, bufSize); return true;-/切换到用户地址空间void Switch_To_Address_Space(struct User_Context *userContext) /TODO(Switch to user address space using segmentation/LDT); ushort_t ldtSelector= userContext-ldtSelector;/* Switch to the LDT of the new user context */ _asm_ _volatile_ (lldt %0:a(ldtSelector); = kthread.c =添加头文件 #include -/创建一个用户进程/*static*/ void Setup_User_Thread(struct Kernel_Thread* kthread, struct User_Context* userContext) /TODO(Create a new thread to execute in user mode); ulong_t eflags = EFLAGS_IF; unsigned csSelector=userContext-csSelector;/CS选择子 unsigne
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 浙江汽车职业技术学院《深度报道研究》2023-2024学年第二学期期末试卷
- 黑龙江林业职业技术学院《信息系统开发与应用综合专题》2023-2024学年第二学期期末试卷
- 河北医科大学临床学院《土地规划设计》2023-2024学年第二学期期末试卷
- 重庆信息技术职业学院《环境与健康》2023-2024学年第二学期期末试卷
- 新疆维吾尔医学专科学校《卫生监督学A》2023-2024学年第二学期期末试卷
- 晋中师范高等专科学校《机械基础与液压传动》2023-2024学年第二学期期末试卷
- 上海中侨职业技术大学《中医诊断学实验》2023-2024学年第二学期期末试卷
- 湖南司法警官职业学院《机器视觉系统设计与应用》2023-2024学年第二学期期末试卷
- 2024年医学研究与试验发展服务项目资金申请报告代可行性研究报告
- 连续刚构桥毕业设计答辩
- 天然气管道工程段线路安装工程鱼塘(水塘)穿越施工方案
- 教练技术三阶段讲义
- 证券公司营业部网络结构拓扑图
- 2001船舶修理价格本中文
- 某污水处理厂自控系统调试方案(常用)
- 蓝色背景-PPT模板
- 设备检维修作业票填写模板
- 危大工程动态管控表
- 商场重大危险源评估标准
- 新生儿和儿童吞咽障碍的临床评估
- DB62∕T 25-3111-2016 建筑基坑工程技术规程
评论
0/150
提交评论