📄 unix time_share_system .htm
字号:
file. Still larger files use the twelfth device address of the i-node to point
to a double-indirect block naming 128 indirect blocks, each pointing to 128
blocks of the file. If required, the thirteenth device address is a
triple-indirect block. Thus files may conceptually grow to
[(10+128+128^2+128^3)*512] bytes. Once opened, bytes numbered below 5120 can be
read with a single disk access; bytes in the range 5120 to 70,656 require two
accesses; bytes in the range 70,656 to 8,459,264 require three accesses; bytes
from there to the largest file (1,082,201,088) require four accesses. In
practice, a device cache mechanism (see below) proves effective in eliminating
most of the indirect fetches. </P>
<P>The foregoing discussion applies to ordinary files. When an I/O request is
made to a file whose i-node indicates that it is special, the last 12 device
address words are immaterial, and the first specifies an internal <B>device
name</B>, which is interpreted as a pair of numbers representing, respectively,
a device type and subdevice number. The device type indicates which system
routine will deal with I/O on that device; the subdevice number selects, for
example, a disk drive attached to a particular controller or one of several
similar terminal interfaces. </P>
<P>In this environment, the implementation of the <I>mount</I> system call
(Section 3.4) is quite straightforward. <I>mount</I> maintains a system table
whose argument is the i-number and device name of the ordinary file specified
during the <I>mount</I>, and whose corresponding value is the device name of the
indicated special file. This table is searched for each i-number/device pair
that turns up while a path name is being scanned during an <I>open</I> or
<I>create</I>; if a match is found, the i-number is replaced by the i-number of
the root directory and the device name is replaced by the table value. </P>
<P>To the user, both reading and writing of files appear to be synchronous and
unbuffered. That is, immediately after return from a <I>read</I> call the data
are available; conversely, after a <I>write</I> the user's workspace may be
reused. In fact, the system maintains a rather complicated buffering mechanism
that reduces greatly the number of I/O operations required to access a file.
Suppose a <I>write</I> call is made specifying transmission of a single byte.
The system will search its buffers to see whether the affected disk block
currently resides in main memory; if not, it will be read in from the device.
Then the affected byte is replaced in the buffer and an entry is made in a list
of blocks to be written. The return from the <I>write</I> call may then take
place, although the actual I/O may not be completed until a later time.
Conversely, if a single byte is read, the system determines whether the
secondary storage block in which the byte is located is already in one of the
system's buffers; if so, the byte can be returned immediately. If not, the block
is read into a buffer and the byte picked out. </P>
<P>The system recognizes when a program has made accesses to sequential blocks
of a file, and asynchronously pre-reads the next block. This significantly
reduces the running time of most programs while adding little to system
overhead. </P>
<P>A program that reads or writes files in units of 512 bytes has an advantage
over a program that reads or writes a single byte at a time, but the gain is not
immense; it comes mainly from the avoidance of system overhead. If a program is
used rarely or does no great volume of I/O, it may quite reasonably read and
write in units as small as it wishes. </P>
<P>The notion of the i-list is an unusual feature of Unix. In practice, this
method of organizing the file system has proved quite reliable and easy to deal
with. To the system itself, one of its strengths is the fact that each file has
a short, unambiguous name related in a simple way to the protection, addressing,
and other information needed to access the file. It also permits a quite simple
and rapid algorithm for checking the consistency of a file system, for example,
verification that the portions of each device containing useful information and
those free to be allocated are disjoint and together exhaust the space on the
device. This algorithm is independent of the directory hierarchy, because it
need only scan the linearly organized i-list. At the same time the notion of the
i-list induces certain peculiarities not found in other file system
organizations. For example, there is the question of who is to be charged for
the space a file occupies, because all directory entries for a file have equal
status. Charging the owner of a file is unfair in general, for one user may
create a file, another may link to it, and the first user may delete the file.
The first user is still the owner of the file, but it should be charged to the
second user. The simplest reasonably fair algorithm seems to be to spread the
charges equally among users who have links to a file. Many installations avoid
the issue by not charging any fees at all. </P>
<H4>V. PROCESSES AND IMAGES </H4>
<P>An <B>image</B> is a computer execution environment. It includes a memory
image, general register values, status of open files, current directory and the
like. An image is the current state of a pseudo-computer. </P>
<P>A <B>process</B> is the execution of an image. While the processor is
executing on behalf of a process, the image must reside in main memory; during
the execution of other processes it remains in main memory unless the appearance
of an active, higher-priority process forces it to be swapped out to the disk.
</P>
<P>The user-memory part of an image is divided into three logical segments. The
program text segment begins at location 0 in the virtual address space. During
execution, this segment is write-protected and a single copy of it is shared
among all processes executing the same program. At the first hardware protection
byte boundary above the program text segment in the virtual address space begins
a non-shared, writable data segment, the size of which may be extended by a
system call. Starting at the highest address in the virtual address space is a
stack segment, which automatically grows downward as the stack pointer
fluctuates. </P>
<H4>5.1 Processes </H4>
<P>Except while the system is bootstrapping itself into operation, a new process
can come into existence only by use of the <I>fork</I> system call:
<DL>
<DT>
<DD><TT><PRE>processid = fork()
</PRE></TT></DD></DL>When <I>fork</I> is executed, the process splits into two
independently executing processes. The two processes have independent copies of
the original memory image, and share all open files. The new processes differ
only in that one is considered the parent process: in the parent, the returned
<I>processid</I> actually identifies the child process and is never 0, while in
the child, the returned value is always 0.
<P></P>
<P>Because the values returned by <I>fork</I> in the parent and child process
are distinguishable, each process may determine whether it is the parent or
child. </P>
<H4>5.2 Pipes </H4>
<P>Processes may communicate with related processes using the same system
<I>read</I> and <I>write</I> calls that are used for file-system I/O. The call:
<DL>
<DT>
<DD><TT><PRE>filep = pipe()
</PRE></TT></DD></DL>returns a file descriptor <I>filep</I> and creates an
inter-process channel called a <B>pipe</B>. This channel, like other open files,
is passed from parent to child process in the image by the <I>fork</I> call. A
<I>read</I> using a pipe file descriptor waits until another process writes
using the file descriptor for the same pipe. At this point, data are passed
between the images of the two processes. Neither process need know that a pipe,
rather than an ordinary file, is involved.
<P></P>
<P>Although inter-process communication via pipes is a quite valuable tool (see
Section 6.2), it is not a completely general mechanism, because the pipe must be
set up by a common ancestor of the processes involved. </P>
<H4>5.3 Execution of programs </H4>
<P>Another major system primitive is invoked by
<DL>
<DT>
<DD><TT><PRE>execute(file, arg1, arg2, ... , argn)
</PRE></TT></DD></DL>which requests the system to read in and execute the
program named by <I>file</I>, passing it string arguments <I>arg1</I>,
<I>arg2</I>, <I>...</I>, <I>argn</I>. All the code and data in the process
invoking <I>execute</I> is replaced from the <I>file</I>, but open files,
current directory, and inter-process relationships are unaltered. Only if the
call fails, for example because <I>file</I> could not be found or because its
execute-permission bit was not set, does a return take place from the
<I>execute</I> primitive; it resembles a ``jump'' machine instruction rather
than a subroutine call.
<P></P>
<H4>5.4 Process synchronization </H4>
<P>Another process control system call:
<DL>
<DT>
<DD><TT><PRE>processid = wait(status)
</PRE></TT></DD></DL>causes its caller to suspend execution until one of its
children has completed execution. Then <I>wait</I> returns the <I>processid</I>
of the terminated process. An error return is taken if the calling process has
no descendants. Certain status from the child process is also available.
<P></P>
<H4>5.5 Termination </H4>
<P>Lastly:
<DL>
<DT>
<DD><TT><PRE>exit(status)
</PRE></TT></DD></DL>terminates a process, destroys its image, closes its open
files, and generally obliterates it. The parent is notified through the
<I>wait</I> primitive, and <I>status</I> is made available to it. Processes may
also terminate as a result of various illegal actions or user-generated signals
(Section VII below).
<P></P>
<H4>VI. THE SHELL </H4>
<P>For most users, communication with the system is carried on with the aid of a
program called the shell. The shell is a command-line interpreter: it reads
lines typed by the user and interprets them as requests to execute other
programs. (The shell is described fully elsewhere [9], so this section will
discuss only the theory of its operation.) In simplest form, a command line
consists of the command name followed by arguments to the command, all separated
by spaces:
<DL>
<DT>
<DD><TT><PRE>command arg1 arg2 ... argn
</PRE></TT></DD></DL>The shell splits up the command name and the arguments into
separate strings. Then a file with name <I>command</I> is sought; <I>command</I>
may be a path name including the ``/'' character to specify any file in the
system. If <I>command</I> is found, it is brought into memory and executed. The
arguments collected by the shell are accessible to the command. When the command
is finished, the shell resumes its own execution, and indicates its readiness to
accept another command by typing a prompt character.
<P></P>
<P>If file <I>command</I> cannot be found, the shell generally prefixes a string
such as <I>/bin/</I> to <I>command</I> and attempts again to find the file.
Directory <I>/bin</I> contains commands intended to be generally used. (The
sequence of directories to be searched may be changed by user request.) </P>
<H4>6.1 Standard I/O </H4>
<P>The discussion of I/O in Section III above seems to imply that every file
used by a program must be opened or created by the program in order to get a
file descriptor for the file. Programs executed by the shell, however, start off
with three open files with file descriptors 0, 1, and 2. As such a program
begins execution, file 1 is open for writing, and is best understood as the
standard output file. Except under circumstances indicated below, this file is
the user's terminal. Thus programs that wish to write informative information
ordinarily use file descriptor 1. Conversely, file 0 starts off open for
reading, and programs that wish to read messages typed by the user read this
file. </P>
<P>The shell is able to change the standard assignments of these file
descriptors from the user's terminal printer and keyboard. If one of the
arguments to a command is prefixed by ``>'', file descriptor 1 will, for the
duration of the command, refer to the file named after the ``>''. For
example:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -