Everything搜索实现

最近编写NTFS文件实时搜索工具, 类似 Everything 这样, 速度快还小巧, 于是花了约3周进行研究, 总结下学习过程中一些经验

 

实现分3部分完成

 

一. 解析NTFS 主文件表(MFT)

这一步是获取文件数据的唯一迅速且可靠的来源

NTFS_MFT_Parse.h

#pragma once
#include "NTFS_Base.h"
#include <functional>#define NTFS_MFT_PARSE_FILE_BUF_COUNT       (1024 * 16)            // MFT块分析文件项缓冲数量// 文件名处理回调
using NtfsFilenameCb = std::function<bool(const NTFS_VOLUME_INFO& volInfo, PNTFS_FILE_RECORD_HEADER pHeader, FILE_REFERENCE record, const MFT_FILE_INFO_LIST& mftInfoList)>;// NTFS 主文件表 解析类
// FlameCyclone 2024.12.11
class NTFS_MFT_Parse:public NTFS_Base
{
public:NTFS_MFT_Parse();~NTFS_MFT_Parse();//// @brief: 解析所有 NTFS 卷文件记录// @param: volFileList          卷文件列表// @param: dwDriveIndexMask     驱动器索引掩码(位组合: C: 0x01 D: 0x02 E: 0x04 F: 0x08...)// @ret:   bool                 操作是否成功static bool ParseRecord(DWORD dwDriveIndexMask = 0xFFFFFFFF,NtfsFilenameCb cb = nullptr);private:// 解析卷static bool _ParseVolume(const NTFS_VOLUME_INFO& volInfo, NtfsFilenameCb cb);// 解析主文件表static bool _ParseMasterFileTable(HANDLE hFile, PNTFS_BOOT_RECORD pBootRecord,PNTFS_FILE_RECORD_HEADER pFileHeaderStart, PNTFS_ATTRIBUTE_HEADER pAttrHeaderStart,const NTFS_VOLUME_INFO& volInfo,NtfsFilenameCb cb);// 获取文件记录static bool _GetFileRecord(HANDLE hFile,FILE_REFERENCE RecordNumber,PNTFS_FILE_RECORD_HEADER pOutFileHeader);// 解析文件名static bool _ParseFileRecord_20_AttributeList(HANDLE hFile,PNTFS_BOOT_RECORD pBootRecord,PNTFS_FILE_RECORD_HEADER pFileHeaderStart,PNTFS_ATTRIBUTE_HEADER pAttrHeaderStart,MFT_FILE_INFO_LIST& fileInfoList,std::vector<FILE_REFERENCE>& vRes);// 解析文件名static bool _ParseFileRecord_30_GetFilename(HANDLE hFile,PNTFS_BOOT_RECORD pBootRecord,PNTFS_FILE_RECORD_HEADER pFileHeaderStart,PNTFS_ATTRIBUTE_HEADER pAttrHeaderStart,MFT_FILE_INFO_LIST& fileInfoList);// 解析文件名static bool _ParseFileRecord_30_Filename(HANDLE hFile,PNTFS_BOOT_RECORD pBootRecord,PNTFS_FILE_RECORD_HEADER pFileHeaderStart,PNTFS_ATTRIBUTE_HEADER pAttrHeaderStart,MFT_FILE_INFO_LIST& fileInfoList);// 解析索引根static bool _ParseFileRecord_90_IndexRoot(HANDLE hFile,PNTFS_BOOT_RECORD pBootRecord,PNTFS_FILE_RECORD_HEADER pFileHeaderStart,PNTFS_ATTRIBUTE_HEADER pAttrHeaderStart,MFT_FILE_INFO_LIST& fileInfoList);// 解析索引根static bool _ParseFileRecord_A0_IndexAllocation(HANDLE hFile,PNTFS_BOOT_RECORD pBootRecord,PNTFS_FILE_RECORD_HEADER pFileHeaderStart,PNTFS_ATTRIBUTE_HEADER pAttrHeaderStart,MFT_FILE_INFO_LIST& fileInfoList);// 解析文件记录static bool _ParseFileRecordAttributes(HANDLE hFile,PNTFS_BOOT_RECORD pBootRecord,PNTFS_FILE_RECORD_HEADER pFileHeaderStart, PNTFS_ATTRIBUTE_HEADER pAttrHeaderStart, MFT_FILE_INFO_LIST& fileInfoList);// 解析主文件表数据static bool _ParseMasterFileTableData(HANDLE hFile,PNTFS_BOOT_RECORD pBootRecord,PNTFS_FILE_RECORD_HEADER pFileHeaderStart,PNTFS_ATTRIBUTE_HEADER pAttrHeaderStart,const NTFS_VOLUME_INFO& volInfo,NtfsFilenameCb cb);// 设置文件偏移static bool SetFileOffset(_In_ HANDLE hFile,_In_ uint64_t liDistanceToMove,_Out_opt_ uint64_t* lpNewFilePointer,_In_ DWORD dwMoveMethod = FILE_BEGIN);
};

 

二. 监控 USN 日志

为了动态监控每个文件的新建, 删除, 更名

NTFS_USN_Parse.h

#include "NTFS_USN_Parse.h"
#include <thread>NTFS_USN_Parse::NTFS_USN_Parse()
{}NTFS_USN_Parse::~NTFS_USN_Parse()
{}bool NTFS_USN_Parse::_CreateJournal(HANDLE hFile,DWORDLONG MaximumSize,DWORDLONG AllocationDelta
)
{// https://learn.microsoft.com/zh-cn/windows/win32/api/winioctl/ns-winioctl-create_usn_journal_dataCREATE_USN_JOURNAL_DATA usnCreate = { 0 };DWORD dwBytes = 0;usnCreate.MaximumSize = MaximumSize;            // NTFS 文件系统为更改日志分配的目标最大大小(以字节为单位)usnCreate.AllocationDelta = AllocationDelta;    // 添加到末尾并从更改日志开头删除的内存分配的大小if (!::DeviceIoControl(hFile,FSCTL_CREATE_USN_JOURNAL,&usnCreate, sizeof(usnCreate),NULL, 0,&dwBytes,NULL)){return false;}return true;
}bool NTFS_USN_Parse::_QueryJournal(HANDLE hFile,PUSN_JOURNAL_DATA_V2 pUsnJournalData
)
{DWORD dwBytes = 0;if (!::DeviceIoControl(hFile,FSCTL_QUERY_USN_JOURNAL,NULL,0,pUsnJournalData,sizeof(USN_JOURNAL_DATA_V2),&dwBytes,NULL)){return false;}return true;
}bool NTFS_USN_Parse::_ReadJournal(HANDLE hFile,PREAD_USN_JOURNAL_DATA pUsnData,LPVOID lpBuf,DWORD dwBufsize,LPDWORD dwBytes
)
{if ( !::DeviceIoControl(hFile,FSCTL_READ_USN_JOURNAL,pUsnData, sizeof(READ_USN_JOURNAL_DATA),lpBuf,dwBufsize,dwBytes,NULL) ){return false;}return true;
}void NTFS_USN_Parse::_ParseJournalV2(HANDLE hFile, const NTFS_VOLUME_INFO& volInfo, PUSN_RECORD_V2 pUsnRecord,NTFS_USN_INFO& usnInfo
)
{usnInfo.strFileName = WStrToTStr(std::wstring(pUsnRecord->FileName, pUsnRecord->FileNameLength / sizeof(wchar_t)));usnInfo.Reason.Data = pUsnRecord->Reason;usnInfo.UpdateSequenceNumber = pUsnRecord->Usn;usnInfo.ReferenceNumber = *(PFILE_REFERENCE)&pUsnRecord->FileReferenceNumber;usnInfo.ParentReferenceNumber = *(PFILE_REFERENCE)&pUsnRecord->ParentFileReferenceNumber;usnInfo.uDriveIndex = volInfo.uDriveIndex;usnInfo.FileAttributes = pUsnRecord->FileAttributes;
}void NTFS_USN_Parse::_ParseJournalV3(HANDLE hFile, const NTFS_VOLUME_INFO& volInfo, PUSN_RECORD_V3 pUsnRecord,NTFS_USN_INFO& usnInfo
)
{usnInfo.strFileName = WStrToTStr(std::wstring(pUsnRecord->FileName, pUsnRecord->FileNameLength / sizeof(wchar_t)));usnInfo.Reason.Data = pUsnRecord->Reason;usnInfo.UpdateSequenceNumber = pUsnRecord->Usn;usnInfo.ReferenceNumber = *(PFILE_REFERENCE)&pUsnRecord->FileReferenceNumber;usnInfo.ParentReferenceNumber = *(PFILE_REFERENCE)&pUsnRecord->ParentFileReferenceNumber;usnInfo.uDriveIndex = volInfo.uDriveIndex;usnInfo.FileAttributes = pUsnRecord->FileAttributes;
}bool NTFS_USN_Parse::_MonitorJournal(const NTFS_VOLUME_INFO& volInfo,DWORD dwReasonMask,UsnNotifyCb usnCb
)
{uint8_t* pBufData = nullptr;HANDLE hFile = INVALID_HANDLE_VALUE;USN usnStart = 0;DWORD dwRetBytes = 0;DWORD dwBytes = 0;DWORD dwError = 0;bool fResult = false;bool fAbort = false;do{// 打开卷hFile = OpenVolume(volInfo);if (INVALID_HANDLE_VALUE == hFile){break;}// 查询日志USN_JOURNAL_DATA_V2 usnData = { 0 };usnData.Flags = FLAG_USN_TRACK_MODIFIED_RANGES_ENABLE;usnData.FirstUsn = usnStart;if (!_QueryJournal(hFile, &usnData)){dwError = ::GetLastError();if (!(ERROR_JOURNAL_DELETE_IN_PROGRESS == dwError || ERROR_JOURNAL_NOT_ACTIVE == dwError)){break;}}// 日志未活动则创建日志if (ERROR_JOURNAL_NOT_ACTIVE == ::GetLastError()){if (!_CreateJournal(hFile, NTFS_USN_MAX_SIZE, NTFS_USN_ALLOCATION_DELTA_SIZE)){break;}usnData.Flags = FLAG_USN_TRACK_MODIFIED_RANGES_ENABLE;usnData.FirstUsn = usnStart;if (!_QueryJournal(hFile, &usnData)){dwError = ::GetLastError();if (!(ERROR_JOURNAL_DELETE_IN_PROGRESS == dwError || ERROR_JOURNAL_NOT_ACTIVE == dwError)){break;}}}usnStart = usnData.NextUsn;// 分配解析缓冲pBufData = new (std::nothrow) uint8_t[usnData.MaximumSize];if (!pBufData){break;}NTFS_USN_INFO usnInfoEmpty;while (true){// 查询日志if (!_QueryJournal(hFile, &usnData)){dwError = ::GetLastError();if (!(ERROR_JOURNAL_DELETE_IN_PROGRESS == dwError || ERROR_JOURNAL_NOT_ACTIVE == dwError)){fAbort = true;break;}}// 读取日志READ_USN_JOURNAL_DATA usnRead = { 0, 0xFFFFFFFF, FALSE, 0, 0 };usnRead.MinMajorVersion = usnData.MinSupportedMajorVersion;usnRead.MaxMajorVersion = usnData.MaxSupportedMajorVersion;usnRead.UsnJournalID = usnData.UsnJournalID;usnRead.StartUsn = usnStart;usnRead.ReasonMask = dwReasonMask;if (!_ReadJournal(hFile, &usnRead, pBufData, (DWORD)usnData.MaximumSize, &dwBytes)){dwError = ::GetLastError();if (!(ERROR_JOURNAL_DELETE_IN_PROGRESS == dwError || ERROR_JOURNAL_NOT_ACTIVE == dwError)){fAbort = true;break;}}dwRetBytes = dwBytes - sizeof(USN);// 未查询到数据if (!dwRetBytes){// 空闲时也回调一下if (!usnCb(false, usnInfoEmpty)){fAbort = true;break;}usnStart = usnData.NextUsn;::Sleep(NTFS_USN_UPDATE_TIME_INTERVAL);continue;}// 解析查询结果PUSN_RECORD_COMMON_HEADER pUsnHeader = (PUSN_RECORD_COMMON_HEADER)(pBufData + sizeof(USN));if (2 == pUsnHeader->MajorVersion){PUSN_RECORD_V2 pUsnRecord = (PUSN_RECORD_V2)(pBufData + sizeof(USN));NTFS_USN_INFO usnInfo;while (dwRetBytes > 0){_ParseJournalV2(hFile, volInfo, pUsnRecord, usnInfo);if (NTFSFileNameType::e05_Root == usnInfo.ParentReferenceNumber.RecordNumber || usnInfo.ParentReferenceNumber.RecordNumber >= NTFSFileNameType::e16_Unuse_Start){if (usnCb){if (!usnCb(true, usnInfo)){fAbort = true;break;}}}dwRetBytes -= pUsnRecord->RecordLength;pUsnRecord = (PUSN_RECORD_V2)(((PCHAR)pUsnRecord) + pUsnRecord->RecordLength);}}if (3 == pUsnHeader->MajorVersion){PUSN_RECORD_V3 pUsnRecord = (PUSN_RECORD_V3)(pBufData + sizeof(USN));NTFS_USN_INFO usnInfo;while (dwRetBytes > 0){_ParseJournalV3(hFile, volInfo, pUsnRecord, usnInfo);if (NTFSFileNameType::e05_Root == usnInfo.ParentReferenceNumber.RecordNumber ||usnInfo.ParentReferenceNumber.RecordNumber >= NTFSFileNameType::e16_Unuse_Start){if (usnCb){if (!usnCb(true, usnInfo)){fAbort = true;break;}}}dwRetBytes -= pUsnRecord->RecordLength;pUsnRecord = (PUSN_RECORD_V3)(((PCHAR)pUsnRecord) + pUsnRecord->RecordLength);}}// 空闲时也回调一下if (!usnCb(false, usnInfoEmpty)){fAbort = true;break;}// 下次解析起始日志号usnStart = usnData.NextUsn;::Sleep(NTFS_USN_UPDATE_TIME_INTERVAL);}if (fAbort){break;}fResult = true;} while (false);if (INVALID_HANDLE_VALUE != hFile){::CloseHandle(hFile);}if (pBufData){delete[] pBufData;}return fResult;
}bool NTFS_USN_Parse::MonitorJournal(DWORD dwDriveIndexMask/* = 0xFFFFFFFF*/,DWORD dwReasonMask/* = 0xFFFFFFFF*/,UsnNotifyCb usnCb/* = nullptr*/
)
{std::vector<NTFS_VOLUME_INFO> vecDriveList = GetVolumeInfoList();std::vector<std::thread> vTask;for (auto& item : vecDriveList){if ((0x01 << item.uDriveIndex) & dwDriveIndexMask){vTask.emplace_back(std::thread([&item, dwReasonMask, &usnCb]() {_MonitorJournal(item, dwReasonMask, usnCb);}));}}for (auto& item : vTask){if (item.joinable()){item.join();}}return true;
}

三. 数据库查询

采用Sqlite3进行数据库操作

NTFS_Search.h

#pragma once
#include "NTFS_Utils/NTFS_MFT_Parse.h"
#include "NTFS_Utils/NTFS_USN_Parse.h"
#include "Sqlite3/CSqlite3.h"
#include <thread>
#include <mutex>
#include <vector>#define SQL_UPDATE_TIME_INTERVAL            (1000)   // 数据库更新间隔时间// 计数
#define SQL_QUERY_COUNT   R"(
SELECT count(*) AS count FROM file_list WHERE path NOT NULL
)"// 更新根路径
#define SQL_QUERY_UPDATE_ROOT_PATH   R"(
WITH RECURSIVE sub_tree(id, parent_id, name, path) AS (SELECT id, parent_id, name, name AS pathFROM file_listWHERE parent_id = 0UNION ALLSELECT c.id, c.parent_id, c.name, p.path || '\' || c.nameFROM file_list cINNER JOIN sub_tree p ON c.parent_id = p.id
)
UPDATE file_list
SET path = (
SELECT path FROM sub_tree 
WHERE sub_tree.id = file_list.id AND sub_tree.parent_id = file_list.parent_id AND sub_tree.name = file_list.name
);
)"// 删除索引
#define SQL_QUERY_DROP_INDEX   R"(
DROP INDEX IF EXISTS idx_file_list_id ON file_list;
DROP INDEX IF EXISTS idx_file_list_parent_id ON file_list;
--DROP INDEX IF EXISTS idx_file_list_name ON file_list;
--DROP INDEX IF EXISTS idx_file_list_path ON file_list;
--DROP INDEX IF EXISTS idx_file_list_id_parent_id_name_path ON file_list;
)"// 创建索引
#define SQL_QUERY_CREATE_INDEX   R"(
CREATE INDEX IF NOT EXISTS idx_file_list_id ON file_list(id);
CREATE INDEX IF NOT EXISTS idx_file_list_parent_id ON file_list(parent_id);
--CREATE INDEX IF NOT EXISTS idx_file_list_name ON file_list(name);
--CREATE INDEX IF NOT EXISTS idx_file_list_path ON file_list(path);
--CREATE INDEX IF NOT EXISTS idx_file_list_id_parent_id_name_path ON file_list(id, parent_id, name, path);
)"// 更新子路径
#define SQL_QUERY_UPDATE_CHILD_PATH   R"(
WITH RECURSIVE sub_tree(id, parent_id, name, path) AS (SELECT id, parent_id, name, path AS pathFROM file_listWHERE id = %llu AND parent_id = %lluUNION ALLSELECT c.id, c.parent_id, c.name, p.path || '\' || c.nameFROM file_list cINNER JOIN sub_tree p ON c.parent_id = p.id
)
UPDATE file_list
SET path = st.path
FROM sub_tree st
WHERE file_list.id = st.id AND file_list.parent_id = st.parent_id AND file_list.name = st.name;
)"// 更新文件路径
#define SQL_QUERY_UPDATE_FILE_PATH   R"(
WITH RECURSIVE path_cte(id, parent_id, name, path) AS (SELECT id, parent_id, name, nameFROM file_listWHERE id = %llu AND parent_id = %llu AND name = "%s"UNION ALLSELECT f.id, f.parent_id, f.name, f.name || '\' ||p.path FROM file_list fINNER JOIN path_cte p ON (f.id = p.parent_id) 
)UPDATE file_list
SET path = (SELECT path FROM path_cte WHERE parent_id = 0)
WHERE id = %llu AND parent_id = %llu AND name = "%s";
)"// 按文件名查找
#define SQL_QUERY_SEARCH_NAME   R"(
SELECT path FROM file_list WHERE name like "%s" ESCAPE '\' AND path NOT NULL ORDER BY path LIMIT %lld
)"// 按路径查找
#define SQL_QUERY_SEARCH_PATH   R"(
SELECT path FROM file_list WHERE path like "%s" ESCAPE '\' AND path NOT NULL ORDER BY path LIMIT %lld
)"// 搜索全部
#define SQL_QUERY_SEARCH_ALL   R"(
SELECT path FROM file_list WHERE path NOT NULL ORDER BY path LIMIT %lld
)"//删除表
#define SQL_QUERY_DELETE_TABLE                R"(
DROP TABLE IF EXISTS file_list;
)"//创建表
#define SQL_QUERY_CREATE_TABLE                R"(
CREATE TABLE IF NOT EXISTS file_list (id        INTEGER NOT NULL,parent_id INT,name      TEXT,attr      INT,path      TEXT,PRIMARY KEY(id, parent_id, name)
);
)"//建表更新数据
#define SQL_QUERY_REPLACE_PREPQRE R"(
REPLACE INTO file_list (id, parent_id, name, attr, path) VALUES (?, ?, ?, ?, ?);
)"// 替换记录
#define SQL_QUERY_REPLACE R"(
REPLACE INTO file_list (id, parent_id, name, attr) VALUES (%llu, %llu, "%s", %d);
)"// 删除记录
#define SQL_QUERY_DELETE R"(
DELETE FROM file_list where id = %llu AND parent_id = %llu AND name = "%s";
)"// 数据库使用的文件ID
typedef union _SQL_FILE_ID
{uint64_t data;struct {uint64_t ReferenceNumber : 32;      // 文件引用号uint64_t dwDriveIndex : 32;         // 驱动器号};}SQL_FILE_ID;bool operator < (const _SQL_FILE_ID& a, const _SQL_FILE_ID& b);class NTFS_Search
{
public:NTFS_Search();~NTFS_Search();//// @brief: 初始化// @param: dwDriveIndexMask     驱动器索引掩码(位组合: C: 0x01 D: 0x02 E: 0x04 F: 0x08...)// @param: strDbPath            数据库文件路径// @param: fRebuildDb           是否重建数据库// @ret:   bool                 操作是否成功bool Initialize(DWORD dwDriveIndexMask = 0xFFFFFFFF,const _tstring& strDbPath = _T(""), bool fRebuildDb = true);//// @brief: 初始化// @param: strDriveList         驱动器列表// @param: strDbPath            数据库文件路径// @param: fRebuildDb           是否重建数据库// @ret:   bool                 操作是否成功bool Initialize(const _tstring& strDriveList = _T("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),const _tstring& strDbPath = _T(""),bool fRebuildDb = true);//// @brief: 反初始化// @ret:   void                 操作是否成功void Uninitialize();//// @brief: 搜索// @param: strKeyWord           关键字// @param: fileList             输出文件路径列表// @param: nLimit               限制结果数量// @ret:   bool                 操作是否成功bool Search(const _tstring& strKeyWord, std::vector<_tstring>& fileList,int64_t nLimit = -1);//// @brief: 获取数据库文件总数// @ret:   size_t               数据库文件总数size_t GetCount();//// @brief: 获取驱动器索引掩码// @ret:   DWORD               驱动器索引掩码 DWORD GetDriveListMask(const _tstring& strDriveList);private:bool _BuildDb(DWORD dwDriveIndexMask,const _tstring& strDbPath,bool fRebuildDb);bool _BuildRecord(DWORD dwDriveIndexMask);bool _BuildFile(const NTFS_VOL_FILE_MAP& volFileList);bool _BuildDrive(DWORD dwDriveIndexMask);bool _StartMonitor(DWORD dwDriveIndexMask);void _Sleep(int dwMilliseconds);int _UpdateFilePath(SQL_FILE_ID fileID, SQL_FILE_ID parentID, _tstring strFilename);int _UpdateChildPath(SQL_FILE_ID fileID, SQL_FILE_ID parentID);private:std::vector<NTFS_USN_INFO>  m_usnInfoList;  // 日志变化列表std::mutex                  m_mutex;        // 互斥锁_tstring                    m_strDbPath;    // 数据库文件路径CSqlite3                    m_sql3;         // 数据库std::thread                 m_monTask;      // 监控任务std::thread                 m_sqlTask;      // 数据库任务bool                        m_fInit;        // 初始化状态bool                        m_fQuit;        // 退出状态bool                        m_fDbReady;     // 数据库准备状态
};

 

0d6a951f29b94b7682bc4926ed51d24b.png

对比:

3512252dc0d547b39e4fa43398247640.png

性能上重建数据库耗时是everything的 2倍, 不过也差不多了, 以后慢慢优化

 

ab2811a34b38401b9703737be3dcce33.png

搜索速度比不上everything, 但是也算是秒速了

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/64209.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

23.DDD与微服务

学习视频来源&#xff1a;DDD独家秘籍视频合集 https://space.bilibili.com/24690212/channel/collectiondetail?sid1940048&ctype0 文章目录 DDD与微服务的关系1. DDD可以用微服务实现&#xff0c;也可以不用微服务实现2. DDD是微服务拆分的必须参考项之一3. 微服务架构…

vscode 打开 setting.json

按下Ctrl Shift P&#xff08;Windows/Linux&#xff09;或Cmd Shift P&#xff08;Mac&#xff09;来打开命令面板。输入open settings&#xff0c;然后选择 Open User Settings(JSON)。打开settings.json文件 ------修改设置-----&#xff1a; 1、 html代码的行长度&am…

汽车电子元件的可靠性保障:AEC-Q102认证

AEC-Q102标准的起源与价值 随着汽车电子系统的日益复杂&#xff0c;电子器件必须能够在极端的温度、湿度、振动和电磁干扰等恶劣条件下保持性能。AEC-Q102标准由汽车电子委员会&#xff08;AEC&#xff09;制定&#xff0c;专门针对LED、激光二极管和光电二极管等光电器件&…

RK3576 Android14,内存大于4G时UVC应用无法申请内存

最近有个项目需要将Linux虚拟成UVC摄像头&#xff0c;开发过程中遇到一个奇怪的事情&#xff0c;通过V4l2框架接口申请内存时&#xff0c;相同的板子&#xff0c;只是内存一个4G一个8G。4G的内存可以申请成功&#xff0c;8G就不行。提示“内存不足” 内存更大反而内存不足&…

RK356X camera 配置

目录 一、RK356x camera硬件 Mipi接口 DVP接口 二、软件链路 三、dts配置 DVP MIPI sensor->dphy->isp_vir 一、RK356x camera硬件 RK356X包含RK3566和RK3568,在camera方面,两颗芯片的软硬件是兼容的,共有两种硬件连接方式:DVP和MIPI。 Mipi接口 图一 如图…

用前端html如何实现2024烟花效果

用HTML、CSS和JavaScript编写的网页&#xff0c;主要用于展示“2024新年快乐&#xff01;”的文字形式烟花效果。下面是对代码主要部分的分析&#xff1a; HTML结构 包含三个<canvas>元素&#xff0c;用于绘制动画。引入百度统计的脚本。 CSS样式 设置body的背景为黑…

批量合并多个Excel到一个文件

工作中&#xff0c;我们经常需要将多个Excel的数据进行合并&#xff0c;很多插件都可以做这个功能。但是今天我们将介绍一个完全免费的独立软件【非插件】&#xff0c;来更加方便的实现这个功能。 准备Excel 这里我们准备了两张待合并的Excel文件 的卢易表 打开的卢易表软件…

GLM4模型详解 - 智谱AI开源大模型全面解析

&#x1f4da; 2024年6月5日,智谱AI在开发者大会上正式开源GLM-4-9B系列大模型。本文将全面解析GLM4的技术特点、部署方案和应用场景。 GLM-4-9B 模型具备了更强大的推理性能、更长的上下文处理能力、多语言、多模态和 All Tools 等突出能力。 “All Tools” 一、模型概述 1.…

ssm-day04 mybatis

mybatis是一个持久层框架&#xff0c;针对的是JDBC的优化 简化数据库操作&#xff0c;能进行单表、多表操作&#xff0c;在这个框架下&#xff0c;需要我们自己写SQL语句 Mapper接口和MapperXML文件就相当于Dao和Dao层的实现 通常将xml文件放在resources包下 &#xff0c;放在…

观测云亮相 Doris Summit,展示流式聚合创新技术

01 技术亮点&#xff1a;重新定义流式聚合的效率与体验 在演讲中&#xff0c;熊豹详细介绍了 GuanceDB 如何支撑观测云的全量业务场景&#xff0c;并分享了 SelectDB 在处理复杂查询中的应用与挑战。从动态表结构管理到函数下推优化&#xff0c;观测云通过灵活的架构设计有效解…

qt中tr的使用

在 Qt 中使用 tr 函数对字符串进行翻译时&#xff0c;通常会通过 Qt Linguist 工具来处理翻译。以下是一个基本的步骤说明&#xff0c;展示如何将 QPushButton *btnnew QPushButton(tr("Hello World"),this); 翻译成其他语言&#xff0c;比如中文&#xff1a; 1.创建…

基于X410的LabVIEW FPGA跳频通信系统开发

跳频通信系统是一种高效的无线通信技术&#xff0c;通过在多个频率间快速切换&#xff0c;提高抗干扰能力和信号安全性。以NI USRP X410为硬件平台&#xff0c;利用LabVIEW FPGA实现一个实时跳频通信系统&#xff0c;涵盖信号生成、触发控制、接收检测及实时数据处理。 系统架构…

校园交友app/校园资源共享小程序/校园圈子集合二手物品交易论坛、交友等综合型生活服务社交论坛

多客校园社交圈子系统搭建 校园交友多功能系统源码: 1、更改学校为独立的模块。整体UI改为绿色&#xff0c;青春色&#xff0c;更贴近校园风格。2、圈子归纳到学校去进行运营。每个学校可建立多个圈子。和其他学校圈子互不干扰。3、增加用户绑定学校&#xff0c;以后进入将默认…

医院跌倒检测识别 使用YOLO,COCO ,VOC格式对4806张原始图片进行标注,可识别病人跌倒,病人的危险行为,病床等场景,预测准确率可达96.7%

医院跌倒检测识别 使用YOLO,COCO ,VOC格式对4806张原始图片进行标注&#xff0c;可识别病人跌倒&#xff0c;病人的危险行为&#xff0c;病床等场景&#xff0c;预测准确率可达96.7&#xff05; 数据集分割 4806总图像数 训练组70&#xff05; 3364图片 有效集20&#…

SQL进阶技巧:如何根据工业制程参数计算良品率?

目录 0 问题描述 1 数据准备 2 问题分析 步骤1&#xff1a;确定每个生产批次的制程参数是否在合格范围内 步骤2&#xff1a;基于中间结果一计算临时良品数量 步骤3&#xff1a;计算良品率&#xff08;最终结果&#xff09; 3 小结 问题拓展&#xff1a;如果制程参数是动…

RK3568平台(内存篇)内存管理架构

一.内存管理架构 内核管理内存的基本单位page页: 物理内存被分割成相同大小的组,内核用页来表示这个组,页是内核管理内存的基本单位,尽管处理器的最小可寻址单位通常是字, 但是, 内存管理单元MMU通常以页为单位进行处理. 因此,从虚拟内存的上来看,页就是最小单位. 页帧…

WEB语义化的新探索:浅析LLMs.txt

【引】有人迷恋使用大模型生成各种有趣的内容&#xff0c; 有人沉醉于大模型相关技术的探索&#xff0c;没有对错&#xff0c;只在于你的乐趣所在。 一项名为 llms.txt 的新提案标志了一些非同寻常的东西的出现: 一个Web网站不仅为人类读者服务&#xff0c;而且为人工智能提供服…

YOLOv10改进,YOLOv10利用DLKAttention融合DCNv3、DCNv4形成全新的可变形大核注意力,并二次创新C2f结构,全网首发

理论介绍 完成本篇需要参考以下三篇文章,并已添加到YOLOv10代码中 YOLOv10改进,YOLOv10添加DCNv3可变性卷积与C2f结构融合(无需编译)YOLOv10改进,YOLOv10添加DCNv4可变性卷积(windows系统成功编译),全网最详细教程YOLOv10改进,YOLOv10添加DLKA-Attention可变形大核注意力…

信奥赛CSP-J复赛集训(bfs专题)(5):洛谷P3395:路障

信奥赛CSP-J复赛集训(bfs专题-刷题题单及题解)(5):洛谷P3395:路障 题目描述 B 君站在一个 n n n\times n n

SSM 技术驱动的垃圾分类系统,引领绿色风尚

第1章 概述 1.1 研究背景 随着现代网络技术发展&#xff0c;对于垃圾分类系统现在正处于网络发展的阶段&#xff0c;所以对它的要求也是比较严格的&#xff0c;要从这个系统的功能和用户实际需求来进行对系统制定开发的发展方式&#xff0c;依靠网络技术的的快速发展和现代通讯…