??xml version="1.0" encoding="utf-8" standalone="yes"?> ?/SPAN>UNIXpȝ函数发生错误Ӟ通常q回一个负|q且整数errno被设|ؓ(f)一个可以给出额外信息的倹{例如,open函数或者返回一个非负的文g描述W(当一切正常时Q,或者生一个错误。一?/SPAN>open的错误能产生15个可能的errno|例如文g不存在,权限问题Q等{。一些函Cq回负|而是使用?fn)惯?gu)来表C错误。例如,多数函数q回一个对象的指针Q而返回一?/SPAN>nullQ空Q指针来表示一个错误?/SPAN> 头文?/SPAN><errno.h>定义?jin)标识?/SPAN>errno?/SPAN>errno的每个可能的帔R倹{这之中的每个常量值都以字W?/SPAN>E开头。在UNIXpȝ手册W二节的W一,名ؓ(f)intro(2)的页面中Q同样列Z(jin)q之中所有的错误帔R。例如,如果errno{于帔REACCESQ这显CZ(jin)一个权限错误,比如没有_的权限来打开所h的文件?/SPAN> ?/SPAN>Linux中,错误帔R被列丑֜手册errno(3)中?/SPAN> POSIX?/SPAN>ISO C?/SPAN>errno扩展定义为可变的整型左倹{它既可以是一个包含了(jin)错误代码的整敎ͼ也可以是一个函敎ͼ该函数返回指向错误代码的指针。以前的定义?/SPAN> extern int errno; 然而在一个支持线E的环境中,q程地址I间在多个线E中׃nQ同时每个线E都需?/SPAN>errno的本地拷贝来防止U程间互相媄(jing)响。例如,Linux通过以下定义来支持多U程讉KerrnoQ?/SPAN> extern int *_ _errno_location(void); #define errno (*_ _errno_location()) errno有两条规则。第一Q如果不发生错误Q?/SPAN>errno的值决不会(x)被程序清除。因此,只有在函数的q回DC错误发生时Q才需要检?/SPAN>errno的倹{第二,M函数都不?x)?/SPAN>errno的D|ؓ(f)0Q同时在<errno.h>中也没有定义M帔Rgؓ(f)0?/SPAN> 标准C中定义了(jin)两个函数来帮助打印错误消息?/SPAN> #include <string.h> char *strerror(int errnum); q回|(x)指向消息字符串的指针 该函数把errno的典型?/SPAN>errnum映射C个错误消息字W串Qƈq回一个指向字W串的指针?/SPAN> perror函数在标准错误生ƈq回一个错误消息,该消息基?/SPAN>errno的当前倹{?/SPAN> #include <stdio.h> void perror(const char *msg); 它输?/SPAN>msg指向的字W串Q接着是一个分号和一个空|然后是与errno值对应的错误消息Q最后是一个新行?/SPAN> ?/SPAN>1.8展示?jin)这两个函数的应用?/SPAN> 如果该程序被~译为文?/SPAN>a.outQ我们将看到 $ ./a.out EACCES: Permission denied ./a.out: No such file or directory 注意我们把程序名?/SPAN>argv[0]作ؓ(f)参数传递给perrorQ?/SPAN>argv[0]的值是./a.out。这?/SPAN>UNIXpȝ的一个标准惯例。通过q样做,如果E序是作为管道的一部分执行Q就像在 prog1 < inputfile | prog2 | prog3 > outputfile 我们p够分清是三个E序中是哪个产生?jin)错误消息?BR>
?/SPAN> 1.8 strerror?/SPAN>perror的示?BR> 本书中的所有的例子都用附?/SPAN>B中的错误函数Q来代替直接调用strerror或?/SPAN>perror。附录中的错误函C用了(jin)ISO C的可变参数列表,可以只用单个C语句来处理错误情c(din)?/SPAN> <errno.h>中定义的错误可以被分Zc:(x)致命的和非致命的。一个致命错误是不能够恢复的。最好的办法是在用户的屏q上打印一条错误消息,或者在一个日志文件中写入错误消息Q接着在退出。另一斚wQ非致命错误在某些时候能够更得体的处理。多数非致命错误是自然的临时错误Q例如当pȝ的活动程序较?yu)时Q(pȝQ资源短~的错误可能׃?x)发生?/SPAN> 资源相关的非致命错误包括EAGAINQ?/SPAN>ENFILEQ?/SPAN>ENOBUFSQ?/SPAN>ENOLCKQ?/SPAN>ENOSPCQ?/SPAN>ENOSRQ?/SPAN>EWOULDBLOCKQ当ENOMEMQ?/SPAN>EBUSH表示一个共享资源正在被使用Ӟ它们也可以被作ؓ(f)非致命错误。某些时候,?/SPAN>EINTR中断?jin)一个缓慢的pȝ调用Ӟ它也可以被看作非致命错误Q详?/SPAN>10.5节)(j)?/SPAN> 资源相关的非致命错误的典型恢复动作就是gq一?x)儿再试。这个技巧也能应用在其它循环中。例如,如果错误表示|络q接没有工作Q那么程序可能会(x)延迟一?x)儿再重新徏立连接。一些程序用指数增长的法Q每ơ等待更长的旉?/SPAN> 最后,由应用程序开发者来军_哪些错误是可以恢复的。如果一个合理的{略能够被用来恢复错误,通过避免异常退出,我们可以增强我们程序的健壮性?/SPAN> E序是存在于盘上目录中的可执行文g?/SPAN>6?/SPAN>exec函数中的L一个,都可以将一个程序读入内存中q由内核执行Q感觉这句没有翻译好Q原句是Q?/SPAN>A program is read into memory and is executed by the kernel as a result of one of the six exec functions.Q。我们将?/SPAN>8.10节中介绍q些函数?/SPAN> 一个正在执行中的程序实例被UCؓ(f)q程Q?/SPAN>processQ,该词语(q程Q几乎会(x)出现在本书中的每一c(din)一些操作系l用dQ?/SPAN>taskQ来U呼一个正在运行中的程序?/SPAN> UNIXpȝ保每一个进E都拥有一个唯一的数字标识符Q称E?/SPAN>ID。进E?/SPAN>IDL非负整数?/SPAN> ?/SPAN>1.6中的E序打印出它的进E?/SPAN>ID?/SPAN> 如果我们把程序编译成a.outq执行它Q我们会(x)看到 $ ./a.out hello world from process ID 851 $ ./a.out hello world from process ID 854 该程序运行时调用getpid函数来获得进E?/SPAN>ID?BR> ?/SPAN> 1.6 打印q程ID
例子
#include "apue.h"
2#include <errno.h>
3
4int
5main(int argc, char *argv[])
6{
7 fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
8 errno = ENOENT;
9 perror(argv[0]);
10 exit(0);
11}
错误恢复
]]>E序
q程和进E?/SPAN>ID
例子
2
3 int
4 main(void)
5 {
6 printf("hello world from process ID %d\n", getpid());
7 exit(0);
8 }
q程控制主要使用三个函数Q?/SPAN>forkQ?/SPAN>exec?/SPAN>waitpid。(exec函数?/SPAN>6个变体,我们通常把它们统UCؓ(f)exec函数。)(j)
使用一个简单的E序Q图1.7Q来展示UNIXpȝ的进E控制特性,该程序从标准输入d命o(h)q且执行q些命o(h)。这是一个类?/SPAN>shellE序的本质(译得不好,原句是:(x)This is a bare-bones implementation of a shell-like program.Q。在q个30行的E序中有许多Ҏ(gu)值得思考?/SPAN>
l 使用标准I/O函数fgetsQ一ơ从标准输入中读取一行。当输入文gl止W(通常?/SPAN>Control-DQ作Z行的W一个字W时Q?/SPAN>fgetsq回null指针Q同时终止@环,接着l止q程。在18章中Q我们描q所有特D的l端字符Q文件终止符Q退格字W,整行擦除字符{等Q,q且介绍怎样改变它们?/SPAN>
l fgetsq回的每一行都l止于一个换行符和跟在换行符后面的一?/SPAN>null字节Q我们用标准的C函数strlen来计字W串的长度,接着把换行符替换Z?/SPAN>null字节。这样做是因?/SPAN>execlp函数需要一个以nulll束的参敎ͼ而不是以换行W结束的参数?/SPAN>
l 调用fork函数来徏立一个进E,q个q程是一个调用者的拯。我们把调用者称为父q程Q把新徏立的q程UCؓ(f)子进E。那?/SPAN>fork函数q回子进E的非负q程IDl父q程Q同时返?/SPAN>0l子q程。因?/SPAN>fork创徏?jin)一个新q程Q我们说fork被调用一ơ就q回两次Q一ơ返回给父进E,一ơ返回给子进E?/SPAN>
l 在子q程中,调用execlp来执行从标准输入d的命令。这把子进E替换ؓ(f)一个新的程序文件?/SPAN>fork函数后跟exec函数的组合,是一些操作系l所谓的产生一个新q程。(译得不好,原句是:(x)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.Q在UNIXpȝ中,q两部分被分Z(jin)独立的函数。第八章中将?x)介l更多这斚w的内宏V?/SPAN>
l 因ؓ(f)子进E调?/SPAN>execlp来执行新的程序文Ӟ父进E就?x)等待直到子q程l束。这些工作由waitpid函数完成?/SPAN>waitpid函数中的pid参数代表?jin)子q程的进E?/SPAN>IDQ该参数用来标识出需要等待的q程?/SPAN>waitpid函数也可以得到子q程的终止状态。在q个单程序中的的status变量׃表了(jin)子进E的l止状态,在程序中我们没有使用q个|但是我们可以通过(g)查这个值来定子进E的l止状态?/SPAN>
l q个E序最Ҏ(gu)的限制在于我们不能像我们所执行的命令传递参数。例如,不能列D特定的目录。我们只可以在工作目录执?/SPAN>ls命o(h)。ؓ(f)?jin)能够传递参敎ͼ我们需要分析输入行Q按某种?fn)惯Q可能的情况是用空格或者制表符Q区别出参数Q接着把每一个参C为独立的参数传递给execlp函数。尽如此,q个E序仍然?/SPAN>UNIXpȝq程控制函数q行?jin)有用说明?/SPAN>
q行q个E序Q我们得C面的输出。注意我们的E序有一个不同的提示W,使用癑ֈP%Q来区别?/SPAN>shell的提C符?/SPAN>
$ ./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 输入文gl止W?/SPAN>
$ shell提示W?BR>
?/SPAN> 1.7 从标准输入读取命令ƈ执行它们
W号^D用代表一个控制字W。控制字W是一U特D的的字W,可以通过同时按下控制键(在你的计机上通常?/SPAN>Control键或Ctrl键)(j)和另一个键来生它?/SPAN>Control-DQ或者说^DQ是默认的文件终止符。在18章讨论终?/SPAN>I/O的时候会(x)看到更多的控制字W?/SPAN>
通常Q一个进E只有一个线E(原句是:(x)Usually, a process has only one thread of control one set of machine instructions executing at a time.不知道怎么译Q留l大虄译)(j)。当有多于一个的U程来控制不同部分时Q一些问题就变得很容易解冟뀂另外,多线E在多处理器pȝ上能够获得^衡性(又一句翻译得不爽Q?/SPAN>Additionally, multiple threads of control can exploit the parallelism possible on multiprocessor systems.Q?/SPAN>
一个进E中的所有线E共享同一个地址I间Q文件描q符Q栈和进E相关的属性。因够访问相同的内存Q线E必d步访问它们自q׃n数据Q以避免冲突?/SPAN>
和进E一PU程qE?/SPAN>ID标识。尽如此,U程ID对于q程ID来说是本地的。也是_(d)一个进E中的线E?/SPAN>ID在另一个进E中是没有意义的。当我们在进E中操作U程的时候,使用U程ID来指出特定的U程?/SPAN>
控制U程的函数和控制q程的函数是一L(fng)。在q程模型建立很久以后Q线E才被加入到UNIXpȝ中,然而,U程模型和进E模型之间有一些复杂的交互Q在12章将?x)看到这些?/SPAN>
文g描述W通常是小的且非负的整敎ͼ内核使用它来标识能被特定q程讉K的文件。只要打开或创Z(jin)新文Ӟ内核p回一个文件描q符Q我们用它来读写文件?/SPAN>
通常Q一旦一个新的程序运行,所有的shell׃ؓ(f)其打开三个描述W:(x)标准输入Q标准输出和标准错误。如果不做什么特别的事,像下面q样一条简单的命o(h)Q?/SPAN>
ls
那么所有的三个描述W都q接到终端。多?/SPAN>shell提供?jin)一U方式来重定向三个描q符到文件。例如:(x)
ls > file.list
执行ls命o(h)Qƈ把它的标准输出重定向到名?/SPAN>file.list的文件?/SPAN>
无缓?/SPAN>I/O?/SPAN>open函数Q?/SPAN>read函数Q?/SPAN>write函数Q?/SPAN>lseek函数?/SPAN>close函数提供。这些函数都需要文件描q符才能工作?/SPAN>
如果我们惌从标准输入读取,从标准输出写入,那么?/SPAN>1.4中的E序可以拷?/SPAN>UNIXpȝ中的常规文g?/SPAN>
apue.h中包含的<unistd.h>头文Ӟ以及(qing)两个帔RSTDIN_FILENO?/SPAN>STDOUT_FILENO都是POSIX标准Q下章中会(x)详细介绍Q的一部分。在该头文g中有许多UNIXpȝ服务的函数的原型Q例如我们调用的read?/SPAN>write函数?/SPAN>
帔RSTDIN_FILENO?/SPAN>STDOUT_FILENO?/SPAN><unistd.h>中定义,它们指定?jin)标准输入和标准输出的文件描q符。通常Q?/SPAN>STDIN_FILENO?/SPAN>STDOUT_FILENO的值分别ؓ(f)0?/SPAN>1Q然而,在便备开发中Q我们用新的名字来表示它们?/SPAN>
3.9节中?x)详l讨?/SPAN>BUFFSIZE帔RQ将看到它的不同值是如何影响一个程序的效率的。尽如此,无论BUFFSIZEZ|?/SPAN>1.4中的E序都会(x)拯常规文g?/SPAN>
read函数q回d的字节数Q这个D用来作ؓ(f)要写入的字节数。当到达输入文g的末时Q?/SPAN>read函数q回0Q程序停止。如果读取时发生错误Q?/SPAN>read函数q回-1。多数系l函数在发生错误时返?/SPAN>1?/SPAN>
如果像下面这h标准名字Q?/SPAN>a.outQ编译我们的E序
./a.out > data
标准输入是l端Q而标准输?gu)重定向到文gdataQ同时标准错误也是终端。如果这个输出文件不存在Q那?/SPAN>shell默认创建它。除非输入终止符Q通常?/SPAN>Control-DQ,E序拷贝输入到标准输出?/SPAN>
如果我们q行
./a.out < infile > outfile
那么文gfinfile被拯到文?/SPAN>outfile?BR>
?/SPAN> 1.4 列出目录中的所有文?/SPAN>
W三章中详l描q无~冲I/O函数?SPAN lang=EN-US>
标准I/O函数为无~冲I/O函数提供?jin)缓冲接口。用标?/SPAN>I/O可以防止在选择合适缓冲大的时候出现错误,例如?/SPAN>1.4中的BUFFSIZE帔R。用标?/SPAN>I/O函数的另一个优Ҏ(gu)其仅仅处理输入的行(UNIX应用E序中的通常事gQ。例?/SPAN>fgets函数Q它d整个一行。另一斚wQ?/SPAN>read函数d指定的字节数。就像将5.4节中看到的那P标准I/O库提供的函数让我们能够控制缓冲的使用风格?/SPAN>
最常见的标?/SPAN>I/O函数是printf。如果在E序中调?/SPAN>printf函数Q可以通过包含apue.h来包?/SPAN><stdio.h>头文Ӟ<stdio.h>头文件中包含?jin)所有标?/SPAN>I/O函数的原型?/SPAN>
?/SPAN>1.5中的E序?x)?/SPAN>5.8节中详细讨论。该E序像前面的E序那样调用read?/SPAN>write函数。该E序拯标准输入到标准输出,同时也能够拷贝Q何常规文件?/SPAN>
getc函数一ơ读取一个字W,该字W被putc函数写入。在最后一个输入的字节被读取后Q?/SPAN>getcq回帔REOFQ?/SPAN><stdio.h>中定义)(j)。标?/SPAN>I/O帔Rstdin?/SPAN>stdout也在<stdio.h>头文件中定义Q它们分别表C标准输入和标准输出?BR>
?/SPAN> 1.5 使用标准I/O拯标注输入到标注输?/SPAN>
UNIX文gpȝ是按层次安排目录和文件的。v始目录被UCؓ(f)根(rootQ,它的名字是一个字W?/SPAN> / ?/SPAN>
目录是一个包含目录项的文件。在逻辑上,可以认ؓ(f)每个目录w包含一个文件名Q同时还包含说明该文件属性的信息。文件属性是Q文件类型,文g长度Q文件所有者,文g的许可权Q例如,其他用户能否能访问该文gQ?/SPAN>,文g最后的修改旉{?/SPAN>start?/SPAN>fstat函数q回一个包含所有文件属性的信息l构。第4章将详细说明文g的各U属性?/SPAN>
目录事实上存储在磁盘上Q对此区别于目录的逻辑看法。多?/SPAN>UNIX文gpȝ的实现ƈ不在目录中存储该目录项自己的属性,因ؓ(f)当文件有很多连接的时候,很难做到同步保存该文件的属性。当我们在第四章讨论连接后Q对此将有清晰的认识?/SPAN>
目录中的各个名字被称为文件名。唯一两个不能出现在文件名中的字符是反斜杠Q?/SPAN>/Q和I字W(nullQ。反斜杠用来把文件名从\径名中区别出来,I字W用来结束一个\径名。因此,把文件名中可用的字符限制在一般打印字W的子集中是一个好?fn)惯。(我们限制可用字符q因为,如果我们在文件名中用了(jin)shell的特D字W,我们必M?/SPAN>shell的引h制来引用文g名,q将D问题复杂化。)(j)
被反斜杆区分开Qƈ且可选的以反斜杆开头一个或多个文g名的序列Q构成了(jin)路径名。以反斜杆v头的路径名称为绝对\径;否则Q称为相对\径。相对\径涉?qing)到的文件相对于当前目录。文件系l的根(/Q的名字是绝对\径的特例Q它没有文g名?/SPAN>
列出一个目录中所有文件的名字q不困难。图1.3展示?/SPAN>lsQ?/SPAN>1Q命令实现的本质?/SPAN>
lsQ?/SPAN>1Q符hUNIXpȝ手册的一般表C方法,用来引用特定的项。它引用W一节中的lsV小节号通常用数?/SPAN>1?/SPAN>8表示Q同时每一节中的所有项都是按字母顺序排列的。本书中Q我们假定你有一份你?/SPAN>UNIXpȝ手册的拷贝?/SPAN>
历史上,UNIXpȝ把所?/SPAN>8个小节集中在UNIXE序员手?/SPAN>中。随着|的增加,势变ؓ(f)把各节安排在不同的手册中Q例如一份ؓ(f)用户准备的,一份ؓ(f)E序员准备的Q一份ؓ(f)pȝ理员准备的?/SPAN>
一?/SPAN>UNIXpȝ使用大写字母在已有的节中进一步细分手册。D例来_(d)所?/SPAN>AT&T中的标准输出/输入(I/O)函数?/SPAN>3S节中,比如fopenQ?/SPAN>3SQ。其它系l用字母来代替数字表C小节号Q例如用C表示命o(h)?/SPAN>
今天Q许多手册用?sh)子形式q行发行。如果你的手册是联机手册Q查看手册中ls命o(h)的方式或许是q样的:(x)
man 1 ls
或?/SPAN>
man ?/SPAN>s1 ls
?/SPAN>1.3是一个程序,其打印出目录中各个文件的名字。如果源码文件的名字?/SPAN>myls.cQ我们按照以下方式把它编译成默认的可执行文ga.outQ?/SPAN>
cc myls.c
ccQ?/SPAN>1Q是早期?/SPAN>C~译器。在支持GNU C~译器的pȝ上,gccQ?/SPAN>1Q是C~译器。这里,cc通常?/SPAN>gcc相连接(译者注Q原句是HereQ?/SPAN>cc is often linked to gcc。没译好,期待哪位大虾指教Q?/SPAN>
一些输Z子如下:(x)
$ ./a.out /dev
.
..
console
tty
mem
kmem
null
mouse
stdin
stdout
stderr
zero
省略其它未显C的?/SPAN>
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
像上面那P我们?x)以如下的方式展C行的命o(h)和该命o(h)的输出:(x)输入的字W以U色加粗字体昄Q而程序的输出?SPAN style="COLOR: red">U色字体昄。如果对于输出需要加注释Q我们会(x)?I style="mso-bidi-font-style: normal">U色斜体字体昄注释。输入行前面的美元符hshell打印出来的提C符。我们始l用美元符号作?/SPAN>shell提示W?/SPAN>
注意文g名ƈ没有按照字母序输出。?/SPAN>ls命o(h)?x)在打印名字之前现对名字排序?/SPAN>
在这?/SPAN>20行的E序中,许多l节应当被考虑Q?/SPAN>
l 首先Q我们包含了(jin)一个我们自q写的头文Ӟ(x)apue.h。几乎本书中所有的E序都包含这个头文g。该头文件包含了(jin)一些标准系l头文gQƈ且定义了(jin)本书例子中用的数值常量和函数原型。该头文件的清单在附?/SPAN>B中?/SPAN>
l main函数的声明用了(jin)ISO C标准的风根{(下章中我们会(x)谈到更多ISO C标准。)(j)
l 我们从命令行得到参数argv[1]作ؓ(f)对象目录的名字。在W七章,我们看?/SPAN>main函数是怎样被调用的Q同时看到命令行参数和环境变量是如何传递给E序的?/SPAN>
l ׃不同UNIXpȝ的目录项格式不尽相同Q我们?/SPAN>opendir函数Q?/SPAN>readdir函数?/SPAN>closedir函数来操作目录?/SPAN>
l opendir函数q回DIRl构的指针,q传递该指针l?/SPAN>readdir函数。不必关?/SPAN>DIRl构的细节。接着在@环中调用readdir函数Q用来读取每个目录项?/SPAN>readdir函数q回一?/SPAN>direntl构的指针,否则Q在无目录项可读时返?/SPAN>null指针。我们只需要检?/SPAN>direntl构中每个目录项的名字(d_nameQ。用这个名字,我们可以调?/SPAN>stat函数Q?/SPAN>4.2节介l)(j)来确定文件的所有属性?/SPAN>
l 我们调用两个我们自己~写的函数来处理错误Q?/SPAN>err_sys?/SPAN>err_quit。从?/SPAN>1.3的输Z我们可以看到Q?/SPAN>err_sys函数打印Z(jin)丰富的信息来描述遇到的错误(?/SPAN>Permission denied”和?/SPAN>Not a directory”)(j)。附?/SPAN>B中列Z(jin)q两个错误处理函数。在1.7节中我们更详细的谈到错误处理?/SPAN>
l 当程序完成时Q以参数0调用exit函数?/SPAN>exit函数l束一个程序。通常Q参?/SPAN>0意味者正常结束,?/SPAN>1?/SPAN>255之间的参数意味着发生?jin)错误?/SPAN>8.5节中Q我们将?x)学习(fn)一个程序(比如shell或者我们自己写的程序)(j)如何获得另一个正在执行中的程序的exit状态?/SPAN>
?/SPAN> 1.3 列DZ个目录中的所有文?/SPAN>
每个q程都有一个工作目录,有时又称为当前工作目录。所有的相对路径都从该目录开始。一个进E能够用chdir函数改变它的工作目录?/SPAN>
例如Q相对\?/SPAN>doc/memo/joe指出memo目录中的文g或目?/SPAN>joeQ?/SPAN>memo目录又在doc目录中,?/SPAN>doc一定是工作目录下的一个目录。查看这个相对\径,我们知道doc?/SPAN>memo一定是目录Q但是我们不能确?/SPAN>joe是一个文件还是一个目录。\?/SPAN>/usr/lib/lint是一个绝对\径,它指Z(jin)lib目录中的文g或目?/SPAN>lintQ?/SPAN>lib目录?/SPAN>usr目录中,?/SPAN>usr目录又在根(rootQ目录中?/SPAN>
当我们登录时Q工作目录被讑֮为我们的起始目录。我们的起始目录是从密码文gQ?/SPAN>1.3节介l)(j)中我们的d得?/SPAN>的?/SPAN>
当我们登?/SPAN>UNIXpȝӞ我们输入我们的登录名Q然后是口o(h)。系l接着在密码文g中扫描我们的d名,密码文g通常位于/etc/passwd。如果我们在密码文g中查看我们的d,会(x)看到它是?/SPAN>7个冒号分ȝ部分l成的:(x)d名,加密口o(h)Q用L(fng)数字IDQ?/SPAN>205Q,l的数字IDQ?/SPAN>105Q,一个注释段Qv始目录(/home/sarQ,q有shellQ注Q以后直接用shell代表解释器)(j)E序Q?/SPAN>/bin/kshQ?/SPAN>
sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh
所有新的系l已l把加密口o(h)Ud另一个文件中?jin)。在W六章,我们查看这些文件和一些函数来讉K他们?/SPAN>
一旦我们登录后Q一些特定的pȝ信息显C出来,接着我们可以在shell中输入命令。(一些系l在你登录后Q会(x)启动H口理E序Q但是一般来_(d)你会(x)以在其中一个窗口中q行shell来结束。)(j)shell是一个命令行解释器,它读入用L(fng)输入q且执行命o(h)。用户通常从终端(一个交互式?/SPAN>shellQ,或者某些时候从一个文Ӟ被称?/SPAN>shell教本Q向shell输入。通常使用?/SPAN>shellȝ在图1.2中?/SPAN>
名字 |
路径 |
FreeBSD |
Linux |
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 |
* |
* |
* |
* |
?/SPAN> 1.2 UNIXpȝ常用shell
pȝ从密码文件中d的最后一个字D中?jin)解到应该执行哪一?/SPAN>shell?/SPAN>
Bourne shellQ由Steve Bourne在贝?dng)实验室开发,?/SPAN>Version 7以来一直在使用Qƈ且被目前几乎所有的UNIXpȝ所支持?/SPAN>The control-flow constructs of the Bourne shell are reminiscent of Algol 68?/SPAN>
C shell是由Bill Joy在伯克利开发的Q被所有的BSD发行版支持。另外,C shell也被AT&T?/SPAN>System V/386 Release 3.2?/SPAN>System V Release 4Q?/SPAN>SVR4Q所支持。(我们在下一章中更详l介l这两个版本UNIXpȝ的不同。)(j)C shell构徏于第六版?/SPAN>shellQ而不?/SPAN>Bourne shell。它?/SPAN>control flow更像C语言Q它也支持一些其它的Q?/SPAN>Bourne shell所不支持的Ҏ(gu):(x)job control, a history mechanism, and command line editing?/SPAN>
Korn shell被认为是?/SPAN>Bourne shell的成功者,它最先被SVR4所支持?/SPAN>Korn shell是由David Korn在贝?dng)实验室开发的Q运行于大多数的UNIXpȝ上。在SVR4之前Q?/SPAN>Korn shell是一个收费组Ӟ所以它q没有其它两?/SPAN>shell行。它也和Bourne shell兼容Q同时包含了(jin)使得C shell行的那些特性:(x)job control, command line editing, and so on?/SPAN>
Bourne-again shell是由所?/SPAN>Linuxpȝ所支持?/SPAN>GNU shell。它被设计ؓ(f)?/SPAN>POSIX一_(d)同时仍然?/SPAN>Bourne shell兼容。它同时支持C shell?/SPAN>Korn shell的特性?/SPAN>
TENEX C shell?/SPAN>C shell的增强版本。它?/SPAN>TENEX操作pȝQ?/SPAN>1972q开发于Bolt Beranek?/SPAN>NewmanQ借用?jin)许多特性,比如命o(h)补全?/SPAN>TENEX C shell对于C shell增加?jin)许多特性,光常被作?/SPAN>C shell的替代品?/SPAN>
Linux使用Bourne-again shell作ؓ(f)默认shell。事实上Q?/SPAN>/bin/sh是/bin/bash的连接?/SPAN>FreeBSD?/SPAN>Mac OS X的默?/SPAN>shell?/SPAN>TENEN C shellQ由?/SPAN>C shell的程序设计语a被广泛的认ؓ(f)很难使用Q所有他们?/SPAN>Bourne shell作ؓ(f)他们的系l(administrativeQ?/SPAN>shell脚本?/SPAN>Solaris有承于BSD?/SPAN>System V的,在图1.2中所列出的所?/SPAN>shell。大多数q些shell的免贚w分能够在因特|上得到?/SPAN>
通过本书Q我们将展示互动?/SPAN>shell例子来执行我们所开发的E序。这些例子?/SPAN>Bourne shellQ?/SPAN>Korn shell?/SPAN>Bourne-again shell的共同特性?/SPAN>
严格的来_(d)操作pȝ被定义ؓ(f)操控计算机硬件和为程序提供运行环境的软g。一般来_(d)我们U改软g为内核(kernelQ,因ؓ(f)其相对较?yu)ƈ居于q行环境的核?j)。图1.1展示?/SPAN>UNIXpȝ的架构?/SPAN>
?/SPAN> 1.1 UNIX操作pȝ的结?/SPAN>
内核的接口是一个Y件层Q被UCؓ(f)pȝ调用?/SPAN>system calls】(?/SPAN>1.1中阴影部分)(j)。常用的函数库就被构建在pȝ调用接口之上Q但是应用程序可以Q意地调用他们二者。(我们在1.11节更多的介绍pȝ调用和常用函数库。)(j)解释器(shellQ是一个特D的应用E序Q其用来为其它程序的q行提供接口?BR> 更一般来_(d)一个操作系l就是内核和所有其它得计机易用q有个性的软g的组合。这些其它的软g包括pȝ工具Q应用程序,解释器(shellQ,常用函数库,{等?BR> 举例来说Q?/SPAN>Linux是?/SPAN>GNU操作pȝ使用的内核。一些hU此?/SPAN>GNU/Linux操作pȝQ然而其通常的被UCؓ(f)Linux。尽这U称法严格来说ƈ不正,然而这U称法却更容易理解,赋予?jin)词l操作系l双重含义。(另一个优Ҏ(gu)q样更简z。)(j)
所有操作系lؓ(f)E序的运行提供服务。典型的服务包括执行一个新的程序,打开文gQ阅LӞ甌内存Q获得当前时_(d){等。本文集中于描述不同版本UNIX操作pȝ提供的服务?/SPAN>
以严格的步进方式描述UNIXpȝQ不对Q?SPAN class=GramE>未学?/SPAN>q的词汇q行前引用几乎是不可能的(或许也是乏味的)(j)。本章从一个程序员的角度对UNIXpȝq行快速浏览。ƈ对书中引用的一些术语和概念q行要的说明q给出实例。在以后各章中,对q些概念作更详细的说明。对于不熟?zhn)?/SPAN>UNIX的程序设计h员,本书要介l了(jin)UNIX提供的各U服务?/SPAN>