计算机系统——异常与信号

一、异常控制流
  从给处理器加电开始,处理器只是简单地读取和执行一个指令序列,这个指令序列就是处理器的控制流。现代系统通过使控制流发生突变,而对程序状态和系统状态的变化做出反应,称为异常控制流

1.1 异常
  异常使指为了响应某个事件而将控制转移给操作系统内核的情况,其中内核指操作系统常驻内存的部分。异常的处理需要软件与硬件的配合,当处理器检测到事件时,会通过异常表进行处理。
  异常表是一张跳转表,系统中可能的每一种类型的异常都分配了唯一的非负整数异常号,异常表的表目k包含了k类异常的处理程序的地址。当处理器检测并确定了事件的类型,即其异常号,便执行间接调用过程,通过表目跳转到响应的异常处理程序。异常处理类似于过程调用,但是:
  -处理器将可能的返回地址压栈,而返回地址可能是当前指令地址或下一指令地址,这取决于异常事件;
  -处理器会将额外的处理器状态也压栈。

1.2 中断
  异常根据发生的时钟周期可以分为同步异常异步异常中断【Interrupts】是异步发生的,由外部I/O设备产生,处理终端的程序称为中断处理程序。中断的处理步骤为:
  -执行当前指令的同时,中断引脚电压变高;
  -完成当前指令,并将控制转移给中断处理程序;
  -中断处理程序运行;
  -中断处理程序完成,将控制转移给调用程序,返回地址为下一指令。
外部I/O设备产生中断的情况包括键入、网络数据到达、磁盘数据到达等。其余的异常均是由于当前指令同步引起,这类指令称为故障指令

1.3 陷阱
  陷阱【Traps】是一种人为设置的异常,系统调用提供了用户程序与内核之间的接口。陷阱的处理步骤为:
  -应用程序调用系统调用接口syscall();
  -将控制转移给陷阱处理程序;
  -陷阱处理程序运行;
  -陷阱处理程序完成,将控制转移给调用程序,返回地址为下一指令。
  每一个x86-64系统调用都有一个唯一的ID号,用于各种系统操作。例如打开文件的ID为2,用户需要调用函数

open(const char *filename, int options);

其部分二进制编码与汇编代码为

00000000000e5d70 <__open>:
	...
	e5d79: b8 02 00 00 00 00	mov $0x2, %eax
	e5d7e: 0f 05				syscall
	e5d80: 48 3d 01 f0 ff ff	cmp $0xfffffffffffff001, %rax
	...
	e5dfa: c3					ret

其中,%rax储存了系统调用号,即0x2。

1.4 故障
  故障【Faults】是意外发生但可能被修复的异常,例如缺页、浮点异常等。故障的处理步骤为:
  -当前指令导致了一个故障;
  -将控制转移给故障处理程序;
  -陷阱处理程序运行;
  -陷阱处理程序完成,若修复,则将控制转移给调用程序,返回地址为当前指令,重新执行;否则返回内核的abort程序终止引起故障的应用程序。
  考虑在页大小为4KB的系统中的如下代码

int a[1000];
int x;
int main(){
	a[10] = 1;
	a[1000] = 3;
	a[10000] = 4;
	return 0;
}

其部分二进制编码与汇编代码为

8048300: c7 05 28 90 04 08 01 00 00 00		movl $0x1, 0x8049028
8048309: c7 05 a0 9f 04 08 03 00 00 00		movl $0x3, 0x8049fa0
8048313: c7 05 40 2c 05 08 04 00 00 00		movl $0x4, 0x8052c40

其中:
  -由于页大小为4KB,即0x1000个字节,故该指令的页的起始地址为0x8048000,因此,在取指阶段不会引起缺页;
  -在0x8048300地址处的代码访问了0x8049028的数据,而其位于起始地址为0x8049000的页中,从而引起了缺页故障,系统将处理缺页,并在结束后重新执行该指令;
  -在0x8048309地址处的代码访问了0x8049fa0的数据,其位于起始地址为0x8049000的页中,由于是第二次访问,故不会引起缺页故障,但实际上,该地址存储的已经不属于该数组,有一种可能的情况就是该地址是x的地址,而将x赋值为了3;
  -在0x8048313地址处的代码访问了0x52c40的数据,其已经偏离了数组首地址9个页面,可能超出了可读写区的范围,而发生保护违例,页故障处理程序使操作系统向用户进程发送不尝试恢复的信号,用户进程则以段错误【srgmentation fault】退出。

1.5 中止
  中止【Aborts】是意外发生的不可恢复的致命错误造成的异常,如非法指令等。中止的处理步骤为
  -当前指令发生可致命错误;
  -将控制转移给中止处理程序;
  -中止处理程序运行;
  -中止处理程序完成,返回内核的abort程序终止引起中止的应用程序。


二、进程
  进程【process】是一个执行中程序【program】的实例。

2.1 用户进程
  进程是操作系统对CPU执行的程序的运行过程的一种抽象。进程有自己的生命周期,由于任务启动而创建,随着任务完成或中止而消亡,所占用的资源也随着进程的中止而释放。
  程序是代码与数据的集合,而代码是机器指令序列,因而程序是一个静态概念;而进程是一个程序的一次运行活动,具有动态含义。一个可执行目标文件,即程序可以执行多次,即一个程序可能对应不同的进程。
  操作系统以外的任务属于用户的任务,而计算机处理用户任务由进程完成,为了强调进程完成的是用户的任务,通常称进程为用户进程
  进程提供给应用程序两个关键的抽象:
  -逻辑控制流,每个程序似乎独占处理器;
  -私有空间地址,每个程序似乎独占内存系统。

2.2 逻辑控制流
  即使系统中通常有许多其他程序在运行,进程也可以向每个程序提供一种假象,每个进程似乎在独占CPU。在调试器单步执行程序时,可以看到一系列的程序计数器的值,指向程序的可执行目标文件或共享对象中的指令,这个PC值的序列称为逻辑控制流
  逻辑控制流表明了进程是轮流使用处理器的。每个进程执行流的一部分,在使用结束后,将寄存器状态保存到内存,然后被暂时挂起。同时,PC切换控制的地址,加载保存在内存的寄存器状态,调度下一个进程执行。
  多核处理器则具有多个CPU,在同一时间内执行多个进程,并且共享主存与缓存,并通过内核负责处理器的调度,核执行独立的进程。
  每个进程是一个逻辑控制流,如果两个逻辑控制流在时间上有重叠,则称两个流为并发流。虽然并发进程的控制流在物理上不相交,但是可以认为并发进程就是并行运行的。

2.3 上下文切换
  进程由常驻内存的操作系统代码块,即内核管理。内核为每一个进程维护一个上下文【context】,即内核重新启动一个被挂起的进程所需的状态。操作系统通过处理器调度让处理器轮流执行多个进程,实现不同进程中指令交替执行的机制称为进程的上下文切换
  处理器调度等事件会引起用户进程在正常执行时被打断,形成异常控制流,而上下文切换机制则很好的解决了这类异常控制流,实现了一个进程安全切换到另一个进程执行的过程。
上下文

  进程的代码数据等物理实体与支持进程运行的环境称为进程上下文;由进程的程序块、数据块、运行时堆、用户栈等组成的用户空间信息称为用户级上下文;由进程标识信息、现场信息、控制信息和系统内核栈等组成的内核空间信息称为系统级上下文;用户级上下文的地址空间与系统级上下文的地址空间构成了进程的整个存储器映像,形如
储存其映像
2.4 系统级错误处理
  当Linux系统级函数遇到错误时,通常返回-1并设置全局整数变量errno来标记出错原因。原则上讲,除了少数返回未空的函数,每个系统级函数的返回状态必须要进行检查。例如在调用fork()时检查错误的代码

if ((pdi = fork()) < 0){
	fprintf(stderr, "fork error: %s \n", stderror(errno));
	exit(0);
}

其中,stderror()返回描述与erron值相关联的错误的文本串,这似乎使得代码臃肿且难以读懂。可以通过错误报告函数,形如

void unix_error(char *msg){
	fprintf(stderr, "%s: %s \n", msg, stderror(errno));
	exit(0);	
}

从而使检查系统级函数错误变得简洁,形如

if ((pdi = fork()) < 0){
	unix_error("fork error");
}

而使用错误处理包装函数,可以进一步简化代码,对于一个给定的基本函数foo,定义一个具有相同参数的包装函数Foo,包装函数用于调用基本函数,检查错误,进行终止,例如fork()函数的错误处理包装函数为

pid_t Fork(void){
	pid_t pid;
	if ((pdi = fork()) < 0){
		unix_error("Fork error");
	}
	return pid;
}

那么对fork()函数的调用仅需

pid = Fork();

2.5 进程控制
  Unix提供了大量从C中操作进程的系统调用。

  每个进程都有一个唯一的非零正数进程ID【process ID,PID】。使用getpid函数返回调用进程的PID,使用getppid函数返回父进程的PID,形如

#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void);
pid_t getppdi(void);

函数将会返回类型为pid_t的正整数的调用者的PID或调用者的父进程的PID。在Linux的types.h中,pid_t定义为int。

  进程可以被认为处于如下状态之一:
  -运行,进程在CPU执行或挂起等待内核调度;
  -停止,由于某些信号被挂起且不会被调度,直到受到某些信号;
  -终止,进程由于信号终止、执行结束、调用终止函数而永远的停止。
其中,中止函数形如

#include <stdlib.h>

void exit(int status);

调用进程将以status退出状态来终止程序,由于执行后程序便中止了,故exit()函数不返回。

  父进程可以通过调用fork()函数创建一个新的运行的子进程,形如

#include <sys/types.h>
#include <unistd.h>

pid_t fock(void);

子进程将执行父进程代码中的fock()及其后续代码,并且该函数在父进程中返回子进程的PID,而在子进程中返回0,或在错误时返回-1。在fock()执行成功时,其被调用了一次,但返回了两次。子进程几乎但不完全与父进程相同:
  -子进程将得到与父进程虚拟地址空间相同但又是独立的一份副本;
  -子进程将得到与父进程任何打开文件描述符相同的副本。
  考虑如下代码

int main(){
	pid_t pid;
	int x = 1;
	pid = Fork();
	
	/* Child */
	if (pid == 0){
		printf("child: x = %d\n", ++x);
		exit(0);
	}

	/* Parent */
	printf("parent: x = %d\n", --x);
	exit(0);
}

运行可以得到

parent: x = 0
child: x = 2

该程序有如下微妙的特点:
  -父进程与子进程并发运行,内核以任意方式交替执行,并不能确定父进程还是子进程先运算输出;
  -如果在fork()返回后立刻暂停两个进程,那么两个进程的地址空间完全相同;然而,由于两个进程是独立的,其私有的地址空间也是独立的,父进程与子进程在之后任何状态的改变都是独立的;
  -父进程与子进程输出在同一窗口,因为子进程继承了父进程所有打开的文件,两者输出的指向是相同的。

  当进程终止时,内核并不是立即把它从系统中清楚,相反的,其会保持在一种已经终止的状态,称为僵死进程,直到其父进程回收。当父进程回收已经终止的子进程,内核会将子进程的退出状态传递给父进程,然后删除已中止的进程,从此时开始,该进程就不存在了。
  如果父进程没有回收其僵死子进程就终止了,内核会安排init进程回收,其中init进程的PID为1,在系统启动时创建且不会终止,是所有进程的祖先。
  长时间运行的程序应当主动回收僵死子进程,因为僵死子进程虽然没有运行,但是仍然消耗系统的内存资源。一个进程可以调用waitpid()函数来将调用函数挂起,等待子进程终止或停止,形如

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *statups, int options);

其中,如果pid置为-1,则父进程会等待所有子进程中任一进程的终止或停止。其在时返回子进程的pid,或在错误时返回0或-1,这取决于失败的类型。其有一个简单版本wait(),形如

pid_t wait(int *statups);

其会挂起当前进程的执行,直到一个子进程终止,并在成功时返回子进程的pid。若*statups不指向空,则该指针指向的整数型会写入关于终止原因和退出状态的信息。

  execve函数会在当前进程的上下文中加载并运行一个新程序,形如

int execve(const char *filename, const char *argv[], const char *envp[]);

其中:
  -filename指向可执行目标文件或脚本;
  -argv为参数列表,并且惯例的,首个元素为filename;
  -envp为环境变量列表。
其会覆盖当前进程的代码、数据与栈,保留PID,继承已经打开的文件与信号。除非在调用过程中出现错误会返回-1,否则将不再返回。在execve()加载了filename之后,就会调用启动代码,设置栈,并将控制传递给主函数,形如

int main(int argc, const char *argv[], const char *envp[]);

在主函数执行时,用户栈的组织结构如下
启动栈
其中,argv[0]指向命令行字符串,envp[0]指向环境变量字符串。考虑在子进程中用当前环境下执行

linux> /bin/ls -lt /usr/include

其等价于在当前进程使用

char argv_0[] = {"/bin/ls"};
char argv_1[] = {"-lt"};
char argv_2[] = {"/usr/include"};
char argv_3[] = {"\0"};
const char *argv[] = {argv_0, argv_1, argv_2, argv_3};
const char *environ[] = {/* envir define */};
execve(argv[0], argv, environ);

三、信号
  信号允许进程和内核中断其他进程。一个信号就是一条消息,其通知进程系统中发生了某个类型的事件。

3.1 信号
  信号类似于异常和中断,其从内核发送到一个进程。信号的类型通过正整数ID来标识,且仅包含ID和目的进程两个信息。典型的有:
  -SIGINT,ID为2,默认行为终止,来自键盘的中断;
  -SIGKILL,ID为9,默认行为终止,用于杀死程序
  -SIGSEGV,ID为11,默认行为终止,由于无效的内存引用,即段故障;
  -SIGALRM,ID为14,默认行为终止,来自于alarm函数的定时器信号;
  -SIGCHLD,ID为17,默认行为忽略,来自于子进程的停止或终止。
内核发送信号的形式为更新目的进程上下文中的某个状态。发送信号可能来源于:
  -内核检测到一个系统事件;
  -一个进程调用了kill,显式的要求内核发送一个信号到目的进程,包括该进程本身。
而当目的进程被内核强迫以某种方式对信号的发送做出反应时,就称为接收信号。进程可以通过如下方式响应信号:
  -忽略信号;
  -终止进程;
  -调用信号处理程序捕获信号,其类似于响应异步中断。

  一个发出而没有被接收的信号叫做待处理信号。在任何时刻,在一个进程中,一个种类至多有一个待处理信号,并且信号不会排队等待,如果一个进程有一个类型为k的待处理信号,那么任何接下来发送到这个进程的类型为k的信号都不会排队等待,而只是被简单的丢弃。一个待处理信号最多只能被接收一次。
  内核为每个进程维护着待处理位向量pending,其是待处理信号的集合,在传送了类型为k的信号时,内核会设置pending中的第k位。

  一个进程可以选择阻塞接受某种信号。阻塞的信号可以被发送,但是不会被接收,直到进程取消了对该信号的阻塞。
  内核位每个进程维护着阻塞维向量blocked,其是被阻塞信号的集合,通过sigprocmask()设置和清除,也称为信号掩码。

3.2 信号的发送
  Unix系统提供了向进程发送信号的机制,基于进程组这个概念。每个进程都只属于一个进程组,进程组由一个正整数进程组ID标识。使用getpgrp()返回当前进程的进程组ID,形如

#include <unistd.h>
pid_t getpgrp(void);

默认的,子进程和其父进程同属于一个进程组。一个进程可以通过setpgid()改变进程的进程组,形如

#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);

其将进程pid的进程组改为pgid,且pid为0时表示调用者进程的pid;如果pgid为0,那么就将pid作为pgid,且将进程pid加入gpid为pid的进程组中,例如pid为15213的进程调用了setpgid(0, 0),那么该进程被加入gpid为15213的进程组中。该函数在成功时返回0,失败时返回-1。

  /bin/kill程序可以向另外的进程或进程组发送任意的信号,如

linux> /bin/kill -9 15213

会将信号9,即SIGKILL发送给进程15213;或者使用负数代表进程组,如

linux> /bin/kill -9 -15213

会将SIGKILL发送给进程组15213的全部进程。

  Unix shell使用作业的抽象概念来表示为对一条命令行求值而创建的进程。在任何时刻,至多存在一个前台作业和多个后台作业。在键入ctrl-c或ctrl-z时,分别会导致内核发送一个SIGINT、SIGTSTP信号,默认情况下,结果分别是终止前台作业与挂起前台作业。

  进程通过调用kill()函数发送信号给其他进程,包括该进程本身,形如

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

其中,pid指示目的进程,在为正数时指向进程pid;在为零时指向调用进程所在进程组的所有进程,包括进程本身;在为负时指向进程组-pid中的每个进程。sig表示了不同的信号。该函数在成功时返回0,在失败时返回1。

3.3 信号的接收
  当内核将进程从内核模式切换到用户模式时,内核将控制传递给进程之前,会检查phd = pengding & ~blocked,即进程未被阻塞的待处理信号的集合。当集合为空时,控制将传递给进程的逻辑控制流中的下一指令;否则,选择集合中最小的非零位,强制进程接收该信号,处罚信号对进程采取某种行为,并在信号处理后,若进程存在,则控制将传递给进程的逻辑控制流中的下一指令。
  每个信号类型都有一个预定义的默认行为,是下面中的一种:
  -进程终止;
  -进程终止并转储内存,即将代码和数据内存段的映像写到磁盘上;
  -进程挂起,直到被SIGCONT信号重启;
  -进程忽略该信号。
进程可以通过使用signal函数修改和信号相关联的默认行为,除了SIGSTOP与SIGKILL,形如

#include <signal.h>
typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, signhandler_t handler);

其中,handler的取值与功能为
  -SIG_IGN,忽略类型为signum的信号;
  -SIG_DFL,将类型未signum的信号行为恢复为默认;
  -否则需要一个用户定义的函数的地址,该函数称为信号处理程序
该函数在成功时返回指向信号处理程序的指针,在出错时返回SIG_ERR。

  使用信号处理程序改变信号的默认行为,称为设置信号处理程序;调用信号处理程序称为捕获信号;执行信号处理程序称为处理信号。在处理信号返回时,控制将传递给控制流中被信号接收所中断的指令处。
  信号处理程序是与主程序同时运行的独立逻辑流,而不是进程。其本身可以被其他信号处理程序中断。

3.4 信号的阻塞和解除
  信号的阻塞提供隐式阻塞机制,即内核默认阻塞与当前正在处理信号类型相同的待处理信号;以及显式阻塞,通过调用函数sigpromask()函数,形如

#include <signal.h>

int sigpromask(int how, const sigset_t *set, sigset_t *oldset);
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);

其中:
  -sigpromask()用于改变阻塞的信号集合,成功时返回0,否则返回-1;
  -sigemptyset()初始化阻塞集合为空,成功时返回0,否则返回-1;
  -sigfillset()将所有信号类型加入集合,成功时返回0,否则返回-1;
  -sigaddset()添加阻塞的信号类型,成功时返回0,否则返回-1;
  -sigdelset()删除阻塞的信号类型,成功时返回0,否则返回-1;
  -sigismember()判断信号是否在阻塞集合中,是则返回1,不是则返回0,不成功则返回-1。

3.5 非本地跳转
  C提供了一个用户级异常控制流形式,称为本地跳转,将控制直接从一个函数转移到另一个正在执行的函数,而不需要经过正常的调用返回序列。其实现形如

#include <setjmp.h>

int setjmp(jum_buf env);

其中,在第一次调用时,setjmp()返回0,且不能赋值给变量。setjmp()在env缓冲区保存当前的调用环境,并使用longjmp函数从env缓冲区中恢复调用环境,然后触发一个从最近一次初始化env的setjmp调用的返回,形如

#include <setjmp.h>

int longjmp(jum_buf env, int retval);

setjmp()与longjmp()的关系似乎令人疑惑,setjmp()只调用一次,但返回多次,分别是调用setjump()与响应的longjmp()调用;而longjmp()被调用一次却不返回。

  非本地跳转的一个重要应用是允许从一个深层嵌套的函数调用中立即返回,例如发现错误情况,就可以直接返回到一个普通的本地化的错误处理程序,而不是费力的反复返回来解开调用栈。
  为了理解非本地跳转的该应用,考虑如下的代码:

#include "csapp.h"

jmp_buf buf;
int error1 = 0;
int error2 = 1;

void foo(void);
void var(void);

int main(){
	swtich(setjmp(buf)){
	case 0:
		foo();
		break;
	case 1:
		printf("Detect an error1 condition in foo\n");
		break;
	case 2:
		printf("Detect an error2 condition in foo\n");
		break;
	default:
		printf("Unknown error condition in foo\n");
	}
	exit(0);
}

void foo(void){
	if (error1)
		longjmp(buf, 1);
	bar();
}

void bar(void){
	if (error2)
		longjmp(buf, 2);
}

其在进入main()后的开关语句中,setjmp()保存了环境,并返回了0,执行情况0,即调用foo(),foo()在正确的情况下调用bar(),完成调用,执行exit(0)关闭进程。如果调用foo()的过程中出现error1,则会跳转到开关语句,setjmp()返回1,执行情况1,报告error1的错误;如果调用bar()的过程出现error2,则会跳转到开关语句,setjmp()返回2,执行情况2,报告error2的错误。

  非本地跳转的另一个重要应用是使信号处理程序分支到一个特殊的代码位置,而不返回到被信号到达中断了的指令的位置。例如程序通过信号和非本地跳转重新定义中断的动作,使得进程不立即终止,而是回到主程序的入口,实现软重启。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对考试很有帮助的.......... 《计算机操作系统》期末复习指导 第一章 计算机操作系统概述 1、操作系统的概念 操作系统(Operating System,OS),是一种软件,属于系统软件; 1、科普的观点 操作系统是计算机系统的管理和控制中心,它依照设计者制定的各种调度策略组织和管理计算机系统资源,使之能高效地运行。 2、功能的观点 操作系统是一个计算机资源管理系统,它负责计算机系统的全部资源的分配、控制、调度和回收。 3、用户的观点 操作系统是计算机与用户之间的接口,用户通过这种接口使用计算机。 4、软件的观点 操作系统是程序和数据结构的集合。 5、管理的观点 操作系统是计算机硬件和软件资源的合理而协调的管理者。 6、 操作系统是一个大型的程序系统,它负责计算机的全部软、硬件资源的分配、调度工作,控制并协调并发活动,实现信息的存取和保护。它提供用户接口,使用户获得良好的工作环境。操作系统使整个计算机系统实现了高效率和高度自动化。 2、操作系统的生成和五大类型 生成:产生最适合自己工作环境的OS内核(kernel)。既方便用户,又使系统开销尽量小;生成的配置过程如UNIX中newconfig命令;DOS中config.sys文件;维护由系统管理员负责。 操作系统的五大类型是批处理操作系统、分时操作系统、实时操作系统、网络操作系统、分布式操作系统。 多道程序设计:即在系统内(内存)同时存放并运行几道相互独立的程序。 多道程序设计的基础:是将运行过程进一步细化成几个小的步骤,从而实现宏观上的并行。但从微观上看,内存中的多道程序轮流地或分时地占用处理机,交替执行。 多道程序系统 ≠ 多重处理系统 ≠ 多用户 ≠ 多终端 多道是指内存中驻留多个程序或一个程序的多个程序段,因此,多用户系统一定是采用多道技术。而多道系统不一定是多用户系统。多重处理系统一般指多CPU系统。当然,一个CPU的系统采用分时技术可以为多用户服务。多用户的关键技术是在用户之间要有保密保安措施。终端指用户使用的硬件设备,即使一个终端也可为多用户使用,例如,银行的自动取款机(ATM)。 •分时与实时 分时技术:把CPU的时间分成很短的时间片(例如,几十至几百毫秒)工作。随着时间片的时间减少,对换时间所占的比例随之增大。随着用户数目的不断增加,这种矛盾会越来越突出。 实时是指计算机对于外来信息能够以足够快的速度进行处理,并在被控对象允许的时间范围内做出快速反应。交互作用能力较差。 3、操作系统的五大功能 •作业管理:包括任务管理、界面管理、人机交互、图形界面、语音控制和虚拟现实等; •文件管理:又称为信息管理; •存储管理:实质是对存储“空间”的管理,主要指对内存的管理; •设备管理:实质是对硬件设备的管理,其中包括对输入输出设备的分配、启动、完成和回收; •进程管理:又称处理机管理,实质上是对处理机执行“时间”的管理,即如何将CPU真正合理地分配给每个任务。 4、表征操作系统的属性 主要有:响应比,并发性,信息的共享、保密与保护,可扩充性、可移植性、可读性、可“生成”性,安全可靠性,可测试性等。 第二章 用户与操作系统的接口 1、基本概念 作业(Job)是让计算机完成一件事或任务,可大可小,可多可少。 作业步(Job steps) :作业顺序执行的工作单元。 作业流(Job Stream) :作业步的控制流程。 作业类别:终端交互作业、批处理作业。 2、用户界面 三代用户界面: •第一代用户界面:操作命令和系统调用在一维空间(命令行界面); •第二代用户界面:图形界面在二维空间(图形界面); •第三代用户界面:虚拟现实在三维空间(虚拟现实的界面元素)。 3、传统的人机接口 •操作命令 联机(键盘操作命令)、脱机(作业控制语言) 用户组合自编(Shell语言):DOS Shell;UNIX ;BShell、CShell等 •系统调用(System Call) 4、作业输入输出方式 •输入输出方式:脱机、直接耦合(交互联机) •SPOOLing:联机外围同时操作,假脱机(排队转储,设备虚拟技术) 5、作业调度 •作业调度的功能: (1)采用JCB(作业控制块)表格,记录各作业状况; (2)按选定的算法,从后备作业队列中选出一部分(多道)或一个作业投入运行; (3)为被选中的作业做好运行前的准备工作。例如建立相应的执行进程和分配系统资源; (4)作业运行结束的善后处理工作。 •作业调度算法: (1)先来先服务(FCFS) 作业平均周转时间=∑(作业完成时刻i-作业提交时刻i)/n个作业 (2)最短作业优先:在作业内容参差很不均衡时有合理性 (3)“响应比”最高的优先 “响应(系数)比”:作业响应时间(等待和运行)/作业运行时间 (4)定时轮转法(按时间片):适合作业不定的情况 (5)优先数法:急事先办的原则 第三章进程及处理机管理 1、为什么要引入“进程” (1)进程调度属于低级处理机管理,即确定系统中哪个进程将获得CPU;而作业调度属于高级处理机管理,即确定系统中哪些作业将获得CPU。 (2)进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。 (3)引入进程的意义是描述多道程序设计系统中程序的动态执行过程。 2、进程的定义及特征 (1)程序和进程的区别 (2)进程的五个基本特征:动态性、并发性、独立性、制约性、结构性 3、进程调度 (1)进程的三个基本状态及转换 三个基本状态是等待、执行和就绪,在一定的条件下,进程的状态将发生转换。 (2)进程调度算法 主要有先来先服务(FCFS)、时间片轮转法、多级反馈轮转法、优先数法。 (3)进程控制块(PCB)是进程存在的唯一标志,它描述了进程的动态性。 4、进程通信 (1)进程的同步与互斥 一般来说同步反映了进程之间的协作性质,往往指有几个进程共同完成一个任务时在时间次序上的某种限制,进程相互之间各自的存在及作用,通过交换信息完成通信。如接力比赛中一组队员使用接力棒等。 进程互斥体现了进程之间对资源的竞争关系,这时进程相互之间不一定清楚其它进程情况,往往指多个任务多个进程间的通讯制约,因而使用更广泛。如打篮球时双方挣抢篮板球等。 (2)临界区 并发进程中与共享资源有关的程序段定义为临界区。进入临界区的准则是:①一次只准一个进程进入临界区;②本进程结束负责通知下一进程;③进程调度,不能阻塞。 (3)原语 原语是不可中断的过程。 •加锁/开锁(LOCK/UNLOCK)原语 优点是实现互斥简单;缺点是效率很低。 •信号量(Semaphore)及PV操作 PV操作能够实现对临界区的管理要求。它由P操作原语和V操作原语组成,对信号量进行操作,具体定义如下: P(S):①将信号量S的值减1,即S=S-1; ②如果S 0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。 V(S):①将信号量S的值加1,即S=S+1; ②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。 信号量的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意信号量的值仅能由PV操作来改变。 一般来说,信号量S 0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S 0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。 •消息缓冲通信原语 高级通信原语,用于一组信息发送(Send)与读取(Read)。 5、死锁 (1)死锁的概念 死锁是两个或两个以上的进程中的每一个,都在等待其中另一个进程释放资源而被封锁,它们都无法向前推进,称这种现象为死锁现象。 产生死锁的原因是共享资源有限,多个进程对共享资源的竞争,而且操作不当。 (2)产生死锁的四个必要条件是资源互斥使用、保持和等待、非剥夺性、循环等待。 (3)解决死锁的方法 一般有死锁的预防,即破坏产生死锁的四个必要条件中的一个或多个,使系统绝不会进入死锁状态;死锁的避免,即在资源动态分配的过程中使用某种办法防止系统进人死锁状态;和允许系统产生死锁,然后使用检测算法及时地发现并解除它。 安全状态、安全系列、银行家算法等 第四章 存储管理 1、存储管理使用的基本概念 •逻辑地址与物理地址 在具有地址变换机构的计算机中,允许程序中编排的地址和信息实际存放在内存中的地址有所不同。前者叫逻辑(相对)地址,后者叫物理(绝对)地址。 •重定位:将逻辑地址转换为物理地址。 •虚拟存储管理 虚存是由操作系统调度,采用内外存的交换技术,各道程序在必需使用时调入内存,不用的调出内存,这样好象内存容量不受限制。 虚存的特点: (1)虚存容量不是无限的,极端情况受内存和外存可利用的总容量限制; (2)虚存容量还受计算机总线地址结构限制; (3)速度和容量的“时空”矛盾,虛存量的“扩大”是以牺牲CPU工作时间以及内外存交换时间为代价的。 •存储管理的目的及功能 目的是方便用户,提高内存资源的利用率,实现内存共享。 功能主要有内存的分配和管理、内存的扩充技术、内存保护技术 2、分区分配存储管理 分为固定分区、可变分区、可重定位分区、多重分区。 内存“扩充”技术: •交换:由操作系统做,用户不知道。 •覆盖:由用户控制,操作系统提供覆盖机制。 内存保护技术: ---保护系统工作区和用户作业区,特别是如何防止系统区被破坏。方法有存储保护键、界限寄存器 3、请求页式存储管理 (1)页式存储管理实现原理 基于程序在运行时不需要一开始都装入内存(局部性原理),更不应该把最近较长一段时间内不用的程序装入内存。 (2)页表的作用是将逻辑页号转换为物理块号。 (3)页面淘汰算法 先进先出算法(FIFO)、循环检测法、最近最少使用页面先淘汰(LRU)、最不经常使用的页面先淘汰(LFU)、最近没有使用页面先淘汰(NUR)、最优淘汰算法(OPT)等。 (4)页式存储管理的优、缺点 优点: •虛存量大,适合多道程序运行,用户不必担心内存不够的调度操作; •内存利用率高,不常用的页面尽量不留在内存; •不要求作业连续存放,有效地解决了“碎片”问题。与分区式相比,不需移动作业;与多重分区比,无零星碎片产生。 缺点: •要处理页面中断、缺页中断处理等,系统开销较大; •有可能产生“抖动”; •地址变换机构复杂,为提高速度采用硬件实现,增加了机器成本。 4、段式、段页式存储管理 段式、页式存储管理的对比。 段页式存储管理特点: •每一段分若干页,再按页式管理,页间不要求连续; •用分段方法分配管理作业,用分页方法分配管理内存; •兼有段式和页式管理的优点,系统复杂和开销增大,一般在大型机器上才使用。 第五章文件管理 1、文件管理任务与功能 任务:把存储、检索、共享和保护文件的手段,提供给操作系统本身和用户,以达到方便用户和提高资源利用率的目的。 功能: ---分配与管理外存 ---提供合适的存储方法 ---文件共享、保护,解决命名冲突 文件组织结构:文件、文件元素、文件系统 •文件系统 = 文件管理程序(文件和目录的集合)+ 它所管理的全部文件; •文件系统是用户与外存的接口; •为用户提供统一方法(以数据记录的逻辑单位),访问存储在物理介质上的信息。 2、文件分类 (1)按文件性质与用途分:系统文件、库文件、用户文件 (2)按操作保护分:只读文件、可读可写文件、可执行文件 (3)按使用情况分:临时文件、永久文件、档案文件 (4)按用户观点分:普通文件、目录文件、特殊文件 (5)按存取的物理结构分:顺序(连续)文件、链接文件、索引文件 (6)按文件的逻辑存储结构分:有结构文件、无结构文件 (7)按文件中的数据形式分:源文件、目标文件 3、文件的逻辑结构和物理结构 •文件的逻辑结构 ---从用户观点看 ---按文件名及记录号存取文件,是一维、连续的字符序列,方便存储、检索或加工 ---文件由若干个逻辑记录组成,并加以命名或编号 •文件的物理结构 又称文件的存储结构,是指文件在外存上的存储组织形式,是与存储介质的存储性能有关; 空闲空间的管理方法主要有:空闲表法、空闲(自由)链表法、成组链接法 4、文件目录 (1)文件目录分类:一级文件目录、二级文件目录、多级文件目录 (2)文件目录的管理 •目录做成文件,文件系统便于内部统一管理,目录文件在使用时调入内存; •在操作系统中,大量采用“表格”管理。 5、文件存取控制 •解决文件保护、保密和共享 •常用的文件存取控制方法有:存取控制矩阵、用户权限表、使用口令、使用密码 6、文件系统的数据结构和表示 UNIX或Linux操作系统中文件系统的主要特点 (1)操作系统文件的目录组织是一个树形结构,从根结点到叶子称为文件的全路径名,文件可以由其全路径名唯一确定; (2)文件本身是无结构的字符流; (3)把外部设备的特殊文件和普通文件以及目录文件都统一在文件这一概念上,对于一般文件的访问、共享和保护方式也可以适用于外部设备。 第六章 输入输出设备管理 1、设备管理的任务和功能 •设备管理的任务 (1)按用户需求提出的要求接入外部设备,系统按一定算法分配和管理控制,而用户不必关心设备的实际地址和控制指令; (2)尽量提高输入输出设备的利用率,例如发挥主机与外设以及外设与外设之间的真正并行工作能力。 •设备管理的功能 (1)分配设备 (2)控制和实现真正的输入输出操作 (3)对输入输出缓冲区进行管理 (4)在一些较大系统中实现虚拟设备技术 2、外部设备分类 (1)按系统和用户分:系统设备、用户设备 (2)按输入输出传送方式分(UNIX或Linux操作系统):字符型设备、块设备 (3)按资源特点分:独享设备、共享设备、虚拟设备 (4)按设备硬件物理特性分:顺序存取设备、直接存取设备 (5)按设备使用分:物理设备、逻辑设备、伪设备 •设备I/O方式:询问、通道、中断 •I/O设备分配算法:先来先服务(FCFS)、按优先级进行分配 3、设备管理技术 (1)I/O设置缓存理由 •解决信息的到达率和离去率不一致的矛盾; •缓存起中转站的作用; •使得一次输入的信息能多次使用; •在通道或控制器内设置局部寄存器作为缓冲存储器,可暂存I/O信息,以减少中断CPU的次数。这种情形可进一步推广,使得一次读入的信息可多次重复使用。 (2)虚拟设备的技术(SPOOLing) SPOOLing,即外围设备联机并行操作,它是关于慢速字符设备如何与计算机主机交换信息的一种技术,通常也叫做“假脱机技术”。是一种预输入、缓输出和转储的管理技术. SPOOLing系统的特点: •提高了I/O速度; •将独享设备改造为共享设备(典型例子是打印机的“共享”); •实现了虚拟设备功能。 4、设备处理程序编制内容 •设备驱动程序的功能 (1)将接收到的抽象要求转换为具体要求; (2)检查用户I/O请求的合法性,了解I/O设备的状态,传递有I/O关参数,设置设备的工作方式; (3)发出I/O命令,启动分配到的I/O设备,完成指定的I/O 操作; (4)及时响应由控制器或通道发来的中断请求,并根据其中断类型调用相应的中断处理程序进行处理; (5)对于设置有通道的计算机系统,驱动程序还应能够根据用户的 I/O请求,自动地构成通道程序。 •设备驱动程序的特点 (1)驱动程序主要是在请求I/O的进程与设备控制器之间的一个通信程序。 (2)驱动程序与I/O设备的特性紧密相关。 (3)驱动程序与I/O控制方式紧密相关。 (4)由于驱动程序与硬件紧密相关,因而其中的一部分程序用汇编语言书写,目前有很多驱动程序,其基本部分已经固化,放在ROM中。 •设备处理方式 (1)将抽象要求转换为具体要求 (2)检查I/O请求的合法性 (3)读出和检查设备的状态 (4)传送必要的参数 (5)方式的设置和I/O设备启动 难点分析 •如何理解操作系统在计算机系统中的地位? 操作系统是软件,而且是系统软件。它在计算机系统中的作用,大致可以从两方面体会:对内,操作系统管理计算机系统的各种资源,扩充硬件的功能;对外,操作系统提供良好的人机界面,方便用户使用计算机。它在整个计算机系统中具有承上启下的地位。 •系统调用与一般过程调用的区别。 系统调用在本质上是一种过程调用,但它是一种特殊的过程调用,它与一般过程调用的主要区别如下: (1)运行状态不同。一般的过程调用,其调用和被调用过程都是用户程序,它们都运行在同一系统状态下;而系统调用的调用过程是用户程序,它运行在用户态,其被调用过程是系统过程,运行在系统态。 (2)进入方式不同。一般过程调用可以直接通过过程调用语句将控制转移到被调用过程;而执行系统调用时,由于调用和被调用过程处于不同系统状态,必须通过访管中断进入。 (3)代码层次不同。一般过程调用中的被调用程序是用户级程序,而系统调用是操作系统中的代码程序,是系统级程序。 •下表给出作业l、2、3的提交时间和运行时间。采用先来先服务调度算法和短作业优先调度算法,试问平均周转时间各为多少?(时间单位:小时,以十进制进行计算。) 解:采用先来先服务调度策略,则调度顺序为l、2、3。  平均周转时间T=(8+11.6+12)/3=10.53 采用短作业优先调度策略,则调度顺序为l、3、2。  平均周转时间T=(8+8+12.6)/3=9.53 •试述文件管理系统设置打开文件、关闭文件命令的原因。 解:操作系统需要处理大量用户文件,而访问一个文件需要查询目录,有时甚至需要多次查询目录。由于文件目录与文件一起存放在辅存上,当存取文件时,必须先到辅存中读取文件目录信息,从中获得文件的存放地址,然后再去存取文件。这样一来,文件信息的存取将花费很多时间。如果将整个文件目录放入主存,虽然可以提高存取速度,但这需要占用大量主存空间,显然这也是不可取的。 实际上,在一段时间内使用的文件数总是有限的,因此只要将目录中当前要使用的那些文件的目录表目复制到内存中就可以了。这样既不占用太多的主存空间,又可显著提高查询文件目录的速度。为此,大多数操作系统中设置了两个文件操作:打开文件和关闭文件。 打开文件操作完成的功能是将文件的有关目录信息复制到主存活动文件表中,以建立用户和这个文件的联系。关闭文件操作的功能是用户宣布这个文件当前不再使用,系统将其在主存中的相应目录信息删去,因而也就切断了用户同这个文件的联系。 •有一个文件系统如图(a)所示,图中的框表示目录,圈表示普通文件。根目录常驻内存,目录文件组织成链接文件,不设文件控制块,普通文件组织成索引文件。目录表目指示下一级文件名及其磁盘地址(各占2个字节,共4个字节)。若下级文件是目录文件,指示其第一个磁盘块地址。若下级文件是普通文件,指示其文件控制块的磁盘地址。每个目录文件磁盘块最后4个字节供拉链使用。下级文件在上级目录文件中的次序在图中为从左至右。每个磁盘块有512字节,与普通文件的一页等长。 普通文件的文件控制块组织结构如图(b)所示,其中每个磁盘地址占2个字节,前10个地址直接指示该文件前10页的地址。第11个地址指示一级索引表地址,一级索引表中每个磁盘地址指示一个文件页地址;第12个地址指示二级索引表地址,二级索引表中每个地址指示一个一级索引表地址;第13个地址指示三级索引表地址,三级索引表中每个地址指示一个二级索引表地址。问: (1)一个普通文件最多可有多少个文件页? (2)若要读文件J中的某一页,最多启动磁盘多少次? (3)若要读文件W中的某一页,最少启动磁盘多少次? 答:(1)由题目中所给条件可知,磁盘块大小为512字节,每个磁盘地址占2个字节。因此,一个一级索引表可容纳256个磁盘地址。同样地,一个二级索引表可容纳256个一级索引表地址,一个三级索引表可容纳256个二级索引表地址。这样,一个普通文件最多可有页数为:10+256+256×256+256×256×256=16843018 (2)从图(a)中可以看出,目录文件A和目录文件D中的目录项都只有两个,因此这两个目录文件都不需要拉链。若要读文件J中的某一项,首先从内存的根目录中找到目录文件A的磁盘地址,将其读入内存(第1次访问磁盘)。然后再从目录A中找出目录文件D的磁盘地址,并将其读入内存(第2次访问磁盘)。从目录D中找出文件J的文件控制块地址,将文件J的文件控制块读入内存(第3次访问磁盘)。在最坏情况下,要访问页的磁盘地址需通过三级索引才能找到,这时要三次访问磁盘才能将三级索引表读入内存(第4、5、6次访问磁盘)。最后读入文件J中的相应页(第7次访问磁盘)。 由此可知,若要读文件J中的某一页,最多启动磁盘7次。 (3)从图(a)中可以看出,目录文件C和目录文件U中,目录项数目较多,若目录项数超过127(512/4-l=127),则目录文件的读入可能需要多次磁盘读(因目录文件组织成链接文件)。在最好情况下,所找的目录项都在目录文件的第一个磁盘块中。若要读文件W中的某一页,首先从内存的根目录中找到目录文件C的磁盘地址,将其读入内存(第1次访问磁盘)。在最好情况下,能从目录C的第一个磁盘块中找出目录文件互的磁盘地址,并将其读入内存(第2次访问磁盘)。从目录I中找出目录文件P的的磁盘地址,将其读入内存(第3次访问磁盘)。从目录P中找到目录文件U的磁盘地址,将其读入内存(第4次访问磁盘)。在最好情况下,能从目录U的第一个磁盘块中找出文件W的文件控制块地址,将文件W的文件控制块读入内存(第5次访问磁盘)。在最好情况下,要访问的页在前10页中,这时可直接得到该页的磁盘地址。最后读入文件W中的相应页(第6次访问磁盘)。 由此可知,若要读文件W中的某一页,最少启动磁盘6次。 •采用可变分区管理存储空间时,若主存中按地址顺序依次有五个空闲区,大小分别为15K、28K、10K、226K、110K。现有五个作业J1到J5,它们所需的主存空间依次是10K、15K、102K、26K、180K。问如果采用首次适应分配算法,能否把这五个作业按J1到J5的次序全部装入主存。使用哪种分配算法装入这五个作业,可使主存的利用率最高? 解:按首次适应分配算法,不能把这五个作业全部依次装入主存。这时J1、J2装入第1、2个空闲区,J3、J4装入第4、5个空闲区,J5有180K,无法装入仅有的10K空闲区。 能使主存利用率最高的是采用最佳适应分配算法。这时,这五个空闲块分别装入作业J2、J4、J1、J5、J3。 •某虚拟存储器的用户编程空间共32个页面,每页为1KB,内存为16KB。假定某时刻一用户页表中已调入内存的页面的页号和物理块号的对照表如下: 请计算逻辑地址0A5C(H)所对应的绝对地址。 解:页式存储管理的逻辑地址分为两部分:页号和页内地址。由已知条件“用户编程空间共32个页面”,可知页号部分占5位;由“每页为1KB”,1K=210,可知内页地址占10位。由“内存为16KB”,可知有16块,块号为4位。 逻辑地址0A5C(H)所对应的二进制表示形式是:000 1010 0101 1100 ,根据上面的分析,下划线部分为页内地址,编码 “000 10” 为页号,表示该逻辑地址对应的页号为2。查页表,得到物理块号是4(十进制),即物理块地址为:01 00 ,拼接块内地址10 0101 1100,得01 0010 0101 1100,即125C(H)。 •某采用页式存储管理的系统,接收了一个共7页的作业,作业执行时依次访问的页为:1、2、3、4、2、1、5、6、2、1、2、3、7。当内存块数量为4时,请分别用先进先出(FIFO)调度算法和最近最少使用(LRU)调度算法,计算作业执行过程中会产生多少次缺页中断?写出依次产生缺页中断后应淘汰的页。(所有内存开始时都是空的,凡第一次用到的页面都产生一次缺页中断。要求写出计算过程) 解:(1)采用先进先出(FIFO)调度算法,页面调度过程如下: 所以,共产生10次缺页中断,依次淘汰的页是1、2、3、4、5、6。 (2)采用最近最少使用(LRU)调度算法,页面调度过程如下: 因此,共产生8次缺页中断,依次淘汰的页是3、4、5、6。 •试述分页式存储管理系统和分段式存储管理系统的主要区别。 解:分页和分段有许多相似之处,比如两者都不要求作业连续存放。但在概念上两者完全不同,主要表现在以下几个方式: (1)页是信息的物理单位,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要。段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现共享,满足用户的需要。 (2)页的大小固定,由系统确定,将逻辑地址划分为页号和页内地址是由机器硬件实现的。而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时根据信息的性质来划分。 (3)分页的作业地址空间是一维的。分段的地址空间是二维的。 •为什么说有了通道技术和中断技术才真正做到了CPU与外设的并行操作? 解:通道是负责外围设备与主存之间进行数据交换,能单独完成输入输出操作的处理机。有了通道,主存和外围设备之间的数据交换就不要CPU干预了,CPU可以做与输入输出无关的其他工作,从而使计算机系统获得了CPU与外围设备之间并行工作的能力。 I/O中断是通道和CPU协调工作的一种手段。如果没有中断技术,CPU就要不断去查询通道以及设备执行的情况,这样一来,CPU还是把大量的时间花在了查询上,不能很好地为其他进程服务。使用中断技术,CPU可以完全不管通道和设备的执行情况,因为无论操作正常结束或操作异常结束,通道都会发出中断,通知CPU来处理。 综上所述,通道技术和中断技术的出现,使得主存可以直接与外设交换数据,而CPU得以并行地工作,大大提高了CPU的使用效率。 •某分时系统的进程出现如图所示的状态变化。 试问:(1)你认为该系统采用的是何种进程调度算法? (2)把图中所示的六个状态变化的原因写出来。 解:(1)该分时系统采用的进程调度算法是时间片轮转法。 (2)①进程被选中,变成运行态;②时间片到,运行的进程排入就绪队列尾部;③运行的进程启动打印机,等待打印;④打印工作结束,等待的进程排入就绪队列尾部;⑤等待磁盘读文件工作;⑥磁盘传输信息结束,等待的进程排入就绪队列尾部。 •怎样理解操作系统的作业调度和进程调度的关系? 解:作业调度和进程调度都属于处理机调度。作业调度是处理机管理的高级形式,它的主要功能是审查系统是否能满足用户作业的资源要求以及按照一定的算法来选取作业。进程调度是处理机管理的低级形式,它的主要功能是根据一定的算法将CPU分派给就绪队列中的一个进程。 作业的状态及其转换 操作系统中作业的状态主要有:提交、后备、执行、完成,进程的状态主要有等待、就绪、执行。作业调度和进程调度的转换关系见下图。 •用PV操作实现进程间的同步与互斥应该注意什么? 解:用PV操作实现进程间的同步与互斥,应该注意以下四方面问题: (1)对每一个共享资源都要设立信号量。互斥时对一个共享资源设立一个信号量;同步时对一个共享资源可能要设立两个或多个信号量,要视由几个进程来使用该共享变量而定。 (2)互斥时信号量的初值一般为1;同步时至少有一个信号量的初值大于等于1。 (3)PV操作一定要成对调用。互斥时在临界区前后对同一信号量作PV操作;同步时则对不同的信号量作PV操作,PV操作的位置一定要正确。 (4)对互斥和同步混合问题,PV操作可能会嵌套,一般同步的PV操作在外,互斥的PV操作在内。 三、课程练习及参考解答 一、填空 1、设备I/O方式有如下三种:_________、__________和___________。 2、文件存取方式按存取次序通常分_________________、_______________,还有一类 ______________。 3、从用户观点看,UNIX系统将文件分三类:___________________、___________________和 _________________。 4、引起死锁的四个必要条件是 、________________、 和__________________。 5、进程的三个最基本状态是_____________、____________和_____________。 6、传统操作系统提供编程人员的接口称为________________。 7、三代人机界面的发展是指:______________、_________________和_______________。 8、常用的进程调度算法有_________________、_________________和___________________。 二、选择一个正确答案的序号填入括号中 1、计算机操作系统是一个( )。 A. 应用软件 B. 硬件的扩充 C. 用户软件 D.系统软件 2、操作系统程序结构的主要特点是( )。 A. 一个程序模块 B. 分层结构 C. 层次模块化结构 D. 子程序结构 3、面向用户的组织机构属于( )。 A. 虚拟结构 B. 逻辑结构 C. 实际结构 D. 物理结构 4、操作系统中应用最多的数据结构是( )。 A. 堆栈 B. 队列 C. 表格 D. 树 5、可重定位内存分区分配目的为( )。 A. 解决碎片问题 B. 便于多作业共享内存 C. 回收空白区方便 D. 摆脱用户干预 6、逻辑地址就是( )。 A. 用户地址 B. 相对地址 C. 物理地址 D.绝对地址 7、原语是( )。 A. 一条机器指令 B. 若干条机器指令组成 C. 一条特定指令 D. 中途能打断的指令 8、索引式(随机)文件组织的一个主要优点是( )。 A. 不需要链接指针 B. 用户存取方便 C.回收实现比较简单 D.能实现物理块的动态分配 9、几年前一位芬兰大学生在Internet上公开发布了以下一种免费操作系统核心( ),经过许多人的努力,该操作系统正不断完善,并被推广。 A. Windows NT B. Linux C. UNIX D. OS2 10.文件目录的主要作用是( )。 A. 按名存取 B.提高速度 C.节省空间 D.提高外存利用率 11、某进程在运行过程中需要等待从磁盘上读入数据,此时该进程的状态是( )。 A. 从就绪变为运行 B.从运行变为就绪 C. 从运行变为阻塞 D.从阻塞变为就绪 12、把逻辑地址转变为内存的物理地址的过程称作( )。 A.编译 B.连接 C.运行 D.重定位 13、进程和程序的一个本质区别是( )。 A.前者分时使用CPU, 后者独占CPU B.前者存储在内存,后者存储在外存 C.前者在一个文件中,后者在多个文件中 D.前者为动态的,后者为静态的 三、是非题,正确的在括号内划√,错的划×。 ( )1、进程间的相互制约关系体现为进程的互斥和同步。 ( )2、只有一个终端的计算机无法安装多用户操作系统。 ( )3、UNIX的最大特点是分时多用户、多任务和倒树型文件结构。 ( )4、常用的缓冲技术有双缓冲,环形缓冲和缓冲池。 ( )5、实时操作系统的响应系数最小,设备利用率最差。 ( )6、死锁是指两个或多个进程都处于互相等待状态而无法继续工作。 ( )7、具有多道功能的操作系统一定是多用户操作系统。 ( )8、一般的分时操作系统无法做实时控制用。 ( )9、多用户操作系统在单一硬件终端硬件支持下仍然可以工作。 ( )10、常用的缓冲技术是解决慢速设备与快速CPU处理之间协调工作。 四、回答题 1、试以生产者——消费者问题说明进程同步问题的实质。 2、以一台打印机为例,简述SPOOLing 技术的优点。 3、简述请求页式存储管理的优缺点。 4、虚拟存储器的基本特征是什么?虚拟存储器的容量主要受到什么限制? 5、现代操作系统与传统操作系统相比,设计中采用了哪些先进技术? 练习参考解答 一、填空 1、询问、中断、通道 2、顺序存取、直接存取、按键索引 3、普通(用户)、目录、特殊 4、互斥使用、保持和等待、非剥夺性、循环等待 5、准备(就绪)、执行、等待 6、系统调用 7、一维命令行、二维图形界面、三维虚拟现实 8、先来先服务、优先数法、轮转法 二、选择题 1、D 2、C 3、B 4、C 5、A 6、B 7、B 8、D 9、B 10、A 11、C 12、D 13、D 三、是非题 有错误的是第2、5、7题,其余均是正确的。 四、回答题 1、答:一个生产者,一个消费者和一个产品之间关系是典型的进程同步问题。设信号量S为仓库内产品,P-V操作配对进行缺一不可。生产者进程将产品放入仓库后通知消费者可用;消费者进程在得知仓库有产品时取走,然后告诉生产者可继续生产。 2、答:以一台打印机为例, SPOOLing 技术的主要优点是在多用户情况下,每一个用户使用打印机就好像自己拥有一台打印机。不会产生打印机“忙”而等待。 3、答:优点: (1)虛存量大,适合多道程序运行,用户不必担心内存不够的调度操作。动态页式管理提供了内存与外存统一管理的虚存实现方式。 (2)内存利用率高,不常用的页面尽量不留在内存。 (3)不要求作业连续存放,有效地解决了“碎片”问题。与分区式比,不需移动作业;与多重分区比,无零星碎片产生。UNIX操作系统较早采用。 缺点: (1)要处理页面中断、缺页中断处理等,系统开销较大。 (2)有可能产生“抖动”。 (3)地址变换机构复杂,为提高速度采用硬件实现,增加了机器成本。 4、答:虚存是由操作系统调度,采用内外存的交换技术,各道程序在必需使用时调入内存,不用的调出内存,这祥好像内存容量不受限制。但要注意: (1)虚存容量不是无限的,极端情况受内存、外存的可使用的总容量限制; (2)虚存容量还受计算机总线长度的地址结构限制; (3)速度和容量的“时空”矛盾,虛存量的“扩大”是以牺牲CPU工作时间以及内、外存交换时间为代价的。 5、答:现代操作系统是指网络操作系统和分布式操作系统,采用了网络地址方案、网络协议、路由技术和微内核等先进技术。
1.操作系统概述 操作系统的形成,操作系统的定义与功能,操作系统的分类 2.处理机管理 多道程序设计技术,用户与操作系统的两种接口,进程的定义、特征和基本状态,进程控制块(PCB)和控制块队列(运行、就绪、阻塞),进程的各种调度算法(先来先服务、时间片轮转、优先数、多级队列),进程管理的基本原语(创建、撤消、阻塞、唤醒),作业与作业调度算法(先来先服务、短作业优先、响应比高者优先)。 3.存储管理 地址的静态重定位和动态重定位,单一连续区存储管理,固定分区存储管理,可变分区存储管理,空闲区的合并,分区的管理与组织方式(表格法、单链表法、双链表法),分页式存储管理,页表、快表及地址转换过程,内存块的分配与回收(存储分块表、位示图、单链表),虚拟存储器的概念,请求分页式存储管理,缺页与缺页中断位,缺页中断与页面淘汰,页面淘汰算法(先进先出、最近最久未用、最近最少用、最优),页面走向,缺页中断率,抖动,异常现象。 4.设备管理 计算机设备的分类(基于从属关系、基于分配特性、基于工作特性),记录间隙,设备管理的目标与功能,输入/输出的处理步骤,设备管理的数据结构(SDT、DCB、IVT),独享设备的分配,共享磁盘的调度算法(先来先服务、最短查找时间优先、电梯、单向扫描),设备控制器,数据传输的方式(循环测试、中断、直接存储器存取、通道),I/O的缓冲技术(单缓冲、双缓冲、多缓冲、缓冲池),虚拟设备,SPOOLing技术。 5.文件管理 文件,文件系统,文件的逻辑结构(流式文件、记录式文件),文件的物理结构(连续文件、串联文件、索引文件),文件的存取(顺序、随机),磁盘存储空间的管理(位示图、空闲区表、空闲块链),文件控制块(FCB),目录的层次结构(一级目录,二级目录、树型),主目录,根目录,绝对路径,相对路径,按名存取的实现,文件共享,文件保护,文件上的基本操作。 6.进程间的制约关系 与时间有关的错误,资源竞争——互斥,协同工作——同步,信号量,信号量上的P、V操作,用P、V操作实现互斥,用P、V操作实现同步,用P、V操作实现资源分配,死锁,死锁产生的必要条件,死锁的预防,死锁的避免,死锁的检测与恢复,银行家算法,进程间的高级通信。 7.操作系统实例分析 Windows操作系统,Linux操作系统,MS-DOS操作系统。
目 录 第1章 不间断电源方案设计 4 1.1 概述 4 1.2 供电安全技术 4 1.3 机房供电设计 5 1.4 山特3C3系列 UPS产品介绍 6 1.4.1 产品外观 6 1.4.2 山特3C3 UPS产品性能………………………………………………………………………6 1.4.3 山特3C3系列 UPS技术参数 11 1.5 山特UPS的监控软件及远程监控: 12 1.5.1 山特WINPOWER智能监控软件 12 1.5.2 山特WebPower卡(远程监控卡):(选配) 14 1.5.3 山特AS400卡(选配) 15 1.5.4 山特CMC卡(选配) 16 1.6 山特STK3C3-6KVA报价 17 不间断电源方案设计 1 概述 机房供配电系统一般由3部分组成—计算机设备供电系统、机房辅助设备供电系统与备 用供电系统。机房供配电系统是否合适直接决定计算机设备能否正常运行。各类计算机 对机房供配电系统虽有不同的标准,但都不外是不间断供电电源,电网电压、频率稳定 ,无噪声信号,抗干扰性强,具有可靠的监控或检测保护系统等几个方面。针对以上几 方面国家标准《计算站场地技术条件》作如下规定。 计算机机房电源应采用电网频率50HZ,电压380/220V,采用三相五线制(或三相四线 制)/单相三线制供电,其电源波动范围不可超出表格所示值。 "指标级别 "A级 "B级 "C级 " "项 目 " " " " "电压变动(%) "-5~+5 "-10~+7 "-15~+10 " "周波变化(HZ) "-0.2~+0.2 "-0.5~+0.5 "-1~+1 " 可根据计算机用途采用以下几种供电方式: "级 别 "一类供电 "二类供电 "三类供电 " "项 目 " " " " "供电方式 "需建立不停电供电系统"需建立带备用的供电系"按一般用户供电考" " " "统 "虑 " 2 供电安全技术 不间断供电 如机房供电系统突然断电会带来严重后果,因此必须保证机房的不间断供电,最低要 求也应做到突然停电时尚有足够的时间对机上工作做出适当处理,将影响与损失降到最 小程度。实现机房的不间断供电手段有三:一是市电双线供电,二是市电专线与柴油机 发电机组供电,三是在机房配备不间断电源UPS。在实际应用中,一、二级负荷用户多采 用双路供电或市电——自备发电机组加UPS的方式保证机房的连续供电。三级负荷的一般用 户则仅在机房配置UPS以保证突然停电时有足够的时间对机上工作作出适当处理后关机。 静电与电磁的影响及防护措施 静电与电磁波是干扰机房设备正常运行影响机房工作人员身心健康的两大因素。 静电对机房设备有危害在于当设备上聚集了大量静电荷时,会导致磁盘读写错误以及 损坏磁头,划伤磁盘,烧毁MPS、FET(场效应管)管半导体器件。 电磁场对机房的干扰主要表现为使电子电路的噪声增大,使计算机及磁性媒体及记录 设备的输入输出数据流产生错误,导致设备误动作或数据丢失,甚至使计算机完全瘫痪 。电磁场对人体的影响则是干扰人体磁场,导致人体某些机能紊乱,使工作人员的精神 及健康状况下降。 漏电与触电 漏电与触电对机房设备及工作人员危害的连带性,要求我们对漏电与触电的防护应进 行综合考虑。防漏电主要是加强对环境温度与湿度的控制,加强对设备过载过流的监测 ,设置合理的接地系统、加设漏电监测及保护动作装置或系统,这些也是预防触电事故 的主要措施。 接地与屏蔽技术 机房接地系统主要有工作接地与保护接地两大类。工作接地有交流工作接地与直流接 地(逻辑接地);保护接地有安全保护接地、防雷接地、防静电接地、屏蔽接地等。 3 机房供电设计 XXXX检察院目前机房是中心机房,我们建议对机房作基本装修、铺设防静电地板,基 本市电供配电系统、安全防雷以后,并配置二台山特6KVA的4小时在线不间断电源,二台 并机运行,这样可以提供更高的可靠性。由于机房设在二楼,具体在实施不间断电源线 路配线时,提供多路的输出,其中一路接二楼机房,一路接X楼的视频会议室,一路接x xxxxxxxxxx。 4 山特3C3系列 UPS产品介绍 1 产品外观 3C3 UPS外观 山特城堡并联冗余系列 3C3 UPS, 采用双转换纯在线式的架构,是有效解决所有电源问题的最佳架构设计。该架构能够有 效阻隔异常电源对负载的冲击,同时保证输出电源的稳定、精密、可靠,让负载安全的 运行。该产品采用数字化控制技术,能实现并联扩容和并联冗余的功能,为用户提供电 源规划的弹性和更安全的保障。 2 山特3C3 UPS产品性能 1 双转换纯在线3相输入、3相输出UPS 山特3C3系列采用的是双转换的纯在线架构,如图1,经过滤波器后,再经PFC ( Power Factor Correction,功率因素修正器 ) 将交流转换成直流,
第1章 操作系统概述 1 1.1 认识操作系统 1 1.1.1 从使用者角度 1 1.1.2 从程序开发者角度 2 1.1.3 从操作系统在整个计算机系统中所处位置 2 1.1.4 从操作系统设计者的角度 3 1.2 操作系统的发展 4 1.2.1 操作系统的演变 4 1.2.2 硬件的发展轨迹 5 1.2.3 软件的轨迹 6 1.2.4 单内核与微内核操作系统 7 1.3 开放源代码的Unix/Linux操作系统 8 1.3.1 Unix的诞生和发展 8 1.3.2 Linux的诞生 9 1.3.3 操作系统标准POSIX 9 1.3.4 GNU和Linux 9 1.3.5 Linux的开发模式 10 1.4 Linux内核 10 1.4.1 Linux内核的位置 10 1.4.2 Linux内核的作用 11 1.4.3 Linux内核子系统 11 1.5 Linux内核源代码 13 1.5.1 多版本的内核源代码 13 1.5.2 Linux内核源代码的结构 13 1.5.3 Linux内核源代码分析工具 14 习题1 15 第2章 内存寻址 17 2.1 内存寻址简介 17 2.1.1 Intel x86 CPU寻址方式的演变 18 2.1.2 IA32寄存器简介 19 2.1.3 物理地址、虚拟地址及线性地址 21 2.2 分段机制 22 2.2.1 地址转换及保护 24 2.2.2 Linux中的段 24 2.3 分页机制 25 2.3.1 页与页表 25 2.3.2 线性地址到物理地址的转换 28 2.3.3 分页示例 28 2.3.4 页面高速缓存(cache) 29 2.3.5 Linux中的分页机制 30 2.4 Linux中的汇编语言 31 2.4.1 AT&T与Intel汇编语言的比较 31 2.4.2 AT&T汇编语言的相关知识 32 2.5 Linux系统地址映射示例 33 习题2 35 第3章 进程 37 3.1 进程介绍 37 3.1.1 程序和进程 37 3.1.2 进程的层次结构 38 3.1.3 进程状态 39 3.1.4 进程实例 40 3.2 进程控制块 41 3.2.1 进程状态 42 3.2.2 进程标识符 43 3.2.3 进程之间的亲属关系 43 3.2.4 进程控制块的存放 44 3.3 进程的组织方式 45 3.3.1 进程链表 45 3.3.2 散列表 46 3.3.3 可运行队列 47 3.3.4 等待队列 47 3.4 进程调度 48 3.4.1 基本原理 48 3.4.2 时间片 50 3.4.3 Linux进程调度时机 50 3.4.4 进程调度的依据 51 3.4.5 调度函数schedule()的实现 52 3.5 进程的创建 54 3.5.1 创建进程 55 3.5.2 线程及其创建 56 3.6 与进程相关的系统调用及其应用 58 3.6.1 fork系统调用 58 3.6.2 exec系统调用 59 3.6.3 wait系统调用 60 3.6.4 exit系统调用 62 3.6.5 进程的一生 63 3.7 与调度相关的系统调用及应用 63 习题3 65 第4章 内存管理 67 4.1 Linux的内存管理概述 67 4.1.1 虚拟内存、内核空间和用户空间 67 4.1.2 虚拟内存实现机制间的关系 69 4.2 进程用户空间的管理 70 4.2.1 进程用户空间的描述 71 4.2.2 进程用户空间的创建 74 4.2.3 虚存映射 76 4.2.4 进程的虚存区示例 76 4.2.5 与用户空间相关的系统调用 78 4.3 请页机制 79 4.3.1 缺页异常处理程序 79 4.3.2 请求调页 81 4.3.3 写时复制 83 4.4 物理内存的分配与回收 83 4.4.1 伙伴算法 85 4.4.2 物理页面的分配 86 4.4.3 物理页面的回收 88 4.4.4 slab分配模式 89 4.4.5 内核空间非连续内存区的分配 93 4.5 交换机制 95 4.5.1 交换的基本原理 95 4.5.2 页面交换守护进程kswapd 99 4.6 内存管理实例 99 4.6.1 相关背景知识 100 4.6.2 代码体系结构介绍 100 4.6.3 实现步骤 103 4.6.4 程序代码 103 习题4 108 第5章 中断和异常 110 5.1 中断的基本知识 110 5.1.1 中断向量 110 5.1.2 外设可屏蔽中断 111 5.1.3 异常及非屏蔽中断 112 5.1.4 中断描述符表 112 5.1.5 相关汇编指令 113 5.2 中断描述符表的初始化 114 5.2.1 IDT表项的设置 114 5.2.2 对陷阱门和系统门的初始化 115 5.2.3 中断门的设置 116 5.3 中断处理 116 5.3.1 中断和异常的硬件处理 116 5.3.2 中断请求队列的建立 117 5.3.3 中断处理程序的执行 119 5.3.4 从中断返回 121 5.4 中断的下半部处理机制 121 5.4.1 为什么把中断分为两部分来处理 122 5.4.2 小任务机制 122 5.4.3 下半部 124 5.4.4 任务队列 125 5.5 中断应用——时钟中断 125 5.5.1 时钟 125 5.5.2 时钟运作机制 126 5.5.3 Linux的时间系统 127 5.5.4 时钟中断处理程序 128 5.5.5 时钟中断的下半部处理 129 5.5.6 定时器及其应用 129 习题5 132 第6章 系统调用 133 6.1 系统调用与应用编程接口、系统命令、内核函数的关系 133 6.1.1 系统调用与API 133 6.1.2 系统调用与系统命令 134 6.1.3 系统调用与内核函数 134 6.2 系统调用处理程序及服务例程 135 6.2.1 初始化系统调用 136 6.2.2 system_call()函数 136 6.2.3 参数传递 137 6.2.4 跟踪系统调用的执行 139 6.3 封装例程 140 6.4 添加新系统调用 141 6.5 实例——利用系统调用实现一个调用日志收集系统 143 6.5.1 代码体系结构 143 6.5.2 把代码集成到内核中 146 6.5.3 实现步骤 148 习题6 148 第7章 内核中的同步 149 7.1 临界区和竞争状态 149 7.1.1 临界区举例 149 7.1.2 共享队列和加锁 150 7.1.3 确定保护对象 151 7.1.4 死锁 152 7.1.5 并发执行的原因 153 7.2 内核同步方法 153 7.2.1 原子操作 153 7.2.2 自旋锁 155 7.2.3 信号量 156 7.3 并发控制实例 157 7.3.1 内核任务及其并发关系 158 7.3.2 实现机制 158 7.3.3 关键代码解释 162 7.3.4 实现步骤 163 习题7 164 第8章 文件系统 165 8.1 Linux文件系统基础 165 8.1.1 Linux文件结构 165 8.1.2 Linux文件系统 166 8.1.3 文件类型 167 8.1.4 文件访问权限 168 8.2 虚拟文件系统 168 8.2.1 虚拟文件系统的引入 168 8.2.2 VFS中的数据结构 170 8.2.3 VFS超级块数据结构 171 8.2.4 VFS的索引节点 173 8.2.5 目录项对象 174 8.2.6 与进程相关的文件结构 176 8.2.7 主要的数据结构之间的关系 179 8.3 文件系统的注册、安装与卸载 180 8.3.1 文件系统的注册和注销 180 8.3.2 文件系统的安装 181 8.3.3 文件系统的卸载 183 8.4 页缓冲区 183 8.4.1 address_space对象 183 8.4.2 address_space对象的操作函数表 184 8.5 文件的打开与读写 185 8.5.1 打开文件 185 8.5.2 读写文件 187 8.6 编写一个文件系统 189 8.6.1 Linux文件系统的实现要素 189 8.6.2 什么是romfs文件系统 191 8.6.3 romfs文件系统的布局与文件结构 191 8.6.4 具体实现的对象 192 习题8 195 第9章 设备驱动 196 9.1 概述 196 9.2 设备驱动程序基础 198 9.2.1 I/O端口 199 9.2.2 设备文件 200 9.2.3 中断处理 201 9.2.4 设备驱动程序框架 203 9.3 字符设备驱动程序 204 9.3.1 字符设备驱动程序的注册 204 9.3.2 简单的字符设备驱动程序示例 205 9.4 块设备驱动程序 208 9.4.1 块设备驱动程序的注册 209 9.4.2 块设备请求 212 习题9 215 附录A 内核中的链表 216 A.1 链表数据结构简介 216 A.2 内核链表数据结构的定义及初始化 217 A.3 操作链表的接口 218 A.4 遍历链表 219 附录B 内核模块 221 B.1 什么是模块 221 B.2 编写一个简单的模块 221 B.3 模块编程的基础知识 222 B.4 模块的编译 224 B.5 模块实用程序modutils 226 附录C Linux内核编译 228 C.1 内核简介 228 C.2 为什么重新编译内核 228 C.3 内核编译模式 229 C.4 新版本内核的获取和更新 229 C.5 内核编译 230 C.6 修改并重启管理器 232 附录D Linux编程基础(C语言环境) 233 D.1 Linux编程常识 233 D.1.1 相关标准(ANSI C、POSIX、SVID、XPG) 233 D.1.2 函数库和系统调用 234 D.1.3 在线文档(man、info、HOWTO) 235 D.1.4 C语言编程风格 237 D.2 Linux上的C/C++编译器和调试器 238 D.2.1 运行gcc/egcs 238 D.2.2 gcc/egcs的主要选项 240 D.2.3 gdb简介 240 D.2.4 gdb的常用命令 241 D.2.5 gdb使用示例 242 D.3 GNU make和makefile 243 D.3.1 GNU make 243 D.3.2 makefile的基本结构 243 D.3.3 makefile的变量 244 D.3.4 GNU make的主要预定义变量 245 D.3.5 GNU make的隐含规则 245 D.3.6 运行make 246
本书是关于计算机科学与工程领域的基础性研究科目之一——数据结构与算法的专著。 本书在简要回顾了基本的C++ 程序设计概念的基础上,全面系统地介绍了队列、堆栈、树、图等基本数据结构,以及贪婪算法、分而治之算法、分枝定界算法等多种算法设计方法,为数据结构与算法的继续学习和研究奠定了一个坚实的基础。更为可贵的是,本书不仅仅介绍了理论知识,还提供了50多个应用实例及600多道练习题。 本书内容广博权威,结构清晰合理,是一本全新的有关数据结构与算法的教材,对于计算机科学与工程领域的从业人员也是一本很好的参考书。 目 录 译者序 前言 第一部分 预备知识 第1章 C++程序设计 1 1.1 引言 1 1.2 函数与参数 2 1.2.1 传值参数 2 1.2.2 模板函数 3 1.2.3 引用参数 3 1.2.4 常量引用参数 4 1.2.5 返回值 4 1.2.6 递归函数 5 1.3 动态存储分配 9 1.3.1 操作符new 9 1.3.2 一维数组 9 1.3.3 异常处理 10 1.3.4 操作符delete 10 1.3.5 二维数组 10 1.4 类 13 1.4.1 类Currency 13 1.4.2 使用不同的描述方法 18 1.4.3 操作符重载 20 1.4.4 引发异常 22 1.4.5 友元和保护类成员 23 1.4.6 增加#ifndef, #define和#endif语句 24 1.5 测试与调试 24 1.5.1 什么是测试 24 1.5.2 设计测试数据 26 1.5.3 调试 28 1.6 参考及推荐读物 29 第2章 程序性能 30 2.1 引言 30 2.2 空间复杂性 31 2.2.1 空间复杂性的组成 31 2.2.2 举例 35 2.3 时间复杂性 37 2.3.1 时间复杂性的组成 37 2.3.2 操作计数 37 2.3.3 执行步数 44 2.4 渐进符号(O、 健?、 o) 55 2.4.1 大写O符号 56 2.4.2 椒?58 2.4.3 符号 59 2.4.4 小写o符号 60 2.4.5 特性 60 2.4.6 复杂性分析举例 61 2.5 实际复杂性 66 2.6 性能测量 68 2.6.1 选择实例的大小 69 2.6.2 设计测试数据 69 2.6.3 进行实验 69 2.7 参考及推荐读物 74 第二部分 数据结构 第3章 数据描述 75 3.1 引言 75 3.2 线性表 76 3.3 公式化描述 77 3.3.1 基本概念 77 3.3.2 异常类NoMem 79 3.3.3 操作 79 3.3.4 评价 83 3.4 链表描述 86 3.4.1 类ChainNode 和Chain 86 3.4.2 操作 88 3.4.3 扩充类Chain 91 3.4.4 链表遍历器类 92 3.4.5 循环链表 93 3.4.6 与公式化描述方法的比较 94 3.4.7 双向链表 95 3.4.8 小结 96 3.5 间接寻址 99 3.5.1 基本概念 99 3.5.2 操作 100 3.6 模拟指针 102 3.6.1 SimSpace的操作 103 3.6.2 采用模拟指针的链表 106 3.7 描述方法的比较 110 3.8 应用 111 3.8.1 箱子排序 111 3.8.2 基数排序 116 3.8.3 等价类 117 3.8.4 凸包 122 3.9 参考及推荐读物 127 第4章 数组和矩阵 128 4.1 数组 128 4.1.1 抽象数据类型 128 4.1.2 C++数组 129 4.1.3 行主映射和列主映射 129 4.1.4 类Array1D 131 4.1.5 类Array2D 133 4.2 矩阵 137 4.2.1 定义和操作 137 4.2.2 类Matrix 138 4.3 特殊矩阵 141 4.3.1 定义和应用 141 4.3.2 对角矩阵 143 4.3.3 三对角矩阵 144 4.3.4 三角矩阵 145 4.3.5 对称矩阵 146 4.4 稀疏矩阵 149 4.4.1 基本概念 149 4.4.2 数组描述 149 4.4.3 链表描述 154 第5章 堆栈 161 5.1 抽象数据类型 161 5.2 派生类和继承 162 5.3 公式化描述 163 5.3.1 Stack的效率 164 5.3.2 自定义Stack 164 5.4 链表描述 166 5.5 应用 169 5.5.1 括号匹配 169 5.5.2 汉诺塔 170 5.5.3 火车车厢重排 172 5.5.4 开关盒布线 176 5.5.5 离线等价类问题 178 5.5.6 迷宫老鼠 180 5.6 参考及推荐读物 188 第6章 队列 189 6.1 抽象数据类型 189 6.2 公式化描述 190 6.3 链表描述 194 6.4 应用 197 6.4.1 火车车厢重排 197 6.4.2 电路布线 201 6.4.3 识别图元 204 6.4.4 工厂仿真 206 6.5 参考及推荐读物 217 第7章 跳表和散列 218 7.1 字典 218 7.2 线性表描述 219 7.3 跳表描述 222 7.3.1 理想情况 222 7.3.2 插入和删除 223 7.3.3 级的分配 224 7.3.4 类SkipNode 224 7.3.5 类SkipList 225 7.3.6 复杂性 229 7.4 散列表描述 229 7.4.1 理想散列 229 7.4.2 线性开型寻址散列 230 7.4.3 链表散列 234 7.5 应用——文本压缩 238 7.5.1 LZW压缩 239 7.5.2 LZW压缩的实现 239 7.5.3 LZW解压缩 243 7.5.4 LZW解压缩的实现 243 7.6 参考及推荐读物 247 第8章 二叉树和其他树 248 8.1 树 248 8.2 二叉树 251 8.3 二叉树的特性 252 8.4 二叉树描述 253 8.4.1 公式化描述 253 8.4.2 链表描述 254 8.5 二叉树常用操作 256 8.6 二叉树遍历 256 8.7 抽象数据类型BinaryTree 259 8.8 类BinaryTree 260 8.9 抽象数据类型及类的扩充 263 8.9.1 输出 263 8.9.2 删除 264 8.9.3 计算高度 264 8.9.4 统计节点数 265 8.10 应用 265 8.10.1 设置信号放大器 265 8.10.2 在线等价类 268 8.11 参考及推荐读物 275 第9章 优先队列 276 9.1 引言 276 9.2 线性表 277 9.3 堆 278 9.3.1 定义 278 9.3.2 最大堆的插入 279 9.3.3 最大堆的删除 279 9.3.4 最大堆的初始化 280 9.3.5 类MaxHeap 281 9.4 左高树 285 9.4.1 高度与宽度优先的最大及最小 左高树 285 9.4.2 最大HBLT的插入 287 9.4.3 最大HBLT的删除 287 9.4.4 合并两棵最大HBLT 287 9.4.5 初始化最大HBLT 289 9.4.6 类MaxHBLT 289 9.5 应用 293 9.5.1 堆排序 293 9.5.2 机器调度 294 9.5.3 霍夫曼编码 297 9.6 参考及推荐读物 302 第10章 竞?303 10.1 引言 303 10.2 抽象数据类型WinnerTree 306 10.3 类WinnerTree 307 10.3.1 定义 307 10.3.2 类定义 307 10.3.3 构造函数、析构函数及Winner 函数 308 10.3.4 初始化赢者树 308 10.3.5 重新组织比赛 310 10.4 输者树 311 10.5 应用 312 10.5.1 用最先匹配法求解箱子装载 问题 312 10.5.2 用相邻匹配法求解箱子装载 问题 316 第11章 搜索树 319 11.1 二叉搜索树 320 11.1.1 基本概念 320 11.1.2 抽象数据类型BSTree和 IndexedBSTree 321 11.1.3 类BSTree 322 11.1.4 搜索 322 11.1.5 插入 323 11.1.6 删除 324 11.1.7 类DBSTree 326 11.1.8 二叉搜索树的高度 327 11.2 AVL树 328 11.2.1 基本概念 328 11.2.2 AVL树的高度 328 11.2.3 AVL树的描述 329 11.2.4 AVL搜索树的搜索 329 11.2.5 AVL搜索树的插入 329 11.2.6 AVL搜索树的删除 332 11.3 红-黑树 334 11.3.1 基本概念 334 11.3.2 红-黑树的描述 336 11.3.3 红-黑树的搜索 336 11.3.4 红-黑树的插入 336 11.3.5 红-黑树的删除 339 11.3.6 实现细节的考虑及复杂性分析 343 11.4 B-树 344 11.4.1 索引顺序访问方法 344 11.4.2 m 叉搜索树 345 11.4.3 m 序B-树 346 11.4.4 B-树的高度 347 11.4.5 B-树的搜索 348 11.4.6 B-树的插入 348 11.4.7 B-树的删除 350 11.4.8 节点结构 353 11.5 应用 354 11.5.1 直方图 354 11.5.2 用最优匹配法求解箱子装载 问题 357 11.5.3 交叉分布 359 11.6 参考及推荐读物 363 第12章 图 365 12.1 基本概念 365 12.2 应用 366 12.3 特性 368 12.4 抽象数据类型Graph和Digraph 370 12.5 无向图和有向图的描述 371 12.5.1 邻接矩阵 371 12.5.2 邻接压缩表 373 12.5.3 邻接链表 374 12.6 网络描述 375 12.7 类定义 376 12.7.1 不同的类 376 12.7.2 邻接矩阵类 377 12.7.3 扩充Chain类 380 12.7.4 类LinkedBase 381 12.7.5 链接类 382 12.8 图的遍历 386 12.8.1 基本概念 386 12.8.2 邻接矩阵的遍历函数 387 12.8.3 邻接链表的遍历函数 388 12.9 语言特性 389 12.9.1 虚函数和多态性 389 12.9.2 纯虚函数和抽象类 391 12.9.3 虚基类 391 12.9.4 抽象类和抽象数据类型 393 12.10 图的搜索算法 394 12.10.1 宽度优先搜索 394 12.10.2 类Network 395 12.10.3 BFS的实现 395 12.10.4 BFS的复杂性分析 396 12.10.5 深度优先搜索 397 12.11 应用 399 12.11.1 寻找路径 399 12.11.2 连通图及其构件 400 12.11.3 生成树 402 第三部分 算法设计方法 第13章 贪婪算法 405 13.1 最优化问题 405 13.2 算法思想 406 13.3 应用 409 13.3.1 货箱装船 409 13.3.2 0/1背包问题 410 13.3.3 拓扑排序 412 13.3.4 二分覆盖 415 13.3.5 单源最短路径 421 13.3.6 最小耗费生成树 424 13.4 参考及推荐读物 433 第14章 分而治之算法 434 14.1 算法思想 434 14.2 应用 440 14.2.1 残缺棋盘 440 14.2.2 归并排序 443 14.2.3 快速排序 447 14.2.4 选择 452 14.2.5 距离最近的点对 454 14.3 解递归方程 462 14.4 复杂性的下限 463 14.4.1 最小最大问题的下限 464 14.4.2 排序算法的下限 465 第15章 动态规划 467 15.1 算法思想 467 15.2 应用 469 15.2.1 0/1背包问题 469 15.2.2 图像压缩 471 15.2.3 矩阵乘法链 476 15.2.4 最短路径 480 15.2.5 网络的无交叉子集 483 15.2.6 元件折叠 486 15.3 参考及推荐读物 491 第16章 回溯 492 16.1 算法思想 492 16.2 应用 496 16.2.1 货箱装船 496 16.2.2 0/1背包问题 503 16.2.3 最大完备子图 506 16.2.4 旅行商问题 508 16.2.5 电路板排列 510 第17章 分枝定界 516 17.1 算法思想 516 17.2 应用 519 17.2.1 货箱装船 519 17.2.2 0/1背包问题 526 17.2.3 最大完备子图 528 17.2.4 旅行商问题 529 17.2.5 电路板排列 532

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值