加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 UNIX环境高级编程中文版

UNIX环境高级编程中文版.pdf

UNIX环境高级编程中文版

00wei00
2012-01-05 0人阅读 举报 0 0 暂无简介

简介:本文档为《UNIX环境高级编程中文版pdf》,可适用于IT/计算机领域

第章UNIX基础知识引言所有操作系统都向它们运行的程序提供服务。典型的服务有执行新程序、打开文件、读文件、分配存储区、获得当前时间等等本书集中阐述了UNIX操作系统各种版本所提供的服务。以严格的步进方式、不超前引用尚未说明过的术语的方式来说明UNIX几乎是不可能的(可能也会是令人厌烦的)。本章从程序设计人员的角度快速浏览UNIX并对书中引用的一些术语和概念进行简要的说明并给出实例。在以后各章中将对这些概念作更详细的说明。本章也对不熟悉UNIX的程序设计人员简要介绍了UNIX提供的各种服务。登录登录名登录UNIX系统时先键入登录名然后键入口令。系统在其口令文件通常是etcpasswd文件中查看登录名。口令文件中的登录项由个以冒号分隔的字段组成:登录名加密口令数字用户ID()数字组ID()注释字段起始目录(homestevens)以及shell程序(binksh)。很多比较新的系统已将加密口令移到另一个文件中。第章将说明这种文件以及存取它们的函数。shell登录后系统先显示一些典型的系统信息然后就可以向shell程序键入命令。shell是一个命令行解释器它读取用户输入然后执行命令用户通常用终端有时则通过文件(称为shell脚本)向shell进行输入。常用的shell有:•Bourneshell,binsh•Cshell,bincsh•KornShell,binksh系统从口令文件中登录项的最后一个字段中了解到应该执行哪一个shell。自V以来Bourneshell得到了广泛应用几乎每一个现有的UNIX系统都提供Bourneshell。Cshell是在伯克利开发的所有BSD版本都提供这种shell。另外ATT的系统VR和SVR也提供Cshell(下一章将对这些不同的UNIX版本作更多说明)。KornShell是Bourneshell的后继者它由SVR提供。KornShell在大多数UNIX系统上运行但在SVR之前通常它需要另行购买所以没有其他两种shell流行。本书将使用很多shell实例以执行已开发的程序其中将应用Bourneshell和KornShell都具有的功能。Bourneshell是SteveBourne在贝尔实验室中开发的其控制流结构使人想起Algol。Cshell是在伯克利由BillJoy完成的其基础是第版shell(不是Bourneshell)。其控制结构很像C语言它支持一些Bourneshell没有提供的功能如作业控制历史机制和命令行编辑。KornShell是DavidKorn在贝尔实验室中开发的它兼容Bourneshell并且也包含了使Cshell非常流行的一些功能如作业控制、命令行编译等。本书将使用这种形式的注释来描述历史并对不同的UNIX实现进行比较。当我们了解了历史缘由后采用某种特定实现技术的原因将变得清晰起来。文件和目录文件系统UNIX文件系统是目录和文件的一种层次安排目录的起点称为根(root)其名字是一个字符。目录(directory)是一个包含目录项的文件在逻辑上可以认为每个目录项都包含一个文件名同时还包含说明该文件属性的信息。文件属性是:文件类型文件长度文件所有者文件的许可权(例如其他用户能否能访问该文件)文件最后的修改时间等。stat和fstat函数返回一个包含所有文件属性的信息结构。第章将详细说明文件的各种属性。文件名目录中的各个名字称为文件名(filename)。不能出现在文件名中的字符只有两个斜线()和空操作符(null)。斜线分隔构成路径名(在下面说明)的各文件名空操作符则终止一个路径名。尽管如此好的习惯是只使用印刷字符的一个子集作为文件名字符(只使用子集的理由是:如果在文件名中使用了某些shell特殊字符则必须使用shell的引号机制来引用文件名)。当创建一个新目录时自动创建了两个文件名:(称为点)和(称为点点)。点引用当前目录点点则引用父目录。在最高层次的根目录中点点与点相同。某些UNIX文件系统限制文件名的最大长度为个字符BSD版本则将这种限制扩展为个字符。路径名个或多个以斜线分隔的文件名序列(可以任选地以斜线开头)构成路径名(pathname)以斜线开头的路径名称为绝对路径名(absolutepathname)否则称为相对路径名(relativepathname)。实例不难列出一个目录中所有文件的名字程序是ls()命令的主要实现部分程序列出一个目录中的所有文件UNIX环境高级编程ls()这种表示方法是UNIX的惯用方法用以引用UNIX手册集中的一个特定项。它引用第一部分中的ls项各部分通常用数字至表示在每个部分中的各项则按字母顺序排列。假定你有一份所使用的UNIX系统的手册。早期的UNIX系统把个部分都集中在一本手册中现在的趋势是把这些部分分别安排在不同的手册中:有用户专用手册程序员专用手册系统管理员专用的手册等等。某些UNIX系统把一个给定部分中的手册页又用一个大写字母进一步分成若干小部分例如ATT〔e〕中的所有标准IO函数都被指明在S部分中例如fopen(S)。某些UNIX系统例如以Xenix为基础的系统不是采用数字将手册分成若干部分而是用C表示命令(第部分)S表示服务(通常是第、部分)等等。如果你有联机手册则可用下面的命令查看ls命令手册页:manls程序只打印一个目录中各个文件的名字不显示其他信息如若该源文件名为mylsc,则可以用下面的命令对其进行编译编译的结果送入系统默认名为aout的可执行文件名:ccmylsc某种样本输出是:$aoutdevMAKEDEVconsolettymemkmemnull此处略去多行第章UNIX基础知识printer$aoutvarspoolmqueuecan'topenvarspoolmqueue:Permissiondenied$aoutdevttycan'topendevtty:Notadirectory本书将以这种方式表示输入的命令以及其输出:输入的字符以粗体表示程序输出则以另一种字体表示。如果欲对输出添加注释则以中文宋体表示输入之前的美元符号($)是shell打印的提示符本书将shell提示符显示为$。注意列出的目录项不是以字母顺序排列的ls命令则一般按字母顺序列出目录项。在这行的程序中有很多细节需要考虑:•首先其中包含了一个头文件ourhdrh。本书中几乎每一个程序都包含此头文件。它包含了某些标准系统头文件定义了许多常数及函数原型这些都将用于本书的各个实例中附录B列出了常用头文件。•main函数的说明使用了ANSIC标准所支持的新风格(下一章将对ANSIC作更多说明)。•取命令行的第个参数argv〔〕作为列出的目录名。第章将说明main函数如何被调用程序如何存取命令行参数和环境变量。•因为各种不同UNIX系统的目录项的实际格式是不一样的所以使用函数opendir,readdir和closedir处理目录。•opendir函数返回指向DIR结构的指针并将该指针传向readdir函数。我们并不关心DIR结构中包含了什么。然后在循环中调用readdir来读每个目录项。它返回一个指向dirent结构的指针而当目录中已无目录项可读时则返回null指针。在dirent结构中取出的只是每个目录项的名字(dname)。使用该名字此后就可调用stat函数(见节)以决定该文件的所有属性。•调用了两个自编的函数来对错误进行处理:errsys和errquit。从上面的输出中可以看到errsys函数打印一条消息(“Permissiondenied(许可权拒绝)”或“Notadirectory(不是一个目录)”)说明遇到了什么类型的错误。这两个出错处理函数在附录B中说明节将更多地叙述出错处理。这两个出错处理函数在附录B中说明节将更详细地叙述出错处理。•当程序将结束时它以参数调用函数exit。函数exit终止程序。按惯例参数的意思是正常结束参数值~则表示出错。节将说明一个程序(例如shell或我们所编写的程序)如何获得它所执行的另一个程序的exit状态。工作目录每个进程都有一个工作目录(workingdirectory有时称为当前工作目录(currentworkingdirectory))。所有相对路径名都从工作目录开始解释。进程可以用chdir函数更改其工作目录。例如相对路径名docmemojoe指的是文件joe它在目录memo中而memo又在目录doc中doc则应是工作目录中的一个目录项。从该路径名可以看出doc和memo都应当是目录但是却不清楚joe是文件还是目录。路径名ursliblint是一个绝对路径名它指的是文件(或目录)lint而lint在目录lib中lib则在目录usr中usr则在根目录中。起始目录登录时工作目录设置为起始目录(homedirectory)该起始目录从口令文件(见节)中UNIX环境高级编程的登录项中取得。输入和输出文件描述符文字描述符是一个小的非负整数内核用以标识一个特定进程正在存访的文件。当内核打开一个现存文件或创建一个新文件时它就返回一个文件描述符。当读、写文件时就可使用它。标准输入、标准输出和标准出错按惯例每当运行一个新程序时所有的shell都为其打开三个文件描述符:标准输入、标准输出以及标准出错。如果像简单命令ls那样没有做什么特殊处理则这三个描述符都连向终端。大多数shell都提供一种方法使任何一个或所有这三个描述符都能重新定向到某一个文件例如:ls>filelist执行ls命令其标准输出重新定向到名为filelist的文件上。不用缓存的IO函数open、read、write、lseek以及close提供了不用缓存的IO。这些函数都用文件描述符进行工作。实例如果愿意从标准输入读并写向标准输出则程序可用于复制任一UNIX文件。程序将标准输入复制到标准输出头文件<unistdh>(ourhdrh中包含了此头文件)及两个常数STDINFILENO和STDOUTFILENO是POSIX标准的一部分(下一章将对此作更多的说明)。很多UNIX系统服务的函数原型例如我们调用的read和write都在此头文件中。函数原型也是ANSIC标准的一部分本章的第章UNIX基础知识稍后部分将对此作更多说明。两个常数STDINFILENO和STDOUTFILENO定义在<unistdh>头文件中它们指定了标准输入和标准输出的文件描述符。它们的典型值是和但是为了可移植性我们将使用这些新名字。节将详细讨论BUFFSIZE常数说明各种不同的值将如何影响程序的效率。但是不管该常数的值如何此程序总能复制任一UNIX文件。read函数返回读得的字节数此值用作要写的字节数。当到达文件的尾端时read返回程序停止执行。如果发生了一个读错误read返回-。出错时大多数系统函数返回-。如果编译该程序其结果送入标准的aout文件并以下列方式执行它:aout>data那么标准输入是终端标准输出则重新定向至文件data标准出错也是终端。如果此输出文件并不存在则shell创建它。第章将更详细地说明不用缓存的IO函数。标准IO标准IO函数提供一种对不用缓存的IO函数的带缓存的界面。使用标准IO可无需担心如何选取最佳的缓存长度例如程序中的BUFFSIZE常数。另一个使用标准IO函数的优点与处理输入行有关(常常发生在UNIX的应用中)。例如fgets函数读一完整的行而另一方面read函数读指定字节数。我们最熟悉的标准IO函数是printf。在调用printf的程序中总是包括<stdioh>(通常包括在ourhdrh中)因为此头文件包括了所有标准IO函数的原型。实例程序的功能类似于调用read和write的前一个程序节将对程序作更详细的说明。它将标准输入复制到标准输出于是也就能复制任一UNIX文件。程序用标准IO将标准输入复制到标准输出函数getc一次读个字符然后putc将此字符写到标准输出。读到输入的最后个字节时getc返回常数EOF。标准输入、输出常数stdin和stdout定义在头文件<stdioh>中它们分别表示标准输入和标准输出文件。UNIX环境高级编程程序和进程程序程序(program)是存放在磁盘文件中的可执行文件。使用个exec函数中的一个由内核将程序读入存储器并使其执行。节将说明这些exec函数。进程和进程ID程序的执行实例被称为进程(process)。本书的每一页几乎都会使用这一术语。某些操作系统用任务表示正被执行的程序。每个UNIX进程都一定有一个唯一的数字标识符称为进程ID(processID)。进程ID总是一非负整数。实例程序用于打印进程ID。程序打印进程ID如果要编译该程序其结果送入aout文件然后执行它则有:$aouthelloworldfromprocessID$aouthelloworldfromprecessID此程序运行时它调用函数getpid得到其进程ID。进程控制有三个用于进程控制的主要函数:fork、exec和waitpid(exec函数有六种变体但经常把它们统称为exec函数)。实例程序从标准输入读命令并执行第章UNIX基础知识UNIX的进程控制功能可以用一个较简单的程序(见程序)说明该程序从标准输入读命令然后执行这些命令。这是一个类似于shell程序的基本实施部分。在这个行的程序中有很多功能需要思考:•用标准IO函数fgets从标准输入一次读一行当键入文件结束字符(通常是CtrlD)作为行的第个字符时fgets返回一个null指针于是循环终止进程也就终止。第章将说明所有特殊的终端字符(文件结束、退格字符、整行擦除等等)以及如何改变它们。•因为fgets返回的每一行都以新行符终止后随一个null字节故用标准C函数strlen计算此字符串的长度然后用一个null字节替换新行符。这一操作的目的是因为execlp函数要求的是以null结束的参数而不是以新行符结束的参数。•调用fork创建一个新进程。新进程是调用进程的复制品故称调用进程为父进程新创建的进程为子进程。fork对父进程返回新子进程的非负进程ID对子进程则返回。因为fork创建一新进程所以说它被调用一次(由父进程)但返回两次(在父进程中和在子进程中)。•在子进程中调用execlp以执行从标准输入读入的命令。这就用新的程序文件替换了子进程。fork和跟随其后的exec的组合是某些操作系统所称的产生一个新进程。在UNIX中这两个部分分成两个函数。第章将对这些函数作更多说明。•子进程调用execlp执行新程序文件而父进程希望等待子进程终止这一要求由调用waitpid实现其参数指定要等待的进程(在这里pid参数是子进程ID)。waitpid函数也返回子进程的终止状态(status变量)。在此简单程序中没有使用该值。如果需要可以用此值精确地确定子进程是如何终止的。•该程序的最主要限制是不能向执行的命令传递参数。例如不能指定列出的目录名只能对工作目录执行ls命令。为了传递参数先要分析输入行然后用某种约定把参数分开(很可能使用空格或制表符)然后将分隔后的各个参数传递给execlp函数。尽管如此此程序仍可用来说明UNIX的进程控制功能。如果运行此程序则得到下列结果。注意该程序使用了一个不同的提示符()。UNIX环境高级编程$aoutdateFriJun::MSTwhostevensconsoleJun:stevensttypJun:pwdhomestevensdocapueproclsMakefileaoutshelllcˆD键入文件结束符$输出常规的shell提示符ANSIC本书中的所有实例都用ANSIC编写。函数原型头文件<unistdh>包含了许多UNIX系统服务的函数原型例如已调用过的readwrite和getpid函数。函数原型是ANSIC标准的组成部分。这些函数原型如下列形式:ssizetread(int,void*,sizet)ssizetwrite(int,constvoid*,sizet)pidtgetpid(void)最后一个的意思是:getpid没有参数(void)返回值的数据类型为pidt。提供了这些函数原型后编译程序在编译时就可以检查在调用函数时是否使用了正确的参数。在程序中如果调用带参数的getpid(如getpid())则ANSIC编辑程序将给出下列形式的出错信息:line:toomanyargumentstofunction"getpid"另外因为编译程序知道参数的数据类型所以如果可能它就会将参数强制转换成所需的数据类型。类属指针从上面所示的函数原型中可以注意到另一个区别:read和write的第二个参数现在是void*类型。所有早期的UNIX系统都使用char*这种指针类型。作这种更改的原因是:ANSIC使用void*作为类属指针来代替char*。函数原型和类属指针的组合消去了很多非ANSIC编辑程序需要的显式类型强制转换。例如给出了write原型后可以写成:floatdata〔〕write(fd,data,sizeof(data))若使用非ANSI编译程序或没有给出函数原型则需写成:write(fd,(void*)data,sizeof(data))也可将void*指针特征用于malloc函数(见节)。malloc的原型为:第章UNIX基础知识void*malloc(sizet)这使得可以有如下程序段:int*ptrptr=malloc(*sizeof(int))它无需将返回的指针强制转换成int*类型。原始系统数据类型前面所示的getpid函数的原型定义了其返回值为pidt类型这也是POSIX中的新规定。UNIX的早期版本规定此函数返回一整型。与此类似read和write返回类型为ssizet的值并要求第三个参数的类型是sizet。以t结尾的这些数据类型被称为原始系统数据类型。它们通常在头文件<systypesh>中定义(头文件<unistdh>应已包括该头文件)。它们通常以Ctypedef说明加以定义。typedef说明在C语言中已超过年了(所以这并不要求ANSIC)它们的目的是阻止程序使用专门的数据类型(例如int,short或long)来允许对于一种特定系统的每个实现选择所要求的数据类型。在需要存储进程ID的地方分配类型为pidt的一个变量(注意程序已对名为pid的变量这样做了)。在各种不同的实现中这种数据类型的定义可能是不同的但是这种差别现在只出现在一个头文件中。我们只需在另一个系统上重新编辑应用程序。出错处理当UNIX函数出错时往常返回一个负值而且整型变量errno通常设置为具有特定信息的一个值。例如open函数如成功执行则返回一个非负文件描述符如出错则返回-。在open出错时有大约种不同的errno值(文件不存在许可权问题等)。某些函数并不返回负值而是使用另一种约定。例如返回一个指向对象的指针的大多数函数在出错时将返回一个指针。文件<errnoh>中定义了变量errno以及可以赋与它的各种常数。这些常数都以E开头另外UNIX手册第部分的第页intro()列出了所有这些出错常数。例如若errno等于常数EACCES这表示产生了权限问题(例如没有打开所要求文件的权限)。POSIX定义errno为:externinterrnoPOSIX中errno的定义较C标准中的定义更为苛刻。C标准允许errno是一个宏它扩充成可修改的整型左值(lvalue)(例如返回一个指向出错数的指针的函数)。对于errno应当知道两条规则。第一条规则是:如果没有出错则其值不会被一个例程清除。因此仅当函数的返回值指明出错时才检验其值。第二条是:任一函数都不会将errno值设置为在<errnoh>中定义的所有常数都不为。C标准定义了两个函数它们帮助打印出错信息。#include<stringh>char*strerror(interrnum)返回:指向消息字符串的指针此函数将errnum(它通常就是errno值)映射为一个出错信息字符串并且返回此字符串的指针。UNIX环境高级编程perror函数在标准出错上产生一条出错消息(基于errno的当前值)然后返回。#include<stdioh>voidperror(constchar*msg)它首先输出由msg指向的字符串然后是一个冒号一个空格然后是对应于errno值的出错信息然后是一个新行符。实例程序显示了这两个出错函数的使用方法。程序例示strerror和perror如果此程序经编译结果送入文件aout则有:$aoutEACCES:Permissiondeniedaout:Nosuchfileordirectory注意我们将程序名(argv〔〕其值是aout)作为参数传递给perror。这是一个标准的UNIX惯例。使用这种方法如程序作为管道线的一部分执行如:prog<inpile|prog|prog>outpile则我们就能分清三个程序中的哪一个产生了一条特定的出错消息。本书中的所有实例基本上都不直接调用strerror或perror而是使用附录B中的出错函数。该附录中的出错函数使用了ANSIC的可变参数表设施用一条C语句就可处理出错条件。用户标识用户ID口令文件登录项中的用户ID(userID)是个数值它向系统标识各个不同的用户。系统管理员在确定一个用户的登录名的同时确定其用户ID。用户不能更改其用户ID。通常每个用户有一个唯一的用户ID。下面将介绍内核如何使用用户ID以检验该用户是否有执行某些操作的适当许可权。用户ID为的用户为根(root)或超级用户(superuser)。在口令文件中通常有一个登录项其登录名为root我们称这种用户的特权为超级用户特权。我们将在第章中看到如果一个进程具有超级用户特权则大多数文件许可权检查都不再进行。某些操作系统功能只限于向超第章UNIX基础知识级用户提供超级用户对系统有自由的支配权。实例程序用于打印用户ID和组ID(在下面说明)。程序打印用户ID和组ID调用getuid和getgid以返回用户ID和组ID。运行该程序产生:$aoutuid=,gid=组ID口令文件登录项也包括用户的组ID(groupID)它也是一个数值。组ID也是由系统管理员在确定用户登录名时分配的。一般来说在口令文件中有多个记录项具有相同的组ID。在UNIX下组被用于将若干用户集合到课题或部门中去。这种机制允许同组的各个成员之间共享资源(例如文件)。节将说明可以设置文件的许可权使组内所有成员都能存取该文件而组外用户则不能。组文件将组名映射为数字组ID它通常是etcgroup。对于许可权使用数值用户ID和数值组ID是历史上形成的。系统中每个文件的目录项包含该文件所有者的用户ID和组ID。在目录项中存放这两个值只需个字节(假定每个都以双字节的整型值存放)。如果使用字节的登录名和字节的组名则需较多的磁盘空间。但是对于用户而言使用名字比使用数值方便所以口令文件包含了登录名和用户ID之间的映射关系而组文件则包含了组名和组ID之间的映射关系。例如UNIXlsl命令使用口令文件将数值用户ID映射为登录名从而打印文件所有者的登录名。添加组ID除了在口令文件中对一个登录名指定一个组ID外某些UNIX版本还允许一个用户属于另外一些组。这是从BSD开始的它允许一个用户属于多至个另外的组。登录时读文件etcgroup寻找列有该用户作为其成员的前个登记项就可得到该用户的添加组ID(supplementarygroupID)。信号信息是通知进程已发生某种条件的一种技术。例如若某一进程执行除法操作其除数为则将名为SIGFPE的信号发送给该进程。进程如何处理信号有三种选择:()忽略该信号。有些信号表示硬件异常例如除以或访问进程地址空间以外的单元等UNIX环境高级编程因为这些异常产生的后果不确定所以不推荐使用这种处理方式。()按系统默认方式处理。对于除系统默认方式是终止该进程。()提供一个函数信号发生时则调用该函数。使用这种方式我们将能知道什么时候产生了信号并按所希望的方式处理它。很多条件会产生信号。有两种键盘方式分别称为中断键(interruptkey通常是Delete键或CtrlC)和退出键(quitkey通常是Ctrl)它们被用于中断当前运行进程。另一种产生信号的方法是调用名为kill的函数。在一个进程中调用此函数就可向另一个进程发送一个信号。当然这样做也有些限制:当向一个进程发送信号时我们必需是该进程的所有者。实例回忆一下基本shell程序(见程序)。如果调用此程序然后键入中断键则执行此程序的进程终止。产生这种后果的原因是:对于此信号(SIGINT)的系统默认动作是终止此进程。该进程没有告诉系统核对此信号作何处理所以系统按默认方式终止该进程。为了更改此程序使其能捕捉到该信号它需要调用signal函数指定当产生SIGINT信号时要调用的函数名。因此编写了名为sigint的函数当其被调用时它只是打印一条消息然后打印一个新提示符。在程序中加了行构成了程序(添加的行以行首的号指示)。程序从标准输入读命令并执行第章UNIX基础知识因为大多数重要的应用程序都将使用信号所以第章将详细介绍信号。UNIX时间值长期以来UNIX系统一直使用两种不同的时间值:()日历时间。该值是自年月日::以来国际标准时间(UTC)所经过的秒数累计值(早期的手册称UTC为格林尼治标准时间)。这些时间值可用于记录文件最近一次的修改时间等。()进程时间。这也被称为CPU时间用以度量进程使用的中央处理机资源。进程时间以时钟滴答计算多年来每秒钟取为、或个滴答。系统基本数据类型clockt保存这种时间值。另外POSIX定义常数CLKTCK用其说明每秒滴答数。(常数CLKTCK现在已不再使用。节将说明如何用sysconf函数得到每秒时钟滴答数。)当度量一个进程的执行时间时(见节)UNIX系统使用三个进程时间值:•时钟时间。•用户CPU时间。•系统CPU时间。时钟时间又称为墙上时钟时间(wallclocktime)。它是进程运行的时间总量其值与系统中同时运行的进程数有关。在我们报告时钟时间时都是在系统中没有其他活动时进行度量的。用户CPU时间是执行用户指令所用的时间量。系统CPU时间是为该进程执行内核所经历的时间。例如只要一个进程执行一个系统服务例如read或write则在内核内执行该服务所花费的时间就计入该进程的系统CPU时间。用户CPU时间和系统CPU时间的和常被称为CPU时间。要取得任一进程的时钟时间、用户时间和系统时间很容易只要执行命令time()其参数是要度量其执行时间的命令例如:$cdusrinclude$timegrepPOSIXSOURCE**h>devnullrealmsusermssysmstime命令的输出格式与所使用的shell有关。节将说明一个运行进程如何取得这三个时间。关于时间和日期的一般说明见节。系统调用和库函数所有的操作系统都提供多种服务的入口点由此程序向内核请求服务。各种版本的UNIX都提供经良好定义的有限数目的入口点经过这些入口点进入内核这些入口点被称为系统调用(systemcall)。系统调用是不能更改的一种UNIX特征。UNIX第版提供了约个系统调用BSD提供了约个而SVR则提供了约个。UNIX环境高级编程系统调用界面总是在《UNIX程序员手册》的第部分中说明。其定义也包括在C语言中。这与很多早期的操作系统不同这些系统按传统方式在机器的汇编语言中定义内核入口点。UNIX所使用的技术是为每个系统调用在标准C库中设置一个具有同样名字的函数。用户进程用标准C调用序列来调用这些函数然后函数又用系统所要求的技术调用相应的内核服务。例如函数可将一个或多个C参数送入通用寄存器然后执行某个产生软中断进入内核的机器指令。从应用角度考虑可将系统调用视作为C函数。《UNIX程序员手册》的第部分定义了程序员可以使用的通用函数。虽然这些函数可能会调用一个或多个内核的系统调用但是它们并不是内核的入口点。例如printf函数会调用write系统调用以进行输出操作但函数strcpy(复制一字符串)和atoi(变换ASCII为整数)并不使用任何系统调用。从执行者的角度来看系统调用和库函数之间有重大区别但从用户角度来看其区别并不非常重要。在本书中系统调用和库函数都以C函数的形式出现两者都对应用程序提供服务但是我们应当理解如果希望的话我们可以替换库函数但是通常却不能替换系统调用。以存储器分配函数malloc为例。有多种方法可以进行存储器分配及与其相关的无用区收集操作(最佳适应首次适应等)并不存在对所有程序都最佳的一种技术。UNIX系统调用中处理存储器分配的是sbrk()它不是一个通用的存储器管理器。它增加或减少指定字节数的进程地址空间。如何管理该地址空间却取决于进程。存储器分配函数malloc()实现一种特定类型的分配。如果我们不喜欢其操作方式则可以定义自己的malloc函数它可能将使用sbrk系统调用。事实上有很多软件包它们实现自己的存储器分配算法但仍使用sbrk系统调用。图显示了应用程序、malloc函数以及sbrk系统调用之间的关系。从中可见两者职责不同相互分开内核中的系统调用分配另外一块空间给进程而库函数malloc则管理这一空间。另一个可说明系统调用和库函数之间的差别的例子是UNIX提供决定当前时间和日期的界面。某些操作系统提供一个系统调用以返回时间而另一个则返回日期。任何特殊的处理例如正常时制和夏时制之间的转换由内核处理或要求人为干预。UNIX则不同它只提供一条系统调用该系统调用返回国际标准时间年月日零点以来所经过的秒数。对该值的任何解释例如将其变换成人们可读的使用本地时区的时间和日期都留给用户进程运行。在标准C库中提供了若干例程以处理大多数情况。这些库函数处理各种细节例如各种夏时制算法。应用程序可以调用系统调用或者库函数而很多库函数则会调用系统调用。这在图中显示。系统调用和库函数之间的另一个差别是:系统调用通常提供一种最小界面而库函数通第章UNIX基础知识图malloc函数和sbrk系统调用应用代码用户进程内核存储分配函数mallocsbrk系统调用常提供比较复杂的功能。我们从sbrk系统调用和malloc库函数之间的差别中可以看到这一点在以后当比较不带缓存的IO函数(见第章)以及标准IO函数(见第章)时还将看到这种差别。进程控制系统调用(fork,exec和wait)通常由用户的应用程序直接调用(请回忆程序中的基本shell)。但是为了简化某些常见的情况UNIX系统也提供了一些库函数例如system和popen。节将说明system函数的一种实现它使用基本的进程控制系统调用。节还将强化这一实例以正确地处理信号。为使读者了解大多数程序员应用的UNIX系统界面我们不得不既说明系统调用只介绍某些库函数。例如若只说明sbrk系统调用那么就会忽略很多应用程序使用的malloc库函数。本书除了必须要区分两者时都将使用术语函数(function)来指代系统调用和库函数两者。小结本章快速浏览了UNIX。说明了某些以后会多次用到的基本术语介绍了一些小的UNIX程序的实例从中可感知到本书的其余部分将会进一步介绍的内容。下一章是关于UNIX的标准化以及这方面的工作对当前系统的影响。标准特别是ANSIC标准和POSIX标准将影响本书的余下部分。习题在在系统上查证除根目录外目录和是不同的。在分析程序的输出说明进程ID为和的进程可能会发生什么情况在在节中perror的参数是用ANSIC的属性const定义的而rerror的整型参数则没有用此属性定义为什么在附录B包含了出错处理函数errsys当调用该函数时保存了errno的值为什么在若日历时间存放在带符号的位整型数中那么到哪一年它将溢出在若进程时间存放在带符号的位整型数中而且每秒为滴答那么经过多少天后该时间值将会溢出UNIX环境高级编程图C库函数和系统调用之间的差别应用代码用户进程内核C库函数系统调用第章UNIX标准化及实现引言UNIX和C程序设计语言的标准化工作已经做了很多。虽然UNIX应用程序在不同的UNIX版本之间进行移植相当容易但是年代UNIX版本的剧增以及它们之间差别的扩大导致很多大用户(例如美国政府)要求对其进行标准化。本章将介绍正在进行的各种标准化工作然后讨论这些标准对本书所列举的实际UNIX实现的影响。所有标准化工作的一个重要部分是对每种实现必须定义的各种限制的说明所以我们将说明这些限制以及确定它们值的多种方法。UNIX标准化ANSIC年后期C程序设计语言的ANSI标准X得到批准〔ANSI〕。此标准已被采用为国际标准ISOIEC:。ANSI是美国国家标准学会它是由制造商和用户组成的非赢利性组织。在美国它是全国性的无偿标准交换站在国际标准化组织(ISO)中是代表美国的成员。ANSIC标准的意图是提供C程序的可移植性使其能适合于大量不同的操作系统而不只是UNIX。此标准不仅定义了C程序设计语言的语法和语义也定义了其标准库〔ANSI第章PlaugerKernighan及Ritchie中的附录B〕。因为很多新的UNIX系统(例如本书介绍的几个UNIX系统)都提供C标准中说明的库函数所以此库对我们来讲是很重要的。按照该标准定义的各个头文件可将该库分成区。表中列出了C标准定义的头文件以及下面几节中说明的另外两个标准(POSIX和XPG)定义的头文件。在其中也列举了SVR和BSD所支持的头文件。本章也将对这两种UNIX实现进行说明。表由各种标准和实现定义的头文件头文件标准实现说明ANSICPOSIXXPGSVRBSD<asserth>•••验证程序断言<cpioh>••cpio归档值<ctypeh>•••字符类型<direnth>••••目录项(节)<errnoh>•••出错码(节)<fcntlh>••••文件控制(节)<floath>•••浮点常数<ftwh>••文件树漫游(节)(续)头文件标准实现说明ANSICPOSIXXPGSVRBSD<grph>••••组文件(节)<langinfoh>••语言信息常数<limitsh>•••实施常数(节)<localeh>•••本地类别<mathh>•••数学常数<nltypesh>••消息类别<pwdh>••••口令文件(节)<regexh>•••正则表达式<searchh>••搜索表<setjmph>•••非局部goto(节)<signalh>•••信号(第章)<stdargh>•••可变参数表<stddefh>•••标准定义<stdioh>•••标准IO库(第章)<stdlibh>•••公用函数<stringh>•••字符串操作<tarh>••tar归档值<termiosh>••••终端IO(第章)<timeh>•••时间和日期(节)<ulimith>••用户限制<unistdh>••••符号常数<utimeh>••••文件时间(节)<sysipch>•••IPC(节)<sysmsgh>••消息队列(节)<syssemh>••信号量(节)<sysshmh>•••共享存储(节)<sys

用户评价(4)

  • 无关风月887671 很好,很强大,pdf版本,带书签,超高清。值得拥有。

    2013-01-22 21:28:04

  • malganiss 高清的呀,真好

    2012-11-08 21:12:18

  • caolinglingcool 是第一版

    2012-07-05 03:20:06

  • 119.6.72.22 good,高清晰,而且是整合在一个pdf里面的,带书签,谢谢分享

    2012-06-17 08:36:08

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/49

UNIX环境高级编程中文版

仅供在线阅读

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利