Platinum Edition Using Windows NT Server 4

Previous chapterNext chapterContents


Chapter 3

Workstation versus Server

Some of the main topics in this chapter are

Since its inception, Windows NT has been available in two forms: a workstation operating system (Windows NT Workstation) and a server operating system (Windows NT Server). Microsoft sells the two platforms with different licenses and different bundled software and portrays them as suitable for different workloads. But what is the difference between the two systems when you remove the software and licenses from consideration? Microsoft has stated publicly that Workstation and Server "share the same kernel architecture," and an article on Microsoft's Web site goes further, describing how Workstation and Server are tuned in a variety of ways to suit their distinct roles: Workstation is for desktop interactive use, and Server is for file, print, and Web serving. Given this information, the reasonable assumption is that Microsoft constructed Workstation and Server from different, although possibly shared, code bases. A binary comparison of the installation CDs, however, reveals that they're the same.

This chapter describes the differences between the two products, starting with the files on the setup. It then shows that only a Registry key or two determines which type of runtime tuning the kernel and user-level applications perform. A detailed study of the operating system and device driver tuning differences follows, and the chapter concludes with a tour of applications that check, for one reason or another, to see whether they're running on Server or Workstation.


NOTE: The work presented here is not based on Windows NT source code, but is the result of careful study of Windows NT's behavior.

File Differences

Windows NT's core operating system and support components are on the setup CDs under directories that identify processor-specific versions. The Windows NT 4.0 final release has about 2,500 unique files when you combine Workstation's and Server's files for any particular processor set.

A binary comparison of corresponding processor subdirectories from a Server CD and a Workstation CD flags about 200 inconsistent files. Roughly 100 of these flagged files ship with Server but not with Workstation. This number includes files related to Dynamic Host Configuration Protocol (DHCP), Domain Name System (DNS) administration, JET database integration, license management, log viewing, Macintosh volume management, network client administration, NetWare migration, remote-system policy management, remote boot management, domain user management, and domain configuration management. Finally, the Server logon bitmaps are only on the Server CD.

Another 10 flagged files ship with Workstation but not Server. Local machine user management and a different version of Disk Manager Help files make up the bulk of this group. In addition, the workstation logon bitmaps ship only on the Workstation CD, of course.

The other 90 or so flagged files have different content for each platform and are related to text-based INF setup files. Most differences in these files result from Windows NT Setup's internal management of virtual setup disks that have different names for Workstation and Server. The remaining differences are the result of file entries related to components that ship on one platform but not the other.

Beneath the processor-specific subdirectories on the Workstation and Server CDs are INETSRV directories. These directories are the locations from which Internet Information Server (IIS) installs on a Server setup and from which Peer Web Services installs on a Workstation setup. Except for a few setup files, the files that make up these products are identical at the binary-level. Server ships with a network monitor and Microsoft's FrontPage Web site-creation software, neither of which comes with Workstation.

How Does the Operating System Know What It Is?

On Windows NT 3.51, only one Registry value separates Workstation from Server:

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ProductOptions\ ProductType

This entry is defined as a string that can be one of three values, as shown in Table 3.1. The value is read early in system initialization, and a little later, the Windows NT Memory Manager initializes in MmInitSystem(), where it sets a global variable, MmProductType, to reflect ProductType's value. Internally, the operating system does not distinguish between a server and an advanced server, so MmProductType is set to 1 for either server and to 0 for Workstation.

Table 3.1 ProductType Registry Values and Corresponding System Types

ProductType System
"Winnt" Workstation
"Servernt" Server
"Lanmannt" Advanced Server

On Windows NT 4.0, two Registry values play a role in separating Workstation from Server, and the ProductType value's role is carried forward from Windows NT 3.51. An 8-byte additional value (two DWORDs) is involved, and it has a component that must be set according to the ProductType value. A violation of this rule results in a blue-screen error during boot. This SYSTEM_LICENSE_VIOLATION error makes the system inoperative.

Another change from Windows NT 3.51 is that Windows NT 4.0 creates Registry notification work items for the two values in question in such a way that if somebody attempts to change them, a system worker thread is notified to write the original value over the change. Attempting to alter ProductType also results in a message box indicating that the user has tried to violate the licensing agreement.

Other operating system subsystems and even device drivers can query the system's type by invoking the exported Memory Manager function MmIsThisAnNtAsSystem(). It simply returns the MmProductType value.

User-level applications have two options for determining the operating system configuration. The first is just to read the ProductType Registry value directly. The second is to call a native Windows NT function, RtlGetNtProductType(), that is in the NTDLL.DLL dynamic link library. When called for the first time after a system boot, RtlGetNtProductType() reads the ProductType registry value and caches the result in an internal variable that it returns to the caller. The internal variable is 1 if the Winnt string was read, 2 if Lanmannt was read, and 3 if Servernt was obtained. Subsequent calls avoid reading the Registry by returning the cached variable.

Operating System Tuning

The MmProductType variable and MmIsThisAnNtAsSystem() function are accessed several times during the Windows NT operating system's initialization to directly affect the values of about 25 internal variables, and they indirectly affect many more. The basic philosophy evidenced by the tuning is that when acting as a server, the responsiveness of the system to file- and network-related work is of primary concern, and the memory footprint is secondary in importance. For Workstation, responsiveness is aimed at systems with multiple applications running simultaneously, and memory footprints are kept smaller to accommodate more applications. Because of this, the responsiveness of system-related functions can suffer. Optimizations based on the product type are performed by the Windows NT Executive, the Memory Manager, the File System Run-Time, the Process Manager, the I/O Manager, the Cache Manager, and the Object Manager.

System Size Determination

One variable directly affected by the product type that has repercussions on many other dynamic tuning variables is the system size identifier, MmSystemSize. It can have one of three values: MmSmallSystem, MmMediumSystem, and MmLargeSystem. The system size is used as the basis for tuning throughout the operating system and affects things such as the number of worker threads that are created and the amount of memory that is set aside for specific subsystem tasks. Listing 3.1 shows a pseudo-code portion of MmInitSystem, in which the system size is calculated, and Table 3.2 shows the amount of memory necessary to rate a system as small, medium, or large. Note that the threshold for large on Server is double the large threshold for Workstation.

Listing 3.1 Pseudo-Code for MmInitSystem()

ULONG      MmMinimumFreePages = 0x1A; // default for workstation
MMInitSystem(...)
{
     ...
     if( MmNumberOfPhysicalPages <= 0xD00 ) {
          // really small system < 12MB
          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;

     if( MmNumberOfPhysicalPages > 0x2100 ) {
          // 33 MB cut-off
          MmModifiedPageMinimum = 0x190;
          MmModifiedPageMaximum = 0x320;
          MmSystemCacheWsMinimum = 0x1F4;
          MmSystemCacheWsMaximum = 0x384;
     }
     // set throttling ranges based on platform
     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 = 0x51;
     }
     MiAdjustWorkingSetManagerParameters( !MmProductType );
     ...
}

Table 3.2 System Size Thresholds

System Size Workstation Thresholds Server Thresholds
Small 0-19M 0-19M
Medium 20-32M 20-64M
Large > 32M > 64M

Enabled Processors

When Windows NT is running on a multiprocessor, it does not necessarily use all processors available. When installed out-of-the-box, Workstation limits itself to two processors, and Server limits itself to four. One Registry setting controlled this in Windows NT 3.51:

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Licensed Processors

As of Windows NT 4.0 Service Pack 2, a second key cross-checks the preceding setting and is protected from modification by system threads (it's illegal to modify these settings). This is the reason that a special "OEM version" of Windows NT is required for machines with more than four processors. The OEM version is little more than Windows NT, with install files that set the number of licensed processors to a specific value that can be greater than four.

Worker Threads

The Windows NT Executive creates system worker threads, and it initializes them in the ExpWorkerInitialization() routine, as shown in Listing 3.2, which uses both the system size and the product type to determine how many threads should be created. Three types of worker threads exist on a Windows NT system, each aimed at different work priorities: delayed worker threads perform low-priority tasks; critical worker threads perform jobs that must be completed as soon as possible, so they run at a real-time priority (their priority is 16), and there is one hypercritical worker thread that is used only for specific system-related operations such as exited-process cleanup. Ideally, the number of worker threads should be high enough that threads pick up work tasks as soon as they're assigned. The trade-off is that idle worker threads needlessly use system resources.

Listing 3.2 Worker Thread Initialization

ExpWorkerInitialization()
{
     BOOLEAN      server;
     int          syssize;
     int          addcritthreads;
     int          adddelaythreads;
     int          count;
     OBJECT_ATTRIBUTES attributes;

     server = MMIsThisAnNtAsSystem();
     switch( MmQuerySystemSize() ) {
     case MmSmallSystem:
          adddelay = 3;
          // 12MB cutoff
          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;
     }
     if( ExpAdditionalCriticalWorkerThreads > 16 )
          ExpAdditionalCriticalWorkerThreads = 16;
     if( ExpAdditionalDelayedWorkerThreads > 16 )
          ExpAdditionalDelayedWorkerThreads = 16;
     KeInitializeQueue( &ExWorkerQueue[ DelayedWorkQueue] , 0 );
     KeInitializeQueue( &ExWirkerQueue[ CriticalWorkQueue], 0);
     KeInitializeQueue( &ExWorkerQueue[ HyperCriticaWorkQueue], 0 );
     InitializeObjectAttributes( &attributes, NULL, NULL, NULL, NULL );

     // create the critical worker threads
     count = 0;
     if( addcrit + ExpAdditionalCriticalWorkerThreads ) {
          do {
               if( PsCreateSystemThread( &threadhandle,
                    FILE_ALL_ACCESS,
                    &attributes, NULL, NULL,
                    ExpWorkerThread, CriticalWorkQueue ) < 0 )
                    break;
               ZwClose( threadhandle );
               ExCriticalWorkerThreads++;
               count++;
          } while( ExpAdditionalCriticalWorkerThreads + addcrit > count );
     }

     // create delayed worker threads
     count = 0;
     if( adddelay + ExpAdditionalDelayedWorkerThreads ) {
          do {
               if( PsCreateSystemThread( &threadhandle,
                    FILE_ALL_ACCESS,
                    &attributes, NULL, NULL,
                    ExpWorkerThread, DelayedWorkQueue ) < 0 )
                    break;
               ZwClose( threadhandle );
               ExDelayedWorkerThreads++;
               count++;
          } while( ExpAdditionalDelayedWorkerThreads + adddelay > count );
     }
     // create 1 hypercritical thread
     if( retval = PsCreateSystemThread( &threadhandle, FILE_ALL_ACCESS,
               &attributes, NULL, NULL, ExpWorkerThread,
               HyperCriticalWorkQueue ) <= 0 )
          ZwClose( threadhandle );
     return retval;
}

Table 3.3 shows how many critical and delayed worker threads are created by default for different parameters. Notice that on medium and large systems, a server has twice as many critical worker threads as a workstation. However, an administrator can direct a workstation to have just as many--or even more--worker threads than a default server configuration by changing Registry settings under this key:

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Executive

The AdditionalCriticalWorkerThreads value under this key controls the number of extra critical worker threads that are created and can be set as high as 16. Similarly, AdditionalDelayedWorkerThreads controls the number of extra delayed worker threads created and can also be set as high as 16. The calculation of the number of worker threads to be created can be seen in the pseudo-code in Listing 3.2, as well as the place where the Executive limits the additional threads to a maximum of 16. Changing the numbers of threads might be necessary in server environments in which tasks are blocked because all available worker threads are busy.

Table 3.3 Worker Threads

System Size Delayed Worker Threads Critical Worker Threads Workstation Server
Small 3 3 3
Medium 3 3 6
Large 3 5 10

As soon as worker threads are started, they execute the function ExpWorkerThread(), which is where the threads sleep. They are awoken when an item is placed on a work queue that the worker threads must process. Pseudo-code for ExpWorkerThread() is shown in Listing 3.3, and it demonstrates that the form of sleep performed by server worker threads differs from that performed by workstation worker threads. Server threads sleep with their stacks locked into memory, as specified with a kernel wait mode, whereas workstation worker threads can have their stacks paged to disk, as indicated with their user wait modes. This optimization means that server worker threads are generally more responsive when work arises because there's never a delay reading their stacks from the disk, but it also means that server threads always contribute to the in-memory footprint of the operating system.

Listing 3.3 Worker Thread Function

ExpWorkerThread( WORK_QUEUE_TYPE workqueue )
{
     KPROCESSOR_MODE          waitmode;

     waitmode = UserMode;
     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:
     do {
          // sleep until something to 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;
}

Memory Manager Tuning

The Memory Manager uses the product type to make tuning decisions in several places. The first decision occurs in MmInitSystem() (refer to Listing 3.1), where it sets the variables MmThrottleTop and MmThrottleBottom. These variables are used by the system's lazy modified-page writer thread in its determination of whether or not to write pages that have been changed out to the paging file, in anticipation of a future need to do so. On a server, these values are about twice as high as on a workstation. The reasoning behind this arrangement is that a workstation is likely to have more paging activity as processes and threads of different interactive applications are started and stopped. It's assumed that servers run a few unchanging applications that have fairly stable memory requirements, and, hence, the anticipation for paging activity is lower.

The next place in which the Memory Manager performs product type tuning is the initialization of the system's working-set manager. The working-set manager is a background thread whose purpose is to trim the in-memory footprint of applications to fit within certain ranges, using an algorithm so complex mere mortals cannot understand it. What is clear is that on workstations with less than 32M of memory, a flag named MiDoPeriodicAggressiveTrimming is set to TRUE, causing the working-set manager to trim the footprints of active processes every second or so, so that more applications are given a chance to simultaneously squeeze at least part of their required data and code into memory. In addition, during process creation on workstations, the Process Manager flags a process for aggressive trimming if either the process's executable image is marked for aggressive working set trimming, or the system size is Small.

The final place in which the Memory Manager accounts for running on a server rather than a workstation is where it determines the size of the operating system's pool of pageable memory, located in MmBuildPagedPool(). The pageable pool is the area from which the operating system and device drivers allocate data, and after it's exhausted, the system fails to function properly. On a server, the paged pool size is a minimum of 50M, but on a workstation, the paged pool size is a function of the available physical memory. The default paged pool and non-paged pool sizes can be controlled by changing the byte-granular values PagedPoolSize and NonPagedPoolSize under this key:

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager

Long File Name Tunneling

The File System Run-Time performs an interesting optimization that has been introduced with Windows NT 4.0. To preserve long file names in the face of legacy 16-bit applications that would otherwise destroy them, the Windows NT 4.0 file system supports the notion of long file name tunneling. Tunneling is necessary when a 16-bit application, such as a word processor, maintains the current version of a document in a temporary file. When the user saves the document, the original is deleted and the temporary is renamed to have the original file's name.

If the original file had a long file name, the name is lost in the absence of tunneling because the rename of the temporary file would only re-create the short-name form of the original file. When tunneling is in effect, the file system "remembers" delete operations for a time (typically 15 seconds), and if a new short-file name file is created with the name of a file that has recently been deleted, the file is automatically assigned the long name of the recently deleted file. On a server, the number of remembered delete operations is 1024 by default, but on a workstation, the number is only 256. The best way to understand this difference is to factor in the assumption that servers, more so than workstations, are likely to serve file systems to large numbers of clients that tend to have much more activity over short periods of time. The default number can be overridden in the Registry value:

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\File System\MaximumTunnelEntries

The time-based window of recall for delete operations can be tuned by setting the MaximumTunnelEntryAgeInSeconds value under the same key.

To see tunneling at work, perform the following steps in an empty directory:

1. Create a file named longfilename.

2. Delete it.

3. Create a new file named longfi~1.

When you do a dir /x, you'll see the reappearance of longfilename!

Process Manager Tuning

One dramatic optimization performed by the Process Manager is that the foreground execution quantum on a server is set to twice that of the foreground quantum on a workstation, and the background quantum on a server, which is identical to its foreground quantum, is six times larger than the corresponding quantum on a workstation. This means that on a server, threads execute for longer periods of time without interruption, and fewer threads generally get a share of a processor over a given duration. Because the implicit assumption is that servers run fewer and less interactive applications than workstations, this optimization usually aids server throughput by doing away with needless thread-switching overhead.

The process quantum adjustment takes place in PspInitPhase0(), part of which is shown in Listing 3.4. In the code, an array of three quantums, named PspForegroundQuantum, is initialized. This array is used by the function PsSetProcessPriorityByClass(), shown in Listing 3.5. PsSetProcessPriorityByClass() is called by the win32 kernel-mode component, win32k.sys, as well as csrss.exe, to set the quantum and memory priorities of foreground and background windows. When the desktop's window focus changes, for example, win32k calls the routine first to set the quantum and memory priority of the window that previously had the focus to background status, and it calls the function again to set the priorities of the new focus window to foreground level. This is where the PspForegroundQuantum array comes into play. The first entry in the array is the quantum assigned to background processes, and either the second or third entries are used for foreground processes. The choice of which foreground quantum to use is determined by the setting of a value in the Registry:

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\PriorityControl\ Win32PrioritySeperation

This value can be defined as 1 or 2, and is used to index into PspForegroundQuantum to get the foreground thread quantum. By default the value is 2. Table 3.4 lists the quantums used by workstation and server, with Foreground1 representing a Win32PrioritySeperation of 1, and Foreground2 representing a Win32PrioritySeperation of 2. By definition, each quantum unit is approximately 3.3 milliseconds in length on an x86 system, making the server execution quantum 120 milliseconds, and the foreground quantum on a workstation 60 milliseconds.

Listing 3.4 Process Manager Initialization

DWORD  PsMinimumWorkingSet = 0x14;
DWORD  PsMaximumWorkingSet = 0x2D;
PspInitPhase0(...)
{
     ...
     switch( MMQuerySystemSize() ) {
     case MmMediumSystem:
          PsMinimumWorkingSet += 0xA;
          PsMaximumWorkingSet += 0x64;
          break;
     case MmLargeSystem:
          PsMinimumWorkingSet += 0x1E;
          PsMaximumWorkingSet += 0x12C;
          break;
     }
     // set up the quantum arrays used by PsSetProcessPriorityByClass
     if( MmIsThisAnNtAsSystem() ) {
          PspForegroundQuantum[0] = 0x24; // background
          PspForegroundQuantum[1] = 0x24; // foreground1
          PspForegroundQuantum[2] = 0x24; // foreground2
     } else {
          PspForegroundQuantum[0] = 0x6; // background
          PspForegroundQuantum[1] = 0xC; // foreground1
          PspForegroundQuantum[2] = 0x12; // foreground2
     }
     ...
}

Listing 3.5 Setting Priorities

PsSetProcessPriorityByClass( int Type, PEPROCESS Process )
{
     BYTE     class;

     class    = Process->PriorityClass;
     priority = PspPriorityTable[ class ];
     if( !Type ) {
          Process->0x15F &= 0xFD;
          seperation  = PsPrioritySeperation;
          mempriority = 2;
     } else {
          seperation  = 0;
          mempriority = 0;
     }
     // set the quantum according to the passed parameter and the
     // foreground quantum array
     if( class != 1 )
          Process->ForegroundQuantum =
               PspForegroundQuantum[ seperation ];
     else
          Process->ForegroundQuantum = 6;
     retval = KeSetPriorityProcess( Process, priority );
     if( Type != 2 )
          retval = MmSetMemoryPriorityProcess( Process, mempriority );
     return retval;
}

Table 3.4 Process Quantums

Type Workstation Server
Background 6 36
Foreground1 12 36
Foreground2 18 36

Lookaside List Tuning

The rest of the optimizations performed by the operating system involve the creation of lookaside lists, new in Windows NT 4.0. Lookaside lists are essentially private stashes of fixed-sized memory chunks that are created for specific tasks. When memory is available in a private store, a more expensive call to the system-wide memory allocator is avoided. When the system memory allocator is called, it might have to flush data to the paging file to free in-core space. Thus, avoiding the system allocator can be a significant performance booster. In all these cases, the lookaside lists are made larger if the system is configured as a server, because performance of these various operations in a server environment is considered more important than the impact of removing this memory from the general pool.

The Object Manager creates a stash for allocating internal object names as well as object creation data structures. On a server, the depths of these lists are 32 and 64 entries, respectively, and on a workstation they're 16 and 32 entries in length.

The Cache Manager creates a lookaside list for the disk write-behind and read-ahead threads that it makes with 128 entries on large workstations and 256 entries on large servers.

Finally, the I/O manager creates three lookaside lists: one for small I/O Request Packets (IRPs) that has four stack locations, one for large IRPs, and one for Memory Descriptor Lists (MDLs). IRPs are commands that are typically sent to device drivers and file systems. MDLs are control structures that describe locked memory regions for device drivers and file systems. Table 3.5 shows how many of each are created for a server versus a workstation. Like the Cache Manager differences, these differences only exist if running on a system where MmSystemSize is equal to MmLargeSystem.

Table 3.5 I/O Manager Lookaside List Sizes for Large Systems

Type Workstation Server
Small IRP 32 96
Large IRP 64 128
MDL 128 256

Device Driver Tuning

Only four device drivers that ship with Windows NT behave differently depending on the platform on which they run: AFD.SYS, SRV.SYS, NWLNKNB.SYS, and NTFS.SYS. AFD.SYS is the device driver responsible for managing Microsoft's Winsock TCP/IP communications protocol. This device driver's first modification is to set the size of the network transfer frame to 4K if running on a workstation and 64K if running on a server. A larger size means that communications will usually be faster but also that allocated buffers will have a negative impact on the system's available memory. The second place in which AFD.SYS modifies a variable (depending on the product type) is where it sets the limit on simultaneous network file transfers to 2 if running on a workstation, but checks the following Registry entry for the limit if running on a server:

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Services\Afd\ Parameters\MaxActiveTransmitFileCount

This variation exists purely to limit the functionality of a workstation, rather than to serve as a performance optimization. Changing the value on a workstation has no effect because it's not even checked.

SRV.SYS is the network server device driver that controls network connections to a machine and its check of the product type is also for functionality limitation. About ten of its variables are restricted to certain values if running on a workstation, the most notable of which is named SrvMaxUsers. On a server, SrvMaxUsers is basically set to an infinite value, but on a workstation, it has an upper limit of ten, meaning that at most, ten simultaneous network connections are allowed to the machine, whether via file transfers or network logons.

Unlike SRV.SYS, NWLNKNB.SYS (the NetWare network NetBIOS communications protocol driver) queries the system type for performance optimization purposes. It maintains an internal cache of network routes that on server systems is made three times larger than on workstations, in apparent anticipation of more network connections.

NTFS.SYS also checks the product type for tuning. In its initialization, a call is made to MmIsThisAnNtAsSystem(), and based on the result, it sets the size of 11 lookaside lists that it creates for memory caching. These lookaside lists are each dedicated to different temporary data structures that must be allocated during file system operations. Once again, in anticipation of more file system activity, on large servers (according to the MmSystemSize variable), the lists are made twice as big as they are on large workstations.

User-Level Application Tuning

Figure 3.1 shows a list of the user-level components that query the system type. The reasons for these checks vary from limiting Workstation's capabilities to performance tuning. I'll highlight a few illustrations.

Winlogon

Winlogon checks the product type to determine which splash screen to display, LANMAN.BMP (for 16-color video modes) or LANMA256.BMP for server logon and WINNT.BMP or WINNT256.BMP for workstation logon. Explorer checks the product type and shows a bitmap along the left side of the Start menu. The bitmap reads Windows NT Server or Windows NT Workstation, as appropriate.

Disk Administrator

The last application in Figure 3.1 is WINDISK.EXE, the Windows NT disk administrator program. It reads the Registry directly to determine on which platform it's executing. If Windisk is on a server, it provides a Fault Tolerance menu that has entries you can use to create striped sets with parity and mirrored drives. On a workstation, Windisk does not make this menu available. This difference explains why only servers can create and manage fault-tolerant disks.

Fig. 3.1

These user-mode components check the system type.

BackOffice Applications

Several Microsoft products have setup programs that check the product type. Notable examples are BackOffice and IIS. BackOffice refuses to install any BackOffice suite programs, SQL Server, Exchange Server, IIS, and Systems Management Server (SMS), if the product is on a workstation. There's no technical reason for this limitation: Tests show that these applications function properly if the setup program is foiled into installing them on a workstation.

As mentioned earlier, the version of IIS that ships with the Windows NT 4.0 CD checks the product type and installs itself as Peer Web Services if its setup program detects that it's on a workstation but as IIS if it detects that it's on a server.


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.