转自:http://qbaok.blog.163.com/blog/static/10129265201112302014782/
对于监控指定目录内文件变更,window 系统提供了两个未公开API:SHChangeNotifyRegister SHChangeNotifyDeregister 分别用于注册Notify以及监视。
同时,还提供了ReadDirectoryChangesW 函数(貌似NT以上可用)。
在 .net framework 中,另提供了封装好的 FileSystemWatcher 来实现此功能。
至于 JAVA PHP 神马的,太晚了,懒得查了,以后再说。
============ 【分割线】=============
ReadDirectoryChangesW 函数具体信息如下:
对指定的目录进行监控,返回详细的文件变化信息。
函数形式
01 BOOL WINAPI ReadDirectoryChangesW(
02 __in HANDLE hDirectory, // 对目录进行监视的句柄
03 __out LPVOID lpBuffer, // 一个指向DWORD类型的缓冲区,其中可以将获取的数据结果将其返回。
04 __in DWORD nBufferLength, // 指lpBuffer的缓冲区的大小值,以字节为单位。
05 __in BOOL bWatchSubtree, // 监视目录. 一般选择 TRUE
06 __in DWORD dwNotifyFilter, // 对文件过滤的方式和标准
07 __out_opt LPDWORD lpBytesReturned, // 将接收的字节数转入lpBuffer参数
08 __inout_opt LPOVERLAPPED lpOverlapped, // 一般选择 NULL
09 __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // 一般选择 NULL
10 );
1.hDirectory [中]
This directory must be opened with the FILE_LIST_DIRECTORY access right.
被监视的目录必须打开FILE_LIST_DIRECTORY的访问权限
2.lpBuffer[中]
The structure of this buffer is defined by the FILE_NOTIFY_INFORMATION structure
这个缓冲区的定义是FILE_NOTIFY_INFORMATION结构。
This buffer is filled either synchronously or asynchronously,
depending on how the directory is opened and what value is given to the lpOverlapped parameter.
这个缓冲区充满要么同步或异步,这取决于如何打开目录什么价值给予lpOverlapped参数。
3.nBufferLength [中]
The size of the buffer that is pointed to by the lpBuffer parameter, in bytes.
大小的缓冲区,是指出的lpBuffer参数,以字节为单位。
4.bWatchSubtree [中]
If this parameter is TRUE, the function monitors the directory tree rooted at the specified directory.
如果这个参数是TRUE,那么这个函数会监视目录树,所指定的当前的根目录(整个路径信息都显示出来)。
If this parameter is FALSE, the function monitors only the directory specified by the hDirectory parameter.
如果这个参数是FALSE ,则函数则只监视hDirectory句柄所指定的目录下的内容(只显示出发生变化的文件目录)。
5.dwNotifyFilter [中]
The filter criteria that the function checks to determine if the wait operation has completed.
该过滤器的标准,功能检查,以决定是否等待操作完成。
This parameter can be one or more of the following values.这个参数可以是一个或多个下列值。
【FILE_NOTIFY_CHANGE_FILE_NAME】 0x00000001
Any file name change in the watched directory or subtree causes a change notification wait operation to return.
任何文件名改变 都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
Changes include renaming, creating, or deleting a file.
变化包括重命名,创建或删除文件。
【FILE_NOTIFY_CHANGE_DIR_NAME】 0x00000002
Any directory-name change in the watched directory or subtree causes a change notification wait operation to return.
任何目录名称改变 都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
Changes include creating or deleting a directory.
改变包括建立或删除一个目录。
【FILE_NOTIFY_CHANGE_ATTRIBUTES】 0x00000004
Any attribute change in the watched directory or subtree causes a change notification wait operation to return.
任何属性变化,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
【FILE_NOTIFY_CHANGE_SIZE】 0x00000008
Any file-size change in the watched directory or subtree causes a change notification wait operation to return.
任何文件大小的变化,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
The operating system detects a change in file size only when the file is written to the disk.
操作系统检测改变文件大小,只有当该文件被写入到磁盘时发生。
For operating systems that use extensive caching, detection occurs only when the cache is sufficiently flushed.
操作系统使用广泛缓存,检测时才会发生的缓存足够同满。
【FILE_NOTIFY_CHANGE_LAST_WRITE】0x00000010
Any change to the last write-time of files in the watched directory or subtree causes a change notification wait operation to return.
任何改变过去修改时间的文件 ,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
The operating system detects a change to the last write-time only when the file is written to the disk.
操作系统检测改变过去写的时间只有当该文件被写入到磁盘。
For operating systems that use extensive caching, detection occurs only when the cache is sufficiently flushed.
操作系统使用广泛缓存,检测时才会发生的缓存足够同满。
【FILE_NOTIFY_CHANGE_LAST_ACCESS】0x00000020
Any change to the last access time of files in the watched directory or subtree causes a change notification wait operation to return.
任何改变文件最近访问时间,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
【FILE_NOTIFY_CHANGE_CREATION】 0x00000040
Any change to the creation time of files in the watched directory or subtree causes a change notification wait operation to return.
任何改变文件的创建时间的,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
【FILE_NOTIFY_CHANGE_SECURITY】0x00000100
Any security-descriptor change in the watched directory or subtree causes a change notification wait operation to return.
任何安全描述符被改变的,都会查看所在目录或子目录的变更,并将结果通知给等待操作返回。
6.lpBytesReturned [了,可选]
For synchronous calls, this parameter receives the number of bytes transferred into the lpBuffer parameter.
同步调用,这个参数接收的字节数转入lpBuffer参数。
For asynchronous calls, this parameter is undefined.
异步调用,这个参数是未定义的。
You must use an asynchronous notification technique to retrieve the number of bytes transferred.
您必须使用异步通知技术检索的字节数转移。
7.lpOverlapped [中,那样,可选]
A pointer to an OVERLAPPED structure that supplies data to be used during asynchronous operation.
一个指针的重叠结构,提供供数据时使用的异步操作。
Otherwise, this value is NULL.
否则,这个值为NULL 。
The Offset and OffsetHigh members of this structure are not used.
OFFSET和OffsetHigh成员结构不使用。
8.lpCompletionRoutine [中,可选]
A pointer to a completion routine to be called when the operation has been completed or canceled and the calling
thread is in an alertable wait state.
一个指针一个完成例程 如果在呼叫使用函数操作时已经完成或取消和调用线程是在alertable等待状态。
返回值
If the function succeeds, the return value is nonzero.
如果函数成功,返回值为非零。
For synchronous calls, this means that the operation succeeded.
同步要求,这意味着操作取得了成功。
For asynchronous calls, this indicates that the operation was successfully queued.
异步调用,这表明操作成功排队。
If the function fails, the return value is zero.
如果函数失败,返回值是零。
To get extended error information, call GetLastError .
要获得扩展错误信息,请用GetLastError返回错误 。
If the network redirector or the target file system does not support this operation, the function fails with ERROR_INVALID_FUNCTION.
如果网络重定向或目标文件系统不支持这一行动,该功能失败, ERROR_INVALID_FUNCTION 。
========= 抄来的 delphi FileSystemWatcher 实现 ==========
{*******************************************************}
{ }
{ FileSystemWatcher }
{ }
{ 版权所有 (C) 2007 solokey }
{ }
{ http://blog.csdn.net/solokey }
{ }
{*******************************************************}
unit FileSystemWatcher;
interface
uses
Windows, Classes, SysUtils;
type
TFileOperation = (foAdded, foRemoved, foModified, foRenamed);
TFileDealMethod = procedure(FileOperation: TFileOperation; const FileName1,FileName2: string) of object;
TNotifyFilter = (nfFileNameChange, nfDirNameChange, nfAttributeChange,
nfSizeChange, nfWriteChange, nfAccessChange, nfCreationDateChange, nfSecurityChange);
TNotifyFilters = set of TNotifyFilter;
TNotificationBuffer = array[0..4095] of Byte;
PFileNotifyInformation = ^TFileNotifyInformation;
TFileNotifyInformation = record
NextEntryOffset: DWORD;
Action: DWORD;
FileNameLength: DWORD;
FileName: array[0..0] of WideChar;
end;
TShellChangeThread = class(TThread)
private
FActived: Boolean;
FDirectoryHandle: Cardinal;
FCS: TRTLCriticalSection;
FChangeEvent: TFileDealMethod;
FDirectory: string;
FWatchSubTree: Boolean;
FCompletionPort: Cardinal;
FOverlapped: TOverlapped;
FNotifyOptionFlags: DWORD;
FBytesWritten: DWORD;
FNotificationBuffer: TNotificationBuffer;
protected
procedure Execute; override;
procedure DoIOCompletionEvent;
function ResetReadDirctory: Boolean;
procedure Lock;
procedure Unlock;
public
constructor Create(ChangeEvent: TFileDealMethod); virtual;
destructor Destroy; override;
procedure SetDirectoryOptions(Directory : String; Actived: Boolean; WatchSubTree : Boolean;
NotifyOptionFlags : DWORD);
property ChangeEvent : TFileDealMethod read FChangeEvent write FChangeEvent;
end;
TFileSystemWatcher = class(TComponent)
private
FActived: Boolean;
FWatchedDir: string;
FThread: TShellChangeThread;
FOnChange: TFileDealMethod;
FWatchSubTree: Boolean;
FFilters: TNotifyFilters;
procedure SetWatchedDir(const Value: string);
procedure SetWatchSubTree(const Value: Boolean);
procedure SetOnChange(const Value: TFileDealMethod);
procedure SetFilters(const Value: TNotifyFilters);
function NotifyOptionFlags: DWORD;
procedure SetActived(const Value: Boolean);
protected
procedure Change;
procedure Start;
procedure Stop;
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
published
property Actived:Boolean read FActived write SetActived;
property WatchedDir: string read FWatchedDir write SetWatchedDir;
property WatchSubTree: Boolean read FWatchSubTree write SetWatchSubTree;
property NotifyFilters: TNotifyFilters read FFilters write SetFilters;
property OnChange: TFileDealMethod read FOnChange write SetOnChange;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TFileSystemWatcher]);
end;
{ TShellChangeThread }
constructor TShellChangeThread.Create(ChangeEvent: TFileDealMethod);
begin
FreeOnTerminate := True;
FChangeEvent := ChangeEvent;
InitializeCriticalSection(FCS);
FDirectoryHandle := 0;
FCompletionPort := 0;
inherited Create(True);
end;
destructor TShellChangeThread.Destroy;
begin
CloseHandle(FDirectoryHandle);
CloseHandle(FCompletionPort);
DeleteCriticalSection(FCS);
inherited Destroy;
end;
procedure TShellChangeThread.DoIOCompletionEvent;
var
TempBuffer: TNotificationBuffer;
FileOpNotification: PFileNotifyInformation;
Offset: Longint;
FileName1, FileName2: string;
FileOperation: TFileOperation;
procedure DoDirChangeEvent;
begin
if Assigned(ChangeEvent) and FActived then
ChangeEvent(FileOperation, FileName1, FileName2);
end;
function CompleteFileName(const FileName:string):string;
begin
Result := '';
if Trim(FileName) <> '' then
Result := FDirectory + Trim(FileName);
end;
begin
Lock;
TempBuffer := FNotificationBuffer;
FillChar(FNotificationBuffer, SizeOf(FNotificationBuffer), 0);
Unlock;
Pointer(FileOpNotification) := @TempBuffer[0];
repeat
with FileOpNotification^ do begin
Offset := NextEntryOffset;
FileName2 := '';
case Action of
FILE_ACTION_ADDED..FILE_ACTION_MODIFIED: begin
FileName1 := CompleteFileName(WideCharToString(FileName));
FileOperation := TFileOperation(Action - 1);
DoDirChangeEvent;
end;
FILE_ACTION_RENAMED_OLD_NAME: begin
FileName1 := CompleteFileName(WideCharToString(FileName));
FileOperation := TFileOperation(Action - 1);
end;
FILE_ACTION_RENAMED_NEW_NAME: begin
if FileOperation = foRenamed then begin
FileName2 := CompleteFileName(WideCharToString(FileName));
DoDirChangeEvent;
end;
end;
end;
end;
Pointer(FileOpNotification) := Pointer(PChar(FileOpNotification) + OffSet);
until Offset=0;
end;
procedure TShellChangeThread.Execute;
var
numBytes: DWORD;
CompletionKey: DWORD;
PFOverlapped: POverlapped;
TempDirectoryHandle: Cardinal;
TempCompletionPort: Cardinal;
begin
while not Terminated do begin
Lock;
TempDirectoryHandle := FDirectoryHandle;
TempCompletionPort := FCompletionPort;
Unlock;
if TempDirectoryHandle > 0 then begin
PFOverlapped := @FOverlapped;
GetQueuedCompletionStatus(TempCompletionPort, numBytes, CompletionKey, PFOverlapped, INFINITE);
if CompletionKey = Handle then begin
Synchronize(DoIOCompletionEvent);
FBytesWritten := 0;
FillChar(FNotificationBuffer, SizeOf(FNotificationBuffer), 0);
ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), FWatchSubTree, FNotifyOptionFlags, @FBytesWritten, @FOverlapped, nil);
end;
end;
end;
PostQueuedCompletionStatus(TempCompletionPort, 0, 0, nil);
end;
procedure TShellChangeThread.Lock;
begin
EnterCriticalSection(FCS);
end;
function TShellChangeThread.ResetReadDirctory: Boolean;
var
TempHandle: Cardinal;
TempCompletionPort: Cardinal;
begin
Result := False;
CloseHandle(FDirectoryHandle);
PostQueuedCompletionStatus(FCompletionPort, 0, 0, nil);
CloseHandle(FCompletionPort);
TempHandle := CreateFile(PChar(FDirectory), GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED, 0);
Lock;
FDirectoryHandle := TempHandle;
Unlock;
if (GetLastError = ERROR_FILE_NOT_FOUND) or (GetLastError = ERROR_PATH_NOT_FOUND) then begin
Lock;
FDirectoryHandle := 0;
FCompletionPort := 0;
Unlock;
Exit;
end;
TempCompletionPort := CreateIoCompletionPort(FDirectoryHandle, 0, Handle, 0);
Lock;
FCompletionPort := TempCompletionPort;
Unlock;
FBytesWritten := 0;
FillChar(FNotificationBuffer, SizeOf(FNotificationBuffer), 0);
Result := ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), FWatchSubTree, FNotifyOptionFlags, @FBytesWritten, @FOverlapped, nil);
end;
procedure TShellChangeThread.SetDirectoryOptions(Directory: String; Actived: Boolean;
WatchSubTree: Boolean; NotifyOptionFlags : DWORD);
begin
FWatchSubTree := WatchSubTree;
FNotifyOptionFlags := NotifyOptionFlags;
FDirectory := IncludeTrailingBackslash(Directory);
FActived := Actived;
ResetReadDirctory;
end;
procedure TShellChangeThread.Unlock;
begin
LeaveCriticalSection(FCS);
end;
{ TFileSystemWatcher }
procedure TFileSystemWatcher.Change;
begin
if csDesigning in ComponentState then
Exit;
if Assigned(FThread) then begin
FThread.SetDirectoryOptions(FWatchedDir, FActived, LongBool(FWatchSubTree), NotifyOptionFlags);
end;
end;
constructor TFileSystemWatcher.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FActived := False;
FWatchedDir := 'C:\';
FFilters := [nfFilenameChange, nfDirNameChange];
FWatchSubTree := True;
FOnChange := nil;
end;
destructor TFileSystemWatcher.Destroy;
begin
if Assigned(FThread) then
FThread.Terminate;
inherited Destroy;
end;
function TFileSystemWatcher.NotifyOptionFlags: DWORD;
begin
Result := 0;
if nfFileNameChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_FILE_NAME;
if nfDirNameChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_DIR_NAME;
if nfSizeChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_SIZE;
if nfAttributeChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_ATTRIBUTES;
if nfWriteChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_LAST_WRITE;
if nfAccessChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_LAST_ACCESS;
if nfCreationDateChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_CREATION;
if nfSecurityChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_SECURITY;
end;
procedure TFileSystemWatcher.SetActived(const Value: Boolean);
begin
if FActived <> Value then begin
FActived := Value;
Change;
if FActived then
Start
else
Stop;
end;
end;
procedure TFileSystemWatcher.SetFilters(const Value: TNotifyFilters);
begin
if FFilters <> Value then begin
FFilters := Value;
Change;
end;
end;
procedure TFileSystemWatcher.SetOnChange(const Value: TFileDealMethod);
begin
FOnChange := Value;
if Assigned(FOnChange) and FActived then
Start
else
Stop;
Change;
end;
procedure TFileSystemWatcher.SetWatchedDir(const Value: string);
begin
if not SameText(FWatchedDir, Value) then begin
FWatchedDir := Value;
Change;
end;
end;
procedure TFileSystemWatcher.SetWatchSubTree(const Value: Boolean);
begin
if FWatchSubTree <> Value then begin
FWatchSubTree := Value;
Change;
end;
end;
procedure TFileSystemWatcher.Start;
begin
if csDesigning in ComponentState then
Exit;
if Assigned(FOnChange) then begin
FThread := TShellChangeThread.Create(FOnChange);
FThread.SetDirectoryOptions(FWatchedDir, FActived, LongBool(FWatchSubTree), NotifyOptionFlags);
FThread.Resume;
end;
end;
procedure TFileSystemWatcher.Stop;
begin
if csDesigning in ComponentState then
Exit;
if Assigned(FThread) then begin
FThread.Terminate;
FThread := nil;
end;
end;
end.
============ 【分割线】=============
至于 Linux 么…… PW 哥给找的 Linux inotify 还是很给力的。
-
#!/bin/bash
-
SRC=/home/pw/workspace/daobo/web_demo/
-
DST=username@remote_host:/home/pw/workspace/resin-3.0.21/webapps/daobo-demo
-
inotifywait -mrq -e modify --exclude=".*\.swp|\.svn|.*\~" $SRC |while read D E F;do
-
rsync -avz $SRC $DST --exclude=".svn" --exclude=".*\.swp"
-
done
至于这鸟玩意是个啥,怎么装,资料如下:
inotify 是什么?
Inotify一种强大的、细粒度的、异步文件系统监控机制,它满足各种各样的文件监控需要,可以监控文件系统的访问属性、读写属性、权限属性、删除创建 移动等操作,也就是可以监控文件发生的一切变化,我们可以利用内核提供的这个属性,在文件发生任何变化时都触发rsync同步动作,这样就做到数据的实时 同步了。
Inotify的监控功能是需要内核支持的,Linux从kernel 2.6.13开始,Inotify功能正式加入内核,在RHEL5版本已经完全支持。
为什么使用 inotify?
使用 inotify 取代 dnotify 的原因有很多。第一个原因是,dnotify 需要您为每个打算监控是否发生改变的目录打开一个文件描述符。当同时监控多个目录时,这会消耗大量的资源, 因为有可能达到每个进程的文件描述符限制。
除此之外,文件描述符会锁定目录,不允许卸载(unmount)支持的设备,这在存在可移动介质的 环境中会引发问题。在使用 inotify 时,如果正在监控被卸载的文件系统上的文件,那么监控会被自动移除并且您会接收到一个卸载事件。
dnotify 不如 inotify 的第二个原因是 dnotify 有点复杂。注意,使用 dnotify 基础设施的简单文件系统监控粒度只 停留于目录级别。为了使用 dnotify 进行更细粒度的监控,应用程序编程 人员必须为每个受监控的目录保留一个 stat 结构的缓存。该用户空间的 stat 结构缓存需要用来明确确定当接收到通知信号时目录发生了什么变化。当获得通知信号时,生成 stat 结构列表并与最新的状态相比较。显而易见,这种技术是不理想的。
inotify 的另一个优点是它使用文件描述符作为基本接口,使应用程序开发者使用 select 和 poll 来监控设备。这允许有效的多路 I/O 和与 Glib 的 mainloop 的集成。相反,dnotify 所使用的信号常常使程序员头疼并且感觉不太优雅。
inotify 通过提供一个更优雅的 API 解决了这些问题,该 API 使用最少的文件描述符,并确保更细粒度的监控。与 inotify 的通信是通过设备节点提供的。基 于以上原因,对于监控 Linux 2.6 平台上的文件,inotify 是您最明智的选择。
另外,要在shell下使用inotify提供的特性,还需要安装 inotify-tools,我们依次介绍如下:
安装inotify-tools
可以在 http://inotify-tools.sourceforge.net/ 下载到inotify-tools,接着编译安装:
[root@web253 ~]# tar zxvf inotify-tools-3.13.tar.gz
[root@web253 ~]# cd inotify-tools-3.13
[root@web253 inotify-tools-3.13]# ./configure
[root@web253 inotify-tools-3.13]#make
[root@web253 inotify-tools-3.13]#make install
完成后,在系统下执行man inotify 、 man inotifywait、man inotifywatch即可得到相应的帮助信息。表示安装成功。
使用inotify功能
可以通过如下命令查看系统是否支持inotify
[root@web253 bin]# uname -a
Linux web253 2.6.18-8.el5 #1 SMP Fri Jan 26 14:15:21 EST 2007 i686 i686 i386 GNU/Linux
[root@web253 bin]# ll /proc/sys/fs/inotify
total 0
-rw-r--r-- 1 root root 0 Feb 21 01:15 max_queued_events
-rw-r--r-- 1 root root 0 Feb 21 01:15 max_user_instances
-rw-r--r-- 1 root root 0 Feb 21 01:15 max_user_watches
如果有输出,表示系统已经内核已经支持inotify。
inotify 可以监视的文件系统事件包括:
IN_ACCESS, 即文件被访问
IN_MODIFY, 文件被 write
IN_ATTRIB, 文件属性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE, 可写文件被 close
IN_CLOSE_NOWRITE, 不可写文件被 close
IN_OPEN, 文件被 open
IN_MOVED_FROM, 文件被移走,如 mv
IN_MOVED_TO, 文件被移来,如 mv、cp
IN_CREATE, 创建新文件
IN_DELETE, 文件被删除,如 rm
IN_DELETE_SELF, 自删除,即一个可执行文件在执行时删除自己
IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
IN_UNMOUNT,宿主文件系统被 umount
IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)
注:上面所说的文件也包括 目录。
inotifywait 仅执行阻塞,等待 inotify 事件。您可以监控任何一组文件和目录,或监控整个目录树(目录、 子目录、子目录的子目录等等)。在 shell 脚本中使用 inotifywait。
inotifywatch 收集关于被监视的文件系统的统计数据,包括每个 inotify 事件发生多少次。
inotify的系统相关参数
/proc interfaces
The following interfaces can be used to limit the amount of kernel memory consumed by inotify:
/proc/sys/fs/inotify/max_queued_events
The value in this file is used when an application calls inotify_init(2) to set an upper limit on the number of events that can be queued to the corresponding inotify instance. Events in excess of this limit are dropped, but an IN_Q_OVERFLOW event is always generated.
/proc/sys/fs/inotify/max_user_instances
This specifies an upper limit on the number of inotify instances that can be created per real user ID.
/proc/sys/fs/inotify/max_user_watches
This specifies a limit on the number of watches that can be associated with each inotify instance.
inotifywait 相关的命令(更多,查看manpage):
inotifywait
This command simply blocks for inotify events, making it appropriate for use in shell scripts. It can watch any set of files and directories, and can recursively watch entire directory trees.
-m, --monitor
Instead of exiting after receiving a single event, execute indefinitely. The default behaviour is to exit after the first event occurs.
-r, --recursive
Watch all subdirectories of any directories passed as arguments. Watches will be set up recursively to an unlimited depth. Symbolic links are not traversed. Newly created subdirectories will also be watched.
-q, --quiet
If specified once, the program will be less verbose. Specifically, it will not state when it has completed establishing all inotify watches.
-e <event>, --event <event>
Listen for specific event(s) only. The events which can be listened for are listed in the EVENTS section. This option can be specified more than once. If omitted, all events are listened for. use“,”separate multi events。