If DOS is single-tasking, how was multitasking possible in old version of Windows?
I read that DOS is a single-tasking OS.
But if old versions of Windows (also including Windows 95?) were just wrappers of DOS, how could Windows run as a multitasking OS?
Solution 1:
Windows 95
Windows 95 was far more than "just a wrapper" for MS-DOS. Quoting Raymond Chen:
MS-DOS served two purposes in Windows 95.
- It served as the boot loader.
- It acted as the 16-bit legacy device driver layer.
Windows 95 actually hooked/overrode just about all of MS-DOS, keeping it as a compatibility layer while doing all the heavy lifting itself. It also implemented preemptive multitasking for 32-bit programs.
Pre-Windows 95
Windows 3.x and older were mostly 16-bit (with the exception of Win32s, a kinda compatibility layer that bridges 16 and 32, but we'll ignore that here), were more dependent on DOS, and used only cooperative multitasking - that's the one where they don't force a running program to switch out; they wait for the running program to yield control (basically, say "I'm done" by telling the OS to run the next program that's waiting).
Multitasking was cooperative, just like in old versions of MacOS (though unlike Multitasking DOS 4.x, which sported preemptive multitasking). A task had to yield to the OS in order to schedule a different task. The yields were built into certain API calls, notably message processing. As long as a task processed messages in a timely manner, everything was great. If a task stopped processing messages and was busy executing some processing loop, multitasking was no more.
Windows 3.x architecture
As for how early Windows programs would yield control:
Windows 3.1 uses cooperative multitasking - meaning that each application that is in the process of running is instructed to periodically check a message queue to find out if any other application is asking for use of the CPU and, if so, to yield control to that application. However, many Windows 3.1 applications would check the message queue only infrequently, or not at all, and monopolize control of the CPU for as much time as they required. A preemptive multitasking system like Windows 95 will take CPU control away from a running application and distribute it to those that have a higher priority based on the system's needs.
source
All DOS would see is this single application (Windows or other) running, which would pass control around without exiting. In theory, preemptive multitasking can possibly be implemented on top of DOS anyway with the use of a real-time clock and hardware interrupts to forcibly give control to the scheduler. As Tonny comments, this was actually done by some OSes running on top of DOS.
386 enhanced mode?
Note: there have been some comments on 386 enhanced mode of Windows 3.x being 32-bit, and supporting preemptive multitasking.
This is an interesting case. To summarise the linked blog post, 386 enhanced mode was basically a 32-bit hypervisor, which ran virtual machines. Inside one of those virtual machines ran Windows 3.x standard mode, which does all the stuff listed above.
MS-DOS would also run inside those virtual machines, and apparently they were preemptively multitasked - so it seems that the 386 enhanced mode hypervisor will share CPU time slices between the virtual machines (one of which ran normal 3.x and others which ran MS-DOS), and each VM will do its own thing - 3.x would cooperatively multitask, while MS-DOS would be single-tasked.
MS-DOS
DOS itself was single-tasking on paper, but it did have support for TSR programs, that would stay in the background until triggered by a hardware interrupt. Far from true multitasking, but not fully single-tasked either.
All this talk of bit-ness? I asked about multitasking!
Well, strictly speaking the bit-ness and multitasking are not dependent on each other. It should be possible top implement any multitasking mode in any bit-ness. However, the move from 16-bit processors to 32-bit processors also introduced other hardware functionality that could have made preemptive multitasking easier to implement.
Also, since 32-bit programs were new, it was easier to get them to work when they're forcibly switched out - which might have broken some legacy 16-bit programs.
Of course, this is all speculation. If you really want to know why MS didn't implement preemptive multitasking in Windows 3.x (386 enhanced mode notwithstanding), you'll have to ask someone who worked there.
Also, I wanted to correct your assumption that Windows 95 was jsut a wrapper for DOS ;)
Solution 2:
It continuously ran a single program, the one called windows. That one spread the CPU time (and other resources) between different programs.
Consider this analogy:
You have an office which can only have one person at the time (That person is called mister or missus DOS). That person works on one thing at the time. E.g. it phones a single person and starts to chat 24/7 with him/her.
Now you replace that person with Mr. secretary. (windows). It will phone someone and talk all the time with it (still a single task). Then after some time the other person will say "I have talked enough for now. Go talk to someone else and call me back in a bit".
Mr. secretary will call the other person. Chat with that one until that person says the same thing. Then it will call the next person until it is at the end of the list of people to speak with. At that time it will begin again at the top.
- In technical terms this is called cooperative multitasking. It requires the other person to say that he/she had enough CPU time. If one does not do that then all falls apart.
- Modern systems are much smarter. Including pre-emptive multitasking. Think of the secretary setting an alarm clock and cutting the other person off after 5 minutes. "That is nice Jane. But I have to talk to Joe now. I'll call you back in a bit. - Click."
If you add multiple processors it gets even more complicated. :)
Solution 3:
In a modern operating system, the operating system controls all hardware resources, and running applications are kept in sandboxes. An application is not permitted to access memory that the OS has not allocated to that application, and it cannot directly access hardware devices in the computer. If hardware access is required, the application must communicate through device drivers.
The OS can enforce this control, because it forces the CPU to enter protected mode.
DOS, on the other hand, never enters protected mode, but stays in real mode *. In real mode, the running applications can perform anything that it wants to, e.g. access hardware directly. But an application running in real mode can also tell the CPU to enter protected mode.
And this last part allows applications like Windows 95 to start a multi threaded environment even though they were basically launched from DOS.
DOS (Disk Operating System) was, afaik, not much more than file management system. It provided a file system, mechanisms for navigating the file system, a few tools, and the possibility to launch applications. It did also allow for some applications to stay resident, e.g. mouse drivers and EMM emulators. But it did not attempt to control the hardware in the computer the way a modern OS does.
* When DOS was first created in the 70's, protected mode did not exist in the CPU. It wasn't until the 80286 processor in the mid 80's that protected mode became part of the CPU.
Solution 4:
Prior to Windows 3.x which was the first version to multitask DOS applications, there were programs such as DesqView which could do likewise. If one was e.g. running three DOS sessions at once, then DesqView would create four virtual machines. The three DOS sessions would each think that they "owned" the entire machine, except that none of them would actually perform file I/O. Instead, the version of DOS running in each session would be patched so that it would forward any requests for file I/O to a special session, which was dedicated to that purpose. Since the PC's text mode hardware would continuously display the contents of an area of memory as characters; DesqView could let each session have its own virtual screen by mapping each session's 0xB8000-0xB9FFF range to its own area of RAM, and periodically copying the current application's area to the physical screen buffer. Graphical support was much harder, because 256K of RAM on the display board was controlled using 64K of address space, some I/O registers, and some "interesting" hardware which required things to be read and written in certain specific sequences. In text mode, when an application wrote something to the text buffer was written, DesqView could set a flag indicating it should be copied to the display on the next timer tick; only the first write to the text buffer in a given time tick would require DesqView's intervention; all the others would be consolidated to the next timer tick.
By contrast, virtualizing graphics mode would require DeskView to trap every single individual write to display memory or I/O registers. Given that this would slow down memory writes by a factor of about 100, and graphics programs had to write a lot more data than text programs, real-time virtualization of most graphic software was not practical. Instead, graphics were handled by having any non-foreground application which tried to do graphics pause until it became the foreground application, and then giving it full control over the screen. When control switched to a different application, DesqView would try to make a copy of the state of all graphics registers and then switch. Upon switching back to the graphical application, DesqView would restore the saved state.
In a way, multi-tasking non-graphical DOS applications was easier than multi-tasking Windows applications because there were very few shared resources, and applications didn't have to interact with each other. In Windows, by contrast, it's necessary to deal with things like the clipboard, or the possibility that one program's windows might move in such a fashion as to obscure another's. Windows 95 was the first version of Windows which could overcome such limitations by including things like a windowing system which could accommodate having an area of the screen become unavailable while code was trying to draw to it (with the effect that the drawing would be masked off).