• 863.44 KB
  • 2022-04-22 11:52:18 发布

《UNIX系统与软件开发》习题解答.doc

  • 58页
  • 当前文档由用户上传发布,收益归属用户
  1. 1、本文档共5页,可阅读全部内容。
  2. 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,可选择认领,认领后既往收益都归您。
  3. 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细先通过免费阅读内容等途径辨别内容交易风险。如存在严重挂羊头卖狗肉之情形,可联系本站下载客服投诉处理。
  4. 文档侵权举报电话:19940600175。
'目录第1章入门知识-1-第2章shell与shell命令-3-第3章用户、组和密码管理-8-第4章文件系统及管理-11-第5章进程与作业管理-15-第6章系统安装、启动与管理-19-第7章软硬件管理及系统的扩充与升级-22-第8章网络管理与网络应用-24-第9章bshell编程-26-第10章C编程基础和方法-31-第11章文件部分系统调用与标准I/O-33-第12章UNIX系统进程环境-38-第13章文件属性与目录编程-39-第14章进程关系与进程控制-41-第15章进程间通讯-45-第16章线程编程基础-47-第17章终端与curses库编程-48-第18章数据库的使用及编程-50-第19章网络编程基础-53- 第1章入门知识1.UNIX系统的创始人是谁?书中所提到的哪些人为UNIX的发展做出了重大贡献?答:可参见§1.1及网络资源。(1)Unix系统的两大发明人是贝尔实验室的KenThompson和DennisM.Ritchie。(2)为UNIX系统作出贡献的有很多人,书提到的有KenThompson,DennisM.Ritchie,BillJoy,Tanenbaum,RichardMatthewStallman(RMS),WilliamJolitz和LinusBenedictTorvalds等。2.UNIX的大部分代码是用一种流行的程序设计语言编写的,这种语言是什么?答:C。3.UNIX系统的特点有哪些?答:可参见§1.1.2及网络资源。UNIX有以下特点:多任务、多用户、并行处理能力、设备无关性、丰富的工具与Shell编程、安全可靠性与错误处理、强大的网络功能、开放性和可移植性。还有,现代的Unix/Linux系统均有良好的用户界面。4.什么是Linux?其创始人是谁?答:可参见§1.2.1及网络资源。Linux是一个功能强大的操作系统,同时它是一个自由软件,是免费的、源代码开放的,可以自由使用的UNIX兼容产品或称为类UNIX(UNIXLike)系统。Linux内核在8月25日由21岁的芬兰大学生LinusBenedictTorvalds公开发布。5.Linux操作系统的诞生、发展和成长过程始终依赖着的重要支柱有哪些?答:可参见§1.2.1及网络资源。Linux操作系统的诞生、发展和成长过程始终依赖着以下五个支柱:UNIX操作系统、MINIX操作系统、GNU计划、POSIX标准和Internet网络。6.常见的Linux的发行版本有哪些?借助其它资源,简述Linux系统的特点。答:可参见§1.2.1及网络资源。(1)Linux的发行版本很多,常见的有Debian、Ubuntu、linuxmint、RedHat、Fedora、Deepin、ChromeOS和Andriod等。(2)Linux是某种版本的Unix,具有Unix系统的所有特点,见习题3。7.注销与关机有什么不同?用户如何从系统中注销?在字符界面下使用什么命令?答:可参见§1.3.2和§1.3.3。(1)注销是指正在工作的用户从系统中退出,以防止别有用心的人见缝插针地做非授权事情,注销是用户自己的事;关机是指关闭整个系统,系统关闭后所有用户就均无法工作了。(2)不论用户在图形界面下工作或字符界面下工作,均可注销使自己退出系统。在字符界面下,可以使用exit和logout命令或组合键ctrl_D注销自己。8.运行级别的切换是怎么回事?你所用系统有哪些运行级别?如何在不同运行级间切换?答:可参见§1.3.3。Unix/Linux系统可以运行在不同的级别,以完成不同工作处理不同的事务,可用的运行的级别如表1-1所示。表1-1UNIX/Linux系统常用的运行级别或状态级别意义Fedora/LinuxFreeBSD9Solaris110系统关闭系统关闭系统关闭1单用户或系统维护模式同左同Fedoras|S单用户或系统维护模式未使用同Fedora2没有NFS的完整多用户未使用同Fedora 3没有X的完整多用户未使用完整多用户4保留未使用保留5具X的完整多用户未使用系统关闭6重启动重启动重启动q|Q重新检查配置文件无同Fedora由于表中的某些级别并不能真正的运行,比如0和6是用于系统关闭和重启动的,因此运行级别又被称为运行“状态”。运行级别的切换可以使用init命令来实现,具体方法为:#initL#L为运行级别(0~6)或#telinitL比如#init6#立即重启。Fedora、FreeBSD9&Solaris11#init0#立即关闭。Fedora、FreeBSD9&Solaris11#init1#切换到单用户或系统维护模式:Fedora、FreeBSD9&Solaris11#inits#切换到单用户或系统维护模式:Fedora&Solaris119.在线手册是怎么组织的?以read为例,说明如何使用man对其进行帮助?答:可参见§1.4.1。man是传统UNIX系统的在线手册页,通过它用户可以得到某命令、系统调用或配置文件格式等的在线帮助。在UNIX/Linux系统中,手册页被分成不同的部分(章节)。每个系统都自己的手册页,组织方式略有不同,大致情况可参考表1-3。表1-3man手册页的组成章节内容Fedora/LinuxFreeBSD9Solaris11111普通用户指令部分222系统调用部分333库函数447设备及特别文件及接口554配置文件格式666游戏775公约及杂项888、1M系统维护命令(只有超级用户可以使用)999内核及接口以对read帮助为例,使用方法如下:$man-aread#对所有的read进行帮助$man-S2read#对系统调用read进行帮助(FreeBSD)$man-s2read#对系统调用read进行帮助(FedoraLinux&Solaris)$man2read#对系统调用read进行帮助(FedoraLinux&FreeBSD) 第2章shell与shell命令1.shell的基本功能有哪些?解:可参见§2.1.1。shell的基本功能有6,它们分别是:命令解释执行、系统环境设置、I/O重定向、连通管道建立、各种替换与扩展和shell编程。2.Fedora、FreeBSD9和Solaris11系统中的主要的目录有哪些?各有什么作用?解:可参见§2.1.4.1。UNIX系统的典型目录结构如图下所示。/:系统的根目录。/dev:系统的设备目录,其中存放着几乎所有的设备文件。/etc:存放系统和大部分应用软件的配置文件。/home:用户家目录所在目录。默认情况下,每创建一个用户,就会在这里新建一个与用户名同名的目录,为用户分配一个自己的空间。比如用户zh3的家目录默认为/home/zh3。/root:root用户的家目录。在某些早期的Unix系统中,root的家目录为/。/lost+found:被修复文件的存在目录。由于非法关机等原因造成的文件系统损坏,经修复后一些丢失的文件将以其i节点号为名并存放在这里(Linux系统使用)。FreeBSD未使用此目录。/mnt:外来文件的挂接点。传统Unix系统用此目录来安装外来文件系统,比如光盘、U盘等移动设备或磁盘分区。在现代具有图形界面的系统中,外来文件系统或移动存储会被自动安装/media目录下。/boot:引导程序所在目录。引导程序或软件安装在这里。/proc:伪文件系统(也即虚拟文件系统),用于反映系统内进程运行及状态变化情况。通过它的内容可以查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来动态改变内核配置或访问系统内核参数等信息。/usr:用户级目录。/tmp,/usr/tmp:临时目录。FreeBSD不使用/usr/tmp。/sbin,/usr/sbin:系统级的命令与实用程序存放目录。/bin,/usr/bin:用户级的命令与实用程序存放目录。/usr/include:C语言,内核编译所需要的头文件存放目录。/lib,/usr/lib:库文件存放目录,其中有静态库和动态库。/usr/src:系统源程序目录,编译内核时使用。/var:通常用来存放一些经常变化的内容,大多数网络服务使用此目录。/var/log:系统和应用程序日志存放目录。/var/spool:邮件、新闻、打印队列等假脱机系统使用的目录。/usr/share:存放用户共享使用的,比如各种说明文档和在线帮助等的目录,比如man手册就存放在/usr/share/man/目录内。3.工作目录及其父目录可分别用什么表示?在如图2-3所示的目录结构中,若处在usr1目录中的用户要访问include目录中的stdio.h,可以采用什么样的路径,对应的带有路径的文件名是什么?解:可参见§2.1.4。工作目录也叫当前目录,可用“.”表示。父目录也叫上级目录,可用“..”表示。 若在用户自在如图2-3所示的目录内,要访问include目录中的stdio.h,可分别采用相对路径或绝对路径。使用绝对路径时可表示为“/usr/include/stdio.h”;使用相对路径时可表示为“../../usr/include/stdio.h”。4.Linux系统中常用的通配符有哪些?试举例说明它们的作用。解:可参见§2.1.2。通配符是指在模式匹配,如文件名匹配、路经名搜索、字符串查找等中起统配作用的字符。常用的通配符有“*”、“?”和“[]”。(1)*:表示从它所在位置开始的到某个符合条件的结束符之间的任何字符串。例如:“f*.c”匹配以f打头的所有C语言程序。(2)?:表示它所在位置上的任何可能的单个字符。例如:“f?.c”匹配以f打头的主名只有2个字符的C语言程序,比如f1.c、fx.c等。(3)[]:表示[]中所含字符的任何一个。方括号中的字符范围可以由直接给出的字符组成,也可以由表示限定范围的起始字符、终止字符及中间的连字符“-”组成。例如:[abcd]代表或a或b或c或d;[a-d]与[abcd]的作用相同。[]内的第一个字符若“^”或“!”(“!”在有系统中可能有问题),表示非,意为不匹配[]内的任何字符,例如[^a-d]表示不匹配方括号内的a~d的字符集。说明:连字符“-”仅在方括号内且不在最前或最后时才表示字符范围,如在方括号外面或在方括号内最前或最后就成为普通字符;相反地,字符“^”或“!”只有在方括号内且位于首字符位置才起“非”的作用;“*”和“?”只在方括号外才是通配符,若出现在方括号内,就成了普通字符。5.Unix/Linux的文件类型有哪些?试结合命令ls-l/dev的输出,描述文件的类型及用途。解:可参见§2.1.3。Unix/Linux的文件类型有:(1)普通文件;(2)目录文件;(3)设备文件。常见设备文件有:块设备文件(b):字符设备文件(c);命名管道设备文件(p)。除此之外,还有硬链接和符号链接等。在命令ls-l/dev输出中,第一列首个字符为文件的类型,比如,其中的lrwxrwxrwx1rootroot32014-01-2020:31cdrom->sr0#符号链接drwxr-xr-x5rootroot1002014-01-2020:31disk#目录文件brw-r-----1rootfloppy2,02014-01-2020:31fd0#块设备文件(软件)brw-rw----+1rootdisk11,02014-01-2020:31sr0#块设备文件(光驱)crw-rw-rw-1rootroot1,32014-01-2020:31null#字符设备文件(空设备)crw-rw-rw-1roottty5,02014-01-2020:31tty#字符设备文件(终端设备)分别依次描述了符号链接(l)、目录文件(d)、块设备文件(b)和字符设备文件(c)等。6.常用的shell环境变量有哪些?怎么查询和设置环境变量?解:可参见§2.1.7.2、§2.2.7.4和§9.4.3.1。用户在登录成功后,启动shell的过程中,就已经定义了一些和用户的工作环境有关的变量,这些变量被称为环境变量。环境变量可用命令env来查询,用户还可以重新定义这些环境变量或新增环境变量。(1)常用环境变量环境变量有很多,常用的有:HOME:用户家目录的完整路径名。IFS:命令行内部域分割符:白空格。PATH:由冒号分隔的路径名。shell将按PATH变量中给出的顺序搜索这些目录,从中查找要执行的命令。找到的第一个与命令名称一致的可执行文件将被执行。OLDPWD:刚刚离开的目录。TERM:终端的类型。PWD:当前工作目录的绝对路径名。该变量的取值随cd命令的使用而变化。PS1:主提示符。默认情况下,超级用户的主提示符是#,普通用户主提示符是$。PS2:辅提示符,默认为“>”。(2)环境变量的查询可用命令env来查询环境变量。#env#查询所有环境变量#env|grepPATH#查询环境变量PATH(3)环境变量的设置环境变量的设置和定义可参阅后述的§2.2.7.4中的env命令和§9.4.3.1中的export命令。7.系统的标准流有哪些?它们的描述符和所用物理设备分别是什么? 解:参见§2.1.8。标准输入的文件描述符为0,对应的默认物理设备是键盘;标准输出和标准错误对应的文件描述符分别为1和2,对应的默认物理设备是终端屏幕。当执行一个命令时,shell通常会自动为其打开三个标准流:标准输入流、标准输出流和标准错误流。默认情况下,进程将从标准输入读取输入数据,而将正常输出数据写到标准输出,将错误信息送到标准错误中。8.什么是输入/输出重定向?管道的功能是什么?试举例说明如何使用它们。解:参见§2.1.8。1)I/O重定向(也称作I/O改道)是指通过文件的形式实现I/O输入和输出。I/O重定向可以符号<、<<、>、2>、<<、>>、2>>、&>>、&>、>&和|等符号实现的。(1)输入重定向输入重定向是指让命令(或可执行程序)的标准输入从指定的文件中读取数据,也就是说,输入可以不来自键盘,而来自一个事先准备好的文件。例如:#wc/etc/passwd#统计文件/etc/passwd的行、词和字符信息。也可用以下方式:#wc)和追加方式(>>)两种方式。例如:$ls/>dir.out#以覆盖方式,将根目录信息重定向到文件dir.out$ls/usr>>dir.out#以追加方式,将目录/usr信息重定向到文件dir.out输出的重定向是有副作用的,表现在它可能覆盖已经存在的文件或可能会在一个具有完整意义的文件后面追加了一些无意义的信息。(3)标准错误的重定向标准错误的重定向与标准输出的重定向意义相同,但这是针对标准错误的。比如:$ls-l/home/www2>/tmp/err.out#将标准错误重定向到文件/tmp/err.out$ls/www2>>/tmp/err.out#将标准错误追加到文件/tmp/err.out$cat/tmp/err.out#查看文件/tmp/err.out内容(4)标准输出和标准错误同时重定向是指将标准输出和标准错误同时重定向到指定文件。比如:$ls-l/www/usr>/tmp/err.out2>&12)管道管道机制是在两个或多个进程之间建立一种连接,使得前一个命令的输出作为后一个命令的输入。管道机制常用于进程间的通信。用于实现管道机制的符号是“|”,用其将输入程序和输出程序连接起来,使得前一个程序的输出可以作为后一个命令的输出。应用管道的其它示例如下:$ls/dev|wc-l#统计设备目录/dev内有多少文件或子目录$ls/dev/hd*|wc-l#统计设备目录/dev内有多少hd开头文件$cat/etc/passwd|grep"root"|wc-l#统计/etc/passwd内有多少行包含root9.shell中的引号有哪几种?各有什么作用?试举例说明之。解:参见§2.1.10。在shell中引号分为三种:单引号(’),双引号(”)和反单引号(`)。(1)单引号由单引号括起来的字符都作为普通字符。特殊字符用单引号括起来以后,也会失去原特殊意义。例如:$str="$PATH"#定义变量str$y="tn"#定义变量y$echo$str$y#显示变量的值。输出为:$PATHtn(2)反单引号反单引号的作用是命令替换。所谓命令替换是指反单引号内的内容将作为命令首先被执行,然后再将命令执行的结果替换反单引号及其括住位置的内容。例如:$x=`pwd`#通过命令pwd替换定义变量x。pwd的功能是显示当前工作目录$y=`whoami`#通过命令whoami替换定义变量y。whoami的功能是显示用户名$echo$x$y#显示变量x和y的内容在bash中,命令替换的另一种形式是:$(cmd)比如$MyVar=$(whoami)#通过命令whoami替换定义变量MyVar$echo$MyVar$(pwd)#显示变量MyVar和$(pwd)的值命令替换也叫命令扩展。(3)双引号双引号的作用与单引号的功能基本一样,可用来定义变量,所不同的是,在双引号内可进行变量替换和命令替换。双引号中的特殊字符仍具有特殊意义。若在双引号内使用特殊字符本身,则必须使用转义字符,比如双引号中的双引号” 必须表示为”,必须表示为\。变量替换也叫变量扩展。双引号使用示例如下:$myname=`whoami`#通过命令替换定义变量myname##通过变量myname和命令替换定义变量me$me=”Iamastudentmyunameis$mynameandmyworkingdiris`pwd`.”10.什么是变量替换、命令替换和参数替换?试举例说明如何使用它们。解:可参见§2.1.10。变量替换、命令替换和参数替换有更丰富的内容,本章只介绍其中的入门或很少一部分,简单的示例可参考§2.1.10。现在,可以把它们理解为,在任何不在单引号之内的对变量、参数或命令替换的引用,可以替换为被引用的值。更多关于变量替换、命令替换和参数替换的内容可参考后述的第9章或sh或bash的在线文档。引号的使用与变量、命令和参数替换的示例如下str="$PATH"#定义变量stry="tn"#定义变量yecho$str$y#显示变量的值。输出为:$PATHtnx=`pwd`#通过命令pwd替换定义变量x。pwd的功能是显示当前工作目录y=`whoami`#通过命令whoami替换定义变量y。whoami的功能是显示用户名echo$x$y#显示变量x和y的内容myname=`whoami`#通过命令替换定义变量myname##通过变量myname和命令替换定义变量meme="Iamastudentmyunameis$mynameandmyworkingdiris`pwd`."##定义变量me1。注意:使用了转义字符me1="My$myname="$myname"andmyworkingdiris"`pwd`"."echo$me#显示变量me的值echo$me1#显示变量me1的值./DispAllVarIam`whoami`thevalueofxis$x说明:程序DispAllVar显示自己所有命令行参数的脚本程序,可由以下命令生成:#echo"echo$*">DispAllVar#将字符串echo$*写入DispAllVar#chmod+xDispAllVar#为DispAllVar增加执行权11.shell的种类有哪些?如何在当前提示下符启动其它的shell?解:可参见§2.1.11及相关网络资源。UNIX中的shell有多种类型,其中最常用的几种是Bourneshell(sh或bsh)、Bourneagainshell(bash),Cshell(csh)、tcshell(tcsh)、Kornshell(ksh)和Zshell(zsh)等。bsh是UNIX早期shell的一种,且在众多的UNIX系统中bsh基本保持着一致,它是大多UNIX系统默认shell。bash是GNU工程中使用的bshell,即GNU操作系统的默认shell。bash是在bsh基础上发展起来的shell。bash与bsh稍有不同,兼有csh、tcsh和ksh的特色。大多数bsh脚本程序可在其上不加修改就可运行。csh是在bsh之后,ksh之前由vi的设计者BillJoy编写的,它不是bsh的扩展。它采用C语言作为语法模型,是一种比Bourneshell更适于编程的shell。Linux为喜欢使用Cshell的人提供了tcsh。tcsh是Cshell的一个扩展版本,包括命令行编辑、可编程、单词补全、拼写校正、历史命令替换、作业控制和类似C语言的语法。ksh是由Bell实验室的DavidG.Korn而命名的,可以说它是对bsh的发展,在大部分内容上与bsh兼容,几乎所有bsh脚本程序都可以在ksh上运行。Kornshell集合了Cshell和Bourneshell的优点。Linux和Solaris系统下,所有用户的默认shell是bash,在FreeBSD下root使用的默认shell是csh,普通用户默认使用的是sh,但也支持bash。在Fedora和FreeBSD9下,可能命令cat/etc/shells查看已经安装的shell。若要在当前提示下符启动其它的shell,可直接输出shell名称后回车,比如$csh#在当前shell下,启动csh,按^D退回原shell$sh#在当前shell下,启动bsh,按^D退回原shell$bash#在当前shell下,启动bash,按^D退回原shell12.针对所用系统,试观查并说明用户登录时shell的启动过程。在shell启动过程中用到了哪些重要文件?如何影响shell的启动,或在shell启动时加入用户自己的内容?解:可参见§2.3。 UNIX系统启动完毕后,就在活动终端上出现了登录界面并等待用户登录。用户输入正确的用户名和密码,并通过经系统验证之后,系统就会出现提示符,提示用户可以输入相关的命令来使用系统了。当用户完成所需操作后,可以通过注销的办法退出shell,终端重新出现登录界面。UNIX/Linux的用户登录和shell启动过程可参见图2-4。在用户输入和密码后,系统将检查/etc/passwd和/etc/shadow(FreeBSD下为/etc/master.passwd),以验证用户和密码的正确性,若通过了验证,则依次执行/etc/profile、~/.bash_profile、~/.bashrc或/etc/bashrc为用户设置工作环境并出现系统提示符或图形界面。之后,用户就可以工作了。工作完毕之后,在用户签退时,shell退出时,并执行~/.bash_logout做些清理工作。FedoraLinux或Solaris系统的shell启动过程可参阅§2.3.1.1,[FreeBSD]csh的启动过程可参阅§2.3.1.2。Shell的启动过程是人工控制和定制的,主要是修改相关的启动控制文件,具体做法可参阅§2.3.2。13.试述在Unix/Linux系统中如何进行日期和时间管理。解:可参见§2.2.5。在Unix/Linux系统中,传统的日期和时间管理命令是date。其功能是显示或设置系统的日期与时间。date命令的使用方法在不同的系统中有差别,大致情况参见§2.2.5。时间的设置与显示受环境变量TZ的影响,格林威治标准时区的TZ=UTC,中国的标准时区在Fedora和FreeBSD9下为TZ=CST,在Solaris11下为TZ=RPC。若在系统中设置了TZ环境变量,则使用TZ变量显示或设置时间;若没有设置TZ,则使用/etc/localtime文件。在Fedora、FreeBSD9和Solaris11中使用的均是/etc/localtime文件。在Fedora和FreeBSD9下,/etc/localtime是/usr/share/zoneinfo/Asia/Shanghai的一个拷贝;在Solaris11中/etc/localtime是/usr/share/lib/zoneinfo/Asia/Shanghai的一个链接。时区,在与时间相关的具体业务系统是非常重要的。若设置不正确,可能会出现人还没有到下班时间可系统已经下班的情况,或者相反;上班时间也可能出错;结帐、汇总、产生报表等时间也可能会出错;如果设置成了美国某个州的时区那就更麻烦了。因此,时区必须设置正确。若要修改时区,可以参照此通过拷贝或链接方式实现。14.在UNIX/Linux系统中有些操作是有副作用的,比如cp,mv、rm和输出重定向等,应该如何避免?解:可参见§2.2.2。有副作用的操作可以通过交互提醒确认或提前备份来实现,也可通过技术手段来实现。比如,在Linux、FreeBSD和Solaris系统中cp,mv和rm命令都提供有-i参数,每当遇到冲突或覆盖危险时要求用户确认。为了保证自动提醒,可以使用别名机制来实现自动提醒。比如aliascp="cp-i"aliasmv="mv-i"aliasrm="rm-i"可实现自动实现交互提示。原因是在命令搜索时,别名等于外部命令。在FedroaLinux下,还可通过-b选项实现覆盖时自动备份。15.UNIX始终坚持命令要能够处理文本文件,试说明如何判断一个文本文件中是否包含某个字符串,它出现了多少次?解:可参见§2.2.3、§2.2.6及后述的§9.1、§9.2、§9.3等。UNIX始终坚持命令要能够处理文本文件,原因是因为它有一套能够处理文本文件的有力工具。本章所介绍的grep、sort、uniq、wc及vi等就是其中的一部分,还包括第9章的正规表达式和sed和awk等,还有很多本书没有提及的部分。grep命令可以从文本文件中过滤(可由正则表达式或固定的)字符串,-w用于整字匹配、-x用于整行匹配、-o用于显示所有匹配(含同行中的多次匹配,若不使用-o则同行的多次匹配只显示一次)。用户可以使用这些选项完成特定的工作,也可将这些选项与其它选项配合可能实现更复杂的匹配处理。若要实现显示匹配的内容使用以上选项就行了。若要统计匹配出现的次数,可与wc配合使用。比如$grepstringfiles#显示文件files中所有与string匹配的行$grepstringfiles|wc#统计文件files中所有与string匹配的行数$grep-ostringfiles#显示文件files中所有与string匹配的词$grep-ostringfiles|wc#统计文件files中所有与string匹配的词数$grep-w-ostringfiles#显示文件files中所有与string完全匹配的词$grep-ostringfiles|wc#统计文件files中所有与string完全匹配的词数 第3章用户、组和密码管理1.什么是用户名和uid,组名和gid?如何查询它们?解:可参见§3.1。用户(user)是UNIX系统的管理与使用的主体。自然人要想使用系统,必须以某个已存在于系统中的用户身份登录,且通过密码验证后,才能进入系统,并按权限使用。在UNIX系统中,每个用户都有一个用户名(username),系统还给每个用户分配了一个用户标识uid(useridentification)。用户uid是系统识别用户的唯一标识,而用户名则是用户的外部表示。uid为一个大于或等于0的整数,在不同的系统中,uid的取值范围不同。在创建用户时,系统为每个用户安排了一个归属组(group)。系统中的每个组都有一个组名(groupname)和一个组标识gid(groupidentification)。组是一个具有某种联系或关系的用户集合。同组中的用户,可以享有某些共同的权力。用户uid和gid均通过命令id来查询。2.针对你所用的系统,试述与用户、组和密码管理相关的文件及用途,它们的结构是什么,各字段的意义和用途是什么?解:可参见§3.2和§2.3。与用户、组和密码管理相关的文件有/etc/passwd、/etc/group、/etc/shadow(/etc/master.passwd)和/etc/skel等。/etc/passwd是系统用户数据库文件,它包括系统内所有已经注册用户的信息。该文件是一个文本文件,它的每一行描述一个用户的信息,是由“:”分隔的七个字段。结构请参见§3.2.1或在线手册。/etc/shadow是影子密码文件。当系统启用影子密码时,用于存放系统内用户的加密后的密码和用户登录控制信息。在FreeBSD下此文件为/etc/master.passwd且结构不同。文件结构参见§3.2.2、§3.2.6或在线手册。/etc/group是组定义文件。它是一个文本文件,每行描述一个组,结构为参见§3.2.3。skel文件为新建用户家目录的柜架内容。每当创建一个新用户时,就从skel目录复制文件用户家目录,并重新设置用户主组、用户组和权限。/etc/passwd、/etc/group、/etc/shadow(/etc/master.passwd)用于控制用户登录。用户的登录过程及对这些文件的使用请参考§2.3和图2-4。3.试述如何创建和修改一个用户。解:可参见§3.3.1.2、§3.3.2.2和§3.3.3。FedoraLinux和Solaris系统中用户属性的修改请参见§3.3.1.2;FreeBSD下用户属性的修改请参见§3.3.2.2和pw命令的在线手册;修改用户的登录shell可参考§3.3.3。用户创建示例如下#缺省方式#useraddtest1#以缺省方式创建用户test1(Linux&Solaris)#pwuseradd-nmytest#以缺省方式创建用户test1(FreeBSD)#非缺省方式#useradd-d/usr/mytest-mmytest#Fedora/Linux#useradd-b/usr/mytest-mmytest#Solaris11#addusermytest#FreeBSD:进入交互界面后再输入相关信息用户修改示例如下#usermod–gngrouptest#将test用户的归属组修改为ngroup#usermod–s/bin/shtest#将test用户的登录shell修改为/bin/sh#usermod–lmytesttest#将test用户名更改为mytest#以下为FreeBSD#chpasstest#修改test属性。进入交互界面后再输入相关信息#pwusernod-ntest-lmytest-s/bin/sh#将test名修改为mytest且使用登录shell/bin/shpw为FreeBSD用于用户和组管理的综合命令,用于用户修改的用法为pwusermod[name|uid][-q][-nname][-uuid][-ccomment][-ddir][-ggroup][-Ggrouplist][-lname][-m][-Mmode][-sshell]关于pw的用法请参见FreeBSD中pw的在线手册。4.为何要上锁一个用户?如何锁定一个用户?当需要时如何解锁? 解:可参见§3.3.1.2、§3.3.2.2和§3.5.3。系统中的用户,若因出差、学习等原因暂时不使用系统了的话,可通过上锁的办法让其暂时停用,待用户回来后,可再为其解锁重新启用他。关于用户的上锁与解锁的示例请参考本章习题6的答案。5.为何要删除一个用户?在删除一个用户时对用户数据应该如何处理?若确认在删除一个用户时也同时删除它的家目录,在做操作时应注意些什么问题?解:可参见§3.3。删除一个用户可能有多种原因,比如多余或无人使用用户。总之,对于系统中不再使用的用户帐号,应尽早的删除它,因为多余用户的存在可能是安全的隐患。删除用户的命令是userdel(Fedora/Solaris)或rmuser(FreeBSD)。另外,在FreeBSD下还有一个用于用户、组管理的综合命令pw,用于删除用户时用法为pwuserdel[-name/uid][-nname][-uuid][-r]-r用于删除用户家目录。由于篇幅的原因,本书定稿删除了此部分。删除用户时,一般不直接删除用户的家目录,因为用户家目录中可能存放有重要数据。对用户家目录的删除,应在事后经确认后由管理员来进行。不过,若要在删除用户时也同时删除用户家目录可以使用-r选项。6.为了提高系统的安全性,对用户密码应如何管理?如何为自己或其它用户修改密码?解:可参见§3.5。密码安全问题是一个非常复杂的问题,牵涉到技术、制度和应用多方面的问题。就一般应用来讲,关于密码的管理与使用应该注意的问题可参考§3.5.1。在UNIX/Linux系统中,用于密码管理的命令是passwd,可用于密码的修改和用户的上锁与解锁。一般用户可以为自己设置或修改密码,超级用户还可以为其他用户设置密码。关于passwd命令的使用可参考§3.5.2或passwd的在线手册。使用passwd进行密码管理和用户上锁与解锁的示例如下。(1)修改用户密码#passwdmytest#为用户mytest修改密码#passwd#为用户自己修改密码(2)删除用户密码#passwd-dmytest#为用户mytest删除密码(3)用户上锁#passwd-luser#对用户user上锁(Fedora&Solaris)#usermod-Luser#对用户user上锁(Fedora)#pwlockuser#对用户user上锁(FreeBSD)(4)已上锁用户的解锁#passwd-uuser#对用户user解锁(Fedora&Solaris)#usermod-Uuser#对用户user解锁(Fedora)#pwunlockuser#对用户user解锁(FreeBSD)7.如何确定用户所使用的终端?解:可参见§3.6.5。tty命令用于确定所使用的终端设备。tty非常简单但也非常有用。当它在一个控制终端上执行时,无-s选项时输出所使用的终端名,并返回0,此终端名可用于说明执行者的工作位置0。当它不是通过控制终端执行时,将没有输出,但返回值为非0值(通常为1)。因此,据tty的返回值可以判断命令是在前后或后台工作的。这一点在shell脚本程序设计时非常有用,但一般用户或功能可能不一定能用到这一点。8.su命令的功能是什么?如何使用su命令以其他人的身份执行命令?请结合你的系统说明有那些注意事项。解:可参见§3.6.6。命令su的功能是在用户不退出系统的情况下而将自己变为其他用户,或以其它用户的身份工作。在以他人身份工作过程中,自己的有效uid(euid)和有效gid(egid)变为了新用户的uid和gid。在以新用户工作结束时,自动回到原来的自己,或在交互shell下按^D回到自己。su命令的用法为:su[options][newuser[args]]newuser为欲切换的新用户名,若不指定,则默认root;args为要以新用户身份执行的命令,若不指定,则默认为新用户的登录shell;选项-或-l(Solaris11不支持-l)用于以新用户身份和环境进行工作,若不使用-或-l,则不真正切换用户环境。 用户可在执行su前后使用id、env或set命令来观查身份或环境的变化。关于su在不同系统中的安全控制与注意事项,请参考教材可参见§3.6.6.4“安全控制及说明”或各自系统的在线手册。9.sudo的配置文件是什么?如何修改该配置文件?怎样使用sudo命令以root身份执行命令?解:可参见§3.6.7。sudo的配置文件为soders,在不同的系统下位置可能不同,通常位于/etc/或/usr/local/etc/目录下,但在不同的系统中内容、意义和配置方法是相同的。关于soders配置文件的修改与使用示例可参见§3.6.7。关于配置文件较详细的说明可参考参考文献[12]或系统的在线文件。10.何为chroot?试以chroot/var/newroot为例说明,如何为一个$SHELL命令的执行进行准备,并使其执行成功?解:请参见§3.6.8。(1)一般准备过程下面以chroot/var/myrootcmd为例说明chroot命令的准备和使用过程如下:①查询/var/myroot目录是否存在,若无则先创建之。②查询cmd的位置,并记住该位置。③查询cmd所使用共享库,并记住库名和位置。④在/var/myroot下创建cmd命令和其所需共享库所用的对应目录。⑤将cmd命令及所需的共享库和其它必须的文件复制或(硬)链接到/var/myroot/下相应位置。⑥执行命令chroot/var/myrootcmd。⑦若成功,则以/var/myroot/为根目录执行cmd,程序执行完毕后自动退出chroot。当cmd不能自动退出者,比如cmd为$SHELL时,可按Ctrl_D或exit退出。从①~⑤是真正的准备过程,而⑥、⑦是对准备工作检验。根据系统的不同,这个准备过程可能会经过多次反复,最后才会真正完成。在不同的系统间,也可能存在着差异。(2)具体示例以chroot/var/newrootbash说明以上准备过程如下。要求:若系统没有bash请先安装之。①创建/var/chroot#mkdir/var/chroot②检查bash的位置#whichbash③查看bash所依赖的文件(并记下所有的输出)#ldd`whichbash`linux-gate.so.1=>(0xb7741000)libtinfo.so.5=>/lib/libtinfo.so.5(0x42a54000)libdl.so.2=>/lib/libdl.so.2(0x4160b000)libgcc_s.so.1=>/lib/libgcc_s.so.1(0x4165c000)libc.so.6=>/lib/libc.so.6(0x41458000)/lib/ld-linux.so.2(0x41433000)④参照③的输出创建对应目录mkdir/var/newroot/bin/var/newroot/lib⑤参照③的输出,将所应文件复制到/var/newroot/下对应目录#cp`whichbash`/var/newroot/bin#cp/lib/ld-linux.so.2/lib/libtinfo.so.5/lib/libdl.so.2/lib/libgcc_s.so.1/lib/libc.so.6/var/newroot/lib6)执行chroot/var/newrootbash#chroot/var/newrootbashbash-4.2##执行成功,这是新的提示符bash-4.2#pwd#执行内部命令pwd成功/#此为pwd内部命令的输出bash-4.2#ls#执行外部命令lsbash:ls:commandnotfound#执行外部命令ls的错误提示(因为ls不是内部命令)用户可按^D退出chroot。以上过程是基于Fedora16的,能在Fedora16、FreeBSD9下使用的通用bshell脚本程序,请参见第14章习题9的答案。 第4章文件系统及管理1.何为磁盘分区,如何查看一个硬盘的分区及类型?解:可参见§4.1和§4.5.3。磁盘分区是磁盘上用于存放数据的连续区域,磁盘的分区一般会占有多个临近的完整磁道。在传统的DOS或称为fdisk格式分区的磁盘中,分区分为基本分区和逻辑分区。在基本分区有一种被称为扩展分区的分区,在其中可以再划分逻辑分区。硬盘中的主引导扇区结构如图4-1所示,DOS/fdisk格式磁盘中分区结构如图4-3所示。用于磁盘分区管理的通用命令是fdisk。它可用于创建、删除和显示硬盘分区及类型等,但在不同系统中用法有所不同(请参阅§4.5.3或各自系统的fdisk在线手册页)。2.试述Unix/Linux的权限管理机制,与权限管理相关的命令有哪些?试举例说明如何使用它们进行权限管理?解:可参见§4.3和§4.4。在Unix/Linux系统中,一个文件的访问权限是针对某类人来分配的。系统根据工作关系,或人为指定,将用户分为三类:用户主(user)、同组人(group)和其他人(other),然后就只对这三类人分配权限。每类人对文件操作的权限分别依次为读(r:read)、写(w:write)和执行(x:execute),即rwx。为了说明是否拥有某种权限,可在无权限时标为-,有权限的时标为相应权限。比如有读权限,而没有写和执行权,可表示为r--;有读和执行权、而没有写权限可表示为r-x。一个文件对三类人的所分配的权限按用户主、同组人和其他人的顺序依次排列就得到了文件的权限。比如,用户的权限为rwx、同组人的权限为r-x、其他人没有权限,则整个权限表示为rwxr-x---。若将有某权限表示为1,没有权限表示为0,则文件权限可表示为一个9位的二进制或三位的八进制数,比如rwxr-x---可以为111101000或750。与权限管理相关的命令有umask、chmod、chown等。umask用于设置文件创建掩码、chmod用于直接文件修改权限、chown用于修改文件主和组。具体用法请参见§4.4。3.umask的作用是什么?默认情况下新建文件或目录的权限是什么?若一个用户要对新建新建文件或目录的其他人不分配任何权限,设置umask的完整命令是什么?解:参见§4.3.4。在UNIX/Linux系统中,当创建文件或目录时,系统将为它们设置默认权限。文件或目录的默认权限由文件权限掩码(umask)来控制,可用命令umask来设置或显示当前的文件或目录创建掩码umask的值。默认情况下,umask的值为0022,在此umask值控制下,新创建文件和目录的权限分别为:文件:rw-r--r--或644目录:rwxr-xr-x或755若一个用户要对新建文件或目录的其他人不分配任何权限,而对用户和组使用默认值,此时的umask值应为027。设置新的umask值的完整命令为:#umask0027之后,再创建的文件和目录的权限分别为文件:rw-r-----或640目录:rwxr-x---或7504.Unix/Linux系统有几种文件类型?它们分别是什么?如何确定一个文件的类型?解:可参见§2.1.3和§2.2.2.9。Unix/Linux的文件类型可参见§2.1.3,可具体为:(1)普通文件;(2)目录文件;(3)设备文件。常见设备文件有:块设备文件(b):字符设备文件(c);命名管道设备文件(p)。除此之外,还有硬链接和符号链接等。确定文件的类型可以使用file命令。比如#file/dev/null#/dev/null为字符设备(characterspecial)#file/tmp#/tmp为具有sticky改性的目录文件(stickydirectory)#file/etc/passwd#/etc/passwd为普通ASCII文本文件(ASCIItext)#file`whicipasswd`#passwd为具有suid的ELF格式可执行文件5.Unix/Linux 所支持的文件系统类型有那些?在你所使用的系统中它们分别是如何描述的?在所用系统下如何创建或格式化一个FAT格式的分区?解:可参见§4.1(表4-1)、§4.5.2和§4.5.3.2(表4-12)。不同的系统对文件系统支持的类型和数量有差别,常见情况可参见表4-12。若将一个分区(设设备文件名为/dev/sdx,视具体系统而定)格式化为FAT格式的文件系统可使用:#mkfs-tvfat/dev/sdx#Linux#mkfs-Fpcfs/dev/sdx#Solaris#newfs_msdos/dev/sdx#FreeBSD6.试述外来文件系统或移动存储的使用方法。假设有一个光盘,试说明如何将其中的内容复制到系统临时目录下的disk目录。解:可参见§4.5.5。外来文件系统或移动存储必须首先挂载到系统的目录树上,然后才能使用。传统UNIX系统为外来文件系统保留有/mnt用于挂载外来文件系统。在现代的桌面系统上,外来的文件系统一般是会被挂载到/media目录上。整体上说,外来文件系统的使用大致过程是“挂载”->“使用”->“卸载”。设备光驱设备名为/dev/cdrom(随所用系统而定)。若要让一张光盘的内复制到系统临时目录下的disk目录内,可以依次使用以下的命令:(1)将光盘放入光驱,并关上仓门(2)安装光盘#mount/dev/cdrom/mnt#/mnt为安装点,也可以是其它暂不使用的目录(3)复制数据#cp-r/mnt/tmp/disk#带子目录复制(4)卸载光盘#umount/dev/cdrom#拆卸安装的设备或#umount/mnt#拆卸安装点(5)必要时打开光驱,取出光盘7.何为裸设备,如何使用裸设备?以裸方式使用设备时应注意些什么?如何为一个光盘构造映像文件?解:可参见§4.5.4.5和§4.6.2。裸设备(RawDevice)是指其上没有文件系统的设备。Unix/Linux允许以裸方式使用设备,因为Unix/Linux将所有的设备都视为文件,从而通过设备文件名可以直接访问或使用裸设备。裸设备也叫原始设备。当以原始方式使用设备时,是直接将设备作为文件来使用的,而不考虑其上的文件系统。已经创建有文件系统的设备,仍然可作为裸设备或原始设备来使用。通过裸方式使用具有文件系统的设备时,可以绕过的文件控制而直接访问其上数据,但这需要更多的知识和经验。但使用裸方式写设备时,是会覆盖或损坏其上的原有文件系统的。若要为一个光盘构造映像则需要按裸方式使用光盘设备。设系统中所用的光盘设备为/dev/cdrom(需要根据不同系统而定设备名),光盘映像文件名为disk.img,构造光盘映像的方法是:1)放入光盘2)执行命令#ddif=/dev/cdromof=disk.img[bs=2K]说明:/dev/cdrom为光驱设备名,不同系统内光驱设备名可能不同,需要据实指定;disk.img为光盘映像文件名,由用户自行指定;bs=2K为读写块的大小,可以不指定而由系统默认。因为光盘容量一般较大,制作光盘映像时要确保所有文件系统内有足够多的空间。若要复制光盘,尤其是将1张光盘复制多张,可以在制作一个映像之后,然后多次地使用cdrecord命令复制,方法是#cdrecorddev=/dev/cdwriterdisk.img说明:/dev/cdwriter为刻录设备名,在不同系统内名字可能不同,也需要据实指定。cdrecord命令的用法可参见§4.5.4.5。8.如何构造一个Unix/Linux(RockRidge)格式的光盘映像?如何刻录一个可启动光盘?若将当前目录下的所有文件或子目录刻录成RockRidge格式的光盘映像/tmp/mydisk.iso,请写命令或命令序列。解:可参见§4.5.3.3。创建一个具有RockRidge格式的光盘映像,只需要在使用mkisofs创建光盘映像时使用-R或-r选项就可以了。若要创建一个可启动光盘映像,需要事先准备好一个启动盘映像(记为boot.img),并将其以相对路径方式存放在所有刻录内容的当前目录内,并在制作光盘映像时使用-bboot.img指定启动盘映像文件名。 若将当前目录下的所有文件或子目录刻录成RockRidge格式的光盘映像/tmp/mydisk.iso,可使用:$mkisofs-R-o/tmp/mydisk.iso•若要创建一个可启动光盘映像,可使用命令$mkisofs-R-bboot.img-o/tmp/mydisk.iso•9.何为硬链接,何为符号链接,如何创建硬链接和符号链接?能为一个目录创建硬链接吗?解:可参见§2.1.3和§4.6.3。在UNIX/Linux系统中,硬链接是指两个或多个文件名共用一个文件体,或者说一个文件可以具有多个不同的名字,但这些文件名具有相同的i节点号,拥有相同的文件内容。硬链接来自UNIX系统本身,不能跨越文件系统。符号链接也叫软链接,在Windows系统中被称为快捷方式,在MACOSX中称为替身。它是在现代计算机应用基础上发展起来的,具有广泛的灵活性,可以跨文件系统链接,可以指向网络资源,甚至可以指向一个不存在的资源或位置。与硬链接不同的,链接和被链接者不是同一个文件,各有自己的i节点,但可通过链接文件的内容指向被链接文件。不能为目录直接创建硬链接,但可为其创建称号链接。10.如果某个设备文件被误删除了,应该如何恢复或重建它?解:可参见§4.6.4。如果设备文件被误码删除了,可以通过mknod命令重建它。方法如下mknodnodenametype[majorminor]#Linux&Solarismknodnodenametypemajorminor[owner:group]#FreeBSD前提是必须记得设备原来的名字、类型、主、次设备等信息。必要时,可查询其它相同的系统得到这些信息,然后再使用此命令。若已知被删除的块设备文件名为/dev/fd0、主次设备号分别为2和0,则可用以下方法重新重建它mknod/dev/fd0b20#Linux,FreeBSD&Solaris如果需要的话,可再根据实际设置它的主、组信息。11.请结合Unix/Linux文件系统的结构和命令df,说明如何管理磁盘空间,若所管理的文件系统空间将满,应该如何处理?请给出你的解决方案。解:参见§4.6.5。UNIX系统的文件系统结构如图e4-1所示;Linux系统的ext2文件系统结构如图e4-2所示。0#1#3#…s_isizes_isize+1…n引导块超级块i节点区数据区图e4-1Unix文件系统的结构引导块块组0块组1块组2…块组n…超级块组描述块数据块位图i节点位图i节点区数据区图e4-2Linuxext2文件系统的结构在图e4-1中,第0块为引导块(只有一块),用于系统的引导;第1块为超级块(也只有1块)用于对本文件系统的描述,或者说是本文件系统的属性块;i节点区有若干块组成,用于存放系统的i节点(文件属性),所有文件的i节点信息按编号在这里依次存放;数据区也由若干块组成,用于存放数据或文件目录。一个文件系统一经划定(格式化),大小是不变的,因此可以存放文件数量和整个大小是固定。这种划分对于一般情况来说是比例是合理的,但对于特殊或极端情况,比如系统中存放的全是大文件或全是小文件,这种比例就可能失调。若全为小文件,则会再现i节点先满的问题;而对全是大文件的情况则会出现数据区先满的问题。因此,作为管理人员,应该清楚地知道文件系统中各区域的使用情况。df命令可以帮助管理人员做文件系统的使用情况的监督与查询工作。带有选项-i的df命令可以查询i节点区的使用情况,不带-i选项的df命令可以查询数据区的使用情况。通过查询,若发现任何一部分的使用情况超过了限定范围(比如90~95%等),就要拿出具体的应对措施,比向管理部门提出扩充存储建议。并且,在建议没有得到批准或实施之前还要拿出应急措施,比如删除一些不常用的文件等。实事上,通过对df结果的观察,在还没有出现以上情况下,也应该根据系统的数据日均增长情况,估算剩余的存储空间还能坚持多久,以提出合理的磁盘存储管理建议,以确保不会因失查而导致磁盘存储空间的不够用。对图e4-2所示的Linuxext2文件系统,与图e4-1所示的结构有类似之处,可以采用相同办法进行监督和管理。 12.请结合日本的海啸和我国的汶川、玉树等自然灾害,说明数据备份的重要性。数据备份有哪些形式,各用于什么场合?tar和cpio命令常用于数据备份,试分别以实例说明如何使用它们进行数据备份,以及如何管理备份数据?解:可参见§4.7。数据备份及重要性可参考§4.7.1或相关网络资源。至少有一点可以肯定,若没有数据备份,若遇海啸、地震、泥石流等自然灾害,火灾、水灾、盗窃、破坏等人为因素将可能会造成数据的丢失。如果有充分、有效的备份数据,则可以恢复使用。数据备份有哪些形式和适用场合也有不同的说法,要根据实际情况而定。数据备份可根据单位或个人的需要按数据备份制度进行操作。数据备份可分为系统数据备份、用户数据备份和业务数据备份等。数据备份有多种方式,比方说完整备份、增量备份和日志备份等。至于采取何种备份方式,需要根据实际需要或规章制度等来决定。但是为了保证数据的完整性和正确性,必须制订切实可行的数据备份制度,并严格执行之。数据备份工作,必须坚持的原则是要保证备份数据的足够性和可用性,或叫做有效性。因此,有效的数据备份可以是几种备份办法的结合。为了保证备份数据的足够性,要按制度和要求定期、定时、定点进行备份,尤其是在关键的时间点上一定要做数据备份。为了保证数据备份的安全性,备份数据要异地存放。为了保证备份数据的有效性,可将备份数据在备用系统恢复、验证和使用。在UNIX/Linux系统中常用的数据备份工具是tar和cpio,就现在来讲,tar更具有实际意义,因为tar备份的数据包可以在Windows、UNIX/Linux等系统或具有图形界面的系统下得到支持。在某些系统中,还有rsync系统用于远程/异地数据备份。关于tar命令用于数据备份的示例可参考§4.7.3;cpio用于数据备份的示例可参考§4.7.4;其它的可参考在线文档。13.硬盘的主引导扇区(MBR)是非常重要的,也是引导型病毒经常光顾的地方,请描述MBR的大致结构,并说明如何备份和恢复主引导扇区(假设所用硬盘格式为DOS或fdisk格式的)。解:可参见§4.1、§4.6.2及图4-1和图4-3。关于DOS/fdisk格式的硬盘主引导扇区(MBR)的结构请参考图4-1和图4-3。在UNIX/Linux系统下备份MBR非常简便,方法和工具可参考dd命令(§4.6.2)。比如将第一个IDE硬盘的主引导扇区复制到文件MBR.hda,可使用如下命令。#ddif=/dev/hdaof=MBR.hdabs=1bcount=1#一个块即可恢复时,只需要按相反方向就可以了。#ddif=MBR.hdaof=/dev/hdabs=1bcount=1#备份文件必须正确,否则后果严重恢复主引导扇区扇区时,备份的数据、块大小和块必须指定正确,否则将会带来严重的后果。需要说明的是,备份容易恢复难。难就难在,若MBR一经破坏,系统就启动不起来了。若遇此种情况,则需要在其它系统下,通过备份的MBR恢复损坏的硬盘。14.试述造成UNIX/Linux文件系统受损的常见原因,如何能有效地避免文件系统受损?如何修复受损的文件系统?解:可参见§4.5.5。造成UNIX/Linux文件系统受损的常见原因有系统非正常关闭,如强行断电或意外掉电,肯定会造成操作系统或数据的损坏。另一大原因是,文件系统非正常卸载或强行拔出。没有按照存储设备的“安装->使用->卸载”使用方法正常使用存储设备。当然,还有其它方面的问题。对于受损的文件系统,系统在启动时会自行清理和修复,对于检查和修复可能有不能正常通过还需要手工修改的情况,修复工具为fsck。fsck用法为:fsck[options][-tfstyp][filesystems]#Linux&FreeBSDfsck[options][-Ffstyp][filesystems]#Solaris其中的选项或参数请参见表4-15。fsck只能对原始设备进行检查,因此,一般不用于已经安装且正在使用的文件系统。当系统没有正常关闭时,再次启动时会自行调用fsck进行检查。手工检查和清理的常用方法是:fsck-y这里没有指定文件系统,而是让系统参照fstab文件进行检查。对没有正常拆卸文件系统的检查的方法为:fsck-y-t/-Fdevice#-F用于Solaris/-t用于Linux&FreeBSDdevice为设备名,要视具体系统而定。假设在Linux、FreeBSD和Solaris下device分别为sdb1、da1s1和c1t0d0p1,文件系统类型为fat,此时命令分别为:#fsck-y-tvfat/dev/sdb1#Linux#fsck-y-tmsdosfs/dev/da1s1#FreeBSD#fsck-y-Fpcfs/dev/rdsk/c1t0d0p1#Solaris 第5章进程与作业管理1.UNIX/Linux系统的程序、作业和进程是什么,它们之间关系如何?解:参见§5.1.1。程序是存储在存储介质上的可执行文件,它一般是既具有可执行的属性又且具有执行内容的普通文件。程序的执行过程表现为对某个或某些数据的加工和处理过程,它要完成某种特定的任务。进程是一个程序动态的执行过程,一个程序的不同次执行,表现为不同的进程。在一个系统中同时可以运行很多进程,甚至同一个程序同时运行多次,但它们对应不同的进程,在系统内有不同的进程标识。也就是说,不同的进程可以执行同一个程序。作业(job)或任务(task)是用户需要操作系统完成某项任务时计算机系统所要做的工作的集合,是一个任务实体,比如在某个时刻或时间点重启系统,在某个时刻要打印某个文件等。一个作业可能需要几个程序联合才能完成。2.UNIX/Linux系统有几类进程,试说明后台进程的作用或执行过程?解:可参见§5.1.2UNIX/Linux系统中的所有进程可分为3类:前台进程、后台进程和批处理进程。后台进程(backgroundprocess)是指在后台运行,且运行过程不需要用户干预的进程。在一个系统中,可能运行着很多一般用户不知道、也不关心的进程,比如系统本身用于管理和控制的进程、各种服务器进程等。服务器进程也叫守候进程,它是后台进程的一种,由于在后台默默地工作,故又被称为精灵进程(daemonprocess)。此类进程生命周期无限长,在系统启动时启动,在系统关闭时才停止。前台进程也可放在后台运行,这时就不能再进行交互了,若需要输入、输出的话,可在启动时使用I/O重定向。后台进程的通常启动方法是:cmd选项参数output&关于服务器启动与设计可参见后述的§6.3和§19.1.1等部分。3.试述0#和1#进程的作用及进程树的形成与查看。解:可参见§5.1.3、§5.1.4及图5-1。Unix/Linux系统的启动是通过加电和系统自检后,把主引导程序(MBR)装入内存并把控制权交给它。在主引导程序的控制下再装入Unix/Linux的引导程序(引导块),并把控制权交给它。由于该引导块是属于操作系统的,它知道继续引导应该做什么,于是在它控制下系统继续引导,在把核心装入内存后,系统开始进一步的初始化过程。首先初始化系统内部数据结构,然后,将根文件系统安装到根“/”下,并创建0#进程、设置它的运行环境,至此系统的内核启动完成,但还不能做太多的工作。为此,系统继续初始化,创建1#进程,然后在1#进程的控制下做进一步的初始化工作,直到启动完毕。根据系统配置或所安装软件的不同,在系统的启动过程中,可能要创建或启动很多其它进程。具体系统的引导可分别参见§6.3。系统启动完毕后,在(字符或图形)终端上出现了登录界面,此时用户可以登录了。进程树的查看可使用pstree(Linux/FreeBSD)或ptree(Solaris)。0#进程与1#进程是UNIX/Linux系统中最重要的两个进程。在UNIX系统中,0#进程是唯一只在核心态下执行的进程,功能有三:调度分配处理机;负责进程交换;初始化时创建1#进程。在Linux系统中,0#进程在创建1#进程后,变成了闲逛进程(Idle),当系统中没有其它进程就绪时它才运行。事实上,在现代的系统中,0#进程的任务已经被分解,它的任务由一些核心进程或线程来具体完成,在这点上,不同系统有不同的实现,用户可以通过ps-A|more来观查系统内的全部进程。在Linux系统下看不到0#进程,0#进程在FreeBSD中名为kernel,在Solaris中名为sched。1#(init)进程是系统的初始化进程,在系统启动时它按照启动规划文件(/etc/inittab或/etc/rc.conf等)创建其它进程。系统初始化完成后,1#进程了变成回收进程,回收状态为ZOMBIE的僵尸进程或领养没有父进程的孤儿进程。关于init进程及系统的启动控制,在不同系统间差别较大,请参考§6.3。从进程创建关系来看,0#进程创建了1#进程,1#进程在系统启动过程中创建了系统所需要的其它进程,除了0#进程外,1#进程是其它所有进程的祖先进程。4.试述UNIX/Linux系统的进程状态及转换?解:参见§5.1.5及图5-2。在UNIX系统中,“正在执行”的进程可能处于下列状态之一: (1)用户态运行。进程占有处理机,正在执行。(2)系统态运行。当在用户态执行的进程需要系统提供服务时,转入内核执行,进入此状态。图5-2UNIX系统的进程状态及转换(3)就绪状态。进程仍未运行或已经运行了一段时间,由于时间片用完而放弃处理机或被抢占,进入此状态。处于此状态的进程已具备运行条件,只要调度进程调度它占有处理机,就可立即进入运行状态。(4)内存中睡眠。当正在执行的进程需要资源但暂时得不到满足时进入此状态。(5)外存中睡眠。进程在内存中睡眠过程中,因内存不足而导致它被换出内存到交换区或交换设备上,进入此状态。(6)外存就绪。当处于外存睡眠或等待的进程等待的事件发生或条件满足后,进入此状态。处于此状态的进程,必须变为内存就绪后才有可能再次得到执行。(7)进程从系统态返回。当进程从系统服务返回时处于此状态。若此时系统中有了比该进程优先级高的进程,它的执行权将被剥夺,而让优先级高的进程执行。若该进程被剥夺执行权,它被放在就绪队列中,进入就绪状态。(8)创建状态。当进程被创建时进入此状态。创建一个进程要分作两步:①为新进程创建一个PCB结构,并填写必要的信息;②为其分配资源。一个进程处于此两步之间时的状态就被称为处于创建状态。引入创建状态的目的是为了保证进程调度必须在创建完成之后进行。一般来说,刚被创建后的进程将会被优先执行,所以刚被创建后的进程处于就绪状态。(9)僵尸状态。当进程完成指定任务或因某些特殊原因而结束自己时,进入此状态。此时进程不再活跃,也不再占内存或拥有其它资源,而仅在PCB表中保留一个记录来记录它的退出码和记时等信息供父进程使用。这是进程在整个生存期内的最后一个状态,当父进程调用wait()或waitpid()对其信息处理后,将立即从系统中消失。5.何为suid、sgid和sticky属性,它们的存在有什么意义,应该如何管理它们?解:可参见§5.3。suid即setuid(setuid),称为设置uid。当一个程序具有suid属性时,不论是谁在执行它,它执行时的有效uid将变为该程序所有者的uid,因此执行者的有效身份被改变了。于是就出现了,执行者的uid与执行时的uid不同的问题,我们把执行者的uid叫做真实的uid,记为ruid;把执行时的uid叫做有效的uid,记为euid。sgid即setgid(setgid),称为设置gid。当一个程序具有sgid属性时,它执行时的gid将是该程序的gid,即有效gid,记为egid,而执行者的原来gid叫为真实gid,记为rgid。suid/sgid属性只对二进制可执行文件有效,对脚本文件无效。suid和sgid的存在,改变提高了执行者的地位或放大了执行者的权限,可以带来方便,但也可以带来了危险和安全隐患,应加以有效控制,因此设置这种权限的程序不宜太多。UNIX/Linux系统还允许目录使用sticky位属性(粘着位,以后我们把它叫做sticky位或sticky属性)。当一个目录设置了sticky位后,它内部的文件只能被文件主、目录主或超级用户删除、更名或移动。sticky属性常用于像/tmp和/usr/tmp这样可供所有用户以写使用的目录。sticky位也可以用于二进制可执行文件,但在现在的很多Unix系统中,sticky位对于普通文件的作用已经失去了原有意义。suid、sgid和sticky属性或权限的管理工具仍然是chmod,管理示例如下。#chmodu+sp1#为p1设置suid#chmodg+sp2#为p2设置sgid#chmodug+sp3#为p3同时设置suid和sgid#chmodg-sp3#去除p3的sgid权限#chmod+tmyd#为myd增加sticky属性或设置sticky位#chmod4755p1#为p1设置suid#chmod2775p2#为p2设置sgid#chmod6777p3#为p3同时设置suid和sgid#chmod1755myd#为myd增加sticky属性和权限6.UNIX/Linux系统中常见进程状态标志有哪些?各是什么意义?解:可参见§5.4.2及表5-4。 在ps-lA的输出信息中的状态(S或STAT)栏常见标志及意义如表5-4所示。表5-4ps输出信息中的进程状态信息状态意义LinuxFreeBSDSolarisRRunable:进程在执行中就绪就绪DDlaying:非中断性睡眠外存等待,非中断性睡眠SSleeping:正在睡眠睡眠少于20秒睡眠等待事件发生TTraced/stopped:被跟踪或停止同左同左ZZombie:僵尸状态同左同左WWait:内存无页面(2.6后不再支持)中断睡眠等待CPUIIdle(睡眠超过20秒)O在某个CPU上运行在FreeBSD系统中,进程还可以有以下子状态:N-低优先级进程;L-Locked,页面被锁进内存;s-会话头进程;J-进程以chroot方式进行;+-终端控制进程;<-高优先级进程。7.如何查看系统内的进程及状态,如何终止不需要或失控的进程?解:可参见§5.4.2。查看系统内的进程及其状态可带选项-l执行ps命令。比如ps-Al的输出格式为:Linux/Solaris:FSUIDPIDPPIDCPRINIADDRSZWCHANTTYTIMECMDFreeBSD:UIDPIDPPIDCPUPRINIVSZRSSMWCHANSTATTTTIMECMD其中的S(Linux/Solaris)或STAT(FreeBSD)为状态列,状态信息及意义可参考表5-4。若要终止一个进程可以使用kill、killall(Linux和FreeBSD)或pkill,具体操作方法参见kill、killall和pkill命令的用法。需要说明的是,当使用进程名终止进程时,可能会造成误伤,因为系统中可能会存在着多个同名进程。8.试述kill、killall和pkill有什么相同与不同点?解:可参见§2.2.4、§5.4.3和§5.4.5kill、killall和pkill的主要功能是向指定进程发送指定信号的,若不指定信号默认发送SIGTERM。具体相同或不同点及相关用法请分别参考§2.2.4、§5.4.3和§5.4.5或在线手册。9.试述fuser的功能与作用,如何终止在终端tty2运行的所有进程?解:可参见§5.4.6。fuser可以确定使用某个设备或文件系统的进程或用户的情况,还可以向使用设备的进程发送信号(让它们终止)。Fuser在不同系统间用法有区别,具体地可参考§5.4.6及其中的表5-6。若要终止在终端tty2运行的所有进程可以使用以下方法:#fuser-k/dev/tty2#Linux#fuser-k-9/dev/tty2#Linux#fuser-k-s9/dev/ttyv1#FreeBSD#fuser-k-s9/dev/vt/2#Solaris10.进程的优先级是怎么回事,如何观查和调整进程的优先级?解:可参见§5.2.1、§5.4.2、§5.4.8和§5.4.9UNIX是按照时间片+优先级方式调度进程执行的。每个进程都有一个动态变化着的优先级,只有优先级最高的进程才能得到执行。一般来说,在系统中维护一个就绪队列和几个因不同原因的阻塞而等待有队列,只有在就绪队列中按优先进行排队的进程才有资格等待调度程序调度执行。一个优先级最高的进程,若在自己的时间片内完成了任务就退出,否则执行执行了一段时间后,因时间片用完或资源不到位而自动放弃处理机。若没有执行完毕而时间片用完则再次排到就绪队列的尾部,它将变为优先级“最低”的进程;若因为其它事件(比如等待I/O)则放弃处理机,则加入到其它阻塞队列,等到等待的事件发生或I/O完成后,则再次加入就绪队列参与排队等待调度执行。排在就绪队列的进程,则随着等待时间的增加,优先级也逐步增加,直到达到最高才能被再次调度执行,如此反复直到执行完毕。在优先级计算过程中,还有一个人工可以控制的参数参与运算,这就是传统UNIX/LinuxNice Level优先级。按传统做法,人们把它称为NICE,这是一个人工设置静态值。系统也为每个进程设置了一个默认的NICE值(Linux和FreeBSD中NICE的取值范围为-20~19,默认为0,Solaris中的NICE取值范围为0~39,默认为20),用户或管理员可以通过命令nice调整进程的NICE值,从而达到调整优先级的目的。Nice级的优先级NICE与通常意义下的优先级有着相反的意义,其值越大,进程的“优先级”就越低,为了区别人们把它叫做优先数。通常把不重要或所完成任务不紧迫进程的NICE设置的大一些。可以使用ps命令的-l选项来显示进程的优先级和NICE值。比如ps-Al的输出格式为:Linux/Solaris:FSUIDPIDPPIDCPRINIADDRSZWCHANTTYTIMECMDFreeBSD:UIDPIDPPIDCPUPRINIVSZRSSMWCHANSTATTTTIMECMD其中的PRI和NI分别表示优先级和NICE。可以通过nice命令来设置一个进程的NICE值,使用方法参见§5.4.8。也可通过renice调整一个正在执行进程的NICE值,使用方法参见§5.4.9。11.调度作业的执行的工具有哪些?如何调度一个任务的执行?解:可参考§5.5。作为任务和作业调度的客户端工具有at、batch和crontab。at、batch用于任务或作业和定时执行,而crontab用于作业的周期性执行。关于at和crontab用于任务和作业的调度及示例请参考教材的第121~122页和第123页。 第6章系统安装、启动与管理1.UNIX/Linux系统需要哪些分区?在安装系统时应如何规划分区?解:可参见§6.1.2。通常情况下,不同的系统所需的分区在数量、大小及命名等方面存在区别,但大致情况差不多。(1)引导分区系统的引导分区(boot)是负责本系统引导的,应位于硬盘上可引导范围之内。引导分区不需要太大,够用就行,只要能安装下负责系统启动的软件包就可以了。一般来说200M就足够了。引导分区不是必须的,当没有分配引导区时,它共享系统的根分区。(2)根分区根分区(root)是UNIX/Linux系统的安装和工作的分区,不能太小,要根据安装系统的内容和将来的用途来决定。考虑到将来的系统升级等方面的要求的话,根分区要安排得足够大。(3)交换区交换区(swap)是UNIX/Linux系统用于动态扩充内存的,安装时可以不指定,但是为了保证安装的成功率,建议设置独立的交换区。交换区的建议大小为内容的1.5~2.5倍,若磁盘空间富裕的话,还可以再大一点。在Linux系统中可以设置独立的交换分区,在FreeBSD和Solaris中,可以在自己所占的分区内设置交换区。不论是否指定了交换区,系统安装或运行时是需要交换区的。若未指定交换区,系统会在自己的根分区上找一块连续的区域用作交换区。(4)其它分区仅从安装的角度,有了根分区和交换区就可以了,boot区可以共享根分区。在系统工作时可能还需要/var、/home和/tmp等分区,若不指定则共享系统的根分区。一般来说,根分区应该相对稳定,其大小只要能够安装下所有的软件就可以了。其它分区,比如/var是用于存放像ftp、mail、web、DNS等经常变化的数据的,大小很难估算准确,建议做成单独的分区,或放在一个单独的硬盘上,这样可以避免因大量数据的增加,导致根文件系统资源用尽而影响整个系统的正常运行的情况。2.你所的系统的引导程序是什么?在系统安装时可安装在哪些位置?可起到什么作用?解:可参见§6.2及其中的§6.2.1、§6.2.4和§6.2.7。本书所涉及的3个系统中,Fedora、Solaris使用的引导程序是GRUB,可以安装系统的主引导扇区或系统自己的逻辑引导扇区。当安装主引导扇区时将影响整个机器中的所有共享硬盘的系统,而安装在本系统逻辑引导扇区时,则默认情况下只影响自己(若经特殊配置也可启动或影响共享硬盘的其它系统)。不过,若要安装在主引导扇区的话,可引导系统可位于更大的分区范围,比如,可位于逻辑分区内。FreeBSD使用自己引导程序,应安装在自己逻辑引导区中。不过FreeBSD在自己的ports中,也有grub和grub2程序,用户可以有选择的安装它。3.试分析所用系统是如何启动的?系统启动所涉及的目录或文件有哪些?如何影响系统的启动?解:可参见§6.3及各系统的启动管理。此题内容较多,且在各系统间存在较大差异,请用户结合具体系统自行解答。4.如何在系统启动时启动用户程序?试以实例说明之。解:可参见§6.3及各系统的启动管理。若要在系统启动时启动用户程序有多种方法,简单且有效的办法是将用户启动脚本添加到文件/etc/rc.local(FedroaLinux、FreeBSD和Solaris均使用此文件)。但要注意的是,若要启动的程序不是按服务器方式设计的,应该以后台方式启动(在命令的后面追加&),否则将造成程序启动后不会退出,而影响系统的后续启动。还要注意,若程序需要I/O的,应该使用I/O重定向。假设要在系统启动时要替用户完成以下任务:(1)删除临时目录/tmp/下的tmp.*文件;(2)将/tmp/中的文件boot.flag的访问时间改为启动时间;(3)启动用户服务器userserver。可以/etc/rc.local中,添加以下行rm-f/tmp/tmp.*2>/dev/null&touch/tmp/boot.flaguserserveroutfile&rm命令的执行时间是短暂的,完成删除任务后就结束了,其后&是可选的;touch命令的执行更是瞬间结束,其后可不使用&;但服务器程序userserver的执行时间却是无尽的,若userserver不是按照服务器编程规则(参见§19.1.1)设计的,则必须在其后使用&,既使userserver是按服务器编程规则设计的,在其后跟上一个&也是不会有问题。 由于touch能瞬间执行完毕,故没有在其后使用&。5.所用系统的服务是怎样管理的?试以ftp服务器为例说明服务的管理。解:可参见§6.4。(1)整个服务管理部分位于§6.4,传统Linux、SsyV和Fedora9之前系统参见§6.4.1;Fedora15之后的系统参见§6.4.2;Solaris11参见§6.4.3;FreeBSD9参见§6.4.4;Fedora9~Fedora14参见参考文献[12]。(2)假设ftp服务器及相关软件包均已按各自系统的默认方式安装配置完毕。1)Linux及Fedora9有前系统:开机自启动设置:#chkconfig--level235vsftpdon#让vsftpd服务工作在2、3和5级#chkconfigvsftpdon#让vsftpd服务工作在当前级#chkconfig--level2345vsftpdoff#在运行级2345关闭vsftpd立即启动:#servicevsftpdstart#启动httpd服务#servicevsftpdstop#停止httpd服务#servicevsftpdrestart#重启动httpd服务2)Fedora15+激活ftp服务#systemctlenablevsftpd.service#若成功返回0,否则非0或立即启动/停止/重启/重载服务#systemctlstart/stop/restart/reloadvsftpd.service#或禁用vsftpd服务#systemctldisablevsftpd.service#成功时返回0,否则为非0或3)Solaris11激活并启动ftp服务#svcadmenablenetwork/ftp#激活并启动ftp服务器禁用并停止ftp服务#svcadmdisablenetwork/ftp#禁用并停止ftp服务器4)FreeBSD9在FreeBSD9下ftp服务器可在超级服务器管理下的运行,也可作为独立服务器运行。①在超级服务器管理下运行a.激活inetd超级服务若是在超级服务器管理下运行,使用ftp服务前,需首先启动超级服务器inetd,方法是修改默认配置文件/etc/defaults/rc.conf,将其中的inetd_enable="NO"#Runthenetworkdaemondispatcher(YES/NO).中的"NO"修改为"YES",并保存。或编辑/etc/rc.conf文件,在其中添加一行inetd_enable="YES"以上两种方法均可实现将inetd服务设为开机自启动,不过一般使用后者。若要临时启动inetd,可使用和命令#/etc/rc.d/inetdonestart#启动未启用的服务inetd或#serviceinetdonestart#同上b.激活ftp服务编辑/etc/inetd.conf文件,使以下行生效ftpstreamtcpnowaitroot/usr/libexec/ftpdftpd-l②以独立方式运行若要使ftp服务器独立运行,只需要在/etc/rc.conf文件中增加以下行即可ftpd_enable="YES"6.超级服务器是怎么一回事?如何使其正常运行?解:可参见§6.4.5。现在操作系统中,一般都有很多服务,若对每个服务系统均运行一个守护进程来监听对应端口,这通常意味着系统资源的浪费,为此,引进了“超级服务器”的概念,或叫“委托服务器”。它可以为多种服务管理连接,收到连接请求时,它能够确定连接所需的程序,启动相应的进程来与客户端通讯,完成服务工作。使用这种服务器,主要用来管理那些负载不重,且不经常使用的服务,这样有助于降低系统负载,因为它不需要为每个服务都启动独立的守候程序。 包括FreeBSD9和Solaris11在内的很多Unix使用的超级服务器为inetd,Linux和Fedora使用的超级服务器为xinetd。为使其能正常运行,需要对超级服务器进行一定的配置,必要的要首先安装超级服务器软件包。具体的配置情况参见§6.4.5和各系统的服务管理部分。7.系统常见日志有哪些,如何阅读它们?解:可参见§6.5。系统的常见日志可参阅表6-12或日志系统配置文件/etc/syslog.conf或/etc/rsyslog.conf。系统的日志分文本型和二进制型两种。文本型日志可以使用文本编辑(vi,gedit等)或阅读器(grep、cat、head、tail、more和lees等),还可借助系统提供的图形图面日志阅读工具来进行。当使用文本编辑或阅读器阅读文本日志时,还要知道日志文件的位置,具体地可参阅日志系统配置文件/etc/syslog.conf或/etc/rsyslog.conf。而对于非文本日志可以借助专用命令who、whoami、last、lastlog和lastb来阅读;内核启动日志可使用dmesg命令来阅读。需要说明的是,在不同的系统中,日志系统的配置和文件名并不完全相同,请视具体系统而定。8.系统管理的任务是什么?如何做好系统管理员工作?解:可参见§6.6.1及表6-13。每个Unix/Linux系统应至少有一个人负责系统的维护与日常管理,这个人被称为系统管理员。系统管理员的职责是计划、安装、扩充、升级和维护系统,确保系统的高效、平稳、安全运行。系统管理员的职责或任务应该包括以下方面:(1)系统的开启与关闭以及对启动过程的维护;(2)数据备份并保存好备份数据,确保备份数据的有效性和安全性;(3)处理好计算机有限资源的利用问题(内存、磁盘空间和进程数等);(4)设备管理与维护;(5)网络管理与维护;(6)系统升级、扩充等维护和修补;(7)系统安全与网络访问控制;(8)对用户提供一般性服务及用户培训等。系统管理的经常性工作参见表6-13。除此之外,还有月任务、年任务等。9.如何了解所用系统的比如节点名、系统名和硬件平台等信息?解:可参见§6.6.2.1和§8.4.4。了解所有系统的相关信息可以使用uname,具体用法参见§6.6.2中的uname命令用法。查询主机名/节点名还可使用命令hostname。hostname的请参考§8.4.4。10.试结合vmstat的输出,说明如何做好系统管理工作?解:可参见§6.6.2中的vmstat命令及其在线手册。vmstst的输出中包含了进程活动、内存使用、交换区使用、I/O活动、CPU和页面活动等系统活动信息,对观察和分析系统的运行有着重要的支持作用,作为系统管理者,可对这些信息加以综合应用。(1)在进程(procs)信息中:(2)(2)对于内存(memory)信息:(3)关于交换(swap)信息,若长期换入/换出量较大的话,说明内存紧张,可采(1)中办法解决。(4)I/O信息(io):是对块I/O监督,可以看到磁盘I/O的情况。若长期I/O较重,说明系统负担较重。可从改进I/O系统(比如多磁盘、阵列等),也可以考虑分解任务。(5)系统信息(system/faults):(5)CPU信息:(6)页面(page)信息:11.如何查看系统内最活跃的进程?如果系统内最活跃进程的活动给系统带来了负面影响应该怎么处理?答:可参见§6.6.2中的top命令及进程管理相关命令。使用top命令可以查看系统中最活跃的进程。在top的输出中,第6行以后的内容为以次按活跃程度顺序排列的进程,并会根据活跃程度不断变化。若某个或某些进程非常或持续的活跃,乃至给系统带来了负面影响,要仔细对它们进行观察和分析。若是不该如此活跃的,可以通过renice命令降低其优先级,不需要运行者,可以使用kill/pkill命令终止其运行。 第7章软硬件管理及系统的扩充与升级1.软件包管理功能用哪些?试以mysql数据库服务器及客户端程序说明软件包的安装与管理。解:参见§7.1。每个系统均提供有自己的软件包管理工具。一般来说软件包管理工具提供有软件包安装(install)、卸载(remove)、升级或更新(upgrade)、查询(query)和验证(verify)等功能。mysql数据库服务器及客户端软件包在不同系统下的安装方法分别如下。FedoraLinux:#yuminstallmysql*#安装所有的mysql开头的包FreeBSD:#pkg_addmysql*#本地安装mysql服务器和客户端软件包。在/packages/All/下#pkg_add-rmysql*#远程安装mysql服务器和客户端软件包Solaris:#pkg_installmysql*#本地安装mysql服务器和客户端软件包2.如何升级所用系统?解:可参见§7.2。系统升级在不同的系统中有不同的方法,但在不同的系统的图形界面下,都提供基本相同或相似的升级方法(参见§7.2)。每个系统还都提供有字符界面的工具,比如在不同的系统下可分别使用以下命令升级整个系统:FedoraLinux:update[package(s)]#升级软件包,若不指定包名,则升级所有包FreeBSD:freebsd-update-rnewreleaseupgrade#教材中未介绍Solaris:pkgupdate#升级系统这里需要指出的是,手工升级系统不是件容易事,可能会遇到很多困难,因此建议在图形界面下进行。3.系统的常用设备有哪些?在所用系统中SCSI存储设备是怎么命名的?如何使用移动存储设备?解:可参见§4.5.2和§7.3。在Unix/Linux系统中有很多各种各样的设备,部分较常用的设备及名称请参见表7-5。Unix/Linux系统对SCSI设备命名可参见表7-5和§4.5.2。系统对移动存储设备的命名是按SCSI方式进行,且使用也是按SCSI存储方式使用的。4./dev/null和/dev/zero是什么设备,有何用处?试举例说明如何使用它们?解:可参见§7.3.1。/dev/null和/dev/zero是所有UNIX/Linux都提供的2个特殊设备文件,它们是人工制造出来的,不对应任何物理设备,但却非常有用。/dev/null被称为空设备,用户或程序可把不想要的东西扔给它,让它悄悄地消失。通过I/O重定向将信息重定向到/dev/null时,不会启动I/O设备,也不产生磁盘空间占用。/dev/zero为0字符生成器。这时的0字符,指的二进制的真正的0(0x00或)。当从该设备读取信息时将会得到0字符。使用示例如下:ls-l/www/usr>/dev/null#将标准输出重定向到空设备ls-l/www/usr2>/dev/null#丢弃标准错误ls-l/www/usr>/dev/null2>&1#同时丢弃标准输出和标准错误ddif=/dev/zeroof=nyfilebs=1kcount=1#创建一个长度为1K的文件,内容全为05.CUPS是什么意思?如何配置和使用CUPS打印机?解:可参见§7.4。CUPS是CommonUNIXPrintingSystem的缩写,意为通用打印机管理系统。在UNIX/Linux下很久以来都是用lpd,它不支持IPP(Internet打印协议),而且也不支持同时使用多个打印设备。但CUPS给Unix/Linux用户提供了一种可靠、有效、统一的方法来管理打印,提供一个完整的打印解决方案。CUPS是一种模块化开源打印系统,使用Internet打印协议IPP作为基础来管理打印机、打印请求和打印队列,支持网络打印机浏览和基于PostScript打印机描述的打印选项,还提供了跨越本地网络的公共打印接口。 在各Unix/Linux系统中,配置CUPS的通用方式是浏览器方式(参见§7.4.2)。6.何为交换区或交换设备?系统安装时应该怎样分配交换空间?如何添加或扩展系统的交换空间?解:可参见§7.5。交换区或交换设备是系统用于对内存的动态扩充的载体,大小一般设为物理内存容量的1.5~2.5倍(参见P126)。如果出现了已配置交换区不够用的情况,可以再行扩充。用于交换区的设备可以是磁盘设备文件,在有的系统中,也可以是具有特殊格式普通文件。关于交换区的管理与使用请参见§7.5,具体地,Fedora系统参见§7.5.1;FreeBSD9参见§7.5.2;Solaris11参见§7.5.3。7.如何使用stty命令显示或设置终端属性?解:参见§7.6.3。在命令级设计终端参数或性能可通过stty命令。具体示例如下。#stty-a#获得终端设置(Fedora、FreeBSD和Solaris)#sttysize#获取终端屏幕大小(Fedora和FreeBSD)#sttyspeed#获取终端传输速率(Fedora和FreeBSD)#stty-echo#设置为非回送模式(Fedora、FreeBSD和Solaris)#sttyecho#设置为回送模式(Fedora、FreeBSD和Solaris)#sttytostop#禁止后台进程写终端#stty-tostop#允许后台进程写终端#sttysane#将出“问题”的终端恢复正常(Fedora、FreeBSD和Solaris)^Jsttysane^J#^J为Control+J8.何为终端性能?如何使用终端性能?解:可参见§7.6.4。这里的“终端性能”就是教材中的终端能力(terminalcapability),它涉及到termcap文件和一些使用此文件的程序。termcap是一个文本文件,描述了很多终端类型,相关程序可以通过读取该文件而得到特殊的escape序列来控制正在使用的终端。termcap是一个已经过时了的终端数据库,只是一些传统的老程序还在使用它,而新的程序使用terminfo数据库。不论是termcap还是terminfo,都是用于终端控制与设置的,特别是全屏幕编辑等操作的。截止到教材的现在阶段,对终端及终端性能的使用还只能局限的命令方面。比如可以使用stty命令设置终端,通过tput命令等使用终端性能(数据库)(参见§7.6.3和§7.6.4)。关于在编程中对终端性能的使用可参见第17章。9.使用tput编写一个shell程序用于光标控制(内容自行拟定)。解:可参见§7.6.4.3。设shell脚本程序为etput.sh,清单如图e07-1所示。tputcleartputcup510echo"+-----------------------------------------+"tputcup610echo"||"tputcup710echo"|1.functio12.function23.quit|"tputcup810echo"||"tputcup910echo"+-----------------------------------------+"tputcup1020;echo"Pleaseinputakey:";tputcup1040;readx图e07-1etput.sh程序清单脚本程序中的tputclear用于清理屏幕;tpucupxy用于将光标定位到第x行,第y列;echo用于在当前光标开始处显示字符串。etput.sh程序执行方法请参考§9.4.5。 第8章网络管理与网络应用1.通用网络配置文件有哪些?各自的结构和功能是什么?解:可参见§8.2。通用网络配置文件有/etc/hosts、/etc/services、/etc/resolv.conf和/etc/networks等。/etc/hosts是将IP地址和主机名联系起来的一个文本文件。通过它可以将主机名映射为IP地址,从而可以起到DNS的作用。/etc/hosts文件中的空行或以“#”开头的行为注释行被忽略,其它为有效行。有效行的结构为:ip_addrhostname[alias…]其中ip_addr为IP地址,hostname为主机名或域名,alias为别名。别名可以没有,也可以有多个。/etc/services是服务和端口与协议对应文件,或者叫服务定义文件,其中的空行或以“#”开头的注释行被忽略,有效行结构为:servicenameport/protocolalias…其中servicename为服务名,port为端口号,protocol为服务所使用的协议,alias为服务别名。在此文件中每个有效行定义一个服务,用户也可在此文件中定义自己的服务。/etc/resolv.conf是DNS客户端配置文件,用于设置DNS服务器的IP地址及DNS域名,还包含了主机的域名搜索顺序。它的每一行都包含一个关键字和由白空格分隔的参数。合法的参数及其意义如下:(1)nameserver:域名服务器名。最多可以有3个nameserver,每一个带一个IP地址。(2)domain:声明本地主机的域名。该选项可以不设置。当不设置时,使用本地域名。(3)search:定义由白空格分隔的域名搜索列表,最多加入6个,通常只有localdomain。如果resolv.conf不存在或是空的,系统就假设DNS在本地的主机上。/etc/networks是一个文本文件,用于定义已知的子网。每个有效定义一个子网,格式为:netnameaddr[alias…]netname为子网名称;addr为点分十进制IP地址形式,后面的.0部分可以省略;alias为子网别名,也可以不使用。networks只支持A、B和C类网络,定义在其中的子网可以被route和netstat等使用。除此之外,每个系统还都有自己的用于网络配置的其它文件,可分别参考§8.3.3、§8.3.4和§8.3.5。2.什么是端口,什么是服务,如何定义自己的服务?解:可参见§8.1.4。端口和服务的描述可参见§8.1.4,更精确的描述可考相关网络资源。关于对服务定义,一般是通过/etc/services文件实现的,具体结构可参考§8.2.2。当用户需要服务及端口时,可以按services文件格式定义自己服务,但要避免使用众所周知或注册端口,服务名字也不要重复。因此,可以按格式要求,在49152~65535间选择未用端口,并为自己的服务定义一个与众不同的名字,比如myserv56789/tcp3.何为主机名,何为官方主机名?如何查看和修改主机名?解:可参见§8.1.5。主机名就是本计算机系统的名字或叫计算机名,网络内可通过主机名来进行彼此识别,比如,网上邻居就是根据主机名来识别的。一般来说,在同一个网络内不应有相同的主机名。主机名有本地主机名和官方主机名之分。两者可以不一致,一般来说本地主机名(或系统名)较短,可按网内规定自行指定,但在同一网内不能有重名。而官方主机名是InterNet是使用且可以通过DNS进行解析的名字,一般较长比如www.hncj.edu.cn、www.163.com等。UNIX/Linux系统中,通常使用hostname命令来显示或设置系统的主机名。关于本地主机名的查看与设置请参考§8.4.4的hostname命令。Hostname只能临时设置主机名,当系统系统重启后,会丢失或恢复为原来的名字,永久修改主机名的方法是修改配置文件。主机名的修改在不同的系统中处理方法不同,因系统而异,在Fedora、FreeBSD9和Solaris11中的设置方法可请参考§8.4.4。4.何为DNS?如果在某个局域内没有DNS服务器,应该如何为网内主机进行名字解析?实现名字解析的客户端工具有哪些?如何使用? 解:可参见§8.1.5、§8.2.1和§8.2.3。DNS是指域名系统(DomainNameSystem)。5.网络的配置方式有哪些?假设系统内只有一个网卡如何为其配置网络地址?如何在一个网卡上配置多个IP地址?解:可参见§8.3.1、§8.3.3、§8.3.4和§8.3.5。可以采用静态或自动配置的方法配置网络地址信息。Fedroa16、Solaris11和FreeBSD9的配置过程和示例请分别参见§8.3.3、§8.3.4和§8.3.5。6.netstat的功能是什么?如何使用netstat命令观查系统内的网络链接、路由和端口活动情况?解:可参见§8.4.2。netstat主要用于显示系统网络状态信息,包括网络接口、路由表和网络链接通讯统计信息等。通过这些信息可以了解网络接口、路由表、开启的端口、正在为哪些用户服务以及服务的状态等,可参见§8.4.2。具体方法如下:#netstat-i#显示网络接口及使用信息#netstat-r-n#显示核心路由表信息#netstat-a-ptcp#显示所有TCP端口(FreeBSD)#netstat-a-Ptcp#显示所有TCP端口(Solaris)#netstat-a-t#显示所有TCP端口(Linux)7.如何配置和使用telnet命令及telnet服务?如何在telnet服务不允许root登录的情况下,通过root用户实现远程维护?解:可参见§8.5.1、§8.6.1及§3.6.6和§3.6.7。关于配置问题,请参考§8.6.1,若需要安装服务软件包的话,请按照各系统的软件包管理办法安装相应的软件包。若服务器不允许root用户登录的话,可先使用一般用户登录,登录成功后,再使用su–将自己“变为”root,然后再执行相关的命令。当然,也可以使用sudo执行相关的命令,过程要视目的而定,具体过程从略。客户端的应用可参见§8.5.1。8.如何配置和使用ftp服务器和客户端,并实现匿名用户下载和本地用户登录?解:可参见§8.5.2和§8.6.3。1)ftp客户端使用方法可参考§8.5.2。一般使用方法如下(1)浏览器方式①匿名用户登录方式:在浏览器地址栏中输入FTP服务器IP地址或主机名。ftp://ftp_svr#服务器名据实际情况而定,比如②用户登录方式:在浏览器地址栏中输入FTP服务器IP地址或主机名,且在主机或IP地址前指定本地用户名。ftp://user@ftp_svr#用户名和服务器名据实际情况而定,比如(2)命令方式#ftpftp_svr2)各系统、各版本的ftp服务器端配置差别很大,不能一概而论,需要参考各自系统的ftp服务器配置在线手册。Fedora16的ftp服务器为vsftpd,在服务名为vsftpd.service或vsftpd,默认配置时能够支持匿名用户的下载,但要注意防火墙iptables的配置。使用前需要激活该服务,方法如下#systemctlenablevsftpd.service#激活vsftpd服务,以后为开机自启动#systemctlrestartvsftpd.service#启动vsftpd服务关于防火墙iptables的配置,可以通过图形界面或直接修改iptables配置文件/etc/sysconfig/iptables的办法解决。在图形方式下,可以按顺序“Application”->“Other”->“Firewall”->“TrustedServices”->“勾选ftpd”->“应用/Apply”。当手工修改防火墙配置文件/etc/sysconfig/iptables时,只需要在其中,添加如下一行内容-AINPUT-mstate--stateNEW-mtcp-ptcp--dport21-jACCEPT然后,重启防火墙服务,方法是:systemctlrestartiptables.service#重启防火墙服务关于FreeeBSD9和Solaris11的配置请直接参考§8.6.3。 第9章bshell编程1.正则表达式可用于模式匹配与搜索,常见的正则表达式有几类?BRE的正则表达式可完全使用在ERE吗?答:可参见§9.1或其它网络资源。常见的正则表达式有(1)SRE(simpleregularexpression);(2)BRE(basicregularexpression);(3)ERE(extendedregularexpression)。在Linux系统中还有ARE(advancedregularexpression)是对ERE的扩展,因此ERE的表达式可直接在ARE上使用。ERE与BRE在用法上不尽相同,且ERE比BRE有更丰富的语法,但是ERE不是BRE的超集,一些BRE表达式若不加修改,在ERE中可能不能被正常使用。2.shell是可编程语言吗?它解释语言还是编译语言?答:参见§2.1。shell是可编程语言,且是解释语言。3.sed的地址是如何表示的?若要用sed实现wc-l功能应该怎么处理?解:可参见§9.2.2。(1)sed的地址形式参见表9-5。(2)可用以下形式命令实现wc-l对文件行数的统计工作:sed-n"$="file4.试要用sed实现head和tail的功能。解:可参见§2.2.3.5和§9.2。(1)实现headN或head-Nsed-n"1,Np"file或sed-n"1,+Np"file2)实现tail实现tailN或tail-N(N为具体数字,比如10)sed-n"$((`sed-n$=file`-N)),$p"file说明:将N换为具体数,比如10;file为具体文件名。5.awk的地址是如何表示的?若要用awk实现wc-l功能应该怎么处理?解:可参见§9.3。(1)awk没有明显的地址表现形式,但可以通过FNR或模式的匹配与搜索定位文件位置。(2)使用awk实现wc–l的命令为:awk"END{printFNR}"file#file为具体文件名6.awk的默认域分隔符是什么,如何改变awk的域分隔符?试将/etc/passwd的各域输出为由制表符分隔的表格。解:可参见§9.3。(1)awk默认的输入域分隔符为白空格。可以通过FS/OFS改变输入/输出的分隔符。(2)将/etc/passwd的各域输出为由制表符分隔的表格。awk脚本为awk-F:-vOFS="t""{print$1,$2,$3,$4,$5,$6,$7}"/etc/passwd或awk"BEGIN{FS=":";OFS="t"};{print$1,$2,$3,$4,$5}"/etc/passwd7.若规定在脚本文件中注释行和空行为无效行,其它的为有效行。试分别使用grep、sed和awk分别实现。解:可参见§2.2.3.1、§9.2和§9.3。设要处理的脚本文件名为file,处理方法如下。(1)找到并显示某文本文件的所有无效行的行号;grep-n"^[[:space:]]*#"filesed-n"/^[[:space:]]*#/="fileawk"/^[[:space:]]*#/{printFNR}"file 说明:这里只搜索了注释行,而没有处理空行。若要处理空行请参照(2)修改(2)显示某文本文件的所有有效行。grep-E-v"(^[[:blank:]]*#)|(^[[:blank:]]*$)"filesed"/^[[:blank:]]*#/d;/^[[:blank:]]*$/d"fileawk"!(/^[[:blank:]]*$/||/^[[:blank:]]*#/){print$0}"file(3)参照(2)对(1)的修改grep-n-E-v"(^[[:blank:]]*#)|(^[[:blank:]]*$)"filesed-n"/^[[:blank:]]*#/=;/^[[:blank:]]*$/="fileawk"!(/^[[:blank:]]*$/||/^[[:blank:]]*#/){printFNR}"file说明:对于grep说来,输出中除了行号外,还有内容。若有去内容只显示行号,可与awk配合使用,方法如下grep-n"^[[:space:]]*#"file|awk-F:"{print$1}"grep-n-E-v"(^[[:blank:]]*#)|(^[[:blank:]]*$)"file|awk-F:"{print$1}"8.shell脚本程序有几种执行方式,分别是什么?试举例说明之。解:可参见§9.4.5。(1)子shell执行。比如#./script.sh#脚本有执行权时#shscript.sh#脚本无执行权时(2)当前shell执行。比如.script.sh(3)覆盖方式。比如execscript.sh#或execcmd#比如:execls9.bshell的位置参数是如何表示和使用的?试编写一个bshell脚本程序计算它的所有命令行参数的和,并在计算机过程判断命令行参数作为数字的有效性,若遇无效数字就给出提示后返回0。解:可参见§9.4.3.2和§9.4.6。(1)bshell的位置参数可分别表示为$0、$1、$2、…,其中的$0表示本脚本程序名,在bshell脚本程序中可直接或间接的使用它们。(2)为了计算所有命令行参数的和,需要用于$*或$@,就本题来说$*或$@均可。设脚本程序名为esh01.sh,程序清单如图e09-1所示。#!/bin/shif[$#-lt1];then#无参数时,显示用法信息后返回1表示出错echo-e"Usage:n$0Natural_Numbera";exit1fisum=0#和变量,初始化为0foryin$*;do#搜索所有命令行参数x=`echo$y|awk"/^[[:digit:]]*$/{print$0}"`#判断是否只包含数字if[-z"$x"];then#如果不是全数字,则提示,然后忽略此参数echo-e"Invalidcommandlineparameter:$ya";continue;fisum=`expr$sum+$y`#累加doneecho"TheSumis$sum";exit0图e09-1esh01.sh程序清单esh01.sh的用法为esh01.sh数1数2…比如$./esh01.sh1235$./esh01.sh15034510.bshell程序是如何处理信号的,试设计一个作为登录控制脚本的菜单程序userlogin,功能自定,但一定要有退出功能,要求捕获各种可能的信号并采取一定办法控制它们不让脚本非正常终止。并修改某用户的~/.bashrc或~/.bshrc,将userlogin添加进去,使用用户成功登录后进入本菜单程序,程序退出后退出系统,不能系统作别的事情。解:可参见§9.4.6和§2.3。 为了保持脚本程序的命名一致性,将userlogin重命名为esh02.sh。(1)esh02.sh的一种实现如图e09-2所示。#!/bin/shfunc(){#功能函数1、2echo"Thisisfunction$1"}#捕获可能的信号trap""12315#忽略信号1、2、3和15trap""USR1USR2#忽略信号1、2、3和15USR1&USR2#若在图形界面下运行,则直接登录。若无此部分,则可能影响图形界面的工作tty>/dev/null2&>1if[$?-ne0];thenexit0;fiwhiletrue;doclear#清屏#显示菜单echo-n"AMenuDemonstrationbshellScripts+-----------------------------------------------+|1.func12.func23.ps4.whoq.quit|+-----------------------------------------------+Pleasegetachoice:"readx#读功能号case$xin#case结构开始:匹配功能1|2)func$x;;#功能1和23)ps-ef|less;;#功能34)who|more;;#功能4q|Q)exit0;;#功能q或Q*)echo-e"Invalidinputa";;#其它esac#case结构结束echo"PressENTERtocontinue:"#暂停以观察各功能执行过程readx#暂停以观察各功能执行过程,回车继续done图e09-2esh02.sh程序清单esh02.sh为对教材中图9-7所示程序sh04.sh的改进。(2)可将userlogin添加到用户登录控制文件(.*profile和.*shrc),当用户从字符界面登录后就直接取得控制,进入userlogin提供的界面。当用户输入q或Q键并回车退出时,用户会自动签退,重新回到登录界面。要求esh02.sh必须是具有执行权的,且要放在合适的位置,比如/usr/bin/或用户家目录。1)FedoraLinux可以当前shell执行方式,将userlogin添加到.bash_profile或.bashrc文件最后,建议添加到.bash_profile的最后,即其最后添加如下一行:.esh02.sh2)FreeBSD9在FreeBSD9下,bash所使用的.profile或.shrc,操作方法同FedoraLinux。对于使用csh的用户也是可以的,比如将其加入到.login文件。方法是在~/.login的最后加入如下一行内容:exec./esh02.sh3)Solaris11在Solaris11中使用的文件与FedoraLinux下相同,但是默认情况下.bashrc是只读,因此只能添加到.bash_profile中,方法同FedoraLinux。11.bshell脚本是如何处理命令参数和选项的?试编写bshell脚本程序,通过命令参数和选项处理,实现表2-8所示的cat命令的-b、-n、-E、-T和-s功能。解:可参见§2.2.2、§9.4和参见§9.3。参见§2.2.2中的表2-8可知cat命令的-b、-n、-E、-T和-s功能。设shell脚本名称为esh03.sh,清单如图e09-3所示。 图e09-3esh03.sh程序清单在esh03.sh的设计中,主要使用awk来处理相关有功能,-s没有实现留给读者,但程序的注释中已经给出了部分提示。另外添加了-c、-C和-w分别用于显示完内容后显示总行数、只显示总行数和只显示总域(词)数。在此程序对awk的使用中,采用了不同的方式,这里涉及到如何向awk脚本内传递shell变量的问题。有的使用的是双引号,意在进行参数替换传递shell变量EARG到awk,但awk脚本的内容却是awk"{printFNR,$0"$EARG"}"$f;有的采用的却是单引号,理由是因为使用了选项-vE=$EARG定义了awk的变量E。请注意和观察两者的区别。程序的用法为esh03.sh[-b|ncCeTw][Estr]file…应用示例如下#./esh03.sh-nesh03.sh#显示时添加行号#./esh03.sh-bT-E"$"esh03.sh#只为非空行加行号且将t显示为^I并在每行末显示$#./esh03.sh-nc-E"$"esh03.sh#加行号且在每行末显示$,最后显示总行数#./esh03.sh-cesh03.sh#仅显示内容,且在最后显示总行数#./esh03.sh-Cesh03.sh#仅仅显示总行数#./esh03.sh-wesh03.sh#仅仅显示总域数需要说明是,在Solaris系统中默认位置(/usr/bin/)的awk不支持-vvar=VALUE选项,需要使用/usr/xpg4/bin/下的awk才行。若在solaris中使用其它命令时遇到此类问题,也可照此办理。12.设计一个程序,计算1*1*1!+2*2*2!+3*3*3!+…+n*n*n!n是一个正整数,要求从命令行参数取得。若命令行参数不只一个,则要对每一个命令行参数都执行以上计算并输出结果。程序中要判断作为命令行参数输入n的数字合法性。解:可参见§9.5.2.11。为了简化代码或发挥shell脚本的优势,这里使用了bash。设脚本程序名为esh04.sh,清单如图e09-4所示。#!/bin/bash#注:首行必须设置正确:#Fedora/Linux:#!/bin/bash#FreeBSD:#!/usr/local/bin/bash#Solaris:#!/usr/bin/bash#hereitworksinLinuxif[$#-eq0];thenecho"Usage:$0INTEGER";exit1elsex=`echo$1|awk"/^[[:digit:]]*$/{print$0}"`#判断是否只包含数字if[-z"$x"];then#如果不是全数字,则提示错误,然后退出echo-e"Invalidcommandlineparameter:$ya";exit1;fifiif[$1-eq0];thenecho$1;exit0;fi#输入为0时f=1;s=0;t=1while[$t-le$1]dof=$((f*t));s=$((s+t*t*f));t=$((++t));doneecho"1*1*1!+...+$1*$1*$1!=$s"exit0;图e09-4esh04.sh程序清单由于使用的bash,所首行设置必须正确,否则本脚本不能正确执行提示找不到脚本解释器。首行的正确设计在不同的系统下分别为:Fedora/Linux:#!/bin/bashFreeBSD:#!/usr/local/bin/bashSolaris:#!/usr/bin/bashesh04.sh的用法为esh04.shN#N为正整数示例为 $./esh04.sh513.设计一个程序,要求从命令行参数传入一个作为用户名的参数。然后实现(1)判断命令参数是否是一个系统中的用户。若不是给出提示信息后错误退出。(2)若是,判断用户是否已经登录系统,并就是否登录给出输出信息后正常返回。解:可参见§9.4.2.2、§9.3、§3.6.2及§9.4。设脚本程序名为esh05.sh,清单如图e09-1所示。#!/bin/shgrep-q$1<#include"headfile.h"2种使用方式。两者的区别是:前者搜索头文件的默认位置/usr/include/;而后者则先从当前目录搜索头文件,若当前目录中不存在,则再搜索标准位置;若在当前目录搜索到了,则不再搜索标准位置。若头文件没有存放在标准位置,在编译时需要使用-Idir选项,告诉编译器让其将目录dir也包括在头文件搜索范围之内。(2)库文件库文件是一些预先编译好的函数的集合,或者简单地说,库文件就函数仓库。 库有静态链接库和动态链接(共享)库两种。静态链接库简称为静态库,文件名形式为*.a,动态链接库也叫共享库,文件格式为*.so。库文件命名必须遵守一定规则,库文件名永远以lib开头,后紧跟库名,扩展名为.a或.so,比如标准C库文件名为libc.a或libc.so,标准C++库名为libstdc++.a或libstdc++.so。为了区分版本,在库文件名的末尾还可以追加一个版本号,此时的文件名形式为libname.so.N,N为版本号。库文件不是可独立执行程序,不能直接执行,但可供其它程序使用。在Unix/Linux系统中,库文件的标准位置是/lib或/usr/lib或/usr/local/lib等,若库文件没有被存放在标准位置,则在编译或链接时需要使用-Ldir告诉编译或链接器,让其在链接时将目录dir也包括在库文件搜索范围之内。当使用的不是标准库时,在编译或链接时,需要使用-lname告诉编译器或链接器所需使用的库。4.试分别为§10.3和§10.4的静态库和共享库编写makefile。解:可参见§10.3、§10.4和§10.5。对于1个多模块工程的管理,尤其是makefile的编写,有多种方法。如图e10-1和e10-2所示可分别作为§10.3和§10.4的静态库和共享库makefile实现的一种,可供参考。(1)§10.3静态库构造的makefile在§10.3.1中的多模块工程由模块file1.c、file2.c和file3.c构成,其中的file3.c包含有主函数,不能包含在库中,因此静态库可由file1和file2构成。设由file1.c和file2.c构造静态库的makefile为est.makefile,清单如图e10-1所示。myl=libmyl.a#定义总目标变量all:f#all目标(依赖f)f:file1.ofile2.o#f目标:根据依赖的成员$<(file1.ofile2.o)生成静态库$(myl):libmyl.aarcrv$(myl)$^#等同于arcrvlibmyl.afile1.ofile2.o*.o:*.c#定义由源代码生成目标文件的规则gcc-c$<#编译源代码生成对应的目标文件clean:#cleanallmyobjectfilef?.orm-ffile1.ofile2.o$(bmyl)图e10-1est.makefile清单(2)共享库的构造由§10.3.1中的多模块工程的模块file1.c、file2.c构造共享库的makefile文件为esh.makefile,清单如图e10-2所示。CC=gcc#指定C语言编译器SRC=file1.cfile2.c#源程序文件OBJ=$(SRC:.c=.o)#目标文件dst=libmyl.so#要生成的共享库文件all:$(dst)#整个工程伪目标$(dst):$(OBJ)#最后产品生成规则,等同于:libmyl.so:file1.ofile2.o$(CC)-shared-o$@$(OBJ)#gcc–shared–olibmyl.sofile1.ofile2.o%.o:%.c#目标文件生成规则$(CC)-fPIC-c$<#gcc-fPIC-c*.cclean:#删除目标文件和成品共享库文件rm$(OBJ)$(dst)>/dev/null2>&1install:all#安装:先编译整个工程,再将libmyl.so复制到/usr/libcp$(dst)/usr/libuninstall:#删除/usr/lib/libmyl.sorm/usr/lib/$(dst)>/dev/null2>&1图e10-2esh.makefile清单5.如何生成管理静态库?试为自己设计工程让其包含多个模块,并用其中的通用部分构造自己的静态库。编写构造静态库的工程控制文件,并用它生成静态库。解:可参见§10.3。静态库管理工具为ar和ranlib,另外还可以通过nm查询静态库或目标文件中的符号或函数表。生成静态库过程大致是先编译源代码生成目标文件,然后再根据目标文件通过ar的cr参数将目标模块添加到指定的库文件中。根据源代码文件生成静态的makefile示例可参见习题4。6.如何生成管理动态库?试为自己设计工程让其包含多个模块,并用其中的通用部分构造一个的动态库。编写构造动态库的工程控制文件makefile,并用它生成动态库。 解:可参见§10.4。共享库的构造需要使用gcc/cc命令,结合-shared选项对用于生成共享库的源代码或目标代码进行链接,必要时还需要配合-fpic或-fPIC选项。还可能使用ldconfig和ldd进行管理和查询。根据源代码或目标代码生成共享库的makefile示例可参见习题4。7.试就习题5或6中的工程使用gdb进行调试。解:可参见§10.6。本题可重新描述为,结合习题5或6中的工程,分别使用如图10-4所示的file3.c和如图10-5所示的file3dl.c生成可执行程序,然后对它们进行调试。需要注意的是,编译时要使用-g参数。必要时,可使用命令#debuginfo-installglic.i686安装glib的调试信息。调试过程从略。第11章文件部分系统调用与标准I/O1.与文件操作相关的系统调用的返回值是怎么样的?当系统调用出错时,errno会被设置,如何使用系统调用的返回值和errno检查错误原因?答:可参见§10.1.5和§11.1。与文件操作相关的系统调用总是在成功时返回一个大于或等于0的整数,而在失败时返回-1。当系统调用返回-1时,说明系统调用出现了问题,此时错误码errno被设置,可通过errno找到出错的原因。在Unix/Linux系统中,对于以上出错的处理是通过errno检测的。errno定义在头文件中,程序中若要检测errno,则必须包含此头文件。用于处理errno及相应错误信息的函数有perror()和strerror()等。perror()用于根据当前的错误号errno显示出当前的错误信息;strerror()用于显示指定错误号errno的错误描述信息。errno的值只有在出现错误的时候才有意义,或者说只有在知道确实出现什么错误时才能使用errno。在系统调用和各种函数实现中,没有哪个函数或操作会对errno清0,也就是说,在一个程序中一但发生错误并设置errno之后,在下次出现错误之前errno值一直保持不变,不论在发生下次之前的任何时候测试errno,都将得到相同的“错误”。对于不设置errno的函数或操作,就不能通过errno来检测错误,自己定义的函数要自行定义或处理错误。但不论如何,都要捕获所有可能错误。2.何为文件指针,应该如何移动文件指针?答:可参见§11.1.4和§11.2.8。每个打开文件都有一个与其相关联的“当前位移量”,即文件位置指针,它是指从文件开始处计算的字节数。按系统默认,当打开一个文件时,除指定O_APPEND时文件指针指向文件末尾外,其它情况总是指向文件的开始处,即文件位移量被设置为0。对文件的读、写操作都从当前文件位置处开始,并使指针增加所读或写的字节数。文件位置指针可以通过系统调用lseek()来移动。可通过lseek()显式地定位一个打开文件的位置指针,其用法为off_tlseek(intfd,off_toffset,intwhence);其中的fd为已经打开的文件描述符;offset为欲移动的距离;whence为移动的出发点:SEEK_SET,从文件开始移动;SEEK_CUR,从当前位置移动;SEEK_END,从文件末尾移动。offset可正可负:正时为前向移;负时为后向移;为0时用于确定文件指针的当前位置。若文件指针已经指向文件的结尾,移动0长度可用于确定文件的长度;再向前移动将在文件中产生一个“移动长度”(offset)的空洞。在标准I/O中,用于流定位的函数有fseek,ftell,fgetpos,fsetpos和rewind,用法及参数等请参考§11.2.8。3.何为文件内的“空洞”,是如何形成的?试在某已经存在的文本文件中形成一个1K的空洞,并在所用系统中观查文件长度和内容的变化。解:可参见§11.1.4。文件中的“空洞”,是由于文件指针移动超过了文件最大长度后形成的从移动前文件的真实长度到移动后的现有长度之间的“部分”。这个空洞部分,在不同的系统中处理方法不同。有的是暂不分配磁盘空间,待以后用到时再分配,有的是分配磁盘空间,但先填上无用的垃圾数,待以后用到时再覆盖。 如图e11-1所示的程序esys01.c可以实现在指定的文件中形成一个长度为1K的空洞。#include#include#include#include//#include#includemain(intargc,char*argv[]){intfd;off_tlen;charbuff[10];if(argc<2){//处理命令行参数。若无则报错fprintf(stderr,"Usagen%sFILEn%s_wiilmake_a_holeinFILEn",argv[0],argv[0]);exit(1);}if((fd=open(argv[1],O_RDWR))==-1){//打开文件fprintf(stderr,"%s:%sn",argv[1],strerror(errno));exit(errno);}if((len=lseek(fd,1024L,SEEK_END))==-1){//移动文件指针,形成1024字节长的空洞fprintf(stderr,"%s:%sn",argv[1],strerror(errno));exit(errno);}else{sprintf(buff,"%sn","End");//准备写入字符串write(fd,buff,strlen(buff));//写入len=lseek(fd,0L,SEEK_END);//确定文件长度printf("Thelengthoffile%sis%dn",argv[1],len);//显示文件长度close(fd);exit(0);//退出}}图e11-1esyscall01.c程序清单说明:esys01要在已经存在的文件中,形成一个大小为1K的“空洞”,因此原文件要事先存在,且不能是有用的或专用于试验的文件。为此,首先创建一个临时文件x:ls/>x#或为其它名字lsx#查看x的长度catx#查看x的内容od-tx1x#按16进制方式显示文件的内容(以备与以后作比较)vix#通过vi查看x的内容(不需要操作,只需要:q退出)./esyscall01x#在x中形成空洞1K的空洞,并显示文件新的长度空洞产生之后,可再用以上办法查看文件长度及内容。4.文件是如何共享的?有几种文件共享方式?这些共享方式是如何形成的?在共享文件被打开后,进程打开文件表、系统打开文件和内存I节点表的关系是怎么样的?解:请参见§11.1.5。这里的共享指的是进程对文件的共享操作,分为进程内和进程间共享方式。在两种共享方式下,文件被打开后,进程打开文件表、系统打开文件和内存I节点表的关系分别如图11-5和图11-6所示。5.何为文件的互斥访问?何为文件锁?对文件有几种锁控办法,怎么实现?解:可参见§11.1.7。文件的互斥访问,这表现为一个文件或其中的一部分不允许两个进程同时访问。若一个文件的某一部分正被某一进程以写方式访问,另一进程也要访问同一个地方或与本访问部分有重叠,则它必须等待。文件的互斥可以表示两类:一是一个进程采用不允许其它进程访问或在本进程允可的情况下才能访问的方式,这表现为文件的互斥,在锁上表现为文件锁;另一个是一个文件大家都可以访问,但文件中的某一部分在一个进程使用期间不允许其它进程访问,这表现为部分互斥,在锁上表示为记录锁。可以通过flock()实现文件的互斥和共享锁,通过lockf()或fcntl()实现记录锁,具体细节请参考flock()、lockf()和 fcntl()在线手册。6.何为原子操作?试根据向文件追加内容情况说明如何才能实现原子操作?解:可参见§11.1.9。原子操作是指不能被打断的操作,要么彻底做完,要么一点也不能做。在多进程(线程)的操作系统中,一个不能被其它进程(线程)打断的操作称为原子操作。文件的原子操作也是这样,比如当一个进程正在修改文件中的某个记录时,其它要操作本记录的进程要等待本进程操作完毕后才能再进行,修改过程不许被打断。一般来说,一次系统调用所做的工作是一个原子操作,也就是在这个操作过程中不会被其它“过程”介入或干扰,这是因为,在Unix系统中,系统调用是在内核中执行的,且在此次系统调用返回前,其它进程是不能再通过系统调用进入内核的。一个原子操作可以由多个步骤组成,但在它的执行过程中,要么一次执行完毕,要么一步也不执行。当向一个文件追加内容时,可有两种方法。一是先以读写方式(O_RDWR)或写方式(O_WRONLY)打开文件,再将文件指针移向文件的尾部,最后再写入追加的内容;另一种是按追加(O_APPEND)方式打开,然后再向文件中写信息。前者是非原子性的,因为在在移动文件指针操作和写文件操作之间,可被别的进程插入其它操作。后者是原子操作,可以保证在对文件写入之前,文件指针总是定位到文件尾部。7.何为标准I/O的流和文件对象,它与系统调用级的文件对象有什么关系,试通过stdio.h文件的内容,观查FILE结构的的构造。解:可参见§11.2.1。标准I/O的操作总是围绕流(stream)进行的。系统为每个进程预定义了三个流:标准输入、标准输出和标准出错,在程序启动或进程创建时自动打开并为进程使用。这三个标准I/O流可通过预定义流指针stdin、stdout和stderr加以引用。当用标准I/O打开或创建一个文件时,函数fopen返回一个指向FILE对象的指针,该对象通常是一个结构,它包含了I/O库为管理该流所需要的所有信息:用于实际I/O的文件描述符、指向流缓存的指针、缓存的长度、当前在缓存中的字符数、指针和出错标志等等。在UNIX系统中,标准I/O是建立在系统调用基础上的,因此,在FILE结构中一定要包含系统调用级的整型文件描述符信息。我们可以通过:FILE*fdopen(intfd,constchar*type);intfileno(FILE*stream);取得两者间联系。必要时可以通过标准I/O处理低级I/O,相反的,也可以用低级I/O处理标准I/O。标准I/O的实现在不同的系统间差别较大,可通过stdio.h及其子包含文件内容观查FILE结构的构造。在SCOOpenServerRelease5中,FILE结构定义如下:typedefstruct_FILE_{/*mustbebinary-compatiblewitholdversions*/int__cnt;/*numberofavailablecharactersinbuffer*/unsignedchar*__ptr;/*nextcharacterfrom/tohereinbuffer*/unsignedchar*__base;/*thebuffer(notreally)*/unsignedchar__flag;/*thestateofthestream*/unsignedchar__file;/*filedescriptor*/unsignedchar__buf[2];/*microbufferasafall-back*/}FILE;其中成员__file就是系统调用级的文件描述符。在Linux-2.4.20中,FILE结构定义如下:struct_IO_FILE{int_flags;/*High-orderwordis_IO_MAGIC;restisflags.*/…………/*ThefollowingpointerscorrespondtotheC++streambufprotocol.*/char*_IO_read_ptr;/*Currentreadpointer*/…………int_fileno;…………}FILE;其中,成员_fileno就是系统调用级的文件描述符。在FreeBSD9下的FILE结构定义为typedefstruct__sFILE{unsignedchar*_p;/*(*)currentpositionin(some)buffer*/ int_r;/*(*)readspaceleftforgetc()*/int_w;/*(*)writespaceleftforputc()*/short_flags;/*(*)flags,below;thisFILEisfreeif0*/short_file;/*(*)fileno,ifUnixdescriptor,else-1*/struct__sbuf_bf;/*(*)thebuffer(atleast1byte,if!NULL)*/int_lbfsize;/*(*)0or-_bf._size,forinlineputc*/………}FILE;其中,成员_file就是系统调用级的文件描述符。8.标准I/O、系统调用和shell的三个标准流及描述符分别是什么,应该如何使用它们?解:可参见§2.1.8和§11.2.1。在UNIX/Linux系统中,三个标准流是在进程创建时自动打开,用户可以直接使用。标准I/O、系统调用和shell的三个标准流及描述符的情况如下表所示。标准流系统调用shell文件号标准I/O描述符所用设备标准输入0(STDIN_FILENO)0stdin键盘标准输出1(STDOUT_FILENO)1stdout终端屏幕标准错误2(STDERR_FILENO)2stderr终端屏幕在标准I/O,可以通过scanf()、getchar()等直接使用标准输入;还可以通过fscanf(stdin,…)、fgetc(stdin)等指定标准输入。可以通过printf()、putchar()等直接使用标准输出;还可以通过fprintf(stdout,…)、fputc(stdout,…)等指定标准输出。可以通过perror()、strerror()直接使用标准错误;还可以fprintf(stderr,…)、fputc(stderr,…)等指定标准错误。在低级I/O,可以直接使用read(0,…)、write(1,…)和write(2,…)等直接使用标准I/O。在shell中,可以通过read、echo直接使用标准I/O。还可轻松实现标准流间重定向。9.标准I/O是使用缓冲的,有哪些缓冲类型?缓冲的特征是什么?是否可以修改缓冲类型?若能如何修改?解:可参见§11.2.3。1)标准I/O提供了三种类型的缓存:(1)全缓存。当标准I/O缓存被填满后才进行实际I/O操作。(2)行缓存。当在输入和输出中遇到换行符(’n’或0x0a)时,才执行I/O操作。(3)不缓存。标准错误流stderr通常采用这种方式,以便及早发现“错误”,而不管是否含有换行符。缓冲的存在使得先执行的输出不一定先输出到指定位置,因此可能会造成屏幕的“混乱”现象。2)缓冲的特征ANSIC要求缓存具有以下特征:(1)当且仅当标准I/O并不涉及交互作用设备时,才使用全缓冲。(2)标准错误决不使用全缓冲。UNIX缓冲的默认特征是:(1)标准错误不带缓存。(2)如若是涉及终端设备的其他流,则它们是行缓存的,否则是全缓存的。3)缓存类型更改对于一个给定的流,可以使用缓冲区管理函数setbuf()、setvbuf()、setlinebuf()和setbuffer()等修改缓冲区的类型、位置和大小等。10.在标准I/O中如何定位流的位置指针?所有的流指针均可进行重定位吗?解:可参见§11.2.8。在标准I/O可以使用fseek()、ftell()、fgetpos()、fsetpos()和rewind()等实现流指针定位。不是所有流都可以定位的,比如当对应的流为FIFO(无名管道)、PIPE(命名管道)和socket时,就不能定位。若要定位,将产生ESPIPE(Illegalseek)的错误。11.如何将文件作为标准I/O,比如,如何实现将一个文件作为标准输入,试编写程序实现说明之。解:可参见§11.3.2。在shell或低级I/O里,标准输入、标准输出和标准错误的描述符分别为0、1和2。对于一个在低级I/O已经打开的文件,若使其作为标准I/O来使用,只需要将他们的文件号复制到标准输入或标准输出就可以了。用于实现文件描述符复制的系统调用有dup、dup2和fcntl。如图e11-2所示的程序esyscall02.c可以实现将指定文件用于标准输入,然后将其内容显示到标准输出上。#include#include #include#include#include#includemain(intargc,char*argv[]){//0变量定义intfdi;//输入文件描述符charbuf[4096];//缓冲区//1处理命令行参数if(argc<2){fprintf(stderr,"%sinfilen",argv[0]);exit(1);}//2打开文件(命令行参数1)if((fdi=open(argv[1],O_RDONLY))==-1){//打开错误时返回perror(argv[1]);exit(errno);}//3将fdi定向到标准输入if(dup2(fdi,0)==-1){perror("dup2()");exit(errno);}while(gets(buf)!=NULL)printf("%sn",buf);exit(0);}图e11-2esyscall02.c程序清单esyscall02的用法为esyscall02infileesys02首先打开输入文件,然后得到dup2()将重定向到标准输入(0),之后的就通过标准I/O读取文件了。为了表明使用的是标准I/O,在程序中使用了不安全的gets()从标准输入读取文件信息,每读入一行就将它显示到标准输出上。由于程序中对输入文件的读入使用的标准I/O,做输入文件最好是文本文件,否则标准输入对应的屏幕将被打乱。esyscall02的一个潜在的功能是文件复制,此功能需要借助输出重定向。比如$./esyscall02file1>file2将把文件file1复制到file2。这里需要说明的是,当输入文件是非文本文件时,此功能不能正确履行职责。这主要是标准I/O不能正确处理非文本信息造成的,理由非常简单,至少有一点是肯定的,标准I/O是将’’作为字符串结束符,因此所有’’将会被丢弃。基于此,可以考虑其它的非文本或控制字符。用户可以执行命令$.esyscall02esyscall02>x然后使用命令“ls-lesyscall02x”来观察它们的大小。12.请使用标准I/O重新实现syscall05.c,要求其数据结构中的成员尽量使用数值型的。解:可参见§11.2.7和syscall05.c。syscall05仅简单地实现了记录型文件的修改。由于在syscall05.c中,记录中的成员都是字符或字符串型的,构造一个这样的文件只能使用编辑器(比如vi等)编辑就可以了。但是,若记录成员是数值型的,使用此方法就不行了,于是需要再对syscall05.c的功能进行扩充,增加了记录的输入、指定记录显示、全部记录显示功能。设程序名esyscall03.c,清单如图e11-3所示。图e11-3esyscall03.c程序清单esyscall03的用法为esyscall03[-iI]|[-aA]|[-d|DNo.]|[-m|MNo.]-i/I用于以追加方式输入记录;-a/A:用于显示全部记录;-d|DNo.:用于显示由No.指定的第No.号记录;-m|MNo:用于修改由No指定的第No号记录。当不指定选项时,显示用法信息。esyscall03使用示例如下:$./esyscall03-I#以追加方式输入记录$./esyscall03-d3#显示第3号记录$./esyscall03-a#显示所有记录$./esyscall03-m2#修改第2条记录13.何为临时文件?如何在编程时使用临时文件?解:可参见§8.4.8.7和§11.4。§11.4中如图11-9所示的tmpfile01.c可以作为本题的答案。tmpfile01.c以多种方式产生和使用了临时文件,这里不再给出其它示例.临时文件使用完毕后,应该程序结束前主动删除它。这方面的示例可参考第12章的习题4和第13章的习题5。 第12章UNIX系统进程环境1.在C语言程序内是通过什么途径可获得并使用命令行参数的?如何分解程序的命令选项与参数?解:可参见§12.1。通过main函数的第2个字符串指针数组参数。在编程时,可使用getopt()分解命令行选项和参数,细节参见§12.1.2或getopt()在线文档。命令行选项和参数分解的示例可参考如图12-3所示的env03.c源程序及后续的其它程序,比如习题2。2.试编写程序实现文件复制。要求能够识别选项-f或-F,当使用它们时采用强制覆盖方式,否则为交互确认方式。解:可参见§12.1.2、§12.1.3和§11.1。设程序名为eenv01.c,清单如图e12-1所示。图e12-1eenv01.c程序清单eenv01的用法为eenv01[-fF]srcdst#eenv01[-fF]源文件名目标文件名执行示例如下$./eenv01/etc/passwd/tmp/x3.在C语言程序内是通过什么途径可获得、使用和设置环境变量的?试编写程序并通过执行结果说明,父进程的环境变量可以传递给子进程,但子进程设置的环境变量不能传递给父进程。解:可参见§12.2。可通过main函数的第3个字符串指针数组参数,或通过全局外部变量environ来访问环境变量。编程时,可通过getenv(envname)获得envname环境变量的值;可通过putenv(string)或setenv(name,value,overwrite)增加或修改环境变量及其值;也可通过unsetenv(name)取消或删除环境变量name。参考如图12-6所示的程序env06.c,可以观察环境变量从父进程向子进程传递的情况(程序清单参见教材)。现在修改env06.c,在倒数第7行前添加如下1行内容:charcmd[100];sprintf(cmd,"%s%s",argv[0],p);system(cmd);设修改后的程序名为eenv02.c,清单如图e12-2所示。图e12-2eenv02.c程序清单程序可按以下方式执行:$./eenv02-sMYVAR=MY_NEW_VAR程序的输出为NewEnvironmentVar:MYVAR=MY_NEW_VAR#父进程输出MYVAR=MY_NEW_VAR#子进程输出由子进程的输出可以看出,子进程已经看到了使用的环境变量MYVAR,其值为MY_NEW_VAR。这与命令行参数的设置是一致的。在eenv02结束后,如果用户使用$echo$MYVAR查看MYVAR的值,将没有结果,说明子进程设置的环境变量不能传递给父进程。4.试说明exit()与_exit()的区别。试编写程序,在其中使用临时文件,并使用atexit()让程序在退出前提醒用户删除临时文件。解:可参见§12.3和§11.4。_exit和exit都用于程序的正常终止,_exit是一系统调用,而exit是一个库函数。当调用_exit时直接进入内核,进行程序正常终止的工作,主要工作是进程的撤销和资源的回收;当调用exit时,则先在用户空间作一些准备工作,比如关闭已经打开的文件等,然后再转入_exit执行终止工作。由此可见,两者是有区别的,用户最好不直接调用_exit来使程序终止。程序的设计可参考如图11-9所示临时文件使用示例程序tmpfile01.c和如图12-7所示的env07.c程序。将两者结合起来就可设计出所要求的程序,但由于没有具体的任务,这里只能给出一个框架。设程序名为eenv03.c,清单如图所示。图e12-3eenv03.c程序清单 eenv03的执行将产生形似/tmp/eenv03.??????的临时文件,在程序结束将提醒用户删除此临时文件,但程序并未删除它,用户可手工删除。或参考第13章习题5,修改exit1(),在程序结束时自动删除该临时文件。5.如何获取系统、内核和主机名信息?请自己动手设计shell命令uname和hostname。解:可参见§6.6.2.1、§8.4.4、§12.4.1和§12.4.2。。在shell级我们有命令hostname和uname用于主机和系统信息显示或设置,在编程时可使用uname()函数获取系统相关信息;还可通过gethostname()和sethostname()取得或设置主机名。(1)uname命令的实现实现uname命令的功能需要使用uname()系统调用,uname()需要一个structutsname结构的参数,其中的成员包含与系统相关的信息。设程序名为eenv04.c,清单如图e12-4所示。图e12-4eenv04.c程序清单说明:eenv04.c在不同系统下表现有差异。若要使其功能完全与所用系统相同,还需针对具体系统进行完善。(1)hostname命令的实现hostname命令本身在不同的系统中的表现就有较大差异,这里仅实现主机名的查询与设置功能,其它功能省略。设为eenv05.c,清单如图e12-5所示。图e12-5eenv05.c程序清单eenv05的用法为eenv05[-sdf][newhostname]应用示例如下$./eenv05–s#短格式显示$./eenv05-d#显示域名$./eenv05[-f]#长格式显示#./eenv05newhostname#设置新主机名为newhostname第13章文件属性与目录编程1.编写程序,从命令行获取一个文件名,实现stat命令的不带任何选项的功能。解:可参见§2.2.2.8和§13.1.1。可参考如图13-1所示的file01.c,若经过修改的file01.c重命名为efile01.c,程序清单如图e13-1所示。图e13-1efile01.c程序清单程序efile01.c的输出是按FedoraLinux和Solaris的默认格式输出的,与FreeBSD的默认格式不同。efile01.c的输出格式控制比file01.c的输出控制格式更加完善了,这一点可以从printf()的输出表中可以看出,因为加进了类型转换。程序的编译方法为:$gcc-oefile01efile01#FedoraLinux&FreeBSD$gcc-DSOLARIS-oefile01efile01#Solaris程序的用法为$./efile01file2.编写程序,实现chmod命令的不带任何选项的功能,即通过符号或八进制数字方式为文件设置或修改权限,进而结合表11-2实现chmod的suid、sgid和sticky位操作。解:可参见§4.4.2、§13.1.2.3和§13.1.2.4。设程序名为efile02.c,清单如图e13-2所示。图e13-2efile02.c程序清单efile02.c可以实现对文件或目录的简单权限操作且支持多文件操作,且在OS帮助下,支持文件名中的统配符。所支持的操作表达式为:[ugo]<+-=>[rwxst],[ugo]<+-=>[rwxst],… 但不支持go=u-w等形式。使用示例如下:$./efile02755xyz*$./efile03u=rwx,g+w,o=xyz*$./efile03ug+rwx,o-wxyz*3.编写程序实现chownown:groupfile和chgrpgrpfile功能。解:可参见§4.4.3、§4.4.4和§13.4。可参见如图13-5所示的file05.c。若要在所使用OS上实现chown或chgrp功能的话,可以参照所用系统的chown或chgrp命令。需要说明的是,chown或chgrp在不同的系统中的用法是不同的,就基本用法:chownowner:groupfiles而言,owner和group即可是用户或组名,也可是UID或GID,当然使用名字可能会更直观一些。当使用名字时,在使用chown()等系统调用前,需要首先使用structpasswd*getpwnam(constchar*uname);structpasswd*getpwuid(uid_tuid);或structgroup*getgrnam(constchar*gname);structgroup*getgrgid(gid_tgid);得到UID或GID信息。具体的编程实现可参见§12.5中的图12-10所示的env10.c。最后,结合env10.c和file05.c就可设计出本题所要求的程序。设程序名为efile04.c,清单如图e13-3所示。图e13-3efile03.c程序清单程序efile03.c同时支持全数字UID或字符串型的用户名或组名。若字符串型的用户名或组名,则必须通过getpwnam(uname)或getgrnam(gname)得到数值型uid或gid。就是数字型的UID或GID也必须转换为数值型的uid或gid。函数get_uid()和get_gid()的设计思路来源于Fedora的chown()系统调用在线手册中的示例。efile03的用法为$./efile03owner:groupfiles$./efile03ownerfiles$./efile03:groupfiles4.编写程序,实现ls、ls–l、ls–i和ls–li功能。解:可参见§12.1和§13.8。参考如图13-7所示的file07.c,再结合如图12-3所示的命令行参数处理程序env03.c,可设计出如图e13-4所示程序efile04.c,清单如下。图e13-4efile04.c程序清单程序接收选项-i和-l,用法为efile04[-li]file运行示例如下$efile04/tmp$efile04-i/tmp$efile04-l/tmp$efile04-li/tmp5.继续第12章习题4,实现在程序结束时删除临时文件。解:可参见§12.3、§11.4和§13.3。。修改第12章习题4程序eenv03.c的结束处理函数exit1()如下://定义退出处理函数voidexit1(){//退出时,删除自定义临时文件filenameif(unlink(filename)==-1)perror("unlink");}6.编写程序,实现单个文件的更名或移动。解:可参见§2.2.2.5和§13.7。编程时,文件的更名与移动可以使用rename()。设程序名为efile05.c,清单如图e13-5所示。 #include#include#include#includemain(intargc,char*argv[]){if(argc<3){fprintf(stderr,"%soldnamenewnamen",argv[0]);exit(100);}if(rename(argv[1],argv[2])==-1){fprintf(stderr,"%s%s%s:%sn",argv[0],argv[1],argv[2],strerror(errno));exit(errno);}else{printf("%s%s%s:OKn",argv[0],argv[1],argv[2]);exit(0);}}图e13-5efile05.c程序清单程序可以实现在同一个文件系统的移动与更名,但不能跨越文件系统。使用示例如下$./efile05xy#将x本地移动/更名为y$./efile05x/y#将x移动到/并更名为y$./efile05../x/y#将../x移动到/并更名为y若要设计出能够跨越文件系统的移动,则需要更改此程序,思路是:首先将原文件复制到目标文件,在成功后,再删除原文件。7.编写程序,实现文件的ln或ln-s功能,并通过-u或-r实现文件删除功能。解:可参见§4.6.3和可参见§12.3。设程序名为efile06.c,清单如图e13-6所示。图e13-6efile06.c程序清单设生成的可执行程序为efile06,程序的用法为efile06[-s]srcdst#创建链接:-s用于创建符号链接efile06[-ru]file…#删除文件执行方法为:$./efile06xxln#为x生成硬链接xln$./efile06-sxxLN#为x生成符号链接xLN$./efile06-rxln#删除硬链接xln$./efile06-uxLN#删除符号链接xLN8.设计一套自己的(类似ls、pwd、cd、mkdir、rmdir等功能的)目录管理命令解:可参见§13.8及本章的习题4。§13.8.6中的file07.c可作为本题的答案。第14章进程关系与进程控制1.试比较fork()、exec()和system(),说明它们之间的相同或不同。解:可参见§14.1.1~4。fork()和vfork()的作用都是创建子进程。如果fork()调用成功的话,则创建一个与自己几乎完全相同的子进程,子进程是父进程的一个拷贝或复制品,差别仅在于PID和资源的使用时间等方面,换句话说,子进程可从父进程继承几乎全部资源。成功时,fork()调用一次返回两次:一次是父进程中返回,返回值是刚创建子进程的PID;一次是在子进程中,返回值为0。进程可以根据这两个返回值判断是运行在子进程中还是父进程中,然后再去执行不同的分支,完成不同的任务。exec系列函数用于执行其它程序。如果exec()调用成功,则被执行程序的进程空间将覆盖调用者的进程空间,也就是说,如果exec调用成功,就不再返回,被调用者将替换调用者,但父进程可通过wait()或waitpid()得到子进程的返回状态或退出码。但是,若exec()返回了,则说明调用失败。 system()是在编程中执行一个shell命令的灵活而有效的函数。它在执行一个命令(比如cmd)的过程中,首先调用fork()/vfork()产生子进程,然后再由子进程调用exec()去执行命令cmd,可以理解为fork()与exec()的综合。在调用者使用system()执行其它程序,调用者将等待被调用者执行结束,并得到其返回值。可能返回值为:(1)成功时返回被执行者的返回值;(2)如果fork()失败或waitpid()失败则返回-1,且errno被设置;(3)如果exec调用失败,返回127。2.如何更改进程的uid和gid?如何设计一个程序,既使它具有suid或sgid属性,也不能使用户的权力放大?解:可参见§14.1.5、§14.1.6和§5.3.1.1。setuid()用于设置进程的euid,如果具有超级用户权限,则同时设置ruid和Suid,若用户没有超级用户权限,但是uid等于ruid或Suid,则setuid()只将euid设置为uid,而不改变ruid和Suid。对于setgid()也有类似的用法。当编程时,需要改为用户的uid和gid时可以使用它。出于安全程序设计的考虑,如果在程序设计中需要调用或执行一个其它程序时,应尽量不要使用system()函数,因为整个调用过程对于程序设计者来说是不可控的,system()没有给我们留下插手控制的机会。但是,可以分阶段使用fork()和exec()来进行处理,在调用fork()之后、exec()之前可以插入代码将调用者进程的ruid、euid、rgid和egid修改为拥有者的用户ruid、euid、rgid和egid,从而保证真实和有效身份都不发生变化。3.何为僵尸进程?它是如何形成的?如何避免僵尸进程的出现?解:可参见§14.2。僵尸进程是指处于僵尸状态的进程。僵尸状态是进程生命周期的最后一个状态。当进程不论以何种原因结束或终止时,进入该状态。处于该状态的进程已经释放了大部分资源,此时只占有一个PCB表项,并在其中记录了该进程对系统资源的使用及退出码等信息。当一个子进程终止或退出时系统将向其父进程发SIGCHLD信号,如果父进程不处理该信号,子进程将变为“僵尸”进程,且在父进程对SIGCHLD信号处理前一直处于此状态。因此,父进程只需要使用wait()或waitpid()等等待一下该子进程,它就会立即释放所有资源,而彻底地结束。如果一个进程在生命周期内创建很多子进程,而不使用wait()或waitpid()等进行处理,则会形成大量的僵尸进程而占有系统资源,因此,创建有子进程的父进程有义务为自己的子进程处理好善后事宜。4.何为孤儿进程?Unix系统对孤儿进程是如何处理的?解:可参见§14.2.3。孤儿进程是指父进程退出了,而作为子进程仍在执行的进程,或者说,孤儿进程则是父进程先于子进程退出形成的。在UNIX/Linux系统中,除0#进程外,每个进程都要有一个父进程,每进程的PCB结构也都记录有子进程及家族关系信息,进程按父子关系、家族关系形成进程树。如果一个具有子进程的进程“死掉”了,它的子孙进程构成的子树就与整个系统的进程树断开了,它的子进程就成为了孤儿进程。为了保证每个进程都有父进程,维持一个完整的进程树,系统规定由1#进程init来领养所有的孤儿进程。对于由于父进程没有做善后处理的僵尸进程也会在父进程终止时由1#进程领养后,变为init的子进程而正常终止。5.什么是信号、未决信号和信号的生命周期?当一个进程收到信号后就立即处理信号吗?所有信号都可以捕获吗?在一个程序中,对一个可以捕获的信号有哪些处理办法?解:可参见§2.2.4.2、§5.5.2和§14.3.1。信号是向进程发送的软件通知,告知接收者进程有事件发生了。引发信号产生的事件发生时,信号就被生成了。接收者进程对信号采取行动时,信号就被传递了。信号的生命周期为从生成到被传递之间的时间间隔。已经生成但还未被传递的信号称为未决信号。信号自生成到传递可能要经过一段时间,因为只在传递的目的进程占有处理机时,传递才能真正进行。也就是说,当一个进程收到信号后并不一定立即处理它。至于说何时处理收到的信号,要取决进程当时的执行状态:一般来说,进程收到信号后,先将其记录在PCB表中。若进程处在用户态执行状态则立即处理;若处系统态执行状态,则等到从系统状态返回到用户态时再处理。其它情况下,则只能等到进程执行时才能处理。对一个可以捕获的信号可有以下处理办法:(1)执行系统默认动作;(2)忽略信号;(3)捕获信号。对信号的忽略也是一种处理办法,比如对子进程退出信号的处理,若采用系统默认,则终止的子进程将变成“僵尸”,如果采取了明显的忽略机制,子进程将会正常结束。捕获信号是指为信号重新定义一个执行线路(子程序),当信号到来时让其沿新的线路执行。6.试编写一个程序实现nohup的功能。解:可参见§5.4.7和§14.3.2。 由§5.4.7可知,通过nohup启动的进程要对SIGHUP具有免疫力,即要忽略SIGHUP信号。如果启动时,被启动命令的标准输入没有被正确重定向,则将重定向到/dev/null;如果标准输出没有重定向,则将标准输出追加到当前目录的nohup.out文件,如果对此文件没有写权限,输出将被重定向到$HOME/nohup.out,如果对两者均无写权限,则命令不能执行。如果有写权限且nohup不存在,则创建之,在创建时不对同组和其它人赋访问权,若文件已经存在则不改变原文件的存取权限。程序的未重定向的标准错误也将追加到nohup.out文件。由于nohup没有输出,只能通过它的返回值来确定命令的执行情况,其返回值及意义如下:125-nohup自身错误(Solaris);126-命令cmd存在但不能执行;127-命令cmd找不到或nohup自身错误(Linux&FreeBSD);其它-命令cmd的返回值。根据以上描述可设计出具有nohup功能的程序。设程序名为eproc01.c,清单如图e14-1所示。图e14-1eproc01.c程序清单eproc01.c基本上实现的了nohup的功能,但在具体的细节可能与具体的系统中的实现有所不同,若要完全与所用系统一致,还要针对具体系统做进一步的改进。7.试使用SIGUSR1和SIGUSR2,结合alarm()和pause()编写程序,实现父子进程间的同步。要求父进程向子进程发送SIGUSR1,子进程父进程发送SIGUSR2,各自收到信号后,在屏幕上显示能标识自己的信息,做到信息在屏幕上不交差。解:可参见§5.2.2、§14.3。参考§14.3.4中如图14-11所示的proc10.c(清单见教材)。对proc10.c加以修改,可得到所需结果。设计实现程序名为eproc02.c,清单如图e14-2所示。图e14-2eproc02.c程序清单eproc02.c与proc10.c内容基本相同,甚至还更简单,用法也相同。只不过是为避免标准I/O的缓冲,程序中使用了系统调用(write())写屏的办法进行输出。eproc02用法为eproc02[N]#N为显示次数,若不指定默认3次示例如下$./eproc02#缺省3次$./eproc02#指定10次8.设计程序,创建并观察前台、后台进程组。解:可参见§14.4。程序eproc03.c可用于对控制终端、进程组、会话等进行观察,并分析它们之间的关系。eproc03.c清单如图e14-3所示。图e14-3eproc03.c程序清单eproc03.c程序可以使用-ffile、-p、-s和-t参数,-f用于指定一个用于输出的文件名,-p用于输出当前进程的PID、PGID和SID;-s用于将当前进程的PGID设置为自己的PID后,然后执行-p的功能;-t用于显示前台进程组的PGID。以下我们将通过此程序的运行来观察进程组、会话和前台进程的变化情况。eproc03.c的编译方法为:$gcceproc03.c-oeproc03#Fedora&Solaris$gcc-DBSDeproc03.c-oeproc03#FreeBSD为了便于观察,特设计一个shell脚本程序eproc03.sh,清单如图e14-4所示。#!/bin/shf="/tmp/eproc03.out"#定义临时文件echo$$>$f#写PID入临时文件./eproc03-f$f-t#获得前台进程的PGID./eproc03-f$f-p|./eproc03-f$f-p|./eproc03-f$f-p&#显示PID、PGID和SID./eproc03-f$f-s|./eproc03-f$f-s|./eproc03-f$f-s&#设置PGID显示./eproc03-f$f-pt|./eproc03-f$f-st|./eproc03-f$f-pt#只有一个进程设置PGID图e14-4eproc03.sh程序清单在为其增加执行权后,可让其分别在当前shell和子shell执行,以观察PID、PGID和SID的情况。首先在当前shell下运行:#../eproc03.sh其输出为:2046#当前shell的PID(每次都会所不同,其它的PID也如此)TTY=/dev/tty17806#前台进程组的PGID.第3行的输出./eproc03PID=17807PGID=17807SID=2046#第4行的输出./eproc03PID=17808PGID=17807SID=2046./eproc03PID=17809PGID=17807SID=2046 ./eproc03PID=17810PGID=17810SID=2046#第5行的输出./eproc03PID=17811PGID=17811SID=2046./eproc03PID=17812PGID=17812SID=2046TTY=/dev/tty17813#前台进程组的PGID.第6行的输出./eproc03PID=17813PGID=17813SID=2046TTY=/dev/tty17813./eproc03PID=17814PGID=17814SID=2046TTY=/dev/tty17813./eproc03PID=17815PGID=17813SID=2046#进程名PIDGIDSID由输出可以看到,它们的SID是相同的,都与登录shell的PID相同(2046)。观察输出还可以看出,第一个前台进程PGID=17807,在脚本的第4行执行的3个进程的PGID是相同的(17807),这是因为引起管道操作进程的PID为17807的进程创建了PGID为17807的进程组,其它两进程都继承了该PGID;而第5行脚本的3个进程由于使用-s参数重新设置了PGID,它们的PGID是各不相同的,但它们的PGID均与自己的PID相同。在脚本的第6行,PID=17813的引起了管道操作,它创建了PGID为17813的进行组,处于中间的PID=17814的进程的PGID也本该是17813,但创建了新的进程组,PGID为它的PID17814,PID为17815的进程没有没有创建新进程组,故它的PGID仍为17813。我们还可以看到,随着脚本程序的执行,先后出现了两个前台进程组。它们与控制终端相关的PGID分别为17806和17813。对于本示例,我们可以看出,SID为2046的会话包括有一个登录shell和7个进程组,会话中的进程和组如图e14-5所示。前台进程组(17806)17806前台进程组(17813)178131781417815后台进程组(17807)1780711780817809登录shell(2046)后台进程组(17810)17810后台进程组(17811)17811后台进程组(17812)17812前台进程组(17814)17814会话(2046)图e14-5会话中的进程组与进程在子shell下运行时,首先使用echo$$命令查看当前shell的PID,结果仍为2046,接着在子shell下执行该shell脚本#./pdroc03.sh输出为:17817#当前shell的PID(注意:此为子shell的PID,每次都会有不同)TTY=/dev/tty17817./eproc03PID=17825PGID=17817SID=2046./eproc03PID=17826PGID=17817SID=2046./eproc03PID=17827PGID=17817SID=2046./eproc03PID=17828PGID=17828SID=2046./eproc03PID=17829PGID=17829SID=2046TTY=/dev/tty17817./eproc03PID=17830PGID=17830SESSION=2046TTY=/dev/tty17817./eproc03PID=17832PGID=17832SESSION=2046TTY=/dev/tty17817./eproc03PID=17833PGID=17817SESSION=2046#进程名PIDPGIDSID观察此输出可以发现,第一个后台进程组的PGID为前台进程组的PGID,为子shell的PID,其它情况基本相同。说明,在不同的系统下可能有不同结果,请根据具体情况作具体分析。9.编写一个bshell程序,作为chroot命令和chroot()的准备。要求在所用系统上能够实现为chrootnewrootcmd准备的完全自动化。解:可参见§3.6.8和§14.5。按照§3.6.8的“准备”部分的要求,可以设计出如图e14-6所示的在FedoraLinux、FreeBSD和Solaris下均可使用的为chroot命令做准备bshell脚本程序emkchroot.sh。考虑到系统间的兼容性,在设计过程要放弃一些bshell在各系统中比较个性的东西,使用最基本、通用的部分。 图e14-6emkchroot.sh脚本程序清单mkchroot.sh的用法为:mkchroot.shnewrootcmd1cmd2…其中,newroot为chroot要切换的根目录;cmd1cmd2…为将要以newroot为根目录执行的命令,也就是说,本mkchroot.sh就是要为它们在新的根目录newroot下的执行做准备的。使用示例如下:#./mkchroot.shnewrootbashlsdate第15章进程间通讯1.管道有几种?请说明它们的用处。如何在shell命令或C语言中创建命名管道?解:可参见§2.1.3、§2.1.9和§15.1.1。管道又分为匿名管道和命名管道。匿名管道也被称为无名管道,用于为建立管道的进程及其子孙提供一条以比特流方式传送消息的通信通道。该管道在逻辑上被看作管道文件,在物理上则只由文件系统的高速缓冲区构成,而很少启动外设。命名管道与匿名管道不同,他是存放在文件系统内的设备文件,当进程需要使用命名管道通讯时,要像对文件的操作一样:首先通过open()打开管道,然后再用write()和read()对文件进行读写操作。当然,这种操作也是按先进先出方式进行的。命名管道能实现非同族进程或没有亲情关系间的相互通信,但不能在这些进程间复用一个命名管道为多对通信进程提供私用通道,也就是说,有名管道不能识别其通信伙伴,也不能有选择地接收信息,只能按先进先出方式进行通讯。按照通常的称呼,匿名管道称为管道(PIPE),命名管道称为FIFO,如不特指某种管道的话,一般遵循这种约定。2.匿名管道常用作家族间或有亲情关系的进程间的通信。请编写程序,以C/S方式实现主子进程间通过匿名管道通信。解:可参见§15.1.1和§15.2.1。§15.2.1中如图15-5所示的ipc02.c可直接作为本题的解。3.试描述命名管道用于进程间通信的机制。请据图15-8所示模型,自拟内容,编写使用命名管道进行C/S通讯的程序。解:可参见§15.2.2。任务:编写程序,通过FIFO实现C/S模式通讯,服务器要求是并发型的。要求:服务器首先创建一个“众所周知”的约定管道(/tmp/w_k_fifo)供所有客户写,然后服务器就守候此管道并从中读取客户请求。客户请求发来的是指定长度的自己的PID信息。每当来了客户请求,服务器就根据该客户机PID创建一个专用管道(/tmp/request.xxxxxx,xxxxxx为6位的PID)供客户机与服务器通讯使用。设客户端和服务端程序名分别为eipc01clt.c和eipc01svr.c,程序清单分别如图e15-2和e15-3所示,公共头文件eipc01.h清单如图e15-1所示。//0数据定义#definewell_known"/tmp/w_k_fifo"//定义众所周知管道#defineprefix"/tmp/request."//定义私有管道名字前缀#defineL7charmy_file[100];图e15-1eipc01.h头文件清单图e15-2eipc01clt.c程序清单图e15-3eipc01svr.c程序清单将客户端和服务器程序共用的定义部分放在单独的头文件中,是为保持公共定义的一致性。若分散在不同的程序中,可能会因彼此的修改导致“公共”变量的不一致。程序eipc01svr和eipc01svr可运行在同一个终端或不同终端上。当在同一个终端上时,服务器程序要在后台运行,且要首先启动。如果你的终端不允许后台进程写,请先执行命令#stty-tostop#允许后台进程写终端(Fedora时使用,以后同)然后再启动服务器: #./eipc01svr&#启动服务器端(后台方式)I’mwaitingconnection…#服务器等待请求openwell_knownOK!#打开周知管道ANewSeession.Tmp_FIFOis:/tmp/request.002072#请求到来Talkingwith002074OK!Waitingconnection...ANewSeession.Tmp_FIFOis:/tmp/request.002076|#./eipc01clt#启动客户端Open/tmp/w_k_fifoOK!open/tmp/request.002078OK!Readfrom/tmp/request.002078,andgot:002078#ls-l/tmp/w_k_fifo/tmp/request*#查看临时文件(输出可能有所不同)4.在一个C程序中,如何将管道用作标准I/O?试编写程序模拟shell的I/O重定向。解:请参见§15.2.3及其中的ipc04.c。ipc04.c只是无名管道的读入端用于了标准输入,用户也可以再修改它,使得无名管道的2端均用于标准I/O。设修改后的程序名为eipc02.c,清单如图如e15-4所示。图e15-4eipc02.c程序清单请比较修改前后//4部分的变化。这里的wait(0)被注释掉了,否则将陷入永久等待,如果有必要作进一步改进的话,由用户自行完成。5.如何使用C语言实现进程间的管道通讯?比如ls|wc。解:§15.2.4。§15.2.4中如图15-10所示的程序ipc05.c可作为本题的解。6.什么是IPC?在C语言编程中如何使用这种机制?如何使用命令管理IPC资源?解:可参见§15.1.2。在多用户、多任务系统中,进程之间是需要协作的,因此进程之间是必须能进行通讯的。进程间通讯传送的信息有两类:控制信息和数据信息。实现控制信息传送主要是通过信号方式,IPC(InterProccesscommunication)则在进程间传送数据信息。现在,一般意义说的进程间通讯主要是指消息队列、信号量和共享存储区机制,POSIX标准也提供这些支持。IPC的三种通讯方式在UNIXSystemV中是作为一个整体实现的,它们具有下例共同性质:(1)每种机制都用两种基本数据结构来描述。①索引表:索引表由索引表项组成。每个索引表项描述一个通信实例,它由关键字、访问控制结构及操作状态信息组成。②实例表:一个实例表项描述一个具体通信实例的相关特征。(2)索引表项中的关键字。这是一个大于零的整数,由用户选择或指定。(3)每种通信机制都提供有一个“get”类系统调用,用于创建一个新的索引表项或获得已建立的索引表项的描述字。(4)每种通信机制的“ctl”(control)类系统调用可用来查询索引表项中的状态,以及置状态信息或从系统中删除表项。(5)索引表的访问控制结构中含有创建该表项进程的用户的UID、GID和权限控制信息(参见sys/ipc.h中的ipc_perm结构)。可以通过“control”类系统调用设置许可权限,从而起到通信保护的作用。编程时,总是先使用“get”类系统调用(msgget()、shmget()和semget())打开或获取一个(类似文件操作的)描述符,然后再使用操作类系统调用进程操作访问。最后还都可以使用“ctl”类系统调用删除由“get”类系统调用创建的IPC资源。说明:IPC资源的内核级的,不会自动消失,除非有意保留,使用后应主动删除它。用于IPC机制管理的命令有ipcs和ipcrm。ipcs用于IPC机制的状态信息查询,ipcrm用于删除指定的消息队列、共享内存或信号量。详情参见§15.1.2。7.试使用消息或共享内存机制实现C/S模式通讯,任务自行拟定。解:可参见§15.3和可参见§15.4。1)消息机制编程可参见ipc06.h、ipc06clt.c和ipc06svr.c(清单见教材)。2)共享内存机制编程可参考ipc07.h、ipc07clt.c和ipc07svr.c(程序清单参见教材)。8.试编写程序实现操作系统原理中临界区的互斥访问。解:可参见§15.5。请参见§15.5.4如图15-18所示程序ipc09.c(清单见教材)。 第16章线程编程基础1.什么是线程?它与进程有什么关系?解:可参见§16.2.1。一个进程内的基本调度单位称为线程或称为轻量级进程(LightWeightProcess)。引入线程主要目的是为了减少处理机的空转时间和调度切换时间,提高系统的执行效率和并行执行程度。每个进程都有一个进程ID(PID),每个线程也都有一个线程ID(TID)。进程是资源分配的基本单位。线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。线程只由相关堆栈、寄存器和线程控制表TCB组成。进程拥有一个完整的虚拟地址空间,不同的进程拥有不同的虚拟地址空间。线程是进程的一部分,同一进程内的不同线程共享同一地址空间。线程是独立调度和分派的基本单位,这个调度单位既可以由操作系统内核控制,也可以由用户程序控制,可并发执行。2.什么是可重入函数、信号安全和线程安全函数?如何才能做到函数的线程安全?解:可参见§16.2.2。可重入代码(ReentrantCode)又称为“纯代码”(PureCode)是一种允许多个进程同时执行的代码。为使各个进/线程所执行的代码完全相同,绝对不允许修改可重入代码的任何部分,因此,可重入代码又是一种不允许任何进/线程对它进行修改的代码。可重入至少包含两个意思:(1)可以被中断,中断返回后继续执行而不会产生错误的结果;(2)可以被并发,允许同时被多次调用执行,且结果正确。可重入函数就是基于可重入代码的函数。可重入函数可以由多于一个任务并发使用,而不必担心数据错误。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。可重入函数要么使用本地变量,要么在使用全局变量时确保互斥操作。如果能从信号处理程序中安全地调用一个函数,这个函数被称为是异步信号安全(async-signalsafe)的。POSIX标准定义并列出了异步信号安全函数(请参见IEEEStd1003.1-1990)。一个函数被称为线程安全的(thread-safe),当且仅当被多个并发线程反复的调用时,它会一直产生正确的结果。如前所述,如果一个线程函数是可重入的,则它是线程安全的。如果函数是可重入的,那么它既是信号安全的,也是线程安全的。3.一个线程如何结束自己?所有线程都有返回值吗?若一个线程有返回值,如果取得它的返回值?解:可参见§16.2.1和§16.2.2。当一个线程完成使命需要结束时,可以显式调用pthread_exit()来结束自己,也可以在线程函数正常结束时结束。线程调用pthread_exit(*retval)可以终止自己并返回一个指向某个对象的指针retval。若线程是非分离的,进程中的其他线程可以调用pthread_join(tid,**retval)获取该retval的值。示例程序可参考§16.2.2中的程序pth01.c。4.请设计一个线程程序描述C语言线程程序结构。解:可参见§16.2。§16.2.2中的如图16-1所示的pth01.c程序可作为本题的解。5.何为线程属性?应该怎么操作和使用线程属性?解:可参见§16.3。线程具有属性,用pthread_attr_t表示,在对该结构进行处理之前必须进行初始化,在使用后需要对其去除初始化。初始化工具为pthread_attr_init()。相反的,pthread_attr_destroy()可对其进行去除初始化或清理。当使用intpthread_create(pthread_t*tid,pthread_attr_t*attr,void*(*start_routine)(void*),void*arg);创建一个进程时,可将第2个参数置为NULL以使用默认的属性。线程属性结构大致如下:typedefstruct__pthread_attr_s{int__detachstate;//联合状态(可联合的/分离的)int__schedpolicy;//调度策略struct__sched_param__schedparam;//调度参数int__inheritsched;//优先继承int__scope;//调度范围(用户级/系统级) void*__stackaddr;//堆栈地址size_t__stacksize;//堆栈大小}pthread_attr_t;关于线程属性的操作,请参考§16.3.3。6.线程有哪些同步机制?试举例说明如何利用这些机制实现线程的同步?解:可参见§16.4.线程的同步机制有多种,比如信号量、互斥锁和条件变量等。利用这些机制实现同步的示例请参考程序pth03.c、pth04.c和pth06.c(程序清单参见教材)。7.试描述如何使用线程实现并发服务器。解:可参见§16.5和§19.5。利用线程实现并发服务器的框架如图16-8所示。基于线程的并发服务器程序可参考后述的§19.5中如图19-12所示的程序net02spth.c。第17章终端与curses库编程1.什么是终端的规范模式和非规范模式,它们是以什么方式工作的?规范模式下的行规程的功能是什么?解:可参见§17.1.5和§17.1.6。1)规范模式就我们通常使用的终端工作模式。规范模式是在shell级使用命令sttyicanon或在C语言中将termios结构中的c_lflag标志开启后的终端工作模式,此时EOF、EOL、EOL2.ERASE、KILL、LNEXT、REPRINT、STATUS和WERASE等被启用。在此种模式下,发布一个读请求时,当终端输入一行后返回。关于规范模式更多细节请参见§17.1.5。2)非规范模式是用于读取密码、热键的终端特殊工作模式。在非规范模式下,输入不再按行进行组装,且不处理特殊字符。非规范模式,可以使用命令#stty-icanon进行设置,或者在编程时通过将termios结构中的clflag中的ICANON关闭的方法来设置非规范模式。具体做法是*.c_lflag&=~ICANON;关于非规范模式的更多细节,请参见§17.1.6。2.终端的termios结构只有7个成员,而表17-1中有那么多的终端标志或属性值,应该怎样使用表17-1中的属性修改termios结构成员?解:可参见§17.1.1。termios的结构structtermios{tcflag_tc_iflag;/*输入模式标志*/tcflag_tc_oflag;/*输出模式标志*/tcflag_tc_cflag;/*控制模式标志*/tcflag_tc_lflag;/*本地模式标志*/cc_tc_cc[NCCS];/*控制字符*/speed_tc_ispeed;/*输入速度*/speed_tc_ospeed;/*输出速度*/};中,只有c_iflag(输入模式标志)、c_oflag(输出模式标志)、c_cflag(控制模式标志)、c_lflag(本地模式标志)、c_cc[](控制字符)、c_ispeed(输入速度)和c_ospeed(输出速度)。其中的前4个是表17-1中相关子项(标志)的与(&)、或(|)、非(~)运算实现的;c_cc[]由对应的标志前加V形成的下标来访问,比如VINTR、VSTOP等;c_ispeed和c_ospeed分别控制着输入和输出的传输速率,可由表17-1中c_ispeed和c_ospeed中的对应标志,比如B9600、B19200、B38400等直接设置。3.试编写程序,实现stty的部分功能(内容自行拟定)。解:可参见§17.1.2。§17.1.2中如图17-1所示的term01.c可作为本题的答案(清单教材)。 4.编写程序,在主窗体上创建2个子窗体,并为它们设置不同的边框,子窗体大小自行指定。要求让其中的一个在主窗体内按某种策略“自由”移动,每碰到主窗体边界就改变方向(方向也由设计者自行指定),而操作者可以使用光标键控制另一个子窗体移动,尽量不让两者接触。每两者接触后程序结束。窗体的移动速度也由设计者指定。解:可参见§17.3。实现此题的方法和思路可以很多,这里只给出一种较简单者。设程序名为ecur01.c,清单如图e17-1所示。图e17-1ecur01.c程序清单程序ecur01执行时的初始状态如图e17-2所示。在上下居中的最左侧为win1、最右侧win2,初始化大小分别为11x11和6x6(单位:字符)。使用者可以修改程序,比如更改初始位置或重置大小等。在程序执行过程中,win1首先向左移动每次一字符,直到最右侧再向左移动,如此往复。用户可以通过光标键控制win2的移动:HOME/END:左上/下各移一字符;PageUP/PageDwon:右上/下各移一字符;←/→:左/右各移一字符;↑/↓:上/下移一字符,当win1和win2相遇时程序结束,中途也可通过q/Q/ESC键结束运行。窗体win1不会自动移动,只有当用户通过光标键驱动win2时win1才移动。若要让win1不受win2影响的移动,程序还需要改进。图e17-2ecur01执行画面*5.编写程序实现多窗体的操作。要求创建多个窗体和子窗体,实现窗体、子窗体及其内容的显示、属性设置、边框设置、位置移动和窗体显示层次的调整等,内容自拟。解:可参见§17.3。设程序名为ecur02.c,清单如图e17-3所示。程序ecur02.c实现了窗体win1、win2和子窗体swin2的创建及相关操作,并以stdscr作为控制或提示窗口,每对win1、win2和swin2进行操作,就会在标准窗体stdscr上率先给出提示。为了能够清楚地看到对窗体的操作过程,在程序中加入了一些sleep()语句。程序的执行是一个动态的过程,表现为一系列动画,可以通过执行ecur02动态观察到每个细节,程序执行的最后画面如图e17-4所示。图e17-3ecur02.c清单ecur02.c使用了3个独立窗体stdscr、win1、win2和win2的子窗体swin2。首先将标准stdscr填满字符’0’,在整个操作过程中,stdscr的上半部分用于操作过程提示,下半部分未使用或被其它窗体覆盖。接着创建win1,并将它填满’1’,然后再为它添加边框。通过添加边框操作可以看出,边框是占用窗体空间的:有了边框,窗体的可用区域变小了,且边框与窗体内容互相影响,甚至窗体的滖动也会带动边框,这是我们所不希望的,应尽量回避或避免。对win2的设置了滖动属性。当对它进行全屏填写字符串时,由于屏满,或光标到达了窗体的最后一个字符时,导致了整个窗体上翻一行。接着进行了上滖1行、2行和下滖3行的操作,操作后可以发现,窗体的上下都出现了空白行,这说明窗体滖动被移出的部分信息丢失,不可再“挽回”。在对win1和win2创建后显示窗体时,在还没有填入内容前,窗体是空的,但在为win2创建子窗体swin2后,显示时子窗体的内容是不空的,且为原父窗体的内容,这说明在创建子窗体时,子窗体的是不会清空的,而且复制父窗体原来位的内容。在删除子窗体后,其内容也没有被清除,而是作为父窗体的内容保留在父窗体原来的位置,这是父、子窗体创建和撤销的区别。对主窗体的删除就不是这样,而是要删除清除窗体在屏幕的内容,以至于我们在程序的最后,将对窗体win1的win2的删除分部注释掉,才能观察到如图e17-4所示的“最终”结果。 删除swin2之后,继续移动win2。移动过程动态地分了几步,当达到与win1不重叠后结束。为了保持窗体的层次在移动时不被改变,或不因移动win2导致win1被stdscr所遮挡,多次使用了touchwin()和wrefresh()函数。程序的最后是窗体删除部分,为了保持图e17-4所示的效果,没有让它们发挥作用。程序的编译方法为$gcc-lcursesecurs02.c-oecurs02#Fedora&FreeBSD$gcc-I/usr/xpg4/include-L/usr/xpg4/lib-lcursesecur02.c-oecur02#Solaris在Solaris下,由于与/usr/xpg4下配套的动态链接库文件也在/usr/xpg4/lib目录,因此还必须设置环境变量LD_LIBRARY_PATH,方法是exportLD_LIBRARY_PATH=/lib:/usr/xpg4/lib图e17-4ecur02执行最后结果第18章数据库的使用及编程1.完善程序dbs01.c,使之具有删除和查询所有记录的功能。解:可参见§17.3和18.1。设完善后后部分程序为edbs01.c,其部分edbs01_part.c清单如图e18-1所示。图e18-1edbs01_part.c程序清单用户需要用完善的部分替换dbs01.c的原位置部分。程序edbs01.c的编译和链接方法如下:$gcc-DLINUXedbs01.c-lcurses-lgdbm-oedbs01#FedoraLinux$gcc-DLINUXedbs01.c-lcurses-lgdbm_compat-oedbs01#Fedora16$gccedbs01.c-lcurses-oedbs01#FreeBSD$gccedbs01.c-lcurses-oedbs01-I/usr/xpg4/include-L/usr/xpg4/lib#Solaris$exportLD_LIBRARY_PATH=/lib:/usr/xpg4/lib#Solaris2.MySQL脚本程序有几种执行办法?试用即时文档方式,向数据库students的stds和marks分别增添几个记录。解:可参见§18.2.1.4。1)MySQL脚本程序执行有3种办法:(1)I/O重定向;(2)使用mysql的source内部命令;(3)使用即时文档。2)试用即时文档方式,向数据库students的stds和marks分别增添几个记录。设向数据库添加记录的脚本语句为USEstudents;INSERTINTOstds(id,cls,u_no,unm,sex,bth)values("201102200801011235","061410005","0614100105","Zhang3","F","20080105"),("201102200909091236","061411006","0614110106","Li4","M","20080106");insertINTOmarks(id,eng,chn,math)VALUES("201102200801011235",95,95,95), ("201402200710103450",90,90,80);!以上的执行过程可以整理成脚本文件,我们把它命名为emysql.script,清单如图e18-2所示。mysql</tmp/students.sql#生成统一的/tmp/students.sql2)恢复students要求数据库students要事先存在。若不存在,则先创建之。数据库和表的恢复方法很多,以下为其中之一(使用即时文档)。(1)逐个表恢复mysql<0)exit(0);//父进程退出:把子进程交给1#进程setsid();//设置新会话:子进程成为进程组组长,并放弃控制终端chdir("/");//改变工作目录umask(002);//设置新文件创建掩码close(0);close(1);close(2);//关闭三个标准I/O(可选)图e19-1enet02sd_part.c程序的部分清单其它部分不变,然后再将改变后的程序命名为enet02sd.c,并假设编译生成的可执行程序为enet02sd。其实,这段程序可以加在程序中任何合适的地方,比如程序的开始,或//2之前。但这里我们把它放在了(//7之前)为通讯准备的一切工作就绪之后,这样可以保证程序运行的成功率。由于添加进了这段程序,原来通过标准I/O进行的操作就都失灵了,从而相关的输出就都看不到了。但一般情况下,程序执行是要留下“痕迹” 的。为了保证该服务器的工作过程信息能够被记录在文件中以供事后使用,可将它们记录在日志文件中(参见net03s.c)。(2)将enet02sd设置为开机自启动将enet02sd设置为开机自启动的方法有多种,其中之一是在本地启动文件rc.local末添加如下行:net02sdtestsvr//testsvr为/etc/ervices中定义的服务本地服务启动文件在Fedora/Linux、FreeBSD和Solaris下的位置和文件名均为/etc/rc.local。(3)说明经运行可以发现,enet02sd.c还是有问题的。问题不在enet02sd.c程序的自己,而在设计时忽略了一个问题:对子进程的等待。若不进行对子进程等待,则每来一个请求便产生一个子进程,子进程服务结束后生便形成一个僵尸进程,若有大量的服务请求到来的话,会产生大量的僵尸进程,这样很快就会用尽系统的PCB表的。为了完善enet02sd.c,还需要加进对子进程的等待处理部分。①添加信号处理函数可在main()函数的前面添加对子进程结束信号SIGCHLD的处理函数child_wait()(名字自定)的定义部分#includevoidchild_wait(intsig){wait(0);}②添加信号捕获函数在如图e19-1所示的代码之后加入对信号SIGCHLD的捕获设置语句signal(SIGCHLD,child_wait);经过以上处理之后,程序(命名为enet02sd_sig.c)就可以作为完全正常的服务器运行了。6.模拟net03s.c和net03c.c编写一套基于UDP通讯的程序。解:§19.3、§6.5和§12.8。直接参考或使用net03s.c和net03c.c,具体过程从略。7.试结合网络和MySQL数据库编程,编写C/S结构的数据访问程序。要求:(1)只能在服务器上访问数据,客户端只提供输入和结果显示界面;(2)数据库可以是第18章的students;(3)建议运行时将客户端和服务器分别放不同系统上。解:可参见§19.1、§19.2和§18.3。程序被分为3部分:公共头文件enet04.h、服务器程序enet04s.c和客户端程序enet04c.c。1)头文件为了减小篇幅,而将服务器和客户端程序的头文件和公共定义部分摘出而形成的一个文件,命名为enet04.h,清单如图e19-2所示。图e19-2enet04.h清单enet04.h定义了enet04s.c和enet04c.c所共用的变量和函数以及头文件,其中的3个函数定义如下:my_err(intret,char*h,interr_no,constchar*err_msg,char*buf);strtov(char*str,char*sp,char**v,intcnt);log_file(char*s1,char*s2,char*s3);my_err()用于产生和显示数据库处理过程中出现的错误,其中ret为调用者提供的错误或信息编号,原样显示;err_no为mysql_errno();err_msg为与err_no对应的的错误信息;buf为经前3个参数加工出的最终结果。my_err()也可用于正常情况下正常信息的处理。strtov()用于将字符串str按分隔符sp分隔成字符串数组v[],其中cnt为分解的最大个数。log_file()用于将字符串s1、s2和s3按date:time~s1~s2~s3格式写入logfile文件,且每个这样的记录占一行。2)服务器端程序服务器程序enet04s.c的清单如图e19-3所示,它有网络Socket建立与通讯、Mysql数据库链接及访问等功能。在对MySQL数据库students访问时,主要针对其中的marks表进行操作,实现的功能有查询(do_query)、插入(do_input)和删除(do_delete)。enet04s.c要求一个命令行参数作为服务名,若不提供则在给出用法提示后退出(//1)。在经过通讯准备(//2)后,进入通讯部分(//3),等待客户端提出请求。在得到客户请求后,进行数据库处理(//4),由客户请求数据得到功能号,若为合理功能,初始化数据库对象(//4.1)、链接数据库(//4.2),然后根据功能号,完成相关的功能处理(//4.3),//4.4为服务数据的回写。snet04s.c中所使用的3个主要函数定义如下: do_query(MYSQLmysql,char*tbl,char*dat,char*rst);do_input(MYSQLmysql,char*tbl,char*dat,char*rst);do_delete(MYSQLmysql,char*tbl,char*dat,char*rst);mysql为数据库对象;tbl为表名;dat为客户端请求数据;rst为处理的结果字串。在执行do_delete()函数的执行过程中,要首先查询经用户确认后才能真正地执行删除功能。图e19-3enet04s.c程序清单服务器程序enet04s.c还处理了信号SIGCHLD,以避免僵尸进程的产生。对于服务器程序这是必须。如果不对SIGCHLD进行处理,在子进程结束后将变成僵尸,过多僵尸进程的存在将造成资源的浪费,甚至可导致系统不能正常工作;如果采用wait()方式处理了,在没有子进程结束时,将造成调用wait()者阻塞,从而造成主服务器不能接受客户请求。这里,对SIGCHLD的处理是通过捕获信号方式进行的。首先定义了一个信号捕获函数child_exit():voidchild_exit(){wait(0);}其作用是等待任意子进程的结束。对于这里的情况,由于信号SIGCHLD只在有子进程结束时才产生,所以采用捕获信号的办法处理是合适,既不会形成僵尸进程,也不会造成主服务进程阻塞。enet04s.c还有一个记录日志的功能,能将所有经过本程序的通讯信息完整的记录下来,日志文件记录格式为:s1:客户端请求信息s2:为请求处理状态信息,具体内容及编号参见enet04.h中的sat[]字符串数组s3:为请求处理结果信息针对具体的业务系统,比如涉及资金流动的系统,日志必须记录完整且格式合理,还要能根据日志文件进行统计,根据明细计算出业务发生笔数、总金额等信息。这样的日志,要求通讯的双方都记录,以便在合适的时间对帐、划帐及清算。从这个意义止讲,还应该增加,统计、报表及输出等功能。编译方法:#FedoraLinux$gccenet04s.c-oenet04s-L/usr/lib/mysql-lmysqlclient#Fedora64位$gccenet04s.c-oenet04s-L/usr/lib64/mysql-lmysqlclient#FreeBSD$gccenet04s.c-oenet04s-I/usr/local/include-L/usr/local/lib/mysql-lmysqlclient#Solaris$gccenet04s.c-oenet04s-I/usr/mysql/include-L/usr/mysql/lib/mysql-lmysqlclient-lsocket-lnsl3)客户端示例程序设客户端程序为enet04c.c(清单如图e19-4所示),它提供了操作和显示界面及通讯功能。enet04c.c需要2个参数:参数1为与服务器所用相同的服务名或端口号;参数2为服务器所在的主机名或IP地址。设生成的可执行程序为enet04c,执行方法为:$./enet04cservicehostenet04c.c中定义了以下函数:queryid(char*service,char*host,char*p):查询功能delete(char*service,char*host,char*p):删除功能input(char*service,char*host,char*p):输入功能tcp_com(char*service,char*host,char*p,char*dat,char*rst):TCP/IP通讯处理main_menu():主界面显示dsp(inty,intx,char**it,char**v,intcnt,intmod):输入和查询的显示。在它们的参数中,service、host和p分别为命令行参数中服务名、目的主机或进程名;dat为请求数据;rst为返回结果数据。图e19-4enet04c.c清单程序的编译方法如下#Fedora&FreeBSD$gcc-oenet04cenet04c.c–lcurses#Solaris$gcc-oenet04cnet04c.c-I/usr/xpg4/include-L/usr/xpg4/lib–lcurses-lsocket–lnsl 4)程序执行首先在/etc/services文件中定义服务testsvr56789/tcp在/etc/hosts文件中定义服务器程序所在主机名,比如svrhost,比如192.168.254.16svrhost若可以使用DNS解析的话,也可不定义。若服务器与客户机程序工作在同一个系统上的话,可以使用localhost或127.0.0.1。服务器程序启动方式如下:$./enet04stestsvr在另一个系统或终端上运行客户程序,方法如下$./enet04ctestsvrsvrhost8.试在习题7的基础上,编写一套基于C/M/S的通过网络访问MySQL数据库的程序。要求同习题7。解:可参见§19.2、19.4和习题7.仍设C/M/S结构的客户端为程序enet04c.c、服务器端程序为enet04s.c,这里只需要再增加一个中间件程序就可以了。设中间件程序为enet04mid.c,清单如图e19-5所示。图e19-5enet04mid.c程序清单中间件有很多功能可以实现,但程序enet04mid.c仅仅是实现了通讯转发工作,如果需要更多的功能,可根据实际需要再丰富其内容。设enet04mid.c生成的可执行程序为enet04mid,程序的编译与链接方式如下$gcc-oenet04midenet04mid.c#Fedora&FreeBSD$gcc-oenet04midnet04mid.c-lsocket–lnsl#Solaris其执行方法为$./enet04midlservicesserviceshost其中,lservice是作为服务器守候的服务;sservice为作为客户端向服务器链接的服务;shost为中间件需要链接的服务主机名或IP地址。要求lservice和sservice服务在文件/etc/services中有定义,shost要求在/etc/hosts文件中被定义,或可通过DNS服务器解析,得到IP地址。enet04mid.c还有一个记录日志的功能,能将所有经过本中间件的通讯信息完整的记录下来(格式参见enet04s.c),必要的话还可为其增加报表生成与统计输出功能。 '