




已阅读5页,还剩7页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1 org 07c00h ;这是告诉编译器将这段程序加载到内存偏移地址0x7c00处 mov ax,cs mov ds,ax mov es,ax call DispStr jmp $DispStr: mov ax,BootMessage mov bp,ax mov cx,16 mov ax,01301h mov bx,000ch mov dl,0 int 10h retBootMessage: db hello,os world!times 510-($-$) db 0dw 0xaa55这是第一章的程序,从中可以看出起始位置是07c00h,程序的结束标志是0xaa55。$表示当前行被汇编后的地址。$表示一个节的开始处被汇编后的地址。那么$-$表示本行距离程序开始处的相对距离。1.对于程序的说明 实际上以上程序只是一个引导扇区(Boot Sector),不是完整的OS,当计算机电源被打开时,它会加电自检(POST),然后寻找启动盘,如果是从软盘启动,计算机就会检查软盘的0面0磁道1扇区,如果发现它以0xaa55结束,并且包含了少于512字节的执行码,那么BIOS认为它是一个引导扇区。 一旦发现了引导扇区,就将这512字节放入内存地址0000:7c00处,然后跳转并且移交控制器。2.制作软盘过程 本身这段程序是boot.asm,是汇编程序,然后使用工具nasm将其转换为boot.bin,然后使用软盘绝对扇区读写工具将这个文件写到一张空白软盘的第一个扇区。(.bin文件的说明:*.bin是Foxbase定义的一种特殊的可执行文件,它同DOS的.com文件类似,区别在于.com文件是从100H开始运行的,而.bin从0开始,.bin的调用参数保存在DS:BX中。如果在Foxbase中,你可以使用Call命令调用。如果你在VFP中,将无法直接调用,只能使用Debug反汇编,分析其功能,然后重新编写.)3.方括号的使用 在NASM中,任何不被方括号括起来的标签或变量名都被认为是地址,访问标签中的内容必须使用。3.1认识保护模式1.对于这一节的程序的理解数据区: 先是定义了一些描述符(LABEL_GDT,LABEL_DESC_CODE32,LABEL_DESC_VIDEO),然后定义了GdtPtr,它是一个6字节的数据结构,前两字节表示GDT的界限(即描述符的界限),后4字节表示GDT的基地址(GdtPtr就是用来描述GDT的,会用一个寄存器GDTR来保存它的值)。然后定义两个选择子,分别指向已定义的描述符。代码区:初始化32位代码段描述符,为加载GDTR作准备,加载GDTR: lgdt GdtPtr,然后是关中断:cli,(保护模式下中断处理的机制是不同的,不关中断会出错)打开地址线A20,然后将cr0的PE位置为1(0:实模式,1:保护模式),准备切换到保护模式,跳转到保护模式中 jmp dword SelectorCode32:0 2.Descriptor是一个宏,它是一个8字节的数据结构。三个描述符组成了一个结构数组。3.BITS 16说明这是一个16位的代码段,而BITS 32是32位的代码段。程序中的知识点要搞清楚的有:GDT是一个索引,指向一个数据结构的表项。GDT作用:提供段式存储机制。程序对GDT做了什么?jmp SelectorCode32:0? GDT1.在保护模式下,CPU有着巨大的寻址能力(保护模式下为4G,实模式下为1M),并为强大的32位操作系统提供了更好的硬件保障。2.在保护模式下,段值仅仅变成了一个索引,这个索引指向一个数据结构的一个表项,表项中定义了段的起始地址、界限、属性等内容。这个数据结构就叫GDT,表项就叫描述符。3.描述符分为代码段和数据段描述符,还可以分为系统段描述符和门描述符。4.选择子 TI位 0:GDT,1:LDT当TI和RPL都为0时,选择子就变为了偏移。5.段:偏移经过段机制转化为“线性地址”,而不是物理地址。 JMP1.对于cr0的设置是实模式和保护模式的关键2.jmp dword SelectorCode32:0的作用是将描述符DESC_CODE32对应的段的首地址。3.为什么那儿有一个dword呢?(没有dword的话编译出来是16位的代码,而目标地址是32位的。) 进入保护模式的主要步骤:1.准备GDT2.用lgdt加载gdtr3.打开A204.设置cr0的PE位5.跳转,进入保护模式 描述符的属性1.P位存在位。P=1,表示在段内存中存在;P=0则在内存中不存在。2.DPL位描述符特权级。有0,1,2,3级,数字越小级别越大3.S位指明描述符是数据段/代码段(s=1),还是系统段/门描述符(s=0) 3.2保护模式进阶在这一节中,首先在上一节程序的基础上改写:(1)跳入保护模式之后再次跳回实模式(2)读写大地址内存对于读大地址内容,它是先读,然后再写,然后再读,以此来判断是否可以读写大地址内存。(注意:程序中调用子程序时要保存edi的值,要对其进行压栈,所以要用到堆栈,那么在程序中也要有堆栈的描述符和堆栈的选择子。)从保护模式跳转到实模式时要复杂一些(从实模式到保护模式只要一个跳转就可以了),我们不能直接从32位的代码段中返回实模式,只能从16位代码段中返回,因为无法从32为代码段返回时cs高速缓存寄存器中的属性符合实模式的要求。所以增加一个Normal描述符,在返回实模式前把对应的选择子加载到ds、ss和es. 跳回实模式之后要做的事情:用Normal描述符的选择子加载到ds、ss和es,值cr0的PE=0,关闭A20地址线,开中断STI。 LDT:局部描述符表1.LDT与GDT差不多,一个是全局的,一个是局部的。在代码中也要有一个LDT的描述符和选择子,它的初始化有所不同,有两步:(1)初始化LDT在GDT中的描述符,(2)初始化LDT中的描述符。其中,第二步与GDT类似,多了一个在GDT中定义LDT。2.LDT与GDT的区别就在于选择子中TI位(0为GDT,1为LDT)3.在使用LDT时需要先用lldt指令加载ldtr(lgdt加载gdtr)4.使用LDT的目的:多任务处理。5.保护模式“保护”的含义(1)描述符中的段基址和段界限定义了一个段的范围,对超越段界限之外的地址的访问是被禁止的,这是对段的一种保护。(2)有点复杂的段属性作为对一个段各个方面的定义规定和限制了段的行为和性质以上两点是静态的(3)在涉及特权级的每一步中,处理器都会对CPL,DPL和RPL等内容进行比较,这种比较是动态的。 特权级概述1.常规保护模式错误2.将Level0叫做内核,Level1、Level2叫做服务,Level3叫做应用程序。3.CPL、DPL、RPLCPL 是当前执行的程序或任务的特权级。通常情况下,CPL等于代码所在段的特权级,当程序转移到不同特权级的代码段时,处理器将改变CPL。当处理器访问一个与CPL特权级不同的一致代码段时,CPL不会被改变。DPL 表示段或者门的特权级,下面是各种类型的段或者门的情况 数据段:高级-低级,相同级别之间 非一致代码段:相同级别之间 调用门:与数据段一致 一致代码段和通过调用门访问的非一致代码段:低级-高级,相同级别之间 TSS(Task-State Stack:任务状态栈):与数据段一致RPL 通过选择子的第0位和第1位表现出来的。操作系统过程往往用RPL来避免低特权级应用程序访问高特权级段内的数据。 4.不同特权级代码之间的转移程序从一个代码转移到另一个代码之前,目标代码的选择子将会被加载到cs中。通常使用jmp和call指令来实现转移,转移分为两大类:(1)直接转移 (2)间接转移 特权级转移1.如果目标是非一致代码段,要求CPL必须等于目标代码段的DPL,同时要求RPL小于等于DPL。如果目标是一致代码段,要求CPL大于或者等于目标段的DPL,RPL此时不做检查。这种直接转移是非常有限的,为了实现不同特权级之间的转移,可以使用门描述符或者TSS。2.门是一种描述符,它由一个选择子和一个偏移所指定的线性地址。 门的种类:调用门,中断门,陷阱门,任务门。 作用:实现不同特权级之间的转换。 在原程序的基础上做的一些工作:定义门描述符及其选择子,初始化描述符,使用call指令来实现跳转到已经定义好的通过门描述符能够跳转到的目标段。 门实际上是一个入口地址,只不过增加了若干的属性而已。3.门转移总结: 通过调用门和call指令,可以实现从低特权级到高特权级的转移,无论目标代码是一致的还是非一致的。我们的目标是从低到高,在从高到低,那么下面的任务就是如何从高到低了。4.长的和短的jmp和call的区别:对jmp而言,短跳转对应段内,长跳转对应段间; call要复杂一些,因为call指令会影响堆栈。5. 在使用call指令时,由于某些原因堆栈发生了切换,也就是说call指令执行前后的堆栈已经不是同一个。Intel提供了一种机制,将堆栈A的诸多内容赋值到堆栈B中。事实上,由于每一个任务最多可能在4个特权级间转移,所以,每个任务实际上需要4个堆栈,此时要用到一种数据结构,即TSS(Task-State Stack)。6.综合:使用调用门的过程实际上分为两个部分,一部分是从低特权级到高特权级,通过调用门和call指令来实现;另一部分则是从高特权级到低特权级,通过ret指令来实现。7.用程序实现从ring0到ring3 在这里我们需要在原程序的基础上增加ring3的代码段描述符和堆栈段描述符,并添加响应的选择子,做好工作后,执行一下程序: push SelectorStack3 push TopOfStack3 push SelectorCodeRing3 push 0 retf 为什么会进入ring3呢? 原来程序是执行在ring0的,当使用retf指令后,堆栈将被自动切换到ring3的堆栈段,执行ring3的代码,也就完成了从高特权级项低特权级的转换。8.从高特权级项低特权级的转换和从低特权级项高特权级的转换都需要用到TSS。 3.页式存储1.页的概念 页是一块内存,在80386中,大小固定为4K。在Pentium中,大小可以是2MB或4MB,并且可以访问多余4GB的内存。2.逻辑地址、线性地址、物理地址 在未打开分页机制的时候,逻辑地址经过分段机制直接转换成物理地址。 打开分页机制后,逻辑地址经过分段机制先是转换成线性地址, 线性地址再经过分页机制转换成物理地址。3.分页的目的 实际上使用分段机制已经提供了很好的保护机制, 分页的目的在于实现虚拟存储器。 用代码启动分页机制代码PageDirBaseequ200000h; 页目录开始地址: 2MPageTblBaseequ201000h; 页表开始地址: 2M+4K。LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW;Page DirectoryLABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 1023, DA_DRW|DA_LIMIT_4K;Page Tables。SelectorPageDirequLABEL_DESC_PAGE_DIR- LABEL_GDTSelectorPageTblequLABEL_DESC_PAGE_TBL- LABEL_GDT。; 启动分页机制 -SetupPaging: ; 为简化处理, 所有线性地址对应相等的物理地址.; 首先初始化页目录 movax, SelectorPageDir; 此段首地址为 PageDirBase moves, axmovecx, 1024; 共 1K 个表项xoredi, edixoreax, eaxmoveax, PageTblBase | PG_P | PG_USU | PG_RWW.1:stosdaddeax, 4096; 为了简化, 所有页表在内存中是连续的.loop.1; 再初始化所有页表 (1K 个, 4M 内存空间)movax, SelectorPageTbl; 此段首地址为 PageTblBasemoves, axmovecx, 1024 * 1024; 共 1M 个页表项, 也即有 1M 个页xoredi, edixoreax, eaxmoveax, PG_P | PG_USU | PG_RWW.2:stosdaddeax, 4096; 每一页指向 4K 的空间loop.2moveax, PageDirBasemovcr3, eaxmoveax, cr0oreax, 80000000hmovcr0, eaxjmpshort .3.3:nopret; 分页机制启动完毕 -代码过程说明: 首先定义页目录项(PDE)和页表项(PTE)的描述符和选择子,然后将es:edi指向页目录表的开始。经过1024个循环之后,将所有PDE赋值完,他们属性相同,都是指向可读可写的用户级别页表。然后将es:edi指向页表的开始。经过1024*1024个循环,将所有PTE赋值完,他们属性相同,都是指向可读可写的用户级别页。下面就是让cr3(cr3是一个指向PDE的页目录表,又叫PDBR(Page-Directory Base Register))指向页目录, 然后设置cr0的PG位(cr0的PG位为最高位,为1表示分页机制启动,为0表示未启动),这样,分页机制就完成了。 关于PDE、PTE的一些说明1.P位存在位。P=1表示在物理内存中存在;P=0表示不存在,如果处理器视图访问此页,那么将会产生页异常。2.A位指示页或页表是否被访问3.处理器会将最近常用的页目录和页表项保存在一个叫做TLB(转移后背缓冲区)的缓冲区中,只有当TLB中找不到被请求也的转换信息时,才到内存中去找,这样加快了速度。4.当页目录或页表项被更新时,OS会马上让TLB无效。当cr3被加载时,所有TLB都会自动失效,除非页或页表的G位被设置。 克勤克俭用内存这部分的目的: 在前面的是4KB-4MB-4GB,但是内存也不一定那么大,如果内存本身就很小,几十MB,那么你的页表(4MB)就占了很多,所以有必要知道内存大小。获取内存大小的方法:使用中断15h我们想得到内存的信息,那么采用的方法是将得到的描述信息保存到一段缓冲区中。因为一个ARDS的大小是20字节,那么这里定义一个256字节的缓冲区(实际上也是不确定其大小的)。然后待会儿可以显示出来。得到内存信息代码:_MemChkBuf:times256db0 ;内存缓冲区。; 得到内存数movebx, 0movdi, _MemChkBuf.loop:moveax, 0E820hmovecx, 20movedx, 0534D4150hint15hjcLABEL_MEM_CHK_FAIL ;该语句就是判断是否有进位adddi, 20incdword _dwMCRNumber ;用于记录循环次数,也就是地址范围描述符结构的个数cmpebx, 0jne.loopjmpLABEL_MEM_CHK_OKLABEL_MEM_CHK_FAIL:movdword _dwMCRNumber, 0LABEL_MEM_CHK_OK:代码说明:地址范围描述符(ARDS)的大小就是20个字节,所以di每次增加20,在代码中es:di的赋值似乎是BIOS自动填充的,没有手动填充的痕迹。跳出循环的条件是ebx=0或者CF没有进位(CF=0)1.关于ARDS的type的说明 1) AddressRangeMemory 可以使用 2) AddressRangeReserved 不可以使用2.我们处理得到内存的大小外,还得到了可用内存的分布信息。并且内存的分布可能是不连续的。3.在得到内存大小后,将该值除以4MB,就可以得到PDE的个数,将PDE初始化,也就是将1024替换掉。然后是要初始化PTE,只需要将PDE的个数*1024即可。 进一步体会分页机制在这里我们要实现:先执行某个线性地址处的模块,然后通过改变cr3来转换地址映射关系,再执行同一个线性地址处的模块,由于地址映射已经改变,所以两次得到的应该是不同的输出结果。其实分页机制的好处还有,程序使用的都是线性地址,而不是直接的物理地址。此时OS为应用程序提供了一个不依赖于硬件(物理内存)的平台。在3.3中,是在实模式下得到的内存信息,然后在保护模式下将其显示出来。而在保护模式下,原来的中断向量表由IDT(Interrupt Descriptor Table,中断描述符表)代替,IDT中的描述符可以是以下三种之一:(1)中断门描述符(2)陷阱门描述符(3)任务门描述符IDT的作用是将每一个中断向量和描述符对应起来。在前面使用调用门是通过call指令,这里使用中断门和陷阱门是使用int指令。 3.4中断和异常机制1.中断:中断通常在程序执行时因为硬件而随机发生,他们通常用来处理处理器外部的事件,比如外围设备的请求。软件通过执行int n指令也可以产生中断。(软硬件都可以)2.异常:异常通常在处理器执行指令过程中检测到的错误时发生,比如遇到除零的情况。(多指软件方面)问题:处理器可以对何种类型的通知作出反应。接到某种通知时做出何种反应。 3.Fault(错误)、Trap(陷阱)和Abort(终止)三种类型的异常Fault是一种可以被更正的异常,而且一旦被更正,程序可以不失连续性地继续执行。当一个Fault发生时,处理器会把产生fault指令之前的状态保存起来。异常处理程序的返回地址将会是fault的指令,而不是其后的那条指令。Trap是一种在发生trap指令执行后立即被报告的异常,他也允许程序或任务不失连续性地继续执行。异常处理程序的返回地址将会是产生trap的指令之后的那条指令。Abort是一种不总是报告精确异常发生的位置的异常,它不允许程序或任务继续执行,而是用来报告严重错误的。 写一个硬件中断程序,所做的工作有两点:(1)设置8259A 使用OCW1、OCW2、OCW3的情况, 一是屏蔽或打开外部中断 二是发送EOI给8259A以通知它中断处理结束(2)建立IDT(这部分跟建立GDT或LDT差不多) 时钟中断实验1.外部可屏蔽中断的发生受到两个因素的影响,只有当IF=1,并且IMR(OCW1)位为0时才发生。2.从中断或异常返回时必须使用指令iretd,它与ret相似,只是它同时会改变eflags的值。iretd执行时Error Code不会被自动从堆栈中弹出,所以,执行它之前要先将它从栈中清除。3.中断门和陷阱门的区别:中断门会影响标志IF(由中断门引起的中断会复位IF,因为可以避免其他中断干扰当前中断的处理。随后的iret指令会从堆栈上恢复IF的原值)。陷阱门不会改变IF。 保护模式下的I/O1.对I/O的限制是保护模式的内容,可以通过两个方面的限制来实现:IOPL和I/O许可位图2.IPOL位于eflags的12、13位 指令in,ins,out,outs,cli,sli只有在CPL加载内核如内存跳入保护模式开始执行内核 而引导扇区(Boot Sector)只有512字节,要完成以上全部过程是不行的,大小受到了限制。该怎么办呢?我们可以采用用Boot Sector来引导把Loader加载入内存并且把控制权交给它,让它来实现加载内核等的工作。 FAT121.FAT12是文件系统,磁盘分层: 扇区:磁盘上的最小数据单元 簇: 一个或多个扇区 分区:通常指整个文件系统2.引导扇区位于FAT12的第0个扇区,在这个扇区中有一个很重要的数据结构叫BPB。 引导扇区中记录了FAT12的信息,比如BPB_RootEntCnt表示根目录文件数最大值。 还有就是每扇区字节数,每簇扇区数等。3.规定Loader位于根目录区,根目录区的每一个条目占用32字节,有文件名、属性、大小等信息,那么找Loader就要到根目录区中去一个一个的找。 4.数据区的第一个簇的簇号是2,而不是0或1。5.根目录区的开始扇区号为19(扇区大小可以看BPB_BytsPerSec,书上是512字节), 因为根目录区的大小是不确定的,那么数据区的开始位置也是不确定的。 数据区的开始扇区号=19+根目录区的大小 根目录区的大小=(BPB_RootEntCnt*32+BPB_BytsPerSec-1)/BPB_BytsPerSec 从中可以看出,根目录区是保存文件的属性信息,而数据区是保存文件的内容信息的。6.现在有一个问题,那就是既然通过上述方法都是可以找到文件及其内容的,那又何必要 什么FAT项呢!? (实际上,对于小于512字节的文件来说,上述方法就可以解决了,而对于大于512字节的文件来说,我们需要FAT表来找到所有的簇(扇区)。)7.FAT的结构每12位称为一个FAT项,代表一个簇。第0个和第1个不实用,与数据区对应。 8.FAT值的说明 FAT项的值代表的是文件的下一个簇号,但是如果值大于或等于0xFF8,则表示当前簇已经是本文件的最后一个簇。如果值为0xFF7,表示它是一个坏簇。 加载Loader入内存1.软盘容量的由来 2(共有两面,磁头号0和1)*80(每面有80个磁道,磁道号079)*18(每个磁道有18个扇区)*512(每个扇区的字节)=1.44MB。2.找Loader需要知道的一些循环(1)根目录区的扇区数目(2)由一个扇区的大小和根目录的文件大小确定的文件个数(3)Loader.bin的长度:113.当我们找到Loader.bin之后,需要用扇区号来做两件事:(1)把起始扇区装入内存中(2)通过起始扇区的内容来找到FAT项,从而找到Loader占用的其余所有扇区。4.由于一个FAT项可能跨越两个扇区,所以在代码中一次总是读两个扇区,以免在边界发生错误。5.已知簇号为x,求从引导扇区开始算起是第几个扇区的方法。 x+RootDirSectors+19-2,RootDirSectors代表根目录区的扇区数,在前面已经求出,减2是因为簇号是从2开始的。 注意:这一章有两个运算,一个是求根目录去所占的扇区数(已知根目录区文件最大数), 还有一个就是已知簇号求扇区号。 这一章遗留的问题在这一章,实际上我们做的工作就是找到Loader并且执行它(可以把第三章中保护模式的内容作为Loader,这样它会更高级一些),但是它并不是操作系统内核,我们希望的是通过Loader来加载内核并且将内核放入内存中,而且内核开始执行的时候已经是在保护模式下了,所以,Loader要做的事情至少有两件:(1)加载内核入内存(2)跳入保护模式5.1在linux下用汇编写hello world当有一个hello.asm文件的时候,将其在Linux下运行。当然,我们希望的是运行elf格式的可执行文件,所以需要进行如下处理:lshello.asmnasm -f elf hello.asm -o hello.o(-f elf指定了输出文件的格式为ELF格式)lshello.asm hello.old -s hello.o -o hellols hello.asm hello.o hello5.3ELFELF文件的结构如下 ELF头(ELF header) 程序头表(Program header table) 节(Sections) 节头表(Section header table)实际上,只有ELF头的位置是固定的,其余部分的位置、大小等信息由ELF头中的信息的各项值来决定。Program header描述的是一个段在文件中的位置、大小以及它被放进内存后所在的位置和大小。5.4从loader到内核 用Loader加载ELF1.找内核实际上和找Loader很像,都是寻找文件、定位文件以及读入内存。2.找Loader和内核的比较 文件头位置 文件内容位置Loader 根目录区 数据区Kernel 程序头表 书上没说3.新函数KillMotor:作用是关闭软驱马达,否则软驱的灯会一直亮着。 跳入保护模式这一部分的目的就是将那个简单的内核程序换成能在保护模式下运行的程序,这样程序变得更高级了。1. Loader是由我们自己加载的,段地址已经被确定为BaseOfLoader,也就是说在Loader中出现的标号(变量)的物理地址可以得出。Boot中也需要,它可以确定Loader的物理地址(09000h,0100h)Loader中也需要,它可以确定Kernel的物理地址(刚开始是08000h,0h;后来整理后为03000h,0h) 在以上基础上打开分页机制1. 首先要获取内存信息,然后显示内存信息(内存可用的大小用dwMemSize(保护模式下)来保存),然后是启动分页机制的函数(SetupPaging)显示内存信息并且启动分页机制 push szMemChkTitl
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025版铁塔基站租赁与广告位合作合同范本
- 2025版桥架安装与防雷接地工程承包合同样本
- 2025年建筑材料供货与绿色建筑认证服务合同
- 2025年二手房买卖合同:针对老旧房屋改造的特别约定条款
- 2025年度高效节水农业种植技术服务合同范本
- 2025年跨境矿山资源承包与国际贸易合同
- 2025版文化旅游区建筑承包合同范本
- 2025年数字经济法律咨询项目评标保密与委托合同
- 2025版石料矿山安全生产责任协议
- 2025年度旅游行程变更免责协议及游客须知
- XXX有限公司报销审核制度
- WS/T 427-2013临床营养风险筛查
- GA/T 1047-2013道路交通信息监测记录设备设置规范
- GJB9001C内审员培训讲学课件
- 五牌一图(完整版)
- 幼儿园绘本故事:《十二生肖》 课件
- DDI定向井难度系数
- 激光跟踪仪使用手册
- 新媒体运营知识考核试题与答案
- 金属材料的主要性能ppt课件(完整版)
- 丽声北极星自然拼读绘本第二级 Fat Cat 课件
评论
0/150
提交评论