1.7 错误处理
来源: BlogBus 原始链接: http://pigworld.blogbus.com:80/s1066266/ 存档链接: https://web.archive.org/web/20061104011726id_/http://pigworld.blogbus.com:80/s1066266/
PIGWORLD C++,算法,Linux 2006-01-10 1.7 错误处理 TAG: 《UNIX环境高级编程(第二版)》翻译 1.7 错误处理 当 UNIX 系统函数发生错误时,通常返回一个负值,并且整数 errno 被设置为一个可以给出额外信息的值。例如, open 函数或者返回一个非负的文件描述符(当一切正常时),或者产生一个错误。一个 open 的错误能产生 15 个可能的 errno 值,例如文件不存在,权限问题,等等。一些函数不返回负值,而是使用习惯方法来表示错误。例如,多数函数返回一个对象的指针,而返回一个 null (空)指针来表示一个错误。 头文件 <errno.h> 定义了标识符 errno 和 errno 的每个可能的常量值。这之中的每个常量值都以字符 E 开头。在 UNIX 系统手册第二节的第一页,名为 intro (2) 的页面中,同样列出了这之中所有的错误常量。例如,如果 errno 等于常量 EACCES ,这就显示了一个权限错误,比如没有足够的权限来打开所请求的文件。 在 Linux 中,错误常量被列举在手册 errno (3) 中。 POSIX 和 ISO C 把 errno 扩展定义为可变的整型左值。它既可以是一个包含了错误代码的整数,也可以是一个函数,该函数返回指向错误代码的指针。以前的定义是 extern int errno; 然而在一个支持线程的环境中,进程地址空间在多个线程中共享,同时每个线程都需要 errno 的本地拷贝来防止线程间互相影响。例如, Linux 通过以下定义来支持多线程访问 errno : extern int _ _errno_location(void); #define errno (_ _errno_location()) errno 有两条规则。第一,如果不发生错误, errno 的值决不会被程序清除。因此,只有在函数的返回值表示错误发生时,才需要检查 errno 的值。第二,任何函数都不会把 errno 的值设置为 0 ,同时在 <errno.h> 中也没有定义任何常量值为 0 。 标准 C 中定义了两个函数来帮助打印错误消息。 #include <string.h> char *strerror(int errnum); 返回值:指向消息字符串的指针 该函数把 errno 的典型值 errnum 映射到一个错误消息字符串,并返回一个指向字符串的指针。 perror 函数在标准错误产生并返回一个错误消息,该消息基于 errno 的当前值。 #include <stdio.h> void perror(const char *msg); 它输出 msg 指向的字符串,接着是一个分号和一个空格,然后是与 errno 值对应的错误消息,最后是一个新行。 例子 图 1.8 展示了这两个函数的应用。 如果该程序被编译为文件 a.out ,我们将看到 $ ./a.out EACCES: Permission denied ./a.out: No such file or directory 注意我们把程序名字 argv[0] 作为参数传递给 perror , argv[0] 的值是 ./a.out 。这是 UNIX 系统的一个标准惯例。通过这样做,如果程序是作为管道的一部分执行,就像在 prog1 < inputfile | prog2 | prog3 > outputfile 我们就能够分清是三个程序中是哪个产生了错误消息。 1.8 strerror 和 perror 的示范 1 #include " apue.h " 2 #include < errno.h
3 4 int 5 main( int argc, char * argv[]) 6 { 7 fprintf(stderr, " EACCES: %s\n " , strerror(EACCES)); 8 errno
ENOENT; 9 perror(argv[ 0 ]); 10 exit( 0 ); 11 } 图 本书中的所有的例子都使用附录 B 中的错误函数,来代替直接调用 strerror 或者 perror 。附录中的错误函数使用了 ISO C 的可变参数列表,可以只用单个 C 语句来处理错误情况。 错误恢复 <errno.h> 中定义的错误可以被分为两类:致命的和非致命的。一个致命错误是不能够恢复的。最好的办法是在用户的屏幕上打印一条错误消息,或者在一个日志文件中写入错误消息,接着在退出。另一方面,非致命错误在某些时候能够更得体的处理。多数非致命错误是自然的临时错误,例如当系统的活动程序较少时,(系统)资源短缺的错误可能就不会发生。 资源相关的非致命错误包括 EAGAIN , ENFILE , ENOBUFS , ENOLCK , ENOSPC , ENOSR , EWOULDBLOCK ,当 ENOMEM , EBUSH 表示一个共享资源正在被使用时,它们也可以被作为非致命错误。某些时候,当 EINT R 中断了一个缓慢的系统调用时,它也可以被看作非致命错误(详见 10.5 节)。 资源相关的非致命错误的典型恢复动作就是延迟一会儿再试。这个技巧也能应用在其它循环中。例如,如果错误表示网络连接没有工作,那么程序可能会延迟一会儿再重新建立连接。一些程序使用指数增长的算法,每次等待更长的时间。 最后,由应用程序开发者来决定哪些错误是可以恢复的。如果一个合理的策略能够被用来恢复错误,通过避免异常退出,我们就可以增强我们程序的健壮性。 Pigwen @ 20:07:55 | 阅读全文 | 评论 0 | 引用 0 | 编辑 2006-01-06 1.6 程序和进程 TAG: 《UNIX环境高级编程(第二版)》翻译 1.6 程序和进程 程序 程序是存在于磁盘上目录中的可执行文件。 6 个 exec 函数中的任意一个,都可以将一个程序读入内存中并由内核执行(感觉这句没有翻译好,原句是: A program is read into memory and is executed by the kernel as a result of one of the six exec functions. )。我们将在 8.10 节中介绍这些函数。 进程和进程 ID 一个正在执行中的程序实例被称为进程( process ),该词语(进程)几乎会出现在本书中的每一页。一些操作系统用任务( task )来称呼一个正在运行中的程序。 UNIX 系统确保每一个进程都拥有一个唯一的数字标识符,称为进程 ID 。进程 ID 总是非负整数。 例子 图 1.6 中的程序打印出它的进程 ID 。 如果我们把程序编译成 a.out 并执行它,我们会看到 $ ./a.out hello world from process ID 851 $ ./a.out hello world from process ID 854 该程序运行时调用 getpid 函数来获得进程 ID 。 图 1.6 打印进程 ID 进程控制 进程控制主要使用三个函数: fork , exec 和 waitpid 。( exec 函数有 6 个变体,我们通常把它们统称为 exec 函数。) 例子 使用一个简单的程序(图 1.7 )来展示 UNIX 系统的进程控制特性,该程序从标准输入读取命令并且执行这些命令。这是一个类似 shell 程序的本质(翻译得不好,原句是: This is a bare-bones implementation of a shell-like program. )。在这个 30 行的程序中有许多特性值得思考。 l 使用标准 I/O 函数 fgets ,一次从标准输入中读取一行。当输入文件终止符(通常是 Control-D )作为一行的第一个字符时, fgets 返回 null 指针,同时终止循环,接着终止进程。在 18 章中,我们描述所有特殊的终端字符(文件终止符,退格字符,整行擦除字符等等),并且介绍怎样改变它们。 l fgets 返回的每一行都终止于一个换行符和跟在换行符后面的一个 null 字节,我们使用标准的 C 函数 strlen 来计算字符串的长度,接着把换行符替换为一个 null 字节。这样做是因为 execlp 函数需要一个以 null 结束的参数,而不是以换行符结束的参数。 l 调用 fork 函数来建立一个进程,这个进程是一个调用者的拷贝。我们把调用者称为父进程,把新建立的进程称为子进程。那么 fork 函数返回子进程的非负进程 ID 给父进程,同时返回 0 给子进程。因为 fork 创建了一个新进程,我们说 fork 被调用一次就返回两次,一次返回给父进程,一次返回给子进程。 l 在子进程中,调用 execlp 来执行从标准输入读取的命令。这就把子进程替换为一个新的程序文件。 fork 函数后跟 exec 函数的组合,就是一些操作系统所谓的产生一个新进程。(翻译得不好,原句是: This replaces the child process with the new program file. The combination of a fork , followed by an exec , is what some operating systems call spawning a new process. )在 UNIX 系统中,这两部分被分为了独立的函数。第八章中将会介绍更多这方面的内容。 l 因为子进程调用 execlp 来执行新的程序文件,父进程就会等待直到子进程结束。这些工作由 waitpid 函数完成。 waitpid 函数中的 pid 参数代表了子进程的进程 ID ,该参数用来标识出需要等待的进程。 waitpid 函数也可以得到子进程的终止状态。在这个简单程序中的的 status 变量就代表了子进程的终止状态,在程序中我们没有使用这个值,但是我们可以通过检查这个值来确定子进程的终止状态。 l 这个程序最根本的限制在于我们不能像我们所执行的命令传递参数。例如,不能列举特定的目录。我们只可以在工作目录执行 ls 命令。为了能够传递参数,我们需要分析输入行,按某种习惯(可能的情况是使用空格或者制表符)区别出参数,接着把每一个参数作为独立的参数传递给 execlp 函数。尽管如此,这个程序仍然对 UNIX 系统进程控制函数进行了有用说明。 运行这个程序,我们得到下面的输出。注意我们的程序有一个不同的提示符,使用百分号( % )来区别于 shell 的提示符。 $ ./a.out % date Sun Aug 1 03:04:47 EDT 2004 % 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 提示符 图 1.7 从标准输入读取命令并执行它们 符号 ^D 用代表一个控制字符。控制字符是一种特殊的的字符,可以通过同时按下控制键(在你的计算机上通常为 Control 键或 Ctrl 键)和另一个键来产生它。 Control-D ,或者说 ^D ,是默认的文件终止符。在 18 章讨论终止 I/O 的时候会看到更多的控制字符。 线程和线程 ID 通常,一个进程只有一个线程(原句是: Usually, a process has only one thread of control one set of machine instructions executing at a time. 不知道怎么翻译,留给大虾翻译)。当有多于一个的线程来控制不同部分时,一些问题就变得很容易解决。另外,多线程在多处理器系统上能够获得平衡性(又一句翻译得不爽: Additionally, multiple threads of control can exploit the parallelism possible on multiprocessor systems. )。 一个进程中的所有线程共享同一个地址空间,文件描述符,栈和进程相关的属性。因为能够访问相同的内存,线程必须同步访问它们自己的共享数据,以避免冲突。 和进程一样,线程由线程 ID 标识。尽管如此,线程 ID 对于进程 ID 来说是本地的。也就是说,一个进程中的线程 ID 在另一个进程中是没有意义的。当我们在进程中操作线程的时候,使用线程 ID 来指出特定的线程。 控制线程的函数和控制进程的函数是一样的。在进程模型建立很久以后,线程才被加入到 UNIX 系统中,然而,线程模型和进程模型之间有一些复杂的交互,在 12 章将会看到这些。 Pigwen @ 00:28:29 | 阅读全文 | 评论 0 | 引用 0 | 编辑 2006-01-02 1.5 输入和输出 TAG: 《UNIX环境高级编程(第二版)》翻译 1.5 输入和输出 文件描述符 文件描述符通常是小的且非负的整数,内核使用它来标识能被特定进程访问的文件。只要打开或创建了新文件,内核就返回一个文件描述符,我们使用它来读写文件。 标准输入,标准输出和标准错误 通常,一旦一个新的程序运行,所有的 shell 就为其打开三个描述符:标准输入,标准输出和标准错误。如果不做什么特别的事,就像下面这样一条简单的命令: ls 那么所有的三个描述符都连接到终端。多数 shell 提供了一种方式来重定向三个描述符到文件。例如: ls > file.list 执行 ls 命令,并把它的标准输出重定向到名为 file.list 的文件。 无缓冲 I/O 无缓冲 I/O 由 open 函数, read 函数, write 函数, lseek 函数和 close 函数提供。这些函数都需要文件描述符才能工作。 例子 如果我们想要从标准输入读取,从标准输出写入,那么图 1.4 中的程序就可以拷贝 UNIX 系统中的常规文件。 apue.h 中包含的 头文件,以及两个常量 STDIN_FILENO 和 STDOUT_FILENO 都是 POSIX 标准(下章中将会详细介绍)的一部分。在该头文件中有许多 UNIX 系统服务的函数的原型,例如我们调用的 read 和 write 函数。 常量 STDIN_FILENO 和 STDOUT_FILENO 在<unistd.h> 中定义,它们指定了标准输入和标准输出的文件描述符。通常, STDIN_FILENO 和 STDOUT_FILENO 的值分别为 0 和 1 ,然而,在便携设备开发中,我们使用新的名字来表示它们。 3.9 节中会详细讨论 BUFFSIZE 常量,将看到它的不同值是如何影响一个程序的效率的。尽管如此,无论 BUFFSIZE 为何值,图 1.4 中的程序都会拷贝常规文件。 read 函数返回读取的字节数,这个值被用来作为要写入的字节数。当到达输入文件的末尾时, read 函数返回 0 ,程序停止。如果读取时发生错误, read 函数返回 -1 。多数系统函数在发生错误时返回 1 。 如果像下面这样按标准名字( a.out )编译我们的程序 ./a.out > data 标准输入就是终端,而标准输出被重定向到文件 data ,同时标准错误也是终端。如果这个输出文件不存在,那么 shell 将默认创建它。除非输入终止符(通常是 Control-D ),程序将拷贝输入到标准输出。 如果我们运行 ./a.out < infile > outfile 那么文件 finfile 将被拷贝到文件 outfile 。 图 1.4 列出目录中的所有文件 第三章中将详细描述无缓冲 I/O 函数。 标准 I/O 标准 I/O 函数为无缓冲 I/O 函数提供了缓冲接口。使用标准 I/O 可以防止在选择合适缓冲大小的时候出现错误,例如图 1.4 中的 BUFFSIZE 常量。使用标准 I/O 函数的另一个优点是其仅仅处理输入的行( UNIX 应用程序中的通常事件)。例如 fgets 函数,它读取整个一行。另一方面, read 函数读取指定的字节数。就像将 5.4 节中看到的那样,标准 I/O 库提供的函数让我们能够控制缓冲的使用风格。 最常见的标准 I/O 函数就是 printf 。如果在程序中调用 printf 函数,可以通过包含 apue.h 来包含 头文件, 头文件中包含了所有标准 I/O 函数的原型。 例子 图 1.5 中的程序会在 5.8 节中详细讨论。该程序像前面的程序那样调用 read 何 write 函数。该程序拷贝标准输入到标准输出,同时也能够拷贝任何常规文件。 getc 函数一次读取一个字符,该字符被 putc 函数写入。在最后一个输入的字节被读取后, getc 返回常量 EOF ( 中定义)。标准 I/O 常量 stdin 和 stdout 也在 头文件中定义,它们分别表示标准输入和标准输出。 图 1.5 使用标准 I/O 拷贝标注输入到标注输出 Pigwen @ 01:17:55 | 阅读全文 | 评论 0 | 引用 0 | 编辑 2006-01-02 1.4 文件与目录 TAG: 《UNIX环境高级编程(第二版)》翻译 1.4 文件与目录 文件系统 UNIX 文件系统是按层次安排目录和文件的。起始目录被称为根( root ),它的名字是一个字符 / 。 目录是一个包含目录项的文件。在逻辑上,可以认为每个目录项都包含一个文件名,同时还包含说明该文件属性的信息。文件属性是:文件类型,文件长度,文件所有者,文件的许可权(例如,其他用户能否能访问该文件) , 文件最后的修改时间等。 start 和 fstat 函数返回一个包含所有文件属性的信息结构。第 4 章将详细说明文件的各种属性。 目录项事实上存储在磁盘上,对此区别于目录项的逻辑看法。多数 UNIX 文件系统的实现并不在目录项中存储该目录项自己的属性,因为当文件有很多硬连接的时候,很难做到同步保存该文件的属性。当我们在第四章讨论硬连接后,对此将有清晰的认识。 文件名 目录中的各个名字被称为文件名。唯一两个不能出现在文件名中的字符是反斜杠( / )和空字符( null )。反斜杠用来把文件名从路径名中区别出来,空字符用来结束一个路径名。因此,把文件名中可用的字符限制在一般打印字符的子集中是一个好习惯。(我们限制可用字符还因为,如果我们在文件名中使用了 shell 的特殊字符,我们就必须使用 shell 的引号机制来引用文件名,这将导致问题复杂化。) 被反斜杆区分开,并且可选的以反斜杆开头一个或多个文件名的序列,构成了路径名。以反斜杆起头的路径名称为绝对路径;否则,称为相对路径。相对路径涉及到的文件相对于当前目录。文件系统的根( / )的名字是绝对路径的特例,它没有文件名。 例子 列出一个目录中所有文件的名字并不困难。图 1.3 展示了 ls ( 1 )命令实现的本质。 ls ( 1 )符号是 UNIX 系统手册的一般表示方法,用来引用特定的项。它引用第一小节中的 ls 项。小节号通常用数字 1 到 8 表示,同时每一小节中的所有项都是按字母顺序排列的。本书中,我们假定你有一份你的 UNIX 系统手册的拷贝。 历史上, UNIX 系统把所有 8 个小节集中在 UNIX 程序员手册 中。随着页数的增加,趋势变为把各节安排在不同的手册中:例如一份为用户准备的,一份为程序员准备的,一份为系统管理员准备的。 一些 UNIX 系统使用大写字母在已有的小节中进一步细分手册。举例来说,所有 AT&T 中的标准输出 / 输入 (I/O) 函数在 3S 小节中,比如 fopen ( 3S )。其它系统用字母来代替数字表示小节号,例如用 C 表示命令。 今天,许多手册用电子形式进行发行。如果你的手册是联机手册,查看手册中 ls 命令的方式或许是这样的: man 1 ls 或者 man –s1 ls 图 1.3 是一个程序,其打印出目录中各个文件的名字。如果源码文件的名字是 myls.c ,我们按照以下方式把它编译成默认的可执行文件 a.out : cc myls.c cc ( 1 )是早期的 C 编译器。在支持 GNU C 编译器的系统上, gcc ( 1 )是 C 编译器。这里, cc 通常与 gcc 相连接(译者注:原句是 Here , cc is often linked to gcc 。没翻译好,期待哪位大虾指教)。 一些输出例子如下: $ ./a.out /dev . .. console tty mem kmem null mouse stdin stdout stderr zero 省略其它未显示的行 cdrom $ ./a.out /var/spool/cron can’t open /var/spool/cron: Permission denied $ ./a.out .dev/tty can’t open /dev/tty: Not a directory 像上面那样,我们会以如下的方式展示运行的命令和该命令的输出:输入的字符以 红色加粗字体显示 ,而程序的输出以 红色字体 显示。如果对于输出需要加注释,我们会以 红色斜体字体 显示注释。输入行前面的美元符号是 shell 打印出来的提示符。我们始终使用美元符号作为 shell 提示符。 注意文件名并没有按照字母顺序输出。而 ls 命令会在打印名字之前现对名字排序。 在这个 20 行的程序中,许多细节应当被考虑: l 首先,我们包含了一个我们自己编写的头文件: apue.h 。几乎本书中所有的程序都包含这个头文件。该头文件包含了一些标准系统头文件,并且定义了本书例子中使用的数值常量和函数原型。该头文件的清单在附录 B 中。 l main 函数的声明使用了 ISO C 标准的风格。(下章中我们会谈到更多 ISO C 标准。) l 我们从命令行得到参数 argv[1] 作为对象目录的名字。在第七章,我们将看到 main 函数是怎样被调用的,同时看到命令行参数和环境变量是如何传递给程序的。 l 由于不同 UNIX 系统的目录项格式不尽相同,我们使用 opendir 函数, readdir 函数和 closedir 函数来操作目录。 l opendir 函数返回 DIR 结构的指针,并传递该指针给 readdir 函数。不必关心 DIR 结构的细节。接着在循环中调用 readdir 函数,用来读取每个目录项。 readdir 函数返回一个 dirent 结构的指针,否则,在无目录项可读时返回 null 指针。我们只需要检查 dirent 结构中每个目录项的名字( d_name )。使用这个名字,我们就可以调用 stat 函数( 4.2 节介绍)来确定文件的所有属性。 l 我们调用两个我们自己编写的函数来处理错误: err_sys 和 err_quit 。从图 1.3 的输出中我们可以看到, err_sys 函数打印出了丰富的信息来描述遇到的错误(“ Permission denied ”和“ Not a directory ”)。附录 B 中列出了这两个错误处理函数。在 1.7 节中我们将更详细的谈到错误处理。 l 当程序完成时,以参数 0 调用 exit 函数。 exit 函数结束一个程序。通常,参数 0 意味者正常结束,而 1 到 255 之间的参数意味着发生了错误。 8.5 节中,我们将会学习一个程序(比如 shell 或者我们自己写的程序)如何获得另一个正在执行中的程序的 exit 状态。 图 1.3 列举出一个目录中的所有文件 工作目录 每个进程都有一个工作目录,有时又称为当前工作目录。所有的相对路径都从该目录开始。一个进程能够用 chdir 函数改变它的工作目录。 例如,相对路径 doc/memo/joe 指出 memo 目录中的文件或目录 joe , memo 目录又在 doc 目录中,而 doc 一定是工作目录下的一个目录。查看这个相对路径,我们知道 doc 和 memo 一定是目录,但是我们不能确定 joe 是一个文件还是一个目录。路径 /usr/lib/lint 是一个绝对路径,它指出了 lib 目录中的文件或目录 lint , lib 目录在 usr 目录中,而 usr 目录又在根( root )目录中。 起始( Home )目录 当我们登陆时,工作目录被设定为我们的起始目录。我们的起始目录是从密码文件( 1.3 节介绍)中我们的登陆项得到的。 Pigwen @ 01:12:27 | 阅读全文 | 评论 0 | 引用 0 | 编辑 2006-01-02 1.3 登陆 TAG: 《UNIX环境高级编程(第二版)》翻译 1.3 登陆 登陆名 当我们登陆 UNIX 系统时,我们输入我们的登陆名,然后是口令。系统接着就在密码文件中扫描我们的登陆名,密码文件通常位于 /etc/passwd 。如果我们在密码文件中查看我们的登陆项,将会看到它是由 7 个冒号分离的部分组成的:登陆名,加密口令,用户的数字 ID ( 205 ),组的数字 ID ( 105 ),一个注释段,起始目录( /home/sar ),还有 shell (注:以后直接用 shell 代表解释器)程序( /bin/ksh )。 sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh 所有新的系统已经把加密口令移到另一个文件中了。在第六章,我们将查看这些文件和一些函数来访问他们。 Shell 一旦我们登陆后,一些特定的系统信息就显示出来,接着我们就可以在 shell 中输入命令。(一些系统在你登陆后,会启动窗口管理程序,但是一般来说,你会以在其中一个窗口中运行 shell 来结束。) shell 是一个命令行解释器,它读入用户的输入并且执行命令。用户通常从终端(一个交互式的 shell ),或者某些时候从一个文件(被称为 shell 教本)向 shell 输入。通常使用的 shell 总结在图 1.2 中。 名字 路径 FreeBSD 5.2.1 Linux 2.4.22 Mac OS X 10.3 Solaris 9 Bourne shell /bin/sh * link to bash Link to bash * Bourne-again shell /bin/bash Optional * * * C shell /bin/csh link to tcsh Link to tcsh link to tcsh * Korn shell /bin/ksh * TENEX C shell /bin * * * * 图 1.2 UNIX 系统常用 shell 系统从密码文件中登录项的最后一个字段中了解到应该执行哪一个 shell 。 Bourne shell ,由 Steve Bourne 在贝尔实验室开发,自 Version 7 以来一直在使用,并且被目前几乎所有的 UNIX 系统所支持。 The control-flow constructs of the Bourne shell are reminiscent of Algol 68 。 C shell 是由 Bill Joy 在伯克利开发的,被所有的 BSD 发行版支持。另外, C shell 也被 AT&T 的 System V/386 Release 3.2 和 System V Release 4 ( SVR4 )所支持。(我们将在下一章中更详细介绍这两个版本 UNIX 系统的不同。) C shell 构建于第六版的 shell ,而不是 Bourne shell 。它的 control flow 更像 C 语言,它也支持一些其它的, Bourne shell 所不支持的特性: job control, a history mechanism, and command line editing 。 Korn shell 被认为是对 Bourne shell 的成功者,它最先被 SVR4 所支持。 Korn shell 是由 David Korn 在贝尔实验室开发的,运行于大多数的 UNIX 系统上。在 SVR4 之前, Korn shell 是一个收费组件,所以它并没有其它两个 shell 流行。它也和 Bourne shell 兼容,同时包含了使得 C shell 流行的那些特性: job control, command line editing, and so on 。 Bourne-again shell 是由所有 Linux 系统所支持的 GNU shell 。它被设计为与 POSIX 一致,同时仍然与 Bourne shell 兼容。它同时支持 C shell 和 Korn shell 的特性。 TENEX C shell 是 C shell 的增强版本。它从 TENEX 操作系统( 1972 年开发于 Bolt Beranek 和 Newman )借用了许多特性,比如命令补全。 TENEX C shell 对于 C shell 增加了许多特性,其通常被作为 C shell 的替代品。 Linux 使用 Bourne-again shell 作为默认 shell 。事实上, /bin/sh 就是 /bin/bash 的连接。 FreeBSD 和 Mac OS X 的默认 shell 是 TENEN C shell ,由于 C shell 的程序设计语言被广泛的认为很难使用,所有他们使用 Bourne shell 作为他们的系统( administrative ) shell 脚本。 Solaris 有继承于 BSD 和 System V 的,在图 1.2 中所列出的所有 shell 。大多数这些 shell 的免费部分能够在因特网上得到。 通过本书,我们将展示互动的 shell 例子来执行我们所开发的程序。这些例子使用 Bourne shell , Korn shell 和 Bourne-again shell 的共同特性。 Pigwen @ 01:10:30 | 阅读全文 | 评论 0 | 引用 0 | 编辑 2006-01-02 1.2 UNIX结构 TAG: 《UNIX环境高级编程(第二版)》翻译 1.2 UNIX 结构 严格的来说,操作系统被定义为操控计算机硬件和为程序提供运行环境的软件。一般来说,我们称改软件为内核( kernel ),因为其相对较小并居于运行环境的核心。图 1.1 展示了 UNIX 系统的架构。 图 1 .1 UNIX 操作系统的结构 内核的接口是一个软件层,被称为系统调用【 system calls 】(图 1.1 中阴影部分)。常用的函数库就被构建在系统调用接口之上,但是应用程序可以任意地调用他们二者。(我们将在 1.11 节更多的介绍系统调用和常用函数库。)解释器( shell )是一个特殊的应用程序,其用来为其它程序的运行提供接口。 更一般来说,一个操作系统就是内核和所有其它使得计算机易用并有个性的软件的组合。这些其它的软件包括系统工具,应用程序,解释器( shell ),常用函数库,等等。 举例来说, Linux 就是被 GNU 操作系统使用的内核。一些人称此为 GNU/Linux 操作系统,然而其通常的被简称为 Linux 。尽管这种称法严格来说并不正确,然而这种称法却更容易理解,赋予了词组操作系统双重含义。(另一个优点是这样更简洁。) Pigwen @ 01:03:56 | 阅读全文 | 评论 0 | 引用 0 | 编辑 2006-01-02 1.1 简介 TAG: 《UNIX环境高级编程(第二版)》翻译 1.1 简介 所有操作系统为程序的运行提供服务。典型的服务包括执行一个新的程序,打开文件,阅读文件,申请内存,获得当前时间,等等。本文集中于描述不同版本 UNIX 操作系统提供的服务。 以严格的步进方式描述 UNIX 系统,不对任何 未学习 过的词汇进行超前引用几乎是不可能的(或许也是乏味的)。本章从一个程序员的角度对 UNIX 系统进行快速浏览。并对书中引用的一些术语和概念进行简要的说明并给出实例。在以后各章中,将对这些概念作更详细的说明。对于不熟悉 UNIX 的程序设计人员,本书简要介绍了 UNIX 提供的各种服务。 Pigwen @ 01:02:30 | 阅读全文 | 评论 0 | 引用 0 | 编辑 2006-01-02 第一章 UNIX系统概览 TAG: 《UNIX环境高级编程(第二版)》翻译 第一章 UNIX 系统概览 1.1 简介 1.2 UNIX 结构 1.3 登陆 1.4 文件与目录 1.5 输入和输出 Pigwen @ 00:59:54 | 阅读全文 | 评论 0 | 引用 0 | 编辑 分页 : 访问统计: