欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

计算机系统--hello的自白大作业

程序员文章站 2024-03-07 13:22:27
...

摘 要
本文将通过对hello,一个简单的输出程序,来探寻它曲折的生成与执行的历程。在这样的过程中,将会进一步感受到计算机设计者一个个精妙绝伦的设计,感慨于这般巧妙的思路。同时,在hello的生命历程中,将是对计算机系统这门课程的一个知识总结,将一个个概念付诸于实践,借以加深印象融会贯通。

关键词:预处理;编译;汇编;链接;进程管理;存储管理;I/O管理;

(摘要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文件是通过vim、Code:Blocks等IDE创建的C语言文本文件。在Linux中通过gcc编译器,经过预处理,编译,汇编,链接最后生成可执行文件hello。通过shell执行hello,调用fork()产生子进程,调用execve执行。由此完成了P2P,即Program到Process的转变。
调用execve()后,程序代码从磁盘空间载入主存,分配用户虚拟内存。指令逐条进入CPU处理,根据汇编语言指令执行取指、译码、执行、更新的操作。借助cache,tlb缓存数据,通过I\O系统根据代码指令进行输出,当程序运行结束时回收进程,释放内存,删除与执行程序相关的数据结构。由此完成了O2O过程,即从空间占用为0,到执行后依然不占用任何空间。
1.2 环境与工具
硬件环境:X64 CPU Intel Core i5 8300HQ; 2.3GHz;
软件环境:Windows10,Ubuntu 18.04.1LTS
开发与调试工具: vim、gcc、edb、readelf
1.3 中间结果
hello.c 源程序
hello.i 预处理后修改的源程序
hello.s 编译后的汇编程序
hello.o 汇编后的可重定位目标程序
hello.o.txt 可重定位目标程序反汇编
hello 链接后的可执行目标程序
hello.txt 可执行目标程序反汇编
1.4 本章小结
介绍的hello的P2P以及O2O过程,说明硬件设备以及软件环境,并列出了此次大作业产生的中间文件。
(第1章0.5分)

第2章 预处理
2.1 预处理的概念与作用
预处理是源程序由预处理器完成的第一步操作,处理以#号开头的代码行和一些特殊符号,完成文本替换 宏命令展开,生成.i文件即被修改了的源程序。
具体作用在于,把#include的头文件插入到相应位置,展开所有的宏并删除#define,处理条件预编译指令,删除注释,添加行号和文件名标识。
2.2在Ubuntu下预处理的命令
gcc -E hello.c -o hello.i

图2.2.1 预处理
2.3 Hello的预处理结果解析

图2.3.1 预处理结果

图2.3.2 stdio.h复制

图2.3.2 stdlib.h复制

上图可见, <stdio.h> <unistd.h> <stdlib.h>三个库文件,先列出各包含文件的路径,然后把内容复制到程序中。

.c程序依然是文本形式,而且头文件已经删去了。
2.4 本章小结
预处理是C语言编译系统的一部分,针对以#开头的编译预处理命令。包含#include #if #define等等的预处理指令,还有双下划线的特殊符号的替换。
总的来说,程序预编译过程中完成了三件事,一是宏替换,二是文件包含,三是条件编译。是C语言区别于其它高级程序设计语言的特征,带来了很大的便利。

(第2章0.5分)

第3章 编译
3.1 编译的概念与作用
编译是由编译器完成的第二步操作,主要做词法分析、语法分析、语义分析等,在检查无错误后后,把代码翻译成汇编语言,生成.i文件即被汇编程序。
具体作用,在于通过词法分析提取出基本字,标识符、常数、运算符和界符,然后通过语法分析分划出同类型作为整体,再通过词义分析产生四元式的中间代码,经过优化后,开始存储空间分配,寄存器的调度等复杂工作生成最终的目标代码。

3.2 在Ubuntu下编译的命令
gcc -S hello.i –o hello.s

图3.2.1 编译
3.3 Hello的编译结果解析

图3.3.1 编译结果

3.3.1 数据
常量以立即数形式给出,以$开头接64为二进制表示。
变量由寄存器表示,以%接寄存器名调用,若变量个数超过寄存器个数,则存入栈中由栈顶指针偏移表示。
表达式都被解析成四元式形式,运算符,源操作数,目的操作数的形式。
类型由命令末尾字符来表示长度,b字节为char,w字为short,l双字为int,q四字为long或char*,s单精度为float。

3.3.2 类型转换
显示类型转换,用(类型)变量将变量从原有类型转换为指定类型,通常会发生溢出舍去高位或者舍入。
隐式类型转换,如一个运算数是有符号的而另一个是无符号的,会将有符号的转换为无符号的。

3.3.3 sizeof
利用指针的步进值,返回数据类型长度。

3.3.4 算数操作
ADD加法,SUB减法,IMUL乘法,idivq除法,INC加一,DEC减一。

3.3.5 关系操作
基于cmp测试s2-s1和test测试s1&s2改变条件码,通过条件码的逻辑组合来完成不同关系的表示。
CF无符号溢出,ZF零,SF负数,OF有符号溢出。则
== cmp测试下的ZF
! = cmp测试下的~ZF

(SF^OF)&ZF
< SF^OF

3.3.6 指针数组结构体操作
*可以间接引用指针,访问后接地址中的值。
&可以获取后接变量指针。
访问数组结构体,都需要给出变量首地址和偏移量进行访问。

3.3.7 控制转移
if else/do while/for,通过cmp或test改变条件码表示条件语句,然后使用jmp跳转指令中的条件跳转来表示不同情况下代码执行情况。数据条件传送使用相应的cmove语句。
switch,当开关情况较少时同上一种方法,情况较多且值跨度较小会使用跳转表,标定不同值对应代码首地址。

3.3.8 函数操作
调用函数使用call命令,接函数相对或绝对地址。执行时先将当前PC值入栈,更新PC为所调用函数首地址开始执行。
返回函数使用ret命令,将先前的PC值出栈更新PC,回到原函数中。
参数传递,传入参数可以通过寄存器,以及栈中原PC值之上的地方。返回值通过%rax寄存器传递。

3.4 本章小结
编译将代码变换成机器级的低级语言代码,这阶段工作非常复杂,设计到硬件系统功能部件的运用,机器指令的选择,各种数据类型变量的存储空间分配,以及寄存器的调度。
汇编语言,面向机器有着高速度和高效率的特点。即使在高级语言众多的今天,学会读写汇编语言仍然是有必要的。在嵌入式和底层开发中编写硬件友好程序的优化还是非常重要的。
(第3章2分)

第4章 汇编
4.1 汇编的概念与作用
汇编是由汇编器完成的第三步操作,主要将.s汇编程序翻译成二进制机器语言,生成.o文件即被可重定位的目标程序。将数据代码放入不同的段。

4.2 在Ubuntu下汇编的命令
gcc –c hello.s –o hello.o

图4.2.1 汇编

4.3 可重定位目标elf格式

图4.3.1 elf头部标出了程序信息

图4.3.2各节起始地址,类型大小信息

图4.3.3重定位节

各引用重定位信息,offset是在原代码中的偏移量,type是地址类型,PC是相对地址没有PC的是绝对地址,name表示符号,addend是偏移量用于计算重定位地址。

图4.3.4符号表

4.4 Hello.o的结果解析
objdump -d -r hello.o 得

图4.4.1 hello.o反汇编

机器语言的构成,二进制的机器指令序列集合,机器指令由操作码和功能码,以及操作数A,B,及立即数组成。
与汇编语言的映射关系,汇编语言指令是机器指令的一种符号表示,而不同类型的CPU 有不同的机器指令系统,汇编语言也就不同。

图4.4.2 汇编与机器码比较

汇编代码用标号来跳转,机器代码用相对或绝对地址来完成跳转。在可重定位目标程序中,用00 00 00 00充当占位符,下有一个可重定位条目表示指向符号,地址类型偏移量。

4.5 本章小结
汇编阶段将汇编代码处理成可重定位目标程序,将数据和代码组织成节的形式,提供elf头便于后续与其他文件链接时使用。同时将文本程序转换成二进制的机器指令格式,更加接近最终的可执行程序,这一阶段是非常重要的。
(第4章1分)

第5章 链接
5.1 链接的概念与作用
链接是由链接器完成的第四步操作,主要将.o可重定位目标文件翻译成可执行文件,生成.out文件即可执行程序。
具体作用,在于将函数全局变量及静态变量的引用与其定义链接起来,将不同文件的相同段归类整合成一个整体。再将每个符号定义与相应的内存位置链接起来。
5.2 在Ubuntu下链接的命令
ld -o hello -dynamic-linker
/lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o

图5.2.1 链接命令
5.3 可执行目标文件hello的格式

图5.3.1 elf头信息

图5.3.2 各段起始地址

5.4 hello的虚拟地址空间

图5.4.1 从000开始是elf节头表内容

图5.4.2 从660开始.text代码段内容

图5.4.3从870开始.rodata数据段内容

5.5 链接的重定位过程分析

图5.4.1 hello与hello.o比对

链接将可重定位条目计算出内存地址,对于.text节中函数调用和条件跳转中所需要的地址根据重定位条目进行计算,替换了原有的00 00 00 00占位符。而且hello中比hello.o多了一些节。

5.6 hello的执行流程
ld-2.27.so! dl_start 0x7ffee5aca680
ld-2.27.so! dl_init 0x7f9f48629630
hello!_start 0x0x400500
ld-2.27.so!_libc_start_main 0x7f9f48249ab0
libc-2.27.so! cxa_atexit 0x7f4523fd6af7
libc-2.27.so! lll_look_wait_private 0x7f4523ff8471
libc-2.27.so!_new_exitfn 0x7f87ff534220
hello!_libc_csu_init 0x7f87ff512b26
libc-2.27.so!_setjmp 0x7f87ff512b4a
libc-2.27.so!_sigsetjmp 0x7f87ff52fc12
libc-2.27.so!__sigjmp_save 0x7f87ff52fbc3
hello_main 0x400532
[email protected] 0x4004b0
[email protected] 0x4004e0
[email protected] 0x400587
[email protected] 0x400594
[email protected] 0x4005a3
dl_runtime_resolve 0x7f169ad84750
libc-2.27.so!exit 0x7fce8c889128

5.7 Hello的动态链接分析
对于共享库函数,采用延迟绑定技术将过程地址的绑定推迟到第一次调用该过程。找到got和plt段

图5.7.1 elf头中plt的adress

每个可被执行的库函数都有自己的PLT条目

图5.7.2 plt段

例如puts函数

图5.7.3 main中调用

转到

图5.7.4 plt调用

*0x200b62(%rip)就是puts的地址。
所有函数都通过 _dl_runtime_resolve动态库函数在第一次调用时重定位的。

5.8 本章小结
链接可以将多组代码和数据片段组合成为一个整体文件的过程,使这样组合的文件可被加载到内存并执行。这样的方法极大程度上方便了程序的书写调用,尤其在大型程序中,不用反复重复一段代码,同时极大程度节省了内存开销,从静态库再到动态库到共享库的不断发展,链接的重要性不言而喻。
(第5章1分)

第6章 hello进程管理
6.1 进程的概念与作用
概念,一个执行中程序的实例。
作用,一是有一个独立的逻辑控制流。二是提供一个私有的地址空间。使得表面上看当前运行的程序独占处理器和内存空间。

6.2 简述壳Shell-bash的作用与处理流程
作用,充当命令解释器的作用,将用户输入的命令翻译给系统执行。提供一些内建命令供用户使用
流程,获取终端输入,先判断是否为内建命令。如果为外部命令,先创建一个子程序,然后在子程序中执行次命令。还会处理命令参数以及选择在shell的前台或者后台执行。

6.3 Hello的fork进程创建过程
使用fork函数,子进程得到与父进程用户级虚拟地址空间的副本,包括代码段数据段,堆栈共享库,以及打开文件的副本。然后在父进程中返回子进程的pid,在子进程中返回0,如果出现错误返回-1给父进程。

6.4 Hello的execve过程
首先将可执行文件从磁盘加载到主存中去,execve会指定传给新进程的命令行参数argv,以及参数envp指定新程序的环境列表,根据参数初始化用户栈。调用_start启动代码,并将控制传递给新程序的main函数。

6.5 Hello的进程执行
上下文,包括寄存器用户栈,内核数据结构等。当正在执行的程序发生阻塞时或者内核判断当前程序以及执行过长时间时,会调用内核中的调度器,首先保存当前进程的上下文,恢复将要转移到的进程上下文,最后将控制传给新的进程。
进程时间片,在上下文切换的机制中,每一个进程在切换前的执行时间称为此进程的时间片。
用户模式与内核模式转换,当程序发生异常时。会从程序执行的用户模式会通过上下文切换,进入内核模式调用异常处理程序做出对应的修复或者终止操作。
体现在hello中,当运行到sleep时,当前进程挂起2.5秒,进入到内核模式,在被抢占了2.5秒后,再恢复它被抢占时的上下文,并把控制交给它,并回到了用户模式。
6.6 hello的异常与信号处理
SIGTSTP 来自终端的停止信号 即Ctrl-Z 默认使正在运行的程序挂起直到下一个SIGCONT信号。。

图6.6.1 来自终端的停止信号

SIGINT 来自键盘的中断 即Ctrl-C 默认使正在运行的程序终止

图6.6.2 来自键盘的中断信号

任意键入ps jobs pstree fg kill 等命令

图6.6.3 内建函数

会将输入缓存到输入流中,等hello结束后当成指令执行。
还会遇到缺页异常,缓存不命中异常等等。

6.7本章小结
异常控制流与进程的学习,是建立在应用是如何与操作系统交互。应用程序通过有意的使用陷阱异常调用内核权限的函数,还可以通过故障异常修复缺页以及缓存不命中的情况,通过终止来保护硬件不会被程序破坏,异常控制的重要性可见一斑,编写异常处理包装函数也会使程序更加健壮。在信号的学习中,进一步的了解到应用程序如何与父进程,其他进程,操作系统的交互方法。同时了解到并行程序的基本操作。
(第6章1分)

第7章 hello的存储管理
7.1 hello的存储器地址空间
逻辑地址应用程序角度看到的内存单元和存储单元
线性地址,逻辑地址到物理地址变换之间的中间层
虚拟地址,程序访问存储器所使用的逻辑地址
物理地址,数据在内存中实际的存贮地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理
段寄存器用于存放段选择符,通过段选择符可以得到对应段的首地址。段选择符分为索引、TI和RPL特权级三个部分。先通过段描述符得到段基址,然后与偏移量结合得到线性地址,从而得到了虚拟地址
7.3 Hello的线性地址到物理地址的变换-页式管理
首先处理器生成一个虚拟地址,并把它传送给MMU。MMU生成PTE地址,并从主存中页表得到PTE条目。如果命中,则MMU构造出物理地址,再到主存cache中查找数据,返回给CPU。如果缺失,则触发异常,传递控制到操作系统内核中的缺页异常处理程序。确定页表中牺牲页替换继续操作。
7.4 TLB与四级页表支持下的VA到PA的变换
将VA中的VPN分为等长的4部分,一个部分对应一级页表的匹配,从一级页表开始一直匹配到四级页表,取出PPN与VPO结合,得到物理地址。
7.5 三级Cache支持下的物理内存访问
处理器生成一个物理地址,划分为组索引,标记,偏移位。首先在一级cache中匹配,找到相应组搜索标记和有效位如果命中则返回。如果确实则访问二级cache进行同样的匹配操作。直到三级cache,如果缺失则到主存中直接查找地址,将数据写入到三级cache,如果cache已满则根据LRU或LFU替换算法选择驱逐项替代。然后逐次写回到一级cache,最终返回处理器。
7.6 hello进程fork时的内存映射
fork会创建与父进程一样的用户地址空间和页表的副本,并将每个页面都设置为写时复制。先与父进程共享同一个只读的物理副本,当子程序试图修改一个页面时,会在物理内存中创建页面的一个副本,并跟新页表条目指向新的页地址,并更改权限为可写。
7.7 hello进程execve时的内存映射
exceve会先删除已存在的用户区域,映射私有区域,为新程序的代码数据,栈区域创建新的用户地址空间。然后映射共享区域,最后将程序计数器设置为新程序的_start启动代码地址。
7.8 缺页故障与缺页中断处理
缺页故障,当所引用的数据并未缓存在页表中。MMU会从内存中读取虚拟页对应的页表,触发一个缺页异常。缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页。如果被牺牲的页面被修改了,那么内核会把它复制回磁盘。然后将引用的数据读入主存更新页表。
缺页中断,首先保护cpu现场,分析中断原因,转入缺页中断处理函数,恢复cpu现场,继续执行
7.9动态存储分配管理
动态内存分配器维护着一个进程的虚拟内存区域即为堆。分为显式分配器,使用malloc来分配free来回收。还有隐式分配器,用垃圾收集器来实现自动释放已分配的块。malloc使用堆虚拟内存区域,在不修改已分配块的前提下,申请空闲块,保持对齐要求。如果堆大小不够,则使用sbrk函数拓展堆。
实现方式有隐式空间链表,使用头信息来表示块大小和使用情况。以及其优化方式,同时使用头脚信息优化空闲块合并流程。还有显示空闲链表,维护着已分配块,空闲块的链式地址。还有多种分离式的空闲链表结构。
7.10本章小结
虚拟内存将主存看成是只保存活动区域,高效的使用了主存。同时为每个进程提供了一致的地址空间,简化了内存管理。又保护了每个进程的地址空间不被其他进程破坏。所以虚拟内存的创建是非常核心的概念。虚拟内存同时使一个非常强大的功能,可以创建和销毁内存片,映射到磁盘的某个部分,与其他程序共享。
(第7章 2分)

第8章 hello的IO管理
8.1 Linux的IO设备管理方法
所有的I/O设备都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行。Linux内核引出的应用接口是 Unix I/O
8.2 简述Unix IO接口及其函数
设备可以通过Unix I/O接口被映射为文件
int open(char *filename, int flags, mode_t mode); 打开一个存在的文件或者创建一个新文件的
int close(int fd); 关闭已打开的文件
ssize_t read(int fd, void *buf, size_t n);
ssize_t write(int fd, const void *buf, size_t n); 执行输入和输出
off_t lseek(int handle, off_t offset, int fromwhere); 显式地修改当前文件的位置
int vsprintf(char *buf, const char *fmt, va_list args) 发送格式化输出

8.3 printf的实现分析
printf代码

图8.3.1 printf函数

fmt是一个指针,这个指针指向第一个const参数
(char*)(&fmt) + 4) 表示的是…中的第一个参数

图8.3.2 vsprintf函数

vsprintf返回要打印出字符串的长度
write(buf, i); 把buf中的i个元素的值写到终端
write是系统级函数,程序在调用的时候,用syscall发生陷阱类异常,执行函数返回。

图8.3.3 write汇编

syscall将字节“Hello 学号 姓名”从寄存器中通过总线复制到显卡的显存中,以ASCII码形式存储。字符显示驱动子程序将通过ASCII码在字模库中找到点阵信息将点阵信息存储到vram中。显示芯片会按照一定的刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点。
8.4 getchar的实现分析
int getchar(void) {
char c;
return (read(0,&c,1)==1)?(unsigned char)c:
EOF }
getchar函数通过调用read函数返回字符。其中read函数的第一个参数是描述符fd,0代表标准输入。第二个参数输入内容的指针,字符c的地址,最后一个参数是1,代表读入一个字符。
read函数同样通过syscall中断来调用内核中的系统函数。键盘中断处理子程序会接受按键扫描码并将其转换为ASCII码后保存在缓冲区。然后read函数调用的系统函数可以对缓冲区ASCII码进行读取,直到接受回车键返回。

8.5本章小结
I/O接口与文件的抽象,使得应用程序更加便捷的调用底层,并对输入输出设备进行操作。Linux本身也提供了对底层的调用的一些系统函数。printf函数通过write向标准输出这个文件输出内容,函数会调用syscall触发中断以内核模式对硬件进行操作。

(第8章1分)
结论
程序的生成与执行是一个复杂的过程:
.c文件生成.exe文件的过程总共是经历了预处理,编译,汇编,链接
在shell中键入程序名,通过IO设备读入。
shell中fork出子进程,调用execve函数执行
磁盘读取代码进主存、虚拟内存映射、CPU逐条执行指令、缓存加载数据
异常信号处理
Unix I/O输入与输出
进程终止后shell对进程的回收
这些构成了一个程序大致的轮廓。

一个看似微乎其微的hello程序,在计算机中需要操作系统,内核,软件各个硬件各方的协调分工,甚至需要上万条代码来完成一个最基本的输出功能。让人不由的感慨计算机系统的巧妙与精细,对于现代社会中更加复杂的计算环境又是怎样繁杂,让人很难以想象。同时,如此庞杂的知识,更让我深感知识水平的欠缺,需要学习的东西太多太多。

(结论0分,缺少 -1分,根据内容酌情加分)

附件
列出所有的中间产物的文件名,并予以说明起作用。

hello.c 源程序
hello.i 预处理后修改的源程序
hello.s 编译后的汇编程序
hello.o 汇编后的可重定位目标程序
hello.o.txt 可重定位目标程序反汇编
hello 链接后的可执行目标程序
hello.txt 可执行目标程序反汇编

(附件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.
(参考文献0分,确实 -1分)

相关标签: 计算机系统