日期:2011-11-01  浏览次数:20599 次

摘 要: 本文提出了基于操作系统内核服务和多线程技术的网站实时监控系统,解决了以往监控系统不能及时恢复异常网页的问题。重点介绍了系统的传送控制部分和监控部分

  关键词: 实时监控;多线程;API

  引言

  对网页监控比较成熟的技术是定时监控,即由用户设定时间间隔,系统按时对需监控的网页文件轮询一遍,来判断文件是否被非法删除或篡改。若发现,立即用备份盘上的备份文件进行恢复。这样的监控存在一个缺陷:被非法删除或篡改的网页不能得到及时的恢复。

  本文介绍的网站实时监控系统创造性地利用操作系统内核提供的系统服务和多线程技术,所有的文件非法变更事件都会被操作系统及时通知监控程序,该机制完全区别于扫描技术,不需要与备份库对比分析的繁琐过程,可以做到监控的实时性和低耗性。 2 系统工作原理

  为了实现网站的实时自动恢复,系统需要由三个部分组成:网站监控,网站备份,传送控制。网站监控负责监视Web服务器内部的变化,发现对网页文件的非法篡改;网站备份是Web服务器内网页文件系统的映像,以及有关数据库系统;传送控制对网站监控部分实施控制并用于页面维护工作,而且还负责产生有关审计记录,报警信息等。这三个部分组成一个有机整体,工作原理如下:网站监控发现异常并分析出异常点,接着通知传送控制部分;传送控制部分控制网站备份部分将与异常点有关的正确内容传送到Web服务器覆盖异常部分,同时产生相应的审计记录和报警信息。

  传送控制部分

  使用本系统时,用户首先通过定制监控目录选择要监控的网页文件。当进行实时监控时,系统启动多个并发的监控线程对多个目录实施实时监控,一个线程监控一个目录。传送控制部分负责启动监控线程,接收监控线程传来目录变更的消息,并根据消息将备份文件覆盖异常文件,产生报警和审计信息。

  1、监控线程的启动

  由于用户定制的须监控网页的个数是不固定的,因此所须监控的目录的个数也是不固定的,所以要启动的线程的数目也将是不固定的。鉴于此,我们用链表结构来表示所须监控的目录。链表结构包括目录名和指向下一个监控目录结构的指针,结构为: struct list{

string dirname; //记录目录名称的字符串
struct list *next;} // 指向下一节点的指针
  如图1所示


  图1 三节点的链表
  链表的数据来源可以是静态的用户配置文件或动态的用户操作选取。当链表中的一个节点指向下一节点的指针为NULL时,说明该节点是链表的末节点。根据链表,启动多个线程,链表有一个节点,就代表有一个目录需要监控,就需要启动一个监控线程,遍历整个链表,就可以根据用户需求启动所有线程,监控所有定制的目录。实现过程如图2所示。


  链表的初始地址àP

P=NULL?
CreateThread( NULL,0,monitor,P,0,&ThreadId )
P=PàNEXT
终止
Y
N
  图2中P是指向链表中一个节点的指针,最初指向链表的首节点,每启动一个线程,P向后移一个节点。P同时作为参数传递给线程的控制函数,控制函数通过指针P可以知道自己正在监控的是哪一个目录。Windows系统函数CreateThread( NULL,0,monitor,P,0,&ThreadId )启动监控线程,monitor是线程控制函数的起始地址。

  2、变更消息的处理

  在传送控制部分定义一个DealFun函数专门用于处理网站监控部分发送的目录变更消息。网站监控部分发送的变更消息有三个参数:发生了什么样的改变,发生改变的文件名和发生改变的文件所在目录。DealFun函数根据文件名和目录名到文件备份数据库中找到该文件的备份文件,并用备份文件覆盖该文件;再根据改变的类型(比如文件被删除,文件被修改等)产生相应的报警信息。同时把每次变更写入日志数据库中。

  网站监控部分

  网站监控部分负责实现监控线程中的控制函数,对指定的一个目录进行监控,当该目录中有文件发生改变,负责把变更信息发送给传送控制部分。 Windows提供了对文件和目录监控的系统服务,并且为应用程序提供了两个API函数,它们分别是:FindFirstChangeNotification和ReadDirectoryChangesW。由于通过FindFirstChangeNotification函数只能监控到某一目录下有文件发生改变,而不能监控到具体是哪一文件发生改变,所以本系统选用ReadDirectoryChangesW函数。该函数的定义为:

BOOL ReadDirectoryChangesW(
HANDLE hDirectory,
LPVOID lpBuffer,
DWORD nBufferLength,
BOOL bWatchSubtree,
DWORD dwNotifyFilter,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
  第一个参数hDirectory是要监控目录的句柄,可以通过指定目录名,利用CreateFile函数的返回值获得。用户代码通过第二个和第三个参数来告知操作系统该把目录变化通知放在首地址为lpBuffer, 长度为nBufferLength的一块内存区域当中的。但是该内存又是怎样组织的呢?操作系统是把他们放在FILE_NOTIFY_INFORMATION这个结构里面的:

typedef struct _FILE_NOTIFY_INFORMATION {
DWORD NextEntryOffset;
DWORD Action;
DWORD FileNameLength;
WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
  这是一个链表结构,第一个字段存储了要获得下一个记录需要跳过多少字节数,如果它的值为0,就表示本记录已经是链表中的最后一条记录了。该字段其实也可以看作是一个指向下一条记录的指针。第二个字段的含义是:本次通知了哪种类型的目录变化。第三个字段表示的是变化的文件名称的长度。第四个字段是一个存放变化的文件名称的Unicode字符数组的首地址。

  另外一个与本系统有关的参数是dwNotifyFilter。它是目录变化通知过滤器。要监控文件名发生变化,此参数应设为FILE_NOTIFY_CHANGE_FILE_NAME;文件被非法改写为FILE_NOTIFY_CHANGE_LAST_WRITE等等。根据过滤器的设置,ReadDirectoryChangesW函数可以监控文件名改变、文件属性改变、文件大小改变、文件内容被改写、文件被删除等多种类型的变化。

  监控线程的控制函数就是利用ReadDirectoryChangesW函数实现对一个目录进行监控的。具体的做法是:首先使用CreateFile获取要监控目录的句柄;然后在一个While循环里面调用ReadDirectoryChangesW,并且把自己分配的