`//**************************************************************************** // // TUNING IN NTOSKRNL.EXE // // This psuedocode was generated from observations of NT 4.0 final release // (build 1381) behavior. No NT source code was used in this study. // // Note that Service Pack 1 has modifications that add an additional // consistency check when determining if a system is workstation or server. // //**************************************************************************** // // This is how the system size variable is determined: // // PhysPages MB MmSystemSize // =< 0x1300 19 Small // =< 0x2000 32 Medium // workstation: > 0x2000 32 Large // server: > 0x4000 64 Large // // this is *the* kernel variable that is used to differentiate server from // Workstation. See ExInitSystemPhase2(), below, to see how the variable is // initialized. BOOLEAN MmProductType; // used by MmIsThisAnNtAsSystem #define PRODWORKSTATION 0 #define PRODSERVER 1 // my own private definitions #define WORKSTATION 1 #define LANMANAGER 2 #define SERVER 3 // // no change from NT 3.51 // // Worker threads perform global tasks on behalf of many operating system // and device driver components. // // Basically, server critical and hypercritical worker threads mark themselves // as kernel waitmode so that their stacks are ineligable for paging. On a // workstation the thread stacks are subject to paging. While this makes the // server work threads more responsive when work arises, but means they // contribute to the systems in-core footprint at all times. // ExpWorkerThread( WORK_QUEUE_TYPE workqueue ) { KPROCESSOR_MODE waitmode; // // Default wait mode is user // waitmode = UserMode; // // Do initialization depending on which workqueue this thread // will service // switch( workqueue ) { case CriticalWorkQueue: // // real-time thread (priority is set to 16 here) // if( MMIsThisAnNtAsSystem() ) waitmode = KernelMode; KeSetPriorityThread( curthread, 16 ); break; case DelayedWorkQueue: // // low-priority (priority is set to 12 here) // KeSetBasePriorityThread( curthread, 4 ); break; case HyperCriticalWorkQueue: // // medium priority thread (priority set to 15 here) // if( MMIsThisAnNTAsSystem() ) waitmode = KernelMode; KeSetBasePriorityThread( curthread, 7 ); break; } dowork: // // loop waiting for work to appear on the worker queues // do { workitem = KeRemoveQueue( ExWorkerQueue[ workqueue ], waitmode, 0 ); workitem->function( workitem->reference ); } while( !KeGetCurrentIrql() ) // // oops - error! // KeBugCheckEx( IRQ_NOT_LESS_OR_EQUAL, workitem->reference, KeGetCurrentIrql(), workitem->reference, workitem ); goto dowork; } // // // FsRtlInitializeTunnel // // New in NT 4.0. Tunneling allows for the preservation of short // file names in the face of legacy 16-bit "non long name aware" applications. // // By default server allocates 64 tunneling lookaside entries and workstation // allocates 16. In additionl, server keep track of up to 1024 tunneling // operations per 15 seconds versus workstation's 256 . // Overrides for these defaults are checked for in: // // \Registry\Machine\System\CurrentControlSet\Control\FileSystem // // with the values: // // MaximumTunnelEntries // and MaxiumumTunnelEntryAgeInSeconds // UNICODE_STRING fstunnelparam; WCHAR maxtunnelentries[] = "MaximumTunnelEntries"; WCHAR maxtunnelage[] = "MaximumTunnelEntryAgeInSeconds"; FsRtlInitializeTunnel() { // // tunnel defaults // if( MMIsThisAnNtAsSystem() ) TunnelMaxEntries = 1024; else TunnelMaxEntries = 256; TunnelMaxAge = 15; //seconds // // check registry for override for entries // fstunnelparam.Length = 0x28; fstunnelparam.MaximumLength = 0x2A; fstunnelparam.Buffer = maxtunnelentries; FsRtlGetTunnelParameterValue( &fstunnelparam, &TunnelMaxEntries ); // // check registry for override for age // fstunnelparam.Length = 0x3C; fstunnelparam.MaximumLength = 0x3E; fstunnelparam.Buffer = maxtunnelage; FsRtlGetTunnelParameterValue( &fstunnelparam, &TunnelMaxAge ); if( !TunnelMaxAage ) TunnelMaxEntries = 0; // // convert seconds to ticks // TunnelMaxAge *= 0x989680; // // do validation // maxtunnel = 256; if( TunnelMaxEntries <= 0xFFFF ) maxtunnel = TunnelMaxEntries / 16; if( !maxtunnel && TunnelMaxEntries ) maxtunnel = TunnelMaxEntries + 1; if( maxtunnel > 256 ) maxtunnel = 256; // // create the lookaside list // return ExInitializePagedLookasideList( &TunnelLookasideList, NULL, NULL, NULL, 0x88 /* size */, 'TunL', maxtunnel ); } // // The parameters set here are used for working set trimming, which normally // occurs when memory usage is high. In server, as well as 32MB+ workstation // systems, agressive trimming is not performed, whereas in smaller // workstations agressive trimming generally means that when the number of // system free pages falls within the agresssive trim ranges, that // MmWorkingSetManager() will try to trim the working set of the current // process to fit within the cache working set ranges (agressivecachewsmin/ // max). Note that on workstation, the process manager honors a bit in the // PE (Portable Executable) header of a process' executable image file, that // can specify that the process memory should be aggressively trimmed. // LONG MmWorkingSetReductionMax = 0x3C; LONG MmWorkingSetReductionMin = 0xC; LONG MmPeriodicAgressiveWsMin = 0x4E2; LONG MiIdealPassFaultCountDisable = 0xF; BOOLEAN MiDoPeriodicAgressiveTrimming = FALSE; MiAdjustWorkingSetManagerParameters( BOOLEAN IsWorkstation ) { // // cutoff for aggressive trimming is just less than 32MB // if( IsWorkstation && MmNumberOfPhysicalPages <= 0x1F00 ) { MiDoPeriodicAgressiveTrimming = TRUE; MmWorkingSetVolReductionMax = 0x64; MiIdealPassFaultCountDisable = 0x2D; MmWorkingSetReductionMax = 0x64; MmWorkingSetReductionMin = 0x28; MmPeriodicAgressiveCacheWsMin = 0x3E8; // >= 15MB if( MmNumberOfPhysicalPages >= 0xF00 ) MmPeriodicAgressiveCacheWsMin = 0x44C; // >= 19MB if( MmNumberOfPhysicalPages >= 0x1300 ) MmPeriodicAgressiveCacheWsMin = 0x4E2; // >= 23MB if( MmNumberOfPhysicalPages >= 0x1700 ) MmPeriodicAgressiveCacheWsMin = 0x5DC; } else { MiIdealPassFaultCountDisable = 0xF; } } // // identical in NT 3.51 // // The ProdType variable is not referenced anywhere in the kernel. // // The throttling parameters are used to tune the lazy modified page writer. It // will not write a modified page out immediately if the number of available // pages in the system is within the throttling ranges (and a several // other conditions hold). Server also attempt to keep 81 pages free at // all times while workstation only tries to keep 26 free (where did they get // these numbers?!) // ULONG MmMinimumFreePages = 26; // default for workstation MMInitSystem(...) { ... if( MmNumberOfPhysicalPages <= 0xD00 ) { // // really small system <= 13MB // MmSystemSize = MmSmallSystem; MmMaximumDeadKernelStacks = 0; MmModifiedPageMaximum = 0x64; MmModifiedPageMinimum = 0x28; MmCodeClusterSize = 1; MmReadClusterSize = 2; MmDataClusterSize = 0; } else if( MmNumberOfPhysicalPages <= 0x1300 ) { // // small system <= 19MB // MmSystemSize = MmSmallSystem; MmMaximumDeadKernelStacks = 2; MmModifiedPageMaximum = 0x96; MmModifiedPageMinimum = 0x50; MmSystemCacheWsMaximum = 0x96; MmSystemCacheWsMinimum = 0x64; MmCodeClusterSize = 2; MmDataClusterSize = 1; MmReadClusterSize = 4; } else { // // other - make it medium sized system for now // MmSystemSize = MmMediumSystem; MmMaximumDeadKernelStacks = 5; MmModifiedPageMaximum = 0x12C; MmModifiedPageMinimum = 0x96; MmSystemCacheWsMinimum = 0x190; MmSystemCacheWsMaximum = 0x320; MmCodeClusterSize = 7; MmDataClusterSize = 3; } // // cutoff for workstation large system is 32MB, for a server its 64MB // if( MmNumberOfPhysicalPages > 0x2000 && MmProductType == 'Wi' || MmNumberOfPhysicalPages > 0x4000 ) MmSystemSize = MmLargeSystem; // // adjust settings for 33MB or greater // if( MmNumberOfPhysicalPages > 0x2100 ) { // 33 MB cut-off MmModifiedPageMinimum = 0x190; MmModifiedPageMaximum = 0x320; MmSystemCacheWsMinimum = 0x1F4; MmSystemCacheWsMaximum = 0x384; } // // set lazy writer throttling ranges // if( MmProductType == 'Wi' ) { ProdType = WORKSTATION; MmProductType = PRODWORKSTATION; MmThrottleBottom = 0x1E; MmThrottleTop = 0xFA; } else { if( MmProductType == 'La' ) ProdType = LANMANAGER; else ProdType = SERVER; MmProductType = PRODSERVER; MmThrottleBottom = 0x50; MmThrottleTop = 0x1C2; MmMinimumFreePages = 81; } MiAdjustWorkingSetManagerParameters( !MmProductType ); ... } // // new in NT 4.0 // // ccdepth is the depth of the lookaside list that is used for work-item // data structures that are used by the read-ahead and write-behind worker // threads. // CcInitializeCacheManager(..., depthparam, ...) { // // CcCapturedSystemSize is just a copy of MmSystemSize. // Use it to determine how big to make the cache manager // lookaside list // switch( CcCapturedSystemSize ) { case MmSmallSystem: ccdepth = 32; break; case MmMediumSystem: ccdepth = 64; break; case MmLargeSystem: // server is different from workstation if( MmIsThisAnNtAsSystem ) ccdepth = 256; else ccdepth = 128; break; default: ccdepth = depthparam; break; } ExInitializeNPagedLookasideList( CCTwilightLookasideList, NULL, NULL, NULL, 0x10, 'Ccwk', ccdepth /* depth */); } // // same in NT 3.51 // // Server: more critical worker threads than workstation, however, the number // of worker threads can be tuned through the registry key // // \Registry\Machine\System\CurrentControlSet\Control\Session Manager\Executive // // with the values: // // AdditionalCriticalWorkerThreads and // AdditionalDelayedWorkerThreads // // Note that the number of additional threads for each queue that can // be specified through the Registry is 16. // // Note that the term 'hypercritical' used for one of the worker queues is // actually a misnomer since its thread runs at a priority lower than critical // worker threads. Its purpose is process cleanup when processes exit. // ExpWorkerInitialization() { BOOLEAN server; int syssize; int addcrit; int adddelay; int count; OBJECT_ATTRIBUTES attributes; server = MMIsThisAnNtAsSystem(); // // Set the default numbers of worker threads // switch( MmQuerySystemSize() ) { case MmSmallSystem: adddelay = 3; // adjust down by 1 if system is smaller than // 12MB addcrit = 3 - ( MmNumberOfPhysicalPages < 0xC00 ); break; case MmMediumSystem: addcrit = 3; adddelay = 3; if( server ) addcrit = 6; break; case MmLargeSystem: adddelay = 3; addcrit = 5; if( server ) addcrit = 10; break; default: adddelay = 2; addcrit = 2; } // // limit the additional critical threads specified in the registry // if( ExpAdditionalCriticalWorkerThreads > 16 ) ExpAdditionalCriticalWorkerThreads = 16; // // limit the additional delayed threads specified in the registry // if( ExpAdditionalDelayedWorkerThreads > 16 ) ExpAdditionalDelayedWorkerThreads = 16; // // initialize the worke queues // KeInitializeQueue( &ExWorkerQueue[CriticalWorkQueue], 0 ); KeInitializeQueue( &ExWorkerQueue[DelayedWorkQueue], 0); KeInitializeQueue( &ExWorkerQueue[HyperCriticalWorkQueue], 0 ); // // initialize object attributes for use in all thread creation calls // InitializeObjectAttributes( &attributes, NULL, NULL, NULL, NULL ); // // create critical worker threads // count = 0; if( addcrit + ExpAdditionalCriticalWorkerThreads ) { do { if( PsCreateSystemThread( &threadhandle, THREADY_ANY_ACCESS, &attributes, NULL, NULL, ExpWorkerThread, CriticalWorkQueue ) < 0 ) break; ZwClose( threadhandle ); ExCriticalWorkerThreads++; count++; } while( ExpAdditionalCriticalWorkerThreads + addcrit > count ); } // // critical delayed worker threads // count = 0; if( adddelay + ExpAdditionalDelayedWorkerThreads ) { do { if( PsCreateSystemThread( &threadhandle, THREAD_ANY_ACCESS, &attributes, NULL, NULL, ExpWorkerThread, DelayedWorkQueue ) < 0 ) break; ZwClose( threadhandle ); ExDelayedWorkerThreads++; count++; } while( ExpAdditionalDelayedWorkerThreads + adddelay > count ); } // // Create one hypercritical worker thread (for exited process // cleanup) // if( retval = PsCreateSystemThread( &threadhandle, THREAD_ANY_ACCESS, &attributes, NULL, NULL, ExpWorkerThread, HyperCriticalWorkQueue ) <= 0 ) ZwClose( threadhandle ); return retval; } // // similar IRP zone size tuning takes place in NT 3.51 // // 4 stack locations for small Irps // IoInitSystem(...) { ... switch( MmQuerySystemSize() ) { case MmSmallSystem: smalldepth = 6; largedepth = 8; mdldepth = 0x10; break; case MmMediumSystem: smalldepth = 0x18; largedepth = 0x20; mdldepth = 0x5a; break; case MmLargeSystem: if( MMIsThisAnNtAsSystem() ) { smalldepth = 0x60; largedepth = 0x80; mdldepth = 0x10; } else { smalldepth = 0x20; largedepth = 0x40; mdldepth = 0x80; } break; default: largedepth = 0x7C; smalldepth = 0x7C; mdldepth = 0x7C; break; } // // Create large IRP lookaside list // ExInitializeNPagedLookasideList( IopLargeIrpLookasideList, NULL, NULL, NULL, IopLargeIrpStackLocations * 0x24 /* size */, 'Irpl', largedepth ); // // Create small IRP lookaside list // ExInitializeNPagedLookasideList( IopSmallIrpLookasideList, NULL, NULL, NULL, 0x94 /* size */, 'Irps', smalldepth ); // // Create MDL lookaside list // ExInitializeNPagedLookasideList( IopMdlLookasideList, NULL, NULL, NULL, 0x78 /* size */, 'Mdl ', mdldepth ); ... } // // same in NT 3.51 // // The size of the paged pool indicates that maximum amount of paged pool // memory that can be charged on the system. Pagedpool is typically the // physical memory size in workstation, but in server its a min of 50MB. // // This adjustment can be emulated by changing values in the registry. Both // the paged and non-paged pool sizes can be adjusted in the key: // // \Registry\Machine\System\CurrentControlSet\Control\Session Manager\ // Memory Management // // by modifying these values: // // PagedPoolSize // and NonPagedPoolSize // // The values are interpreted as bytes. // MiBuildPagedPool(...) { ... MMSizeOfPagedPoolInBytes = MmMaximumNonPagedPoolInBytes * 2; // // minimum 50MB paged pool for server // if( MmIsThisAnNtAsSystem() && MMSizeOfPagedPoolInBytes < 0x03000000 ) MMSizeOfPagedPoolInBytes = 0x03000000; if( MmSizeOfPagedPoolInBytes < MmNonPagedSystemStart - MmPagedPoolStart ) MmSizeOfPagedPoolInBytes = MmNonPagedSystemStart; ... } // // The Object Manager maintains stashed os memory (lookaside lists) // for interal object creation // // create depths same in NT 3.51, name lookaside is new for NT 4.0 // ObInitSystem(...) { // // If initializing // if( InitializationPhase ) { // // Determine size of lookaside lists based // on system size and product type // if( MMQuerySystemSize() == MmLargeSystem ) { if( MMIsThisAnNtAsSystem() ) { createdepth = 0x40; namedepth = 0x20; } else { createdepth = 0x20; namedepth = 0x10; } } else { createdepth = 0x3; namedepth = 0x3; } // // Create object info lookaside list // ExINitializeNPagedLookasideList( ObpCreateInfoLookasideList, NULL, NULL, NULL, 0x30, 0x6943624F 'ObCi', createdepth ); // // Create object name lookaside list // ExINitializeNPagedLookasideList( ObpNameBufferLookasideList, NULL, NULL, NULL, 0xF8, 'ObNm', namedepth ); } ... } // // new in NT 4.0 // // The process manager sets the execution quanta based on // the system type. One quanta unit is about 3.3 milliseconds on // x86 systems. The first entry in the array is the quantum for // background threads. Foreground threads have a quantum determined // by the entry in the array corresponding the foreground/background // priority seperation value. This value is taken from the registry // and is 0,1, or 2. The value is set using the performance tab // in the "System" applet in the control panel. // // Thus, the foreground quantum on a Workstation is 60 milliseconds // and the background quantum is 20 milliseconds. On Server, the quantum // for foreground and background threads are the same at 120 milliseconds. // DWORD PsMinimumWorkingSet = 0x14; DWORD PsMaximumWorkingSet = 0x2D; PspInitPhase0(...) { ... switch( MMQuerySystemSize() ) { case MmMediumSystem: PsMinimumWorkingSet += 10; PsMaximumWorkingSet += 0x64; break; case MmLargeSystem: PsMinimumWorkingSet += 30; PsMaximumWorkingSet += 0x12C; break; } // // see PsSetProcessPriorityByClass for an explanation of the // different entries // if( MmIsThisAnNtAsSystem() ) { PspForegroundQuantum[0] = 36; // background PspForegroundQuantum[1] = 36; // foreground1 PspForegroundQuantum[2] = 36; // foreground2 } else { PspForegroundQuantum[0] = 6; // background PspForegroundQuantum[1] = 12; // foreground1 PspForegroundQuantum[2] = 18; // foreground2 } ... } // // same in NT 3.51 // // Routine used by OS and device drivers to query the system type // BOOLEAN MmIsThisAnNtAsSystem() { return MmProductType; } // // workstation adds a higher priority for threads that are woken - presumably // for interactive apps - new in NT 4.0 // // This is called from functions like ExReleaseFastMutexUnsage(), // ExReleaseHandleTableShared(), ExReleaseHandleTableExclusive(), // ExReleaseResourceForThreadLite(), and ExReleaseResourceLite() // KeSetEventBoostPriority(...) { ... // add a priority boost of 1 for workstation KiUnwaitThread( !MmProductType(), thread ); // thread passed in ecx ... } // // where worker threads watch the keys - watcher threads and systemprefix // are new NT 4.0 // WCHAR nameprefixkey[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Setup"; WCHAR naemprefixvalue[] = "SystemPrefix"; WCHAR nameprodkey[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ProductOptions" ); WCHAR nameprodvalue[] = "ProductType"; LARGE_INTEGER SystemPrefix; ExInitSystemPhase2() { ... // // Process systemprefix key // RtlInitUnicodeString( &sysprefix, nameprefixkey ); InitializeObjectAttributes( &attributes, &sysprefixname, NULL, NULL, NULL ); if( ZwOpenKey( &ExpSetupKey, STANDARD_RIGHTS_WRITE, &attributes ) < 0 ) goto error; // // Query system prefix value // RtlInitUnicodeString( &sysprefixval, nameprefixvalue ); if( ZwQueryValueKey( ExpSetupKey, &sysprefix, KeyValuePartialInformation, &PrefixInfo, 0x22, &reslength ) < 0 ) goto error; // // copy the prefix into global // ExpSetupSystemPrefix.LowPart = PrefixInfo.Data[0]; ExpSetupSystemPrefix.HighPart = PrefixInfo.Data[1]; // // watch the system prefix value // ExpWatchSystemPrefixWorkItem = 0; ExpSystemPrefixValid = 1; if( ZwNotifyChangeKey( ExpSetupKey, 0, ExpWatchSystemPrefixWorkItem, 1, ExpSystemPrefixIoSb, REG_LEGAL_CHANGE_FILTER, 0, ExpSystemPrefixChangeBuffer, 4, 1 ) < 0 ) goto error; // // process product type key // ExpProductWorkItem = 0; RtlInitUnicodeString( &prodtype, nameprodkey ); // // open its key // InitializeObjectAttributes( &attributes, prodtype, NULL, NULL, NULL ); if( ZwOpenKey( &ExpProductTypeKey, STANDARD_RIGHTS_WRITE, &attributes ) < 0 ) goto error; RtlInitUnicodeString( &prodtypeval, nameprodvalue ); if( (ExpProductTypeValueInfo = ExAllocatePool( NonPagedPool, 0x22 )) < 0 ) goto error; // // query its value // if( ZwQueryValueKey( ExpProductTypeKey, &prodtypeval, KeyValuePartialInformation, ExpProductTypeValueInfo, 0x22, &reslength ) < 0 ) goto error; // // make sure product type and system prefix match // if( ExpSystemPrefixValid ) { actualprod = ExpProductTypeValueInfo->Data; if( ExpSetupSystemPrefix.LowPart & 0x04000000 ) { // // must be server or lanman // RtlInitUnicodeString( &tmpstring, L"LanmanNT" ); if( !RtlEqualUnicodeString( actualprod, &tmpstring, FALSE )) { RtlInitUnicodeString( &tmpstring, L"ServerNT" ); if( !RtlEqualUnicodeString( actualprod, &tmpstring, FALSE )) { // // Fatal error if no match // KeBugCheck( SYSTEM_LICENSE_VIOLATION); // no return } } } else { // // must be workstation // RtlInitUnicodeString( &tmpstring, L"WinNT" ); if( !RtlEqualUnicodeString( actualprod, &tmpstring, FALSE )) { // // Fatal error if no match // KeBugCheck( SYSTEM_LICENSE_VIOLATION ); // no return } } // // check validity of the rest of system prefix // ... // // have worker threads watch the value // if( ZwNotifyChangeKey( ExpProductTypeKey, 0, ExpWatchProductTypeWorkItem, 1, ExpProductTypeIoSb, REG_LEGAL_CHANGE_FILTER, 0, ExpProductTypeChangeBuffer, 4, 1 ) < 0 ) goto error; } ... } // // this it the routine that is pointed at by ExpProductTypeIoSb in the // changekey notification call. A system worker thread calls this when // somebody attempts to write a new value to the product type value - a // similar function handles the system prefix, but doesn't throw the // hard error. // ProducTypeRewrite() { PHANDLE threadhandle; UNICODE_STRING tmpstring; // // Close and reopen the product type key // ZwClose( ExpProductTypeKey ); RtlInitUnicodeString( &tmpstring, nameprodkey ); InitializeObjectAttributes( &attributes, prodtype, NULL, NULL, NULL ); if( ZwOpenKey( &ExpProductTypeKey, STANDARD_RIGHTS_WRITE, &attributes ) < 0 ) return; // // If this isn't setup mode, rewrite the original value // if( !ExpSetupModeDetected ) { // // write back the original value that was read // RtlInitUnicodeString( &tmpstring, nameprodvalue ); if( ZwSetValueKey( ExpProductTypeKey, &tmpstring, KeyValuePartialInformation, ExpProductTypeValueInfo->Data, ExpProductTypeValueInfo->Length) < 0 ) return; // // flush the change // ZwFlushKey( ExpProductTypeKey ); } // // watch the new key // ZwNotifyChangeKey( ExpProductTypeKey, 0, ExpWatchProductTypeWorkItem, 1, ExpProductTypeIoSb, REG_LEGAL_CHANGE_FILTER, 0, ExpProductTypeChangeBuffer, 4, 1 ); // // If this isn't setup mode, throw a hard error to create a // message box // if( !ExpSetupModeDetected ) { // // post a hard error, which is displayed in the message box // if( PsCreateSystemThread( &threadhandle, FILE_ANY_ACCESS, NULL, NULL, NULL, ExpEpirationThread, SYSTEM_LICENSE_VIOLATION ) < 0 ) return; ZwClose( threadhandle ); } } // // Used by crss and win32k to change a process' priority. // The type parameter controls the function: // 0: set foreground quantum according to seperation priority, set // memory priority 1 // 1: set foreground quantum to 6, set memory priority 0 // 2: set foreground quantum according to seperation priority, don't // set memory priority // // PsPrioritySeperation is obtained from the registry key: // // \Registry\Machine\System\CurrentControlSet\Control\PriorityControl // // in the value: // // Win32PrioritySeperation // // It is by default set to 2, so the second entry in the quantum table // accessed (see PspInitPhase0() to see the priority seperation quantum tables // being initialized). This value is controlled by the System control panel // applet where a user can control the priority boost of foreground // applications. // PsSetProcessPriorityByClass( int Type, PEPROCESS Process ) { BYTE class, mempriority; KPRIORITY priority; class = Process->PriorityClass; priority = PspPriorityTable[ class ]; if( !Type ) { // // foreground process // Process->0x15F &= 0xFD; seperation = PsPrioritySeperation; mempriority = 2; } else { // // background process // seperation = 0; mempriority = 0; } // // Foreground quanta is either taken fromt the // table, or made *very* background // if( class != 1 ) Process->ForegroundQuantum = PspForegroundQuantum[ seperation ]; else Process->ForegroundQuantum = 6; // // Set the execution priority // retval = KeSetPriorityProcess( Process, priority ); // // Set the memory priority // if( Type != 2 ) retval = MmSetMemoryPriorityProcess( Process, mempriority ); return retval; } //**************************************************************************** // // TUNING IN AFD.SYS // //**************************************************************************** Afd is the device driver that manages winsock communications. The default network transfer size is 64K for server and 4K for workstation. This means that performance will be better for server, but it will have a bigger footprint. //**************************************************************************** // // TUNING IN SRV.SYS // //**************************************************************************** Srv is the device driver that manages network file connections. Srv restricts several variables to these values if running on a workstation: SrvMaxUsers = 10 // max network connections SrvMaxReceiveWorkItemCount = 64 SrvMaxThreadsPerQueue = 5 SrvCacheOpenLimit = 0 SrvMaxCachedDirectory = 0 // server: 5 SrvMaxFreeRfcbs = 0 // server: 20 SrvMaxFreeMfcbs = 0 // server: 20 In addition, srv threads that are waiting for work have their stacks locked in memory if running on server, but have them pageable if on workstation (similar to a workstation kernel's critical worker threads, they call KeRemoveQueue() with waitmode == UserMode). //**************************************************************************** // // TUNING IN NTFS.SYS // //**************************************************************************** The sizes of 11 NTFS lookaside lists are twice as large on large servers than on large workstations. //**************************************************************************** // // TUNING IN NWLNKNB.SYS // //**************************************************************************** This driver is the Net BIOS communications driver. Its address cache is 3 times larger on a server than on a workstation.