Windows 95 Multi-tasking

Windows 95 is a 32-bit operating system, and it runs in protected mode. Earlier versions of Windows such as Windows 3.x are 16-bit operating systems, although some features use 32-bit operation. Though both Windows 3.1 and Windows 95 support multi-tasking, they are quite different. In this article, I am going to talk about it.

What is the main difference on multi-tasking between Windows 3.x and Windows 95? It is the way of controlling the execution flow. In multi-tasking systems, resources of the computer are shared. The CPU is shared, the memory is shared, disk drives and screens are shared, and so on. The phrase "execution flow" means the process of executing all running programs on the CPU. There are several ways operating systems control the execution flow. Windows 3.x uses a method called "collaborative multi-tasking", while Windows 95 uses a method called "preemptive multi-tasking". Their difference is, in Windows 3.x, when the CPU is assigned to an application, the application runs for a short while, and then releases the CPU back to Windows. In Windows 95, the application can also release the CPU back to Windows 95 actively. However, in Windows 95, if the application runs for too long, longer than a time slice (it is described as "the process of the application has taken up the time slice"), Windows 95 would preempt the CPU. This results in a major difference between the two generations of Windows: In Windows 3.x, a badly written application may halt the whole system, but in Windows 95, this is not allowed to happen. However, a more accurate description is that, in Windows 3.x, a badly written Win16 application may halt the whole system, but in Windows 95, a badly written Win32 application cannot manage to halt the system.

Windows 95 still kept a lot of 16-bit features. Device drivers in Windows 95 are possibly 16-bit real mode device drivers. Most of the 16-bit device drivers exist in the form of VxD files and DRV files. However, Windows 95 supports Win32 applications. Windows user interface functions locate in USER32.DLL. Most of these functions pass work down to the 16-bit version user interface library, the module USER.DLL. This means Windows 95 still contains a lot of 16-bit code.

Since 16-bit Windows applications are not prepared for preemptive multi-tasking, there should be someway to let them properly run. There are several ideas. The safest way is to implement a virtual machine for 16-bit applications, just like NTVDM (Windows NT Virtual DOS Machine). In this way, all 16-bit tasks are queued and they wouldn't conflict with any 32-bit processes. However, they cannot share data with 32-bit applications via virtual memory (although they can send messages to 32-bit applications). They may be interrupted, and then the OS passes the control to another virtual machine or a 32-bit application. However, the operating system wouldn't interrupt the current 16-bit application and pass the control to another 16-bit application in the same virtual machine. Using a virtual machine makes it easy for the operating system to handle the case. Another way is to let 16-bit applications have exclusive control. This is the way Windows 95 works. It never preempts a long-running Win16 application. It always waits for it to release the CPU.

Why Microsoft brought up and accepted this plan is because Windows 95 is intended to work on old computers that don't have much memory. In order to save memory, Windows 95 doesn't use virtual machines. Of course this design brings problems: If a 16-bit application is not well programmed, it may halt the whole system. On the other hand, USER.DLL functions do run like 16-bit Windows applications so they also have exclusive control. However, because they are well programmed, they never halt the system.

Another technology Windows 95 uses is Win16 Mutex. It protects 16-bit dlls such as USER.DLL from being renentered by multiple threads in one or more processes, no matter whether they are 16-bit or 32-bit. However, this usually doesn't cause the system to halt, assuming the program code in USER.DLL works properly, releasing the Mutex quickly.

A reader provided valuable input to this article about NTVDM compatibility with Win16 programs:

Strictly speaking, 16-bit Windows applications run on Windows NT using Windows on Windows (WoW) and NTVDM and the CPU's VM8086 (virtual mode 8086). WoW on Windows NT is some sort of stripped-down version of Windows WfW (Windows for Workgroups) 3.1. Windows applications also usually do not access the hardware directly, but instead use the Windows interfaces that function as an abstraction layer.

Only programs that do not adhere to the Windows environment and e.g. use DOS functions as a Windows 16 application or want to talk directly to the hardware could have problems in WoW and the VM8086 of Windows NT if the accesses are not intercepted and emulated.

But strictly speaking, such programs are not real Windows programs but rather a mix between a Win16 and DOS application. However, such programs are likely to be extremely rare, because access to DOS as a protected mode Windows application requires that the application must make use of DPMI and the CPU is thus switched to real mode. From this point of view, it makes no sense to go to such lengths and program such an application.

Those who developed for Windows 3.x usually remained in protected mode and only used the APIs and driver interfaces of Windows to communicate with the hardware.

As for MS-DOS programs, in fact, in Windows 3.x and in Windows 95, they run under preemptive multi-tasking (in 386 enhanced mode), and they run in V86 (virtual 8086) mode. V86 mode provides a safe environment for MS-DOS applications that the applications cannot write to memory other than what they own. What's more, MS-DOS system calls are passed to Windows. Windows can filter these calls so the applications cannot halm the system.

For Win32 applications, Windows 95 supports multiple threads. The technology of multi-threading makes user applications more responsive. Each application has one thread when it is started. Each running application is called a process. A process has at least one thread. A process can have many threads. In fact, an application ends when all its threads end; there is no such a concept as a "main thread"; but, the CRT (C language runtime code) calls exit() to force an exit when the main() function in the initial thread ends, so it natually becomes the main thread). Usually, the application can use one thread to interact with the user (called a "UI thread"), and use other threads to do some work in background (called "worker threads"). This means the application needn't be written in an ugly manner when it both needs to interact with the user and to do some work. Originally, if we write a 16-bit Windows application to do both things, we need to use PeekMessage, and then dispatch the message to the window procedures, and go on peeking until no more messages are seen. After all messages are processed, do some work and call PeekMessage again. Actually with the collaborative multi-tasking feature of Windows 3.x, which allows the task to release the CPU by using the PeekMessage or Sleep calls (collaborative multi-tasking is implemented with the help of the assembly language), the work is already much simpler. If it were not for the feature, the computation state must be saved in a global or static variable, and computation stacks should be implemented manually: recursive functions cannot be used. Thus, the advantage of using threads is obvious.

Return to Windows 9x/NT Overview