/*************************************************/
/*本文档由鸿剑青翻译,原始英文文档在 */
/*http://techpubs.sgi.com/library/tpl/c*/
/*i-bin/getdoc.cgi/0650/bks/SGI_Develop*/
/*er/books/IIDsktp_IG/sgi_html/ch08.htm*/
/*l#LE29093-PARENT */
/*日期:2005年8月31日 */
/*双横线中的内容未代码 */
/*单横线中的内容为提示或注意事项 */
/*自己翻译的,如有问题请指正,谢谢! */
/*************************************************/
FAM接口
这一节描述了你的应用程序连接到FAM所使用的函数。
1打开和关闭一个FAM连接
函数FAMOpen()打开一个到fam的连接。
=======================================
int FAMOpen(FAMConnection* fc)
=======================================
如果成功,FAMOpen()返回0,如果失败,则返回-1。FAMOpen()初始化的FAMConnection结构体将被用于传递给所有在你应用程序中被调用的FAM子进程。
FAMConnection结构体中有一个基本的元素,它是FAM用来和你的应用程序进行通信的接口(socket)的相关文件描述符。你需要这个文件描述符在socket上执行select()操作。你可以通过使用FAMCONNECTION_GETFD()宏来获得这个文件描述符。
=======================================
FAMCONNECTION_GETFD(fc)
=======================================
函数FAMOpen2告诉FAM应用程序的名字
=======================================
int FAMOpen2(FAMConnection* fc, const char* appName)
=======================================
当FAM打印出错信息的时候会使用到应用程序名(appName)
函数FAMClose()关闭一个到fam的连接
=======================================
int FAMClose(FAMConnection* fc)
=======================================
如果成功,FAMClose()返回0,否则返回-1.
2监视一个文件或目录
FAMMonitorDirecotry()和FAMMonitorFile()分别告诉FAM开始的监视一个目录和文件。
=======================================
int FAMMonitorDirecotry(FAMConnection *fc,char *filename,FAMRequest* fr,void* userData)
int FAMMonitorFile(FAMConnection *fc,char *filename,FAMRequest* fr,void* userData)
======================================
FAMMonitorDirectory()不仅仅监视指定的目录文件本身的变化,还监视该目录下文件的变化。如果该目录包含有子目录,则FAMMonitorDirecotry()仅仅只监视这些子目录的变化,而不监视这些子目录下文件和目录的变化。FAMMonitorFile()仅仅只显示指定文件发生了什么变化。这两个函数在成功后都返回0,否则都返回-1.
这些函数的第一个参数是被FAMOpen()初始化的FAMConnection结构体。第二个参数是要被监视的目录或文件的全路径及名称。注意,不能使用相对路径。
第三个参数是这两个函数初始化的一个FAMRequest结构体。你可以传递这个结构体给FAMSuspendMonitor(),FAMResumeMonitor()或FAMCancelMonitor()去分别暂停、恢复或取消对文件或目录的监视。
第四个参数是一个指针,这个指针指向了当一个文件或目录改变时,你所想要包含进由FAMNextEvent()返回的FAMEvent结构体中的一个强制(arbitrary)用户数据。
不论何时,当FAM侦测到所监视的文件或目录改变时,FAM产生FAM事件。
两个相似的例程是FAMMonitorDirectory2()和FAMMonitorFile2():
===============================================
int FAMMonitorDirectory2(FAMConnection *fc,char *filename,FAMrequest* fr);
int FAMMonitorFiles2(FAMConnection *fc,char *filename,FAMRequet* fr);
===============================================
在这两个例程中,调用者挑选请求号码,而不是libfam,调用者在调用之前通过把要求的号码放置在FAMRequest中来指定所要求的号码。比如:
===============================================
FAMConnection fc;
FAMRequest frq;
…
frq.reqnum=some_number_associated_with_tmp;
if (FAMMonitorDirectory2(&fc,"/tmp",&frq) <0)
perror("can’t monitor /tmp");
===============================================
如果你使用-2例程,你必须选择独一无二的要求号码,参看下面的FAMAcknowledge.
使用哪种例程是由你自己决定的:-2例程或原始例程。
3暂停、恢复和取消监视
一旦你已经开始监视一个文件或目录,那么你可以取消或临时暂停监视,以及以后再恢复监视。
FAMSuspendMonitor()临时暂停监视一个文件或目录。FAMResumeMonitor()恢复对一个文件或目录的监视。当你的应用程序不需要显示关于某个文件的信息是,暂停监视是非常有用的。
———————————————–
注意:FAM在暂停监视的时候会将文件所发生的任何改变排成队列,当你的应用程序恢复监视的时候,文件所有的改变
都会被通知。
———————————————–
这些函数的语法如下:
===============================================
int FAMSuspendMonitor(FAMConnection *fc,FAMRequest *fr);
int FAMResumeMonitor(FAMConnection *fc,FAMRequest *fr);
===============================================
fc是由FAMOpen()所返回的FAMConnection,fr是由FAMMonitorFile()或FAMMonitorDirecotry()所返回的FAMRequest。两个函数在成功后都返回0,否则返回-1.
当你的应用程序完成对一个文件或目录的监视后,它应该呼叫FAMCancelMonitor():
===============================================
int FAMCancelMonitor(FAMConnection *fc,FAMRequest *fr)
===============================================
FAMCancelMonitor()命令FAM不再对由fr所指定的文件或目录进行监视。如果成功返回0,否则返回-1.
调用完FAMCancelMonitor(),FAM发送一个FAMAcknowledge事件,当你已经看过FAMAcknowledge事件,你会知道它安全的重新使用了请求号码(如果你在使用-2形式的例程)
4侦测文件和目录的改变
无论何时FAM侦测到它所监视的文件发生改变,都会产生一个FAM事件,你的应用程序可以通过两种方法中的一种来接收FAM事件:
Select方法
你的应用程序执行一个select(2)在一个文件描述符上,这个文件描述符存在于FAMOpen()返回的FAMConnection结构体中。当这个文件描述符变成激活的,应用程序调用FAMNextEvent()去检索(取回retrieve)即将到来(pending)的FAM事件。
Polling方法
你的应用程序周期性的调用FAMPending()(通常为系统等待输入)。当FAMPending返回一个正数的值时,你的应用程序呼叫FAMNextEvent()去检索(取回retrieve)随后的(pending)FAM事件。
FAMPending()有着如下的语法:
==============================================
int FAMPending(FAMConnection *fc)
==============================================
如果存在一个FAM事件队列,则它返回1,如果没有FAM事件队列则返回0,如果发生错误则返回-1。FAMPending()不会等待一个事件,它即时返回值。
一旦你确定存在一个FAM事件队列,不论使用select方法还是polling方法,都调用FAMNextEvent()去接收(retrieve)它。
==============================================
int FAMNextEvent(FAMConnection *fc,FAMEvent *fe)
==============================================
如果成功,FAMNextEvent()返回0,如果存在一个错误则返回-1.FAMNextEvent()的第一个参数是由FAMOpen()初始化的FAMConnection结构体。第二个参数是一个指向FAMEvent结构体的指针,这个结构体由FAMNextEvent()用关于FAM事件的信息填充。FAMEvent结构体的格式是:
==============================================
typedf struct{
FAMConnection* fc;
FAMRequest fr;
char *hostname;
char filename[PATH_MAX];
void *userdata;
FAMCodes code;
}FAMEvent;
==============================================
fc是由FAMOpen()初始化的FAMConnection结构体。
fr是当你所要求被FAM监视的文件或目录发生改变后,由FAMMonitorFile()或FAMMonitorDirecotry()所返回的FAMRequest结构体。
hostname是一个废旧的字段。不要在你的应用程序里使用它。
filename是被你所监视的文件或目录的完整的路径和文件名。
userdata是当你调用FAMMonitorFile()或FAMMonitorDirectory()时,由你提供的一个强制数据的指针。如果你使用-2例程FAMMonitorDirector2()或FAMMonitorFile2(),userdata是没有定义的。
code是用于描述发生改变的FAMCodes的枚举类型的值,它可以采用以下的任意值:
FAMChanged 关于文件或目录可以获得的最后改变的一些值
FAMDeleted 被监视的文件或目录被删除了。
FAMStartExecuting 一个被监视的可执行文件被执行了,这个事件仅仅在文件被执行的第一时间被触发。
FAMStopExecuting 一个被监视的可执行文件运行终止了。如果一个可执行产生多个进程,则该事件只在最后一个进程终结的时候产生。
FAMCreated 一个被监视的目录中被创建了一个文件。
FAMAcknowledge FAM产生一个FAMAcknowledge事件来响应一次FAMCancelMonitor()的调用。如果你指定了一个无效的请求,也就是说,一个相对路径,FAM自动取消这个请求并且立即发送一个FAMAcknowledge事件。
FAMExists 当应用程序请求的那个文件已经被监视,FAM为那个文件产生一个FAMExists事件(如果文件存在的话)。当你的应用程序请求的那个目录已经被监视时,FAM为那个目录以及目录下的每一个文件产生一个FAMExists事件。
FAMEndExist 当应用程序请求的文件或目录被监视时,FAM在最后一个FAMExists事件后产生一个FAMEndExist事件。(所以,如果你监视一个文件,FAM只产生一个FAMExists事件,随后产生FAMEndExist事件。)
————————————————-
注意:在IRIX 6.2之前,FAMNextEvent()没有在FAMEndExist事件中初始化filename字段。你应该使用要求码去寻找事件所参考的文件或目录。
————————————————-
5符号链接
如果你给FAMMonitorDirector()或FAMMonitorFile()所指明的路径文件名是一个符号链接,FAM仅仅监视符号链接自身,而不是符号链接的目标。虽然逻辑上可能感觉应自动监视符号链接的目标,但考虑到如果目标是一个自动挂载的文件系统,monitoring the target triggers and holds an automount。另一个用监视链接自身代替目标的理由是目标可能不存在。
没有一个通用的解决方法来处理监视符号链接的目标。你可以决定一个适合的方法用于你的应用程序去监视目标,即使它是自动挂载的。
————————————————-
提示:当你解决一个连接到链接的最终目标的情形时,libc例程realpath(3C)非常有用。
————————————————-
————————————————-
提示:使用statvfs(2)去识别一个远程文件。
————————————————-
另一方面,为了避免triggering and holding an automount,你可以手动的追踪符号链接,直到达到一个你可以监视的本地的目标。如果目标是不存在的文件系统,那么你应该决定不去监视目标。另一个选择是对目标进行一次测试,如果它存在(local),which triggers an automount only once if the target is automounted.
举个例子,下面的例程判定给定的路径是一个不存的、不稳定的链接、本地的还是远程的。
=================================================
#include<errno.h>
#include<limits.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/statvfs.h>
/*
* determine a file’s location
*/
enum location {ERROR,NONEXISTENT,DANGLING,LOCAL,REMOTE};
enum location
file_location(const char *path)
{
char target_path[PATH_MAX];
struct stat statbuf;
struct statvfs svfsbuf;
if (!realpath(path,target_path))
{
/*
*realpath failed — probably a permission problem,
*dangling link or nonexistent file.
*/
if (errno==EACCES)
return ERROR;
if (lstat(path,&statbuf)==0)
return DANGLING;
else if (errno==ENOENT)
return NONEXISTENT;
else
return ERROR;
}
/*
* Realpath succeeded.Find out if file is local.
*/
if (statvfs(target_path,&svfsbuf) <0)
return ERROR;
if (svfsbuf.f_flag &ST_LOCAL)
return LOCAL;
else
return REMOTE;
}
=================================================
6FAM例子
下面的例子展示了在某些特定情形下FAM发送的事件流。
例子:一个客户端监视一个存在的文件。稍后,另外一个程序在被监视的文件上追加了数据。再随后,客户端取消了对监视的请求。
=================================================
User calls FAMMonitorFile(… "/a/b/c"…)
FAM events: FAMExists /a/b/c
FAMEndExist /a/b/c
Other program appends to file.
FAM event: FAMChanged /a/b/c
User calls FAMCancelMonitor(…)
FAM event: FAMAcknowledge /a/b/c
=================================================
例子:一个客户端监视了一个包含有两个文件的目录。稍后,另一个程序创建了第三个文件。
=================================================
User calls FAMMonitorDirectory(…"/a/b"…)
FAM events: FAMExists /a/b
FAMExists file_one
FAMExists file_two
FAMEndExist /a/b
Third file created.
FAMCreated file_three
=================================================
例子:一个客户端监视一个已经执行的可执行文件。稍后,这个程序退出了。
=================================================
User calls FAMMonitorFile(…"/a/b/program"…)
FAM events: FAMExists /a/b/program
FAMEndExist /a/b/program
FAMStartExecuting /a/b/program
Program exits.
FAM event: FAMStopExecuting /a/b/program
=================================================
例子:一个客户端做了一次无效的请求。
=================================================
User calls FAMMonitorDirectory(…"relative/path"…)
FAM event: FAMAcknowledge relative/path
=================================================
例子:一个客户端监视了一个不存在的文件,稍后,另一个程序创建了这个文件。
=================================================
User calls FAMMonitorFile(…"/a/b/c"…)
FAM events: FAMDeleted /a/b/c
FAMEndExist
File is created.
FAM event: FAMCreated /a/b/c
=================================================
例子:一个客户端监视一个含有一些文件的目录。另一个程序删除了这个目录,然后又创建了一个和改目录同名的文件。
=================================================
User calls FAMMonitorDirectory(…"/a/b"…)
FAM events: FAMExists /a/b
FAMExists file_one
FAMExists file_two
FAMEndExist /a/b
Directory and files are deleted.
FAM events: FAMDeleted /a/b
FAMChanged /a/b
FAMDeleted file_one
FAMDEleted file_two
File with same name created.
FAM events: FAMCreated /a/b
FAMChanged /a/b
================================================
7使用FAM
如同“侦测文件和目录的改变”里写的,你的应用程序可以检查通过两种方法监视的文件和目录的变化。
×使用select()去等待直到FAM socket激活,指明一个改变,它被描述在“等待文件改变”。
×使用FAMPending()去周期的查询(poll)FAM,它被解释在“轮询(Polling)文件改变”
这一节描述了如何使用这两种方法。
7.1等待文件改变
在你的应用程序中使用下面的步骤,使用的是select方发去检测文件的改变。
1.调用FAMOpen()创建一个到fam的连接。这个例程返回一个将被用于所有FAM进程的FAMConnection结构体。
2.调用FAMMonitorFile()和FAMMonitorDirectory()去告知fam哪些文件和目录被监视。
3.当fam socket是可读时,选择在fam socket上的文件描述符并调用FAMNextEvent()。
4.当应用程序完成对一个文件或目录的监视,调用FAMCancelMonitor()。如果你想临时暂停监视一个文件或目录,调用FAMSuspendMonitor()。当你准备再次开始监视时,调用FAMResumeMonitor()。
5.当应用程序不再需要对文件和目录监视时,调用FAMClose()去释放与文件被监视的相关的资源。关闭到fam的连接,这一步是可选的,如果你简单的退出你的应用程序。
例子8-1在一个简单的程序中演示了这一过程。
例子8-1.对FAM使用Select
===================================================
/*
* monitor.c–monitor arbitrary file or directory using fam
*/
#include <fam.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/select.h>
/*event_name() – return printable name of fame event code */
const char *event_name(int code)
{
static const char *famevent[]={
"",
"FAMDeleted",
"FAMStartExecuting",
"FAMStopExecuting",
"FAMCreated",
"FAMMoved",
"FAMAcknowledge",
"FAMExists",
"FAMEndExist"
};
static char unknow_event[10];
if (code < FAMChaged || code > FAMEndExist)
{
sprintf(unknow_event,"unknow (%d)",code);
return unknown_event;
}
return famevent ;
}
void main(int argc,char *argv[])
{
int i,nmon,rc,fam_fd;
FAMConnection fc;
FAMRequest *frp;
struct stat status;
fd_set readfds;
/*Allocate storage for requests */
frp=malloc(argc * sizeof *frp);
if (!frp)
{
perror("malloc");
exit(1);
}
/* Open fam connection */
if ((FAMOpen(&fc)) < 0)
{
perror("fam");
exit(1);
}
/*Reuqest monitoring for each program argument */
for (nmon=0,i=1;i<argc;i++)
{
if (stat(argv[i],&status) < 0)
{
perror(argv[i]);
status.st_mode=0;
}
if ((status.st_mode & S_IFMT)==S_IFDIR)
rc=FAMMonitorDirectory(&fc,argv[i],frp+i,NULL);
else
rc=FAMMonitorFile(&fc,argv[i],frp+i,NULL);
if (rc < 0)
{
perror("FAMMonitor failed");
continue;
}
nmon++;
}
if (!nmon)
{
fprintf(stderr,"Nothing monitored.n");
exit(1);
}
/*Initialize select data structure */
fam_fd=FAMCONNECTION_GETfd(&fc);
FD_ZERO(&readfds);
FD_SET(fam_fd,&readfds);
/*Loop forever. */tic char unknow_even
while(1)
{
if (select(fam_fd + 1,&readfds,NULL,NULL,NULL) < 0)
{
perror("select failed");
exit(1);
}
if (FD_ISSET(fam_fd,&readfds))
{
if (FAMNextEvent(&fc,&fe) < 0)
{
perror("FAMNextEvent");
exit(1);
}
printf("%-24s %sn",fe.filename,event_name(fe.code));
}
}
}
================================================
7.2轮询(Polling)文件改变
在你的应用程序中遵循下面的步骤使用FAM,使用的是轮询的方法去侦测文件的改变。
1.调用FAMOpen()去创建一个到fam的连接。这个例程返回一个以后所有FAM进程都要用到的FAMConnection结构体。
2.调用FAMMonitorFile()和FAMMonitorDirectory()去告知fam哪些文件和目被监视。
3.当有一个pending FAM事件时,调用FAMPending()去监测,如果一个事件被侦测到,则调用FAMNextEvent()。
4.当应用程序完成监视一个文件或目录时,调用FAMCancelMonitor()。如果想临时暂停对一个文件或目录的监视,调用FAMSuspendMonitor()。当你准备再次开始监视时,调用FAMResumeMonitor()。
5.当应用程序不再需要监视文件和目录时,调用FAMClose()去释放文件被监视时的相关资源并且关闭到fam的连接。这一步是可选的,如果你简单的推出你的应用程序。
举例,你可以通过删除掉例8-1中关于select data structure的代码并且用例8-2中的代码替换调while loop来在monitor.c程序中使用轮询(polling)方法。这个简单的程序演示了这个过程。
例8-2.对FAM使用Polling方法
==================================================
while(1)
{
rc=FAMPending(&fc);
if (rc ==0)
break;
else if(rc==-1)
perror ("FAMPending");
if (FAMNextEvent(&fc,&fe) < 0)
{
perror ("FAMNextEvent");
exit(1);
}
printf("%-24s %sn",fe.filename,event_name(fe.code));
}
==================================================
当你想要在Xt工作过程中使用轮询来查询改变,这是一个非常有用的方法。例8-3现实了这种工作过程的基本代码。
例8-3.在Xt工作过程中使用轮询FAM
==================================================
Boolean monitorFiles(XtPointer clientData)
{
int rc=FAMPending(&fc);
if (rc == 0)
return(FALSE);
else if (rc == -1)
XtAppError(app_context,"FAMPending error");
if (FAMNextEvent(&fc,&fe) < 0)
{
XtAppError(app_context,"FAMNextEvent error");
}
handleFileChange(fe);
return(FALSE);
}
===================================================