1.6.程序和进程(Programs and Processes)

来源:互联网 发布:菜鸟网络合作物流 编辑:程序博客网 时间:2024/06/01 10:42

1.6.程序和进程(Programs and Processes)

程序(Program)
一个程序是一个存储在磁盘上某个文件夹中的可执行文件。A program is read into memory and is executed by the kernel as a result of one of the six exec functions. Section 8.10将说明这些exec函数。

进程和进程ID(Processes and Process ID)
程序的一个正在执行的实例(executing instance)被称为进程。本书的每一页几乎都会使用这一术语。某些操作系统用术语任务(task)来表示一个正在被执行的程序。
UNIX系统确保每个进程都一定有一个唯一的数字标识符,称为进程ID(process ID)。进程ID总是一非负整数(a non-negative integer)。

Example
程序figure1.6打印出它的进程ID。
If we compile this program into the file a.out and execute it, we have
   $ ./a.out
   hello world from process ID 851
   $ ./a.out
   hello world from process ID 854
此程序运行时,它调用函数getpid得到其进程ID。
--------------------------------------------------------
[lizl@localhost apue_code]$ ./print_the_process_id.out
hello world from process ID 28877
[lizl@localhost apue_code]$ ./print_the_process_id.out
hello world from process ID 28878
[lizl@localhost apue_code]$ ./print_the_process_id.out
hello world from process ID 28879
[lizl@localhost apue_code]$ ./print_the_process_id.out
hello world from process ID 28880
--------------------------------------------------------
程序figure1.6 打印进程ID
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;

int
main(void)
{
    printf("hello world from process ID %d/n", getpid());
    return 0;
}

进程控制(Process Control)
有三个用于进程控制的主要函数:fork,exec和waitpid(exec函数有六种变体,但经常把它们简单地统称为exec函数)。

Example
UNIX系统的进程控制功能可以用一个较简单的程序(figure1.7)说明,该程序从标准输入读命令,然后执行这些命令。这是一个类shell程序的基本实施部分(a bare-bones implementation of a shell-like program)。在这个30行的程序中,有很多功能需要思考。


• 我们用标准I/O函数fgets从标准输入一次读一行。当我们键入文件结束字符(通常是Ctrl-D)作为一行的第1个字符时,fgets返回一个null指针,于是循环停止,进程也就终止。第18章将描述所有特殊的终端字符(文件结束、退格字符、整行擦除等等),以及如何改变它们。


• 因为fgets返回的每一行都以新行符(a newline character)终止,后随一个null字节,故用标准C函数strlen计算此字符串的长度,然后用一个null字节替换新行符。这一操作的目的是因为execlp函数需要的是以null结束的参数,而不是以新行符结束的参数。


• 我们调用fork创建一个新进程,新进程是调用进程的复制品(a copy of the caller)。故我们称调用进程(caller)为父进程,新创建的进程为子进程。fork对父进程返回新建子进程的非负进程ID,对子进程则返回0。因为fork创建一新进程,所以我们说它被父进程调用一次,但返回两次(在父进程中和在子进程中)。


• 在子进程中,我们调用execlp以执行从标准输入读入的命令。这就用新的程序文件替换了子进程。fork和跟随其后的一个exec的组合是某些操作系统所称的产生一个新进程(spawning a new process)。在UNIX系统中,这两个部分分成两个函数。第8章将对这些函数作更多说明。


• 由于子进程调用execlp执行新的程序文件,所以父进程希望能够等待子进程先终止,这一要求由调用waitpid实现,其第一个参数pid指定了要等待的进程(在这里,pid参数是子进程ID)。waitpid函数也返回子进程的终止状态(第二个参数status)。但是在这个简单程序中,没有使用该值。如果需要,可以通过检查此值以精确地确定子进程是如何终止的。


• 该程序的最主要限制是不能向我们执行的命令传递参数。例如我们不能指定要列出的目录名,只能对工作目录(working directory)执行ls命令。为了传递参数,先要分析输入行,用某种约定(convention)把参数分开(很可能使用空格space或制表符tab),然后将分隔后的各个参数传递给execlp函数。尽管如此,此程序仍可用来说明UNIX的进程控制功能。


Figure 1.7. Read commands from standard input and execute them(有点问题,先放着)

如果我们运行此程序,则得到下列结果。注意:该程序使用了一个不同的提示符%以用来与shell提示符区别。
   $ ./a.out
   % date
   Sun Aug 1 03:04:47 EDT 2004            programmers work late
   % who
   sar     :0       Jul 26 22:54
   sar     pts/0    Jul 26 22:54 (:0)
   sar     pts/1    Jul 26 22:54 (:0)
   sar     pts/2    Jul 26 22:54 (:0)
   % pwd
   /home/sar/bk/apue/2e
   % ls
   Makefile
   a.out
   shell1.c
   % ^D                                   键入文件结束符
   $                                          常规的shell提示符

符号^D被用来表示一个控制字符(control character)。控制字符是同时按下ctrl和另一个键形成的。ctrl-D或者^D是默认的文件结束字符(end-of-file character)。当我们在18章讨论终端I/O的时候将看到更多的控制字符。

线程和线程ID(Threads and Thread IDs)
通常,一个进程只有一个控制线程(thread of control),来控制每次执行一组机器指令。用多线程来操作一个问题的不同部分会使得该问题变得简单。此外,在多处理器系统中,多个控制线程(multiple threads of control)能够exploit the parallelism。
一个进程内的所有线程share同样的地址空间,文件描述符,栈和其他的一些与进程相关的属性。因为他们能够访问同一块的内存,所以这些线程需要在彼此之间同步对共享数据的访问,以避免冲突(inconsistencies)。
与进程一样,线程也是由ID来标识。但是,线程ID(thread IDs)对一个进程来说是local的。一个进程内的一个线程ID在另一个进程内没有意义。我们使用线程ID来标识特定的线程是限于当我们操作一个进程内的线程时的。
控制线程的函数与控制进程的函数是parallel的。线程是在进程模型(process model)被建立之后很久才加入到UNIX系统中的,但是线程模型(thread model)和进程模型仍有一些复杂的交互作用(interactions),我们将在12章看到。