CSAPP大作业

CSAPP大作业
摘 要
通过对hello程序整个运行过程的分析,回顾本学期所学的内容,加深了印象。分析了从c文件转化为可执行文件过程中的预处理、编译、汇编、链接和可执行文件执行过程中的进程管理、存储空间管理和I/O管理的原理
关键词:预处理:编译:汇编:链接:hello进程管理:hello的存储管理:hello的IO管理;

(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)

目 录

第1章 概述 - 4 -
1.1 HELLO简介 - 4 -
1.2 环境与工具 - 4 -
1.3 中间结果 - 4 -
1.4 本章小结 - 4 -
第2章 预处理 - 5 -
2.1 预处理的概念与作用 - 5 -
2.2在UBUNTU下预处理的命令 - 5 -
2.3 HELLO的预处理结果解析 - 5 -
2.4 本章小结 - 5 -
第3章 编译 - 6 -
3.1 编译的概念与作用 - 6 -
3.2 在UBUNTU下编译的命令 - 6 -
3.3 HELLO的编译结果解析 - 6 -
3.4 本章小结 - 6 -
第4章 汇编 - 7 -
4.1 汇编的概念与作用 - 7 -
4.2 在UBUNTU下汇编的命令 - 7 -
4.3 可重定位目标ELF格式 - 7 -
4.4 HELLO.O的结果解析 - 7 -
4.5 本章小结 - 7 -
第5章 链接 - 8 -
5.1 链接的概念与作用 - 8 -
5.2 在UBUNTU下链接的命令 - 8 -
5.3 可执行目标文件HELLO的格式 - 8 -
5.4 HELLO的虚拟地址空间 - 8 -
5.5 链接的重定位过程分析 - 8 -
5.6 HELLO的执行流程 - 8 -
5.7 HELLO的动态链接分析 - 8 -
5.8 本章小结 - 9 -
第6章 HELLO进程管理 - 10 -
6.1 进程的概念与作用 - 10 -
6.2 简述壳SHELL-BASH的作用与处理流程 - 10 -
6.3 HELLO的FORK进程创建过程 - 10 -
6.4 HELLO的EXECVE过程 - 10 -
6.5 HELLO的进程执行 - 10 -
6.6 HELLO的异常与信号处理 - 10 -
6.7本章小结 - 10 -
第7章 HELLO的存储管理 - 11 -
7.1 HELLO的存储器地址空间 - 11 -
7.2 INTEL逻辑地址到线性地址的变换-段式管理 - 11 -
7.3 HELLO的线性地址到物理地址的变换-页式管理 - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换 - 11 -
7.5 三级CACHE支持下的物理内存访问 - 11 -
7.6 HELLO进程FORK时的内存映射 - 11 -
7.7 HELLO进程EXECVE时的内存映射 - 11 -
7.8 缺页故障与缺页中断处理 - 11 -
7.9动态存储分配管理 - 11 -
7.10本章小结 - 12 -
第8章 HELLO的IO管理 - 13 -
8.1 LINUX的IO设备管理方法 - 13 -
8.2 简述UNIX IO接口及其函数 - 13 -
8.3 PRINTF的实现分析 - 13 -
8.4 GETCHAR的实现分析 - 13 -
8.5本章小结 - 13 -
结论 - 14 -
附件 - 15 -
参考文献 - 16 -

第1章 概述
1.1 Hello简介
hello.c经过预处理,编译,汇编,链接之后,作为目标程序执行,在shell中启动之后,shell会调用fork创建子进程,这是P2P过程
然后shell为execve映射虚拟内存,程序开始后加载物理内存然后进入main函数执行代码,结束之后shell父进程回收hello进程,释放其占用的内存,删除数据结构,这是020过程
1.2 环境与工具
X64 CPU;2GHz;2G RAM;256GHD Disk 以上
Windows7 64位以上;VirtualBox/Vmware 11以上;Ubuntu 16.04 LTS 64位/优麒麟 64位
Dev-C++5.11
1.3 中间结果
hello.i经过预处理得到
hello.s编译后得到的汇编代码
hello.o汇编后得到的可重定位目标执行文件
hello.out链接后得到的二进制文件
hello可执行程序
1.4 本章小结
简单介绍了hello程序,列出了环境与工具还有中间的结果

(第1章0.5分)

第2章 预处理
2.1 预处理的概念与作用
概念:
一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。
作用:
宏定义;文件包含;条件编译
2.2在Ubuntu下预处理的命令
gcc -E -o hello.i hello.c
在这里插入图片描述
2.3 Hello的预处理结果解析
#include <stdio.h> #include <stdlib.h> #include <unistd.h>三个头文件消失,替代的是一大段代码,描述的是运行库在计算机中的位置,方便下一步翻译成汇编语言
2.4 本章小结
理解了预处理的概念和作用,对hello.c进行了预处理,并分析得到的.i文件
(第2章0.5分)

第3章 编译
3.1 编译的概念与作用
概念:
把代码转化为汇编指令的过程
作用:
把用高级程序设计语言书写的源程序,翻译成等价的计算机汇编或机器语言书写的目标程序 编译程序以高级程序设计语言书写的源程序作为输入,而以汇编或机器语言表示的目标程序作为输出。
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序

3.2 在Ubuntu下编译的命令
gcc -S hello.i -o hello.s
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 Hello的编译结果解析
1.字符串
“Usage: Hello \345\255\246\345\217\267 \345\247\223\345\220\215\357\274\201”
对应Usage: Hello 学号 姓名!\n
中文被编码为UTF-8格式
“Hello %s %s\n”
对应第二个printf中的格式化参数
2.变量和赋值
全局变量sleepsecs
先初始化为2,然后存放在.rodata节(只读数据节)
int i
cmpl $9, -4(%rbp),从这里可以看出存到了-4(%rbp)中
movl $0, -4(%rbp),在这里赋值为0
int argc
作为第一个参数被压入栈中,pushq %rbp
3.数组
movq -32(%rbp), %rax
addq $16, %rax
movq (%rax), %rdx
movq -32(%rbp), %rax
addq $8, %rax
movq (%rax), %rax
argv作为一个指针数组,每个地址是8位
4.算数操作
addl $1, -4(%rbp) i++
leaq .LC1(%rip), %rdi,计算格式串地址
5.关系操作
cmpl $9, -4(%rbp) i<10被优化为i<=9
cmpl $3, -20(%rbp) argc!=3
6.控制转移
if(argc!=3)
cmpl $3, -20(%rbp)
je .L2
先比较,如果为0,则直接跳转到L2否则执行接下来的代码(if里面的内容)
for(i=0;i<10;i++)
addl $1, -4(%rbp)
cmpl $9, -4(%rbp)
jle .L4
先把i++,然后与9比较,如果小于等于9,则跳转到L4继续执行循环里的内容,否则则退出循环
7.函数操作
main
call指令将下一条指令的地址压入栈中,然后跳转到main函数,main将%eax设置为0返回
exit
call exit@PLT
printf
第一次call puts@PLT
第二次 call printf@PLT
sleep
call sleep@PLT
getchar
call getchar@PLT
3.4 本章小结
理解编译的概念和作用,对hello.进行编译,然后分析了编译得到的文件,理解机器指令如何实现C语言代码中的操作
(第3章2分)

第4章 汇编
4.1 汇编的概念与作用
概念:
通过汇编器把汇编语言翻译成机器语言
作用:
通过汇编过程把汇编代码转换成机器代码
4.2 在Ubuntu下汇编的命令
gcc hello.s -s hello.o
在这里插入图片描述
4.3 可重定位目标elf格式
文件头
在这里插入图片描述
描述了生成该文件的系统的字的大小和字节顺序,还有帮助链接器语法分析和解释目标文件的信息
节头部表
在这里插入图片描述
包括节名称,类型,读写权限,长度
符号表
在这里插入图片描述
包含main定义和引用的符号的信息
4.4 Hello.o的结果解析
在这里插入图片描述
与hello.s的比较:
左边多了机器码,用于机器识别
分支跳转call有了具体的地址
在.s文件中,call调用函数会直接加上函数名,反汇编中,call后是下一条指令的地址,许多函数要从链接的库中调用,call将他们的相对地址全都设置为0,在.rela.text中设置重定位条目
.s中访问rodata使用段名称%rip,在反汇编代码中是0+%rip,因为rodata中数据地址也是在运行时确定,访问也需要重定位
4.5 本章小结
了解汇编的概念和作用,汇编得到.o文件,然后分析了可重定位目标elf格式,然后使用objdump进行反汇编并与.s文件进行比较,理解机器语言与汇编语言的关系
(第4章1分)

第5章 链接
5.1 链接的概念与作用
概念:
链接是将各种代码和数据片段收集并组合成一个单一文件的过程
作用:
解析未定义的符号引用,将目标文件的占位符替换为符号的地址
5.2 在Ubuntu下链接的命令
在这里插入图片描述
5.3 可执行目标文件hello的格式
在这里插入图片描述

节头部表:
在这里插入图片描述
符号表信息:
在这里插入图片描述
5.4 hello的虚拟地址空间
在这里插入图片描述
代码段偏移为0,被映射到虚拟地址0x0x0000000000400040,位于hello开头的代码被加载到虚拟地址为0x400000的地方,同理PFDR段位于偏移0x40处,被加载到了0x400040处
5.5 链接的重定位过程分析
在这里插入图片描述
各指令对应的地址为重定位之后的虚拟内存的地址,从0x400000开始,而hello.o是从0000000开始
此外在开头hello还多了.init,.fini,.plt,.plt.got节,分别是程序初始化执行的代码,程序终止时需要执行的代码,动态链接中的过程连接表,动态链接中的全局偏移表
5.6 hello的执行流程
在这里插入图片描述
函数 地址
ld-2.27.so!_dl_start 0x7f96ed2e1ea0
ld-2.27.so!_dl_init 0x7f96ed2f0630
hello!_start 0x400500
libc-2.27.so!__libc_start_main 0x7fbdf0cccab0
hello!puts@plt 0x400410
hello!exit@plt 0x400440
hello!printf@plt 0x4004c0
hello!sleep@plt 0x4004e0
hello!getchar@plt 0x4004f0
libc-2.27.so!exit 0x7236b7b9e120
5.7 Hello的动态链接分析
在这里插入图片描述
找到GOT地址0x601000
在这里插入图片描述
在这里插入图片描述
一开始这个地址开始的字节都为0,调用_dl_init函数之后GOT内容产生变化,存储的地址是动态连接器在Id-linux.so的入口点
5.8 本章小结
理解链接的作用,对hello.o进行链接得到hello文件,并对hello进行分析,与hello.o文件进行比较
(第5章1分)

第6章 hello进程管理
6.1 进程的概念与作用
概念:
进程是一个执行中的程序的实例
作用
通过进程,我们会得到一种假象,好像我们的程序是当前唯一运行的程序,我们的程序独占处理器和内存,我们程序的代码和数据好像是系统内存中唯一的对象。
6.2 简述壳Shell-bash的作用与处理流程
Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核。不仅如此,Shell有自己的编程语言用于对命令的编辑,它允许用户编写由shell命令组成的程序。Shell编程语言具有普通编程语言的很多特点,比如它也有循环结构和分支控制结构等
Shell处理流程:
1.打印提示信息
2.等待用户输入
3.接受命令
4.解释命令
5.找到该命令,执行命令,如果命令含有参数,输入的命令解释它
6.执行完成,返回第一步
6.3 Hello的fork进程创建过程
shell先判断出不是内置命令,于是加载可执行文件hello,通过fork创建一个子进程,子进程得到与父进程用户级虚拟地址空间相同的一份副本。子进程还获得与父进程任何打开文件描述符相同的副本。子进程与父进程有不同的pid。fork被调用一次,返回两次。在父进程中fork返回子进程的pid,在子进程中fork返回0.父进程与子进程是并发运行的独立进程。
6.4 Hello的execve过程
在shell创建的子进程中将会调用execve函数,来调用加载器,加载器将可执行目标文件中的代码和数据从磁盘复制到内存中,然后通过跳转到程序的第一条指令或入口点来运行该程序。这个将程序复制到内存并运行的过程叫做加载。。在程序头部表的引导下,加载器将可执行文件的片复制到代码段和数据段。接下来,加载器跳转到程序的入口点,也就是_start函数的地址。这个函数是在系统目标文件ctrl.o中定义的,对所有C程序都是一样的。start函数调用系统启动函数 _libc_start_main,该函数定义在libc.so中,它初始化执行环境,调用用户层的main函数,处理main函数的返回值,并且在需要的时候把控制返回给内核。
6.5 Hello的进程执行
系统中的每个程序都运行在某个进程的上下文中。上下文由程序正确运行所需的状态组成,这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。
内核调度hello的进程开始进行,输出Hello与之前输入的内容,然后执行sleep函数,这个函数是系统调用,它显示地请求让调用进程休眠。内核转而执行其他进程,这时就会发生一个上下文转换。2s后,又会发生一次进程转换,恢复hello进程的上下文,继续执行hello进程。重复9次这个过程。
循环结束后,后面执行到getchar函数,这时读取数据一般需要很长的时间,所以将会发生一个上下文切换转而执行其他进程,当数据已经被读取到缓存区中,将会发生一个中断,使内核发生上下文切换,重新执行hello进程。
6.6 hello的异常与信号处理
回车
在这里插入图片描述
进程被回收
Ctrl-Z
在这里插入图片描述在这里插入图片描述

会发送一个信号,停止前台作业
Ctrl-C
在这里插入图片描述
会向进程传递中断信号,使程序被终止
乱按
在这里插入图片描述
乱按的内容会被保存在缓冲区,按下回车会在程序停止后执行这些内容
6.7本章小结
了解了进程的概念和作用以及shell的作用,分析了hello的执行过程,以及fork和execve的作用
(第6章1分)

第7章 hello的存储管理
7.1 hello的存储器地址空间
物理地址:
CPU通过地址总线的寻址,找到真实的物理内存对应地址。 CPU对内存的访问是通过连接着CPU和北桥芯片的前端总线来完成的。在前端总线上传输的内存地址都是物理内存地址。
逻辑地址:
程序代码经过编译后出现在 汇编程序中地址。逻辑地址由选择符(在实模式下是描述符,在保护模式下是用来选择描述符的选择符)和偏移量(偏移部分)组成。
线性地址:
逻辑地址经过段机制后转化为线性地址,为描述符:偏移量的组合形式。分页机制中线性地址作为输入。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段式内存管理方式就是直接将逻辑地址转换成物理地址,也就是CPU不支持分页机制。其地址的基本组成方式是段号+段内偏移地址。
在x86保护模式下,段的信息即段描述符占8个字节,段信息无法直接存放在段寄存器中。Intel的设计是段描述符集中存放在GDT或LDT中,而段寄存器存放的是段描述符在GDT或LDT内的索引值。
首先给定一个完整的逻辑地址
1.看段选择描述符中的T1字段是0还是1,可以知道当前要转换的是GDT中的段,还是LDT中的段,再根据指定的相应的寄存器,得到其地址和大小,我们就有了一个数组了。
2.拿出段选择符中的前13位,可以在这个数组中查找到对应的段描述符,这样就有了Base,即基地址就知道了。
3.把基地址Base+Offset,就是要转换的下一个阶段的地址。
7.3 Hello的线性地址到物理地址的变换-页式管理
首先先将线性地址分为VPN+VPO的形式,然后再将VPN拆分成TLBT+TLBI,然后去TLB缓存里找所对应的PPN,如果发生缺页情况则直接查找对应的PPN,找到PPN之后,将其与VPO组合变为PPN+VPO就是生成的物理地址了。
7.4 TLB与四级页表支持下的VA到PA的变换
36位的虚拟地址被分割成4个9位的片。CR3寄存器包含L1页表的物理地址。VPN1有一个到L1 PTE的偏移量,找到这个PTE以后又会包含到L2页表的基础地址;VPN2包含一个到L2PTE的偏移量,找到这个PTE以后又会包含到L3页表的基础地址;VPN3包含一个到L3PTE的偏移量,找到这个PTE以后又会包含到L4页表的基础地址;VPN4包含一个到L4PTE的偏移量,找到这个PTE以后就是相应的PPN
7.5 三级Cache支持下的物理内存访问
先将虚拟地址转换为物理地址,再对物理地址进行分析,物理地址一般由CT、CI、CO组成,用CI位进行索引,如果匹配成功且valid值为1,则称为命中,根据偏移量在L1cache中取数,如果不命中,在分别到L2、L3和主存中重复上述过程
7.6 hello进程fork时的内存映射
在用fork创建虚拟内存的时候,要经历以下步骤:
1.创建当前进程的mm_struct,vm_area_struct和页表的原样副本
2.两个进程的每个页面都被标记为只读页面
3两个进程的每个vm_area_struct都被标记为私有
7.7 hello进程execve时的内存映射
exceve函数加载和执行程序Hello,需要以下几个步骤:
1.删除已存在的用户区域。
2.映射私有区域。为Hello的代码、数据、bss和栈区域创建新的区域结构,所有这些区域都是私有的、写时复制的。
3.映射共享区域。比如Hello程序与标准C库libc.so链接,这些对象都是动态链接到Hello的,然后再用户虚拟地址空间中的共享区域内。
4.设置程序计数器(PC)。exceve做的最后一件事就是设置当前进程的上下文中
7.8 缺页故障与缺页中断处理
在虚拟内存的习惯说法中,DRAM缓存不命中称为缺页。例如:CPU引用了VP3中的一个字,VP3并未缓存在DRAM中。地址翻译硬件从内存中读取PTE3,从有效位推断出VP3未被缓存,并且触发一个缺页异常。缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,在此例中就是存放在PP3中的VP4。如果VP4已经被修改了,那么内核就会将它复制回磁盘。无论哪种情况,内核都会修改VP4的页表条目,反映出VP4不再缓存在主存中这一事实。
接下来,内核从磁盘复制VP3到内存中的PP3,更新PTE3,随后返回。当异常处理程序返回时,它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重发送到地址翻译硬件。但是现在VP3已经缓存在主存中了,那么也命中也能由地址翻译硬件正常处理了。
7.9动态存储分配管理
基本方法:维护一个虚拟内存区域“堆”,将堆视为一组不同大小的块的集合来维护,每个块要么是已分配的,要么是空闲的,需要时选择一个合适的内存块进行分配。
1 记录空闲块,可以选择隐式空闲链表,显示空闲链表,分离的空闲链表和按块大小排序建立平衡树
2 放置策略,可以选择首次适配,下一次适配,最佳适配
3 合并策略,可以选择立即合并,延迟合并
4 需要考虑分割空闲块的时机,对内部碎片的忍耐阈值.
7.10本章小结
回顾了存储的一些概念,进程fork和execve内存映射的内容,还有缺页问题和动态存储分配管理的问题

(第7章 2分)

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
设备的模型化:文件
文件的类型:
普通文件:包含任意数据的文件。
目录:包含一组链接的文件,每个链接都将一个文件名映射到一个文件(他还有另一个名字叫做“文件夹”)。
套接字:用来与另一个进程进行跨网络通信的文件
命名通道
符号链接
字符和块设备
设备管理:unix io接口
打开和关闭文件
读取和写入文件
改变当前文件的位置
8.2 简述Unix IO接口及其函数
1.Unix IO接口:
打开文件:一个应用程序通过要求内核打开相应的文件,宣告它想要访问一个I/O设备。内核返回一个小的非负整数,叫做描述符。它在后续对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。应用程序只需记住这个描述符。
shell创建的每个进程开始时都有三个打开的文件:标准输入、标准输出和标准错误。
改变当前的文件位置:对于每个打开的文件,内核保持着一个文件的位置k,初始为0,这个文件的位置是从文件开头起始的字符偏移量。应用程序能够通过执行seek操作,将文件的当前位置设置为k。
读写文件:读操作就是从文件中复制n>0个字节到内存中,从当前文件位置k开始,然后将k增加到k+n。而写操作就是从内存复制字节到文件中。
关闭文件:当应用完成了对文件的访问后,它就通知内核关闭这个文件。作为响应,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中。无论一个进程因为何种原因终止时,内核都会关闭所有打开的文件并释放它们的内存资源。
2.Unix IO函数:
open 函数:调要open函数可以打开或创建一个文件。
create函数:创建一个文件,也可通过以特定参数使用open来实现。
close函数:读文件进行关闭。
Iseek函数:为一个打开的文件设置其偏移量。
read函数:从打开的文件中读数据到buf。
write函数:写入文件。
pread,prwrite函数:主要用于解决文件共享问题。
dup函数
syns函数:用于解决延迟写问题,保证磁盘上实际文件系统和缓冲区高速缓存中内容的一致性。
8.3 printf的实现分析
int printf(const char fmt,…){
int i;
char buf[256];
va_list arg = (va_list)((char
)(&fmt) + 4);
i = vsprintf(buf,fmt,arg);
write(buf,i);
return i;
}
调用了两个函数vsprintf,write
vsprintf函数的作用是将所有的参数内容格式化之后存入buf,然后返回格式化数组的长度。
write函数是将buf中的i个元素写到终端的函数。
从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall.
字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。
显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。
8.4 getchar的实现分析
int getchar(void){
static char buf[BUFSIZ];
static char *bb = buf;
static int n = 0;
if(n == 0)
{
n = read(0, buf, BUFSIZ);
bb = buf;
}
return(–n >= 0)?(unsigned char) *bb++ : EOF;
}
getchar函数调用read函数,将整个缓冲区都读到buf里,并将缓冲区的长度赋值给n。返回时返回buf的第一个元素,除非n<0。
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结
了解了hello的IO管理,以及读写的两个函数的实现
(第8章1分)
结论
用计算机系统的语言,逐条总结hello所经历的过程。
你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。

先用C语言编写hello.c
预处理hello.c,将其与所有外部的库展开合并到hello.i中
编译hello.i成为汇编文件hello.s
汇编hello.s,将其变为可重定位目标文件hello.o
将hello.o与可重定位目标文件和动态链接库链接成为可执行程序hello
运行hello
shell调用fork创建子进程
shell调用execve,映射虚拟内存
进入程序,载入物理内存,进入main函数
CPU分配时间片,hello在一个时间片中顺序执行
正常结束或者ctr-c ctr-z终止或挂起进程
shell父进程回收子进程,hello的一生结束
(结论0分,缺失 -1分,根据内容酌情加分)

附件
列出所有的中间产物的文件名,并予以说明起作用。
hello.c 用C语言编写的hello程序源代码
hello.i 经预处理处理得到的文本文件
hello.s 编译后得到的汇编代码
hello.o 汇编操作得到的可重定位目标执行文件
hello.out 链接后得到的二进制文件
hello 可执行程序
(附件0分,缺失 -1分)

参考文献
为完成本次大作业你翻阅的书籍与网站等
[1] 林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.
[2] 辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.
[3] 赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[4] 谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.
[5] KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.
[6] CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.
[7] Computer Systems:A Programmer's Perspective, Bryant,R.E., 2016
(参考文献0分,缺失 -1分)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值