﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-琢思磋文轩-随笔分类-VC&amp;C++ </title><link>http://www.blogjava.net/dev2dev/category/3721.html</link><description>学问之道贵能下人求告为善，赡才之径假人所长补已之短</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 03:18:21 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 03:18:21 GMT</pubDate><ttl>60</ttl><item><title>Unix环境高级程序设计入门----文件系统的相关编程（下）</title><link>http://www.blogjava.net/dev2dev/archive/2005/12/06/22690.html</link><dc:creator>朱春雷</dc:creator><author>朱春雷</author><pubDate>Tue, 06 Dec 2005 03:29:00 GMT</pubDate><guid>http://www.blogjava.net/dev2dev/archive/2005/12/06/22690.html</guid><wfw:comment>http://www.blogjava.net/dev2dev/comments/22690.html</wfw:comment><comments>http://www.blogjava.net/dev2dev/archive/2005/12/06/22690.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/dev2dev/comments/commentRss/22690.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/dev2dev/services/trackbacks/22690.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在软件开发领域，Unix环境高级程序设计是用途十分广泛的一种编程技术。笔者愿将个人对此的学习成果，分期总结整理成系列文章与有志深造者共享。本文是第二篇（下），主要阐述与Unix文件系统相关的编程。&nbsp;&nbsp;<a href='http://www.blogjava.net/dev2dev/archive/2005/12/06/22690.html'>阅读全文</a><img src ="http://www.blogjava.net/dev2dev/aggbug/22690.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/dev2dev/" target="_blank">朱春雷</a> 2005-12-06 11:29 <a href="http://www.blogjava.net/dev2dev/archive/2005/12/06/22690.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Unix环境高级程序设计入门----文件系统的相关编程（上）</title><link>http://www.blogjava.net/dev2dev/archive/2005/11/14/19774.html</link><dc:creator>朱春雷</dc:creator><author>朱春雷</author><pubDate>Mon, 14 Nov 2005 14:20:00 GMT</pubDate><guid>http://www.blogjava.net/dev2dev/archive/2005/11/14/19774.html</guid><wfw:comment>http://www.blogjava.net/dev2dev/comments/19774.html</wfw:comment><comments>http://www.blogjava.net/dev2dev/archive/2005/11/14/19774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/dev2dev/comments/commentRss/19774.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/dev2dev/services/trackbacks/19774.html</trackback:ping><description><![CDATA[<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT><FONT size=3>Unix环境高级程序设计入门<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----文件系统的相关编程（上）</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</FONT><FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一、关于目录</FONT></P>
<P><FONT size=2>　　不管是何种操作系统，一提到其中的文件系统首先想到的自然是目录和文件。在Unix系统中一切皆可视为文件，目录是一种特殊的文件。在笔者前已发表的《Unix操作系统的入门与基础》一文中，曾介绍过用户主目录、工作目录以及绝对路径、相对路径的概念，我们也已经知道了使用pwd命令可以获得当前工作目录的绝对路径，那么如何在程序中实现类似于pwd命令的功能呢？这里需要用到getcwd()函数，它的定义是：<BR>&nbsp;&nbsp;&nbsp; <BR>　　#include &lt;unistd.h&gt;<BR>　　char* getcwd(char* buf, size_t size);&nbsp; //成功返回buf,失败返回NULL <BR>&nbsp;&nbsp;&nbsp; <BR>此函数中的第一个参数buf数组用于存放当前工作目录路径名的字符串，size是指这个buf数组最多能装多少数据，而函数返回值与放入buf中的内容是相同的。要注意的是，该缓存必须有足够的长度以容纳绝对路径名再加上一个“/0”终止字符，否则将会返回出错。<BR>　　对于目录的操作，最常见的就是打开目录、读取目录信息、关闭目录，与之对应的函数分别是：</FONT></P>
<P><FONT size=2>　　#include &lt;sys/types.h&gt;<BR>　　#include &lt;dirent.h&gt;<BR>　　DIR* opendir(const char* dirname);&nbsp;&nbsp;&nbsp; //成功时返回指针，失败返回NULL<BR>　　struct dirent* readdir(DIR* dirp);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //成功时返回指针，失败返回NULL<BR>　　int closedir(DIR* dirp);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;//成功时返回0，失败返回-1<BR>&nbsp;&nbsp; <BR>使用opendir()函数打开不存在的目录或者对目录没有访问权限，以及对普通文件使用此函数都将返回NULL。opendir()函数成功操作后将返回指向DIR结构的指针，而DIR结构用来保存被读取目录的相关信息。定义在头文件&lt;dirent.h&gt;中的dirent结构最常用到的成员是d_name，它可以保存文件名。<BR>　　现来看下面的一个例程序：<BR>　　[程序1]</FONT></P>
<P><FONT size=2>　　#include &lt;iostream&gt;<BR>　　#include &lt;unistd.h&gt;<BR>　　#include &lt;sys/types.h&gt;<BR>　　#include &lt;dirent.h&gt;<BR>　　#include &lt;errno.h&gt;<BR>　　using namespace std;<BR>　　int main()<BR>　　{<BR>　　　　DIR* dp;<BR>　　　　cout &lt;&lt; " Please enter a dir name: ";<BR>　　　　char name[255];<BR>　　　　memset(name,0x00,255);<BR>　　　　cin &gt;&gt; name;<BR>　　　　cout &lt;&lt; " ----------------- " &lt;&lt; endl;<BR>　　　　dp = opendir(name);<BR>　　　　if(dp == NULL) <BR>　　　　{<BR>　　　　　　cout &lt;&lt; errno &lt;&lt; " [" &lt;&lt;strerror(errno) &lt;&lt; "]" &lt;&lt; endl;<BR>　　　　　　return -1;<BR>　　　　}<BR>　　　　dirent* dirp;<BR>　　　　while((dirp = readdir(dp))!= NULL)<BR>　　　　{<BR>　　　　　　cout &lt;&lt; dirp-&gt;d_name &lt;&lt; endl;<BR>　　　　}<BR>　　　　closedir(dp);<BR>　　　　return 0;<BR>　　}<BR>&nbsp;<BR>　　在Unix系统中，一旦出现程序执行失败的情况，系统会自动设置一个名为errno的全局变量，用于记录错误的出错ID号。使用strerror(errno)可以获得指定错误的描述信息，不过在此之前一定要包含errno.h的头文件。编译执行程序1时，如果输入一个已存在且有访问权限的目录名，程序会列出此目录下的所有子目录名以及文件名；如果输入的是一个文件名，则会输出20 [Not a directory]；如果输入的是一个不存在的目录名，则会输出2 [No such file or directory]。<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　　　　　</FONT><FONT size=3>二、关于文件</FONT></P>
<P><FONT size=2>　　在介绍文件之前，先来引入文件描述符这一概念。文件描述符是一非负整数，内核以此来标识一个特定进程正在操作的文件。每当打开一个现存文件或创建一个新文件时，内核将向进程返回一个文件描述符，以供读、写文件时使用。对于进程而言，内核会在每个进程空间中维护一文件描述符表，所有打开的文件都将通过此表中的文件描述符来引用。<BR>　　按照规定，文件描述符0与标准输入cin相对应，1与标准输出cout相对应，2与标准出错输出cerr相对应。除此之外，文件描述符表中的每一文件描述符都对应着文件表中的一个文件表项。内核为所有已打开的文件维护一张文件表，每个文件表项包含文件状态标志（读、写、增写、同步、非阻塞等）、当前文件位移量、V节点指针，其中V节点指针指向V节点表。V节点表由V节点项组成，每一V节点项中主要包含V节点信息、I节点信息（包括文件所在的设备、文件存储在硬盘的某一扇区某一磁道等信息）、当前文件长度等。在Unix系统中，一个文件对应着V节点表中惟一的一个V节点项。也就是说，如果两个独立的进程打开同一个文件，在文件表中会有两个文件表项，但这两个文件表项都指向同一个V节点表项。图1显示了文件描述符表、文件表、V节点表三者之间的关系。<BR>&nbsp;&nbsp;&nbsp; <IMG height=266 src="http://blog.donews.com/images/blog_donews_com/dev2dev/unix2-1.jpg" width=529><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>　　如果两个独立的进程各自打开了同一文件，则文件描述符表、文件表、V节点表三者之间的关系如图2中所示。</FONT></P>
<P><FONT size=2><IMG src="http://blog.donews.com/images/blog_donews_com/dev2dev/unix2-2.jpg">&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>　　Unix系统中文件I/O只需用到5个函数：open、read、write、lseek和close。下面将一一进行介绍。<BR>&nbsp;&nbsp;&nbsp; <BR>　　1、open函数可以打开或创建一个文件，它的定义是：<BR>&nbsp;&nbsp;&nbsp; <BR>　　#include &lt;sys/types.h&gt;<BR>　　#include &lt;sys/stat.h&gt;<BR>　　#include &lt;fcntl.h&gt;<BR>　　int open(const char* pathname, int oflag, ...); //成功时返回文件描述符，失败返回-1 <BR>&nbsp;<BR>　　使用open函数成功时返回的文件描述符一定是最小的未用描述符数字。open函数的参数pathname是所要打开或创建的文件的名称，可以是相对路径，也可以是绝对路径，不给路径时默认为当前目录。参数oflag由一个或多个选择项组成，用以指明打开的模式，常用的有O_RDONLY（只读）、O_WRONLY（只写）、O_RDWR（可读可写），此三项互相排斥，我们使用时应该指定为其中的一项。此外，还有以下一些选择项可以选择：O_APPEND（每次写入的数据都加到文件的尾端）、O_CREAT（若文件不存在则创建它；使用此选择项时，需同时提供第三个参数来说明该新文件的存取权限）、O_EXCL（与O_CREAT一起使用时，文件不存在则创建文件，若文件已存在则报错）。open函数的第三个参数写为...，是因为仅当创建新文件时才使用此参数，用以表示所创建文件的访问权限，如0700代表只有用户自身可读可写可执行，其他人无权访问。<BR>　　[程序2]<BR>&nbsp;<BR>　　#include &lt;iostream&gt;<BR>　　#include &lt;unistd.h&gt;<BR>　　#include &lt;sys/types.h&gt;<BR>　　#include &lt;sys/stat.h&gt;<BR>　　#include &lt;fcntl.h&gt;<BR>　　#include &lt;errno.h&gt;<BR><BR>　　</FONT><FONT size=2>using namespace std;</FONT></P>
<P><FONT size=2>　　int main()<BR>　　{<BR>　　　　int i = open("info.txt",O_RDWR);<BR>　　　　cout &lt;&lt; "fd = " &lt;&lt; i &lt;&lt; endl;</FONT></P>
<P><FONT size=2>　　　　int ii = open("new.txt", O_CREAT|O_EXCL,0700);<BR>　　　　if(ii&lt;0) cout &lt;&lt; strerror(errno) &lt;&lt; endl;<BR>　　　　else&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; "Create file successful" &lt;&lt; endl;</FONT></P>
<P><FONT size=2>　　　　return 0;<BR>　　}<BR>&nbsp;<BR>　　假设当前目录中已存在文件info.txt，编译执行程序2，大家会看到如下的运行结果：<BR>&nbsp;<BR>　　fd = 3<BR>　　Create file successful<BR>&nbsp;<BR>　　再次执行程序2，运行结果则是：<BR>&nbsp;<BR>　　fd = 3<BR>　　File exists<BR>&nbsp;&nbsp;<BR>　　2、close函数可以关闭一个打开的文件，它的定义是：<BR>&nbsp;<BR>　　#include &lt;unistd.h&gt; <BR>　　int close (int fd)； //成功返回0，失败返回-1 <BR>&nbsp;<BR>　　使用close函数关闭一个文件的同时也会释放该文件上的所有记录锁。不过，当一个进程终止时，它所有的打开文件都由内核自动关闭，因此程序通常利用这一特性而不用显式调用close来关闭文件了。<BR>&nbsp;<BR>　　3、read函数可以从打开的文件中读取数据，它的定义是：<BR>&nbsp;<BR>　　#include &lt;unistd.h&gt;<BR>　　ssize_t read(int fd, void* buf, size_t nbytes); //成功时返回真正读取的字节数，失败则返回-1<BR>&nbsp;<BR>　　read函数是通过给定的文件描述符，在文件描述符所对应的文件表项中获得当前文件位移量，然后从文件的当前位移量处开始，读取给定长度的数据放入buf中。<BR>&nbsp;<BR>　　4、write函数可以向打开的文件中写入数据，它的定义是：<BR>&nbsp;<BR>　　#include &lt;unistd.h&gt;<BR>　　ssize_t write(int fd, void* buf, size_t nbytes); //成功时返回真正写入的字节数，失败则返回-1 <BR>&nbsp;<BR>　　write函数是从文件的当前位移量处开始，将buf中的数据写入文件中。下面将程序2中的代码做一修改，并假设info.txt文件中已有数据ABCDE。<BR>　　[程序3]<BR>&nbsp;<BR>　　#include &lt;iostream&gt;<BR>　　#include &lt;unistd.h&gt;<BR>　　#include &lt;sys/types.h&gt;<BR>　　#include &lt;sys/stat.h&gt;<BR>　　#include &lt;fcntl.h&gt;<BR>　　#include &lt;errno.h&gt;</FONT></P>
<P><FONT size=2>　　using namespace std;</FONT></P>
<P><FONT size=2>　　int main()<BR>　　{<BR>　　　　int i = open("info.txt",O_RDWR);<BR>　　　　cout &lt;&lt; "fd = " &lt;&lt; i &lt;&lt; endl;</FONT></P>
<P><FONT size=2>　　　　int ii = open("New.txt", O_CREAT|O_EXCL,0700);<BR>　　　　if(ii&lt;0) cout &lt;&lt; strerror(errno) &lt;&lt; endl;<BR>　　　　else&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; "Create file successful" &lt;&lt; endl;</FONT></P>
<P><FONT size=2>　　　　char buf[500];<BR>　　　　memset(buf,0x00,500);<BR>　　　　cout &lt;&lt; read(i,buf,500) &lt;&lt; "\t";<BR>　　　　cout &lt;&lt; buf &lt;&lt; endl;<BR>　　　　memset(buf,0x00,500);<BR>　　　　read(0,buf,500);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //从键盘读取数据<BR>　　　　cout &lt;&lt; write(1,buf,strlen(buf)) &lt;&lt; endl;&nbsp; //向屏幕写入数据<BR>　　　　return 0;<BR>　　}<BR>&nbsp; <BR>　　编译执行程序3，大家会看到如下的运行结果：<BR>&nbsp;<BR>　　fd = 3<BR>　　File exists<BR>　　6&nbsp;&nbsp;&nbsp; ABCDE<BR>　　12345&nbsp;&nbsp; (键盘输入)<BR>　　12345<BR>　　6<BR>&nbsp;<BR>　　5、lseek函数可以对一个打开文件的当前位移量进行显式地调整，它的定义是： <BR>&nbsp;<BR>　　#include &lt;sys/types.h&gt;<BR>　　#include &lt;unistd.h&gt;<BR>　　off_t lseek(int fd, off_t offset, int whence);&nbsp; //成功时返回新的文件位移量，失败则返回-1 <BR>&nbsp;<BR>　　在上文中我们已多次提到“当前文件位移量”这一名词，这里再作一简要解释。当前文件位移量是一非负整数，用以度量从文件开始处到某一位置的字节数。每个打开文件，都有一个与其相关联的“当前文件位移量”。打开一个文件时，除非指定O_APPEND选择项，否则系统默认该位移量被设置为0。通常，读、写操作都从当前文件位移量处开始，并使位移量增加所读或写的字节数。lseek函数中的第二参数是表示将要移动的字节数，第三个参数是移动的策略。若whence是SEEK_SET，则将该文件的位移量设置为距文件开始处offset个字节；若whence是SEEK_CUR，则将该文件的位移量设置为其当前值加offset, offset，可为正或负；若whence是SEEK_END，则将该文件的位移量设置为文件长度加offset, offset，可为正或负。<BR>　　大家可能会对上述中的一种情况感兴趣，那就是采用SEEK_END再往后移动会出现怎么样的情况呢？带着这个问题，我们对程序3做如下修改。<BR>　　[程序4]<BR>&nbsp;<BR>　　#include &lt;iostream&gt;<BR>　　#include &lt;unistd.h&gt;<BR>　　#include &lt;sys/types.h&gt;<BR>　　#include &lt;sys/stat.h&gt;<BR>　　#include &lt;fcntl.h&gt;<BR>　　#include &lt;errno.h&gt;</FONT></P>
<P><FONT size=2>　　using namespace std;</FONT></P>
<P><FONT size=2>　　int main()<BR>　　{<BR>　　　　int i = open("info.txt",O_RDWR);<BR>　　　　cout &lt;&lt; "fd = " &lt;&lt; i &lt;&lt; endl;</FONT></P>
<P><FONT size=2>　　　　int ii = open("New.txt", O_CREAT|O_EXCL,0700);<BR>　　　　if(ii&lt;0) cout &lt;&lt; strerror(errno) &lt;&lt; endl;<BR>　　　　else cout &lt;&lt; "Create file successful" &lt;&lt; endl;</FONT></P>
<P><FONT size=2>　　　　char buf[500];<BR>　　　　memset(buf,0x00,500);<BR>　　　　cout &lt;&lt; read(i,buf,500) &lt;&lt; "\t";<BR>　　　　cout &lt;&lt; buf &lt;&lt; endl;<BR>　　　　memset(buf,0x00,500);<BR>　　　　read(0,buf,500);<BR>　　　　cout &lt;&lt; write(1,buf,strlen(buf)) &lt;&lt; endl;<BR>&nbsp;<BR>　　　　errno = 0;<BR>　　　　lseek(i,3,SEEK_SET);&nbsp;&nbsp; //lseek(i,-3,SEEK_SET); //lseek(i,3,SEEK_END);<BR>　　　　if(errno != 0) cout &lt;&lt; strerror(errno) &lt;&lt; endl;<BR>　　　　write(i,buf,strlen(buf)-1);&nbsp; //strlen(buf)-1表示去掉回车<BR>　　　　return 0;<BR>　　}<BR>&nbsp;<BR>　　编译执行程序4，运行结果是（info.txt文件中的内容是ABCDE）：<BR>&nbsp;<BR>　　fd = 3<BR>　　File exists<BR>　　6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ABCDE<BR>　　abcde&nbsp;（键盘输入）<BR>　　abcde<BR>　　6<BR>&nbsp;<BR>　　使用命令cat info.txt查看文件中的内容是：ABCabcde。<BR>　　如果将程序中lseek语句换成“lseek(i,-3,SEEK_SET);”，重新编译执行。我们在键盘输入12345后，会提示Invalid argument。不过查看info.txt文件，会发现其中的内容已变成：ABCabcde12345。由此可知，负数不能与SEEK_SET一起使用，系统在提示错误的同时，会自动将写入的数据追加在文件末尾。<BR>　　把程序中的lseek语句再改成lseek(i,3,SEEK_END)，重新编译执行。由键盘输入xyz后，查看文件内容是ABCabcde12345xyz，但会发现文件的大小由13变成19。使用命令od -c info.txt，会发现在xyz之前有三个\0。由此可知，在文件结尾处继续向后移动，如果写入数据则会自动在此前补入\0，这种文件被称为空洞文件。</FONT></P><img src ="http://www.blogjava.net/dev2dev/aggbug/19774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/dev2dev/" target="_blank">朱春雷</a> 2005-11-14 22:20 <a href="http://www.blogjava.net/dev2dev/archive/2005/11/14/19774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Unix环境高级程序设计入门----Unix环境及系统数据信息使用</title><link>http://www.blogjava.net/dev2dev/archive/2005/08/17/10380.html</link><dc:creator>朱春雷</dc:creator><author>朱春雷</author><pubDate>Wed, 17 Aug 2005 14:16:00 GMT</pubDate><guid>http://www.blogjava.net/dev2dev/archive/2005/08/17/10380.html</guid><wfw:comment>http://www.blogjava.net/dev2dev/comments/10380.html</wfw:comment><comments>http://www.blogjava.net/dev2dev/archive/2005/08/17/10380.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/dev2dev/comments/commentRss/10380.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/dev2dev/services/trackbacks/10380.html</trackback:ping><description><![CDATA[<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Unix环境高级程序设计入门<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----Unix环境及系统数据信息使用</FONT>&nbsp;&nbsp; </FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　Unix环境高级程序设计是一种难度较大的编程技术，它在软件开发领域的用途十分广泛。目前虽处于“曲高和寡”的状况，但却是程序员不断进阶的“利器”。笔者愿将个人对此的学习所得，分期总结整理后与有志深造者共享互助，携手前行。这一系列文章拟从整体上有着连贯性，而各篇又有相对独立性。文章中若涉及到前已发布的《Unix操作系统的入门与基础》、《Unix的轻巧“约取而实得”》中已解释的知识点，将不再赘述。读者阅读中如果遇到还不甚明了的概念，可以查阅本专栏中的上述文章。同时，本系列文章假设读者已熟悉C++语法，故不对涉及C++语法方面的知识进行解释。文中的代码已在SunOS 5.8中测试通过。</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT><FONT size=3>&nbsp;一、在程序中使用环境变量</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　我们已经知道，环境变量可以用于定制用户的工作环境，即用环境变量可以保存用户对系统进行设置的信息。要想查看系统已定义的所有环境变量，可以使用setenv命令。那么，如何在程序中操作环境变量呢？<BR>&nbsp;&nbsp;&nbsp;　 在系统为每个程序分配的空间中，都会自动保存一份系统环境变量的拷贝。ANSI C中定义的两个函数getenv()和petenv()，分别可以用于获取环境变量的值和设置环境变量的值。这两个函数的定义分别是：</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　#include &lt;stdlib.h&gt;<BR>&nbsp;&nbsp;&nbsp; 　char* getenv(const char* name); //成功返回指向环境变量值的指针，失败返回NULL<BR>&nbsp;&nbsp;&nbsp; 　int putenv(const char* str);&nbsp;&nbsp;&nbsp; //成功返回0，失败返回-1</FONT></P>
<P><FONT size=2>我们通常使用getenv()从环境中获取一个指定的环境变量的值，使用putenv()来改变现有环境变量的值，或增加新的环境变量。putenv()参数形式为“name = value”的字符串，如果name指定的环境变量已经存在，则先删除其原来的定义再赋新值；如果不存在，则增加此环境变量。<BR>&nbsp;&nbsp;&nbsp; 　现来看下面的一个例程序：<BR>&nbsp;&nbsp;&nbsp;　 [程序1]</FONT></P>
<P><FONT size=2>&nbsp;　　#include &lt;stdlib.h&gt;<BR>&nbsp;　　#include &lt;iostream&gt;<BR>&nbsp;　　using namespace std;</FONT></P>
<P><FONT size=2>&nbsp;　　int main()<BR>&nbsp;　　{<BR>&nbsp;&nbsp;　　　char* env = new char[255];<BR>&nbsp;&nbsp;　　　char* a = getenv("ABCDE");<BR>&nbsp;&nbsp;　　　if(a!=NULL)<BR>&nbsp;&nbsp;&nbsp;　　　　strcpy(env,a);<BR>&nbsp;&nbsp;　　　cout &lt;&lt; "env: " &lt;&lt; env &lt;&lt; endl;<BR>&nbsp;　　　&nbsp;if(putenv("ABCDE=aaa")&lt;0)<BR>&nbsp;&nbsp;&nbsp;　　　　exit(-1);<BR>&nbsp;&nbsp;　　　strcpy(env,getenv("ABCDE"));<BR>&nbsp;&nbsp;　　　cout &lt;&lt; "--------------------" &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　cout &lt;&lt; "env: " &lt;&lt; env &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　delete env;<BR>&nbsp;&nbsp;　　　return 0;<BR>　&nbsp;　}</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　运行此程序，将会在程序空间的环境变量表中增加一个新的环境变量ABCDE，其值为aaa。请注意，程序是在系统为其所分配空间的环境变量表中增加环境变量，而不是在系统的环境变量表中增加环境变量。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　　　　　</FONT><FONT size=3>二、获取系统相关信息</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　在Unix系统中定义的uname()函数，可以返回与主机和操作系统相关的信息。uname()的定义是：<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp; 　#include &lt;sys/utsname.h&gt;<BR>&nbsp;&nbsp;&nbsp; 　int uname(struct utsname* name);&nbsp; //成功返回非负值，出错返回-1 </FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　通过参数向其传递一个utsname结构的地址，uname函数将负责填写此结构。utsname结构定义如下：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;　　struct utsname {<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　　char sysname[9]; &nbsp;// name of the operating system<BR>&nbsp;&nbsp;　　　char nodename[9]; &nbsp;// name of this node<BR>&nbsp;&nbsp;　　　char release[9]; &nbsp;// current release of operating system<BR>&nbsp;&nbsp;　　　char version[9]; &nbsp;// current version of this release<BR>&nbsp;&nbsp;　　　char machine[9]; &nbsp;// name of hardware type<BR>　　&nbsp;}; &nbsp;</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　utsname结构中的信息可用“uname -a”命令来显示，在程序中则可以如下方式来查看。<BR>&nbsp;&nbsp;&nbsp; 　[程序2]<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;　 #include &lt;sys/utsname.h&gt;<BR>&nbsp;　　#include &lt;iostream&gt;<BR>&nbsp;　　using namespace std;<BR>&nbsp;<BR>&nbsp;　　#define ERR_QUIT(arg) {cout &lt;&lt; #arg &lt;&lt; endl; exit(-1);}<BR>&nbsp;<BR>&nbsp;　　int main()<BR>&nbsp;　　{<BR>&nbsp;　　　&nbsp;struct utsname buf;<BR>&nbsp;&nbsp;　　　if(uname(&amp;buf) &lt; 0) ERR_QUIT("uname error");<BR>&nbsp;&nbsp;　　　cout &lt;&lt; "Operation System Name: " &lt;&lt; buf.sysname &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　cout &lt;&lt; "Node Name&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : " &lt;&lt; buf.nodename &lt;&lt; endl;<BR>&nbsp;　　　&nbsp;cout &lt;&lt; "O.S. release level&nbsp;&nbsp; : " &lt;&lt; buf.release &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　cout &lt;&lt; "O.S. Version level&nbsp;&nbsp; : " &lt;&lt; buf.version &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　cout &lt;&lt; "Hardware type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : " &lt;&lt; buf.machine &lt;&lt; endl;<BR>&nbsp;　　　&nbsp;return 0;&nbsp;<BR>&nbsp;　　}</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　我们曾经介绍过使用hostname命令来获取主机名，而要在程序中得到主机名，则可以调用Unix系统提供的gethostname()函数，其实hostname命令就是调用了gethostname()函数。</FONT></P>
<P><FONT size=2>&nbsp;　　#include &lt;unistd.h&gt;<BR>　　&nbsp;int gethostname(char* name, int namelen); //成功返回0，失败返回-1 <BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp; 　[程序3]<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;　　#include &lt;iostream&gt;<BR>&nbsp;　　#include &lt;unistd.h&gt;<BR>&nbsp;　　#include &lt;errno.h&gt;<BR>&nbsp;　　using namespace std;<BR>&nbsp;<BR>&nbsp;　　#define ERR_QUIT(arg) {cout &lt;&lt; #arg &lt;&lt; endl; exit(-1);}</FONT></P>
<P><FONT size=2>&nbsp;　　int main()<BR>&nbsp;　　{<BR>&nbsp;&nbsp;　　　char buf[256];<BR>&nbsp;&nbsp;　　　if(gethostname(buf,255)&lt;0) <BR>&nbsp;　　　&nbsp;{<BR>&nbsp;&nbsp;&nbsp;　　　　cout &lt;&lt; "ERR: " &lt;&lt; strerror(errno) &lt;&lt; " : " &lt;&lt; errno &lt;&lt; endl;&nbsp; <BR>&nbsp;&nbsp;&nbsp;　　　　ERR_QUIT("gethostname error.");<BR>&nbsp;&nbsp;　　　}<BR>&nbsp;&nbsp;　　　cout &lt;&lt; "HostName is : " &lt;&lt; buf &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　return 0;<BR>&nbsp;　　}</FONT></P>
<P><BR><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　　　　　</FONT><FONT size=3>三、获取用户相关信息</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　用户每次都需凭一个经系统确认的用户名登录系统，而每个用户名在系统中将对应一个惟一的用户ID。用户ID是一个数值，它用于向系统标识各个不同的用户。在程序中如果想要获得用户的登录名以及用户ID号，可以使用如下的函数：</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;　 #include&lt;pwd.h&gt;<BR>&nbsp;&nbsp;&nbsp; 　char* getlogin();&nbsp;&nbsp; //得到用户登录名<BR>&nbsp;&nbsp;&nbsp; 　int getuid();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到当前登录用户的用户ID号<BR>&nbsp;&nbsp;&nbsp; 　int geteuid();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到当前运行该进程的有效用户ID号<BR>&nbsp;&nbsp;&nbsp; 　struct passwd* getpwuid(int userid); //得到一个指向passwd结构的指针，该结构中包含用户相关信息的记录<BR>&nbsp;&nbsp;&nbsp; 　passwd的结构如下表所示：<BR>&nbsp;&nbsp;&nbsp;&nbsp;　 
<TABLE cellSpacing=0 cellPadding=0 border=1>
<TBODY>
<TR>
<TD vAlign=top width=177>
<DIV align=center>说明</DIV></TD>
<TD vAlign=top width=236>
<DIV align=center>struct passwd成员</DIV></TD></TR>
<TR>
<TD vAlign=top width=177>
<DIV>用户名</DIV></TD>
<TD vAlign=top width=236>
<DIV>char* pw_name</DIV></TD></TR>
<TR>
<TD vAlign=top width=177>
<DIV>密码</DIV></TD>
<TD vAlign=top width=236>
<DIV>char* pw_passwd</DIV></TD></TR>
<TR>
<TD vAlign=top width=177>
<DIV>用户ID</DIV></TD>
<TD vAlign=top width=236>
<DIV>uid_t pw_uid</DIV></TD></TR>
<TR>
<TD vAlign=top width=177>
<DIV>组ID</DIV></TD>
<TD vAlign=top width=236>
<DIV>gid_t pw_gid</DIV></TD></TR>
<TR>
<TD vAlign=top width=177>
<DIV>注释</DIV></TD>
<TD vAlign=top width=236>
<DIV>char* pw_gecos</DIV></TD></TR>
<TR>
<TD vAlign=top width=177>
<DIV>工作目录</DIV></TD>
<TD vAlign=top width=236>
<DIV>char* pw_dir</DIV></TD></TR>
<TR>
<TD vAlign=top width=177>
<DIV>初始shell</DIV></TD>
<TD vAlign=top width=236>
<DIV>char* pw_shell</DIV></TD></TR></TBODY></TABLE><BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp; 　当用户登录后，系统会分配给每个用户一个组ID，它也是一个数值。一般来说，在Unix中的组可被用于将若干用户集合到某个课题或部门中去，这种机制允许同组的各个成员之间共享资源(例如文件)。当然，一个用户可以参加多个课题或项目，因此也就可以同时属于多个组。在程序中如果想要获得用户所属的组ID号，可以使用如下的函数：</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　#include &lt;grp.h&gt;<BR>&nbsp;&nbsp;&nbsp; 　int getgid();&nbsp;&nbsp; //得到当前登录用户的组ID号<BR>&nbsp;&nbsp;&nbsp; 　int getegid();&nbsp; //得到当前运行该进程的有效用户的组ID号<BR>&nbsp;&nbsp;&nbsp; 　struct group* getpgrgid(int groupid);&nbsp; //得到一个指向group结构的指针，该结构中包含用户组相关信息的记录<BR>&nbsp;&nbsp;&nbsp;　 group的结构如下表所示：<BR>&nbsp;&nbsp;&nbsp;&nbsp;　 
<TABLE cellSpacing=0 cellPadding=0 border=1>
<TBODY>
<TR>
<TD vAlign=top width=211>
<DIV align=center>说明</DIV></TD>
<TD vAlign=top width=204>
<DIV align=center>struct group成员</DIV></TD></TR>
<TR>
<TD vAlign=top width=211>
<DIV>组名</DIV></TD>
<TD vAlign=top width=204>
<DIV>char* gr_name</DIV></TD></TR>
<TR>
<TD vAlign=top width=211>
<DIV>密码</DIV></TD>
<TD vAlign=top width=204>
<DIV>char* gr_passwd</DIV></TD></TR>
<TR>
<TD vAlign=top width=211>
<DIV>组ID</DIV></TD>
<TD vAlign=top width=204>
<DIV>int gr_gid</DIV></TD></TR>
<TR>
<TD vAlign=top width=211>
<DIV>指向各用户名的指针数组（其中各指针指向该组的用户名，数组以null结尾）</DIV></TD>
<TD width=204>
<DIV>char** gr_mem </DIV></TD></TR></TBODY></TABLE>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp; 　来看下面的例程序：<BR>&nbsp;&nbsp;&nbsp;　 [程序4]<BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;　#include &lt;pwd.h&gt;<BR>　&nbsp;#include &lt;grp.h&gt;<BR>&nbsp;　#include &lt;unistd.h&gt;<BR>　&nbsp;#include &lt;iostream&gt;<BR>&nbsp;　using namespace std;<BR>&nbsp;　int main()<BR>　&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　struct passwd* pwd;<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　cout &lt;&lt; "Login name: " &lt;&lt; getlogin() &lt;&lt; endl;<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　pwd = getpwuid(getuid());<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　if(pwd)<BR>&nbsp;&nbsp;　　　&nbsp;cout &lt;&lt; "Real user: " &lt;&lt; getuid() &lt;&lt; "(" &lt;&lt; pwd-&gt;pw_name &lt;&lt;")" &lt;&lt; endl;<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　pwd = getpwuid(geteuid());<BR>&nbsp;&nbsp;&nbsp;&nbsp;　 if(pwd)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;　cout &lt;&lt; "Effective user: " &lt;&lt; pwd-&gt;pw_name &lt;&lt; endl;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　struct group* grp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　grp = getgrgid(getgid());<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　if(grp)<BR>&nbsp;&nbsp;&nbsp;　　　cout &lt;&lt; "Real group: " &lt;&lt; getgid() &lt;&lt; "(" &lt;&lt; grp-&gt;gr_name &lt;&lt; ")" &lt;&lt; endl;<BR>&nbsp;&nbsp;　　grp = getgrgid(getegid());<BR>&nbsp;&nbsp;&nbsp;&nbsp; 　if(grp) <BR>&nbsp;&nbsp;&nbsp;　　　cout &lt;&lt; "Effective group: " &lt;&lt; grp-&gt;gr_name &lt;&lt; endl;<BR>&nbsp;}<BR>&nbsp;<BR>　　通常情况下，有效用户ID等于登录用户ID，有效组ID等于登录用户组ID。但是如果某一文件的拥有者通过命令“chmod u+s filename”设置了一个特殊标志位，则任何运行此文件的用户有效ID都将变为该程序拥有者的ID。这一特殊标志位被称为设置-用户-ID(set-user-ID)位，其定义是“当执行此文件时，将进程的有效用户ID设置为文件的所有者”。类似的，还可以通过设置使得执行此文件的进程有效组ID变为文件所有者的组ID。<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　　　　 </FONT><FONT size=3>四、时间与日期的使用</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　由Unix内核提供的基本时间服务是自1970年1月1日00:00:00以来国际标准时间（UTC）所经过的秒数累计值，通常被称为日历时间。日历时间包括时间和日期，这种秒数是以数据类型time_t来表示的（在Solaris中time_t等同于long）。我们可以使用time()函数来获得表示当前时间和日期的秒值。time()函数的定义是：</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;　 #include &lt;time.h&gt;<BR>&nbsp;&nbsp;&nbsp; 　time_t time(time_t* mem);&nbsp; //成功则返回时间值，出错返回-1；如果参数非NULL，则时间值也存放在由mem指向的单元内</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　因此，time()函数常有如下两种使用方法：<BR>&nbsp;&nbsp;&nbsp; 　（1）&nbsp;time_t now1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;　　time(&amp;now1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;　　cout &lt;&lt; now1 &lt;&lt; endl;<BR>&nbsp;&nbsp;&nbsp; 　（2）&nbsp;time_t now2;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　now2=time(NULL);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　cout &lt;&lt; now2 &lt;&lt; endl;<BR>&nbsp;&nbsp;&nbsp;　 [程序5]<BR>&nbsp;&nbsp;&nbsp; <BR>　&nbsp;　#include &lt;time.h&gt; //#include &lt;ctime&gt;<BR>&nbsp;　　#include &lt;iostream&gt;<BR>&nbsp;　　using namespace std;<BR>&nbsp;<BR>&nbsp;　　int main()<BR>&nbsp;　　{<BR>&nbsp;　　&nbsp;　time_t now;<BR>&nbsp;&nbsp;　　　if(time(&amp;now)&lt;0)<BR>&nbsp;&nbsp;　　　{<BR>&nbsp;&nbsp;&nbsp;　　　　cout &lt;&lt; "error" &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　　&nbsp;exit(-1);<BR>&nbsp;&nbsp;　　　}<BR>&nbsp;&nbsp;　　　cout &lt;&lt; now &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　cout &lt;&lt; ctime(&amp;now) &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　exit(0);<BR>&nbsp;　　}<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 　由于这种秒值的表示方式，不能直观的让人们明白当前的时间，因此通常需要再调用其它的时间函数来将其转换为人们可读的时间与日期。图1说明了各种时间函数之间的关系。<BR><BR></FONT><FONT size=2><IMG height=269 alt="" src="http://blog.donews.com/images/blog_csdn_net/dev2dev/1_1time.jpg" width=363><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　　　&nbsp;（图1）<BR></FONT><FONT size=2>&nbsp;&nbsp;&nbsp; 　函数localtime()和gmtime()可以将秒值转换成以年、月、日、时、分、秒、周日表示的时间，并将这些存放在一个tm结构中。tm结构定义如下：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;　　struct tm { <BR>&nbsp;&nbsp;　　　int tm_sec;&nbsp;&nbsp;&nbsp;&nbsp; /* seconds after the minute: [0, 61] */<BR>&nbsp;&nbsp;　　　int tm_min;&nbsp;&nbsp;&nbsp;&nbsp; /* minutes after the hour: [0, 59] */<BR>&nbsp;&nbsp;　　　int tm_hour;&nbsp;&nbsp;&nbsp; /* hours after midnight: [0, 23] */<BR>&nbsp;&nbsp;　　　int tm_mday;&nbsp;&nbsp;&nbsp; /* day of the month: [1, 31] */<BR>&nbsp;　　　&nbsp;int tm_mon;&nbsp;&nbsp;&nbsp;&nbsp; /* month of the year: [0, 11] */<BR>&nbsp;&nbsp;　　　int tm_year;&nbsp;&nbsp;&nbsp; /* years since 1900 */<BR>&nbsp;　　　&nbsp;int tm_wday;&nbsp;&nbsp;&nbsp; /* days since Sunday: [0, 6] */<BR>&nbsp;&nbsp;　　　int tm_yday;&nbsp;&nbsp;&nbsp; /* days since January 1: [0, 365] */<BR>&nbsp;　　　&nbsp;int tm_isdst;&nbsp;&nbsp; /* daylight saving time flag: &lt;0, 0, &gt;0 */<BR>&nbsp;　　};</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　localtime()和gmtime()之间的区别是:localtime将日历时间转换成本地时间(考虑到本地时区和夏时制标志)，而gmtime则将日历时间转换成国际标准时间的年、月、日、时、分、秒、周日。它们的定义如下：</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　struct tm* gmtime(const time_t* mem); <BR>&nbsp;&nbsp;&nbsp; 　struct tm* localtime(const time_t* mem); </FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　函数mktime()则正好相反，它是以存放有本地时间年、月、日等的tm结构作为参数，将其转换成time_t类型的秒值。mktime()函数的定义是：<BR></FONT><FONT size=2><BR>&nbsp;&nbsp;&nbsp;　 time_t mktime(struct tm* tmptr);&nbsp; //成功返回日历时间，失败则返回-1&nbsp;<BR></FONT><FONT size=2><BR>&nbsp;&nbsp;&nbsp; 　函数asctime()和ctime()可以获得人们可读的时间字符串，表示形式如同使用date命令所获得的系统默认的时间输出形式。它们的定义如下：</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;　 char* asctime(const struct tm* tmptr);&nbsp;//参数是指向存放有本地时间年、月、日等的tm结构的指针<BR>&nbsp;&nbsp;&nbsp; 　char* ctime(const time_t* mem);&nbsp;&nbsp; //参数是指向日历时间的指针</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　函数strftime()是最为复杂的时间函数，可用于用户自定义时间的表示形式。函数strftime()的定义如下：</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　size_t strftime(char* buf, size_t maxsize, const char* format, <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const struct tm* tmptr);&nbsp; //有空间则返回所存入数组的字符数，否则为0 </FONT></P>
<P><FONT size=2>自定义格式的结果存放在一个长度为maxsize的buf数组中，如果buf数组长度足以存放格式化结果及一个null终止符，则该函数返回在buf数组中存放的字符数(不包括null终止符)，否则该函数返回0。format参数用于控制自定义时间的表示格式，格式的定义是在百分号之后跟一个特定字符，format中的其他字符则按原样输出。其中特别应注意的是，两个连续的百分号则是表示输出一个百分号。常用的定义格式如下表所示。<BR>
<TABLE cellSpacing=0 cellPadding=0 width=510 border=0>
<TBODY>
<TR>
<TD vAlign=top width=56>
<DIV align=center>格式</DIV></TD>
<TD vAlign=top width=243>
<DIV align=center>说明</DIV></TD>
<TD vAlign=top width=211>
<DIV align=center>例子</DIV></TD></TR>
<TR>
<TD vAlign=top width=56>
<DIV>% a</DIV></TD>
<TD vAlign=top width=243>
<DIV>缩写的周日名</DIV></TD>
<TD vAlign=top width=211>
<DIV>Tue</DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% A </DIV></TD>
<TD width=243>
<DIV>全周日名</DIV></TD>
<TD width=211>
<DIV>Tuesday </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% b </DIV></TD>
<TD width=243>
<DIV>缩写的月名</DIV></TD>
<TD width=211>
<DIV>Jan </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% B </DIV></TD>
<TD width=243>
<DIV>月全名</DIV></TD>
<TD width=211>
<DIV>January </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% c </DIV></TD>
<TD width=243>
<DIV>日期和时间</DIV></TD>
<TD width=211>
<DIV>Wed Aug 17 19:40:30 2005 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% d </DIV></TD>
<TD width=243>
<DIV>月日：[01, 31] </DIV></TD>
<TD width=211>
<DIV>14 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% H </DIV></TD>
<TD width=243>
<DIV>小时（每天2 4小时）：[00, 23] </DIV></TD>
<TD width=211>
<DIV>19 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% I </DIV></TD>
<TD width=243>
<DIV>小时（上、下午各1 2小时[01, 12] </DIV></TD>
<TD width=211>
<DIV>07 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% j </DIV></TD>
<TD width=243>
<DIV>年日：[001, 366] </DIV></TD>
<TD width=211>
<DIV>014 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% m </DIV></TD>
<TD width=243>
<DIV>月：[01, 12] </DIV></TD>
<TD width=211>
<DIV>01 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% M </DIV></TD>
<TD width=243>
<DIV>分：[00, 59] </DIV></TD>
<TD width=211>
<DIV>40 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% p </DIV></TD>
<TD width=243>
<DIV>A M / P M </DIV></TD>
<TD width=211>
<DIV>PM </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% S </DIV></TD>
<TD width=243>
<DIV>秒:[00, 61] </DIV></TD>
<TD width=211>
<DIV>30 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% U </DIV></TD>
<TD width=243>
<DIV>星期日周数：[00, 53] </DIV></TD>
<TD width=211>
<DIV>02 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% w </DIV></TD>
<TD width=243>
<DIV>周日：[ 0 =星期日，6 ] </DIV></TD>
<TD width=211>
<DIV>2</DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% W </DIV></TD>
<TD width=243>
<DIV>星期一周数：[00, 53] </DIV></TD>
<TD width=211>
<DIV>02 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% x </DIV></TD>
<TD width=243>
<DIV>日期</DIV></TD>
<TD width=211>
<DIV>08/17/05</DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% X </DIV></TD>
<TD width=243>
<DIV>时间</DIV></TD>
<TD width=211>
<DIV>19:40:30</DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% y </DIV></TD>
<TD width=243>
<DIV>不带公元的年：[00, 991] </DIV></TD>
<TD width=211>
<DIV>05 </DIV></TD></TR>
<TR>
<TD width=56>
<DIV>% Y </DIV></TD>
<TD width=243>
<DIV>带公元的年</DIV></TD>
<TD width=211>
<DIV>2005</DIV></TD></TR>
<TR>
<TD vAlign=bottom width=56>
<DIV>% Z </DIV></TD>
<TD vAlign=bottom width=243>
<DIV>时区名</DIV></TD>
<TD vAlign=bottom width=211>
<DIV>MST </DIV></TD></TR></TBODY></TABLE></FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 　下面再来看一个具体程序。在此程序中，如提供一个秒值作参数，则会依照自定义的格式输出日期与时间信息；如提供一个以年、月、日、时、分、秒为格式的参数，则会输出从1970年1月1日00:00:00以来国际标准时间（UTC）所经过的秒数累计值。<BR>&nbsp;&nbsp;&nbsp; 　[程序6]<BR>&nbsp;&nbsp;&nbsp; <BR>　　&nbsp;#include &lt;time.h&gt;<BR>&nbsp;　　#include &lt;iostream&gt;<BR>&nbsp;　　#include &lt;string&gt;<BR>&nbsp;　　#include &lt;cstdio&gt;<BR>&nbsp;　　#include &lt;stdexcept&gt;<BR>&nbsp;　　using namespace std;<BR>&nbsp;<BR>　　&nbsp;void usage(char * proc)<BR>　　&nbsp;{<BR>&nbsp;&nbsp;　　　cout &lt;&lt; " Usage: 1." &lt;&lt; proc &lt;&lt; " [YYYY MM DD HH MM SS] (HH -- In 24 hours format)" &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　cout &lt;&lt; "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2." &lt;&lt; proc &lt;&lt; " [time in seconds]" &lt;&lt; endl;<BR>&nbsp;　　}&nbsp;&nbsp;<BR>&nbsp;<BR>　　&nbsp;int main(int argc, char* argv[])<BR>&nbsp;　　{<BR>&nbsp;　　　&nbsp;if(argc == 1)<BR>&nbsp;　　　&nbsp;{<BR>&nbsp;&nbsp;&nbsp;　　　　usage(argv[0]);<BR>&nbsp;&nbsp;&nbsp;　　　　return EXIT_FAILURE;<BR>&nbsp;&nbsp;　　　}<BR>&nbsp;<BR>&nbsp;　　　&nbsp;if(argc == 2) //Case in 2<BR>&nbsp;&nbsp;　　　{<BR>&nbsp;&nbsp;　　　　&nbsp;long transfer;<BR>&nbsp;&nbsp;&nbsp;　　　　transfer = atol(argv[1]);<BR>&nbsp;　　　　&nbsp;&nbsp;struct tm* now = NULL;<BR>&nbsp;&nbsp;　　　　&nbsp;now = localtime(&amp;transfer);<BR>&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;　　　　char timestamp[150];<BR>&nbsp;&nbsp;&nbsp;　　　　strftime(timestamp,sizeof(timestamp),"%Y-%m-%d %H:%M:%S",now);<BR>&nbsp;&nbsp;&nbsp;　　　　strcat(timestamp," (format in: YYYY-MM-DD HH:MM:SS)");<BR>&nbsp;&nbsp;&nbsp;　　　　cout &lt;&lt; endl &lt;&lt; "\tAfter convert: " &lt;&lt; timestamp &lt;&lt; endl &lt;&lt; endl;<BR>&nbsp;&nbsp;&nbsp;　　　　return EXIT_SUCCESS;<BR>&nbsp;　　　&nbsp;}<BR>&nbsp;&nbsp;　　　else //Case in 1<BR>&nbsp;&nbsp;　　　{<BR>&nbsp;&nbsp;&nbsp;　　　　if(argc != 7)<BR>&nbsp;&nbsp;　　　　&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　usage(argv[0]);<BR>&nbsp;&nbsp;&nbsp;　　　　　&nbsp;return EXIT_FAILURE;<BR>&nbsp;&nbsp;　　　　&nbsp;}<BR>&nbsp;&nbsp;&nbsp;　　　　try{<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　struct tm* now = new struct tm;<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　now-&gt;tm_year = atoi(argv[1]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　now-&gt;tm_mon = atoi(argv[2]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　now-&gt;tm_mday = atoi(argv[3]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　now-&gt;tm_hour = atoi(argv[4]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　now-&gt;tm_min = atoi(argv[5]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　now-&gt;tm_sec = atoi(argv[6]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　//Adjust<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　now-&gt;tm_mon--;<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　now-&gt;tm_year -= 1900;<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　cout &lt;&lt; endl &lt;&lt; "\tAfter convert: " &lt;&lt; mktime(now) &lt;&lt; " (format in seconds since UTC)" &lt;&lt; endl;<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　delete now;&nbsp;<BR>&nbsp;&nbsp;&nbsp;　　　　}catch(logic_error&amp; e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　　cout &lt;&lt; e.what() &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　　&nbsp;}<BR>&nbsp;&nbsp;&nbsp;　　　　return EXIT_SUCCESS;<BR>&nbsp;&nbsp;　　　}<BR>&nbsp;&nbsp;　　　return EXIT_SUCCESS;<BR>&nbsp;　　}</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　　　　&nbsp; </FONT><FONT size=3>五、登录会计文件的使用</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;　 Unix系统中提供了下列两个数据文件：<BR>&nbsp;&nbsp;&nbsp; 　（1）utmp文件，用于记录当前登录系统的各个用户，它被放于/var/adm/utmpx；<BR>&nbsp;&nbsp;&nbsp; 　（2）wtmp文件，用于跟踪所有登录与登出系统的事件，它被放于/var/adm/wtmpx。<BR>&nbsp;&nbsp;&nbsp; 　此外，还定义了一个结构体utmpx，其包含了用户登录与登出系统的相关信息。用户每次登录系统，系统会自动填写这一结构，然后将其写入到utmp文件中，同时也将其添写到wtmp文件中。登出时，系统会将utmp文件中相应的记录擦除(每个字节都填以0)，并将一条新记录添写到wtmp文件中。另外，在系统再次启动以及更改系统时间和日期时，也都会在wtmp文件中添写特殊的记录项。<BR>&nbsp;&nbsp;&nbsp; 　上述两个数据文件为二进制文件，人们要是直接打开会看到一堆的乱码，但可以通过who命令读utmp文件并以可读格式输出其内容，使用last命令可以查看wtmp文件中所有的记录。而在自己编写的程序中，则可以使用相应的函数来查看数据文件的实用信息，其程序如：<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 　[程序7]<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 　#include &lt;iostream&gt;<BR>&nbsp;　　#include &lt;utmpx.h&gt;<BR>&nbsp;　　#include &lt;unistd.h&gt;<BR>&nbsp;　　#include &lt;errno.h&gt;<BR>&nbsp;　　using namespace std;</FONT></P>
<P><FONT size=2>&nbsp;　　#define trace(arg) cout &lt;&lt; #arg &lt;&lt; " = " &lt;&lt; (arg) &lt;&lt; endl</FONT></P>
<P><FONT size=2>&nbsp;　　int main(int argc, char** argv)<BR>&nbsp;　　{<BR>&nbsp;&nbsp;　　　if(argc == 2) {<BR>&nbsp;&nbsp;&nbsp;　　　　utmpxname(argv[1]);&nbsp; //打开新的记录文件，请注意由argv[1]参数指定的文件名必须以x结尾<BR>&nbsp;　　&nbsp;　}<BR>&nbsp;&nbsp;　　　struct utmpx * tmp;<BR>&nbsp;&nbsp;　　　tmp = getutxent();&nbsp; &nbsp; //getutxent()可以从wtmpx中获得一行数据记录，即一条登录或登出事件<BR>&nbsp;&nbsp;　　　while(tmp != NULL)<BR>&nbsp;　　　&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　　 trace(tmp-&gt;ut_user);&nbsp;&nbsp; &nbsp;&nbsp;//登录账号<BR>&nbsp;&nbsp;　　　&nbsp;　trace(tmp-&gt;ut_id);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;//登录ID<BR>&nbsp;&nbsp;&nbsp;　　　　trace(tmp-&gt;ut_line);&nbsp;&nbsp; &nbsp;&nbsp;//登录设备名，省略了“/dev/”<BR>&nbsp;&nbsp;&nbsp;　　　　trace(tmp-&gt;ut_pid);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;//进程ID<BR>&nbsp;&nbsp;&nbsp;　　　　trace(tmp-&gt;ut_type);&nbsp;&nbsp; &nbsp;&nbsp;//登录类型，7（或USER_PROCESS）代表登录，8（或DEAD_PROCESS）代表登出<BR>&nbsp;&nbsp;&nbsp;　　　trace(tmp-&gt;ut_tv.tv_sec);&nbsp;//执行当前操作时的时间记录<BR>&nbsp;&nbsp;&nbsp;　　　cout &lt;&lt; "---------------" &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　&nbsp;tmp = getutxent();&nbsp;<BR>&nbsp;&nbsp;&nbsp;　　　sleep(1);<BR>&nbsp;　　&nbsp;　}<BR>&nbsp;&nbsp;　　　return 0;<BR>&nbsp;　　}<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行此程序，会自动读取wtmp文件中的信息，并依照我们所定义的格式输出。再来看一个复杂点的例程序。<BR>&nbsp;&nbsp;&nbsp; 　[程序8]</FONT></P>
<P><FONT size=2>　　&nbsp;#include &lt;iostream&gt;<BR>　　&nbsp;#include &lt;utmpx.h&gt;<BR>　　&nbsp;#include &lt;unistd.h&gt;<BR>　　&nbsp;#include &lt;errno.h&gt;<BR>　　&nbsp;using namespace std;</FONT></P>
<P><FONT size=2>　　&nbsp;void show(struct utmpx*&amp; tmp)<BR>　　&nbsp;{<BR>&nbsp;　　　&nbsp;cout &lt;&lt; tmp-&gt;ut_user &lt;&lt; endl;<BR>&nbsp;　　　&nbsp;cout &lt;&lt; tmp-&gt;ut_id &lt;&lt; endl;<BR>&nbsp;　　　&nbsp;cout &lt;&lt; tmp-&gt;ut_line &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　cout &lt;&lt; tmp-&gt;ut_pid &lt;&lt; endl;<BR>　　　&nbsp;&nbsp;cout &lt;&lt; tmp-&gt;ut_type &lt;&lt; endl;<BR>　　　&nbsp;&nbsp;cout &lt;&lt; tmp-&gt;ut_tv.tv_sec &lt;&lt; endl;&nbsp;<BR>&nbsp;　　}</FONT></P>
<P><FONT size=2>&nbsp;　　int main()<BR>　　&nbsp;{<BR>&nbsp;　　　&nbsp;static int i=0;<BR>&nbsp;&nbsp;　　　struct utmpx * tmp;<BR>&nbsp;&nbsp;　　　struct utmpx * t = new utmpx;<BR>&nbsp;　　　&nbsp;tmp = getutxent();<BR>&nbsp;&nbsp;　　　while(tmp != NULL)<BR>&nbsp;　　　&nbsp;{<BR>&nbsp;&nbsp;　　　　&nbsp;show(tmp);<BR>&nbsp;&nbsp;　　　　&nbsp;cout &lt;&lt; "______________" &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　　&nbsp;tmp = getutxent();&nbsp;<BR>&nbsp;&nbsp;　　　　&nbsp;sleep(1);<BR>&nbsp;&nbsp;&nbsp;　　　　if(++i == 7) break;<BR>&nbsp;&nbsp;　　　　&nbsp;if(i == 5) <BR>&nbsp;&nbsp;&nbsp;　　　　{<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　cout &lt;&lt; "************** " &lt;&lt; endl;<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　memcpy(t,tmp,sizeof(struct utmpx));<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　show(t);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　cout &lt;&lt; "************** " &lt;&lt; endl;<BR>&nbsp;&nbsp;&nbsp;　　　　}<BR>&nbsp;　　　&nbsp;}<BR>&nbsp;&nbsp;　　　struct utmpx* ll;<BR>&nbsp;&nbsp;　　　setutxent();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //将当前文件位移量置0，即从文件头部重新开始读第一条记录<BR>&nbsp;　　　&nbsp;cout &lt;&lt; "************** " &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　ll = getutxline(t);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //找到与t的ut_user相同、ut_type也相同的第一条记录<BR>&nbsp;&nbsp;　　　show(ll);<BR>&nbsp;　　　&nbsp;cout &lt;&lt; "************** " &lt;&lt; endl;<BR>&nbsp;&nbsp;　　　while(1)<BR>&nbsp;　　　&nbsp;{<BR>&nbsp;&nbsp;　　　　&nbsp;ll = getutxent();<BR>&nbsp;&nbsp;　　　　&nbsp;show(ll);<BR>&nbsp;&nbsp;　　　　&nbsp;if(ll-&gt;ut_type == USER_PROCESS || ll-&gt;ut_type == DEAD_PROCESS)<BR>&nbsp;&nbsp;&nbsp;　　　　{<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　memcpy(t,ll,sizeof(struct utmpx));<BR>&nbsp;&nbsp;&nbsp;&nbsp;　　　　　break;<BR>&nbsp;&nbsp;　　　　&nbsp;}<BR>&nbsp;　　　&nbsp;}<BR>&nbsp;&nbsp;　　　utmpxname("utmp.bakx");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //打开一个新的记录文件，请注意文件名必须以x结尾<BR>&nbsp;&nbsp;　　　cout &lt;&lt; " the following will be write into .. \n";<BR>&nbsp;　　　&nbsp;show(t);<BR>&nbsp;&nbsp;　　　pututxline(t);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //将t指向的记录写入新的记录文件<BR>&nbsp;&nbsp;　　　endutxent();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭文件流<BR>&nbsp;&nbsp;　　　delete t;<BR>&nbsp;&nbsp;　　　return 0;<BR>　　&nbsp;}</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp;　每个人都有打电话的经历，在我们拨打电话时会进入电信计费系统，一旦拨通电话则系统会记录登录时间，挂断电话则系统会记录退出时间，两时间差即为用户的通话时长。那么在每个月底将某一用户的通话记录搜索出来放入一个新的记录文件，并将其打印出来便是用户的每月通话账单了。因此，Unix的登录会计文件完全可以用于电信服务中的数据采集工作，电信的数据采集系统可能也正是采用了类似的方式。</FONT></P><img src ="http://www.blogjava.net/dev2dev/aggbug/10380.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/dev2dev/" target="_blank">朱春雷</a> 2005-08-17 22:16 <a href="http://www.blogjava.net/dev2dev/archive/2005/08/17/10380.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>