分类: LINUX
2006-05-08 16:13:33
1.6.6. Signals and Interprocess Communication
Unix signals provide a mechanism for notifying processes of system events. Each event has its own signal number, which is usually referred to by a symbolic constant such as SIGTERM. There are two kinds of system events:
Unix信号提供了一种机制,把系统时间通知给相关的进程。每一种事件都有对应的信号编号,系统为这些编号定义了对应的信号常量如SIGTERM。系统事件有两类:
Asynchronous notifications
异步通知
For instance, a user can send the interrupt signal SIGINT to a foreground process by pressing the interrupt keycode (usually Ctrl-C) at the terminal.
例如某个用户通过按CTRL-C向某个位于前台的进程发送了一个SIGINT信号。
Synchronous notifications
同步通知:
For instance, the kernel sends the signal SIGSEGV to a process when it accesses a memory location at an invalid address.
例如某个进程试图访问一个非法的地址时得到系统发送的SIGSEGV。
The POSIX standard defines about 20 different signals, 2 of which are user-definable and may be used as a primitive mechanism for communication and synchronization among processes in User Mode. In general, a process may react to a signal delivery in two possible ways:
POSIX标准定义了大约20个不同的信号,其中两个允许用户重新定义,可以被用于用户模式下的进程间通信的基本机制。一般来说一个进程可以按以下两种方式对发送给他的信号做出回应:
Ignore the signal.
直接忽略
Asynchronously execute a specified procedure (the signal handler).
异步地执行一个过程(即信号处理函数)
If the process does not specify one of these alternatives, the kernel performs a default action that depends on the signal number. The five possible default actions are:
如果进程没有指定上述两种回应方式中的任何一种,Kernel会按照信号的编号执行一个缺省的动作。缺省的信号处理共有五种:
Terminate the process.
终止进程。
Write the execution context and the contents of the address space in a file (core dump) and terminate the process.
把进程执行的上下文和地址空间的内容写到一个文件里(称为“核心映像转储”),并且结束进程。
Ignore the signal.
忽略该信号。
Suspend the process.
将该进程挂起。
Resume the process's execution, if it was stopped.
恢复该进程的执行(如果此前它被挂起了)。
Kernel signal handling is rather elaborate, because the POSIX semantics allows processes to temporarily block signals. Moreover, the SIGKILL and SIGSTOP signals cannot be directly handled by the process or ignored.
内核信号处理是很复杂的过程,因为POSIX信号语义允许进程临时禁止某些信号的接收。此外SIGKILL和SIGSTOP既不可以被忽略也不可以被直接按照用户定义的方式来处理。
AT&T's Unix System V introduced other kinds of interprocess communication among processes in User Mode, which have been adopted by many Unix kernels: semaphores , message queues , and shared memory . They are collectively known as System V IPC.
AT&T的Unix System V还对用户态进程间通信提供了其他机制。这些机制还被很多Unix系统所采用。这些机制包括信号量,消息队列,共享内存;总称为System V进程间通信。
The kernel implements these constructs as IPC resources. A process acquires a resource by invoking a shmget( ) , semget( ) , or msgget( ) system call. Just like files, IPC resources are persistent: they must be explicitly deallocated by the creator process, by the current owner, or by a superuser process.
内核把这些进程间通信机制实现为进程间通信资源。一个进程通过调用shmget(), semget()或者msgget()系统调用来获得一个进程间通信资源。如同普通文件一样,IPC资源也是持久化的:它们必须被创建者,当前所有者,或者超级用户释放,才从系统中消失。
Semaphores are similar to those described in the section "Synchronization and Critical Regions," earlier in this chapter, except that they are reserved for processes in User Mode. Message queues allow processes to exchange messages by using the msgsnd( ) and msgrcv( ) system calls, which insert a message into a specific message queue and extract a message from it, respectively.
信号量,和“同步与关键区”一节中描述的那种信号量类似,只不过这里所说的信号量是指用于用户态进程的。消息队列使进程能够用msgsnd()和msgrcv()来交换消息,msgsnd()和msgrcv()分别用于向一个消息队列中插入一个消息和取走一个消息。
The POSIX standard (IEEE Std 1003.1-2001) defines an IPC mechanism based on message queues, which is usually known as POSIX message queues . They are similar to the System V IPC's message queues, but they have a much simpler file-based interface to the applications.
POSIX标准1003.1-2001基于消息队列定义了一种IPC机制,就是通常被称为“POSIX消息队列”的。POSIX消息队列和System V消息队列相似,但拥有一个形式更简单、功能更丰富、基于文件的接口。
Shared memory provides the fastest way for processes to exchange and share data. A process starts by issuing a shmget( ) system call to create a new shared memory having a required size. After obtaining the IPC resource identifier, the process invokes the shmat( ) system call, which returns the starting address of the new region within the process address space. When the process wishes to detach the shared memory from its address space, it invokes the shmdt( ) system call. The implementation of shared memory depends on how the kernel implements process address spaces.
共享内存机制为进程间交换和共享数据提供了最快捷的方式。一个进程先调用shmget()来创建指定大小的一片共享内存区域,得到IPC资源标识符之后,进程调用shmat(),这个系统调用返回共享内存区在该进程空间内的起始地址。当进程希望从地址空间中把共享内存释放掉,可以调用shmdt()。共享内存机制的具体实现依赖于系统内核对进程地址空间的实现方法。
1.6.7. Process Management
进程管理
Unix makes a neat distinction between the process and the program it is executing. To that end, the fork( ) and _exit( ) system calls are used respectively to create a new process and to terminate it, while an exec( )-like system call is invoked to load a new program. After such a system call is executed, the process resumes execution with a brand new address space containing the loaded program.
Unix对进程和进程所执行的程序作严格的区分。可以分别用fork()和exit()来创建和终止一个进程。Exec()系列函数用于装载一个新程序。这个系列的函数执行后,原来的进程在一个全新的、被装载了指定的新程序的地址空间内继续运行。
The process that invokes a fork( ) is the parent, while the new process is its child. Parents and children can find one another because the data structure describing each process includes a pointer to its immediate parent and pointers to all its immediate children.
调用fork()的进程是父进程,产生的新进程是子进程。父子进程都可以得到对方的引用,因为描述进程的数据结构中,有成员分别指向其父进程和全部子进程。
A naive implementation of the fork( ) would require both the parent's data and the parent's code to be duplicated and the copies assigned to the child. This would be quite time consuming. Current kernels that can rely on hardware paging units follow the Copy-On-Write approach, which defers page duplication until the last moment (i.e., until the parent or the child is required to write into a page). We shall describe how Linux implements this technique in the section "Copy On Write" in Chapter 9.
一种较为原始的fork()系统调用的实现方法,是把父进程的数据和代码都复制一份,把拷贝交给子进程。这种方法很费时,现代的kernel都能很好地利用硬件中的内存分页功能,以便采用copy on write的策略。这一策略使得内存页面的复制得以被推迟到必须发生的时候,即父进程或者子进程要对该内存页面执行写操作,更改内存页面的内容的时候。我们会在第九章中描述关于这一技术在linux中的实现。
The _exit( ) system call terminates a process. The kernel handles this system call by releasing the resources owned by the process and sending the parent process a SIGCHLD signal, which is ignored by default.
_exit()系统调用用于终止一个进程。核心处理这个调用的时候释放进程的全部资源并向其父进程发送SIGCHLD信号,缺省情况下这个信号是被父进程忽略的。
1.6.7.1. Zombie processes
僵尸进程
How can a parent process inquire about termination of its children? The wait4( ) system call allows a process to wait until one of its children terminates; it returns the process ID (PID) of the terminated child.
一个父进程怎样知道其子进程是否已经执行结束了呢?wait4()系统调用可以用来让父进程等待其任意一个子进程的结束。Wait4()把结束的子进程的PID返回给父进程。
When executing this system call, the kernel checks whether a child has already terminated. A special zombie process state is introduced to represent terminated processes: a process remains in that state until its parent process executes a wait4( ) system call on it. The system call handler extracts data about resource usage from the process descriptor fields; the process descriptor may be released once the data is collected. If no child process has already terminated when the wait4( ) system call is executed, the kernel usually puts the process in a wait state until a child terminates.
执行这个系统调用的时候,kernel先检查是否已经有子进程执行结束。一个特别的“僵尸进程”状态,被用于描述那些执行已结束但父进程未对其调用wait4()的子进程所处的状态。Wait4()系统调用从进程描述符的相关成员中取得进程使用资源的数据,取得这些数据后,这个进程描述符就被释放。如果wait4()被调用时还没有已经结束的子进程,kernel会把调用wait4()的进程转入等待状态,直到其任意一个子进程执行结束。
Many kernels also implement a waitpid( ) system call, which allows a process to wait for a specific child process. Other variants of wait4( ) system calls are also quite common.
很多kernel实现了wait4()系统调用,使进程可以等待子进程执行结束。Wait4()系统调用的其他变体也很常见。
It's good practice for the kernel to keep around information on a child process until the parent issues its wait4( ) call, but suppose the parent process terminates without issuing that call? The information takes up valuable memory slots that could be used to serve living processes. For example, many shells allow the user to start a command in the background and then log out. The process that is running the command shell terminates, but its children continue their execution.
系统内核保留子进程的有关信息,直到其父进程调用wait4()。这通常是一种好的做法,但是如果父进程结束前始终未调用wait4(), kernel保存的进程信息占用了宝贵的内存资源,并且别的活动进程也不能再利用这些资源。例如很多shell允许用户启动一个后台进程,然后注销登录,此时shell进程(父进程)随即结束(而且并未调用wait4),但是那个后台进程(子进程)仍然在继续执行。
The solution lies in a special system process called init, which is created during system initialization. When a process terminates, the kernel changes the appropriate process descriptor pointers of all the existing children of the terminated process to make them become children of init. This process monitors the execution of all its children and routinely issues wait4( ) system calls, whose side effect is to get rid of all orphaned zombies.
这个问题依靠一个称为init的进程来解决。该进程在系统初始化的时候被创建,任何进程结束时,kernel更改其所有子进程的描述符,使他们都变成init进程的子进程,init监视其全部子进程并适时调用wait4()来除去这些僵尸状态的孤儿进程。
1.6.7.2. Process groups and login sessions
进程组和登录会话
Modern Unix operating systems introduce the notion of process groups to represent a "job" abstraction. For example, in order to execute the command line:
现代Unix操作系统增添了进程组的概念,来描述一个抽象的“工作”。例如为了执行命令
$ ls | sort | more
a shell that supports process groups, such as bash, creates a new group for the three processes corresponding to ls, sort, and more. In this way, the shell acts on the three processes as if they were a single entity (the job, to be precise). Each process descriptor includes a field containing the process group ID . Each group of processes may have a group leader, which is the process whose PID coincides with the process group ID. A newly created process is initially inserted into the process group of its parent.
一个支持进程组概念的shell,如bash,会为ls, sort, more对应的三个进程创建一个进程组,这样shell即可把这些进程当作一个实体对其进行操作(准确地说就是一件“工作”)。每个进程描述符都有一个成员描述其所属的进程组,每个进程组可以有一个进程组头进程,它的PID和这个进程组的ID相同。一个新创建的进程一开始总是被加入其父进程所在的进程组。
Modern Unix kernels also introduce login sessions. Informally, a login session contains all processes that are descendants of the process that has started a working session on a specific terminalusually, the first command shell process created for the user. All processes in a process group must be in the same login session. A login session may have several process groups active simultaneously; one of these process groups is always in the foreground, which means that it has access to the terminal. The other active process groups are in the background. When a background process tries to access the terminal, it receives a SIGTTIN or SIGTTOUT signal. In many command shells, the internal commands bg and fg can be used to put a process group in either the background or the foreground.
现代的Unix内核还增加了登录会话的概念。不太精确地讲,一个登录会话包括了某个终端上作为某个工作会话的起始点的那个进程,及其全部后代进程。一般来说这个起始点进程本身也就是用户登录后的第一个shell进程。一个进程组内的全部进程必须属于同一个登录会话。一个登录会话可以同时拥有多个活动的进程组,这些进程组中总有一个位于前台(即可以访问终端);其他进程组都在后台,后台进程组试图访问终端的时候会收到SIGTTIN或SIGTTOUT信号。很多shell里,内部命令fg和bg分别用于把进程组置于前台/后台。