Windows与Linux下文件操作监控的实现
一、需求分析:
随着渲染业务的不断进行,数据传输渐渐成为影响业务时间最大的因素。究其原因就是因为数据传输耗费较长的时间。于是,依托于渲染业务的网盘开发逐渐成为迫切需要解决的需求。该网盘的实现和当前市场上网盘实现有一些的不同,主要在客户端与服务器端的操作需要双向进行,即:用户在客户端的操作需要及时同步到服务器端;在服务器端作业渲染生成的文件要及时同步到客户端。即:用户不在需要单独的下载数据,而是在作业运行的同时,渲染就过就会自动同步到客户端,大大缩短了等待时间。当然,无论是在客户端还是在服务端都面临着一个问题,即:实现对文件操作的监控,这里的文件操作包括:文件(夹)创建、文件(夹)删除、文件(夹)重命名、文件(夹)移动等操作。除此之外还要能够同步客户端文件的修改操作,即:当用户退出网盘后,修改了原有同步目录中的文件,当用户再次启动网盘时通过一次扫描与md5值的比较能缺确定出哪些文件发生了改动,并将改动后的操作及时同步到服务端。这里,先将Windows(客户端实现需要)与Linux(服务端实现需要)下文件监控的实现方法简要概述。
二、文件监控实现方法分析
1> Windows下文件监控的实现
Windows下实现文件监控的原理是利用SHChangeNotifyRegister把指定的窗口添加到系统的消息监视链中,从而注册窗口就可以接收到来自文件系统或Shell的通知了。在继续向下说明之前,需要解释一下Windows外壳名字空间(Shell Name Space)的概念。
外壳名字空间是Windows下的标准文件系统,它大大扩展了Dos文件系统,形成了以”桌面“为根的单一文件系统树,原有的C、D盘等目录树变成了“我的电脑”这一外壳名字控件子树的下一级子树,而像“控制面板”、“回收站”、“网上邻居”等应用程序以及“打印机”等设备也被虚拟成了外壳名字空间中的节点。为了区别于Dos中“目录”的概念,Windows引入了“文件夹”的概念。“文件夹”一般是指外壳名字空间树中的非叶几点,既可以是DOS下的目录,也可以是“控制面板”、“回收站”这类虚拟的目录。
新的“路径”PIDL:外壳对象标识符列表。PIDL是一个元素类型为ITEMIDLIST结构的数组,数组中元素的个数是未知的,但紧接着数组的末尾的必是一个双字节的零。每个数组元素代表了外壳名字空间树中的一层(即一个文件夹或文件),数组中的前一元素代表的是后一元素的父文件夹。由此可见,PIDL实际上就是指向一块由若干个顺序排列的ITEMIDLIST结构组成、并在最后有一个双字节零的空间的指针。所以PIDL的类型就被Windows定义为ITEMIDLIST结构的指针。
DOS中的路径是一个字符串,但PIDL是一种二进制结构,所以我们不能直接从PIDL中获知它所代表的到底是哪个文件夹或文件,而必须调用相应的函数把它转换为代表路径的字符串。如果某绝对PIDL是文件系统的一部分,则调用SHGetPathFromIDList函数即可;但如不是,就无法获得路径字符串了,因为DOS中根本就不存在这种路径。
总体来说:实现Windows下文件监控的基本流程如下:
上图是在windows下利用C++实现文件监控的一种方式(根据需要灵活改动),这里简要叙述一下:1) 利用DialogBoxParam创建一个模态对话框,进入窗口过程函数,在该窗口函数中根据各种消息来完成我们的操作。这里模态对话框与非模态对话框的区别之一是因为它有一套自己的消息泵机制,不需要我们再手动写消息的接收了(非模态对话框要自己接收消息)。拦截用户消息,根据各个不同的阶段可以加入我们自己的操作,比如初始化等。2) 获取指定路径的PIDL:即目标路径的外壳对象标识符,有了它才能继续后续的处理,这里的获取有两种方式,一种是利用IFileDialog打开对话框让用户选择(IFileDialog *pfd),从而通过GetResult(IShellItem *psi ; pfs->GetResult(&psi))获取IShellItem对象;然后利用QueryInterface(IShellItem2 *_psiDrop ; psi->QueryInterface(&_psiDrop))获取IShellItem2对象;最后利用它就可以获得PIDL了(利用SHGetIDListFromObject)。3) 最后利用SHChangeNotifyRegister完成最终目标窗体的挂载,从而将一个目录加入到系统的消息链中,从而可以获取文件系统或Shell中关于文件操作的相关信息。最后将信息解析出来就可以了。
还有一种方式即直接提供目标文件夹的绝对路径,由该路径获取到PIDL,从而将窗体挂载到系统消息链中,注意:如果是在QT中实现的话,可以很轻松的获取到QWidget的窗口句柄。关键代码如下:
- void houqd::RegisterWindow()
- {
- char absoluteFolderPath[] = "C:\\openssl";
- //! 由文件夹的绝对路径获取PIDL:外壳对象标识符列表,即在windows 外壳名字空间 "Shell Name Space"中的表示方法。
- LPITEMIDLIST myFolderPIDL = ParsePidlFromPath(absoluteFolderPath);
- HRESULT res ;
- IShellItem *psi = NULL;
- //! 创建一个IShellItem(interface)对象,IShellItem interface提供了查找一个关于Shell Item相关信息的方法。
- //! IShell Item接口都继承自IUnknown interface
- res = SHCreateShellItem(NULL, NULL, myFolderPIDL, &psi);
- IShellItem2 *ppsi ;
- //! 检索一个对象上支持的接口的指针
- psi->QueryInterface(&ppsi);
- //! =======================================注册文件监控=============================================
- PIDLIST_ABSOLUTE pidlWatch;
- HRESULT hr = SHGetIDListFromObject(ppsi, &pidlWatch);
- if(SUCCEEDED(hr))
- {
- SHChangeNotifyEntry const entries[] = { pidlWatch, true };
- int const nSources = SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_NewDelivery;
- //! 注册窗口主函数
- _ulRegister = SHChangeNotifyRegister(_hdlg, nSources, SHCNE_ALLEVENTS, c_notifyMessage, ARRAYSIZE(entries), entries);
- hr = _ulRegister != 0 ? S_OK : E_FAIL;
- }
- //ShowWindow(SW_HIDE);
- //! ====================================================================================
- }
ParsePidFromPath 的具体实现如下:
- LPITEMIDLIST houqd::ParsePidlFromPath(LPCSTR lpszPath)
- {
- //存放以Unicode内码表示的路径字符串的缓冲区
- OLECHAR szOleChar[MAX_PATH];
- //“桌面“的IshellFolder接口指针
- LPSHELLFOLDER lpsfDeskTop;
- //返回的PIDL
- LPITEMIDLIST lpifq;
- ULONG ulEaten, ulAttribs;
- HRESULT hres;
- //得到“桌面”的IshellFolderr 接口指针
- SHGetDesktopFolder(&lpsfDeskTop);
- //将Ansi字符集的路径字符串转换成Unicode字符串,存入szOleChar
- MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED, lpszPath, -1, szOleChar, sizeof(szOleChar));
- //将szOleChar,中的路径径字符串翻译成相应的PIDL,存入lpifq
- hres = lpsfDeskTop->ParseDisplayName(NULL, NULL, szOleChar, &ulEaten, &lpifq, &ulAttribs);
- hres = lpsfDeskTop->Release();
- //如果翻译失败,则返回NULL
- if(FAILED(hres))
- return NULL;
- return lpifq;
- }
2> Linux下文件监控的实现
Linux下主要是通过inotify实现文件监控。它是一个内核用户通知用户空间程序文件系统变化的机制。在用户状,inotify通过三个系统调用和在返回的文件描述符上的文件I/O操作来使用.
1) 使用inotify的第一步是创建inotify的实例:int fd = inotify_init() ; 每一个inotify实例对应一个独立的排序的队列。
2) int wd = inotify_add_watch(fd , file_dir_path , mask);添加一个目录的监控。
3) 删除一个监控:inotify_rm_watch(fd , wd);
关键代码如下:
- if (m_InotifyFd != LHFSMC_FD_UNCREATED_STATE)
- close(m_InotifyFd);
- //! inotify_init()
- if ((m_InotifyFd = inotify_init()) < 0)
- {
- qDebug() << "[error] LHFileSystemMonitor::Start: inotify_init failure.";
- return 0;
- }
- //! 为m_CreatedDirList中所有保存的目录创建监控
- if(!CreateWatcherForEachDir(m_CreatedDirList))
- {
- qDebug() << "[error] LHFileSystemMonitor::Start: CreateNotifierForEachDir failure.";
- return 0;
- }
CreateWatcherForEachDir的实现:
- int LHFileSystemMonitor::CreateWatcherForEachDir(QStringList &dirLocationList)
- {
- #ifndef WIN32
- for (QStringList::const_iterator iter = dirLocationList.begin();
- iter != dirLocationList.end();
- ++iter)
- {
- int watchDescriptor;
- if ((watchDescriptor = CreateWatcher(iter->toStdString().c_str())) > 0)
- m_MonitoredObjectList.push_back(LHFSMonitorData(*iter, watchDescriptor));
- else
- qDebug() << "[error] LHFileSystemMonitor::CreateWatcherForEachDir: CreateWatcher for"
- << *iter << "failure";
- }
- #endif
- return 1;
- }
CreateWatcher的实现:
- int LHFileSystemMonitor::CreateWatcher(const char *fileLocation) const
- {
- if (fileLocation == NULL)
- return 0;
- QDir dir(fileLocation);
- if (!dir.exists())
- {
- qDebug() << "[error] LHFileSystemMonitor::CreateNotifier:" << fileLocation << "is not exist!";
- return 0;
- }
- return ((m_InotifyFd != LHFSMC_FD_UNCREATED_STATE) ? inotify_add_watch(m_InotifyFd, fileLocation, LHFSMC_MONITOR_EVENT) : -1);
- }
总结:
以上就是Windows以及Linux下文件监控系统实现的相关思路及代码,仅仅作为一个引入,利用这种方式均可以实现对应的功能。
Windows与Linux下文件操作监控的实现的更多相关文章
- Windows、Linux下文件操作(写、删除)错误的产生原因、及解决方法
catalog . 引言 . Linux平台上涉及的File IO操作 . Windows平台上涉及的File IO操作 0. 引言 本文试图讨论在windows.linux操作系统上基于C库进行文件 ...
- linux下文件操作之cp和mv
Linux CP文件夹略过目录的解决 root@QGY:/home/qgy# cp image/newimage_raw /mnt/4T/qin/cp: 略过目录'image/newimage_raw ...
- Linux 下文件操作 shell
删除目录下的所有文件ls *.log | xargs rm -f当前目录所有文件大小的总和ll | awk '{sum += $5}; END {print sum/1048576}'将命令推送到后台 ...
- Linux下 文件操作(base)
1.新建文件夹 mkdir bigdata:在当前文件夹下新建bigdata文件夹: 2.显示当前文件夹全目录 pwd: 3.移动文件:mv /usr/etc/spark-2.3.1-bin-hado ...
- windows与linux之间文件的传输方式总结(转)
当然,windows与linux之间文件的传输的两种方式有很多,这里就仅仅列出工作中遇到的,作为笔记: 方法一:安装SSH Secure Shell Client客户端 安装即可登录直接拖拉到linu ...
- linux下文件结束符
linux下文件结束符,我试过了所有的linux,发现其文件的结束符都是以0a即LF结束的,这个是操作系统规定的,windows下是\r\n符结束,希望可以帮助大家. -------------转:来 ...
- Windows 和 Linux 下 禁止ping的方法
Windows 和Linux 下 禁止ping的方法 目的: 禁止网络上的其他主机或服务器ping自己的服务器 运行环境: Windows 03.08 linux 方法: Windows 03下: ...
- socket在windows下和linux下的区别
原文:socket在windows下和linux下的区别 1)头文件 windows下winsock.h/winsock2.h linux下sys/socket.h 错误处理:errno.h 2 ...
- Linux下的IO监控与分析
Linux下的IO监控与分析 近期要在公司内部做个Linux IO方面的培训, 整理下手头的资料给大家分享下 各种IO监视工具在Linux IO 体系结构中的位置 源自 Linux Performan ...
随机推荐
- leetcode第七题Reverse Integer (java)
Reverse Integer Reverse digits of an integer. Example1: x = 123, return 321 Example2: x = -123, retu ...
- C语言中的volatile
转自C语言的那些小秘密之volatile volatile的重要性对于搞嵌入式的程序员来说是不言而喻的,对于volatile的了解程度常常被不少公司在招聘嵌入式编程人员面试的时候作为衡量一个应聘者是否 ...
- 搭建mongodb分片
搭建mongodb分片 http://gong1208.iteye.com/blog/1622078 Sharding分片概念 这是一种将海量的数据水平扩展的数据库集群系统,数据分表存储在shardi ...
- 一个优秀的http实现框架
package com.ming; import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unir ...
- linux中nginx的安装,linux的版本是ubutu
linux环境下,安装nginx,报错如下: the HTTP rewrite module requires the PCRE library. 1.需要安装pcre,报一下错误: you need ...
- mongodb windows下服务安装与卸载
安装服务.bat 内容: d:\mongodb2.4.3\bin\mongod.exe --dbpath d:\data\db --config d:\mongodb2.4.3\mongod.cfg ...
- Firebug控制台详解
转自:http://www.ruanyifeng.com/blog/2011/03/firebug_console_tutorial.html 作者: 阮一峰 日期: 2011年3月26日 Fireb ...
- linux下使用vim替换文件中的^M换行符
在linux下打开windows编辑过的文本,会出现由于换行符不一致而导致的内容格式错乱的问题.最常见的就是出现^M . 我出现的问题是:在windows编辑过的文件,传到linux上后再用vim打开 ...
- Selenium WebDriver + Grid2 + RSpec之旅(五)---面向对象设计用例
Selenium WebDriver + Grid2 + RSpec之旅(五) ----面向对象设计用例 前几节讲了怎么一步一步的从零开始到编写出一个简单的测试用例,这一节将要讲一下怎么让测试用例变得 ...
- windows apache 配置多个服务站点
原文 方法一:多个APACHE服务 更改第一个站点的根目录: 在文件Apache2.2/conf/httpd.conf中查找 DocumentRoot 属性,将后面的路径改为你的主站点的路径, 如:D ...