Geekos操作系统的研究与实现操作系统课程设计报告书_第1页
Geekos操作系统的研究与实现操作系统课程设计报告书_第2页
Geekos操作系统的研究与实现操作系统课程设计报告书_第3页
Geekos操作系统的研究与实现操作系统课程设计报告书_第4页
Geekos操作系统的研究与实现操作系统课程设计报告书_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

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

文档简介

1、操作系统课程设计报告题 目:Geekos操作系统的研究与实现 专业:学号:学生:指导教师:2012年3月16日目录一、 实验目的2二、 项目设计要求3三、 开发环境的建立41 开发环境的介绍42 开发环境的构建4四、 项目设计原理5五、 项目设计的实现61 Project0项目的具体实现62 Project1项目的具体实现83 Project2项目的具体实现9六、 系统编译运行的结果20七、 遇到的问题和解决方法23八、 课程设计总结241、 实验目的1 Project0:熟悉GeekOS的项目编译、调试和运行环境,掌握GeekOS运行工作过程。2 Project1:熟悉ELF文件格式,了解G

2、eekOS系统如何将ELF格式的可执行程序装入到存,建立核进程并运行的实现技术。3 Project2:扩充GeekOS操作系统核,使得系统能够支持用户级进程的动态创建和执行。2、 项目设计要求1 Project0(1) 搭建GeekOS的编译和调试平台,掌握GeekOS的核进程工作原理。(2) 熟悉键盘操作函数,编程实现一个核进程。该进程的功能是:接收键盘输入的字符并显示到屏幕上,当输入ctrl+d时,结束进程的运行。2 Project1(1) 修改/geekos/elf.c文件:在函数Parse_ELF_Executable( )中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件

3、头、程序头,获取可执行文件长度,代码段、数据段等信息),并填充Exe_Format数据结构中的域值。(2) 在Linux环境下编译系统得到GeekOS镜像文件。(3) 编写一个相应的bochs配置文件。(4) 在bochs中运行GeekOS系统显示结果。3 Project2:要求用户对以下几个文件进行修改:(1) “src/GeekOS/user.c”文件中的函数Spawn(),其功能是生成一个新的用户级进程;(2) “src/GeekOS/user.c”文件中的函数Switch_To_User_Context(),调度程序在执行一个新的进程前调用该函数以切换用户地址空间;(3) “src/G

4、eekOS/elf.c”文件中的函数Parse_ELF_Executable()。该函数的实现要求和项目1一样。(4) “src/GeekOS/userseg.c”文件中主要是实现一些为实现对“src/GeekOS/user.c”中高层操作支持的函数。(5) “src/GeekOS/kthread.c”文件中的Start_User_Thread函数和Setup_User_Thread函数。Setup_User_Thread()函数的功能是为进程初始化核堆栈,堆栈中是为进程首次进入用户态运行时设置处理器状态要使用的数据。Start_User_Thread()是一个高层操作,该函数使用User_C

5、ontext对象开始一个新进程。(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、 开发环境的建立1 开发环境的介绍(1) Gee

6、kos是一个基于X86架构的PC机上运行的微操作系统核,由美国马理兰大学的教师开发,是一个用C语言开发的操作系统,GeekOS主要用于操作系统课程设计,目的是使学生能够实际动手参与到一个操作系统的开发工作中。学生可以在Linux或Unix环境下对其进行功能扩充,也可以在windows下使用Cygwin工具进行开发,其主要特点是:简单、实用、易懂,便于学生们理解操作系统的设计思想和实现过程。出于教学目的,这个系统核设计简单,却又兼备实用性,它可以运行在真正的X86 PC硬件平台。(2)VMware WorkstationVMware Workstation 是一款功能强大的桌面虚拟计算机软件,提

7、供用户可在单一的桌面上同时运行不同的操作系统,和进行开发、测试 、部署新的应用程序的最佳解决方案。它可以使你在一台机器上同时运行二个或更多Windows、DOS、LINUX系统。与“多启动”系统相比,VMWare采用了完全不同的概念。多启动系统在一个时刻只能运行一个系统,在系统切换时需要重新启动机器。VMWare是真正“同时”运行,多个操作系统在主系统的平台上,就象标准Windows应用程序那样切换。而且每个操作系统你都可以进行虚拟的分区、配置而不影响真实硬盘的数据,你甚至可以通过网卡将几台虚拟机用网卡连接为一个局域网,极其方便。安装在VMware操作系统性能上比直接安装在硬盘上的系统低不少,

8、因此,比较适合学习和测试。(3)Bochs PC 模拟器 GeekOS运行于Windows(或Linux)下的Bochs PC模拟器,Bochs 是用 C+ 开发的可移植的 IA-32 (x86) PC 模拟器,Bochs仿真器其实质就是一台虚拟PC机,它包括对 Intel x86 CPU 、通用 I/O 设备和可定制的 BIOS 的模拟,几乎可以运行在所有流行的平台上。目前, Bochs 可以模拟 386, 486, Pentium Pro 或者 AMD64 CPU 。Bochs 的模拟环境中也可以运行大部分的操作系统,包括 Linux, Windows 95, DOS, Windows N

9、T 4, FreeBSD, MINIX 等。2 开发环境的构建(1)下载并安装VMware虚拟机;(2)在VMware虚拟机上安装CentOS版本的linux操作系统。(3)在VMware虚拟机上的linux环境下对GeekOS源文件进行编译和,编译成功后生成fd.img软盘映射文件和hd.img硬盘映射文件;(4)下载并安装系统仿真工具Bochs;(5)在终端打开bochs-2.3-pre3$ ./configure 配置bochs的文件(6)make 工具无论是在Linux还是在UNIX环境中,make都是一个非常重要的系统开发工具。利用make工具,可以将大型的开发项目分解成为多个更易于

10、管理的模块,对于一个包括几百个源文件的应用程序,使用make和makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系。 make能够按照规则自动完成编译工作,并且可以只对程序员在上次编译后修改过的文件进行编译,减少重复编译的工作量。它就是字符操作方式下的项目管理工具。bochs-2.3-pre3$ makebochs-2.3-pre3$ make install(7)配置Bochs环境配置文件Bochsrc.txt:vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latestromimage: file=$BXSHARE/BIOS-bochs-

11、latest, address=0xf0000megs: aboot: cfloppya: 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=0(8)启动Boch环境

12、,在提示的出现GeekOS的欢迎界面。(9)GeekOS系统编译和仿真成功。4、 项目设计原理1. Project0利用bochs模拟计算机执行一个操作系统软件。其中BIOS-bochs-lastest文件模拟bochs硬件的BIOS。VGABIOS-lgpl-lastest文件模拟bochs显示系统的BIOS。bochsrc.txt描述模拟器硬件配置的配置。利用GeekOS提供的键盘处理函数keyboard.h与keyboard.c等进行键盘常用功能的模拟。其中,在keyboard.c里面提供了一个功用函数Keycode Wait_For_Key(void),循环等待一个键盘事件,然后返回一

13、个16位的数据 Keycode型的, 在keyboard.h里定义了所有的键盘代码。Read_Key(Keycode* keycode)函数可以处理队列键盘按键,可以保存到队列中并输出2. Project1在函数Parse_ELF_Executable( )中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息),并填充Exe_Format数据结构中的域值。可执行文件是通过源程序被编译成目标文件,多给目标文件连接生成的,然后被加载到存运行。可执行文件包含一些基本的要素代码、数据、重定位信息和符合信息。以与一些可选辅助信息,如调试信息、

14、硬件信息等。因此,通过分析可执行文件的信息,然后为其分配存并载入建立核线程,供系统调度运行。3. Project2与priject1一样,Entry.c为用户程序外壳,并与用户程序一起编译。再根据传递的中断向量查找并调用相关的中断处理程序,并实现调度进程的选择。5、 项目设计的实现1 Project0项目的具体实现(1)打开在geekos-0.3.0/src/project0/src/geekos/main.c修改main.c函数#include <geekos/bootinfo.h>#include <geekos/string.h>#include <geek

15、os/screen.h>#include <geekos/mem.h>#include <geekos/crc32.h>#include <geekos/tss.h>#include <geekos/int.h>#include <geekos/kthread.h>#include <geekos/trap.h>#include <geekos/timer.h>#include <geekos/keyboard.h>void project0() Print("To Exit hit

16、 Ctrl + d.n"); Keycode keycode; while(1) if( Read_Key(&keycode) ) /读取键盘按键状态 if(!( (keycode & KEY_SPECIAL_FLAG) | (keycode & KEY_RELEASE_FLAG) ) /只处理非特殊按键的按下事件 int asciiCode = keycode & 0xff; /低8位为Ascii码 if( (keycode & KEY_CTRL_FLAG)=KEY_CTRL_FLAG && asciiCode='d&

17、#39;) /按下Ctrl键 Print("n-BYE!-n"); Exit(1); else Print("%c",(asciiCode='r') ? 'n' : asciiCode); void Main(struct Boot_Info* bootInfo) Init_BSS(); Init_Screen(); Init_Mem(bootInfo); Init_CRC32(); Init_TSS(); Init_Interrupts(); Init_Scheduler(); Init_Traps(); Init_Ti

18、mer();Init_Keyboard(); Set_Current_Attr(ATTRIB(BLACK, GREEN|BRIGHT); Print("Welcome to GeekOS!n"); Set_Current_Attr(ATTRIB(BLACK, GRAY); struct Kernel_Thread *thread;thread = Start_Kernel_Thread(&project0,0,PRIORITY_NORMAL,false); Exit(0);(2)编译文件终端打开 $ cd project0/build 的目录: 在其目录下:$ ma

19、ke $ make depend 编译,生成文件 depend.mak和fd.img (3)建立配置文件bochsrc终端打开 $ cd project0/build 的目录:建立配置文件: $ gedit .bochs修改其容如下:vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latestromimage: file=$BXSHARE/BIOS-bochs-latest, address=0xf0000megs: 8boot: afloppya: 1_44=fd.img, status=inserted#floppya: 1_44=fd_aug.img, s

20、tatus=insertedlog: ./bochs.outkeyboard_serial_delay: 200floppy_command_delay: 500vga_update_interval: 300000ips: 1000000mouse: enabled=0private_colormap: enabled=0i440fxsupport: enabled=0(4)运行在build目录下输入:$ bochs f bochsrc2 Project1项目的具体实现(1)终端打开 $geekos-0.3.0/src/project1/src/geekos/elf.c修改elf.c函数#i

21、nclude <geekos/errno.h>#include <geekos/kassert.h>#include <geekos/ktypes.h>#include <geekos/screen.h> /* for debug Print() statements */#include <geekos/pfat.h>#include <geekos/malloc.h>#include <geekos/string.h>#include <geekos/elf.h>int Parse_ELF_Ex

22、ecutable(char *exeFileData, ulong_t exeFileLength,struct Exe_Format *exeFormat) int i; elfHeader *hdr =(elfHeader*) exeFileData; programHeader *phdr=(programHeader *)(exeFileData + hdr->phoff); struct Exe_Segment * segment= exeFormat->segmentList; for( i=0; i< hdr->phnum; i+) segment->

23、;offsetInFile = phdr->offset; segment->lengthInFile = phdr->fileSize; segment->startAddress = phdr->vaddr; segment->sizeInMemory = phdr->memSize; phdr+; segment+; exeFormat->numSegments = hdr->phnum; exeFormat->entryAddr = hdr->entry; return 0;(2)编译文件终端打开目录: $ cd pro

24、ject1/build 在其目录下:$ make $ make depend 编译,生成文件 depend.mak和fd.img (3)建立配置文件bochsrc终端打开目录: $ cd project1/build建立配置文件: $ gedit .bochs修改其容如下:config_interface: textconfigromimage: file=$BXSHARE/BIOS-bochs-latestmegs: 8vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest floppya: 1_44=./fd.img, status=inserteda

25、ta0: 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, sp

26、t=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 (4)运行在build目录下输入:$ bochs f bochsrc3 Project2项目的具体实现1 添加代码(1)修改src/GeekOS/user.c文件中的函

27、数Spawn(),其功能是生成一个用户级进程。(2)src/GeekOS/user.c文件中的函数Switch_To_User_Contex(),调度程序在执行一个新的进程前调用该函数以切换用户地址空间。(3)src/GeekOS/elf.c文件中的函数Prase_ELF_Executable()。该函数的实现要求和项目1一样。(4)src/GeekOS/userseg.c文件主要是实现一些为实现对src/GeekOS/user.c中高层操作支持的函数。 Destroy_User_Context()函数的功能是释放用户态进程占用的存资源。 Load_User_Program()函数的功能通过加

28、载可执行文件镜像创建新进程的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()函数的功能是为进程初始化核堆栈,堆栈中是为进程首次

29、进入用户态运行时设置处理器状态要使用的数据。 Start_User_Thread()是一个高层操作,该函数使用User_Context对象开始一个新进程。(6)src/GeekOS/kthread.c相关函数的修改。(7)src/GeekOS/syscall.c”文件中主要是实现用户程序要求核进行服务的一些系统调用函数定义。 要求用户实现的有Sys_Exit()函数、Sys_PrintString()函数、Sys_GetKey()、Sys_SetAttr()、Sys_GetCursor()、Sys_PutCursor()、Sys_Spawn()函数、Sys_Wait()函数和Sys_GetPI

30、D( )函数。(8)在main.c文件中改写生成第一个用户态进程的函数调用:Spawn_Init_Process(void)= 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;

31、/保存在存缓冲中的用户程序可执行文件 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, &exeFil

32、eLength) != 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

33、= 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函数创建一个进程并使其

34、进入准备运行队列*/ 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 (exe

35、FileData != 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_

36、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_currentUse

37、rContext) 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 conte

38、xt is active */ s_currentUserContext = userContext; = elf.c =copy project1= userseg.c =/需在此文件各函数前增加一个函数,此函数的功能是按给定的大小创建一个用户级进程上下文,具体实现如下:/函数功能:按给定的大小创建一个用户级进程上下文static struct User_Context* Create_User_Context(ulong_t size) struct User_Context * UserContext; size = Round_Up_To_Page(size); UserContext

39、 = (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(段描

40、述符表) /新建一个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_PRIVILEG

41、E, 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,

42、 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) F

43、ree(UserContext->memory); Free(UserContext); return 0;-/摧毁用户上下文void Destroy_User_Context(struct User_Context* userContext) /TODO("Destroy a User_Context");/ KASSERT(userContext->refCount = 0);/* Free the context's LDT descriptor */ Free_Segment_Descriptor(userContext->ldtDescr

44、iptor);/* Free the context's memory */ Disable_Interrupts();/ Free(userContext->memory);/ Free(userContext);/ Enable_Interrupts(); /释放占用的LDT Free_Segment_Descriptor(userContext->ldtDescriptor); userContext->ldtDescriptor=0; /释放存空间 Free(userContext->memory); userContext->memory=0;

45、/释放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; ul

46、ong_t maxva = 0;/要分配的最大存空间 unsigned numArgs;/进程数目 ulong_t argBlockSize;/参数块的大小 ulong_t size, argBlockAddr;/参数块地址 struct User_Context *userContext = 0; /计算用户态进程所需的最大存空间 for (i = 0; i < exeFormat->numSegments; +i) /elf.h struct Exe_Segment *segment = &exeFormat->segmentListi; ulong_t topv

47、a = 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

48、; size += argBlockSize; userContext = Create_User_Context(size);/按相应大小创建一个进程 if (userContext = 0)/如果为核心态进程 return -1; for (i = 0; i < exeFormat->numSegments; +i) struct Exe_Segment *segment = &exeFormat->segmentListi; /根据段信息将用户程序中的各段容复制到分配的用户存空间 memcpy(userContext->memory + segment-&

49、gt;startAddress, exeFileData + segment->offsetInFile,segment->lengthInFile); /格式化参数块 Format_Argument_Block(userContext->memory + argBlockAddr, numArgs, argBlockAddr, command); /初始化数据段,堆栈段与代码段信息 userContext->entryAddr = exeFormat->entryAddr; userContext->argBlockAddr = argBlockAddr;

50、 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_Con

51、text * 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 des

52、tInUser, 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->

53、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

温馨提示

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

评论

0/150

提交评论