操作系统概念(Operating System Concepts Ninth Edition恐龙书)第三章课后非编程题答案

CHAPTER 3

In this chapter we introduce the concepts of a process and concurrent execution;
These concepts are at the very heart of modern operating systems. A process is a
program in execution and is the unit of work in a modern time-sharing system. Such
a system consists of a collection of processes: Operating-system processes executing
system code and user processes executing user code. All these processes can
potentially execute concurrently, with the CPU (or CPUs) multiplexed among them. By
switching the CPU between processes, theoperating system can make the computer more
productive. We also introduce the notion of a thread (lightweight process) and
interprocess communication (IPC). Threads are discussed in more detail in Chapter 4.
在本章中,我们将介绍进程和并发执行的概念;这些概念是现代操作系统的核心。一个进程就是正在执行中
的程序,并且进程是现代分时系统的工作单元。这样的系统由一组进程组成: 操作系统进程执行用户代码
并且用户进程执行用户代码。所有这些进程都可以潜在并发执行,在它们之间多路复用CPUs.通过CPU在进
程间切换,操作系统可以让计算机更有生产力。我们也介绍了线程(轻量级线程概念)和进程间通信(IPC).
线程将在第四章中详细讨论


Exercises

3.1 Describe the differences among short-term, medium-term, and long-term scheduling.
描述短期调度、中期调度和短期调度的差异
Answer:
a. Short-term (CPU scheduler)—selects from jobs in memory those jobs that are
ready to execute and allocates the CPU to them.
a. 短期调用(CPU调用程序)–从内存中的作业中选择当前的将要准备执行的作业,并且分配CPU给
它们
b. Medium-term—used especially with time-sharing systems as an intermediate
scheduling level. A swapping scheme is implemented to remove partially run programs
from memory and reinstate them later to continue where they left off.
b. 中期调用特别在分时系统作为及时调度层使用。 实现一个交换方案来从内存中删除部分执行程序,
随后恢复他们,继续接着执行
c. Long-term (job scheduler)—determines which jobs are brought into memory for
processing.
The primary difference is in the frequency of their execution. The
short-term must select a new process quite often. Long-term is used much less often
since it handles placing jobs in the system and may wait a while for a job to finish
before it admits another one.
c. 长期调度程序(作业调度程序)–确定将那些作业放入内存中处理。
主要的区别在于它们的执行频率。短期调用必须经常选择新的进程.长期调度程序被使用的更少,因为它处理
在系统中放置作业,并且可能要等待作业完成一段时间才继续另一个


3.2 Describe the actions taken by a kernel to context-switch between processes.
内核采取一些动作以便在两个进程之间进行上下文切换,请描述一下
Answer:
In general, the operating system must save the state of the currently running
process and restore the state of the process scheduled to be run next. Saving the
state of a process typically includes the values of all the CPU registers in
addition to memory allocation. Context switches must also perform many
architecture-specific operations, including flushing data and instruction caches.
通常,操作系统必须保存当前正在运行的进程的状态,并且恢复计划下一步运行的进程的状态。
保存a进程的状态通常包括CPU寄存器的值,以及牛才能分配。上下文交换必须也执行许多特定于结构的
操作,包括刷新数据和指令缓存


3.3 大家随便画画,试试命令


3.4 Explain the role of the init process on UNIX and Linux systems in regards to
process termination.

针对Unix和Linux系统的init进程在进程终止方面的作用,请解释一下
Answer:
When a process is terminated, it briefly moves to the zombie state and remains
in that state until the parent invokes a call to wait(). When this occurs, the
process id as well as entry in the process table are both released. However, if a
parent does not invoke wait(), the child process remains a zombie as long as the
parent remains alive. Once the parent process terminates, the init process becomes
the new parent of the zombie. Periodically, the init process calls wait() which
ultimately releases the pid and entry in the process table of the zombie process.
当进程终止时,它会短暂的移动到僵尸状态,并保持该状态(成为僵尸进程),直到父进程调用
wait()方法。当调用后,僵尸进程的id和进程表中的条目都将被释放.


3.5 Including the initial parent process, how many processes are created by the
program shown in Figure 3.32?

包括初始的父进程,总共下面创建了多少个进程
Answer:
16 processes are created. The program online includes printf() statements to
better understand how many processes have been created.
创建了16个进程,该程序可以加入printf()语句来更好说明创建了几个进程

#include <stdio.h>
#include <unistd.h>
int main() 
{
	int i;
	for(i = 0; i < 4; i++)
		fork();
	return 0;
}

将其修改成这样,可以打印出15个不同pid,加上父进程,一共16个进程

#include <stdio.h>
#include <unistd.h>
int main() 
{
	int i;
	int pid = 0;
	for(i = 0; i < 4; i++)
	{
		pid = fork();
		printf("pid:%d\n", pid);
		printf("----\n");
	}
	return 0;
}

可以参考这个博客
fork()面试题
循环中fork()进程数量


3.6 Explain the circumstances when the line of code marked printf(“LINE J”) in
Figure 3.33 is reached.

解释如图所示的标记为printf(“LINE J”)的行所能执行的环境,请解释一下

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

int main() 
{
    pid_t pid;
    /*fork a child process*/
    pid = fork();

    if(pid < 0)
    {/*error occurred*/
        fprintf(stderr, "Fork Failed");
        return 1;
    } 
    else if(pid == 0)
    {/*child process*/
        execlp("/bin/ls", "ls", NULL);
        print("LINE J");
    }
    else 
    {/* parent process*/
        /* parent will wait for the child to complete*/
        wait(NULL);
        printf("Child Complete");
    }

    return 0;
}

将程序适当更改:
可以看到下面的程序打印出来了父进程和子进程的pid,exec是列出当前目录的所有文件,
可以看到执行成功,因此不会返回控制

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int wait();
int main()
{
    pid_t pid;
    /*fork a child process*/
    pid = fork();
	/*
	注:
	对于标准输出设备stdout,输出一般都有缓冲,当遇到刷新标志或缓冲满时
	才把缓冲的数据输出到标准输出设备中。printf()函数采用行缓冲式输出,
	遇到\n或者缓冲区满时,才会输出。
	因此\n才能输出,否则是没有东西的。
	*/
    printf("%d\n", pid);
    if(pid < 0)
    {/*error occurred*/
        fprintf(stderr, "Fork Failed");
        return 1;
    }
    else if(pid == 0)
    {/*child process*/
	/*
	系统调用exec()加载二进制文件到内存(破坏了包含系统调用exec()的原来程序的内存内容)
	,并开始执行。由于调用exec()用新程序覆盖了进程的地址空间,所以调用exec()除非出现
	错误,不会返回控制
	*/
        execlp("/bin/ls", "ls", NULL);
        printf("LINE J");
    }
    else
    {/* parent process*/
        /* parent will wait for the child to complete*/
        wait(NULL);
        printf("Child Complete");
    }

    return 0;
}

Output:

29360
0
createpro  createProcess3.5.c  executeEnv3.6.c  test  第三章课后理论题.md

Answer:
The call to exec() replaces the address space of the process with the program
specified as the parameter to exec(). If the call to exec() succeeds, the new
program is now running and control from the call to exec() never returns. In this
scenario, the line printf(“Line J”); would never be performed.However, if an error
occurs in the call to exec(), the function returns control and therefor the line
printf(“Line J”); would be performed.
exec函数作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容.如果exec()调用
成功,则新进程运行,并且永远不会从exec()返回。这种情况下,printf("Line J);永远不会被执行.
但是,如果exec()调用发生错误,函数返回-1,因此printf(“Line J”)将会被执行.


3.7 Using the program in Figure Figure 3.34, identify the values of pid at lines
A, B, C, and D. (Assume that the actual pids of the parent and child are 2600 and
2603, respectively.)

采用图3.34程序,确定A、B、C、D中pid的值.(假定父进程和子进程的pid分别为2600和2603)

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

int main() {
    pid_t pid, pid1;

    /* fork a child process*/
    pid = fork();

    if(pid < 0)
    {/*error ocurred*/
        fprintf(stderr, "Fork Failed");
        return 1;
    }
    else if(pid == 0) 
    {/*child process*/
        pid1 = getpid();
        printf("child: pid = %d", pid); /*A*/
        printf("child: pid1 = %d", pid1);/*B*/
    }
    else 
    {/*parent process*/
        pid1 = getpid();
        printf("parent: pid = %d", pid);/*C*/
        printf("parent: pid1 = %d", pid1);/*D*/
        wait(NULL);
    }

    return 0;
}

Answer: A = 0, B = 2603, C = 2603, D = 2600
考的是fork()返回值,在父进程中,fork返回新创建子进程的进程id;
在子进程中,fork返回0;如果出现错误,fork返回一个负值


3.8 Give an example of a situation in which ordinary pipes are more suitable
than named pipes and an example of a situation in which named pipes are more
suitable than ordinary pipes.

对于普通管道和命名管道有时候可能一方更加合适,请举例说明
Answer:
Simple communication works well with ordinary pipes. For example, assume
we have a process that counts characters in a file. An ordinary pipe can be
used where the producer writes the file to the pipe and the consumer reads the
files and counts the number of characters in the file. Next, for an example where
named pipes are more suitable, consider the situation where several processes may
write messages to a log. When processes wish to write a message to the log, they
write it to the named pipe. A server reads the messages from the named pipe and
writes them to the log file
简单的通信可以使用普通管道。例如,假设我们有一个计算文件中字符数量的进程.可以使用
一个普通管道,生产者将文件写入管道,消费者从管道一段读取文件,并且计算文件中的字符数。
接着,举一个适合命名管道的例子。考虑多个进程可能会将消息写入日志中的情景,当进程希望将
消息写入日志,它们将其写入命名管道。一个服务器从命名管道中读取消息,并且将它们写入到日志
文件


3.9 Consider the RPC mechanism. Describe the undesirable consequences that could
arise from not enforcing either the “at most once” or “exactly once” semantic.
Describe possible uses for a mechanism that has neither of these guarantees.

对于RPC机制,若没有强制"最多一次"或"正好一次"的语义,描述一下所带来的一些不必要的后果。
讨论一下没有这些强制保证的可能用途
Answer:
If an RPC mechanism cannot support either the “at most once” or “at least once”
semantics, then the RPC server cannot guarantee that a remote procedure will not
be invoked multiple occurrences. Consider if a remote procedure were withdrawing
money from a bank account on a system that did not support these semantics. It is
possible that a single invocation of the remote procedure might lead to multiple
withdrawals on the server.
如果RPC机制不支持"最多一次"或"至少一次"语义,RPC服务器不能保证远程过程不会被调用多次。
考虑一个远程过程在一个不支持这种语义的系统上从一个银行上取钱.远程过程的一次调用将会导致
从服务器上多次执行取钱操作
For a system to support either of these semantics generally requires the server
maintain some form of client state such as the timestamp described in the text.
If a system were unable to support either of these semantics, then such a system
could only safely provide remote procedures that do not alter data or provide
time-sensitive results. Using our bank account as an example, we certainly require
“at most once” or “at least once” semantics for performing a withdrawal (or deposit
!). However, an inquiry into an account balance or other account information such
as name, address, etc. does not require these semantics.
对于一个支持这两种语义之一的系统通常需要服务器维护客户端的某种状态。如文章中
为每个消息附加时间戳。如果一个系统不能支持这两种语义,那么这样一个系统只能安全地提供不需
交换数据或不需提供实时性的结果的远程调用.以我们的银行账户为例,我们对于取款会存款,肯定需
要这两种语义之一。但是,查询账户余额或其他账户信息,如姓名,地址,等等,不需要这些语义。


3.10 Using the program shown in Figure 3.35, explain what the output will be at
lines X and Y.

使用如图3.35所示程序,解释X、Y行输出将会是什么

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

#define SIZE 5

int nums[SIZE] = {0, 1, 2, 3, 4};

int main()
{
    int i;
    pid_t pid;

    pid = fork();
    if(pid == 0)
    {
        for(i = 0; i < SIZE; i++)
        {
            nums[i] *= -i;
            printf("CHILD: %d", nums[i]);/*LINE X*/
        }
    }
    else if(pid > 0)
    {
        wait(NULL);
        for(i = 0; i < SIZE; i++) 
            printf("PARENT: %d", nums[i]);/*LINE Y*/
    }
}

Answer:
注意,系统调用fork(),可创建新锦成,新进程的地址空间复制了原来进程的地址空间,注意是副本
Because the child is a copy of the parent, any changes the child makes will
occur in its copy of the data and won’t be reflected in the parent. As a result,
the values output by the child at line X are 0, -1, -4, -9, -16. The values output
by the parent at line Y are 0, 1, 2, 3, 4
由于child是parent的副本,任何发生在child中的改变将出现在它自己的数据拷贝上,并不会
影响parent父进程。因此,输出结果是:
在子进程X行处, 0、-1、-4、-9、-16
在父进程Y行处, 0、1、2、3、4


3.11 What are the benefits and the disadvantages of each of the following?
Consider both the system level and the programmer level.

下面设计的优缺点是什么?系统层次和用户层次都要考虑
a. Synchronous and asynchronous communication
a. 同步和异步通信
b. Automatic and explicit buffering
b. 自动和显示缓冲
c. Send by copy and send by reference
c. 复制传送和引用传送
d. Fixed-sized and variable-sized messages
d. 固定大小和可变大小消息
Answer:
a. Synchronous and asynchronous communication—A benefit of synchronous
communication is that it allows a rendezvous between the sender and receiver.
A disadvantage of a blocking send is that a rendezvous may not be required and
the message could be delivered asynchronously. As a result, message-passing systems
often provide both forms of synchronization
同步和异步通信–同步的优点是它允许发送方和接收方之间进行约定。阻塞发送的一个缺点是可能
不能约定,并且消息可能异步传递.因此,消息传递系统通常提供两种形式的同步

b. Automatic and explicit buffering—Automatic buffering provides a queue 

with indefinite length, thus ensuring the sender will never have to block while
waiting to copy a message. There are no specifications on how automatic buffering
will be provided; one scheme may reserve sufficiently large memory where much of
the memory is wasted. Explicit buffering specifies how large the buffer is. In
this situation, the sender may be blocked while waiting for available space in the
queue.However, it is less likely that memory will be wasted with explicit buffering.
自动和显示缓冲–自动缓冲提供一个具有不定长度的队列,因此确保发送方在等待复制消息时永远不必
阻塞.关于如何自动缓冲的规范没有提供;一种方案可以保留足够大的内容,这些内存被浪费了.显示缓冲
指出了缓冲区的大小.在这种情况下,发送方在等待队列中可用的空间时被阻塞.但是,显示缓冲不太可能
浪费内存.
c. Send by copy and send by reference—Send by copy does not allow the receiver
to alter the state of the parameter; send by reference does allow it. A benefit of
send by reference is that it allows the programmer to write a distributed version
of a centralized application. Java’s RMI provides both; however, passing a parameter
by reference requires declaring the parameter as a remote object as well.
复制传送和引用传送—复制传送不允许发送者去改变参数的状态;通过引用传送是可以的.引用传送的
一个好处是它允许程序员编写分布式版本的集中应用.Java的RMI都提供了.然而,通过引用传送一个
参数还需要在将这个参数声明为远程对象
d. Fixed-sized and variable-sized messages—The implications of this are mostly
related to buffering issues; with fixed-size messages, a buffer with a specific size
can hold a known number of messages. The number of variable-sized messages that can
be held by such a buffer is unknown. Consider how Windows 2000 handles this situation:
with fixed-sized messages (anything < 256 bytes), the messages are copied from the
address space of the sender to the address space of the receiving process. Larger
messages (i.e. variable-sized messages) use shared memory to pass the message.
固定大小和可变大小消息—这个的实现主要与缓冲问题有关;对于固定大小的消息,具有特定大小的
缓冲区可以容纳已经数量的消息.可变大小消息是不清楚的.考虑windows 2000处理如下情况:
对于固定大小的消息(小于256字节),从发送方的地址空间复制到接受进程的地址空间。更大的消息使用
共享内存传递消息.(很明显是提高效率,减少内存地址空间的浪费)


猜你喜欢

转载自blog.csdn.net/qq_32252957/article/details/86485584