Unix环境高级编程003.pdf

上传人:qwe****56 文档编号:70009135 上传时间:2023-01-14 格式:PDF 页数:50 大小:1.38MB
返回 下载 相关 举报
Unix环境高级编程003.pdf_第1页
第1页 / 共50页
Unix环境高级编程003.pdf_第2页
第2页 / 共50页
点击查看更多>>
资源描述

《Unix环境高级编程003.pdf》由会员分享,可在线阅读,更多相关《Unix环境高级编程003.pdf(50页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、表5-4 使用标准I/O例程得到的时间结果函数用户C P U(秒)系统C P U(秒)时钟时间(秒)程序正文字节数表3-1中的最佳时间0.00.30.3f g e t s,f p u t s2.20.32.61 8 4g e t c,p u t c4.30.34.83 8 4f g e t c,f p u t c4.60.35.01 5 2表3-1中的单字节时间2 3.83 9 7.94 2 3.4对于这三个标准I/O版本的每一个,其用户C P U时间都大于表3-1中的最佳r e a d版本,因为每次读一个字符版本中有一个要执行 1 5 0万次的循环,而在每次读一行的版本中有一个要执行30 0

2、00次的循环。在r e a d版本中,其循环只需执行1 8 0次(对于缓存长度为8 1 9 2字节)。因为系统C P U时间都相同,所以用户C P U时间的差别造成了时钟时间的差别。系统C P U时间相同的原因是因为所有这些程序对内核提出的读、写请求数相同。注意,使用标准I/O例程的一个优点是无需考虑缓存及最佳 I/O长度的选择。在使用f g e t s时需要考虑最大行长,但是最佳I/O长度的选择要方便得多。表5-4中的最后一列是每个m a i n函数的文本空间字节数(由 C编译产生的机器指令)。从中可见,使用g e t c的版本在文本空间中作了 g e t c和p u t c的宏代换,所以它

3、所需使用的指令数超过了调用f g e t c和f p u t c函数所需指令数。观察g e t c版本和f g e t c版本在用户C P U时间方面的差别,可以看到在程序中作宏代换和调用两个函数在进行本测试的系统上并没有造成多大差别。使用每次一行I/O版本其速度大约是每次一个字符版本的两倍(包括用户 C P U时间和时钟时间)。如果f g e t s和f p u t s函数用g e t c和p u t c实现(例如,见K e r n i g h a n和R i t c h i e1 9 8 8的7.7节),那么,可以预期f g e t s版本的时间会与g e t c版本相接近。实际上,可以预

4、料每次一行的版本会更慢一些,因为除了现已存在的 60 000次函数调用外还需增加3百万次宏调用。而在本测试中所用的每次一行函数是用m e m c c p y(3)实现的。通常,为了提高效率,m e m c c p y函数用汇编语言而非C语言编写。这些时间数字的最后一个有趣之处在于:f g e t c版本较表3-1中B U F F S I Z E1的版本要快得多。两者都使用了约3百万次的函数调用,而f g e t c版本的速度在用户C P U时间方面,大约是后者的5倍,而在时钟时间方面则几乎是1 0 0倍。造成这种差别的原因是:使用r e a d的版本执行了3百万次函数调用,这也就引起 3百万次

5、系统调用。而对于f g e t c版本,它也执行3百万次函数调用,但是这只引起3 6 0次系统调用。系统调用与普通的函数调用相比是很花费时间的。需要声明的是这些时间结果只在某些系统上才有效。这种时间结果依赖于很多实现的特征,而这种特征对于不同的 U N I X系统却可能是不同的。尽管如此,使这样一组数据,并对各种版本的差别作出解释,这有助于我们更好地了解系统。在本节及3.9节中我们学到的基本事实是:标准I/O库与直接调用r e a d和w r i t e函数相比并不慢很多。我们观察到使用g e t c和p u t c复制1 M字节数据大约需3.0秒C P U时间。对于大多数比较复杂的应用程序,

6、最主要的用户C P U时间是由应用本身的各种处理花费的,而不是由标准I/O例程消耗的。5.9 二进制I/O5.6节中的函数以一次一个字符或一次一行的方式进行操作。如果为二进制 I/O,那么我们更愿意一次读或写整个结构。为了使用 g e t c或p u t c做到这一点,必须循环通过整个结构,一次1 0 0U N I X环境高级编程下载读或写一个字节。因为 f p u t s在遇到n u l l字节时就停止,而在结构中可能含有 n u l l字节,所以不能使用每次一行函数实现这种要求。相类似,如果输入数据中包含有n u l l字节或新行符,则f g e t s也不能正确工作。因此,提供了下列两个

7、函数以执行二进制 I/O操作。#include size_t fread(void*p t r,size_t s i z e,size_t n o b j,FILE*f p);size_t fwrite(const void*p t r,size_t s i z e,size_t n o b j,FILE*f p);两个函数的返回:读或写的对象数这些函数有两个常见的用法:(1)读或写一个二进制数组。例如,将一个浮点数组的第 2至第5个元素写至一个文件上,可以写作:float data 1 0;if(fwrite(&data2,sizeof(float),4,fp)!=4)err_sys(fwr

8、ite error);其中,指定s i z e为每个数组元素的长度,n o b j为欲写的元素数。(2)读或写一个结构。例如,可以写作:struct short count;long total;char name N A M E S I Z E;item;if(fwrite(&item,sizeof(item),1,fp)!=1)err_sys(fwrite error);其中,指定s i z e为结构的长度,n o b j为1(要写的对象数)。将这两个例子结合起来就可读或写一个结构数组。为了做到这一点,s i z e应当是该结构的s i z e o f,n o b j应是该数组中的元素数。

9、f r e a d和f w r i t e返回读或写的对象数。对于读,如果出错或到达文件尾端,则此数字可以少于n o b j。在这种情况,应调用f e r r o r或f e o f以判断究竟是那一种情况。对于写,如果返回值少于所要求的n o b j,则出错。使用二进制I/O的基本问题是,它只能用于读已写在同一系统上的数据。多年之前,这并无问题(那时,所有 U N I X系统都运行于P D P-11上),而现在,很多异构系统通过网络相互连接起来,而且,这种情况已经非常普遍。常常有这种情形,在一个系统上写的数据,在另一个系统上处理。在这种环境下,这两个函数可能就不能正常工作,其原因是:(1)在一

10、个结构中,同一成员的位移量可能随编译程序和系统的不同而异(由于不同的对准要求)。确实,某些编译程序有一选择项,它允许紧密包装结构(节省存储空间,而运行性能则可能有所下降)或准确对齐,以便在运行时易于存取结构中的各成员。这意味着即使在单一系统上,一个结构的二进制存放方式也可能因编译程序的选择项而不同。(2)用来存储多字节整数和浮点值的二进制格式在不同的系统结构间也可能不同。在不同系统之间交换二进制数据的实际解决方法是使用较高层次的协议。关于网络协议使用的交换二进制数据的某些技术,请参阅S t e v e n s1 9 9 0的1 8.2节。第5章标 准 I/O 库1 0 1下载在8.1 3节中,

11、我们将再回到 f r e a d函数,那时将用它读一个二进制结构U N I X的进程记账记录。5.10 定位流有两种方法定位标准I/O流。(1)ftell和f s e e k。这两个函数自V 7以来就存在了,但是它们都假定文件的位置可以存放在一个长整型中。(2)fgetpos和f s e t p o s。这两个函数是新由ANSI C引入的。它们引进了一个新的抽象数据类型f p o s _ t,它记录文件的位置。在非U N I X系统中,这种数据类型可以定义为记录一个文件的位置所需的长度。需要移植到非U N I X系统上运行的应用程序应当使用f g e t p o s和f s e t p o s

12、。#include long ftell(FILE*f p);返回:若成功则为当前文件位置指示,若出错则为 1 Lint fseek(FILE*f p,long o f f s e t,int w h e n c e);返回:若成功则为0,若出错则为非0void rewind(FILE*f p);对于一个二进制文件,其位置指示器是从文件起始位置开始度量,并以字节为计量单位的。f t e l l用于二进制文件时,其返回值就是这种字节位置。为了用 f s e e k定位一个二进制文件,必须指定一个字节 o f f s e t,以及解释这种位移量的方式。w h e n c e的值与3.6节中l s

13、e e k函数的相同:S E E K _ S E T表示从文件的起始位置开始,S E E K _ C U R表示从当前文件位置,S E E K _ E N D表示从文件的尾端。ANSI C并不要求一个实现对二进制文件支持 S E E K _ E N D规格说明,其原因是某些系统要求二进制文件的长度是某个幻数的整数倍,非实际内容部分则充填为 0。但是在U N I X中,对于二进制文件S E E K _ E N D是得到支持的。对于文本文件,它们的文件当前位置可能不以简单的字节位移量来度量。再一次,这主要也是在非 U N I X系统中,它们可能以不同的格式存放文本文件。为了定位一个文本文件,w h

14、 e n c e一定要是S E E K _ S E T,而且o f f s e t只能有两种值:0(表示反绕文件至其起始位置),或是对该文件的f t e l l所返回的值。使用r e w i n d函数也可将一个流设置到文件的起始位置。正如我们已提及的,下列两个函数是C标准新引进的。#include int fgetpos(FILE*f p,fpos_t*p o s);int fsetpos(FILE*f p,const fpos_t*p o s);两个函数返回:若成功则为0,若出错则为非0f g e t p o s将文件位置指示器的当前值存入由 p o s指向的对象中。在以后调用 f s e

15、 t p o s时,可以使用此值将流重新定位至该位置。1 0 2U N I X环境高级编程下载5.11 格式化I/O5.11.1 格式化输出执行格式化输出处理的是三个p r i n t f函数。#include int printf(const char*f o r m a t,.);int fprintf(FILE*f p,const char*f o r m a t,.);两个函数返回:若成功则为输出字符数,若输出出错则为负值int sprintf(char*b u f,const char*f o r m a t,.);返回:存入数组的字符数p r i n t f将格式化数据写到标准输出

16、,f p r i n t f写至指定的流,s p r i n t f将格式化的字符送入数组b u f中。s p r i n t f在该数组的尾端自动加一个n u l l字节,但该字节不包括在返回值中。4.3 B S D定义s p r i n t f返回其第一个参数(缓存指针,类型为 c h a r*),而不是一个整型。ANSI C要求s p r i n t f返回一个整型。注意,s p r i n t f可能会造成由b u f指向的缓存的溢出。保证该缓存有足够长度是调用者的责任。对这三个函数可能使用的各种格式变换,请参阅U N I X手册,或K e r n i g h a n和R i t c

17、h i e1 9 8 8的附录B。下列三种p r i n t f族的变体类似于上面的三种,但是可变参数表(.)代换成了a rg。#i n c l u d e#i n c l u d e int vprintf(const char*f o r m a t,va_list a rg);int vfprintf(FILE*f p,const char*f o r m a t,va_list a rg);两个函数返回:若成功则为输出字符数,若输出出错则为负值int vsprintf(char*b u f,const char*f o r m a t,va_list a rg);返回:存入数组的字符数

18、在附录B的出错例程中,将使用v s p r i n t f函数。关于ANSI C标准中有关可变长度参数表的详细说明请参阅 K e r n i g h a n和R i t c h i e1 9 8 8的7.3节。应当了解的是,由ANSI C提供的可变长度参数表例程(头文件和相关的例程)与由S V R 3(以及更早版本)和4.3 B S D提供的例程是不同的。5.11.2 格式化输入执行格式化输入处理的是三个s c a n f函数。第5章标 准 I/O 库1 0 3下载#i n c l u d e int scanf(const char*f o r m a t,.);int fscanf(FIL

19、E*f p,const char*f o r m a t,.);int sscanf(const char*b u f,const char*f o r m a t,.);三个函数返回:指定的输入项数,若输入出错,或在任意变换前已至文件尾端则为 E O F如同p r i n t f族一样,关于这三个函数的各个格式选择项的详细情况,请参阅 U N I X手册。5.12 实现细节正如前述,在U N I X中,标准I/O库最终都要调用第3章中说明的I/O例程。每个I/O流都有一个与其相关联的文件描述符,可以对一个流调用f i l e n o以获得其描述符。#include int fileno(FI

20、LE*f p);返回:与该流相关联的文件描述符如果要调用d u p或f c n t l等函数,则需要此函数。为了了解你所使用的系统中标准 I/O库的实现,最好从头文件 开始。从中可以看到:F I L E对象是如何定义的,每个流标志的定义,定义为宏的各个标准 I/O例程(例如g e t c)。K e r n i g h a n和R i t c h i e1 9 8 8中的8.5节含有一个简单的实现,从中可以看到很多U N I X实现的基本样式。P l a u g e r1 9 9 2的第1 2章提供了标准I/O库一种实现的全部源代码。4.3B S D中标准I/O库的实现(由Chris To r

21、e k编写)也是可以公开使用的。实例程序5-3为三个标准流以及一个与一个普通文件相关联的流打印有关缓存状态信息。注意,在打印缓存状态信息之前,先对每个流执行 I/O操作,因为第一个I/O操作通常就造成为该流分配缓存。结构成员_ f l a g、_ b u f s i z以及常数_ I O N B F和_ I O L B F是由作者所使用的系统定义的。如果运行程序5-3两次,一次使三个标准流与终端相连接,另一次使它们重定向到普通文件,则所得结果是:$a.o u tstdin,stdout 和s t d e rr 都连至终端enter any character键入新行符one line to s

22、tandard errorstream=stdin,line buffered,buffer size=128stream=stdout,line buffered,buffer size=128stream=stderr,unbuffered,buffer size=8stream=/etc/motd,fully buffered,buffer size=8192$a.out std.out 2 std.err三个流都重定向,再次运行该程序$cat std.errone line to standard error$cat std.outenter any characterstream=s

23、tdin,fully buffered,buffer size=81921 0 4U N I X环境高级编程下载stream=stdout,fully buffered,buffer size=8192stream=stderr,unbuffered,buffer size=8stream=/etc/motd,fully buffered,buffer size=8192程序5-3 对各个标准I/O流打印缓存状态信息从中可见,该系统的默认是:当标准输入、输出连至终端时,它们是行缓存的。行缓存的长度是1 2 8字节。注意,这并没有将输入、输出的行长限制为 1 2 8字节,这只是缓存的长度。如果要

24、将5 1 2字节的行写到标准输出则要进行四次 w r i t e系统调用。当将这两个流重新定向到普通文件时,它们就变成是全缓存的,其缓存长度是该文件系统优先选用的 I/O长度(从s t a t结构中得到的s t _ b l k s i z e)。从中也可看到,标准出错如它所应该的那样是非缓存的,而普通文件按系统默认是全缓存的。5.13 临时文件标准I/O库提供了两个函数以帮助创建临时文件。#i n c l u d e char*tmpnam(char*p t r);返回:指向一唯一路径名的指针第5章标 准 I/O 库1 0 5下载FILE*tmpfile(void);返回:若成功则为文件指针,

25、若出错则为 N U L Lt m p n a m产生一个与现在文件名不同的一个有效路径名字符串。每次调用它时,它都产生一个不同的路径名,最多调用次数是T M P _ M A X。T M P _ M A X定义在中。虽然T M P _ M A X是由ANSI C定义的。但该C标准只要求其值至少应为2 5。但是,X P G 3却要求其值至少为10 000。在此最小值允许一个实现使用 4位数字作为临时文件名的同时(0 0 0 0 9 9 9 9),大多数U N I X实现使用的却是大、小写字符。若p t r是N U L L,则所产生的路径名存放在一个静态区中,指向该静态区的指针作为函数值返回。下一次

26、再调用t m p n a m时,会重写该静态区。(这意味着,如果我们调用此函数多次,而且想保存路径名,则我们应当保存该路径名的副本,而不是指针的副本。)如若p t r不是N U L L,则认为它指向长度至少是L _ t m p n a m个字符的数组。(常数L _ t m p n a m定义在头文件中。)所产生的路径名存放在该数组中,p t r也作为函数值返回。t m p f i l e创建一个临时二进制文件(类型 w b+),在关闭该文件或程序结束时将自动删除这种文件。注意,U N I X对二进制文件不作特殊区分。实例程序5-4说明了这两个函数的应用。若执行程序5-4,则得:$a.o u t

27、/u s r/t m p/a a a a 0 0 4 7 0/u s r/t m p/b a a a 0 0 4 7 0one line of output加到临时文件名中的5位数字后缀是进程I D,这就保证了对各个进程产生的路径名各不同。t m p f i l e函数经常使用的标准U N I X技术是先调用t m p n a m产生一个唯一的路径名,然后立即u n l i n k它。程序5-4 tmpnam和t m p f i l e函数实例1 0 6U N I X环境高级编程下载请回忆4.1 5节,对一个文件解除连接并不删除其内容,关闭该文件时才删除其内容。t e m p n a m是t

28、m p n a m的一个变体,它允许调用者为所产生的路径名指定目录和前缀。#include char *tempnam(const char*d i re c t o ry,const char*p re f i x;返回:指向一唯一路径名的指针对于目录有四种不同的选择,并且使用第一个为真的作为目录:(1)如果定义了环境变量T M P D I R,则用其作为目录。(在7.9节中将说明环境变量。)(2)如果参数d i re c t o ry非N U L L,则用其作为目录。(3)将中的字符串P _ t m p d i r用作为目录。(4)将本地目录,通常是/t m p,用作为目录。如果p re

29、f i x非N U L L,则它应该是最多包含5个字符的字符串,用其作为文件名的头几个字符。该函数调用m a l l o c函数分配动态存储区,用其存放所构造的路径名。当不再使用此路径名时就可释放此存储区(7.8节将说明m a l l o c和f i e e函数)。t e m p n a m不是P O S I X.1和ANSI C的所属部分,它是X P G 3的所属部分。我们所说明的实现对应于 S V R 4和4.3+B S D。X P G 3版本除了不支持环境变量T M P D I R,其他都与此相同。实例程序5-5显示了t e m p n a m的应用。程序5-5 tempnam函数的应用

30、注意,如果命令行参数(目录或前缀)中的任一一个以空白开始,则将其作为 n u l l指针传送给该函数。下面显示使用该程序的各种方式。$a.out/home/stevens TEMP指定目录和前缀/h o m e/s t e v e n s/T E M P A A A a 0 0 5 7 1$a.out PFX使用默认目录:P _ t m p d i r/u s r/t m p/P F X A A A a 0 0 5 7 2$TMPDIR=/tmp a.out/usr/tmp 使用环境变量;无前缀第5章标 准 I/O 库1 0 7下载/t m p/A A A a 0 0 5 7 3环境变量复设目

31、录$TMPDIR=/no/such/dir a.out/tmp QQQQ/t m p/Q Q Q Q A A A a 0 0 5 7 4忽略无效环境目录$TMPDIR=/no/such/file a.out/etc/uucp MMMMM/u s r/t m p/M M M M M A A A a 0 0 5 7 5忽略无效环境和无效目录两者上述选择目录名的四个步骤按序执行,该函数也检查相应的目录名是否有意义。如果该目录并不存在(例如/n o/s u c h/d i r),或者对该目录并无写许可权(例如/e t c/u u c p),则跳过这些,试探对目录名的下一次选择。从本例中可以看出在路径名

32、中如何使用进程 I D,也可看出在本实现中,P _ t m p d i r目录是/u s r/t m p。设置环境变量的技术(程序名前的 T M P D I R=)适用于B o u r n es h e l l和K o r nSh e l l。5.14 标准I/O的替代软件标准I/O库并不完善。K o r n和Vo1 9 9 1列出了它的很多不足之处某些属于基本设计,但是大多数则与各种不同的实现有关。在标准I/O库中,一个效率不高的不足之处是需要复制的数据量。当使用每次一行函数f g e t s和f p u t s时,通常需要复制两次数据:一次是在内核和标准 I/O缓存之间(当调用 r e a

33、 d和w r i t e时),第二次是在标准 I/O缓存和用户程序中的行缓存之间。快速 I/O库AT&T 1990a中的f i o(3)避免了这一点,其方法是使读一行的函数返回指向该行的指针,而不是将该行复制到另一个缓存中。H u m e1 9 8 8报告了由于作了这种更改,g r e p(1)公用程序的速度增加了2倍。K o r n和Vo1 9 9 1说明了标准I/O库的另一种代替版:s f i o。这一软件包在速度上与f i o相近,通常快于标准I/O库。s f i o也提供了一些新的特征:推广了 I/O流,使其不仅可以代表文件,也可代表存储区;可以编写处理模块,并以栈方式将其压入 I/O

34、流,这样就可以改变一个流的操作;较好的异常处理等。K r i e g e r,Stumm和U n r a u1 9 9 2说明了另一个代换软件包,它使用了映照文件m m a p函数,我们将在1 2.9节中说明此函数。该新软件包称为 ASI(Alloc Stream Interface)。其程序界面类似于U N I X存储分配函数(malloc,realloc和f r e e,这些将在7.8节中说明)。与s f i o软件包相同,A S I使用指针力图减少数据复制量。5.15 小结大多数U N I X应用程序都使用标准I/O库。本章说明了该库提供的所有函数,某些实现细节和效率方面的考虑。应该看到

35、标准 I/O库使用了缓存机制,而这种机制是产生很多问题,引起很多混淆的一个领域。习题5.1在用s e t v b u f完成s e t b u f。5.2在5.8节中程序利用f g e t s和f p u t s函数拷贝文件,每次I/O操作只拷贝一行。若将程序中的M A X L I N E改为4,当拷贝的行超过该最大值时会出现什么情况?5.3在p r i n t f返回0值表示什么?5.4在下面的代码在一些机器上运行正确,而在另外一些机器运行时出错,解释问题所在。#include 1 0 8U N I X环境高级编程下载i n tm a i n(v o i d)char c;while(c=g

36、etchar()!=EOF)putchar(c);5.5在为什么t e m p n a m限制前缀为5个字符?5.6在对标准I/O流如何使用f s y n c函数(见4.2 4节)?5.7在在程序1-5和1-8中打印的提示信息没有包含换行符,程序也没有调用 ff l u s h函数,请解释提示信息是如何输出的?第5章标 准 I/O 库1 0 9下载下载下载第6章系统数据文件和信息6.1 引言有很多操作需要使用一些与系统有关的数据文件,例如,口令文件/e t c/p a s s w d和组文件/e t c/g r o u p就是经常由多种程序使用的两个文件。用户每次登录入 U N I X系统,以

37、及每次执行l s-l命令时都要使用口令文件。由于历史原因,这些数据文件都是A S C I I文本文件,并且使用标准I/O库读这些文件。但是,对于较大的系统,顺序扫描口令文件变得很花费时间,我们想以非 A S C I I文本格式存放这些文件,但仍向应用程序提供一个可以处理任何一种文件格式的界面。对于这些数据文件的可移植界面是本章的主题。本章也包括了系统标识函数、时间和日期函数。6.2 口令文件U N I X口令文件(P O S I X.1则将其称为用户数据库)包含了表6-1中所示的各字段,这些字段包含在中定义的p a s s w d结构中。注意,P O S I X.1只指定p a s s w d

38、结构中7个字段中的5个。另外2个元素由S V R 4和4.3+B S D支持。表6-1 /etc/passwd文件中的字段说明struct passwd 成员P O S I X.1用户名char *pw_name加密口令char *pw_passwd数值用户I Duid_t pw_uid数值组I Dgid_t pw_gid注释字段char *pw_gecos初始工作目录char *pw_dir初始s h e l l(用户程序)char *pw_shell由于历史原因,口令文件是/e t c/p a s s w d,而且是一个文本文件。每一行包含表6-1中所示的7个字段,字段之间用冒号相分隔。例

39、如,该文件中可能有下列三行:root:jheVopR58x9Fx:0:1:The superuser:/:/bin/shn o b o d y:*:6 5 5 3 4:6 5 5 3 4:/:stevens:3hKVD8R58r9Fx:224:20:Richard Stevens:/home/stevens:/bin/ksh关于这些登录项请注意下列各点:通常有一个登录项,其用户名为r o o t,其用户I D是0(超级用户)。加密口令字段包含了经单向密码算法处理过的用户口令副本。因为此算法是单向的,所以不能从加密口令猜测到原来的口令。当前使用的算法(见M o r r i s和T h o m p

40、 s o n1 9 7 9)总是产生1 3个可打印字符(在6 4字符集a-z A-Z 0-9./中)。因为用户名n o b o d y的加密口令字段只包含一个字符(*),所以加密口令决不会与此值相匹配。此用户名可用于网络服务器,这些服务器允许我们登录到一个系统,但其用户 I D和组I D(6 5 5 3 4)并不提供优先权。用此用户 I D和组I D可存取的文件只是大家都可读、写的文件。(这假定用户I D 6 5 5 3 4和组I D 6 5 5 3 4并不拥有任何文件。)在本节稍后部分我们将讨论对口令文件最近所作的更改(阴影口令)。口令文件中的某些字段可能是空。如果密码口令字段为空,这通常就

41、意味着该用户没有口令(不推荐这样做)。n o b o d y有两个空白字段:注释字段和初始 s h e l l字段。空白注释字段不产生任何影响。空白s h e l l字段则表示取系统默认值,通常是/b i n/s h。支持f i n g e r(1)命令的某些U N I X系统支持注释字段中的附加信息。其中,各部分之间都用逗号分隔:用户姓名,办公室地点,办公室电话号码,家庭电话号码。另外,如果注释字段中的用户姓名是一个&,则它被代换为登录名。例如,可以有下列记录:stevens:3hKVD8R58r9Fx:224:20:Richard&,B232,555-1111,555-2222:/h o

42、m e/s t e v e n s:/b i n/k s h即使你所使用的系统并不支持 f i n g e r命令,这些信息仍可存放在注释字段中,因为该字段只是一个注释,并不由系统公用程序解释。P O S I X.1只定义了两个存取口令文件中信息的函数。在给出用户登录名或数值用户 I D后,这两个函数就能查看相关记录。#include#include struct passwd*getpwuid(uid_t u i d);struct passwd*getpwnam(const char*n a m e);两个函数返回:若成功则为指针,若出错则为 N U L Lg e t p w u i d由

43、l s(1)程序使用,以便将包含在一个 i节点中的数值用户 I D映照为用户登录名。g e t p w n a m在键入登录名时由l o g i n(1)程序使用。这两个函数都返回一个指向p a s s w d结构的指针,该结构已由这两个函数在执行时填入了所需的信息。该结构通常是在相关函数内的静态变量,只要调用相关函数,其内容就会被重写。如果要查看的只是一个登录名或用户 I D,那么这两个P O S I X.1函数能满足要求,但是也有些程序要查看整个口令文件。下列三个函数则可用于此。#include#include struct passwd*getpwent(void);返回:若成功则为指针

44、,若出错或到达文件尾端则为 N U L Lvoid setpwent(void);void endpwent(void);第6章系统数据文件和信息1 1 1下载P O S I X.1没有定义这三个函数,但它们受到S V R 4和4.3+B S D的支持。调用g e t p w e n t时,它返回口令文件中的下一个记录。如同上面所述的两个 P O S I X.1函数一样,它返回一个由它填写好的p a s s w o r d结构的指针。每次调用此函数时都重写该结构。在第一次调用该函数时,它打开它所使用的各个文件。在使用本函数时,对口令文件中各个记录安排的顺序并无要求。函数s e t p w e

45、n t反绕它所使用的文件,e n d p w e n t则关闭这些文件。在使用 g e t p w e n t查看完口令文件后,一定要调用e n d p w e n t关闭这些文件。g e t p w e n t知道什么时间它应当打开它所使用的文件(第一次被调用时),但是它并不能知道何时关闭这些文件。实例程序6-1给出了函数g e t p w n a m的一个实现。程序6-1 getpwnam函数在程序开始处调用s e t p w e n t是保护性的措施,以便在调用者在此之前已经调用过 g e t p w e n t的情况下,反绕有关文件使它们定位到文件开始处。g e t p w n a m

46、和g e t p w u i d完成后不应使有关文件仍处于打开状态,所以应调用e n d p w e n t关闭它们。6.3 阴影口令在上一节我们曾提及,对U N I X口令通常使用的加密算法是单向算法。给出一个密码口令,找不到一种算法可以将其反变换到普通文本口令(普通文本口令是在P a s s w o r d:提示后键入的口令)。但是可以对口令进行猜测,将猜测的口令经单向算法变换成加密形成,然后将其与用户的加密口令相比较。如果用户口令是随机选择的,那么这种方法并不是很有用。但是用户往往以非随机方式选择口令(配偶的姓名、街名、宠物名等)。一个经常重复的试验是先得到一份口令文件,然后用试探方法猜

47、测口令。(G a r f i n k e l和S p a ff o r d1 9 9 1的第2章对U N I X口令及口令加密处理方案的历史情况及细节进行了说明。)为使企图这样做的人难以获得原始资料(加密口令),某些系统将加密口令存放在另一个通常称为阴影口令(shadow password)的文件中。该文件至少要包含用户名和加密口令。与该口令相关的其他信息也可存放在该文件中。例如,具有阴影口令的系统经常要求用户在一定时1 1 2U N I X环境高级编程下载间间隔后选择一个新口令。这被称之为口令时效,选择新口令的时间间隔长度经常也存放在阴影口令文件中。在S V R 4中,阴影口令文件是/e t

48、 c/s h a d o w。在4.3+B S D中,加密口令存放在/e t c/m a s t e r.passwd中。阴影口令文件不应是一般用户可以读取的。仅有少数几个程序需要存取加密口令文件,例如l o g i n(1)和p a s s w d(1),这些程序常常设置-用户-I D为r o o t。有了阴影口令后,普通口令文件/e t c/p a s s w d可由各用户自由读取。6.4 组文件U N I X组文件(P O S I X.1称其为组数据库)包含了表6-2中所示字段。这些字段包含在 中所定义的g r o u p结构中。表6-2 /etc/group文件中的字段说明struct

49、 group 成员P O S I X.1组名char *gr_name加密口令char *gr_passwd数字组I Dint gr_gid指向各用户名指针的数组char *gr_memP O S I X.1只定义了其中三个字段。另一个字段g r _ p a s s w d则由S V R 4和4.3+B S D支持。字段g r _ m e m是一个指针数组,其中的指针各指向一个属于该组的用户名。该数组以 n u l l结尾。可以用下列两个由P O S I X.1定义的函数来查看组名或数值组I D。#include#include struct group*getgrgid(gid_t g i

50、d);struct group*getgrnam(const char*n a m e);两个函数返回:若成功则为指针,若出错则为 N U L L如同对口令文件进行操作的函数一样,这两个函数通常也返回指向一个静态变量的指针,在每次调用时都重写该静态变量。如果需要搜索整个组文件,则须使用另外几个函数。下列三个函数类似于针对口令文件的三个函数。#include#include 第6章系统数据文件和信息1 1 3下载struct group*getgrent(void);返回:若成功则为指针,若出错或到达文件尾端则为 N U L Lvoid setgrent(void);void endgrent(

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁