前言
本文总结了2020年至2022年暨南大学 848 操作系统 “ 八、简答题 ” 的题目和答案,并对部分题目进行知识点补充和拓展。
由于找不到 2021 和 2022 年的 848 真题答案,所以本文的 2021 和 2022 简答题的答案是博主作答的,有错误的地方麻烦大家指正。
可搭配以下链接一起学习:
【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第一章
【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第二章
【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第三章
【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第四章
【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第五章
【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第六章
【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第七章
【考研】《数据结构》知识点总结.pdf_其它文档类资源-CSDN文库
【考研】830 + 848 暨大2012-2022真题易混易错题总结(一)
目录
一、2020(每小题5分,共25分)
1. 什么是文件目录、目录文件,各起什么作用?
解:(1)文件目录:为实现 “ 按名存取 ”,必须建立文件名和文件物理位置之间的映射关系,这种文件的索引称为文件目录。
(2)目录文件:将文件目录以文件的形式保存在外存空间,这个文件就被称为目录文件。
(3)作用:文件目录实现了 “ 按名存取 ”,提高了计算机检索速度,缩短了文件访问时间,同时解决了重名问题,目录文件实现了文件目录的管理。
2. 多级树形目录的文件系统,怎样才能提高查找文件的速度?
解:(1)为避免每访问一个文件,都要使用从树根开始直到树叶(数据文件)的全路径名,可为每个进程设置一个当前目录 (工作目录),进程对各文件的访问都相对于当前目录而进行。
(2)Hash 方法,系统利用用户提供的文件名并将它转换为文件目录的索引值,再利用该索引值到目录中去查找,这将提高检索速度。
3. 多线程系统与传统多进程系统相比有哪些优点?
解:(1)引入线程之后,不仅进程之间可以并发运行,同一进程中的线程也可以并发运行,提高了系统的并发性。
(2)线程是程序执行的最小单位,进程是资源分配的最小单位,线程只拥有运行必不可少的资源,因此开销小,切换速度也比进程快,提高了系统的效率。
(3)由于进程是互相独立的,因此进程的通信机制相对比较复杂,而同一进程的线程之间共享资源,所以通信更方便。
4. 分页存储管理和分段存储管理的主要区别有哪些?
解:(1)页是信息的物理单位,分页完全是系统的行为,对用户是不可见的,段是信息的逻辑单位,分段的目的是更好地满足用户需要。
(2)页的大小固定且由系统决定,段的大小不固定,取决于用户所编的程序。
(3)分页的用户程序地址空间是一维的, 是系统的行为,分段的用户地址空间是二维的,是用户的行为。
5. 用伪代码或文字描述 fork() 系统调用是如何创建进程的。
解:(1)申请空白 PCB。
(2)为新进程分派资源。
(3)初始化 PCB。
(4)将新进程插入就绪队列。
二、2021(每小题5分,共25分)
1. 什么是信号(Signal),有什么用途?
答: 信号是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常,即信号用于传递消息。
2. 实时操作系统与分时操作系统的本质区别是什么?
答: 实时操作系统与分时操作系统的本质区别是在于及时性。实时系统能对控制对象做出及时反应,可靠性高,响应及时。分时系统可让多个用户同时使用计算机,每个用户的请求可以得到响应,但在及时性方面远不如实时系统。
【这一题答案博主不确定,有答案的小伙伴可以在评论区说一下】
3. 多线程系统与传统多进程系统相比有哪些优点?
答: (1)调度开销相对较小。
(2)程序逻辑和控制方式简单,且无需跨进程边界。
(3)所有线程可以直接共享内存和变量等。
(4)线程方式消耗的总资源比进程方式好;
【补充】参考:进程、线程、多进程、多线程的优缺点和区别_月夜星星雨的博客-CSDN博客
对资源的管理和保护要求高,不限制开销和效率时,使用多进程。
要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。
4. 为什么说多级反馈队列调度算法能较好地满足各类用户的需求?
答: 多级反馈队列调度算法,从其实现思想可知,其能满足各种类型用户的需要。
多级反馈队列调度算法,其实现思想:
系统中设置多个就绪队列,每个队列对应一个优先级,第一个队列的优先级最高,第二个队列的优先级次之,以下各个队列的优先级逐个降低。
各就绪队列中进程的运行时间片不同,高优先级队列的时间片小,低优先级队列的时间片大,如从高到低依次加倍。
新进程进入系统后,先放入第一队列的末尾,各队列按 FCFS 方式排队,如某个进程在相应时间片内没有完成工作,则把它转到下一级队列的末尾。
系统先运行第一队列中的进程,第一队列为空后,才运行第二队列中的进程,依此类推,最后一个队列(最低级)中的进程采用时间片轮转的方式进行调度。
5. 分析并给出下面程序的输出结果?
void main(){
printf("1111\n");
fork();
printf("2222\n");
fork();
printf("3333\n");
fork();
printf("4444\n");
}
解:调用 fork() 函数会产生了一个新的进程,子进程复制了父进程的运行环境。
所以,程序的输出结果如下:
//答案
1111
2222
2222
3333
3333
3333
3333
4444
4444
4444
4444
4444
4444
4444
4444
/*
在 Linux 环境下终端运行 gcc test.c -o test
再运行 ./test
即得答案
*/
#include<unistd.h>/*#包含<unistd.h>*/
#include<sys/types.h>/*#包含<sys/types.h>*/
#include<stdio.h>
void main(){
printf("1111\n");
fork();
printf("2222\n");
fork();
printf("3333\n");
fork();
printf("4444\n");
}
【拓展】要区分有无 "\n" 的情况。当无 "\n" 时,printf 语句是先放在 buffer 里的。在 fork 的时候,缓存就被复制到了子进程空间。
大家可以观察对比一下以下几张图:
三、2022(每小题5分,共25分)
1. 使用文件前为什么要先打开文件?简述打开文件/home/user01/myfile 的过程?
答: (1)原因:在使用文件之前,进程必须打开相应文件。打开文件的目的是把文件属性和磁盘地址表等信息装入内存,以便后续系统调用能够快速存取该文件。
【补充】
使用文件前通过 open() 系统调用,来根据文件名打开一个文件,再返回该文件的文件描述符。文件打开后,进程便可以根据文件描述符 fd 进行其他操作,比如读,写,关闭等操作。
(2)打开文件/home/user01/myfile 的过程:
- 根据给定的文件名查找文件目录。根据/home/user01/的路径找到文件 myfile ,把相应的文件控制块调入内存的活动文件控制区。
- 检查打开文件的合法性。若用户指定的打开文件之后的操作与文件创建时规定的存取权限不符,则不能打开文件,返回不成功标志。若权限相符,则建立文件系统内部控制结构之间的通路联系,返回相应的文件描述字 fd 。
2. 页式虚拟存储管理采用位示图技术,设主存有 16384 块,采用 32 位的 512 个字作为位示图。若块号、字号和位号(从高位到低位)分别从 1. 0. 0 开始。试计算:5998 块对应的字号和位号;198 字的 20 位对应于哪一块?
答: 5998 块对应 186 字的 13 位。198 字的 20 位对应 6389 块。
(1)第一种解法:
由于块号从 1 开始,所以 5998 块对应第 5998 块。
因为字长 32 位(每行的位数),即每个字可记录 32 个物理块的使用情况。
因为 5998 / 32 = 187.4375,即需要 188 个字长,由于字号从 0 开始,所以为 187 字。
到第 186 个字为止,共保存了(186 + 1) * 32 = 5984 块(0 - 5983),
因为字位都从 0 开始,5984 是 0,5985 是 1,... ,5997 是 13,即第 5998 块。
计数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 |
所以, 5998 块对应 187 字 13 位。
第二种解法:
根据公式,有行号 i = ( b - 1 ) DIV n = 5997 / 32 = 187,
列号 j = ( b - 1 ) MOD n = 5997 % 32 = 13
(逆推:盘块号 b = n * i + j + 1 = 32 * 187 + 13 + 1 = 5998)
所以,5998 块对应 187 字 13 位。
(2)因为字号和位号都是从 0 开始,块号从 1 开始,
所以,198 字的 20 位对应 198 * 32 + 20 + 1 = 6357 块。
【位示图法公式】
b,i,j 分别从 1,1,1 开始,n 是位数:
(1)盘块的分配:盘块号 b = n(i - 1) + j
(2)盘块的回收:i = (b - 1) DIV n + 1, j = (b - 1) MOD n + 1 ( i 行 j 列)
b,i,j 分别从 1,0,0 开始,n 是位数:
(1)盘块的分配:盘块号 b = n * i + j + 1
(2)盘块的回收:行号 i = ( b - 1 ) DIV n,列号 j = ( b - 1 ) MOD n
【这一题的知识点一定要搞清楚,很容易弄混,这类的知识点大家可以再多做几遍】
3. 进程控制块包含哪些类型的信息?进程控制块的信息太多内存放不下应该怎样解决?
答:PCB 包含了进程描述信息、进程控制和管理信息、资源分配清单和处理机相关信息。
进程描述信息 | 进程控制和管理信息 | 资源分配清单 | 处理机相关信息 |
进程标识符(PID) | 进程当前状态 | 代码段指针 | 通用寄存器值 |
用户标识符(UID) | 进程优先级 | 数据段指针 | 地址寄存器值 |
代码运行入口地址 | 堆栈段指针 | 控制寄存器值 | |
程序的外存地址 | 文件描述符 | 标志寄存器值 | |
进入内存的时间 | 键盘 | 状态字 | |
处理机占用时间 | 鼠标 | ||
信号量使用 |
当 PCB 的信息太多,内存放不下时,可利用虚拟内存机制和缺页中断机构等进行解决。
4. 操作系统中为什么要引入缓冲技术,缓冲池技术有什么优点?
答:(本题考查 “ 第六章、设备管理 ” 中缓冲技术的知识点)
引入缓冲技术的主要目的:
(1)缓和 CPU 与 I/O 设备间速度不匹配的矛盾。
(2)提高 CPU 与 I/O 设备之间的并行性。
(3) 减少对 CPU 的中断次数,放宽 CPU 对中断响应时间的要求。
缓冲池技术的优点:
(1)增强系统的处理能力。
(2)提高资源的利用率。
5. 分析下面的程序输出多少个 A 和 B?
int main(void)
{
int i;
for (i = 0; i < 2; i++)
{
fork();
printf("A");
}
printf("B\n");
}
解:输出了 8 个 A 和 4 个 B。
首先了解,fork() 用于创建一个进程(子进程),所创建的进程复制父进程的代码段、数据段和堆/栈等所有用户空间信息。
在本题中,父进程 for 函数中,指令执行 for 循环中,
i = 0,fork 执行完后,系统中出现 2 个进程,分别是父进程1和子进程2。
i = 1,fork 执行完后,系统中出现 4 个进程,分别是父进程1下的父进程11和子进程12,还有子进程2下的父进程21和子进程22。
可看下表:
for | i = 0 | i = 1 |
父进程1 | 父进程11 | |
子进程12 | ||
子进程2 | 父进程21 | |
子进程22 |
对于上述程序,执行 printf(“A”); 语句时,把 “ A ” 放到了缓存中,并没有真正的输出。所以,在 fork 的时候,缓存被复制到了子进程空间。
所以,输出结果为 AABAABAABAAB。即总共有 8 个 A,4 个 B。
【补充】可用以下代码进行实验:
#include<unistd.h>/*#包含<unistd.h>*/
#include<sys/types.h>/*#包含<sys/types.h>*/
#include<stdio.h>
int main(void)
{
int i;
for (i = 0; i < 2; i++)
{
fork();
printf("A");
}
printf("B\n");
return 0;
}
四、fork() 知识点拓展
//程序一
#include<unistd.h>/*#??<unistd.h>*/
#include<sys/types.h>/*#??<sys/types.h>*/
#include<stdio.h>
void main()
{
pid_t fpid1, fpid2, fpid3; // 表示 fork 返回给当前进程的值
fpid1 = fork();
fpid2 = fork();
fpid3 = fork();
printf("pid1:%d , pid2:%d , pid3:%d\n",fpid1,fpid2,fpid3);
}
上面的程序运行后,一共将运行 8 个进程。
(这 8 个进程并没有严格的区分先后顺序,大家可自行测试)
fork() 系统调用是 Linux/Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值 > 0,则是父进程(返回值是子进程的pid)。(如果返回值为负数,则出现错误)
//程序二
#include<unistd.h>/*#??<unistd.h>*/
#include<sys/types.h>/*#??<sys/types.h>*/
#include<stdio.h>
void main(){
printf("i son/pa ppid pid fpid/n");
//ppid 指当前进程的父进程pid
//pid 指当前进程的pid,
//fpid 指 fork 返回给当前进程的值
for(int i = 0; i < 2; i++){
pid_t fpid = fork();
if(fpid == 0)
printf("%d child %4d %4d %4d/n",i, getppid(), getpid(), fpid);
else
printf("%d parent %4d %4d %4d/n",i, getppid(), getpid(), fpid);
}
}
for 函数 | 子进程 | 子子进程 |
(0, 父进程, 784, 1846, 1847) | (0, 子进程, 1846, 1847, 0) | |
(1, 父进程, 1846, 1847, 1849) | (1, 子进程, 1847, 1849, 0) | |
(1, 父进程, 784, 1846, 1848) | (1, 子进程, 1846, 1848, 0) |