UNIX环境高级编程(第3版)(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-22 17:19:21

点击下载

作者:[美]W. Richard Stevens Stephen A. Rago著

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

UNIX环境高级编程(第3版)

UNIX环境高级编程(第3版)试读:

前言

)。本书精选了常用的400多个系统调用和库函数,这些接口基本是UNIX系统软件的核心功能,涵盖了 UNIX/Linux 系统编程的方方面面。本书通过简明完整的例子来说明其用途,不仅仅说明了其基本用法,还反映了不同平台之间细微差异,有助于读者对整个编程环境有全面深入的了解。在翻译本书的过程中,译者也是收益良多,同时,一些经典的案例已经用于大学课堂教学和编程实践中。

本书的第 2 章至第 12 章由同济大学张亚英翻译和校对,其余由上海交通大学软件学院戚正伟翻译和校对,上海交通大学计算机系尤晋元教授对全书统稿。本书第1版和第2版中译本自出版以来,很多读者对其提出了宝贵意见,在本版本中尽量采纳了这些意见。同时,我们的工作还得到上海交通大学软件学院许多研究生(葛馨霓、王佳骏、李垚、王润泽、朱新宇、孙海洋、张子卓、许欣昊、马军、梁丹)的帮助,在此一并表示感谢。

还要特别感谢人民邮电出版社编辑杨海玲在本书的编辑、出版方面所付出的辛勤劳动。

我们希望本书的出版对相关科技人员和读者所有帮助,同时也期望广大专家和读者提出宝贵意见。第2版序

我差不多每次在接受专访当中,或是做技术讲座后的提问时间里,总会被问及这样一个问题:“你想到过 UNIX 会生存这么长时间吗?”自然,每次的回答都是:“没有,我们没想到会是这样。”从某种角度说,UNIX 系统已经伴随了商用计算行业历史的大半,而这也早就不是什么新闻了。

发展的历程错综复杂,充满变数。自20世纪70年代初以来,计算机技术经历了沧海桑田般的变化,尤其体现在网络技术的普遍应用、图形化的无所不在、个人计算的触手可及,然而UNIX系统却奇迹般地容纳和适应了所有这些变化。虽然商业应用环境在桌面领域目前仍然为微软和英特尔两家公司所统治,但是在某些方面已经从单一供应商向多种来源转变,特别是近年来对公共标准和免费可用来源的信赖与日俱增。

UNIX 作为一种现象而不单是商标品牌,有幸能与时俱进,乃至领导潮流。在 20 世纪 70~80年代,AT&T虽对UNIX的实际源代码进行了版权保护,但却鼓励在系统的接口和语言基础上进行标准化的工作。例如,AT&T发布了SVID(System V Interface Definition,系统V接口定义),这成为POSIX及其后续工作的基础。后来,UNIX可以说相当优雅地适应了网络环境,虽不那么轻巧却也充分地适应了图形环境。再往后,开源运动的技术基础中集成了UNIX的基本内核接口和许多它独特的用户级工具。

即使在UNIX软件系统本身还是专有的时候,鼓励出版UNIX系统方面的论文和书籍也是至关重要的,著名的例子就是Maurice Bach的《UNIX操作系统设计》一书。其实我要说明的是, UNIX长寿的主要原因是,它吸引了极具天分的技术作者,为大众解读它的优美和神秘所在。Brian Kernighan是其中之一,Rich Stevens自然也是。本书第1版连同Stevens所著的系列网络技术书籍,被公认为优秀的、匠心独具的名著,成为极其畅销的作品。

然而,本书第1版毕竟出版时间太早了,那时还没有出现Linux,源自伯克利 CSRG的UNIX接口的开源版本还没有广为流行,很多人的网络还在用串行调制解调器。Steve Rago认真仔细地更新了本书,以反映所有这些技术进展,同时还考虑到各种ISO标准和IEEE标准这些年来的变化。因此,他的例子是最新的,也是最新测试过的。

总之,这是一本弥足珍贵的经典著作的更新版。Dennis Ritchie2005年3月于新泽西州默里山市前言引言

从我第一次修订《UNIX环境高级编程》一书以来已经快有8年了,期间发生了很多的变化。

 在出版第2版之前,Open Group完成了2004版的Single UNIX Specification,它涵盖了两套勘误表的修改。2008年,Open Group完成了新版的Single UNIX Specification,它更新了基本定义,添加了新的接口,并且去除了弃用的接口。这套规范被称为 2008 年版的POSIX.1,其中包含第7版的基本规范,并在2009年发行。2010年,它与更新后的curses接口捆绑,一起作为Single UNIX Specification第4版(SUSv4)进行再版。

 运行在Intel处理器上的Mac OS X操作系统的10.5、10.6和10.8版,被Open Group认证为UNIX系统。

 苹果公司停止了PowerPC平台上Mac OS X的开发。在10.6发行版(Snow Leopard)之后只针对x86平台发布了新的操作系统版本。

 Solaris操作系统以开源的形式发布,试图与FreeBSD、Linux和Mac OS X遵循的开源模式在声望上一争高下。在2010年,Oracle收购了Sun Microsystems之后,OpenSolaris的开发被终止。作为替代,Solaris社区组建了Illumos项目来继续基于OpenSolaris 的开源开发。更多详细的信息可以从http://www.illumos.org获得。

 2011年,C语言标准被更新,但是因为系统并未能跟上其变化,本书中依然参照1999版。

最重要的是,在第2版中使用的平台已经过时了。本书这一版中涉及以下平台。(1)FreeBSD 8.0,前身是加州大学伯克利分校计算机系统研究组发布的4.4BSD系统,运行在32位Intel Pentium处理器上。(2)Linux 3.2.0(Ubuntu 12.04发布版),这是一个免费的类UNIX操作系统,运行在64位的Intel Core i5 处理器上。(3)Apple Mac OS X 10.6.8版(Darwin 10.8.0),运行在64位Intel Core2 Duo处理器上(Darwin基于FreeBSD和Mach)。我选择从PowerPC平台转向Intel平台,是因为最新版的Mac OS X不再支持PowerPC平台。这次选择带来的缺点是涉及的处理器倾斜向了Intel,而当讨论到异构性问题时,涉及的处理器如果能在字节序和整数大小等方面有不同的性质将是很有好处的。(4)Solaris 10,Sun Microsystems(现在的Oracle)的System V Release 4的派生系统,运行在64位UltraSPARC IIi处理器上。与第2版的不同

最大的变化之一是POSIX.1-2008中的Single UNIX Specification弃用了一些STREAMS相关接口。这是准备在该标准的未来版本中去掉全部这些接口过程的第一步。因此,我已经不情愿地在这一版中删除了STREAMS的内容。这是一个不幸的变化,因为STREAMS接口为socket接口提供了一个很好的对照,并且在很多方面更为灵活。不可否认,当谈论到STREAMS时我并非绝对公正,但是毫无疑问的是,在现有系统中它的分量已经减轻。

 Linux基础系统中未包含STREAMS,虽然添加该功能的包(LiS和OpenSS7)是可用的。

 虽然Solaris 10中包含了STREAMS,但是Solaris 11的socket实现并没有构建在STREAMS之上。

 Mac OS X不包含STREAMS支持。

 FreeBSD不包含STREAMS支持(也从未包含过)。

随着STREAMS相关内容的去除,新的主题变得有机会替代它,例如POSIX异步I/O。

在本书第2版中,Linux版本是基于2.4版的。在这次的版本中,我们已经更新到了3.2版。两个版本的最大不同之一是线程系统。在 Linux 2.4 和 Linux 2.6 之间,线程的实现变为 Native POSIX Thread Library(NPTL)。NPTL使得Linux线程的行为与其他系统的线程更加相似。

总的来说,这次的版本涵盖了超过70个新的接口,包括处理异步I/O、自旋锁、屏障和POSIX信号量等接口。除了一些普遍使用的接口被保留,大多数弃用的接口均被删除。致谢

许多读者为第2版寄来了评论和错误报告。我很感谢他们提高了第2版的准确性。下面提及的各位是最早提出建议或者指出错误的:Seth Arnold、Luke Bakken、Rick Ballard、Johannes Bittner、David Bronder、Vlad Buslov、Peter Butler、Yuching Chen、Mike Cheng、Jim Collins、Bob Cousins、Will Dennis、Thomas Dickey、Loïc Domaigné、Igor Fuksman、Alex Gezerlis、M. Scott Gordon、Timothy Goya、Tony Graham、Michael Hobgood、Michael Kerrisk、Youngho Kwon、Richard Li、Xueke Liu、Yun Long、Dan McGregor、Dylan McNamee、Greg Miller、Simon Morgan、Harry Newton、Jim Oldfield、Scott Parish、Zvezdan Petkovic、David Reiss、Konstantinos Sakoutis、David Smoot、David Somers、Andriy Tkachuk、Nathan Weeks、Florian Weimer、Qingyang Xu和 Michael Zalokar。

技术审校者也提高了内容的准确性,感谢Steve Albert、Bogdan Barbu和Robert Day。特别感谢Geoff Clare和Andrew Josey为Single UNIX Specification的升华和第2章的准确性提供了帮助。另外,感谢Ken Thompson对历史问题做出了解答。

我得再一次说,与 Addison-Wesley 的工作人员的合作非常愉快。感谢 Kim Boedigheimer、Romny French、John Fuller、Jessica Goldstein、Julie Nahil和Debra Williams-Cauley,此外,感谢Jill Hobbs在这段时间提供了她的专业审稿能力。

最后,感谢我的家人对我在这次再版上花费了如此多时间给予的理解。

和以前一样,书中实例的源码可以从www.apuebook.com上获得,我非常欢迎读者发来邮件,发表评论,提出建议,订正错误。

Stephen A. Rago sar@apuebook.com

2 013年1月于新泽西州沃伦市

第2版前言引言

我与Rich Stevens最早是通过电子邮件开始交往的,当时我发邮件报告他的第一本书《UNIX网络编程》的一个排版错误。他回信开玩笑说我是第一个给他发这本书勘误的人。到他 1999 年故去之前,我们会时不时地通一些邮件,一般都是在有了问题认为对方能解答的时候。我们在USENIX会议期间多次相见,并共进晚餐,Rich在会议中给大家做技术培训。

Rich Stevens真是个益友,行为举止很有绅士风度。我在1993年写《UNIX系统V网络编程》时,试图把书写成他的《UNIX网络编程》的系统V版。Rich高兴地为我审阅了好几章,并不把我当成竞争对手,而是当作一起写书的同事。我们曾多次谈到要合作给他的《TCP/IP详解》写个STREAMS版。天若有情,我们或许已经完成了这个心愿。然而,Rich已经驾鹤西去,修订《UNIX环境高级编程》就成为我跟他一起写书的最易实现的方式。

当Addison-Wesley公司的编辑找到我说想修订 Rich的这本书时,我第一反应是这本书没有多少要改的。尽管 13 年过去了,Rich 的书还是巍然屹立。但是,与当初本书出版的时候相比,今日的UNIX行业已经有了巨大的变化。

 系统V的各个变种渐渐被Linux所取代。原来生产硬件配以各自的UNIX版本的几个主要厂商,要么提供了Linux的移植版本,要么宣布支持Linux。Solaris可能算是硕果仅存的占有一定市场份额的UNIX系统V版本4的后裔了。

 加州大学伯克利分校的CSRG(计算机科学研究组)在发布了4.4BSD之后,已经决定不再开发UNIX操作系统,只有几个志愿者小组还维护着一些可公开获得的版本。

 Linux得到数以千计的志愿者的支持,它的引入使任何一个拥有计算机的人都能运行类似于 UNIX 系统的操作系统,并且可以免费获得源代码支持哪怕最新的硬件设备。在已经存在几种免费BSD版本的情况下,Linux的成功确实是个奇迹。

 苹果公司作为一个富有创新精神的公司,已经放弃了老的 Mac 操作系统,取而代之的是一个在Mach和FreeBSD基础上开发的新系统。

因此,我已经努力更新本书中的内容,以反映这4种平台。

在Rich 1992年出版了《UNIX环境高级编程》之后,我扔掉了手头几乎所有的UNIX程序员手册。这些年来,我桌上最常摆放的就是两本书,一本是字典,另一本就是《UNIX 环境高级编程》。我希望读者也能认为本修订版一样有用。对第1版的改动

Rich 的书依然屹立,我试图不去改动他这本书原来的风格。但是 13 年间世事兴衰,尤其是影响UNIX编程接口的有关标准变化很大。

我依据标准化组织的标准,更新了全书相关的接口方面的内容。第2章改动较大,因为它主要是讨论标准的。本书第1版是根据POSIX.1标准的1990年版写的,本修订版依据2001年版的新标准,内容要丰富很多。1990年ISO的C标准在1999年也更新了,有些改动影响到POSIX.1标准中的接口。

目前的POSIX.1规范涵盖了更多的接口。Open Group(原称X/Open)发布的“Single UNIX Specification”的基本规范现在已经并入 POSIX.1,后者包含了几个 1003.1 标准和另外几个标准草案,原来这些标准是分开出版的。

我也相应地增加了些章节,讨论新主题。线程和多线程编程是相当重要的概念,因为它们为程序员处理并发和异步提供了更清楚的方式。

套接字接口现在也是 POSIX.1 的一部分了。它为进程间通信(IPC)提供了单一的接口,而不考虑进程的位置。它成为IPC章节的自然扩展。

我省略了POSIX.1中的大部分实时接口。这些内容最好是在一本专门讲述实时编程的书中介绍。参考文献里有一本这方面的书。

我把最后面几章的案例研究也更新了,用了更接近现实的例子。例如,现在很少有系统通过串口或并口连接 PostScript 打印机了,多数 PostScript 打印机是通过网络连接的,所以我对PostScript打印机通信的例子做了修改。

有关调制解调器通信的那一章如今已经不太适用了。原始材料我们保留在本书网站上,有两种格式:PostScript(http://www.apuebook.com/lostchapter/modem.ps)和PDF(http://www. apuebook.com/lostchapter/modem.pdf)。

书中实例的源代码也可以从www.apuebook.com上获得。多数实例已经在下述4种平台上运行过了。(1)FreeBSD 5.2.1,是加州大学伯克利分校CSRG的4.4BSD的一个变种,在英特尔奔腾处理器上运行。(2)Linux 2.4.22(Mandrake 9.2发布),是一个免费的类UNIX操作系统,运行于英特尔奔腾处理器上。(3)Solaris 9,是Sun公司系统V版本4的变种,运行于64位的UltraSPARC IIi处理器上。(4)Darwin 7.4.0,是基于FreeBSD和Mach的操作系统环境,也是Apple Mac OS X 10.3版本的核心,运行于PowerPC处理器上。致谢

首先要感谢Rich Stevens独立创作了本书第1版,它立即成为一本经典著作。

没有家人的支持,我不可能修订此书。他们容忍我满屋子散落稿纸(比平常更甚),霸占了家里的好几台机器,成天埋头于电脑屏幕前。我的妻子Jeanne甚至亲自动手帮我在一台测试的机器上安装了Linux。

多名技术审校者提出了很多改进意见,以确保内容准确。我非常感谢David Bausum、David Boreham、Keith Bostic、Mark Ellis、Phil Howard、Andrew Josey、Mukesh Kacker、Brian Kernighan、Bengt Kleberg、Ben Kuperman、Eric Raymond和Andy Rudoff。

我还要谢谢Andy Rudoff给我解答有关Solaris的问题,谢谢Dennis Ritchie不惜花时间从故纸堆中为我寻找有关历史方面问题的答案。再次谢谢Addison-Wesley公司的员工,与他们合作令人愉快,谢谢Tyrrell Albaugh、Mary Franz、John Fuller、Karen Gettman、Jessica Goldstein、Noreen Regina和John Wait。特别感谢Evelyn Pyle细致地编辑了本书。

就像Rich曾经做到的那样,我非常欢迎读者发来邮件,发表评论,提出建议,订正错误。Stephen A. Ragosar@apuebook.com2005年4月于新泽西州沃伦市

第1版前言引言

本书描述了UNIX系统的程序设计接口——系统调用接口和标准C库提供的很多函数。本书针对的是所有的程序员。

与大多数操作系统一样,UNIX 为程序运行提供了大量的服务——打开文件、读文件、启动一个新程序、分配存储区以及获得当前时间等。这些服务被称为系统调用接口(system call interface)。另外,标准C库提供了大量广泛用于C程序中的函数(格式化输出变量的值、比较两个字符串等)。

系统调用接口和库函数可参见《UNIX程序员手册》第2、3部分。本书不是这些内容的重复。手册中没有给出实例及基本原理,而这些则正是本书所要讲述的内容。UNIX标准

20世纪80年代出现了各种版本的UNIX,20世纪80年代后期,人们在此基础上制定了数个国际标准,包括 C程序设计语言的 ANSI 标准、IEEE POSIX 标准系列(还在制定中)、X/Open可移植性指南。

本书也介绍了这些标准,但是并不只是说明标准本身,而是着重说明它们与应用广泛的一些实现(主要指SVR4以及即将发布的4.4BSD)之间的关系。这是一种贴近现实世界的描述,而这正是标准本身以及仅描述标准的文献所缺少的。本书的组织

本书分为以下6个部分。(1)对UNIX程序设计基本概念和术语的简要描述(第1章),以及对各种UNIX标准化工作和不同UNIX实现的讨论(第2章)。(2)I/O——不带缓存的I/O(第3章)、文件和目录(第4章)、标准I/O库(第5章)和标准系统数据文件(第6章)。(3)进程——UNIX进程的环境(第7章)、进程控制(第8章)、进程之间的关系(第9章)和信号(第10章)。(4)更多的I/O——终端I/O(第11章)、高级I/O(第12章)和守护进程(第13章)。(5)IPC——程间通信(第14章和第15章)。(6)实例—一个数据库的函数库(第16章)、与PostScript 打印机的通信(第17章)、调制解调器拨号程序(第18章)和使用伪终端(第19章)。

如果对C语言较熟悉并具有某些应用UNIX的经验,对学习本书将非常有益,但是并不要求读者必须具有UNIX编程经验。本书面向的读者主要是:熟悉UNIX的程序员,以及熟悉其他某个操作系统且希望了解大多数UNIX系统提供的各种服务细节的程序员。本书中的实例

本书包含了大量实例——大约10 000行源代码。所有实例都用ANSI C语言编写。在阅读本书时,建议准备一本你所使用的UNIX系统的《UNIX程序员手册》,在细节方面有时需要参考该手册。

几乎对于每一个函数和系统调用,本书都用一个小的完整的程序进行了演示。这可以让读者清楚地了解它们的用法,包括参数和返回值等。有些小程序还不足以说明库函数和系统调用的复杂功能和应用技巧,所以书中还包含了一些较大的实例(见第16章至第19章)。

所有实例的源代码文件都可在因特网上用匿名 ftp 从因特网主机 ftp.uu.net 的 published/books/stevens. advprog.tar.Z文件下载。读者可以在自己的机器上修改并运行这些源代码。用于测试实例的系统

遗憾的是,所有的操作系统都在不断变更,UNIX也不例外。下图给出了系统V和4.xBSD最近的进展情况。

4.xBSD是由加州大学伯克利分校CSRG开发的。该小组还发布了BSD Net1和BSD Net2版,其公开的源代码源自4.xBSD系统。SVRx表示AT&T的系统V第x版。XPG3指X/Open可移植性指南的第3个发行版。ANSI C是C语言的ANSI标准。POSIX.1是IEEE和ISO的类UNIX系统接口标准。2.2节和2.3节将对这些标准和不同版本之间的差别做更多的说明。本书中用4.3+BSD表示源自伯克利的介于BSD Net2和4.4BSD之间的UNIX系统。

在本书写作时,4.4BSD 尚未发布,所以还不能称之为 4.4BSD。为了用一个简单的名字来引用该系统,故使用4.3+BSD。

本书中的大多数实例曾在下面4种UNIX系统上运行过。(1)U.H公司(UHC)的UNIX系统V/386 R4.0.2(vanilla SVR4),运行于Intel 80386处理器上。(2)加州大学伯克利分校CSRG的4.3+BSD,运行于惠普工作站上。(3)伯克利软件设计公司的BSD/386(是BSD Net2的变种),运行于Intel 80386处理器上。该系统与4.3+BSD几乎相同。(4)Sun公司的SunOS 4.1.1和4.1.2(该系统与伯克利系统有很深的渊源,但也包含了许多系统V的特性),运行于SPARCstation SLC上。

本书还提供了许多对系统进行的时间测试,并注明了用于测试的实际系统。致谢

在过去的一年半中,家人给予了我大力支持和爱,因为写书我们失去了很多快乐的周末,我深感歉疚。写书从许多方面影响了整个家庭。谢谢Sally、Bill、Ellen和David。

我要特别感谢Brian Kernighan对我写作此书的帮助。他审阅了全部书稿,不但提出了大量深入细致的审稿意见,还对更好的行文风格给出了恰当的建议,但愿我能够在最终成稿中已经加以体现。Steve Rago也成为了我的创作源泉,不但审阅了全部书稿,还为我解答了有关系统V的许多技术细节和历史问题。还要感谢Addison-Wesley公司邀请的其他技术审校者,他们对书稿的各个部分提出了很有价值的意见,他们是Maury Bach、Mark Ellis、Jeff Gitlin、Peter Honeyman、John Linderman、Doug McIlroy、Evi Nemeth、Craig Patridge、Dave Presotto、Gary Wilson、Gary Wright。

感谢加州大学伯克利分校CSRG的Keith Bostic和Kirk McKusick给了我一个账号,可在最新的BSD系统上测试书中实例(还要感谢Peter Salus)。UHC的Sam Nataros和Joachim Sacksen给我提供了一份SVR4,用来测试书中例子。Trent Hein则帮助我获得BSD/386的alpha和beta版。

其他朋友在过去这些年以各种方式提供了帮助,看似不大,却非常重要。他们是Paul Lucchina、Joe Godsil、Jim Hogue、Ed Tankus和Gary Wright。本书的编辑是Addison-Wesley公司的John Wait,他自始至终是我的忠实朋友。我不断地延期交稿,写作篇幅也一再超过计划,他从不抱怨。特别还要感谢美国国家光学天文台(NOAO),尤其是Sidney Wolff、Richard Wolff和Steve Grandi,为我提供准确的计算机时间。

真正的UNIX图书应该用troff写成,本书也遵循了这一优秀传统。最终清样是作者用James Clark写的groff软件包做出来的。非常感谢James Clark提供了这个优异的写作软件,并迅速地修正其中所发现的bug。也许有一天我会最终弄清楚troff软件做脚注的技巧。

我十分欢迎读者发来电子邮件,发表评论,提出建议,订正错误。W. Richard Stevensrstevens@kohala.comhttp://www.kohala.com/~rstevens1992年4月于亚利桑那州塔克森市第1章UIX基础知识1.1 引言

所有操作系统都为它们所运行的程序提供服务。典型的服务包括:执行新程序、打开文件、读文件、分配存储区以及获得当前时间等,本书集中阐述不同版本的UNIX操作系统所提供的服务。

想要按严格的先后顺序介绍UNIX,而不超前引用尚未介绍过的术语,这几乎是不可能的(可能也会令人厌烦)。本章从程序员的角度快速浏览UNIX,对书中引用的一些术语和概念进行简要的说明并给出实例。在以后各章中,将对这些概念做更详细的说明。对于初涉UNIX环境的程序员,本章还简要介绍了UNIX提供的各种服务。1.2 UNIX体系结构

从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kernel),因为它相对较小,而且位于环境的核心。图1-1显示了UNIX系统的体系结构。

内核的接口被称为系统调用(system call,图1-1中的阴影部分)。公用函数库构建在系统调用接口之上,应用程序既可使用公用函数库,也可使用系统调用。(我们将在 1.11节对系统调用和库函数做更多说明。)shell 是一个特殊的应用程序,为运行其他应用程序提供了一个接口。图1-1 UNIX操作系统的体系结构

从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特性。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等。

例如,Linux是GNU操作系统使用的内核。一些人将这种操作系统称为GNU/Linux操作系统,但是,更常见的是简单地称其为 Linux。虽然这种表达方法在严格意义上讲并不正确,但鉴于“操作系统”这个词的双重含义,这种叫法还是可以理解的(这样的叫法更简洁)。1.3 登录1.登录名

用户在登录UNIX系统时,先键入登录名,然后键入口令。系统在其口令文件(通常是/etc/passwd文件)中查看登录名。口令文件中的登录项由7个以冒号分隔的字段组成,依次是:登录名、加密口令、数字用户ID(205)、数字组ID(105)、注释字段、起始目录(/home/sar)以及shell程序(/bin/ksh)。

sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh

目前,所有的系统已将加密口令移到另一个文件中。第6章将说明这种文件以及访问它们的函数。2.shell

用户登录后,系统通常先显示一些系统信息,然后用户就可以向 shell 程序键入命令。(当用户登录时,某些系统启动一个视窗管理程序,但最终总会有一个 shell 程序运行在一个视窗中)。shell 是一个命令行解释器,它读取用户输入,然后执行命令。shell 的用户输入通常来自于终端(交互式shell),有时则来自于文件(称为shell脚本)。图1-2总结了UNIX系统中常见的shell。图1-2 UNIX系统中常见的shell

系统从口令文件中相应用户登录项的最后一个字段中了解到应该为该登录用户执行哪一个shell。

自V7以来,由Steve Bourne在贝尔实验室开发的Bourne shell得到了广泛应用,几乎每一个现有的UNIX系统都提供Bourne shell,其控制流结构类似于Algol 68。

C shell是由Bill Joy在伯克利开发的,所有BSD版本都提供这种shell。另外,AT&T的System V/386 R3.2和System V R4(SVR4)也提供C shell(下一章将对这些不同版本的UNIX系统做更多说明)。C shell是在第6版shell而非Bourne shell的基础上构造的,其控制流类似于C语言,它支持Bourne shell没有的一些特色功能,例如作业控制、历史机制以及命令行编辑等。

Korn shell是Bourne shell的后继者,它首先在SVR4中提供。Korn shell是由贝尔实验室的David Korn开发的,在大多数UNIX系统上运行,但在SVR4之前,通常它需要另行购买,所以没有其他两种shell流行。它与Bourne shell向上兼容,并具有使C shell广泛得到应用的一些特色功能,包括作业控制以及命令行编辑等。

Bourne-again shell是GNU shell,所有Linux系统都提供这种shell。它的设计遵循POSIX标准,同时也保留了与 Bourne shell 的兼容性。它支持 C shell 和 Korn shell 两者的特色功能。

TENEX C shell是C shell的加强版本。它从TENEX操作系统(1972年BBN公司开发)借鉴了很多特色,例如命令完备。TENEX C shell在C shell基础上增加了很多特性,常被用来替换C shell。

POSIX 1003.2标准对shell进行了标准化。这项规范基于Korn shell和Bourne shell的特性。

不同的Linux系统使用不同的默认shell。一些Linux默认使用Bourne-again shell。另外一些使用BSD的对Bourne shell的替代品dash(Debian Almquist shell,最早由Kenneth Almquist开发,并在后来移植入Linux)。FreeBSD的默认用户shell衍生于Almquist shell。Mac OS X的默认shell是Bourne-again shell。

Solaries继承了BSD和System V两者,它提供了图1-2中所示的所有shell。在因特网上可以找到shell的自由移植版软件。

本书将使用这种形式的注释来描述历史注释,并对不同的UNIX系统的实现进行比较。当我们了解到历史缘由后,会更好地理解采用某种特定实现技术的原因。

本书将使用很多交互式shell实例来执行所开发的程序,这些实例使用了Bourne shell、Korn shell和Bourne-again shell通用的功能。1.4 文件和目录1.文件系统

UNIX文件系统是目录和文件的一种层次结构,所有东西的起点是称为根(root)的目录,这个目录的名称是一个字符“/”。

目录(directory)是一个包含目录项的文件。在逻辑上,可以认为每个目录项都包含一个文件名,同时还包含说明该文件属性的信息。文件属性是指文件类型(是普通文件还是目录等)、文件大小、文件所有者、文件权限(其他用户能否访问该文件)以及文件最后的修改时间等。stat和fstat函数返回包含所有文件属性的一个信息结构。第4章将详细说明文件的各种属性。

目录项的逻辑视图与实际存放在磁盘上的方式是不同的。UNIX 文件系统的大多数实现并不在目录项中存放属性,这是因为当一个文件具有多个硬链接时,很难保持多个属性副本之间的同步。这一点将在第4章讨论硬链接时理解得更明晰。2.文件名

目录中的各个名字称为文件名(filename)。只有斜线(/)和空字符这两个字符不能出现在文件名中。斜线用来分隔构成路径名的各文件名,空字符则用来终止一个路径名。尽管如此,好的习惯还是只使用常用印刷字符的一个子集作为文件名字符(如果在文件名中使用了某些 shell的特殊字符,则必须使用shell的引号机制来引用文件名,这会带来很多麻烦)。事实上,为了可移植性,POSIX.1 推荐将文件名限制在以下字符集之内:字母(a~z、A~Z)、数字(0~9)、句点(.)、短横线(-)和下划线(_)。

创建新目录时会自动创建了两个文件名:.(称为点)和..(称为点点)。点指向当前目录,点点指向父目录。在最高层次的根目录中,点点与点相同。

Research UNIX System和某些早期UNIX System V的文件系统限制文件名的最大长度为14个字符,BSD版本则将这种限制扩展为255个字符。现今,几乎所有商业化的UNIX文件系统都支持超过255个字符的文件名。3.路径名

由斜线分隔的一个或多个文件名组成的序列(也可以斜线开头)构成路径名(pathname),以斜线开头的路径名称为绝对路径名(absolute pathname),否则称为相对路径名(relative pathname)。相对路径名指向相对于当前目录的文件。文件系统根的名字(/)是一个特殊的绝对路径名,它不包含文件名。实例

不难列出一个目录中所有文件的名字,图1-3是ls(1)命令的简要实现。图1-3 列出一个目录中的所有文件

ls(1)这种表示方法是 UNIX 系统的惯用方法,用以引用 UNIX 系统手册中的一个特定项。ls(1)引用第一部分中的 ls 项。各部分通常用数字1~8 编号,在每个部分中的各项则按字母顺序排列。在本书中始终假定你有自己所使用的UNIX系统的手册。

早期的 UNIX 系统把 8 个部分都集中在一本《UNIX 程序员手册》(UNIX Programmer’s Manual)中。随着页数的增加,现在的趋势是把这些部分分别安排在不同的手册中,例如用户手册、程序员手册以及系统管理员手册等。

一些UNIX系统用大写字母把某一部分手册进一步分成若干小部分,例如,AT&T[1990e]中的所有标准I/O函数都被指明位于3S部分中,例如fopen(3S)。另一些UNIX系统不用数字而是用字母将手册分成若干部分,如用C表示命令部分等。

现今,大多数手册都以电子文档形式提供。如果用的是联机手册,则可用下面的命令查看ls命令手册页:

man 1 ls

man -s1 ls

图1-3 只打印一个目录中各个文件的名字,不显示其他信息,如果该源文件名为 myls.c,则可以用下面的命令对其进行编译,编译结果是生成默认名为a.out的可执行文件中。

cc myls.c

历史上,cc(1)是C编译器。在配置了GNU C编译系统的系统中,C编译器是gcc(1)。其中, cc通常链接至gcc。

示例输出如下:

$ ./a.out /dev

..

cdrom

stderr

stdout

stdin

fd

sda4

sda3

sda2

sda1

sda

tty2

tty1

console

tty

zero

null很多行未显示

mem

$ ./a.out /etc/ssl/private

can't open /etc/ssl/private: Permission denied

$ ./a.out /dev/tty

can't open /dev/tty: Not a directory

本书将以以下方式表示输入的命令及其输出:输入的字符以等宽粗体表示,程序输出则以上面所示的等宽字体表示。对输出的注释以中文宋体表示。输入之前的美元符号($)是shell的提示符,本书总是将shell提示符表示为$。

注意,myls程序列出的目录中的文件名不是以字母顺序列出的,而ls命令一般是按字母顺序打印目录项。

在这个20行的程序中,有很多细节需要考虑。

•首先,其中包含了一个头文件apue.h。本书中几乎每一个程序都包含此头文件。它包含了某些标准系统头文件,定义了许多常量及函数原型,这些都将用于本书的各个实例中,附录B列出了这一头文件。

•接下来,我们包含了一个系统头文件dirent.h,以便使用opendir和readdir的函数原型,以及 dirent 结构的定义。在其他一些系统里,这些定义被分成多个头文件。

比如,在 Ubuntu 12.04 中,/usr/include/dirent.h 声明了函数原型,并且包含bits/dirent.h,后者定义了 dirent 结构(真正存放在/usr/include/x86_64-linux-gnu/bits下)。

•main函数的声明使用了ISO C标准所使用的风格(下一章将对ISO C标准进行更多说明)。

•程序获取命令行的第1个参数argv[1]作为要列出其各个目录项的目录名。第7章将说明main函数如何被调用,程序如何存取命令行参数和环境变量。

•因为各种不同 UNIX 系统目录项的实际格式是不一样的,所以使用函数 opendir、readdir和closedir对目录进行处理。

•opendir函数返回指向DIR结构的指针,我们将该指针传送给readdir函数。我们并不关心 DIR 结构中包含了什么。然后,在循环中调用 readdir 来读每个目录项。它返回一个指向 dirent 结构的指针,而当目录中已无目录项可读时则返回 null 指针。在dirent 结构中取出的只是每个目录项的名字(d_name)。使用该名字,此后就可调用stat函数(见4.2节)以获得该文件的所有属性。

•程序调用了两个自编的函数对错误进行处理:err_sys和err_quit。从上面的输出中可以看到,err_sys函数打印一条消息(“Permission denied”或“Not a directory”),说明遇到了什么类型的错误。这两个出错处理函数在附录B中说明,1.7节将更多地叙述出错处理。

•当程序将结束时,它以参数0调用函数exit。函数exit终止程序。按惯例,参数0的意思是正常结束,参数值1~255则表示出错。8.5节将说明一个程序(如shell或我们所编写的程序)如何获得它所执行的另一个程序的exit状态。4.工作目录

每个进程都有一个工作目录(working directory),有时称其为当前工作目录(current working directory)。所有相对路径名都从工作目录开始解释。进程可以用chdir函数更改其工作目录。

例如,相对路径名doc/memo/joe指的是当前工作目录中的doc目录中的memo目录中的文件(或目录)joe。从该路径名可以看出,doc和memo都应当是目录,但是却不能分辨joe是文件还是目录。路径名/urs/lib/lint是一个绝对路径名,它指的是根目录中的usr目录中的lib目录中的文件(或目录)lint。5.起始目录

登录时,工作目录设置为起始目录(home directory),该起始目录从口令文件(见1.3节)中相应用户的登录项中取得。1.5 输入和输出1.文件描述符

文件描述符(file descriptor)通常是一个小的非负整数,内核用以标识一个特定进程正在访问的文件。当内核打开一个现有文件或创建一个新文件时,它都返回一个文件描述符。在读、写文件时,可以使用这个文件描述符。2.标准输入、标准输出和标准错误

按惯例,每当运行一个新程序时,所有的 shell 都为其打开 3 个文件描述符,即标准输入(standard input)、标准输出(standard output)以及标准错误(standard error)。如果不做特殊处理,例如就像简单的命令ls,则这3个描述符都链接向终端。大多数shell都提供一种方法,使其中任何一个或所有这3个描述符都能重新定向到某个文件,例如:

ls > file.list

执行ls命令,其标准输出重新定向到名为file.list的文件。3.不带缓冲的I/O

函数open、read、write、lseek以及close提供了不带缓冲的I/O。这些函数都使用文件描述符。实例

如果愿意从标准输入读,并向标准输出写,则图1-4中所示的程序可用于复制任一UNIX普通文件。图1-4 将标准输入复制到标准输出

头文件(apue.h中包含了此头文件)及两个常量STDIN_FILENO和STDOUT_FILENO是POSIX标准的一部分(下一章将对此做更多的说明)。头文件包含了很多UNIX系统服务的函数原型,例如图1-4程序中调用的read和write。

两个常量STDIN_FILENO和STDOUT_FILENO定义在头文件中,它们指定了标准输入和标准输出的文件描述符。在POSIX标准中,它们的值分别是0和1,但是考虑到可读性,我们将使用这些名字来表示这些常量。

3.9节将详细讨论BUFFSIZE常量,说明它的各种不同值将如何影响程序的效率。但是不管该常量的值如何,此程序总能复制任一UNIX普通文件。

read函数返回读取的字节数,此值用作要写的字节数。当到达输入文件的尾端时,read返回0,程序停止执行。如果发生了一个读错误,read返回−1。出错时大多数系统函数返回−1。

如果将该程序编译成标准名称的a.out文件,并以下列方式执行它:

./a.out > data

那么标准输入是终端,标准输出则重新定向至文件 data,标准错误也是终端。如果此输出文件并不存在,则shell会创建它。该程序将用户键入的各行复制到标准输出,键入文件结束符(通常是Ctrl+D)时,将终止本次复制。

若以下列方式执行该程序:

./a.out < infile > outfile

会将名为infile文件的内容复制到名为outfile的文件中。

第3章将更详细地说明不带缓冲的I/O函数。4.标准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程序的功能类似于前一个调用了read和write的程序,5.8节将对此程序进行更详细的说明。它将标准输入复制到标准输出,也就能复制任一UNIX普通文件。图1-5 用标准I/O将标准输入复制到标准输出

函数getc一次读取一个字符,然后函数putc将此字符写到标准输出。读到输入的最后一个字节时,getc返回常量EOF(该常量在中定义)。标准I/O常量stdin和stdout也在头文件中定义,它们分别表示标准输入和标准输出。1.6 程序和进程1.程序

程序(program)是一个存储在磁盘上某个目录中的可执行文件。内核使用exec函数(7个exec函数之一),将程序读入内存,并执行程序。8.10节将说明这些exec函数。2.进程和进程ID

程序的执行实例被称为进程(process)。本书的每一页几乎都会使用这一术语。某些操作系统用任务(task)表示正在被执行的程序。

UNIX系统确保每个进程都有一个唯一的数字标识符,称为进程ID(process ID)。进程 ID总是一个非负整数。实例

图1-6程序用于打印进程ID。图1-6 打印进程ID

如果将该程序编译成a.out文件,然后执行它,则有:

$ ./a.out

hello world from process ID 851

$ ./a.out

hello world from process ID 854

此程序运行时,它调用函数 getpid 得到其进程 ID。我们将会在后面看到,getpid 返回一个pid_t数据类型。我们不知道它的大小,仅知道的是标准会保证它能保存在一个长整型中。因为我们必须在printf函数中指定需要打印的每一个变量的大小,所以我们必须把它的值强制转换为它可能会用到的最大的数据类型(这里是长整型)。虽然大多数进程ID可以用整型表示,但用长整型可以提高可移植性。3.进程控制

有3个用于进程控制的主要函数:fork、exec和waitpid。(exec函数有7种变体,但经常把它们统称为exec函数。)实例

UNIX系统的进程控制功能可以用一个简单的程序说明(见图1-7)。该程序从标准输入读取命令,然后执行这些命令。它类似于shell程序的基本实施部分。图1-7 从标准输入读命令并执行

在这个30行的程序中,有很多功能需要考虑。

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

•因为fgets返回的每一行都以换行符终止,后随一个null字节,因此用标准C函数strlen计算此字符串的长度,然后用一个null字节替换换行符。这样做是因为execlp函数要求的参数是以null结束的而不是以换行符结束的。

•调用fork创建一个新进程。新进程是调用进程的一个副本,我们称调用进程为父进程,新创建的进程为子进程。fork对父进程返回新的子进程的进程ID(一个非负整数),对子进程则返回0。因为fork 创建一个新进程,所以说它被调用一次(由父进程),但返回两次(分别在父进程中和在子进程中)。

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

•子进程调用 execlp 执行新程序文件,而父进程希望等待子进程终止,这是通过调用waitpid实现的,其参数指定要等待的进程(即pid参数是子进程ID)。waitpid函数返回子进程的终止状态(status 变量)。在我们这个简单的程序中,没有使用该值。

如果需要,可以用此值准确地判定子进程是如何终止的。

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

如果运行此程序,将得到下列结果。注意,该程序使用了一个不同的提示符(%),以区别于shell的提示符。

$ ./a.out

% date

Sat Jan 21 19:42:07 EST 2012

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载