Recognizer & FS & Filter

本文详细解析了Windows操作系统中文件系统的加载过程,包括文件系统识别器(Filesystem Recognizer)、文件系统过滤器(Filesystem Filter)的工作原理及其实现流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注一:
File system recognizer 文件系统识别器(下文简称为recognizer)
File system            文件系统     (下文简称为fs)
File system filter     文件系统过滤器(下文简称为filter)

文件系统识别器是一个标准的NT内核模式驱动程序;它只实现一项功能:检查物理介质设备,如果它能够识别存储介质的格式便加载相应的文件系统驱动程序,利用它主要是为节约系统内存,文件系统驱动程序没用到时为什么让他在内存中呢?


               文件系统过滤驱动的一般处理流程(参照sfilter):

                     DriverEntry
                        ||
                        ||初始化dispatch表中的IRP_MJ_FILE_SYSTEM_CONTROL
                        ||routine
                        ||         
    DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = SfFsControl ...
                        ||
                        ||注册一个SfFsNotification;当文件系统或文件系统识别器
                        ||registers or unregisters时被调用
                        ||
    IoRegisterFsRegistrationChange(DriverObject, SfFsNotification)
                        ||
                        ||
                        //
     SfFsNotification(PDEVICE_OBJECT DeviceObject, BOOLEAN FsActive)
                        ||
                        || FsActive
        TRUE <----------//------------> FALSE
         ||                              ||
         ||                              ||
IoAttachDeviceToDeviceStack         IoDetachDevice
         ||
         ||
 /-------//------->Attach to (Filesystem)-----------------/IRP_MJ_FILE_SYSTEM_CONTROL
 |                                                        |
 |                                                        |MN:IRP_MN_MOUNT_VOLUME
 /---------------->Attach to (recognizer)                 |你就尽管Mount,只要fs
                        ||                                |处理状态为STATUS_SUCCESS;
                        ||MJ:IRP_MJ_FILE_SYSTEM_CONTROL   |fs中关于卷的Mount处理
                        //                                |参见FatMountVolume函数
    SfFsControl (PDEVICE_OBJECT DeviceObject, PIRP Irp)   |
                        ||                                //filter其他处理参见sfilter
                        ||MN:    
         /--------------//-----------------------------/
         |                                             |
         |IRP_MN_MOUNT_VOLUME                          |
         |                                             |
 filter等待recognizer处理完成                          
         ||                                            |
         ||                                            |
   /-----//-->STATUS_FS_DRIVER_REQUIRED(1)---|/        |
   |                                         ||       
   /-------->STATUS_UNRECOGNIZED_VOLUME(2)---||       
                                             ||        |
                                             ||        |
                                             //        |
不论结果是哪一个,filter什么动作也没有;                 
但IO管理器收到recognizer处理结果后,处理各不相同:          
(1)表示recognizer认识这个卷,但对应的fs还没启动             |
(2)表示recognizer不认识这个卷,对应的fs不能处理            ||
 如果是第一种情况I/O管理器就会向这个设备发送---------------||IRP_MN_LOAD_FILE_SYSTEM
                                                      ||
                                                      ||原来我们一直挂接在recognizer上的
                                                      //
                                                 IoDetachDevice(recognizer)
                                                      ||
                                                      ||
                                             wait recognizer load fs status
                                                      ||
                                                      ||
                              Other status<-----------//----------->STATUS_SUCCESS
                                 ||                                     
                                 ||recognizer加载对应的fs失败
                                 ||只好还挂到recognizer上
                                 ||
                                 //
                  IoAttachDeviceToDeviceStack(recognizer)

 

注二:IoRegisterFsRegistrationChange函数

在winxp,win2003下会对已加载的文件系统重新枚举一遍
而nt4.0和win2000下则不会,具体原因代码见...
(留意一下IopNotifyAlreadyRegisteredFileSystems的作用和实现)

//win2000
NTSTATUS
IoRegisterFsRegistrationChange(
    IN PDRIVER_OBJECT DriverObject,
    IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine
    )

/*++

Routine Description:

    This routine registers the specified driver's notification routine to be
    invoked whenever a file system registers or unregisters itself as an active
    file system in the system.

Arguments:

    DriverObject - Pointer to the driver object for the driver.

    DriverNotificationRoutine - Address of routine to invoke when a file system
        registers or unregisters itself.

Return Value:

    The return status is the final value of the function.

--*/

{
    PNOTIFICATION_PACKET nPacket;

    PAGED_CODE();

    //
    // Begin by attempting to allocate storage for the shutdown packet.  If
    // one cannot be allocated, simply return an appropriate error.
    //

    nPacket = ExAllocatePoolWithTag( PagedPool,
                                     sizeof( NOTIFICATION_PACKET ),
                                     'sFoI' );
    if (!nPacket) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Initialize the notification packet and insert it onto the tail of the
    // list.
    //

    nPacket->DriverObject = DriverObject;
    nPacket->NotificationRoutine = DriverNotificationRoutine;

    ExAcquireResourceExclusive( &IopDatabaseResource, TRUE );
    InsertTailList( &IopFsNotifyChangeQueueHead, &nPacket->ListEntry );
    ExReleaseResource( &IopDatabaseResource );

    //
    // Increment the number of reasons that this driver cannot be unloaded.
    //

    ObReferenceObject( DriverObject );

    return STATUS_SUCCESS;
}

 

//win2003
NTSTATUS
IoRegisterFsRegistrationChange(
    IN PDRIVER_OBJECT DriverObject,
    IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine
    )

/*++

Routine Description:

    This routine registers the specified driver's notification routine to be
    invoked whenever a file system registers or unregisters itself as an active
    file system in the system.

Arguments:

    DriverObject - Pointer to the driver object for the driver.

    DriverNotificationRoutine - Address of routine to invoke when a file system
        registers or unregisters itself.

Return Value:

    STATUS_DEVICE_ALREADY_ATTACHED -
                Indicates that the caller has already registered
                last with the same driver object & driver notification

    STATUS_INSUFFICIENT_RESOURCES
    STATUS_SUCCESS

--*/

{
    PNOTIFICATION_PACKET nPacket;

    PAGED_CODE();

    ExAcquireResourceExclusiveLite( &IopDatabaseResource, TRUE );

    if (!IsListEmpty( &IopFsNotifyChangeQueueHead )) {

        //
        // Retrieve entry at tail of list
        //

        nPacket = CONTAINING_RECORD( IopFsNotifyChangeQueueHead.Blink, NOTIFICATION_PACKET, ListEntry );

        if ((nPacket->DriverObject == DriverObject) &&
            (nPacket->NotificationRoutine == DriverNotificationRoutine)) {

            ExReleaseResourceLite( &IopDatabaseResource);
            return STATUS_DEVICE_ALREADY_ATTACHED;
        }
    }

    //
    // Begin by attempting to allocate storage for the shutdown packet.  If
    // one cannot be allocated, simply return an appropriate error.
    //

    nPacket = ExAllocatePoolWithTag( PagedPool|POOL_COLD_ALLOCATION,
                                     sizeof( NOTIFICATION_PACKET ),
                                     'sFoI' );
    if (!nPacket) {

        ExReleaseResourceLite( &IopDatabaseResource );
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Initialize the notification packet and insert it onto the tail of the
    // list.
    //

    nPacket->DriverObject = DriverObject;
    nPacket->NotificationRoutine = DriverNotificationRoutine;

    InsertTailList( &IopFsNotifyChangeQueueHead, &nPacket->ListEntry );

    IopNotifyAlreadyRegisteredFileSystems(&IopNetworkFileSystemQueueHead, DriverNotificationRoutine, FALSE);
    IopNotifyAlreadyRegisteredFileSystems(&IopCdRomFileSystemQueueHead, DriverNotificationRoutine, TRUE);
    IopNotifyAlreadyRegisteredFileSystems(&IopDiskFileSystemQueueHead, DriverNotificationRoutine, TRUE);
    IopNotifyAlreadyRegisteredFileSystems(&IopTapeFileSystemQueueHead, DriverNotificationRoutine, TRUE);

    //
    // Notify this driver about all already notified filesystems
    // registered as an active file system of some type.
    //


    ExReleaseResourceLite( &IopDatabaseResource );

    //
    // Increment the number of reasons that this driver cannot be unloaded.
    //

    ObReferenceObject( DriverObject );

    return STATUS_SUCCESS;
}


VOID
IopNotifyAlreadyRegisteredFileSystems(
    IN PLIST_ENTRY  ListHead,
    IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine,
    IN BOOLEAN SkipRaw
    )
/*++

Routine Description:

    This routine calls the driver notification routine for filesystems
    that have already been registered at the time of the call.

Arguments:

    ListHead - Pointer to the filesystem registration list head.
    DriverNotificationRoutine - Pointer to the routine that has to be called.

Return Value:

    None.

--*/
{
    PLIST_ENTRY entry;
    PDEVICE_OBJECT fsDeviceObject;

    entry = ListHead->Flink;
    while (entry != ListHead) {

        //
        // Skip raw filesystem notification
        //
        if ((entry->Flink == ListHead) && (SkipRaw)) {
            break;
        }

        fsDeviceObject = CONTAINING_RECORD( entry, DEVICE_OBJECT, Queue.ListEntry );
        entry = entry->Flink;
        DriverNotificationRoutine( fsDeviceObject, TRUE );
    }
}

注三:IoRegisterFileSystem函数

是文件系统在DriverEntry中最后要调用的函数,主要是向IO管理器中注册一下,以后有卷需要Mount时通知我...

VOID
IoRegisterFileSystem(
    IN OUT PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    This routine inserts the device object for the file system which the device
    object represents into the list of file systems in the system.

Arguments:

    DeviceObject - Pointer to device object for the file system.

Return Value:

    None.


--*/

{
    PNOTIFICATION_PACKET nPacket;
    PLIST_ENTRY entry;

    PAGED_CODE();

    //
    // Allocate the I/O database resource for a write operation.
    //

    (VOID) ExAcquireResourceExclusive( &IopDatabaseResource, TRUE );

    //
    // Insert the device object into the appropriate file system queue based on
    // the driver type in the device object.  Notice that if the device type is
    // unrecognized, the file system is simply not registered.
    //

    if (DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) {
        InsertHeadList( &IopNetworkFileSystemQueueHead,
                        &DeviceObject->Queue.ListEntry );
    } else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) {
        InsertHeadList( &IopCdRomFileSystemQueueHead,
                        &DeviceObject->Queue.ListEntry );
    } else if (DeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) {
        InsertHeadList( &IopDiskFileSystemQueueHead,
                        &DeviceObject->Queue.ListEntry );
    } else if (DeviceObject->DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM) {
        InsertHeadList( &IopTapeFileSystemQueueHead,
                        &DeviceObject->Queue.ListEntry );
    }

    //
    // Ensure that this file system's device is operable.
    //

    DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    //
    // Notify all of the registered drivers that this file system has been
    // registered as an active file system of some type.
    //

    //PS:看到了吧,如果你的filter调用了IoRegisterFsRegistrationChange,
    //文件系统注册时就要通过你啦,TRUE-Load/FALSE-unload.
    entry = IopFsNotifyChangeQueueHead.Flink;
    while (entry != &IopFsNotifyChangeQueueHead) {
        nPacket = CONTAINING_RECORD( entry, NOTIFICATION_PACKET, ListEntry );
        entry = entry->Flink;
        nPacket->NotificationRoutine( DeviceObject, TRUE );
    }

    //
    // Release the I/O database resource.
    //

    ExReleaseResource( &IopDatabaseResource );

    //
    // Increment the number of reasons that this driver cannot be unloaded.
    //

    ExInterlockedAddUlong( &DeviceObject->ReferenceCount, 1, &IopDatabaseLock );
}

注四:
   有兴趣的话可以看看文件识别器,IO管理器,文件系统的实现.

const express = require(&#39;express&#39;); const multer = require(&#39;multer&#39;); const cors = require(&#39;cors&#39;); const fs = require(&#39;fs&#39;); const path = require(&#39;path&#39;); const app = express(); const port = 3000; // 中间件 app.use(cors()); app.use(express.json()); // 健康检查端点 app.get(&#39;/api/health&#39;, (req, res) =&gt; { const modelStatus = checkModelStatus(); res.json({ status: &#39;ok&#39;, message: &#39;语音识别服务运行正常&#39;, modelStatus: modelStatus }); }); // 配置multer用于文件上传 const storage = multer.diskStorage({ destination: (req, file, cb) =&gt; { const uploadDir = &#39;uploads&#39;; if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir); } cb(null, uploadDir); }, filename: (req, file, cb) =&gt; { const ext = path.extname(file.originalname) || &#39;.mp3&#39;; cb(null, Date.now() + ext); } }); const upload = multer({ storage: storage, limits: { fileSize: 10 * 1024 * 1024, }, fileFilter: (req, file, cb) =&gt; { if (file.mimetype.startsWith(&#39;audio/&#39;)) { cb(null, true); } else { cb(new Error(&#39;只支持音频文件&#39;), false); } } }); // Vosk模型路径 const modelPath = &#39;./vosk-model&#39;; // 详细的模型检查函数 const checkModelStatus = () =&gt; { if (!fs.existsSync(modelPath)) { return { exists: false, message: &#39;模型文件夹不存在&#39;, requiredFiles: [&#39;am/&#39;, &#39;conf/&#39;, &#39;graph/&#39;], suggestion: &#39;请下载并重命名模型文件夹为 vosk-model&#39; }; } const requiredDirs = [&#39;am&#39;, &#39;conf&#39;, &#39;graph&#39;]; const missingDirs = requiredDirs.filter(dir =&gt; !fs.existsSync(path.join(modelPath, dir)) ); if (missingDirs.length &gt; 0) { return { exists: true, complete: false, missing: missingDirs, message: &#39;模型文件夹不完整&#39;, suggestion: &#39;请下载完整的Vosk中文模型&#39; }; } // 检查关键文件 const requiredFiles = [ &#39;conf/mfcc.conf&#39;, &#39;conf/model.conf&#39;, &#39;graph/HCLr.fst&#39;, &#39;graph/Gr.fst&#39; ]; const missingFiles = requiredFiles.filter(file =&gt; !fs.existsSync(path.join(modelPath, file)) ); if (missingFiles.length &gt; 0) { return { exists: true, complete: false, missingFiles: missingFiles, message: &#39;模型文件不完整&#39; }; } return { exists: true, complete: true, message: &#39;模型完整可用&#39; }; }; // 检查Vosk模块是否可用 let voskAvailable = false; try { require(&#39;vosk&#39;); voskAvailable = true; console.log(&#39;✅ Vosk模块加载成功&#39;); } catch (e) { console.warn(&#39;❌ Vosk模块不可用:&#39;, e.message); } // 模拟识别函数 const simulateRecognition = (filePath) =&gt; { return new Promise((resolve) =&gt; { setTimeout(() =&gt; { const phrases = [ &quot;这是一个语音识别测试结果&quot;, &quot;欢迎使用语音转文字服务&quot;, &quot;今天的天气真不错&quot;, &quot;人工智能正在改变世界&quot;, &quot;语音识别技术越来越先进了&quot; ]; const randomText = phrases[Math.floor(Math.random() * phrases.length)]; resolve({ success: true, text: randomText, simulated: true }); }, 1500); }); }; // 语音转文字API app.post(&#39;/api/speech-to-text&#39;, upload.single(&#39;audio&#39;), async (req, res) =&gt; { console.log(&#39;收到音频文件:&#39;, req.file?.filename); if (!req.file) { return res.status(400).json({ error: &#39;没有上传音频文件&#39; }); } try { const modelStatus = checkModelStatus(); // 如果模型不存在或不完整,使用模拟模式 if (!modelStatus.exists || !modelStatus.complete) { console.log(&#39;使用模拟模式,模型状态:&#39;, modelStatus); const result = await simulateRecognition(req.file.path); // 清理文件 if (fs.existsSync(req.file.path)) { fs.unlinkSync(req.file.path); } return res.json({ ...result, modelStatus: modelStatus }); } // 如果Vosk模块不可用,也使用模拟模式 if (!voskAvailable) { console.log(&#39;Vosk模块不可用,使用模拟模式&#39;); const result = await simulateRecognition(req.file.path); if (fs.existsSync(req.file.path)) { fs.unlinkSync(req.file.path); } return res.json({ ...result, modelStatus: modelStatus }); } // 使用真实的Vosk识别 console.log(&#39;使用Vosk进行语音识别&#39;); const vosk = require(&#39;vosk&#39;); const model = new vosk.Model(modelPath); console.log(&quot;🚀 ~ modelPath:&quot;, modelPath) const rec = new vosk.Recognizer({ model: model, sampleRate: 16000 }); console.log(&quot;🚀 ~ rec:&quot;, rec) const audioData = fs.readFileSync(req.file.path); console.log(&quot;🚀 ~ audioData:&quot;, audioData) let result; if (rec.acceptWaveform(audioData)) { result = rec.result(); } else { result = rec.partialResult(); } console.log(&quot;🚀 ~ result:&quot;, result) // 清理资源 rec.free(); if (fs.existsSync(req.file.path)) { fs.unlinkSync(req.file.path); } res.json({ success: true, text: result.text || &#39;未识别到内容&#39;, result: result, modelStatus: modelStatus }); } catch (error) { console.error(&#39;语音识别错误:&#39;, error); // 清理文件 if (req.file &amp;&amp; fs.existsSync(req.file.path)) { fs.unlinkSync(req.file.path); } res.status(500).json({ error: &#39;语音识别处理失败&#39;, details: error.message, modelStatus: checkModelStatus() }); } }); // 获取模型信息的API app.get(&#39;/api/model-info&#39;, (req, res) =&gt; { const modelStatus = checkModelStatus(); res.json(modelStatus); }); // 启动服务 app.listen(port, &#39;0.0.0.0&#39;, () =&gt; { console.log(&#39;=&#39;.repeat(60)); console.log(&#39;🚀 语音识别服务启动成功&#39;); console.log(&#39;=&#39;.repeat(60)); console.log(`📍 本地访问: http://localhost:${port}`); console.log(&#39;=&#39;.repeat(60)); // 检查模型状态 const modelStatus = checkModelStatus(); console.log(&#39;📦 模型状态:&#39;); console.log(&#39; - 存在:&#39;, modelStatus.exists); console.log(&#39; - 完整:&#39;, modelStatus.complete); if (!modelStatus.exists) { console.log(&#39;❌ 模型文件夹不存在&#39;); console.log(&#39;💡 请执行以下步骤:&#39;); console.log(&#39; 1. 访问 https://alphacephei.com/vosk/models&#39;); console.log(&#39; 2. 下载 vosk-model-cn-0.22.zip&#39;); console.log(&#39; 3. 解压并重命名文件夹为 vosk-model&#39;); console.log(&#39; 4. 放置在项目根目录&#39;); } else if (!modelStatus.complete) { console.log(&#39;❌ 模型不完整&#39;); console.log(&#39;💡 缺失内容:&#39;, modelStatus.missing || modelStatus.missingFiles); console.log(&#39;💡 请下载完整的Vosk中文模型&#39;); } else { console.log(&#39;✅ 模型完整可用&#39;); } console.log(&#39;=&#39;.repeat(60)); console.log(&#39;📋 测试接口:&#39;); console.log(` - 健康检查: http://localhost:${port}/api/health`); console.log(` - 模型信息: http://localhost:${port}/api/model-info`); console.log(&#39;=&#39;.repeat(60)); });后端代码如上,前端代码如下:recorderManager.start({ duration: 60000, // 录音时长,单位ms sampleRate: 44100, // 采样率 numberOfChannels: 1, // 录音通道数 encodeBitRate: 192000, // 编码码率 format: &#39;mp3&#39; // 音频格式,支持aac/mp3 }),发送录音文件后,后端代码输出:收到音频文件: 1755842922597.mp3 使用Vosk进行语音识别 LOG (VoskAPI:ReadDataFiles():model.cc:213) Decoding params beam=12 max-active=5000 lattice-beam=4 LOG (VoskAPI:ReadDataFiles():model.cc:216) Silence phones 1:2:3:4:5:6:7:8:9:10 LOG (VoskAPI:RemoveOrphanNodes():nnet-nnet.cc:948) Removed 0 orphan nodes. LOG (VoskAPI:RemoveOrphanComponents():nnet-nnet.cc:847) Removing 0 orphan components. LOG (VoskAPI:ReadDataFiles():model.cc:248) Loading i-vector extractor from ./vosk-model/ivector/final.ie LOG (VoskAPI:ComputeDerivedVars():ivector-extractor.cc:183) Computing derived variables for iVector extractor LOG (VoskAPI:ComputeDerivedVars():ivector-extractor.cc:204) Done. LOG (VoskAPI:ReadDataFiles():model.cc:282) Loading HCL and G from ./vosk-model/graph/HCLr.fst ./vosk-model/graph/Gr.fst LOG (VoskAPI:ReadDataFiles():model.cc:303) Loading winfo ./vosk-model/graph/phones/word_boundary.int 🚀 ~ modelPath: ./vosk-model 🚀 ~ rec: Recognizer { handle: &lt;Buffer@0x00000292DA298F40 type: { size: 0, indirection: 1, get: [Function: get], set: [Function: set], name: &#39;void&#39;, ffi_type: &lt;Buffer@0x00007FFE45082CF8 name: &#39;void&#39;&gt; }&gt; } 🚀 ~ audioData: &lt;Buffer@0x00000292C6E68F40 ff fb 90 c4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... 54701 more bytes&gt; 🚀 ~ result: { partial: &#39;&#39; }为什么识别不到文字
最新发布
08-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值