<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>https://gunkies.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ak120</id>
		<title>Computer History Wiki - User contributions [en]</title>
		<link rel="self" type="application/atom+xml" href="https://gunkies.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ak120"/>
		<link rel="alternate" type="text/html" href="https://gunkies.org/wiki/Special:Contributions/Ak120"/>
		<updated>2026-04-05T00:12:21Z</updated>
		<subtitle>User contributions</subtitle>
		<generator>MediaWiki 1.30.1</generator>

	<entry>
		<id>https://gunkies.org/w/index.php?title=Inside_OS/2&amp;diff=36401</id>
		<title>Inside OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=Inside_OS/2&amp;diff=36401"/>
				<updated>2025-06-13T12:44:07Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* 14  Interactive Programs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the text from the book.&lt;br /&gt;
&lt;br /&gt;
INSIDE OS/2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
INSIDE OS/2 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Gordon Letwin &lt;br /&gt;
Chief Architect, Systems Software, Microsoft(R)&lt;br /&gt;
&lt;br /&gt;
Foreword by Bill Gates &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
PUBLISHED BY&lt;br /&gt;
Microsoft Press&lt;br /&gt;
A Division of Microsoft Corporation&lt;br /&gt;
16011 NE 36th Way, Box 97017, Redmond, Washington 98073-9717&lt;br /&gt;
&lt;br /&gt;
Copyright (C) 1988 by Microsoft Press&lt;br /&gt;
All rights reserved. No part of the contents of this book may be&lt;br /&gt;
reproduced or transmitted in any form or by any means without the written&lt;br /&gt;
permission of the publisher.&lt;br /&gt;
&lt;br /&gt;
Library of Congress Cataloging in Publication Data&lt;br /&gt;
Letwin, Gordon.&lt;br /&gt;
Inside OS/2.&lt;br /&gt;
&lt;br /&gt;
Includes index.&lt;br /&gt;
1. MS OS/2 (Computer operating system) I. Title.&lt;br /&gt;
II. Title: Inside OS/Two.&lt;br /&gt;
QA76.76.063L48    1988    005.4'46    87-31579&lt;br /&gt;
ISBN 1-55615-117-9&lt;br /&gt;
&lt;br /&gt;
Printed and bound in the United States of America&lt;br /&gt;
&lt;br /&gt;
1 2 3 4 5 6 7 8 9 MLML 8 9 0 9 8&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade in the United States by Harper &amp;amp; Row.&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade in Canada by General Publishing Company, Ltd.&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade outside the United States and Canada by&lt;br /&gt;
Penguin Books Ltd.&lt;br /&gt;
&lt;br /&gt;
Penguin Books Ltd., Harmondsworth, Middlesex, England&lt;br /&gt;
Penguin Books Australia Ltd., Ringwood, Victoria, Australia&lt;br /&gt;
Penguin Books N.Z. Ltd., 182-190 Wairau Road, Auckland 10, New Zealand&lt;br /&gt;
&lt;br /&gt;
British Cataloging in publication Data available&lt;br /&gt;
&lt;br /&gt;
Editor: Patricia Pratt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                              Dedication&lt;br /&gt;
&lt;br /&gt;
                               To R.P.W.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Contents&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Foreword by Bill Gates&lt;br /&gt;
&lt;br /&gt;
Introduction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part I: The Project&lt;br /&gt;
&lt;br /&gt;
Chapter 1.  History of the Project&lt;br /&gt;
             1.1  MS-DOS version 1.0&lt;br /&gt;
             1.2  MS-DOS version 2.0&lt;br /&gt;
             1.3  MS-DOS version 3.0&lt;br /&gt;
             1.4  MS-DOS version 4.0&lt;br /&gt;
&lt;br /&gt;
Chapter 2.  Goals and Compatibility Issues&lt;br /&gt;
             2.1  Goals&lt;br /&gt;
                   2.1.1  Graphical User Interface&lt;br /&gt;
                   2.1.2  Multitasking&lt;br /&gt;
                   2.1.3  Memory Management&lt;br /&gt;
                   2.1.4  Protection&lt;br /&gt;
                   2.1.5  Encapsulation&lt;br /&gt;
                   2.1.6  Interprocess Communication (IPC)&lt;br /&gt;
                   2.1.7  Direct Device Access&lt;br /&gt;
             2.2  Compatibility Issues&lt;br /&gt;
                   2.2.1  Real Mode vs Protect Mode&lt;br /&gt;
                   2.2.2  Running Applications in Real (Compatibility) Mode&lt;br /&gt;
                           2.2.2.1  Memory Utilization&lt;br /&gt;
                           2.2.2.2  File Locking&lt;br /&gt;
                           2.2.2.3  Network Piggybacking&lt;br /&gt;
                   2.2.3  Popular Function Compatibility&lt;br /&gt;
                   2.2.4  Downward Compatibility&lt;br /&gt;
                           2.2.4.1  Family API&lt;br /&gt;
                           2.2.4.2  Network Server-Client Compatibility&lt;br /&gt;
&lt;br /&gt;
Chapter 3.  The OS/2 Religion&lt;br /&gt;
             3.1  Maximum Flexibility&lt;br /&gt;
             3.2  Stable Environment&lt;br /&gt;
                   3.2.1  Memory Protection&lt;br /&gt;
                   3.2.2  Side-Effects Protection&lt;br /&gt;
             3.3  Localization of Errors&lt;br /&gt;
             3.4  Software Tools Approach&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part II: The Architecture&lt;br /&gt;
&lt;br /&gt;
Chapter 4.  Multitasking&lt;br /&gt;
             4.1  Subtask Model&lt;br /&gt;
                   4.1.1  Standard File Handles&lt;br /&gt;
                   4.1.2  Anonymous Pipes&lt;br /&gt;
                   4.1.3  Details, Details&lt;br /&gt;
             4.2  PIDs and Command Subtrees&lt;br /&gt;
             4.3  DosExecPgm&lt;br /&gt;
             4.4  DosCWait&lt;br /&gt;
             4.5  Control of Child Tasks and Command Subtrees&lt;br /&gt;
                   4.5.1  DosKillProcess&lt;br /&gt;
                   4.5.2  DosSetPriority&lt;br /&gt;
&lt;br /&gt;
Chapter 5.  Threads and Scheduler/Priorities&lt;br /&gt;
             5.1  Threads&lt;br /&gt;
                   5.1.1  Thread Stacks&lt;br /&gt;
                   5.1.2  Thread Uses&lt;br /&gt;
                           5.1.2.1  Foreground and Background Work&lt;br /&gt;
                           5.1.2.2  Asynchronous Processing&lt;br /&gt;
                           5.1.2.3  Speed Execution&lt;br /&gt;
                           5.1.2.4  Organizing Programs&lt;br /&gt;
                   5.1.3  Interlocking&lt;br /&gt;
                           5.1.3.1  Local Variables&lt;br /&gt;
                           5.1.3.2  RAM Semaphores&lt;br /&gt;
                           5.1.3.3  DosSuspendThread&lt;br /&gt;
                           5.1.3.4  DosEnterCritSec/DosExitCritSec&lt;br /&gt;
                   5.1.4  Thread 1&lt;br /&gt;
                   5.1.5  Thread Death&lt;br /&gt;
                   5.1.6  Performance Characteristics&lt;br /&gt;
             5.2  Scheduler/Priorities&lt;br /&gt;
                   5.2.1  General Priority Category&lt;br /&gt;
                           5.2.1.1  Background Subcategory&lt;br /&gt;
                           5.2.1.2  Foreground and Interactive&lt;br /&gt;
                                     Subcategories&lt;br /&gt;
                           5.2.1.3  Throughput Balancing&lt;br /&gt;
                   5.2.2  Time-Critical Priority Category&lt;br /&gt;
                   5.2.3  Force Background Priority Category&lt;br /&gt;
                   5.2.4  Setting Process/Thread Priorities&lt;br /&gt;
&lt;br /&gt;
Chapter 6.  The User Interface&lt;br /&gt;
             6.1  VIO User Interface&lt;br /&gt;
             6.2  The Presentation Manager User Interface&lt;br /&gt;
             6.3  Presentation Manager and VIO Compatibility&lt;br /&gt;
&lt;br /&gt;
Chapter 7.  Dynamic Linking&lt;br /&gt;
             7.1  Static Linking&lt;br /&gt;
             7.2  Loadtime Dynamic Linking&lt;br /&gt;
             7.3  Runtime Dynamic Linking&lt;br /&gt;
             7.4  Dynlinks, Processes, and Threads&lt;br /&gt;
             7.5  Data&lt;br /&gt;
                   7.5.1  Instance Data&lt;br /&gt;
                   7.5.2  Global Data&lt;br /&gt;
             7.6  Dynamic Link Packages As Subroutines&lt;br /&gt;
             7.7  Subsystems&lt;br /&gt;
                   7.7.1  Special Subsystem Support&lt;br /&gt;
             7.8  Dynamic Links As Interfaces to Other Processes&lt;br /&gt;
             7.9  Dynamic Links As Interfaces to the Kernel&lt;br /&gt;
             7.10 The Architectural Role of Dynamic Links&lt;br /&gt;
             7.11 Implementation Details&lt;br /&gt;
                   7.11.1  Dynlink Data Security&lt;br /&gt;
                   7.11.2  Dynlink Life, Death, and Sharing&lt;br /&gt;
                   7.11.3  Dynlink Side Effects&lt;br /&gt;
             7.12 Dynlink Names&lt;br /&gt;
&lt;br /&gt;
Chapter 8.  File System Name Space&lt;br /&gt;
             8.1  Filenames&lt;br /&gt;
             8.2  Network Access&lt;br /&gt;
             8.3  Name Generation and Compatibility&lt;br /&gt;
             8.4  Permissions&lt;br /&gt;
             8.5  Other Objects in the File System Name Space&lt;br /&gt;
&lt;br /&gt;
Chapter 9.  Memory Management&lt;br /&gt;
             9.1  Protection Model&lt;br /&gt;
             9.2  Memory Management API&lt;br /&gt;
                   9.2.1  Shared Memory&lt;br /&gt;
                   9.2.2  Huge Memory&lt;br /&gt;
                   9.2.3  Executing from Data Segments&lt;br /&gt;
                   9.2.4  Memory Suballocation&lt;br /&gt;
             9.3  Segment Swapping&lt;br /&gt;
                   9.3.1  Swapping Miscellany&lt;br /&gt;
             9.4  Status and Information&lt;br /&gt;
&lt;br /&gt;
Chapter 10. Environment Strings&lt;br /&gt;
&lt;br /&gt;
Chapter 11. Interprocess Communication (IPC)&lt;br /&gt;
             11.1  Shared Memory&lt;br /&gt;
             11.2  Semaphores&lt;br /&gt;
                    11.2.1  Semaphore Recovery&lt;br /&gt;
                    11.2.2  Semaphore Scheduling&lt;br /&gt;
             11.3  Named Pipes&lt;br /&gt;
             11.4  Queues&lt;br /&gt;
             11.5  Dynamic Data Exchange (DDE)&lt;br /&gt;
             11.6  Signals&lt;br /&gt;
             11.7  Combining IPC Forms&lt;br /&gt;
&lt;br /&gt;
Chapter 12. Signals&lt;br /&gt;
&lt;br /&gt;
Chapter 13. The Presentation Manager and VIO&lt;br /&gt;
             13.1  Choosing Between PM and VIO&lt;br /&gt;
             13.2  Background I/O&lt;br /&gt;
             13.3  Graphics Under VIO&lt;br /&gt;
&lt;br /&gt;
Chapter 14. Interactive Programs&lt;br /&gt;
             14.1  I/O Architecture&lt;br /&gt;
             14.2  Ctrl-C and Ctrl-Break Handling&lt;br /&gt;
&lt;br /&gt;
Chapter 15. The File System&lt;br /&gt;
             15.1  The OS/2 File System&lt;br /&gt;
             15.2  Media Volume Management&lt;br /&gt;
             15.3  I/O Efficiency&lt;br /&gt;
&lt;br /&gt;
Chapter 16. Device Monitors, Data Integrity, and Timer Services&lt;br /&gt;
             16.1  Device Monitors&lt;br /&gt;
             16.2  Data Integrity&lt;br /&gt;
                    16.2.1  Semaphores&lt;br /&gt;
                    16.2.2  DosBufReset&lt;br /&gt;
                    16.2.3  Writethroughs&lt;br /&gt;
             16.3  Timer Services&lt;br /&gt;
&lt;br /&gt;
Chapter 17. Device Drivers and Hard Errors&lt;br /&gt;
             17.1  Device Drivers&lt;br /&gt;
                    17.1.1  Device Drivers and OS/2 Communication&lt;br /&gt;
                    17.1.2  Device Driver Programming Model&lt;br /&gt;
                    17.1.3  Device Management&lt;br /&gt;
                    17.1.4  Dual Mode&lt;br /&gt;
             17.2  Hard Errors&lt;br /&gt;
                    17.2.1  The Hard Error Daemon&lt;br /&gt;
                    17.2.2  Application Hard Error Handling&lt;br /&gt;
&lt;br /&gt;
Chapter 18. I/O Privilege Mechanism and Debugging/Ptrace&lt;br /&gt;
             18.1  I/O Privilege Mechanism&lt;br /&gt;
             18.2  Debugging/Ptrace&lt;br /&gt;
&lt;br /&gt;
Chapter 19. The 3x Box&lt;br /&gt;
&lt;br /&gt;
Chapter 20. Family API&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part III: The Future&lt;br /&gt;
&lt;br /&gt;
Chapter 21. The Future&lt;br /&gt;
             21.1  File System&lt;br /&gt;
             21.2  The 80386&lt;br /&gt;
                    21.2.1  Large Segments&lt;br /&gt;
                    21.2.2  Multiple Real Mode Boxes&lt;br /&gt;
                    21.2.3  Full Protection Capability&lt;br /&gt;
                    21.2.4  Other Features&lt;br /&gt;
             21.3  The Next Ten Years&lt;br /&gt;
&lt;br /&gt;
Glossary&lt;br /&gt;
&lt;br /&gt;
Index&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Acknowledgments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Although a book can have a single author, a work such as OS/2 necessarily&lt;br /&gt;
owes its existence to the efforts of a great many people. The architecture&lt;br /&gt;
described herein was hammered out by a joint Microsoft/ IBM design team:&lt;br /&gt;
Ann, Anthony, Carolyn, Ed, Gordon, Jerry, Mark, Mike, Ray, and Ross. This&lt;br /&gt;
team accomplished a great deal of work in a short period of time.&lt;br /&gt;
     The bulk of the credit, and my thanks, go to the engineers who&lt;br /&gt;
designed and implemented the code and made it work. The size of the teams&lt;br /&gt;
involved throughout the project prevents me from listing all the names&lt;br /&gt;
here. It's hard for someone who has not been involved in a software project&lt;br /&gt;
of this scope to imagine the problems, pressure, chaos, and &amp;quot;reality&lt;br /&gt;
shifts&amp;quot; that arise in a never-ending stream. These people deserve great&lt;br /&gt;
credit for their skill and determination in making OS/2 come to pass.&lt;br /&gt;
     Thanks go to the OS/2 development staffers who found time, in the heat&lt;br /&gt;
of the furnace, to review and critique this book: Ian Birrell, Ross Cook,&lt;br /&gt;
Rick Dewitt, Dave Gilman, Vic Heller, Mike McLaughlin, Jeff Parsons, Ray&lt;br /&gt;
Pedrizetti, Robert Reichel, Rajen Shah, Anthony Short, Ben Slivka, Pete&lt;br /&gt;
Stewart, Indira Subramanian, Bryan Willman, and Mark Zbikowski.&lt;br /&gt;
     I'd like to give special thanks to Mark Zbikowski and Aaron Reynolds,&lt;br /&gt;
the &amp;quot;gurus of DOS.&amp;quot; Without their successes there would never have been an&lt;br /&gt;
opportunity for a product such as OS/2.&lt;br /&gt;
     And finally I'd like to thank Bill Gates for creating and captaining&lt;br /&gt;
one hell of a company, thereby making all this possible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Foreword&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 is destined to be a very important piece of software. During the&lt;br /&gt;
next 10 years, millions of programmers and users will utilize this system.&lt;br /&gt;
From time to time they will come across a feature or a limitation and&lt;br /&gt;
wonder why it's there. The best way for them to understand the overall&lt;br /&gt;
philosophy of the system will be to read this book. Gordon Letwin is&lt;br /&gt;
Microsoft's architect for OS/2. In his very clear and sometimes humorous&lt;br /&gt;
way, Gordon has laid out in this book why he included what he did and why&lt;br /&gt;
he didn't include other things.&lt;br /&gt;
     The very first generation of microcomputers were 8-bit machines, such&lt;br /&gt;
as the Commodore Pet, the TRS-80, the Apple II, and the CPM 80 based&lt;br /&gt;
machines. Built into almost all of them was Microsoft's BASIC Interpreter.&lt;br /&gt;
I met Gordon Letwin when I went to visit Heath's personal computer group&lt;br /&gt;
(now part of Zenith). Gordon had written his own BASIC as well as an&lt;br /&gt;
operating system for the Heath system, and he wasn't too happy that his&lt;br /&gt;
management was considering buying someone else's. In a group of about 15&lt;br /&gt;
people, he bluntly pointed out the limitations of my BASIC versus his.&lt;br /&gt;
After Heath licensed my BASIC, I convinced Gordon that Microsoft was the&lt;br /&gt;
place to be if you wanted your great software to be popular, and so he&lt;br /&gt;
became one of Microsoft's first 10 programmers. His first project was to&lt;br /&gt;
single-handedly write a compiler for Microsoft BASIC. He put a sign on his&lt;br /&gt;
door that read&lt;br /&gt;
&lt;br /&gt;
        Do not disturb, feed, poke, tease...the animal&lt;br /&gt;
&lt;br /&gt;
and in 5 months wrote a superb compiler that is still the basis for all our&lt;br /&gt;
BASIC compilers. Unlike the code that a lot of superstar programmers write,&lt;br /&gt;
Gordon's source code is a model of readability and includes precise&lt;br /&gt;
explanations of algorithms and why they were chosen.&lt;br /&gt;
     When the Intel 80286 came along, with its protected mode completely&lt;br /&gt;
separate from its compatible real mode, we had no idea how we were going to&lt;br /&gt;
get at its new capabilities. In fact, we had given up until Gordon came up&lt;br /&gt;
with the patented idea described in this book that has been referred to as&lt;br /&gt;
&amp;quot;turning the car off and on at 60 MPH.&amp;quot; When we first explained the idea to&lt;br /&gt;
Intel and many of its customers, they were sure it wouldn't work. Even&lt;br /&gt;
Gordon wasn't positive it would work until he wrote some test programs that&lt;br /&gt;
proved it did.&lt;br /&gt;
     Gordon's role as an operating systems architect is to overview our&lt;br /&gt;
designs and approaches and make sure they are as simple and as elegant as&lt;br /&gt;
possible. Part of this job includes reviewing people's code. Most&lt;br /&gt;
programmers enjoy having Gordon look over their code and point out how it&lt;br /&gt;
could be improved and simplified. A lot of programs end up about half as&lt;br /&gt;
big after Gordon has explained a better way to write them. Gordon doesn't&lt;br /&gt;
mince words, however, so in at least one case a particularly sensitive&lt;br /&gt;
programmer burst into tears after reading his commentary. Gordon isn't&lt;br /&gt;
content to just look over other people's code. When a particular project&lt;br /&gt;
looks very difficult, he dives in. Currently, Gordon has decided to&lt;br /&gt;
personally write most of our new file system, which will be dramatically&lt;br /&gt;
faster than our present one. On a recent &amp;quot;vacation&amp;quot; he wrote more than 50&lt;br /&gt;
pages of source code.&lt;br /&gt;
     This is Gordon's debut as a book author, and like any good designer he&lt;br /&gt;
has already imagined what bad reviews might say. I think this book is both&lt;br /&gt;
fun and important. I hope you enjoy it as much as I have.&lt;br /&gt;
&lt;br /&gt;
Bill Gates&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Introduction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technological breakthroughs develop in patterns that are distinct from&lt;br /&gt;
patterns of incremental advancements. An incremental advancement--an&lt;br /&gt;
improvement to an existing item--is straightforward and unsurprising. An&lt;br /&gt;
improvement is created; people see the improvement, know what it will do&lt;br /&gt;
for them, and start using it.&lt;br /&gt;
     A major advance without closely related antecedents--a technological&lt;br /&gt;
breakthrough--follows a different pattern. The field of communication is a&lt;br /&gt;
good example. Early in this century, a large infrastructure existed to&lt;br /&gt;
facilitate interpersonal communication. Mail was delivered twice a day, and&lt;br /&gt;
a variety of efficient services relayed messages. A businessman dictated a&lt;br /&gt;
message to his secretary, who gave it to a messenger service. The service&lt;br /&gt;
carried the message to its nearby destination, where a secretary delivered&lt;br /&gt;
it to the recipient.&lt;br /&gt;
     Into this environment came a technological breakthrough--the&lt;br /&gt;
telephone. The invention of the telephone was a breakthrough, not an&lt;br /&gt;
incremental advance, because it provided an entirely new way to communicate.&lt;br /&gt;
It wasn't an improvement over an existing method. That it was&lt;br /&gt;
a breakthrough development impeded its acceptance. Most business people&lt;br /&gt;
considered it a newfangled toy, of little practical use. &amp;quot;What good does it&lt;br /&gt;
do me? By the time I dictate the message, and my secretary writes it down&lt;br /&gt;
and gives it to the mailroom, and they phone the addressee's mailroom, and&lt;br /&gt;
the message is copied--perhaps incorrectly--and delivered to the&lt;br /&gt;
addressee's secretary, it would have been as fast to have it delivered by&lt;br /&gt;
messenger! All my correspondents are close by, and, besides, with&lt;br /&gt;
messengers I don't have to pay someone to sit by the telephone all day in&lt;br /&gt;
case a message comes in.&amp;quot;&lt;br /&gt;
     This is a classic example of the earliest stages of breakthrough&lt;br /&gt;
technology--potential users evaluate it by trying to fit it into present&lt;br /&gt;
work patterns. Our example businessman has not yet realized that he needn't&lt;br /&gt;
write the message down anymore and that it needn't be copied down at the&lt;br /&gt;
destination. He also doesn't realize that the reason his recipients are&lt;br /&gt;
close by is that they have to be for decent messenger delivery. The&lt;br /&gt;
telephone relaxed this requirement, allowing more efficient locations near&lt;br /&gt;
factories and raw materials or where office space was cheaper. But it&lt;br /&gt;
was necessary for the telephone to be accepted before these advantages &lt;br /&gt;
could be realized.&lt;br /&gt;
     Another impedance to the acceptance of a breakthrough technology is&lt;br /&gt;
that the necessary new infrastructure is not in place. A telephone did&lt;br /&gt;
little good if your intended correspondent didn't have one. The nature of&lt;br /&gt;
telephones required a standard; until that standard was set, your&lt;br /&gt;
correspondent might own a phone, but it could be connected to a network&lt;br /&gt;
unreachable by you. Furthermore, because the technology was in its infancy,&lt;br /&gt;
the facilities were crude.&lt;br /&gt;
     These obstacles were not insurmountable. The communications&lt;br /&gt;
requirements of some people were so critical that they were willing to&lt;br /&gt;
invent new procedures and to put up with the problems of the early stages.&lt;br /&gt;
Some people, because of their daring or ambition, used the new system to&lt;br /&gt;
augment their existing system. And finally, because the new technology was&lt;br /&gt;
so powerful, some used it to enhance the existing technology. For example,&lt;br /&gt;
a messenger service might establish several offices with telephone linkage&lt;br /&gt;
between them and use the telephones to speed delivery of short messages by&lt;br /&gt;
phoning them to the office nearest the destination, where they were copied&lt;br /&gt;
down and delivered normally. Using the telephone in this fashion was&lt;br /&gt;
wasteful, but where demand for the old service was high enough, any&lt;br /&gt;
improvement, however &amp;quot;wasteful,&amp;quot; was welcome.&lt;br /&gt;
     After it has a foot in the door, a breakthrough technology is&lt;br /&gt;
unstoppable. After a time, standards are established, the bugs are worked&lt;br /&gt;
out, and, most important, the tool changes its users. Once the telephone&lt;br /&gt;
became available, business and personal practices developed in new&lt;br /&gt;
patterns, patterns that were not considered before because they were not&lt;br /&gt;
possible. Messenger services used to be fast enough, but only because,&lt;br /&gt;
before the telephone, the messenger service was the fastest technology&lt;br /&gt;
available. The telephone changed the life-style of its users.&lt;br /&gt;
     This change in the structure of human activity explains why an&lt;br /&gt;
intelligent person could say, &amp;quot;Telephones are silly gadgets,&amp;quot; and a few&lt;br /&gt;
years later say, &amp;quot;Telephones are indispensable.&amp;quot; This change in the tool&lt;br /&gt;
user--caused by the tool itself--also makes predicting the ultimate effect&lt;br /&gt;
of the new technology difficult. Extrapolating from existing trends is&lt;br /&gt;
wildly inaccurate because the new tool destroys many practices and creates&lt;br /&gt;
wholly unforeseen ones. It's great fun to read early, seemingly silly&lt;br /&gt;
predictions of life in the future and to laugh at the predictors, but the&lt;br /&gt;
predictors were frequently intelligent and educated. Their only mistake was&lt;br /&gt;
in treating the new development as an incremental advance rather than as a&lt;br /&gt;
breakthrough technology. They saw how the new development would improve&lt;br /&gt;
their current practices, but they couldn't see how it would replace those&lt;br /&gt;
practices.&lt;br /&gt;
     Digital computers are an obvious breakthrough technology, and they've&lt;br /&gt;
shared the classic three-stage pattern: &amp;quot;exotic toys,&amp;quot; &amp;quot;limited use,&amp;quot; and&lt;br /&gt;
&amp;quot;indispensable.&amp;quot; Mainframe computers have gone the full route, in the&lt;br /&gt;
milieu of business and scientific computing. IBM's initial estimate of the&lt;br /&gt;
computer market was a few dozen machines. But, as the technology and the&lt;br /&gt;
support infrastructure grew, and as people's ways of working adapted to&lt;br /&gt;
computers, the use of computers grew--from the census bureau, to life&lt;br /&gt;
insurance companies, to payroll systems, and finally to wholly new&lt;br /&gt;
functions such as MIS (Management Information Sciences) systems and airline&lt;br /&gt;
reservation networks.&lt;br /&gt;
     Microcomputers are in the process of a similar development. The&lt;br /&gt;
&amp;quot;exotic toy&amp;quot; stage has already given way to the &amp;quot;limited use&amp;quot; stage. We're&lt;br /&gt;
just starting to develop standards and infrastructure and are only a few&lt;br /&gt;
years from the &amp;quot;indispensable&amp;quot; stage. In anticipation of this stage,&lt;br /&gt;
Microsoft undertook the design and the development of OS/2.&lt;br /&gt;
     Although studying the mainframe computer revolution helps in trying to&lt;br /&gt;
predict the path of the microcomputer revolution, microcomputers are more&lt;br /&gt;
than just &amp;quot;cheap mainframes.&amp;quot; The microcomputer revolution will follow the&lt;br /&gt;
tradition of breakthroughs, creating new needs and new uses that cannot be&lt;br /&gt;
anticipated solely by studying what happened with mainframe systems.&lt;br /&gt;
     This book was written because of the breakthrough nature of the&lt;br /&gt;
microcomputer and the impact of the coming second industrial revolution.&lt;br /&gt;
The designers of OS/2 tried to anticipate, to the greatest extent possible,&lt;br /&gt;
the demands that would be placed on the system when the tool--the personal&lt;br /&gt;
computer--and the tool user reached their new equilibrium. A knowledge of&lt;br /&gt;
MS-DOS and a thorough reading of the OS/2 reference manuals will not, in&lt;br /&gt;
themselves, clarify the key issues of the programming environment that OS/2&lt;br /&gt;
was written to support. This is true not only because of the complexity of&lt;br /&gt;
the product but because many design elements were chosen to provide&lt;br /&gt;
services that from a prebreakthrough perspective--don't seem needed and&lt;br /&gt;
solve problems that haven't yet arisen.&lt;br /&gt;
     Other books provide reference information and detailed how-to&lt;br /&gt;
instructions for writing OS/2 programs. This book describes the underlying&lt;br /&gt;
architectural models that make up OS/2 and discusses how those models are&lt;br /&gt;
expected to meet the foreseen and unforeseen requirements of the oncoming&lt;br /&gt;
office automation revolution. It focuses on the general issues, problems,&lt;br /&gt;
and solutions that all OS/2 programs encounter regardless of the&lt;br /&gt;
programming and interface models that a programmer may employ.&lt;br /&gt;
     As is often the case in a technical discussion, everything in OS/2 is&lt;br /&gt;
interconnected in some fashion to everything else. A discussion on the&lt;br /&gt;
shinbone naturally leads to a discussion of the thighbone and so on. The&lt;br /&gt;
author and the editor of this book have tried hard to group the material&lt;br /&gt;
into a logical progression without redundancy, but the very nature of the&lt;br /&gt;
material makes complete success at this impossible. It's often desirable,&lt;br /&gt;
in fact, to repeat material, perhaps from a different viewpoint or with a&lt;br /&gt;
different emphasis. For these reasons, the index references every mention&lt;br /&gt;
of an item or a topic, however peripheral. Having too many references&lt;br /&gt;
(including a few worthless ones) is far better than having too few&lt;br /&gt;
references. When you're looking for information about a particular subject,&lt;br /&gt;
I recommend that you first consult the contents page to locate the major&lt;br /&gt;
discussion and then peruse the index to pick up references that may appear&lt;br /&gt;
in unexpected places.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part I  The Project&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==1  History of the Project==&lt;br /&gt;
&lt;br /&gt;
Microsoft was founded to realize a vision of a microcomputer on every&lt;br /&gt;
desktop--a vision of the second industrial revolution. The first industrial&lt;br /&gt;
revolution mechanized physical work. Before the eighteenth century, nearly&lt;br /&gt;
all objects were created and constructed by human hands, one at a time.&lt;br /&gt;
With few exceptions, such as animal-powered plowing and cartage, all power&lt;br /&gt;
was human muscle power. The second industrial revolution will mechanize&lt;br /&gt;
routine mental work. Today, on the verge of the revolution, people are&lt;br /&gt;
still doing &amp;quot;thought work,&amp;quot; one piece at a time.&lt;br /&gt;
     Certain tasks--those massive in scope and capable of being rigidly&lt;br /&gt;
described, such as payroll calculations--have been automated, but the&lt;br /&gt;
majority of &amp;quot;thought work&amp;quot; is still done by people, not by computers. We&lt;br /&gt;
have the computer equivalent of the plow horse, but we don't have the&lt;br /&gt;
computer equivalent of the electric drill or the washing machine.&lt;br /&gt;
     Of course, computers cannot replace original thought and creativity&lt;br /&gt;
(at least, not in the near future) any more than machines have replaced&lt;br /&gt;
design and creativity in the physical realm. But the bulk of the work in a&lt;br /&gt;
white-collar office involves routine manipulation of information. The&lt;br /&gt;
second industrial revolution will relieve us of the &amp;quot;grunt work&amp;quot;--routine&lt;br /&gt;
data manipulation, analysis, and decisions--freeing us to deal only with&lt;br /&gt;
those situations that require human judgment.&lt;br /&gt;
     Most people do not recognize the inevitability of the second&lt;br /&gt;
industrial revolution. They can't see how a computer could do 75 percent of&lt;br /&gt;
their work because their work was structured in the absence of computers.&lt;br /&gt;
But, true to the pattern for technological breakthroughs, the tremendous&lt;br /&gt;
utility of the microcomputer will transform its users and the way they do&lt;br /&gt;
their work.&lt;br /&gt;
     For example, a great deal of work is hard to computerize because the&lt;br /&gt;
input information arrives on paper and it would take too long to type it&lt;br /&gt;
all in. Ten years ago, computer proponents envisioned the &amp;quot;paperless&lt;br /&gt;
office&amp;quot; as a solution for this problem: All material would be generated by&lt;br /&gt;
computer and then transferred electronically or via disk to other&lt;br /&gt;
computers. Offices are certainly becoming more paperless, and the arrival&lt;br /&gt;
of powerful networking systems will accelerate this, but paper continues to&lt;br /&gt;
be a very useful medium. As a result, in recent years growth has occurred&lt;br /&gt;
in another direction--incorporating paper as a computer input and output&lt;br /&gt;
device. Powerful laser printers, desktop publishing systems, and optical&lt;br /&gt;
scanners and optical character recognition will make it more practical to&lt;br /&gt;
input from and output to paper.&lt;br /&gt;
     Although the founders of Microsoft fully appreciate the impact of the&lt;br /&gt;
second industrial revolution, nobody can predict in detail how the&lt;br /&gt;
revolution will unfold. Instead, Microsoft bases its day-to-day decisions&lt;br /&gt;
on dual sets of goals: short-term goals, which are well known, and a long-&lt;br /&gt;
term goal--our vision of the automated office. Each decision has to meet&lt;br /&gt;
our short-term goals, and it must be consonant with our long-term vision, a&lt;br /&gt;
vision that becomes more precise as the revolution progresses.&lt;br /&gt;
     When 16-bit microprocessors were first announced, Microsoft knew that&lt;br /&gt;
the &amp;quot;iron&amp;quot; was now sufficiently powerful to begin to realize this vision.&lt;br /&gt;
But a powerful computer environment requires both strong iron and a&lt;br /&gt;
sophisticated operating system. The iron was becoming available, but the&lt;br /&gt;
operating system that had been standard for 8-bit microprocessors was&lt;br /&gt;
inadequate. This is when and why Microsoft entered the operating system&lt;br /&gt;
business: We knew that we needed a powerful operating system to realize our&lt;br /&gt;
vision and that the only way to guarantee its existence and suitability was&lt;br /&gt;
to write it ourselves.&lt;br /&gt;
&lt;br /&gt;
===1.1  MS-DOS version 1.0===&lt;br /&gt;
&lt;br /&gt;
MS-DOS got its start when IBM asked Microsoft to develop a disk operating&lt;br /&gt;
system for a new product that IBM was developing, the IBM Personal Computer&lt;br /&gt;
(PC). Microsoft's only operating system product at that time was XENIX, a&lt;br /&gt;
licensed version of AT&amp;amp;T's UNIX  operating system. XENIX/UNIX requires a&lt;br /&gt;
processor with memory management and protection facilities. Because the&lt;br /&gt;
8086/8088 processors had neither and because XENIX/UNIX memory&lt;br /&gt;
requirements--modest by minicomputer standards of the day--were nonetheless&lt;br /&gt;
large by microcomputer standards, a different operating system had to be&lt;br /&gt;
developed.&lt;br /&gt;
     CP/M-80, developed by Digital Research, Incorporated (DRI), had been&lt;br /&gt;
the standard 8-bit operating system, and the majority of existing&lt;br /&gt;
microcomputer software had been written to run on CP/M-80. For this reason,&lt;br /&gt;
Microsoft decided to make MS-DOS version 1.0 as compatible as possible with&lt;br /&gt;
CP/M-80. The 8088 processor would not run the existing CP/M-80 programs,&lt;br /&gt;
which were written for the 8080 processor, but because 8080 programs could&lt;br /&gt;
be easily and semiautomatically converted to run on the 8088, Microsoft&lt;br /&gt;
felt that minimizing adaptation hassles by minimizing operating system&lt;br /&gt;
incompatibility would hasten the acceptance of MS-DOS on the IBM PC.&lt;br /&gt;
     A major software product requires a great deal of development time,&lt;br /&gt;
and IBM was in a hurry to introduce its PC. Microsoft, therefore, looked&lt;br /&gt;
around for a software product to buy that could be built onto to create MS-&lt;br /&gt;
DOS version 1.0. Such a product was found at Seattle Computer Products. Tim&lt;br /&gt;
Paterson, an engineer there, had produced a CP/M-80 &amp;quot;clone,&amp;quot; called SCP-&lt;br /&gt;
DOS, that ran on the 8088 processor. Microsoft purchased full rights to&lt;br /&gt;
this product and to its source code and used the product as a starting&lt;br /&gt;
point in the development of MS-DOS version 1.0.&lt;br /&gt;
     MS-DOS version 1.0 was released in August 1981. Available only for the&lt;br /&gt;
IBM PC, it consisted of 4000 lines of assembly-language source code and ran&lt;br /&gt;
in 8 KB of memory. MS-DOS version 1.1 was released in 1982 and worked with&lt;br /&gt;
double-sided 320 KB floppy disks.&lt;br /&gt;
     Microsoft's goal was that MS-DOS version 1.0 be highly CP/M&lt;br /&gt;
compatible, and it was. Ironically, it was considerably more compatible&lt;br /&gt;
than DRI's own 8088 product, CP/M-86. As we shall see later, this CP/M&lt;br /&gt;
compatibility, necessary at the time, eventually came to cause Microsoft&lt;br /&gt;
engineers a great deal of difficulty.&lt;br /&gt;
&lt;br /&gt;
===1.2  MS-DOS version 2.0===&lt;br /&gt;
&lt;br /&gt;
In early 1982, IBM disclosed to Microsoft that it was developing a hard&lt;br /&gt;
disk-based personal computer, the IBM XT. Microsoft began work on MS-DOS&lt;br /&gt;
version 2.0 to provide support for the new disk hardware. Changes were&lt;br /&gt;
necessary because MS-DOS, in keeping with its CP/M-80 compatible heritage,&lt;br /&gt;
had been designed for a floppy disk environment. A disk could contain only&lt;br /&gt;
one directory, and that directory could contain a maximum of 64 files. This&lt;br /&gt;
decision was reasonable when first made because floppy disks held only&lt;br /&gt;
about 180 KB of data.&lt;br /&gt;
     For the hard disk, however, the 64-file limit was much too small, and&lt;br /&gt;
using a single directory to manage perhaps hundreds of files was&lt;br /&gt;
clumsy. Therefore, the MS-DOS version 2.0 developers--Mark Zbikowski, Aaron&lt;br /&gt;
Reynolds, Chris Peters, and Nancy Panners--added a hierarchical file&lt;br /&gt;
system. In a hierarchical file system a directory can contain other&lt;br /&gt;
directories and files. In turn, those directories can con- tain a mixture&lt;br /&gt;
of files and directories and so on. A hierarchically designed system starts&lt;br /&gt;
with the main, or &amp;quot;root,&amp;quot; directory, which itself can contain (as seen in&lt;br /&gt;
Figure 1-1) a tree-structured collection of files and directories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                directory WORK&lt;br /&gt;
                           ADMIN&lt;br /&gt;
               Ú────────── BUDGET&lt;br /&gt;
               ³           CALENDAR&lt;br /&gt;
               ³           LUNCH.DOC&lt;br /&gt;
               ³           PAYROLL ─────────¿&lt;br /&gt;
               ³           PHONE.LST        ³&lt;br /&gt;
               ³           SCHED.DOC        ³&lt;br /&gt;
               ³                            ³&lt;br /&gt;
           directory BUDGET              directory PAYROLL&lt;br /&gt;
                      MONTH                         ADDRESSES&lt;br /&gt;
                      QUARTER                       MONTHLY&lt;br /&gt;
                      YEAR                          NAMES&lt;br /&gt;
                      1986 ────────¿                RETIRED ─────¿&lt;br /&gt;
            Ú──────── 1985         ³                VACATION     ³&lt;br /&gt;
            ³                      ³                WEEKLY       ³&lt;br /&gt;
            ³                      ³                             ³&lt;br /&gt;
     directory 1985         directory 1986             directory RETIRED&lt;br /&gt;
            ³                      ³                             ³&lt;br /&gt;
&lt;br /&gt;
Figure 1-1.  A directory tree hierarchy. Within the WORK directory&lt;br /&gt;
are five files (ADMIN, CALENDAR, LUNCH.DOC, PHONE.LST, SCHED.DOC) and two&lt;br /&gt;
subdirectories (BUDGET, PAYROLL). Each subdirectory has its own&lt;br /&gt;
subdirectories.&lt;br /&gt;
&lt;br /&gt;
===1.3  MS-DOS version 3.0====&lt;br /&gt;
&lt;br /&gt;
MS-DOS version 3.0 was introduced in August 1984, when IBM announced the&lt;br /&gt;
IBM PC/AT. The AT contains an 80286 processor, but, when running DOS, it&lt;br /&gt;
uses the 8086 emulation mode built into the chip and runs as a &amp;quot;fast 8086.&amp;quot;&lt;br /&gt;
The chip's extended addressing range and its protected mode architecture&lt;br /&gt;
sit unused.&lt;br /&gt;
1. Products such as Microsoft XENIX/UNIX run on the PC/AT and&lt;br /&gt;
compatibles, using the processor's protected mode. This is possible&lt;br /&gt;
because XENIX/UNIX and similar systems had no preexisting real mode&lt;br /&gt;
applications that needed to be supported.&lt;br /&gt;
1&lt;br /&gt;
     MS-DOS version 3.1 was released in November 1984 and contained&lt;br /&gt;
networking support. In January 1986, MS-DOS version 3.2--a minor revision--&lt;br /&gt;
was released. This version supported 3-1/2-inch floppy disks and contained&lt;br /&gt;
the formatting function for a device in the device driver. In 1987, MS-DOS&lt;br /&gt;
version 3.3 followed; the primary enhancement of this release was support&lt;br /&gt;
for the IBM PS/2 and compatible hardware.&lt;br /&gt;
&lt;br /&gt;
===1.4  MS-DOS version 4.0====&lt;br /&gt;
&lt;br /&gt;
Microsoft started work on a multitasking version of MS-DOS in January 1983.&lt;br /&gt;
At the time, it was internally called MS-DOS version 3.0. When a new&lt;br /&gt;
version of the single-tasking MS-DOS was shipped under the name MS-DOS&lt;br /&gt;
version 3.0, the multitasking version was renamed, internally, to MS-DOS&lt;br /&gt;
version 4.0. A version of this product--a multitasking, real-mode only MS-&lt;br /&gt;
DOS--was shipped as MS-DOS version 4.0. Because MS-DOS version 4.0 runs&lt;br /&gt;
only in real mode, it can run on 8088 and 8086 machines as well as on 80286&lt;br /&gt;
machines. The limitations of the real mode environment make MS-DOS version&lt;br /&gt;
4.0 a specialized product. Although MS-DOS version 4.0 supports full&lt;br /&gt;
preemptive multitasking, system memory is limited to the 640 KB available&lt;br /&gt;
in real mode, with no swapping.&lt;br /&gt;
2. It is not feasible to support general purpose swapping without&lt;br /&gt;
memory management hardware that is unavailable in 8086 real mode.&lt;br /&gt;
2 This means that all processes have to fit&lt;br /&gt;
into the single 640 KB memory area. Only one MS-DOS version 3.x compatible&lt;br /&gt;
real mode application can be run; the other processes must be special MS-&lt;br /&gt;
DOS version 4.0 processes that understand their environment and cooperate&lt;br /&gt;
with the operating system to coexist peacefully with the single MS-DOS&lt;br /&gt;
version 3.x real mode application.&lt;br /&gt;
     Because of these restrictions, MS-DOS version 4.0 was not intended for&lt;br /&gt;
general release, but as a platform for specific OEMs to support extended PC&lt;br /&gt;
architectures. For example, a powerful telephone management system could be&lt;br /&gt;
built into a PC by using special MS-DOS version 4.0 background processes to&lt;br /&gt;
control the telephone equipment. The resulting machine could then be&lt;br /&gt;
marketed as a &amp;quot;compatible MS-DOS 3 PC with a built-in superphone.&amp;quot;&lt;br /&gt;
     Although MS-DOS version 4.0 was released as a special OEM product, the&lt;br /&gt;
project--now called MS-DOS version 5.0--continued. The goal was to take&lt;br /&gt;
advantage of the protected mode of the 80286 to provide full general&lt;br /&gt;
purpose multitasking without the limitations--as seen in MS-DOS version&lt;br /&gt;
4.0--of a real-mode only environment. Soon, Microsoft and IBM signed a&lt;br /&gt;
Joint Development Agreement that provided for the design and development of&lt;br /&gt;
MS-DOS version 5.0 (now called CP/DOS). The agreement is complex, but it&lt;br /&gt;
basically provides for joint development and then subsequent joint&lt;br /&gt;
ownership, with both companies holding full rights to the resulting&lt;br /&gt;
product.&lt;br /&gt;
     As the project neared completion, the marketing staffs looked at&lt;br /&gt;
CP/DOS, nee DOS 5, nee DOS 4, nee DOS 3, and decided that it needed...you&lt;br /&gt;
guessed it...a name change. As a result, the remainder of this book will&lt;br /&gt;
discuss the design and function of an operating system called OS/2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==2  Goals and Compatibility Issues===&lt;br /&gt;
&lt;br /&gt;
OS/2 is similar to traditional multitasking operating systems in many ways:&lt;br /&gt;
It provides multitasking, scheduling, disk management, memory management,&lt;br /&gt;
and so on. But it is also different in many ways, because a personal&lt;br /&gt;
computer is very different from a multiuser minicomputer. The designers of&lt;br /&gt;
OS/2 worked from two lists: a set of goals and a set of compatibility&lt;br /&gt;
issues. This chapter describes those goals and compatibility issues and&lt;br /&gt;
provides the context for a later discussion of the design itself.&lt;br /&gt;
&lt;br /&gt;
===2.1  Goals===&lt;br /&gt;
&lt;br /&gt;
The primary goal of OS/2 is to be the ideal office automation operating&lt;br /&gt;
system. The designers worked toward this goal by defining the following&lt;br /&gt;
intermediate and, seemingly, contradictory goals:&lt;br /&gt;
&lt;br /&gt;
* To provide device-independent graphics drivers without introducing any significant overhead.&lt;br /&gt;
* To allow applications direct access to high-bandwidth peripherals but maintain the ability to virtualize or apportion the usage of those peripherals.&lt;br /&gt;
* To provide multitasking without reducing the performance and response available from a single-tasking system.&lt;br /&gt;
* To provide a fully customized environment for each program and its descendants yet also provide a standard environment that is unaffected by other programs in the system.&lt;br /&gt;
* To provide a protected environment to ensure system stability yet one that will not constrain applications from the capabilities they have under nonprotected systems.&lt;br /&gt;
&lt;br /&gt;
====2.1.1  Graphical User Interface====&lt;br /&gt;
By far the fastest and easiest way people receive information is through&lt;br /&gt;
the eye. We are inherently visual creatures. Our eyes receive information&lt;br /&gt;
rapidly; they can &amp;quot;seek&amp;quot; to the desired information and &amp;quot;zoom&amp;quot; their&lt;br /&gt;
attention in and out with small, rapid movements of the eye muscles. A&lt;br /&gt;
large part of the human brain is dedicated to processing visual&lt;br /&gt;
information. People abstract data and meaning from visual material--from&lt;br /&gt;
text to graphics to motion pictures--hundreds of times faster than from any&lt;br /&gt;
other material.&lt;br /&gt;
     As a result, if an office automation system is to provide quantities&lt;br /&gt;
of information quickly and in a form in which it can be easily absorbed, a&lt;br /&gt;
powerful graphics capability is essential. Such capabilities were rare in&lt;br /&gt;
earlier minicomputer operating systems because of the huge memory and&lt;br /&gt;
compute power costs of high-resolution displays. Today's microcomputers&lt;br /&gt;
have the memory to contain the display information, they have the CPU power&lt;br /&gt;
to create and manipulate that information, and they have no better use for&lt;br /&gt;
those capabilities than to support powerful, easy-to-use graphical&lt;br /&gt;
applications.&lt;br /&gt;
     Graphics can take many forms--pictures, tables, drawings, charts--&lt;br /&gt;
perhaps incorporating color and even animation. All are powerful adjuncts&lt;br /&gt;
to the presentation of alphanumeric text. Graphical applications don't&lt;br /&gt;
necessarily employ charts and pictures. A WYSIWYG (What You See Is What You&lt;br /&gt;
Get) typesetting program may display only text, but if that text is drawn&lt;br /&gt;
in graphics mode, the screen can show any font, in any type size, with&lt;br /&gt;
proportional spacing, kerning, and so on.&lt;br /&gt;
     The screen graphics components of OS/2 need to be device independent;&lt;br /&gt;
that is, an application must display the proper graphical &amp;quot;picture&amp;quot; without&lt;br /&gt;
relying on the specific characteristics of any particular graphical display&lt;br /&gt;
interface board. Each year the state of the art in displays gets better; it&lt;br /&gt;
would be extremely shortsighted to tie applications to a particular display&lt;br /&gt;
board, for no matter how good it is, within a couple of years it will be&lt;br /&gt;
obsolete.&lt;br /&gt;
     The idea is to encapsulate device-specific code by requiring that each&lt;br /&gt;
device come with a software package called a device driver. The application&lt;br /&gt;
program issues commands for a generic device, and the device driver then&lt;br /&gt;
translates those commands to fit the characteristics of the actual device.&lt;br /&gt;
The result is that the manufacturer of a new graphics display board needs&lt;br /&gt;
to write an appropriate device driver and supply it with the board. The&lt;br /&gt;
application program doesn't need to know anything about the device, and the&lt;br /&gt;
device driver doesn't need to know anything about the application, other&lt;br /&gt;
than the specification of the common interface they share. This common&lt;br /&gt;
interface describes a virtual display device; the general technique of&lt;br /&gt;
hiding a complicated actual situation behind a simple, standard interface&lt;br /&gt;
is called &amp;quot;virtualization.&amp;quot;&lt;br /&gt;
     Figure 2-1 shows the traditional operating system device driver&lt;br /&gt;
architecture. Applications don't directly call device drivers because&lt;br /&gt;
device drivers need to execute in the processor's privilege mode to&lt;br /&gt;
manipulate their device; the calling application must run in normal mode.&lt;br /&gt;
In the language of the 80286/80386 family of processors, privilege mode is&lt;br /&gt;
called ring 0, and normal mode is called ring 3. The operating system&lt;br /&gt;
usually acts as a middleman: It receives the request, validates it, deals&lt;br /&gt;
with issues that arise when there is only one device but multiple&lt;br /&gt;
applications are using it, and then passes the request to the device&lt;br /&gt;
driver. The device driver's response or return of data takes the reverse&lt;br /&gt;
path, winding its way through the operating system and back to the&lt;br /&gt;
application program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         Application                      Kernel             Device driver&lt;br /&gt;
          (ring 3)                       (ring 0)               (ring 0)&lt;br /&gt;
──────────────────────────────────────────────────────────────────────────&lt;br /&gt;
                                   Request&lt;br /&gt;
                             ³     packet:                  ³ Ú──────────¿&lt;br /&gt;
                                Ú──────────¿    Device        ³  Device  ³&lt;br /&gt;
                             ³  ³          ³    descriptor ÄÅÄ´  driver  ³&lt;br /&gt;
Call deviceio (arg 1...argn)    ³ function ³    #1            À──────────Ù&lt;br /&gt;
                             ³  ³   arg1   ³          ú     ³&lt;br /&gt;
        ───────────────────────�³    ù     ÃÄ¿        ú&lt;br /&gt;
                  Ring       ³  ³   argn   ³ ³        ú     ³&lt;br /&gt;
                  transition    ³          ³ ³        ú       Ú──────────¿&lt;br /&gt;
                             ³  À──────────Ù À� Device      ³ ³  Device  ³&lt;br /&gt;
                                                descriptor ÄÄÄ´  driver  ³&lt;br /&gt;
                             ³                  #N          ³ À──────────Ù&lt;br /&gt;
&lt;br /&gt;
Figure 2-1.  Traditional device driver architecture. When an application&lt;br /&gt;
wants to do device I/0, it calls the operating system, which builds a&lt;br /&gt;
device request packet, determines the target device, and delivers the&lt;br /&gt;
packet. The device driver's response follows the opposite route through the&lt;br /&gt;
kernel back to the application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This approach solves the device virtualization problem, but at a cost&lt;br /&gt;
in performance. The interface between the application and the device driver&lt;br /&gt;
is narrow; that is, the form messages can take is usually restricted.&lt;br /&gt;
Commonly, the application program is expected to build a request block that&lt;br /&gt;
contains all the information and data that the device driver needs to&lt;br /&gt;
service the request; the actual call to the operating system is simply&lt;br /&gt;
&amp;quot;pass this request block to the device driver.&amp;quot; Setting up this block takes&lt;br /&gt;
time, and breaking it down in the device driver again takes time. More time&lt;br /&gt;
is spent on the reply; the device driver builds, the operating system&lt;br /&gt;
copies, and the application breaks down. Further time is spent calling down&lt;br /&gt;
through the internal layers of the operating system, examining and copying&lt;br /&gt;
the request block, routing to the proper device driver, and so forth.&lt;br /&gt;
Finally, the transition between rings (privilege and normal mode) is also&lt;br /&gt;
time-consuming, and two such transitions occur--to privilege mode and back&lt;br /&gt;
again.&lt;br /&gt;
     Such a cost in performance was acceptable in nongraphics-based systems&lt;br /&gt;
because, typically, completely updating a screen required only 1920 (or&lt;br /&gt;
fewer) bytes of data. Today's graphics devices can require 256,000 bytes or&lt;br /&gt;
more per screen update, and future devices will be even more demanding.&lt;br /&gt;
Furthermore, applications may expect to update these high-resolution&lt;br /&gt;
screens several times a second.&lt;br /&gt;
1. It's not so much the amount of data that slows the traditional&lt;br /&gt;
device driver model, but the number of requests and replies. Disk&lt;br /&gt;
devices work well through the traditional model because disk&lt;br /&gt;
requests tend to be large (perhaps 40,000 bytes). Display devices&lt;br /&gt;
tend to be written piecemeal--a character, a word, or a line at a&lt;br /&gt;
time. It is the high rate of these individual calls that slows the&lt;br /&gt;
device driver model, not the number of bytes written to the screen.&lt;br /&gt;
1&lt;br /&gt;
     OS/2 needed powerful, device-independent graphical display support&lt;br /&gt;
that had a wide, efficient user interface--one that did not involve ring&lt;br /&gt;
transitions, the operating system, or other unnecessary overhead. As we'll&lt;br /&gt;
see later, OS/2 meets this requirement by means of a mechanism called&lt;br /&gt;
dynamic linking.&lt;br /&gt;
&lt;br /&gt;
====2.1.2  Multitasking====&lt;br /&gt;
To be really useful, a personal computer must be able to do more than one&lt;br /&gt;
chore at a time--an ability called multitasking. We humans multitask all&lt;br /&gt;
the time. For example, you may be involved in three projects at work, be&lt;br /&gt;
halfway through a novel, and be taking Spanish lessons. You pick up each&lt;br /&gt;
task in turn, work on it for a while, and then put it down and work on&lt;br /&gt;
something else. This is called serial multitasking. Humans can also do some&lt;br /&gt;
tasks simultaneously, such as driving a car and talking. This is called&lt;br /&gt;
parallel multitasking.&lt;br /&gt;
     In a serial multitasking computer environment, a user can switch&lt;br /&gt;
activities at will, working for a while at each. For example, a user can&lt;br /&gt;
leave a word-processing program without terminating it, consult a&lt;br /&gt;
spreadsheet, and then return to the waiting word-processing program. Or, if&lt;br /&gt;
someone telephones and requests an appointment, the user can switch from a&lt;br /&gt;
spreadsheet to a scheduling program, consult the calendar, and then return&lt;br /&gt;
to the spreadsheet.&lt;br /&gt;
     The obvious value of multitasking makes it another key requirement for&lt;br /&gt;
OS/2: Many programs or applications can run at the same time. But&lt;br /&gt;
multitasking is useful for more than just switching between applications:&lt;br /&gt;
Parallel multitasking allows an application to do work by itself--perhaps&lt;br /&gt;
print a large file or recalculate a large spreadsheet--while the user&lt;br /&gt;
works with another application. Because OS/2 supports full multitasking,&lt;br /&gt;
it can execute programs in addition to the application(s) the user is&lt;br /&gt;
running, providing advanced services such as network mail without&lt;br /&gt;
interrupting or interfering with the user's work.&lt;br /&gt;
2. Present-day machines contain only one CPU, so at any instant&lt;br /&gt;
only one program can be executing. At this microscopic level,&lt;br /&gt;
OS/2 is a serial multitasking system. It is not considered serial&lt;br /&gt;
multitasking, however, because it performs preemptive scheduling.&lt;br /&gt;
At any time, OS/2 can remove the CPU from the currently running&lt;br /&gt;
program and assign it to another program. Because these&lt;br /&gt;
rescheduling events may occur many times a second at totally&lt;br /&gt;
unpredictable places within the running programs, it is accurate&lt;br /&gt;
to view the system as if each program truly runs simultaneously&lt;br /&gt;
with other programs.&lt;br /&gt;
&lt;br /&gt;
====2.1.3  Memory Management====&lt;br /&gt;
Multitasking is fairly easy to achieve. All that's necessary is a source of&lt;br /&gt;
periodic hardware interrupts, such as a clock circuit, to enable the&lt;br /&gt;
operating system to effect a &amp;quot;context switch,&amp;quot; or to reschedule. To be&lt;br /&gt;
useful, however, a multitasking system needs an effective memory management&lt;br /&gt;
system. For example, a user wants to run two applications on a system. Each&lt;br /&gt;
starts at as low a memory location as possible to maximize the amount of&lt;br /&gt;
memory it can use. Unfortunately, if the system supports multitasking and&lt;br /&gt;
the user tries to run both applications simultaneously, each attempts to&lt;br /&gt;
use the same memory cells, and the applications destroy each other.&lt;br /&gt;
     A memory management system solves this problem by using special&lt;br /&gt;
hardware facilities built into 80286/80386 processors (for example, IBM&lt;br /&gt;
PC/AT machines and compatibles and 80386-based machines).&lt;br /&gt;
3. Earlier 8086/8088 processors used in PCs, PC/XTs, and similar&lt;br /&gt;
machines lack this hardware. This is why earlier versions of MS-DOS&lt;br /&gt;
didn't support multitasking and why OS/2 won't run on such machines.&lt;br /&gt;
3 The memory&lt;br /&gt;
management system uses the hardware to virtualize the memory of the machine&lt;br /&gt;
so that each program appears to have all memory to itself.&lt;br /&gt;
     Memory management is more than keeping programs out of each other's&lt;br /&gt;
way. The system must track the owner or user(s) of each piece of memory so&lt;br /&gt;
that the memory space can be reclaimed when it is no longer needed, even if&lt;br /&gt;
the owner of the memory neglects to explicitly release it. Some operating&lt;br /&gt;
systems avoid this work by assuming that no application will ever fail to&lt;br /&gt;
return its memory when done or by examining the contents of memory and&lt;br /&gt;
ascertaining from those contents whether the memory is still being used.&lt;br /&gt;
(This is called &amp;quot;garbage collection.&amp;quot;) Neither alternative was acceptable&lt;br /&gt;
for OS/2. Because OS/2 will run a variety of programs written by many&lt;br /&gt;
vendors, identifying free memory by inspection is impossible, and assuming&lt;br /&gt;
perfection from the applications themselves is unwise. Tracking the&lt;br /&gt;
ownership and usage of memory objects can be complex, as we shall see in&lt;br /&gt;
our discussion on dynamic link libraries.&lt;br /&gt;
     Finally, the memory management system must manage memory overcommit.&lt;br /&gt;
The multitasking capability of OS/2 allows many applications to be run&lt;br /&gt;
simultaneously; thus, RAM must hold all these programs and their data.&lt;br /&gt;
Although RAM becomes cheaper every year, buying enough to hold all of one's&lt;br /&gt;
applications at one time is still prohibitive. Furthermore, although RAM&lt;br /&gt;
prices continue to drop, the memory requirements of applications will&lt;br /&gt;
continue to rise. Consequently, OS/2 must contain an effective mechanism to&lt;br /&gt;
allocate more memory to the running programs than in fact physically&lt;br /&gt;
exists. This is called memory overcommit.&lt;br /&gt;
     OS/2 accomplishes this magic with the classic technique of swapping.&lt;br /&gt;
OS/2 periodically examines each segment of memory to see if it has been&lt;br /&gt;
used recently. When a request is made for RAM and none is available, the&lt;br /&gt;
least recently used segment of memory (the piece that has been unused for&lt;br /&gt;
the longest time) is written to a disk file, and the RAM it occupied is&lt;br /&gt;
made available. Later, if a program attempts to use the swapped-out memory,&lt;br /&gt;
a &amp;quot;memory not present&amp;quot; fault occurs. OS/2 intercepts the fault and reloads&lt;br /&gt;
the memory information from the disk into memory, swapping out some other&lt;br /&gt;
piece of memory, if necessary, to make room. This whole process is&lt;br /&gt;
invisible to the application that uses the swapped memory area; the only&lt;br /&gt;
impact is a small delay while the needed memory is read back from the&lt;br /&gt;
disk.&lt;br /&gt;
     The fundamental concepts of memory overcommit and swapping are simple,&lt;br /&gt;
but a good implementation is not. OS/2 must choose the right piece of&lt;br /&gt;
memory to swap out, and it must swap it out efficiently. Not only must care&lt;br /&gt;
be taken that the swap file doesn't grow too big and consume all the free&lt;br /&gt;
disk space but also that deadlocks don't occur. For example, if all the&lt;br /&gt;
disk swap space is filled, it may be impossible to swap into RAM a piece of&lt;br /&gt;
memory because no free RAM is available, and OS/2 can't free up RAM because&lt;br /&gt;
no swap space exists to write it out to. Naturally, the greater the load on&lt;br /&gt;
the system, the slower the system will be, but the speed degradation must&lt;br /&gt;
be gradual and acceptable, and the system must never deadlock.&lt;br /&gt;
     The issues involved in memory management and the memory management&lt;br /&gt;
facilities that OS/2 provides are considerably more complex than this&lt;br /&gt;
overview. We'll return to the subject of memory management in detail in&lt;br /&gt;
Chapter 9.&lt;br /&gt;
&lt;br /&gt;
====2.1.4  Protection====&lt;br /&gt;
I mentioned earlier that OS/2 cannot trust applications to behave&lt;br /&gt;
correctly. I was talking about memory management, but this concern&lt;br /&gt;
generalizes into the next key requirement: OS/2 must protect applications &lt;br /&gt;
from the proper or improper actions of other applications that may be&lt;br /&gt;
running on the system.&lt;br /&gt;
     Because OS/2 will run applications and programs from a variety of&lt;br /&gt;
vendors, every user's machine will execute a different set of applications,&lt;br /&gt;
running in different ways on different data. No software vendor can fully&lt;br /&gt;
test a product in all possible environments. This makes it critical that an&lt;br /&gt;
error on the part of one program does not crash the system or some other&lt;br /&gt;
program or, worse, corrupt data and not bring down the system. Even if no&lt;br /&gt;
data is damaged, system crashes are unacceptable. Few users have the&lt;br /&gt;
background or equipment even to diagnose which application caused the&lt;br /&gt;
problem.&lt;br /&gt;
     Furthermore, malice, as well as accident, is a concern. Microsoft's&lt;br /&gt;
vision of the automated office cannot be realized without a system that is&lt;br /&gt;
secure from deliberate attack. No corporation will be willing to base its&lt;br /&gt;
operations on a computer network when any person in that company--with the&lt;br /&gt;
help of some &amp;quot;cracker&amp;quot; programs bought from the back of a computer&lt;br /&gt;
magazine--can see and change personnel or payroll files, billing notices,&lt;br /&gt;
or strategic planning memos.&lt;br /&gt;
     Today, personal computers are being used as a kind of super-&lt;br /&gt;
sophisticated desk calculator. As such, data is secured by traditional&lt;br /&gt;
means--physical locks on office doors, computers, or file cabinets that&lt;br /&gt;
store disks. Users don't see a need for a protected environment because&lt;br /&gt;
their machine is physically protected. This lack of interest in protection&lt;br /&gt;
is another example of the development of a breakthrough technology.&lt;br /&gt;
Protection is not needed because the machine is secure and operates on data&lt;br /&gt;
brought to it by traditional office channels. In the future, however,&lt;br /&gt;
networked personal computers will become universal and will act both as the&lt;br /&gt;
processors and as the source (via the network) of the data. Thus, in this&lt;br /&gt;
role, protection is a key requirement and is indeed a prerequisite for&lt;br /&gt;
personal computers to assume that central role.&lt;br /&gt;
&lt;br /&gt;
====2.1.5  Encapsulation====&lt;br /&gt;
When a program runs in a single-tasking system such as MS-DOS version 3.x,&lt;br /&gt;
its environment is always constant--consisting of the machine and MS-DOS.&lt;br /&gt;
The program can expect to get the same treatment from the system and to&lt;br /&gt;
provide exactly the same interaction with the user each time it runs. In a&lt;br /&gt;
multitasking environment, however, many programs can be running. Each&lt;br /&gt;
program can be using files and devices in different ways; each program can&lt;br /&gt;
be using the mouse, each program can have the screen display in a different&lt;br /&gt;
mode, and so on. OS/2 must encapsulate, or isolate, each program so that it&lt;br /&gt;
&amp;quot;sees&amp;quot; a uniform environment each time it runs, even though the computer&lt;br /&gt;
environment itself may be different each time.&lt;br /&gt;
&lt;br /&gt;
====2.1.6  Interprocess Communication (IPC)====&lt;br /&gt;
In a single-tasking environment such as MS-DOS version 3.x, each program&lt;br /&gt;
stands alone. If it needs a particular service not provided by the&lt;br /&gt;
operating system, it must provide that service itself. For example, every&lt;br /&gt;
application that needs a sort facility must contain its own.&lt;br /&gt;
     Likewise, if a spreadsheet needs to access values from a database, it&lt;br /&gt;
must contain the code to do so. This extra code complicates the spreadsheet&lt;br /&gt;
program, and it ties the program to a particular database product or&lt;br /&gt;
format. A user might be unable to switch to a better product because the&lt;br /&gt;
spreadsheet is unable to understand the new database's file formats.&lt;br /&gt;
     A direct result of such a stand-alone environment is the creation of&lt;br /&gt;
very large and complex &amp;quot;combo&amp;quot; packages such as Lotus Symphony. Because&lt;br /&gt;
every function that the user may want must be contained within one program,&lt;br /&gt;
vendors supply packages that attempt to contain everything.&lt;br /&gt;
     In practice, such chimeric programs tend to be large and cumbersome,&lt;br /&gt;
and their individual functional components (spreadsheets, word processors,&lt;br /&gt;
and databases, for example) are generally more difficult to use and less&lt;br /&gt;
sophisticated than individual applications that specialize in a single&lt;br /&gt;
function.&lt;br /&gt;
     The stand-alone environment forces the creation of larger and more&lt;br /&gt;
complex programs, each of which typically understands only its own file&lt;br /&gt;
formats and works poorly, if at all, with data produced by other programs.&lt;br /&gt;
This vision of personal computer software growing monstrous until&lt;br /&gt;
collapsing from its own weight brings about another OS/2 requirement:&lt;br /&gt;
Applications must be able to communicate, easily and efficiently, with&lt;br /&gt;
other applications.&lt;br /&gt;
     More specifically, an application must be able to find (or name) the&lt;br /&gt;
application that provides the information or service that the client needs,&lt;br /&gt;
and it must be able to establish efficient communication with the provider&lt;br /&gt;
program without requiring that either application have specific knowledge&lt;br /&gt;
of the internal workings of the other. Thus, a spreadsheet program must be&lt;br /&gt;
able to communicate with a database program and access the values it needs.&lt;br /&gt;
The spreadsheet program is therefore not tied to any particular database&lt;br /&gt;
program but can work with any database system that recognizes OS/2 IPC&lt;br /&gt;
requests.&lt;br /&gt;
     Applications running under OS/2 not only retain their full power as&lt;br /&gt;
individual applications but also benefit from cross-application&lt;br /&gt;
communication. Furthermore, the total system can be enhanced by upgrading&lt;br /&gt;
an application that provides services to others. When a new, faster, or&lt;br /&gt;
more fully featured database package is installed, not only is the user's&lt;br /&gt;
database application improved but the database functions of the spreadsheet&lt;br /&gt;
program are improved as well.&lt;br /&gt;
     The OS/2 philosophy is that no program should reinvent the wheel.&lt;br /&gt;
Programs should be written to offer their services to other programs and to&lt;br /&gt;
take advantage of the offered services of other programs. The result is a&lt;br /&gt;
maximally effective and efficient system.&lt;br /&gt;
&lt;br /&gt;
====2.1.7  Direct Device Access====&lt;br /&gt;
Earlier, we discussed the need for a high-performance graphical interface&lt;br /&gt;
and the limitations of the traditional device driver architecture. OS/2&lt;br /&gt;
contains a built-in solution for the screen graphical interface, but what&lt;br /&gt;
about other, specialized devices that may require a higher bandwidth&lt;br /&gt;
interface than device drivers provide? The one sure prediction about the&lt;br /&gt;
future of a technological breakthrough is that you can't fully predict it.&lt;br /&gt;
For this reason, the final key requirement for OS/2 is that it contain an&lt;br /&gt;
&amp;quot;escape hatch&amp;quot; in anticipation of devices that have performance needs too&lt;br /&gt;
great for a device driver model.&lt;br /&gt;
     OS/2 provides this expandability by allowing applications direct&lt;br /&gt;
access to hardware devices--both the I/O ports and any device memory. This&lt;br /&gt;
must be done, of course, in such a way that only devices which are intended&lt;br /&gt;
to be used in this fashion can be so accessed. Applications are prevented&lt;br /&gt;
from using this access technique on devices that are being managed by the&lt;br /&gt;
operating system or by a device driver. This facility gives applications&lt;br /&gt;
the ability to take advantage of special nonstandard hardware such as OCRs&lt;br /&gt;
(Optical Character Readers), digitizer tablets, Fax equipment, special&lt;br /&gt;
purpose graphics cards, and the like.&lt;br /&gt;
&lt;br /&gt;
===2.2  Compatibility Issues===&lt;br /&gt;
&lt;br /&gt;
But OS/2 has to do more than meet the goals we've discussed: It must be&lt;br /&gt;
compatible with 8086/8088 and 80286 architecture, and it must be compatible&lt;br /&gt;
with MS-DOS. By far the easiest solution would have been to create a new&lt;br /&gt;
multitasking operating system that would not be compatible with MS-DOS, but&lt;br /&gt;
such a system is unacceptable. Potential users may be excited about the new&lt;br /&gt;
system, but they won't buy it until applications are available. Application&lt;br /&gt;
writers may likewise be excited, but they won't adapt their products for it&lt;br /&gt;
until the system has sold enough copies to gain significant market share.&lt;br /&gt;
This &amp;quot;catch 22&amp;quot; means that the only people who will buy the new operating&lt;br /&gt;
system are the developers' mothers, and they probably get it at a discount&lt;br /&gt;
anyway.&lt;br /&gt;
&lt;br /&gt;
====2.2.1  Real Mode vs Protect Mode====&lt;br /&gt;
The first real mode compatibility issue relates to the design of the 80286&lt;br /&gt;
microprocessor--the &amp;quot;brain&amp;quot; of an MS-DOS computer. This chip has two&lt;br /&gt;
incompatible modes--real (compatibility) mode and protect mode. Real mode&lt;br /&gt;
is designed to run programs in exactly the same manner as they run on the&lt;br /&gt;
8086/8088 processor. In other words, when the 80286 is in real mode, it&lt;br /&gt;
&amp;quot;looks&amp;quot; to the operating system and programs exactly like a fast&lt;br /&gt;
8088.&lt;br /&gt;
     But the designers of the 80286 wanted it to be more than a fast 8088.&lt;br /&gt;
They wanted to add such features as memory management, memory protection,&lt;br /&gt;
and the ring protection mechanism, which allows the operating system to&lt;br /&gt;
protect one application from another. They weren't able to do this while&lt;br /&gt;
remaining fully compatible with the earlier 8088 chip, so they added a&lt;br /&gt;
second mode to the 80286--protect mode. When the processor is running in&lt;br /&gt;
protect mode, it provides these important new features, but it will not run&lt;br /&gt;
most programs written for the 8086/8088.&lt;br /&gt;
     In effect, an 80286 is two separate microprocessors in one package. It&lt;br /&gt;
can act like a very fast 8088--compatible, but with no new capabilities--or&lt;br /&gt;
it can act like an 80286--incompatible, but providing new features.&lt;br /&gt;
Unfortunately, the designers of the chip didn't appreciate the importance&lt;br /&gt;
of compatibility in the MS-DOS marketplace, and they designed the 80286 so&lt;br /&gt;
that it can run in either mode but can't switch back and forth at will.&lt;br /&gt;
4. The 80286 initializes itself in real mode. There is a command&lt;br /&gt;
to switch from real mode to protect mode, but there is no command&lt;br /&gt;
to switch back.&lt;br /&gt;
&lt;br /&gt;
In other words, an 80286 was designed to run only old 8086/8088 programs,&lt;br /&gt;
or it can run only new 80286 style programs, but never both at the same&lt;br /&gt;
time.&lt;br /&gt;
     In summary, OS/2 was required to do something that the 80286 was not&lt;br /&gt;
designed for--execute both 8086/8088 style (real mode) and 80286 style&lt;br /&gt;
(protect) mode programs at the same time. The existence of this book should&lt;br /&gt;
lead you to believe that this problem was solved, and indeed it was.&lt;br /&gt;
&lt;br /&gt;
====2.2.2  Running Applications in Real (Compatibility) Mode====&lt;br /&gt;
Solving the real mode vs protect mode problem, however, presented other&lt;br /&gt;
problems. In general, the problems came about because the real mode&lt;br /&gt;
programs were written for MS-DOS versions 2.x or 3.x, both of which are&lt;br /&gt;
single-tasking environments.&lt;br /&gt;
     Although MS-DOS is normally spoken of as an operating system, it could&lt;br /&gt;
just as accurately be called a &amp;quot;system executive.&amp;quot; Because it runs in an&lt;br /&gt;
unprotected environment, applications are free to edit interrupt vectors,&lt;br /&gt;
manipulate peripherals, and in general take over from MS-DOS wherever they&lt;br /&gt;
wish. This flexibility is one reason for the success of MS-DOS; if MS-DOS&lt;br /&gt;
doesn't offer the service your program needs, you can always help yourself.&lt;br /&gt;
Developers were free to explore new possibilities, often with great&lt;br /&gt;
success. Most applications view MS-DOS as a program loader and as a set of&lt;br /&gt;
file system subroutines, interfacing directly with the hardware for all&lt;br /&gt;
their other needs, such as intercepting interrupt vectors, editing disk&lt;br /&gt;
controller parameter tables, and so on.&lt;br /&gt;
     It may seem that if a popular application &amp;quot;pokes&amp;quot; the operating system&lt;br /&gt;
and otherwise engages in unsavory practices that the authors or users of&lt;br /&gt;
the application will suffer because a future release, such as OS/2, may not&lt;br /&gt;
run the application correctly. To the contrary, the market dynamics state&lt;br /&gt;
that the application has now set a standard, and it's the operating system&lt;br /&gt;
developers who suffer because they must support that standard. Usually,&lt;br /&gt;
that &amp;quot;standard&amp;quot; operating system interface is not even known; a great deal&lt;br /&gt;
of experimentation is necessary to discover exactly which undocumented side&lt;br /&gt;
effects, system internals, and timing relationships the application is&lt;br /&gt;
dependent on.&lt;br /&gt;
     Offering an MS-DOS-compatible Applications Program Interface (API)&lt;br /&gt;
provides what we call level 1 compatibility. Allowing applications to&lt;br /&gt;
continue to manipulate system hardware provides level 2 compatibility.&lt;br /&gt;
Level 3 issues deal with providing an execution environment that supports&lt;br /&gt;
the hidden assumptions that programs written for a single-tasking&lt;br /&gt;
environment may make. Three are discussed below by way of illustration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.1  Memory Utilization&lt;br /&gt;
The existing real mode applications that OS/2 must support were written for&lt;br /&gt;
an environment in which no other programs are running. As a result,&lt;br /&gt;
programs typically consume all available memory in the system in the belief&lt;br /&gt;
that, since no other program is around to use any leftover memory, they&lt;br /&gt;
might as well use it all. If a program doesn't ask for all available memory&lt;br /&gt;
at first, it may ask for the remainder at some later time. Such a&lt;br /&gt;
subsequent request could never be refused under MS-DOS versions 2.x and&lt;br /&gt;
3.x, and applications were written to depend on this. Therefore, such a&lt;br /&gt;
request must be satisfied under OS/2 to maintain full compatibility.&lt;br /&gt;
     Even the manner of a memory request depends on single-tasking&lt;br /&gt;
assumptions. Programs typically ask for all memory in two steps. First,&lt;br /&gt;
they ask for the maximum amount of memory that an 8088 can provide--1 MB.&lt;br /&gt;
The application's programmer knew that the request would be refused because&lt;br /&gt;
1 MB is greater than the 640 KB maximum supported by MS-DOS; but when MS-&lt;br /&gt;
DOS refuses the request, it tells the application exactly how much memory&lt;br /&gt;
is available. Programs then ask for that amount of memory. The programmer&lt;br /&gt;
knew that MS-DOS would not refuse the second memory request for&lt;br /&gt;
insufficient memory because when MS-DOS responded to the first request it&lt;br /&gt;
told the application exactly how much memory was available. Consequently,&lt;br /&gt;
programmers rarely included a check for an &amp;quot;insufficient memory&amp;quot; error from&lt;br /&gt;
the second call.&lt;br /&gt;
     This shortcut introduces problems in the OS/2 multitasking&lt;br /&gt;
environment. When OS/2 responded to the first too-large request, it would&lt;br /&gt;
return the amount of memory available at that exact moment. Other programs&lt;br /&gt;
are simultaneously executing; by the time our real mode program makes its&lt;br /&gt;
second request, some more memory may have been given out, and the second&lt;br /&gt;
request may also be too large. It won't do any good for OS/2 to respond&lt;br /&gt;
with an error code, however, because the real mode application does not&lt;br /&gt;
check for one (it was written in the belief that it is impossible to get&lt;br /&gt;
such a code on the second call). The upshot is that even if OS/2 refused&lt;br /&gt;
the second call the real mode application would assume that it had been&lt;br /&gt;
given the memory, would use it, and in the process would destroy the other&lt;br /&gt;
program(s) that were the true owners of that memory.&lt;br /&gt;
     Obviously, OS/2 must resolve this and similar issues to support the&lt;br /&gt;
existing base of real mode applications.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.2  File Locking&lt;br /&gt;
Because multitasking systems run more than one program at the same time,&lt;br /&gt;
two programs may try to write or to modify the same file at the same time.&lt;br /&gt;
Or one may try to read a file while another is changing that file's&lt;br /&gt;
contents. Multitasking systems usually solve this problem by means of a&lt;br /&gt;
file-locking mechanism, which allows one program to temporarily prevent&lt;br /&gt;
other programs from reading and/or writing a particular file.&lt;br /&gt;
     An application may find that a file it is accessing has been locked by&lt;br /&gt;
some other application in the system. In such a situation, OS/2 normally&lt;br /&gt;
returns a &amp;quot;file locked&amp;quot; error code, and the application typically gives up&lt;br /&gt;
or waits and retries the operation later. OS/2 cannot return a &amp;quot;file&lt;br /&gt;
locked&amp;quot; error to an old-style real mode application, though, because when&lt;br /&gt;
the application was written (for MS-DOS versions 2.x or 3.x) no such error&lt;br /&gt;
code existed because no such error was possible. Few real mode applications&lt;br /&gt;
even bother to check their read and write operations for error codes, and&lt;br /&gt;
those that do wouldn't &amp;quot;understand&amp;quot; the error code and wouldn't handle it&lt;br /&gt;
correctly.&lt;br /&gt;
     OS/2 cannot compromise the integrity of the file-locking mechanism by&lt;br /&gt;
allowing the real mode application to ignore locks, but it cannot report&lt;br /&gt;
that the file is locked to the application either. OS/2 must determine the&lt;br /&gt;
proper course of action and then take that action on behalf of the real&lt;br /&gt;
mode application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.3  Network Piggybacking&lt;br /&gt;
Running under MS-DOS version 3.1, an application can use an existing&lt;br /&gt;
network virtual circuit to communicate with an application running on the&lt;br /&gt;
server machine to which the virtual circuit is connected. This is called&lt;br /&gt;
&amp;quot;piggybacking&amp;quot; the virtual circuit because the applications on each end are&lt;br /&gt;
borrowing a circuit that the network redirector established for other&lt;br /&gt;
purposes. The two sets of programs can use a single circuit for two&lt;br /&gt;
different purposes without confusion under MS-DOS version 3.1 because of&lt;br /&gt;
its single-tasking nature. The redirector only uses the circuit when the&lt;br /&gt;
application calls MS-DOS to perform a network function. Because the CPU is&lt;br /&gt;
inside MS-DOS, it can't be executing the application software that sends&lt;br /&gt;
private messages, which leaves the circuit free for use by the&lt;br /&gt;
redirector.&lt;br /&gt;
     Conversely, if the application is sending its own private messages--&lt;br /&gt;
piggybacking--then it can't be executing MS-DOS, and therefore the&lt;br /&gt;
redirector code (which is built into MS-DOS) can't be using the virtual&lt;br /&gt;
circuit.&lt;br /&gt;
     This is no longer the case in OS/2. OS/2 is a multitasking system, and&lt;br /&gt;
one application can use the redirector at the same time that the real mode&lt;br /&gt;
application is piggybacking the circuit. OS/2 must somehow interlock access&lt;br /&gt;
to network virtual circuits so that multiple users of a network virtual&lt;br /&gt;
circuit do not conflict.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.3  Popular Function Compatibility&lt;br /&gt;
We've discussed some issues of binary compatibility, providing applications&lt;br /&gt;
the internal software interfaces they had in MS-DOS. This is because it is&lt;br /&gt;
vitally important that existing applications run correctly, unchanged,&lt;br /&gt;
under the new operating system.&lt;br /&gt;
5. An extremely high degree of compatibility is required for&lt;br /&gt;
virtually any application to run because a typical application&lt;br /&gt;
uses a great many documented and undocumented interfaces and&lt;br /&gt;
features of the earlier system. If any one of those interfaces&lt;br /&gt;
is not supplied, the application will not run correctly.&lt;br /&gt;
Consequently, we cannot provide 90 percent compatibility and&lt;br /&gt;
expect to run 90 percent of existing applications; 99.9 percent&lt;br /&gt;
compatibility is required for such a degree of success.&lt;br /&gt;
5 OS/2 also needs to provide functional&lt;br /&gt;
compatibility; it has to allow the creation of protect mode applications&lt;br /&gt;
that provide the functions that users grew to know and love in real mode&lt;br /&gt;
applications.&lt;br /&gt;
     This can be difficult because many popular applications (for example,&lt;br /&gt;
&amp;quot;terminate and stay resident loadable helper&amp;quot; routines such as SideKick)&lt;br /&gt;
were written for a single-tasking, unprotected environment without regard&lt;br /&gt;
to the ease with which their function could be provided in a protected&lt;br /&gt;
environment. For example, a popular application may implement some of its&lt;br /&gt;
features by patching (that is, editing) MS-DOS itself. This cannot be&lt;br /&gt;
allowed in OS/2 (the reason is discussed in Chapter 4), so OS/2 must&lt;br /&gt;
provide alternative mechanisms for protect mode applications to provide&lt;br /&gt;
services that users have grown to expect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4  Downward Compatibility&lt;br /&gt;
So far, our discussion on compatibility has focused exclusively on upward&lt;br /&gt;
compatibility--old programs must run in the new system but not vice versa.&lt;br /&gt;
Downward compatibility--running new programs under MS-DOS--is also&lt;br /&gt;
important. Developers are reluctant to write OS/2-only applications until&lt;br /&gt;
OS/2 has achieved major penetration of the market, yet this very&lt;br /&gt;
unavailability of software slows such penetration. If it's possible to&lt;br /&gt;
write applications that take advantage of OS/2's protect mode yet also run&lt;br /&gt;
unchanged under MS-DOS version 3.x, ISVs (Independent Software Vendors) can&lt;br /&gt;
write their products for OS/2 without locking themselves out of the&lt;br /&gt;
existing MS-DOS market.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4.1  Family API&lt;br /&gt;
To provide downward compatibility for applications, OS/2 designers&lt;br /&gt;
integrated a Family&lt;br /&gt;
6.Family refers to the MS-DOS/OS/2 family of&lt;br /&gt;
operating systems.&lt;br /&gt;
6 Applications Program Interface (Family API) into the&lt;br /&gt;
OS/2 project. The Family API provides a standard execution environment&lt;br /&gt;
under MS-DOS version 3.x and OS/2. Using the Family API, a programmer can&lt;br /&gt;
create an application that uses a subset of OS/2 functions (but a superset&lt;br /&gt;
of MS-DOS version 3.x functions) and that runs in a binary compatible&lt;br /&gt;
fashion under MS-DOS version 3.x and OS/2. In effect, some OS/2 functions&lt;br /&gt;
can be retrofitted into an MS-DOS version 3.x environment by means of the&lt;br /&gt;
Family API.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4.2  Network Server-Client Compatibility&lt;br /&gt;
Another important form of upward and downward compatibility is the network&lt;br /&gt;
system. You can expect any OS/2 system to be on a network, communicating&lt;br /&gt;
not only with MS-DOS 3.x systems but, one day, with a new version of OS/2&lt;br /&gt;
as well. The network interface must be simultaneously upwardly and&lt;br /&gt;
downwardly compatible with all past and future versions of networking MS-&lt;br /&gt;
DOS.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==3  The OS/2 Religion==&lt;br /&gt;
&lt;br /&gt;
Religion, in the context of software design, is a body of beliefs about&lt;br /&gt;
design rights and design wrongs. A particular design is praised or&lt;br /&gt;
criticized on the basis of fact--it is small or large, fast or slow--and&lt;br /&gt;
also on the basis of religion--it is good or bad, depending on how well it&lt;br /&gt;
obeys the religious precepts. Purpose and consistency underlie the design&lt;br /&gt;
religion as a whole; its influence is felt in every individual judgment.&lt;br /&gt;
     The purpose of software design religion is to specify precepts that&lt;br /&gt;
designers can follow when selecting an approach from among the many&lt;br /&gt;
possibilities before them. A project of the size and scope of OS/2 needed a&lt;br /&gt;
carefully thought out religion because OS/2 will dramatically affect this&lt;br /&gt;
and future generations of operating systems. It needed a strong religion&lt;br /&gt;
for another reason: to ensure consistency among wide-ranging features&lt;br /&gt;
implemented by a large team of programmers. Such consistency is very&lt;br /&gt;
important; if one programmer optimizes design to do A well, at the expense&lt;br /&gt;
of doing B less well, and another programmer--in the absence of religious&lt;br /&gt;
guidance--does the opposite, the end result is a product that does neither&lt;br /&gt;
A nor B well.&lt;br /&gt;
     This chapter discusses the major architectural dogmas of the OS/2&lt;br /&gt;
religion: maximum flexibility, a stable environment, localization of&lt;br /&gt;
errors, and the software tools approach.&lt;br /&gt;
&lt;br /&gt;
===3.1  Maximum Flexibility===&lt;br /&gt;
&lt;br /&gt;
The introduction to this book discusses the process of technological&lt;br /&gt;
breakthroughs. I have pointed out that one of the easiest predictions about&lt;br /&gt;
breakthroughs is that fully predicting their course is impossible. For&lt;br /&gt;
example, the 8088 microprocessor is designed to address 1 MB of memory, but&lt;br /&gt;
the IBM PC and compatible machines are designed so that addressable memory&lt;br /&gt;
is limited to 640 KB. When this decision was made, 640 KB was ten times the&lt;br /&gt;
memory that the then state-of-the-art 8080 machines could use; the initial&lt;br /&gt;
PCs were going to ship with 16 KB in them, and it seemed to all concerned&lt;br /&gt;
that 640 KB was overly generous. Yet it took only a few years before 640 KB&lt;br /&gt;
became the typical memory complement of a machine, and within another year&lt;br /&gt;
that amount of memory was viewed as pitifully small.&lt;br /&gt;
     OS/2's design religion addresses the uncertain future by decreeing&lt;br /&gt;
that--to the extent compatible with other elements in the design religion--&lt;br /&gt;
OS/2 shall be as flexible as possible. The tenet of flexibility is that&lt;br /&gt;
each component of OS/2 should be designed as if massive changes will occur&lt;br /&gt;
in that area in a future release. In other words, the current component&lt;br /&gt;
should be designed in a way that does not restrict new features and in a&lt;br /&gt;
way that can be easily supported by a new version of OS/2, one that might&lt;br /&gt;
differ dramatically in internal design.&lt;br /&gt;
     Several general principles result from a design goal of flexibility.&lt;br /&gt;
All are intended to facilitate change, which is inevitable in the general&lt;br /&gt;
yet unpredictable in the specific:&lt;br /&gt;
&lt;br /&gt;
     1.  All OS/2 features should be sufficiently elemental (simple) that&lt;br /&gt;
         they can be easily supported in any future system, including&lt;br /&gt;
         systems fundamentally different in design from OS/2. Either the&lt;br /&gt;
         features themselves are this simple, or the features are built&lt;br /&gt;
         using base features that are this simple. The adjective simple&lt;br /&gt;
         doesn't particularly refer to externals--a small number of&lt;br /&gt;
         functions and options--but to internals. The internal operating&lt;br /&gt;
         system infrastructure necessary to provide a function should be&lt;br /&gt;
         either very simple or so fundamental to the nature of operating&lt;br /&gt;
         systems that it is inevitable in future releases.&lt;br /&gt;
            By way of analogy, as time travelers we may be able to guess&lt;br /&gt;
         very little about the twenty-first century, but we do know that &lt;br /&gt;
         people will still need to eat. The cuisine of the twenty-first&lt;br /&gt;
         century may be unguessable, but certainly future kitchens will&lt;br /&gt;
         contain facilities to cut and heat food. If we bring food that&lt;br /&gt;
         needs only those two operations, we'll find that even if there's&lt;br /&gt;
         nothing to our liking on the twenty-first-century standard menu&lt;br /&gt;
         the kitchen can still meet our needs.&lt;br /&gt;
            We've seen how important upward compatibility is for computer&lt;br /&gt;
         operating systems, so we can rest assured that the future MS-DOS&lt;br /&gt;
         &amp;quot;kitchen&amp;quot; will be happy to make the necessary effort to support&lt;br /&gt;
         old programs. All we have to do today is to ensure that such&lt;br /&gt;
         support is possible. Producing a compatible line of operating&lt;br /&gt;
         system releases means more than looking backward; it also means&lt;br /&gt;
         looking forward.&lt;br /&gt;
&lt;br /&gt;
     2.  All system interfaces need to support expansion in a future&lt;br /&gt;
         release. For example, if a call queries the status of a disk file,&lt;br /&gt;
         then in addition to passing the operating system a pointer to a&lt;br /&gt;
         structure to fill in with the information, the application must&lt;br /&gt;
         also pass in the length of that structure. Although the current&lt;br /&gt;
         release of the operating system returns N bytes of information, a&lt;br /&gt;
         future release may support new kinds of disk files and may return&lt;br /&gt;
         M bytes of information. Because the application tells the&lt;br /&gt;
         operating system, via the buffer length parameter, which version&lt;br /&gt;
         of the information structure that the application understands (the&lt;br /&gt;
         old short version or the new longer version), the operating system&lt;br /&gt;
         can support both old programs and new programs simultaneously.&lt;br /&gt;
            In general, all system interfaces should be designed to support&lt;br /&gt;
         the current feature set without restraining the addition of&lt;br /&gt;
         features to the interfaces in future releases. Extra room should&lt;br /&gt;
         be left in count and flag arguments for future expansion, and all&lt;br /&gt;
         passed and returned structures need either to be self-sizing or to&lt;br /&gt;
         include a size argument.&lt;br /&gt;
            One more interface deserves special mention--the file system&lt;br /&gt;
         interface. Expanding the capabilities of the file system, such as&lt;br /&gt;
         allowing filenames longer than eight characters, is difficult&lt;br /&gt;
         because many old applications don't know how to process filenames&lt;br /&gt;
         that are longer than eight characters or they regard the longer&lt;br /&gt;
         names as illegal and reject them. OS/2 solves this and similar&lt;br /&gt;
         problems by specifying that all filenames supplied to or returned&lt;br /&gt;
         from the operating system be zero-terminated strings (ASCIIZ&lt;br /&gt;
         strings) of arbitrary length.&lt;br /&gt;
            Programmers are specifically cautioned against parsing or&lt;br /&gt;
         otherwise &amp;quot;understanding&amp;quot; filenames. Programs should consider file&lt;br /&gt;
         system pathnames as &amp;quot;magic cookies&amp;quot; to be passed to and from the&lt;br /&gt;
         operating system, but not to be parsed by the program. The details&lt;br /&gt;
         of this interface and other expandable interfaces are discussed in&lt;br /&gt;
         later chapters.&lt;br /&gt;
&lt;br /&gt;
     3.  OS/2 needs to support the addition of functions at any time. The&lt;br /&gt;
         implementation details of these functions need to be hidden from&lt;br /&gt;
         the client applications so that those details can be changed at&lt;br /&gt;
         any time. Indeed, OS/2 should disguise even the source of a&lt;br /&gt;
         feature. Some APIs are serviced by kernel code, others are&lt;br /&gt;
         serviced by subroutine libraries, and still others may be serviced&lt;br /&gt;
         by other processes running in the system. Because a client&lt;br /&gt;
         application can't tell the difference, the system designers are&lt;br /&gt;
         free to change the implementation of an API as necessary. For&lt;br /&gt;
         example, an OS/2 kernel API might be considerably changed in a&lt;br /&gt;
         future release. The old API can continue to be supported by the&lt;br /&gt;
         creation of a subroutine library routine. This routine would take&lt;br /&gt;
         the old form of the API, convert it to the new form, call the OS/2&lt;br /&gt;
         kernel, and then backconvert the result. Such a technique allows&lt;br /&gt;
         future versions of OS/2 to support new features while continuing&lt;br /&gt;
         to provide the old features to existing programs. These techniques&lt;br /&gt;
         are discussed in detail in Chapter 7, Dynamic Linking.&lt;br /&gt;
&lt;br /&gt;
     4.  Finally, to provide maximum flexibility, the operating system&lt;br /&gt;
         should be extensible and expandable in a piecemeal fashion out in&lt;br /&gt;
         the field. In other words, a user should be able to add functions&lt;br /&gt;
         to the system--for example, a database engine--or to upgrade or&lt;br /&gt;
         replace system components--such as a new graphics display driver--&lt;br /&gt;
         without a new release from Microsoft. A microcomputer design that&lt;br /&gt;
         allows third-party hardware additions and upgrades in the field is&lt;br /&gt;
         called an open system. The IBM PC line is a classic example of an&lt;br /&gt;
         open system. A design that contains no provisions for such&lt;br /&gt;
         enhancements is called a closed system. The earliest version of&lt;br /&gt;
         the Apple Macintosh is an example. At first glance, MS-DOS appears&lt;br /&gt;
         to be a closed software system because it contains no provisions&lt;br /&gt;
         for expansion. In practice, its unprotected environment makes MS-&lt;br /&gt;
         DOS the king of the open software systems because every&lt;br /&gt;
         application is free to patch the system and access the hardware as&lt;br /&gt;
         it sees fit. Keeping a software system open is as important as&lt;br /&gt;
         keeping a hardware system open. Because OS/2 is a protected&lt;br /&gt;
         operating system, explicit features, such as dynamic linking, are&lt;br /&gt;
         provided to allow system expansion by Microsoft, other software&lt;br /&gt;
         vendors, and users themselves. The topic of open systems is&lt;br /&gt;
         discussed more fully in Chapter 7.&lt;br /&gt;
&lt;br /&gt;
===3.2  A Stable Environment===&lt;br /&gt;
&lt;br /&gt;
An office automation operating system has to provide its users--the&lt;br /&gt;
application programs and the human operator--with a stable environment.&lt;br /&gt;
Every application should work the same way each time it's run; and each&lt;br /&gt;
time an application is given the same data, it should produce the same&lt;br /&gt;
result. The normal operation of one application should not affect any other&lt;br /&gt;
application. Even a program error (bug) should not affect other programs in&lt;br /&gt;
the system. Finally, if a program has bugs, the operating system should&lt;br /&gt;
detect those bugs whenever possible and report them to the user. These&lt;br /&gt;
certainly are obvious goals, but the nature of present-day computers makes&lt;br /&gt;
them surprisingly difficult to achieve.&lt;br /&gt;
&lt;br /&gt;
====3.2.1  Memory Protection====&lt;br /&gt;
Modern computers are based on the Von Neumann design--named after John Von&lt;br /&gt;
Neumann, the pioneering Hungarian-born American mathematician and computer&lt;br /&gt;
scientist. A Von Neumann computer consists of only two parts: a memory unit&lt;br /&gt;
and a processing unit. The memory unit contains both the data to be&lt;br /&gt;
operated on and the instructions (or program) that command the processing&lt;br /&gt;
unit. The processing unit reads instructions from memory; these&lt;br /&gt;
instructions may tell it to issue further reads to memory to retrieve data,&lt;br /&gt;
to operate on data retrieved earlier, or to store data back into&lt;br /&gt;
memory.&lt;br /&gt;
     A Von Neumann computer does not distinguish between instructions and&lt;br /&gt;
data; both are stored in binary code in the computer's memory. Individual&lt;br /&gt;
programs are responsible for keeping track of which memory locations hold&lt;br /&gt;
instructions and which hold data, and each program uses the memory in a&lt;br /&gt;
different way. Because the computer does not distinguish between&lt;br /&gt;
instructions and data, a program may operate on its own instructions&lt;br /&gt;
exactly as it operates on data. A program can read, modify, and write&lt;br /&gt;
computer instructions at will.&lt;br /&gt;
1. This is a simplification. OS/2 and the 80286 CPU contain&lt;br /&gt;
features that do distinguish somewhat between instructions and&lt;br /&gt;
data and that limit the ability of programs to modify their own&lt;br /&gt;
instructions. See 9.1 Protection Model for more information.&lt;br /&gt;
1&lt;br /&gt;
     This is exactly what OS/2 does when it is commanded to run a program:&lt;br /&gt;
It reads the program into memory by treating it as data, and then it causes&lt;br /&gt;
the data in those locations to be executed. It is even possible for a&lt;br /&gt;
program to dynamically &amp;quot;reprogram&amp;quot; itself by manipulating its own&lt;br /&gt;
instructions.&lt;br /&gt;
     Computer programs are extremely complex, and errors in their logic can&lt;br /&gt;
cause the program to unintentionally modify data or instructions in memory.&lt;br /&gt;
For example, a carelessly written program might contain a command buffer 80&lt;br /&gt;
bytes in size because it expects no commands longer than 80 bytes. If a&lt;br /&gt;
user types a longer command, perhaps in error, and the program does not&lt;br /&gt;
contain a special check for this circumstance, the program will overwrite&lt;br /&gt;
the memory beyond the 80-byte command buffer, destroying the data or&lt;br /&gt;
instructions placed there.&lt;br /&gt;
2. This is a simplified example. Rarely would a present-day,&lt;br /&gt;
well-tested application contain such a naive error, but errors of&lt;br /&gt;
this type--albeit in a much more complex form--exist in nearly&lt;br /&gt;
all software.&lt;br /&gt;
2&lt;br /&gt;
     In a single-tasking environment such as MS-DOS, only one application&lt;br /&gt;
runs at a time. An error such as our example could damage memory belonging&lt;br /&gt;
to MS-DOS, the application, or memory that is not in use. In practice (due&lt;br /&gt;
to memory layout conventions) MS-DOS is rarely damaged. An aberrant program&lt;br /&gt;
typically damages itself or modifies memory not in use. In any case, the&lt;br /&gt;
error goes undetected, the program produces an incorrect result, or the&lt;br /&gt;
system crashes. In the last two cases, the user loses work, but it is clear&lt;br /&gt;
which application is in error--the one executing at the time of the crash.&lt;br /&gt;
(For completeness, I'll point out that it is possible for an aberrant&lt;br /&gt;
application to damage MS-DOS subtly enough so that the application itself&lt;br /&gt;
completes correctly, but the next time an application runs, it fails. This&lt;br /&gt;
is rare, and the new application generally fails immediately upon startup;&lt;br /&gt;
so after a few such episodes with different applications, the user&lt;br /&gt;
generally identifies the true culprit.)&lt;br /&gt;
     As we have seen, errors in programs are relatively well contained in a&lt;br /&gt;
single-tasking system. MS-DOS cannot, unfortunately, correct the error, nor&lt;br /&gt;
can it very often detect the error (these tasks can be shown to be&lt;br /&gt;
mathematically impossible, in the general case). But at least the errors&lt;br /&gt;
are contained within the aberrant application; and should errors in data or&lt;br /&gt;
logic become apparent, the user can identify the erring application. When&lt;br /&gt;
we execute a second program in memory alongside the first, the situation&lt;br /&gt;
becomes more complicated.&lt;br /&gt;
     The first difficulty arises because the commonest error for a program&lt;br /&gt;
to make is to use memory that MS-DOS has not allocated to it. In a single-&lt;br /&gt;
tasking environment these memory locations are typically unused, but in a&lt;br /&gt;
multitasking environment the damaged location(s) probably belong to some&lt;br /&gt;
other program. That program will then either give incorrect results, damage&lt;br /&gt;
still other memory locations, crash, or some combination of these. In&lt;br /&gt;
summary, a memory addressing error is more dangerous because there is more&lt;br /&gt;
in memory to damage and that damage will have a more severe effect.&lt;br /&gt;
     The second difficulty arises, not from explicit programming errors,&lt;br /&gt;
but from conflicts in the normal operation of two or more co-resident&lt;br /&gt;
programs that are in some fashion incompatible. A simple example is called&lt;br /&gt;
&amp;quot;hooking the keyboard vector&amp;quot; (see Figure 3-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
BEFORE&lt;br /&gt;
                      Vector table                    Keyboard device&lt;br /&gt;
                     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿                   driver routine&lt;br /&gt;
  Device interrupt   ³            ³        ÚÄÄÄÄÄÄÄÄ� x x x x: ÄÄÄÄÄÄ&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÃÄÄÄÄÄÄÄÄÄÄÄÄ´        ³                   ÄÄÄÄÄÄ&lt;br /&gt;
             ÀÄÄÄÄÄ� ³  x x x x   ÃÄÄÄÄÄÄÄÄÙ                   ÄÄÄÄÄÄ&lt;br /&gt;
                     ÃÄÄÄÄÄÄÄÄÄÄÄÄ´                            ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³                            ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³&lt;br /&gt;
                     ³            ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
AFTER                                 Application      Keyboard device&lt;br /&gt;
                      Vector table    edits table      driver&lt;br /&gt;
                     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿    ³            Ú� x x x x: ÄÄÄÄÄÄ&lt;br /&gt;
  Device interrupt   ³            ³    ³            ³           ÄÄÄÄÄÄ&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÃÄÄÄÄÄÄÄÄÄÄÄÄ´ �ÄÄÙ            ÀÄÄÄÄÄÄÄÄÄ¿ ÄÄÄÄÄÄ&lt;br /&gt;
             ÀÄÄÄÄÄ� ³  y y y y   ÃÄ¿   Application           ³ ÄÄÄÄÄÄ&lt;br /&gt;
                     ÃÄÄÄÄÄÄÄÄÄÄÄÄ´ ³   routine               ³ ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³ ÀÄ� y y y y: ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ³            ³              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ³            ³              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                                                 ÄÄÄÄÄÄ       ³&lt;br /&gt;
                                                 jmp x x x x ÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 3-1.  Hooking the keyboard vector.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     In this case, an application modifies certain MS-DOS memory locations&lt;br /&gt;
so that when a key is pressed the application code, instead of the MS-DOS&lt;br /&gt;
code, is notified by the hardware. Applications do this because it allows&lt;br /&gt;
them to examine certain keyboard events, such as pressing the shift key&lt;br /&gt;
without pressing any other key, that MS-DOS does not pass on to&lt;br /&gt;
applications which ask MS-DOS to read the keyboard for them. It works fine&lt;br /&gt;
for one application to &amp;quot;hook&amp;quot; the keyboard vector; although hooking the&lt;br /&gt;
keyboard vector modifies system memory locations that don't belong to the&lt;br /&gt;
application, the application generally gets away with it successfully. In a&lt;br /&gt;
multitasking environment, however, a second application may want to do the&lt;br /&gt;
same trick, and the system probably won't function correctly. The result is&lt;br /&gt;
that a stable environment requires memory protection. An application must&lt;br /&gt;
not be allowed to modify, accidentally or deliberately, memory that isn't&lt;br /&gt;
assigned to that application.&lt;br /&gt;
&lt;br /&gt;
====3.2.2  Side-Effects Protection====&lt;br /&gt;
A stable environment requires more than memory protection; it also requires&lt;br /&gt;
that the system be designed so that the execution of one application&lt;br /&gt;
doesn't cause side effects for any other application. Side effects can be&lt;br /&gt;
catastrophic or they can be unremarkable, but in all cases they violate the&lt;br /&gt;
tenet of a stable environment.&lt;br /&gt;
     For example, consider the practice of hooking the keyboard interrupt&lt;br /&gt;
vector. If one application uses this technique to intercept keystrokes, it&lt;br /&gt;
will intercept all keystrokes, even those intended for some other&lt;br /&gt;
application. The side effects in this case are catastrophic--the hooking&lt;br /&gt;
application sees keystrokes that aren't intended for it, and the other&lt;br /&gt;
applications don't get any keystrokes at all.&lt;br /&gt;
     Side effects can plague programs even when they are using official&lt;br /&gt;
system features if those features are not carefully designed. For example,&lt;br /&gt;
a mainframe operating system called TOPS-10 contains a program that&lt;br /&gt;
supports command files similar to MS-DOS .BAT files, and it also contains a&lt;br /&gt;
program that provides delayed offline execution of commands. Unfortunately,&lt;br /&gt;
both programs use the same TOPS-10 facility to do their work. If you&lt;br /&gt;
include a .BAT file in a delayed command list, the two programs will&lt;br /&gt;
conflict, and the .BAT file will not execute.&lt;br /&gt;
     OS/2 deals with side effects by virtualizing to the greatest extent&lt;br /&gt;
possible each application's operating environment. This means that OS/2&lt;br /&gt;
tries to make each application &amp;quot;see&amp;quot; a standard environment that is&lt;br /&gt;
unaffected by changes in another application's environment. The effect is&lt;br /&gt;
like that of a building of identical apartments. When each tenant moves in,&lt;br /&gt;
he or she gets a standard environment, a duplicate of all the apartments.&lt;br /&gt;
Each tenant can customize his or her environment, but doing so doesn't&lt;br /&gt;
affect the other tenants or their environments.&lt;br /&gt;
     Following are some examples of application environment issues that&lt;br /&gt;
OS/2 virtualizes.&lt;br /&gt;
&lt;br /&gt;
     þ  Working Directories. Each application has a working (or current)&lt;br /&gt;
        directory for each disk drive. Under MS-DOS version 3.x, if a child&lt;br /&gt;
        process changes the working directory for drive C and then exits,&lt;br /&gt;
        the working directory for drive C remains changed when the parent&lt;br /&gt;
        process regains control. OS/2 eliminates this side effect by&lt;br /&gt;
        maintaining a separate list of working directories for each process&lt;br /&gt;
        in the system. Thus, when an application changes its working&lt;br /&gt;
        directories, the working directories of other applications in the&lt;br /&gt;
        system remain unchanged.&lt;br /&gt;
&lt;br /&gt;
     þ  Memory Utilization. The simple act of memory consumption produces&lt;br /&gt;
        side effects. If one process consumes all available RAM, none is&lt;br /&gt;
        left for the others. The OS/2 memory management system uses memory&lt;br /&gt;
        overcommit (swapping) so that the memory needs of each application&lt;br /&gt;
        can be met.&lt;br /&gt;
&lt;br /&gt;
     þ  Priority. OS/2 uses a priority-based scheduler to assign the CPU to&lt;br /&gt;
        the processes that need it. Applications can adjust their priority&lt;br /&gt;
        and that of their child processes as they see fit. However, the&lt;br /&gt;
        very priority of a task causes side effects. Consider a process&lt;br /&gt;
        that tells OS/2 that it must run at a higher priority than any&lt;br /&gt;
        other task in the system. If a second process makes the same&lt;br /&gt;
        request, a conflict occurs: Both processes cannot be the highest&lt;br /&gt;
        priority in the system. In general, the priority that a process&lt;br /&gt;
        wants for itself depends on the priorities of the other processes&lt;br /&gt;
        in the system. The OS/2 scheduler contains a sophisticated&lt;br /&gt;
        absolute/relative mechanism to deal with these conflicts.&lt;br /&gt;
&lt;br /&gt;
     þ  File Utilization. As discussed earlier, one application may modify&lt;br /&gt;
        the files that another application is using, causing an unintended&lt;br /&gt;
        side effect. The OS/2 file-locking mechanism prevents unintended&lt;br /&gt;
        modifications, and the OS/2 record-locking mechanism coordinates&lt;br /&gt;
        intentional parallel updates to a single file.&lt;br /&gt;
&lt;br /&gt;
     þ  Environment Strings. OS/2 retains the MS-DOS concept of environment&lt;br /&gt;
        strings: Each process has its own set. A child process inherits a&lt;br /&gt;
        copy of the parent's environment strings, but changing the strings&lt;br /&gt;
        in this copy will not affect the original strings in the parent's&lt;br /&gt;
        environment.&lt;br /&gt;
&lt;br /&gt;
     þ  Keyboard Mode. OS/2 applications can place the keyboard in one of&lt;br /&gt;
        two modes--cooked or raw. These modes tell OS/2 whether the&lt;br /&gt;
        application wants to handle, for example, the backspace character&lt;br /&gt;
        (raw mode) or whether it wants OS/2 to handle the backspace&lt;br /&gt;
        character for it (cooked mode). The effect of these calls on&lt;br /&gt;
        subsequent keyboard read operations would cause side effects for&lt;br /&gt;
        other applications reading from the keyboard, so OS/2 maintains a&lt;br /&gt;
        record of the cooked/raw status of each application and silently&lt;br /&gt;
        switches the mode of the keyboard when an application issues a&lt;br /&gt;
        keyboard read request.&lt;br /&gt;
&lt;br /&gt;
===3.3  Localization of Errors===&lt;br /&gt;
&lt;br /&gt;
A key element in creating a stable environment is localizing errors. Humans&lt;br /&gt;
always make errors, and human creations such as computer programs always&lt;br /&gt;
contain errors. Before the development of computers, routine human errors&lt;br /&gt;
were usually limited in scope. Unfortunately, as the saying goes, a&lt;br /&gt;
computer can make a mistake in 60 seconds that it would take a whole office&lt;br /&gt;
force a year to make. Although OS/2 can do little to prevent such errors,&lt;br /&gt;
it needs to do its best to localize the errors.&lt;br /&gt;
     Localizing errors consists of two activities: minimizing as much as&lt;br /&gt;
possible the impact of the error on other applications in the system, and&lt;br /&gt;
maximizing the opportunity for the user to understand which of the many&lt;br /&gt;
programs running in the computer caused the error. These two activities are&lt;br /&gt;
interrelated in that the more successful the operating system is in&lt;br /&gt;
restricting the damage to the domain of a single program, the easier it is&lt;br /&gt;
for the user to know which program is at fault.&lt;br /&gt;
     The most important aspect of error localization has already been&lt;br /&gt;
discussed at length--memory management and protection. Other error&lt;br /&gt;
localization principles include the following:&lt;br /&gt;
&lt;br /&gt;
     þ  No program can crash or hang the system. A fundamental element of&lt;br /&gt;
        the OS/2 design religion is that no application program can,&lt;br /&gt;
        accidentally or even deliberately, crash or hang the system. If a&lt;br /&gt;
        failing application could crash the system, obviously the system&lt;br /&gt;
        did not localize the error! Furthermore, the user would be unable&lt;br /&gt;
        to identify the responsible application because the entire system&lt;br /&gt;
        would be dead.&lt;br /&gt;
&lt;br /&gt;
     þ  No program can make inoperable any screen group other than its own.&lt;br /&gt;
        As we'll see in later chapters of this book, sometimes design&lt;br /&gt;
        goals, design religions, or both conflict. For example, the precept&lt;br /&gt;
        of no side effects conflicts with the requirement of supporting&lt;br /&gt;
        keyboard macro expander applications. The sole purpose of such an&lt;br /&gt;
        application is to cause a side effect--specifically to translate&lt;br /&gt;
        certain keystroke sequences into other sequences. OS/2 resolves&lt;br /&gt;
        this conflict by allowing applications to examine and modify the&lt;br /&gt;
        flow of data to and from devices (see Chapter 16) but in a&lt;br /&gt;
        controlled fashion. Thus, an aberrant keyboard macro application&lt;br /&gt;
        that starts to &amp;quot;eat&amp;quot; all keys, passing none through to the&lt;br /&gt;
        application, can make its current screen group unusable, but it&lt;br /&gt;
        can't affect the user's ability to change screen groups.&lt;br /&gt;
           Note that keyboard monitors can intercept and consume any&lt;br /&gt;
        character or character sequence except for the keystrokes that OS/2&lt;br /&gt;
        uses to switch screen groups (Ctrl-Esc and Alt-Esc). This is to&lt;br /&gt;
        prevent aberrant keyboard monitor applications from accidentally&lt;br /&gt;
        locking the user into his or her screen group by consuming and&lt;br /&gt;
        discarding the keyboard sequences that are used to switch from&lt;br /&gt;
        screen groups.&lt;br /&gt;
&lt;br /&gt;
     þ  Applications cannot intercept general protection (GP) fault errors.&lt;br /&gt;
        A GP fault occurs when a program accesses invalid memory locations&lt;br /&gt;
        or accesses valid locations in an invalid way (such as writing into&lt;br /&gt;
        read-only memory areas). OS/2 always terminates the operation and&lt;br /&gt;
        displays a message for the user. A GP fault is evidence that the&lt;br /&gt;
        program's logic is incorrect, and therefore it cannot be expected&lt;br /&gt;
        to fix itself or trusted to notify the user of its ill health.&lt;br /&gt;
           The OS/2 design does allow almost any other error on the part of&lt;br /&gt;
        an application to be detected and handled by that application. For&lt;br /&gt;
        example, &amp;quot;Illegal filename&amp;quot; is an error caused by user input, not&lt;br /&gt;
        by the application. The application can deal with this error as it&lt;br /&gt;
        sees fit, perhaps correcting and retrying the operation. An error&lt;br /&gt;
        such as &amp;quot;Floppy disk drive not ready&amp;quot; is normally handled by OS/2&lt;br /&gt;
        but can be handled by the application. This is useful for&lt;br /&gt;
        applications that are designed to operate unattended; they need to&lt;br /&gt;
        handle errors themselves rather than waiting for action to be taken&lt;br /&gt;
        by a nonexistent user.&lt;br /&gt;
&lt;br /&gt;
===3.4  Software Tools Approach===&lt;br /&gt;
&lt;br /&gt;
In Chapter 2 we discussed IPC and the desirability of having separate&lt;br /&gt;
functions contained in separate programs. We discussed the flexibility of&lt;br /&gt;
such an approach over the &amp;quot;one man band&amp;quot; approach of an all-in-one&lt;br /&gt;
application. We also touched on the value of being able to upgrade the&lt;br /&gt;
functionality of the system incrementally by replacing individual programs.&lt;br /&gt;
All these issues are software tools issues.&lt;br /&gt;
     Software tools refers to a design philosophy which says that&lt;br /&gt;
individual programs and applications should be like tools: Each should do&lt;br /&gt;
one job and do it very well. A person who wants to turn screws and also&lt;br /&gt;
drive nails should get a screwdriver and a hammer rather than a single tool&lt;br /&gt;
that does neither job as well.&lt;br /&gt;
     The tools approach is used routinely in nonsoftware environments and&lt;br /&gt;
is taken for granted. For example, inside a standard PC the hardware and&lt;br /&gt;
electronics are isolated into functional components that communicate via&lt;br /&gt;
interfaces. The power supply is in a box by itself; its interface is the&lt;br /&gt;
line cord and some power connectors. The disk drives are separate from the&lt;br /&gt;
rest of the electronics; they interface via more connectors. Each component&lt;br /&gt;
is the equivalent of a software application: It does one job and does it&lt;br /&gt;
well. When the disk drive needs power, it doesn't build in a power supply;&lt;br /&gt;
it uses the standard interface to the power supply module--the power&lt;br /&gt;
&amp;quot;specialist&amp;quot; in the system.&lt;br /&gt;
     Occasionally, the software tools approach is criticized for being&lt;br /&gt;
inefficient. People may argue that space is wasted and time is lost by&lt;br /&gt;
packaging key functions separately; if they are combined, the argument&lt;br /&gt;
goes, nothing is wasted. This argument is correct in that some RAM and CPU&lt;br /&gt;
time is spent on interface issues, but it ignores the gains involved in&lt;br /&gt;
&amp;quot;sending out the work&amp;quot; to a specialist rather than doing it oneself. One&lt;br /&gt;
could argue, for example, that if I built an all-in-one PC system I'd save&lt;br /&gt;
money because I wouldn't have to buy connectors to plug everything&lt;br /&gt;
together. I might also save a little by not having to buy buffer chips to&lt;br /&gt;
drive signals over those connectors. But in doing so, I'd lose the&lt;br /&gt;
advantage of being able to buy my power supply from a very high-volume and&lt;br /&gt;
high-efficiency supplier--someone who can make a better, cheaper supply,&lt;br /&gt;
even with the cost of connectors, than my computer company can.&lt;br /&gt;
     Finally, the user gains from the modular approach. If you need more&lt;br /&gt;
disk capability, you can buy one and plug it in. You are not limited to one&lt;br /&gt;
disk maker but rather can choose the one that's right for your needs--&lt;br /&gt;
expensive and powerful or cheap and modest. You can buy third-party&lt;br /&gt;
hardware, such as plug-in cards, that the manufacturer of your computer&lt;br /&gt;
doesn't make. All in all, the modest cost of a few connectors and driver&lt;br /&gt;
chips is paid back manyfold, both in direct system costs (due to the&lt;br /&gt;
efficiency of specialization) and in the additional capability and&lt;br /&gt;
flexibility of the machine.&lt;br /&gt;
     As I said earlier, the software tools approach is the software&lt;br /&gt;
equivalent of an open system. It's an important part of the OS/2 religion:&lt;br /&gt;
Although the system doesn't require a modular tools approach from&lt;br /&gt;
applications programs, it should do everything in its power to facilitate&lt;br /&gt;
such systems, and it should itself be constructed in that fashion.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Part II  The Architecture&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==4  Multitasking==&lt;br /&gt;
&lt;br /&gt;
I have discussed the goals and compatibility issues that OS/2 is intended&lt;br /&gt;
to meet, and I have described the design religion that was established for&lt;br /&gt;
OS/2. The following chapters discuss individual design elements in some&lt;br /&gt;
detail, emphasizing not only how the elements work and are used but the&lt;br /&gt;
role they play in the system as a whole.&lt;br /&gt;
     In a multitasking operating system, two or more programs can execute&lt;br /&gt;
at the same time. Some benefits of such a feature are obvious: You (the&lt;br /&gt;
user) can switch between several application programs without saving work&lt;br /&gt;
and exiting one program to start another. When the telephone rings, for&lt;br /&gt;
example, you can switch from the word processor application you are using&lt;br /&gt;
to write a memo and go to the application that is managing your appointment&lt;br /&gt;
calendar or to the spreadsheet application that contains the figures that&lt;br /&gt;
are necessary to answer your caller's query.&lt;br /&gt;
     This type of multitasking is similar to what people do when they're&lt;br /&gt;
not working with a computer. You may leave a report half read on your desk&lt;br /&gt;
to address a more pressing need, such as answering the phone. Later,&lt;br /&gt;
perhaps after other tasks intervene, you return to the report. You don't&lt;br /&gt;
terminate a project and return your reference materials to the bookshelf,&lt;br /&gt;
the files, and the library to answer the telephone; you merely switch your&lt;br /&gt;
attention for a while and later pick up where you left off.&lt;br /&gt;
     This kind of multitasking is called serial multitasking because&lt;br /&gt;
actions are performed one at a time. Although you probably haven't thought&lt;br /&gt;
of it this way, you've spent much of your life serially multitasking. Every&lt;br /&gt;
day when you leave for work, you suspend your home life and resume your&lt;br /&gt;
work life. That evening, you reverse the process. You serially multitask a&lt;br /&gt;
hobby--each time picking it up where you left off last time and then&lt;br /&gt;
leaving off again. Reading the comics in a daily newspaper is a prodigious&lt;br /&gt;
feat of human serial multitasking--you switch from one to another of&lt;br /&gt;
perhaps 20 strips, remembering for each what has gone on before and then&lt;br /&gt;
waiting until tomorrow for the next installment. Although serial&lt;br /&gt;
multitasking is very useful, it is not nearly as useful as full&lt;br /&gt;
multitasking--the kind of multitasking built into OS/2.&lt;br /&gt;
     Full multitasking on the computer involves doing more than one thing--&lt;br /&gt;
running more than one application--at the same time. Humans do a little of&lt;br /&gt;
this, but not too much. People commonly talk while they drive cars, eat&lt;br /&gt;
while watching television, and walk while chewing gum. None of these&lt;br /&gt;
activities requires one's full concentration though. Humans generally can't&lt;br /&gt;
fully multitask activities that require a significant amount of&lt;br /&gt;
concentration because they have only one brain.&lt;br /&gt;
     For that matter, a personal computer has only one &amp;quot;brain&amp;quot;--one CPU.&lt;br /&gt;
1. Although multiple-CPU computers are well known, personal computers&lt;br /&gt;
with multiple CPUs are uncommon. In any case, this discussion applies,&lt;br /&gt;
with the obvious extensions, to multiple-CPU systems.&lt;br /&gt;
1&lt;br /&gt;
But OS/2 can switch this CPU from one activity to another very rapidly--&lt;br /&gt;
dozens or even hundreds of times a second. All executing programs seem to&lt;br /&gt;
be running at the same time, at least on the human scale of time. For&lt;br /&gt;
example, if five programs are running and each in turn gets 0.01 second of&lt;br /&gt;
CPU time (that is, 10 milliseconds), in 1 second each program receives 20&lt;br /&gt;
time slices. To most observers, human or other computer software, all five&lt;br /&gt;
programs appear to be running simultaneously but each at one-fifth its&lt;br /&gt;
maximum speed. We'll return to the topic of time slicing later; for now,&lt;br /&gt;
it's easiest--and, as we shall see, best--to pretend that all executing&lt;br /&gt;
programs run simultaneously.&lt;br /&gt;
     The full multitasking capabilities of OS/2 allow the personal computer&lt;br /&gt;
to act as more than a mere engine to run applications; the personal&lt;br /&gt;
computer can now be a system of services. The user can interact with a&lt;br /&gt;
spreadsheet program, for example, while a mail application is receiving&lt;br /&gt;
network messages that the user can read later. At the same time, other&lt;br /&gt;
programs may be downloading data from a mainframe computer or spooling&lt;br /&gt;
output to a printer or a plotter. The user may have explicitly initiated&lt;br /&gt;
some of these activities; a program may have initiated others. Regardless,&lt;br /&gt;
they all execute simultaneously, and they all do their work without&lt;br /&gt;
requiring the user's attention or intervention.&lt;br /&gt;
     Full multitasking is useful to programs themselves. Earlier, we&lt;br /&gt;
discussed the advantages of a tools approach--writing programs so that&lt;br /&gt;
they can offer their services to other programs. The numerous advantages&lt;br /&gt;
of this technique are possible only because of full&lt;br /&gt;
multitasking. For&lt;br /&gt;
example, if a program is to be able to invoke another program to sort a &lt;br /&gt;
data file, the sort program must execute at the same time as its client &lt;br /&gt;
program. It wouldn't be very useful if the client program had to &lt;br /&gt;
terminate in order  for the sort program to run.&lt;br /&gt;
     Finally, full multitasking is useful within a program itself. A thread&lt;br /&gt;
is an OS/2 mechanism that allows more than one path of execution through a&lt;br /&gt;
particular application. (Threads are discussed in detail later; for now it&lt;br /&gt;
will suffice to imagine that several CPUs can be made to execute the same&lt;br /&gt;
program simultaneously.) This allows individual applications to perform&lt;br /&gt;
more than one task at a time. For example, if the user tells a spreadsheet&lt;br /&gt;
program to recalculate a large budget analysis, the program can use one&lt;br /&gt;
thread to do the calculating and another to prompt for, read, and obey the&lt;br /&gt;
user's next command. In effect, multiple operations overlap during&lt;br /&gt;
execution and thereby increase the program's responsiveness to the user.&lt;br /&gt;
     OS/2 uses a time-sliced, priority-based preemptive scheduler to&lt;br /&gt;
provide full multitasking. In other words, the OS/2 scheduler preempts--&lt;br /&gt;
takes away--the CPU from one application at any time the scheduler desires&lt;br /&gt;
and assigns the CPU another application. Programs don't surrender the CPU&lt;br /&gt;
when they feel like it; OS/2 preempts it. Each program in the system (more&lt;br /&gt;
precisely, each thread in the system) has its own priority. When a thread&lt;br /&gt;
of a higher priority than the one currently running wants to run, the&lt;br /&gt;
scheduler preempts the running thread in favor of the higher priority one.&lt;br /&gt;
If two or more runnable threads have the same highest priority, OS/2 runs&lt;br /&gt;
each in turn for a fraction of a second--a time slice.&lt;br /&gt;
     The OS/2 scheduler does not periodically look around to see if the&lt;br /&gt;
highest priority thread is running. Such an approach wastes CPU time and&lt;br /&gt;
slows response time because a higher priority thread must wait to run until&lt;br /&gt;
the next scheduler scan. Instead, other parts of the system call the&lt;br /&gt;
scheduler when they think that a thread other than the one running should&lt;br /&gt;
be executed.&lt;br /&gt;
&lt;br /&gt;
===4.1  Subtask Model===&lt;br /&gt;
&lt;br /&gt;
The terms task and process are used interchangeably to describe the direct&lt;br /&gt;
result of executing a binary (.EXE) file. A process is the unit of&lt;br /&gt;
ownership under OS/2, and processes own resources such as memory, open&lt;br /&gt;
files, connections to dynlink libraries, and semaphores. Casual users would&lt;br /&gt;
call a process a &amp;quot;program&amp;quot;; and, in fact, under MS-DOS all programs and&lt;br /&gt;
applications consist of a single process. OS/2 uses the terms task or&lt;br /&gt;
process because a single application program under OS/2 may consist of more&lt;br /&gt;
than one process. This section describes how this is done.&lt;br /&gt;
     First, some more terminology. When a process creates, or execs,&lt;br /&gt;
another process, the creator process is called the parent process, and the&lt;br /&gt;
created process is called the child process. The parent of the parent is&lt;br /&gt;
the child's grandparent and so on. As with people, each process in the&lt;br /&gt;
system has or had a parent.&lt;br /&gt;
2. Obviously, during boot-up OS/2 creates an initial parentless&lt;br /&gt;
process by &amp;quot;magic,&amp;quot; but this is ancient history by the time any&lt;br /&gt;
application may run, so the anomaly may be safely ignored.&lt;br /&gt;
2 Although we use genealogical terms to describe&lt;br /&gt;
task relationships, a child task, or process, is more like an agent or&lt;br /&gt;
employee of the parent task. Employees are hired to do work for an&lt;br /&gt;
employer. The employer provides a workplace and access to the information&lt;br /&gt;
employees need to do their jobs. The same is generally true for a child&lt;br /&gt;
task. When a child task is created, it inherits (or receives a copy of) a&lt;br /&gt;
great deal of the parent task's environment. For example, it inherits, or&lt;br /&gt;
takes on, the parent's base scheduling priority and its screen group. The&lt;br /&gt;
term inherit is a little inappropriate because the parent task has not&lt;br /&gt;
died. It is alive and well, going about its business.&lt;br /&gt;
     The most important items a child task inherits are its parent's open&lt;br /&gt;
file handles. OS/2 uses a handle mechanism to perform file I/O, as do MS-&lt;br /&gt;
DOS versions 2.0 and later. When a file is opened, OS/2 returns a handle--&lt;br /&gt;
an integer value--to the process. When a program wants to read from or&lt;br /&gt;
write to a file, it gives OS/2 the file handle. Handles are not identical&lt;br /&gt;
among processes. For example, the file referred to by handle 6 of one&lt;br /&gt;
process bears no relationship to the file referred to by another process's&lt;br /&gt;
handle 6, unless one of those processes is a child of the other. When a&lt;br /&gt;
parent process creates a child process, the child process, by default,&lt;br /&gt;
inherits each of the parent's open file handles. For example, a parent&lt;br /&gt;
process has the file \WORK\TEMPFILE open on handle 5; when the child&lt;br /&gt;
process starts up, handle 5 is open and references the \WORK\TEMPFILE file.&lt;br /&gt;
     This undoubtedly seems brain damaged if you are unfamiliar with this&lt;br /&gt;
model. Why is it done in this crazy way? What use does the child process&lt;br /&gt;
have for these open files? What's to keep the child from mucking up the&lt;br /&gt;
parent's files? All this becomes clearer when the other piece of the puzzle&lt;br /&gt;
is in place--the standard file handles.&lt;br /&gt;
&lt;br /&gt;
====4.1.1  Standard File Handles====&lt;br /&gt;
Many OS/2 functions use 16-bit integer values called handles for their&lt;br /&gt;
interfaces. A handle is an object that programmers call a &amp;quot;magic cookie&amp;quot;--&lt;br /&gt;
an arbitrary value that OS/2 provides the application so that the&lt;br /&gt;
application can pass the value back to OS/2 on subsequent calls. Its&lt;br /&gt;
purpose is to simplify the OS/2 interface and speed up the particular&lt;br /&gt;
service. For example, when a program creates a system semaphore, it is&lt;br /&gt;
returned a semaphore handle--a magic cookie--that it uses for subsequent&lt;br /&gt;
request and release operations. Referring to the semaphore via a 16-bit&lt;br /&gt;
value is much faster than passing around a long filename. Furthermore, the&lt;br /&gt;
magic in magic cookie is that the meaning of the 16-bit handle value is&lt;br /&gt;
indecipherable to the application. OS/2 created the value, and it has&lt;br /&gt;
meaning only to OS/2; the application need only retain the value and&lt;br /&gt;
regurgitate it when appropriate. An application can never make any&lt;br /&gt;
assumptions about the values of a magic cookie.&lt;br /&gt;
     File handles are an exceptional form of handle because they are not&lt;br /&gt;
magic cookies. The handle value, in the right circumstances, is meaningful&lt;br /&gt;
to the application and to the system as a whole. Specifically, three handle&lt;br /&gt;
values have special meaning: handle value 0, called STDIN (for standard&lt;br /&gt;
input); handle value 1, called STDOUT (standard output); and handle value&lt;br /&gt;
2, called STDERR (standard error). A simple program--let's call it NUMADD--&lt;br /&gt;
will help to explain the use of these three handles. NUMADD will read two&lt;br /&gt;
lines of ASCII text (each containing a decimal number), convert the numbers&lt;br /&gt;
to binary, add them, and then convert the results to an ASCII string and&lt;br /&gt;
write out the result. Note that we're confining our attention to a simple&lt;br /&gt;
non-screen-oriented program that might be used as a tool, either directly&lt;br /&gt;
by a programmer or by another program (see Figure 4-1 and Listing 4-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      STDIN ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
123 ÄÄÄ¿    ³          ³ STDOUT&lt;br /&gt;
14     ÀÄÄÄ�³  NUMADD  ÃÄÄÄ¿&lt;br /&gt;
            ³          ³   ÀÄÄÄÄ� 137&lt;br /&gt;
            ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-1.  Program NUMADD operation--interactive.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;            /* defines stdin and stdout */&lt;br /&gt;
&lt;br /&gt;
main()&lt;br /&gt;
{&lt;br /&gt;
     int value1, value2, sum;&lt;br /&gt;
&lt;br /&gt;
     fscanf (stdin, &amp;quot;%d&amp;quot;, &amp;amp;value1);&lt;br /&gt;
     fscanf (stdin, &amp;quot;%d&amp;quot;, &amp;amp; value2);&lt;br /&gt;
     sum = value1 + value2;&lt;br /&gt;
     fprintf (stdout, &amp;quot;%d\n&amp;quot;, sum);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Listing 4-1.  Program NUMADD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     By convention, all OS/2 programs read input from STDIN and write&lt;br /&gt;
output to STDOUT. Any error messages are written to STDERR. The program&lt;br /&gt;
itself does not open these handles; it inherits them from the parent&lt;br /&gt;
process. The parent may have opened them itself or inherited them from its&lt;br /&gt;
own parent. As you can see, NUMADD would not contain DosOpen calls;&lt;br /&gt;
instead, it would start immediately issuing fscanf calls on handle 0&lt;br /&gt;
(STDIN), which in turn issues DosRead calls, and, when ready, directly&lt;br /&gt;
issue fprintf calls to handle 1 (STDOUT), which in turn issues DosWrites.&lt;br /&gt;
     Figure 4-2 and Listing 4-2 show a hypothetical application, NUMARITH.&lt;br /&gt;
NUMARITH reads three text lines. The first line contains an operation&lt;br /&gt;
character, such as a plus (+) or a minus (-); the second and third lines&lt;br /&gt;
contain the values to be operated upon. The author of this program doesn't&lt;br /&gt;
want to reinvent the wheel; so when the program NUMARITH encounters a +&lt;br /&gt;
operation, it executes NUMADD to do the work. As shown, the parent process&lt;br /&gt;
NUMARITH has its STDIN connected to the keyboard and its STDOUT connected&lt;br /&gt;
to the screen device drivers.&lt;br /&gt;
3. CMD.EXE inherited these handles from its own parent. This process&lt;br /&gt;
is discussed later.&lt;br /&gt;
3 When NUMADD executes, it reads input from&lt;br /&gt;
the keyboard via STDIN. After the user types the two numbers, NUMADD&lt;br /&gt;
displays the result on the screen via STDOUT. NUMARITH has invoked NUMADD&lt;br /&gt;
to do some work for it, and NUMADD has silently and seamlessly acted as a&lt;br /&gt;
part of NUMARITH. The employee metaphor fits well here. NUMADD acted as an&lt;br /&gt;
employee of NUMARITH, making use of NUMARITH's I/O streams, and as a result&lt;br /&gt;
the contribution of the NUMADD employee to the NUMARITH company is seamless.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
keyboard                                   screen&lt;br /&gt;
    ³     STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT     ³&lt;br /&gt;
    ÀÄÄÄÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
            ÃÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄ´&lt;br /&gt;
            �      ³          ³     �&lt;br /&gt;
            ³      ÀÄÄÄÄÄÂÄÄÄÄÙ     ³&lt;br /&gt;
            ³            ³ DosExec  ³&lt;br /&gt;
   inherits ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿     ³ inherits&lt;br /&gt;
            ³      ³          ³     ³&lt;br /&gt;
            ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄ�ÄÄÙ&lt;br /&gt;
                   ³          ³&lt;br /&gt;
                   ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-2.  Program NUMARITH operation--interactive.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**     Numarith - Perform ASCII Arithmetic&lt;br /&gt;
*&lt;br /&gt;
*       Numarith reads line triplets:&lt;br /&gt;
*&lt;br /&gt;
*               operation&lt;br /&gt;
*               value1&lt;br /&gt;
*               value2&lt;br /&gt;
*&lt;br /&gt;
*       performs the specified operation (+, -, *, /) on&lt;br /&gt;
*       the two values and prints the result on stdout.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
main()&lt;br /&gt;
{&lt;br /&gt;
        char operation;&lt;br /&gt;
&lt;br /&gt;
        fscanf (stdin, &amp;quot;%c&amp;quot;, &amp;amp;operation);&lt;br /&gt;
&lt;br /&gt;
        switch (operation) {&lt;br /&gt;
&lt;br /&gt;
            case '+':  execl (&amp;quot;numadd&amp;quot;,  0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '-':  execl (&amp;quot;numsub&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '*':  execl (&amp;quot;nummul&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '/':  execl (&amp;quot;numdiv&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
            }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Listing 4-2.  Program NUMARITH.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Figure 4-3 shows a similar situation. In this case, however,&lt;br /&gt;
NUMARITH's STDIN and STDOUT handles are open on two files, which we'll call&lt;br /&gt;
DATAIN and DATAOUT. Once again, NUMADD does its work seamlessly. The input&lt;br /&gt;
numbers are read from the command file on STDIN, and the output is properly&lt;br /&gt;
intermingled in the log file on STDOUT. The key here is that this NUMADD is&lt;br /&gt;
exactly the same program that ran in Listing 4-2; NUMADD contains no&lt;br /&gt;
special code to deal with this changed situation. In both examples, NUMADD&lt;br /&gt;
simply reads from STDIN and writes to STDOUT; NUMADD neither knows nor&lt;br /&gt;
cares where those handles point. Exactly the same is true for the parent.&lt;br /&gt;
NUMARITH doesn't know and doesn't care that it's working from files instead&lt;br /&gt;
of from the screen; it simply uses the STDIN and STDOUT handles that it&lt;br /&gt;
inherited from its parent.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
File data in                                     File data out&lt;br /&gt;
     ___                                              ___&lt;br /&gt;
   /     \                                          /     \&lt;br /&gt;
  ³\ ___ /³                                        ³\ ___ /³&lt;br /&gt;
  ³       ³                                        ³       ³&lt;br /&gt;
  ³       ÃÄÄ¿                                  ÚÄ�³       ³&lt;br /&gt;
  ³       ³  ³  STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT  ³  ³       ³&lt;br /&gt;
   \ ___ /   ÀÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÙ   \ ___ /&lt;br /&gt;
                  ÃÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄ´&lt;br /&gt;
                  �      ³          ³     �&lt;br /&gt;
                  ³      ÀÄÄÄÄÄÂÄÄÄÄÙ     ³&lt;br /&gt;
                  ³            ³ DosExec  ³&lt;br /&gt;
         inherits ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿     ³ inherits&lt;br /&gt;
                  ³      ³          ³     ³&lt;br /&gt;
                  ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄ�ÄÄÙ&lt;br /&gt;
                         ³          ³&lt;br /&gt;
                         ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-3.  Program NUMARITH operation--from files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This is the single most important concept in the relationship and&lt;br /&gt;
inheritance structure between processes. The reason a process inherits so&lt;br /&gt;
much from its parent is so that the parent can set up the tool's&lt;br /&gt;
environment--make it read from the parent's STDIN or from a file or from an&lt;br /&gt;
anonymous pipe (see 4.1.2 Anonymous Pipes). This gives the parent the&lt;br /&gt;
flexibility to use a tool program as it wishes, and it frees the tool's&lt;br /&gt;
author from the need to be &amp;quot;all things to all people.&amp;quot;&lt;br /&gt;
     Of equal importance, the inheritance architecture provides&lt;br /&gt;
nesting encapsulation of child processes. NUMARITH's parent process doesn't&lt;br /&gt;
know and doesn't need to know how NUMARITH does its job. NUMARITH can do&lt;br /&gt;
the additions itself, or it can invoke NUMADD as a child, but the&lt;br /&gt;
architecture encapsulates the details of NUMARITH's operation so that&lt;br /&gt;
NUMADD's involvement is hidden from NUMARITH's parent. Likewise, the&lt;br /&gt;
decision of NUMARITH's parent to work from a file or from a device or from&lt;br /&gt;
a pipe is encapsulated (that is, hidden) from NUMARITH and from any child&lt;br /&gt;
processes that NUMARITH may execute to help with its work. Obviously, this&lt;br /&gt;
architecture can be extended arbitrarily: NUMADD can itself execute a child&lt;br /&gt;
process to help NUMADD with its work, and this would silently and invisibly&lt;br /&gt;
work. Neither NUMARITH nor its parent would know or need to know anything&lt;br /&gt;
about how NUMADD was doing its work. Other versions can replace any of&lt;br /&gt;
these applications at any time. The new versions can invoke more or fewer&lt;br /&gt;
child processes or be changed in any other way, and their client (that is,&lt;br /&gt;
parent) processes are unaffected. The architecture of OS/2 is tool-based;&lt;br /&gt;
as long as the function of a tool remains constant (or is supersetted), its&lt;br /&gt;
implementation is irrelevant and can be changed arbitrarily.&lt;br /&gt;
     The STDIN, STDOUT, and STDERR architecture applies to all programs,&lt;br /&gt;
even those that only use VIO, KBD, or the presentation manager and that&lt;br /&gt;
never issue operations on these handles. See Chapter 14, Interactive&lt;br /&gt;
Programs.&lt;br /&gt;
&lt;br /&gt;
====4.1.2  Anonymous Pipes====&lt;br /&gt;
NUMADD and NUMARITH are pretty silly little programs; a moment's&lt;br /&gt;
consideration will show how the inheritance architecture applies to more&lt;br /&gt;
realistic programs. An example is the TREE program that runs when the TREE&lt;br /&gt;
command is given to CMD.EXE. TREE inherits the STDIN and STDOUT handles,&lt;br /&gt;
but it does not use STDIN; it merely writes output to STDOUT. As a result,&lt;br /&gt;
when the user types TREE at a CMD.EXE prompt, the output appears on the&lt;br /&gt;
screen. When TREE appears in a batch file, the output appears in the LOG&lt;br /&gt;
file or on the screen, depending on where STDOUT is pointing.&lt;br /&gt;
     This is all very useful, but what if an application wants to further&lt;br /&gt;
process the output of the child program rather than having the child's&lt;br /&gt;
output intermingled with the application's output? OS/2 does this with&lt;br /&gt;
anonymous pipes. The adjective anonymous distinguishes these pipes from a&lt;br /&gt;
related facility, named pipes, which are not implemented in OS/2 version&lt;br /&gt;
1.0.&lt;br /&gt;
     An anonymous pipe is a data storage buffer that OS/2 maintains. When a&lt;br /&gt;
process opens an anonymous pipe, it receives two file handles--one for&lt;br /&gt;
writing and one for reading. Data can be written to the write handle via&lt;br /&gt;
the DosWrite call and then read back via the read handle and the DosRead&lt;br /&gt;
call. An anonymous pipe is similar to a file in that it is written and read&lt;br /&gt;
via file handle operations, but an anonymous pipe and a file are&lt;br /&gt;
significantly different. Pipe data is stored only in RAM buffers, not on a&lt;br /&gt;
disk, and is accessed only in FIFO (First In First Out) fashion. The&lt;br /&gt;
DosChgFilePtr operation is illegal on pipe handles.&lt;br /&gt;
     An anonymous pipe is of little value to a single process, since it&lt;br /&gt;
acts as a simple FIFO (First In First Out) storage buffer of limited size&lt;br /&gt;
and since the data has to be copied to and from OS/2's pipe buffers when&lt;br /&gt;
DosWrites and DosReads are done. What makes an anonymous pipe valuable is&lt;br /&gt;
that child processes inherit file handles. A parent process can create an&lt;br /&gt;
anonymous pipe and then create a child process, and the child process&lt;br /&gt;
inherits the anonymous pipe handles. The child process can then write to&lt;br /&gt;
the pipe's write handle, and the parent process can read the data via the&lt;br /&gt;
pipe's read handle. Once we add the DosDupHandle function, which allows&lt;br /&gt;
handles to be renumbered, and the standard file handles (STDIN, STDOUT, and&lt;br /&gt;
STDERR), we have the makings of a powerful capability.&lt;br /&gt;
     Let's go back to our NUMARITH and NUMADD programs. Suppose NUMARITH&lt;br /&gt;
wants to use NUMADD's services but that NUMARITH wants to process NUMADD's&lt;br /&gt;
results itself rather than having them appear in NUMARITH's output.&lt;br /&gt;
Furthermore, assume that NUMARITH doesn't want NUMADD to read its arguments&lt;br /&gt;
from NUMARITH's input file; NUMARITH wants to supply NUMADD's arguments&lt;br /&gt;
itself. NUMARITH can do this by following these steps:&lt;br /&gt;
&lt;br /&gt;
# Create two anonymous pipes.&lt;br /&gt;
# Preserve the item pointed to by the current STDIN and STDOUT handles (the item can be a file, a device, or a pipe) by using DosDupHandle to provide a duplicate handle. The handle numbers of the duplicates may be any number as long as it is not the number of STDIN, STDOUT, or STDERR. We know that this is the case because DosDupHandle assigns a handle number that is not in use, and the standard handle numbers are always in use.&lt;br /&gt;
# Close STDIN and STDOUT via DosClose. Whatever object is &amp;quot;on the other end&amp;quot; of the handle is undisturbed because the application still has the object open on another handle.&lt;br /&gt;
# Use DosDupHandle to make the STDIN handle a duplicate of one of the pipe's input handles, and use DosDupHandle to make the STDOUT handle a duplicate of the other pipe's output handle.&lt;br /&gt;
# Create the child process via DosExecPgm.&lt;br /&gt;
# Close the STDIN and STDOUT handles that point to the pipes, and use DosDupHandle and DosClose to effectively rename the objects originally described by STDIN and STDOUT back to those handles.&lt;br /&gt;
&lt;br /&gt;
     The result of this operation is shown in Figure 4-4. NUMADD's STDIN&lt;br /&gt;
and STDOUT handles are pointing to two anonymous pipes, and the parent&lt;br /&gt;
process is holding the other end of those pipes. The parent process used&lt;br /&gt;
DosDupHandle and DosClose to effectively &amp;quot;rename&amp;quot; the STDIN and STDOUT&lt;br /&gt;
handles temporarily so that the child process can inherit the pipe handles&lt;br /&gt;
rather than its parent's STDIN and STDOUT. At this point the parent,&lt;br /&gt;
NUMARITH, can write input values into the pipe connected to NUMADD's STDIN&lt;br /&gt;
and read NUMADD's output from the pipe connected to NUMADD's STDOUT.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT&lt;br /&gt;
 ÄÄÄÄÄÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
          ÀÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄÙ&lt;br /&gt;
          ÚÄÄÄÄÄÄ´          ³�ÄÄÄÄÄÄÄ¿&lt;br /&gt;
          ³      ÀÄÄÄÄÄÂÄÄÄÄÙ        ³&lt;br /&gt;
anonymous ³            ³ DosExecPgm  ³ anonymous&lt;br /&gt;
  pipe    ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿        ³   pipe&lt;br /&gt;
          ³      ³          ³        ³&lt;br /&gt;
          ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                 ³          ³&lt;br /&gt;
                 ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-4.  Invoking NUMADD via an anonymous pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     If you compare Figure 4-4 with Listing 4-2, Figure 4-2, and Figure&lt;br /&gt;
4-3, you see another key feature of this architecture: NUMADD has no&lt;br /&gt;
special code or design to allow it to communicate directly with NUMARITH.&lt;br /&gt;
NUMADD functions correctly whether working from the keyboard and screen,&lt;br /&gt;
from data files, or as a tool for another program. The architecture is&lt;br /&gt;
fully recursive: If NUMADD invokes a child process to help it with its&lt;br /&gt;
work, everything still functions correctly. Whatever mechanism NUMADD uses&lt;br /&gt;
to interact with its child/tool program is invisible to NUMARITH. Likewise,&lt;br /&gt;
if another program uses NUMARITH as a tool, that program is not affected by&lt;br /&gt;
whatever mechanism NUMARITH uses to do its work.&lt;br /&gt;
     This example contains one more important point. Earlier I said that a&lt;br /&gt;
process uses DosRead and DosWrite to do I/O over a pipe, yet our NUMADD&lt;br /&gt;
program uses fscanf and fprintf, two C language library routines. fscanf&lt;br /&gt;
and fprintf themselves call DosRead and DosWrite, and because a pipe handle&lt;br /&gt;
is indistinguishable from a file handle or a device handle for these&lt;br /&gt;
operations, not only does NUMADD work unchanged with pipes, but the library&lt;br /&gt;
subroutines that it calls work as well. This is another example of the&lt;br /&gt;
principle of encapsulation as expressed in OS/2,&lt;br /&gt;
4. And in UNIX, from which this aspect of the architecture was&lt;br /&gt;
adapted.&lt;br /&gt;
4 in which the differences&lt;br /&gt;
among pipes, files, and devices are hidden behind, or encapsulated in, a&lt;br /&gt;
standardized handle interface.&lt;br /&gt;
&lt;br /&gt;
====4.1.3  Details, Details====&lt;br /&gt;
While presenting the &amp;quot;big picture&amp;quot; of the OS/2 tasking and tool&lt;br /&gt;
architecture, I omitted various important details. This section discusses&lt;br /&gt;
them, in no particular order.&lt;br /&gt;
     STDERR (handle value 2) is an output handle on which error messages&lt;br /&gt;
are written. STDERR is necessary because STDOUT is a program's normal&lt;br /&gt;
output stream. For example, suppose a user types:&lt;br /&gt;
&lt;br /&gt;
     DIR  filename &amp;gt;logfile&lt;br /&gt;
&lt;br /&gt;
where the file filename does not exist. If STDERR did not exist as a&lt;br /&gt;
separate entity, no error message would appear, and logfile would&lt;br /&gt;
apparently be created. Later, when the user attempted to examine the&lt;br /&gt;
contents of the file, he or she would see the following message:&lt;br /&gt;
&lt;br /&gt;
     FILE NOT FOUND&lt;br /&gt;
&lt;br /&gt;
For this reason, STDERR generally points to the display screen, and&lt;br /&gt;
applications rarely redirect it.&lt;br /&gt;
     The special meanings of STDIN, STDOUT, and STDERR are not hard coded&lt;br /&gt;
into the OS/2 kernel; they are system conventions. All programs should&lt;br /&gt;
follow these conventions at all times to preserve the flexibility and&lt;br /&gt;
utility of OS/2's tool-based architecture. Even programs that don't do&lt;br /&gt;
handle I/O on the STD handles must still follow the architectural&lt;br /&gt;
conventions (see Chapter 14, Interactive Programs). However, the OS/2&lt;br /&gt;
kernel code takes no special action nor does it contain any special cases&lt;br /&gt;
in support of this convention. Various OS/2 system utilities, such as&lt;br /&gt;
CMD.EXE and the presentation manager, do contain code in support of this&lt;br /&gt;
convention.&lt;br /&gt;
     I mentioned that a child process inherits all its parent's file&lt;br /&gt;
handles unless the parent has explicitly marked a handle &amp;quot;no inherit.&amp;quot; The&lt;br /&gt;
use of STDIN, STDOUT, and STDERR in an inherited environment has been&lt;br /&gt;
discussed, but what of the other handles?&lt;br /&gt;
     Although a child process inherits all of its parent's file handles, it&lt;br /&gt;
is usually interested only in STDIN, STDOUT, and STDERR. What happens to&lt;br /&gt;
the other handles? Generally, nothing. Only handle values 0 (STDIN), 1&lt;br /&gt;
(STDOUT), and 2 (STDERR) have explicit meaning. All other file handles are&lt;br /&gt;
merely magic cookies that OS/2 returns for use in subsequent I/O calls.&lt;br /&gt;
OS/2 doesn't guarantee any particular range or sequence of handle values,&lt;br /&gt;
and applications should never use or rely on explicit handle values other&lt;br /&gt;
than the STD ones.&lt;br /&gt;
     Thus, for example, if a parent process has a file open on handle N and&lt;br /&gt;
the child process inherits that handle, little happens. The child process&lt;br /&gt;
won't get the value N back as a result of a DosOpen because the handle is&lt;br /&gt;
already in use. The child will never issue operations to handle N because&lt;br /&gt;
it didn't open any such handle and knows nothing of its existence. Two side&lt;br /&gt;
effects can result from inheriting &amp;quot;garbage&amp;quot; handles. One is that the&lt;br /&gt;
object to which the handle points cannot be closed until both the parent&lt;br /&gt;
and the child close their handles. Because the child knows nothing of the&lt;br /&gt;
handle, it won't close it. Therefore, a handle close issued by a parent&lt;br /&gt;
won't be effective until the child and all of that child's descendant&lt;br /&gt;
processes (which in turn inherited the handle) have terminated. If another&lt;br /&gt;
application needs the file or device, it is unavailable because a child&lt;br /&gt;
process is unwittingly holding it open.&lt;br /&gt;
     The other side effect is that each garbage handle consumes an entry in&lt;br /&gt;
the child's handle space. Although you can easily increase the default&lt;br /&gt;
maximum of 20 open handles, a child process that intends to open only 10&lt;br /&gt;
files wouldn't request such an increase. If a parent process allows the&lt;br /&gt;
child to inherit 12 open files, the child will run out of available open&lt;br /&gt;
file handles. Writing programs that always raise their file handle limit is&lt;br /&gt;
not good practice because the garbage handles are extra overhead and the&lt;br /&gt;
files-held-open problem remains. Instead, parent programs should minimize&lt;br /&gt;
the number of garbage handles they allow child processes to inherit.&lt;br /&gt;
     Each time a program opens a file, it should do so with the DosOpen&lt;br /&gt;
request with the &amp;quot;don't inherit&amp;quot; bit set if the file is of no specific&lt;br /&gt;
interest to any child programs. If the bit is not set at open time, it can&lt;br /&gt;
be set later via DosSetFHandState. The bit is per-handle, not per-file; so&lt;br /&gt;
if a process has two handles open on the same file, it can allow one but&lt;br /&gt;
not the other to be inherited. Don't omit this step simply because you&lt;br /&gt;
don't plan to run any child processes; unbeknownst to you, dynlink library&lt;br /&gt;
routines may run child programs on your behalf. Likewise, in dynlink&lt;br /&gt;
programs the &amp;quot;no inherit&amp;quot; bit should be set when file opens are issued&lt;br /&gt;
because the client program may create child processes.&lt;br /&gt;
     Finally, do not follow the standard UNIX practice of blindly closing&lt;br /&gt;
file handles 3 through 20 during program initialization. Dynlink subsystems&lt;br /&gt;
are called to initialize themselves for a new client before control is&lt;br /&gt;
passed to the application itself. If subsystems have opened files during&lt;br /&gt;
that initialization, a blanket close operation will close those files and&lt;br /&gt;
cause the dynlink package to fail. All programs use dynlink subsystems,&lt;br /&gt;
whether they realize it or not, because both the OS/2 interface package and&lt;br /&gt;
the presentation manager are such subsystems. Accidentally closing a&lt;br /&gt;
subsystem's file handles can cause bizarre and inexplicable problems. For&lt;br /&gt;
example, when the VIO subsystem is initialized, it opens a handle to the&lt;br /&gt;
screen device. A program that doesn't call VIO may believe that closing&lt;br /&gt;
this handle is safe, but it's not. If a handle write is done to STDOUT when&lt;br /&gt;
STDOUT points to the screen device, OS/2 calls VIO on behalf of the&lt;br /&gt;
application--with potentially disastrous effect.&lt;br /&gt;
     I discussed how a child process, inheriting its parent's STDIN and&lt;br /&gt;
STDOUT, extracts its input from the parent's input stream and intermingles&lt;br /&gt;
its output in the parent's output stream. What keeps the parent process&lt;br /&gt;
from rereading the input consumed by the child, and what keeps the parent&lt;br /&gt;
from overwriting the output data written by the child? The answer is in the&lt;br /&gt;
distinction between duplicated or inherited handles to a file and two&lt;br /&gt;
handles to the same file that are the result of two separate opens.&lt;br /&gt;
     Each time a file is opened, OS/2 allocates a handle to that process&lt;br /&gt;
and makes an entry in the process's handle table. This entry then points to&lt;br /&gt;
the System File Table (SFT) inside OS/2. The SFT contains the seek pointer&lt;br /&gt;
to the file--the spot in the file that is currently being read from or&lt;br /&gt;
written to. When a handle is inherited or duplicated, the new handle points&lt;br /&gt;
to the same SFT entry as the original. Thus, for example, the child's STDIN&lt;br /&gt;
handle shares the same seek pointer as the parent's STDIN handle. When our&lt;br /&gt;
example child program NUMADD read two lines from STDIN, it advanced the&lt;br /&gt;
seek pointer of its own STDIN and that of its parent's STDIN (and perhaps&lt;br /&gt;
that of its grandparent's STDIN and so forth). Likewise, when the child&lt;br /&gt;
writes to STDOUT, the seek pointer advances on STDOUT so that subsequent&lt;br /&gt;
writing by the parent appends to the child's output rather than overwriting&lt;br /&gt;
it.&lt;br /&gt;
     This mechanism has two important ramifications. First, in a situation&lt;br /&gt;
such as our NUMARITH and NUMADD example, the parent process must refrain&lt;br /&gt;
from I/O to the STD handles until the child process has completed so that&lt;br /&gt;
the input or output data doesn't intermingle. Second, the processes must be&lt;br /&gt;
careful in the way they buffer data to and from the STD handles.&lt;br /&gt;
     Most programs that read data from STDIN do so until they encounter an&lt;br /&gt;
EOF (End Of File). These programs can buffer STDIN as they wish. A program&lt;br /&gt;
such as NUMARITH, in which child processes read some but not all of its&lt;br /&gt;
STDIN data, cannot use buffering because the read-ahead data in the buffer&lt;br /&gt;
might be the data that the child process was to read. NUMARITH can't &amp;quot;put&lt;br /&gt;
the data back&amp;quot; by backing up the STDIN seek pointer because STDIN might be&lt;br /&gt;
pointing to a device (such as the keyboard) or to a pipe that cannot be&lt;br /&gt;
seeked. Likewise, because NUMADD was designed to read only two lines&lt;br /&gt;
of input, it also must read STDIN a character at a time to be sure it&lt;br /&gt;
doesn't &amp;quot;overshoot&amp;quot; its two lines.&lt;br /&gt;
     Programs must also be careful about buffering STDOUT. In general, they&lt;br /&gt;
can buffer STDOUT as they wish, but they must be sure to flush out any&lt;br /&gt;
buffered data before they execute any child processes that might write to&lt;br /&gt;
STDOUT.&lt;br /&gt;
     Finally, what if a parent process doesn't want a child process to&lt;br /&gt;
inherit STDIN, STDOUT, or STDERR? The parent process should not mark those&lt;br /&gt;
handles &amp;quot;no inherit&amp;quot; because then those handles will not be open when the&lt;br /&gt;
child process starts. The OS/2 kernel has no built-in recognition of the&lt;br /&gt;
STD file handles; so if the child process does a DosOpen and handle 1 is&lt;br /&gt;
unopened (because the process's parent set &amp;quot;no inherit&amp;quot; on handle 1), OS/2&lt;br /&gt;
might open the new file on handle 1. As a result, output that the child&lt;br /&gt;
process intends for STDOUT appears in the other file that unluckily was&lt;br /&gt;
assigned handle number 1.&lt;br /&gt;
     If for some reason a child process should not inherit a STD handle,&lt;br /&gt;
the parent should use the DosDupHandle/rename technique to cause that&lt;br /&gt;
handle to point to the NULL device. You do this by opening a handle on the&lt;br /&gt;
NULL device and then moving that handle to 0, 1, or 2 with DosDupHandle.&lt;br /&gt;
This technique guarantees that the child's STD handles will all be&lt;br /&gt;
open.&lt;br /&gt;
     The subject of STDIN, STDOUT, and handle inheritance comes up again in&lt;br /&gt;
Chapter 14, Interactive Programs.&lt;br /&gt;
&lt;br /&gt;
===4.2  PIDs and Command Subtrees===&lt;br /&gt;
&lt;br /&gt;
The PID (process identification) is a unique code that OS/2 assigns to each&lt;br /&gt;
process when it is created. The number is a magic cookie. Its value has no&lt;br /&gt;
significance to any process; it's simply a name for a process. The PID may&lt;br /&gt;
be any value except 0. A single PID value is never assigned to two&lt;br /&gt;
processes at the same time. PID values are reused but not &amp;quot;rapidly.&amp;quot; You&lt;br /&gt;
can safely remember a child's PID and then later attempt to affect that&lt;br /&gt;
child by using the PID in an OS/2 call. Even if the child process has died&lt;br /&gt;
unexpectedly, approximately 65,000 processes would have to be created&lt;br /&gt;
before the PID value might be reassigned; even a very active system takes&lt;br /&gt;
at least a day to create, execute, and terminate that many processes.&lt;br /&gt;
     I've discussed at some length the utility of an architecture in which&lt;br /&gt;
child programs can create children and those children can create&lt;br /&gt;
grandchildren and so on. I've also emphasized that the parent need not know&lt;br /&gt;
the architecture of a child process--whether the child process creates&lt;br /&gt;
children and grandchildren of its own. The parent need not know and indeed&lt;br /&gt;
should not know because such information may make the parent dependent on a&lt;br /&gt;
particular implementation of a child or grandchild program, an&lt;br /&gt;
implementation that might change. Given that a parent process starting up a&lt;br /&gt;
child process can't tell if that child creates its own descendants, how can&lt;br /&gt;
the parent process ask the system if the work that the child was to do has&lt;br /&gt;
been completed? The system could tell the parent whether the child process&lt;br /&gt;
is still alive, but this is insufficient. The child process may have farmed&lt;br /&gt;
out all its work to one or more grandchildren and then terminated itself&lt;br /&gt;
before the actual work was started. Furthermore, the parent process may&lt;br /&gt;
want to change the priority of the process(es) that it has created or even&lt;br /&gt;
terminate them because an error was detected or because the user typed&lt;br /&gt;
Ctrl-C.&lt;br /&gt;
     All these needs are met with a concept called the command subtree.&lt;br /&gt;
When a child process is created, its parent is told the child's PID. The&lt;br /&gt;
PID is the name of the single child process, and when taken as a command&lt;br /&gt;
subtree ID, this PID is the name of the entire tree of descendant processes&lt;br /&gt;
of which the child is the root. In other words, when used as a command&lt;br /&gt;
subtree ID, the PID refers not only to the child process but also to any of&lt;br /&gt;
its children and to any children that they may have and so on. A single&lt;br /&gt;
command subtree can conceivably contain dozens of processes (see Figure&lt;br /&gt;
4-5 and Figure 4-6).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                ³  PARENT   ³&lt;br /&gt;
                ÀÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
                ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                ³     A     ³&lt;br /&gt;
                ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
      ÚÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿                   ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
³     B     ³                   ³     C     ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ                   ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     D     ³     ÚÄÄÄÄÄÁÄÄÄÄÄ¿               ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ³     F     ³               ³     G     ³&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÀÄÄÄÄÄÄÄÄÄÄÄÙ               ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
³     E     ³                              ÚÄÄÄÄÄÙ     ÀÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÙ                        ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     H     ³     ³     I     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     J     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-5.  Command subtree. A is the root of (one of) the parent's&lt;br /&gt;
command subtrees. B and C are the root of two of A's subtrees and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                ³  PARENT   ³&lt;br /&gt;
                ÀÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
                ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                ³°°°°°A°°°°°³&lt;br /&gt;
                ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
      ÚÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿                   ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
³     B     ³                   ³     C     ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ                   ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     D     ³     ÚÄÄÄÄÄÁÄÄÄÄÄ¿               ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ³     F     ³               ³     G     ³&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÀÄÄÄÄÄÄÄÄÄÄÄÙ               ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
³°°°°°E°°°°°³                              ÚÄÄÄÄÄÙ     ÀÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÙ                        ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³°°°°°H°°°°°³     ³     I     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     J     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-6.   Command subtree. The shaded processes have died, but the&lt;br /&gt;
subtrees remain. PARENT can still use subtree A to affect all remaining&lt;br /&gt;
subprocesses. Likewise, an operation by C on subtree G will affect&lt;br /&gt;
process J.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Some OS/2 functions, such as DosCWait and DosKillProcess, can take&lt;br /&gt;
either PID or command subtree values, depending on the subfunction&lt;br /&gt;
requested. When the PID form is used, only the named process is affected.&lt;br /&gt;
When the command subtree form is used, the named process and all its&lt;br /&gt;
descendants are affected. This is true even if the child process no longer&lt;br /&gt;
exists or if the family tree of processes contains holes as a result of&lt;br /&gt;
process terminations.&lt;br /&gt;
     No statute of limitations applies to the use of the command subtree&lt;br /&gt;
form. That is, even if child process X died a long time ago, OS/2 still&lt;br /&gt;
allows references to the command subtree X. Consequently, OS/2 places one&lt;br /&gt;
simple restriction on the use of command subtrees so that it isn't forced&lt;br /&gt;
to keep around a complete process history forever: Only the direct parent&lt;br /&gt;
of process X can reference the command subtree X. In other words, X's&lt;br /&gt;
grandparent process can't learn X's PID from its parent and then issue&lt;br /&gt;
command subtree forms of commands; only X's direct parent can. This&lt;br /&gt;
puts an upper limit on the amount and duration of command subtree&lt;br /&gt;
information that OS/2 must retain; when a process terminates, information&lt;br /&gt;
pertaining to its command subtrees can be discarded. The command subtree&lt;br /&gt;
concept is recursive. OS/2 discards information about the terminated&lt;br /&gt;
process's own command subtrees, but if any of its descendant processes&lt;br /&gt;
still exist, the command subtree information pertaining to their child&lt;br /&gt;
processes is retained. And those surviving descendants are still part of&lt;br /&gt;
the command subtree belonging to the terminated process's parent&lt;br /&gt;
process.&lt;br /&gt;
5. Assuming that the parent process itself still exists. In any&lt;br /&gt;
case, all processes are part of a nested set of command subtrees&lt;br /&gt;
belonging to all its surviving ancestor processes.&lt;br /&gt;
5&lt;br /&gt;
&lt;br /&gt;
===4.3  DosExecPgm===&lt;br /&gt;
&lt;br /&gt;
To execute a child process, you use the DosExecPgm call. The form of the&lt;br /&gt;
call is shown in Listing 4-3.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extern unsigned far pascal DOSEXECPGM (&lt;br /&gt;
          char far                 *OBJNAMEBUF,&lt;br /&gt;
          unsigned                 OBJNAMEBUFL,&lt;br /&gt;
          unsigned                 EXECTYPE,&lt;br /&gt;
          char far                 *ARGSTRING,&lt;br /&gt;
          char far                 *ENVSTRING,&lt;br /&gt;
          struct ResultCodes far   *CODEPID,&lt;br /&gt;
          char far                 *PGMNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Listing 4-3.  DosExecPgm call.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The obj namebuf arguments provide an area where OS/2 can return a&lt;br /&gt;
character string if the DosExecPgm function fails. In MS-DOS version 2.0&lt;br /&gt;
and later, the EXEC function was quite simple: It loaded a file into&lt;br /&gt;
memory. Little could go wrong: file not found, file bad format,&lt;br /&gt;
insufficient memory, to name some possibilities. A simple error code&lt;br /&gt;
sufficed to diagnose problems. OS/2's dynamic linking facility is much more&lt;br /&gt;
complicated. The earliest prototype versions of OS/2 were missing these&lt;br /&gt;
obj namebuf arguments, and engineers were quickly frustrated by the error&lt;br /&gt;
code &amp;quot;dynlink library load failed.&amp;quot; &amp;quot;Which library was it? The application&lt;br /&gt;
references seven of them! But all seven of those libraries are alive and&lt;br /&gt;
well. Perhaps it was a library that one of those libraries referenced. But&lt;br /&gt;
which libraries do they reference? Gee, I dunno...&amp;quot; For this reason, the&lt;br /&gt;
object name arguments were added. The buffer is a place where OS/2 returns&lt;br /&gt;
the name of the missing or defective library or other load object, and the&lt;br /&gt;
length argument tells OS/2 the maximum size of the buffer area. Strings&lt;br /&gt;
that will not fit in the area are truncated.&lt;br /&gt;
     The exectype word describes the form of the DosExecPgm. The values are&lt;br /&gt;
as follows.&lt;br /&gt;
&lt;br /&gt;
     0:  Execute the child program synchronously. The thread issuing the&lt;br /&gt;
         DosExecPgm will not execute further until the child process has&lt;br /&gt;
         finished executing. The thread returns from DosExecPgm when the&lt;br /&gt;
         child process itself terminates, not when the command subtree has&lt;br /&gt;
         terminated. This form of DosExecPgm is provided for ease in&lt;br /&gt;
         converting MS-DOS version 3.x programs to OS/2. It is considered&lt;br /&gt;
         obsolete and should generally be avoided. Its use may interfere&lt;br /&gt;
         with proper Ctrl-C and Ctrl-Break handling (see Chapter 14,&lt;br /&gt;
         Interactive Programs).&lt;br /&gt;
&lt;br /&gt;
     1:  Execute the program asynchronously. The child process begins&lt;br /&gt;
         executing as soon as the scheduler allows; the calling thread&lt;br /&gt;
         returns from the DosExecPgm call immediately. You cannot assume&lt;br /&gt;
         that the child process has received CPU time before the parent&lt;br /&gt;
         thread returns from the DosExecPgm call; neither can you assume&lt;br /&gt;
         that the child process has not received such CPU service. This&lt;br /&gt;
         form instructs OS/2 not to bother remembering the child's&lt;br /&gt;
         termination code for a future DosCWait call. It is used when the&lt;br /&gt;
         parent process doesn't care what the result code of the child may&lt;br /&gt;
         be or when or if it completes, and it frees the parent from the &lt;br /&gt;
         necessity of issuing DosCWait calls. Programs executing other&lt;br /&gt;
         programs as tools would rarely use this form. This form might be&lt;br /&gt;
         used by a system utility, for example, whose job is to fire off&lt;br /&gt;
         certain programs once an hour but not to take any action or notice&lt;br /&gt;
         should any of those programs fail. Note that, unlike the detach&lt;br /&gt;
         form described below, the created process is still recorded as a&lt;br /&gt;
         child of the executing parent. The parent can issue a DosCWait&lt;br /&gt;
         call to determine whether the child subtree is still executing,&lt;br /&gt;
         although naturally there is no return code when the child process&lt;br /&gt;
         does terminate.&lt;br /&gt;
&lt;br /&gt;
     2:  This form is similar to form 1 in that it executes the child&lt;br /&gt;
         process asynchronously, but it instructs OS/2 to retain the child&lt;br /&gt;
         process's exit code for future examination by DosCWait. Thus, a&lt;br /&gt;
         program can determine the success or failure of a child process.&lt;br /&gt;
         The parent process should issue an appropriate DosCWait &amp;quot;pretty&lt;br /&gt;
         soon&amp;quot; because OS/2 version 1.0 maintains about 2600 bytes of data&lt;br /&gt;
         structures for a dead process whose parent is expected to DosCWait&lt;br /&gt;
         but hasn't yet done so. To have one of these structures lying&lt;br /&gt;
         around for a few minutes is no great problem, but programs need to&lt;br /&gt;
         issue DosCWaits in a timely fashion to keep from clogging the&lt;br /&gt;
         system with the carcasses of processes that finished hours&lt;br /&gt;
         ago.&lt;br /&gt;
            OS/2 takes care of all the possible timing considerations, so&lt;br /&gt;
         it's OK to issue the DosCWait before or after the child process&lt;br /&gt;
         has completed. Although a parent process can influence the&lt;br /&gt;
         relative assignment of CPU time between the child and parent&lt;br /&gt;
         processes by setting its own and its child's relative priorities,&lt;br /&gt;
         there is no reliable way of determining which process will run&lt;br /&gt;
         when. Write your program in such a way that it doesn't matter if&lt;br /&gt;
         the child completes before or after the DosCWait, or use some form&lt;br /&gt;
         of IPC to synchronize the execution of parent and child processes.&lt;br /&gt;
         See 4.4 DosCWait for more details.&lt;br /&gt;
&lt;br /&gt;
     3:  This form is used by the system debugger, CodeView. The system&lt;br /&gt;
         architecture does not allow one process to latch onto another&lt;br /&gt;
         arbitrary process and start examining and perhaps modifying the&lt;br /&gt;
         target process's execution. Such a facility would result in a&lt;br /&gt;
         massive side effect and is contrary to the tenet of encapsulation&lt;br /&gt;
         in the design religion. Furthermore, such a facility would prevent&lt;br /&gt;
         OS/2 from ever providing an environment secure against malicious&lt;br /&gt;
         programs. OS/2 solves this problem with the DosPtrace function,&lt;br /&gt;
         which peeks, pokes, and controls a child process. This function is&lt;br /&gt;
         allowed to affect only processes that were executed with this&lt;br /&gt;
         special mode value of 3.&lt;br /&gt;
&lt;br /&gt;
     4:  This form executes the child process asynchronously but also&lt;br /&gt;
         detaches it from the process issuing the DosExecPgm call. A&lt;br /&gt;
         detached process does not inherit the parent process's screen&lt;br /&gt;
         group; instead, it executes in a special invalid screen group. Any&lt;br /&gt;
         attempt to do screen, keyboard, or mouse I/O from within this&lt;br /&gt;
         screen group returns an error code. The system does not consider a&lt;br /&gt;
         detached process a child of the parent process; the new process&lt;br /&gt;
         has no connection with the creating process. In other words, it's&lt;br /&gt;
         parentless. This form of DosExecPgm is used to create daemon&lt;br /&gt;
         programs that execute without direct interaction with the user.&lt;br /&gt;
&lt;br /&gt;
     The EnvString argument points to a list of ASCII text strings that&lt;br /&gt;
contain environment values. OS/2 supports a separate environment block for&lt;br /&gt;
each process. A process typically inherits its parent's environment&lt;br /&gt;
strings. In this case, the EnvPointer argument should be NULL, which tells&lt;br /&gt;
OS/2 to supply the child process with a copy of the parent's environment&lt;br /&gt;
strings (see Listing 4-4).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PATH=C:\DOS;C:\EDITORS;C:\TOOLS;C:\XTOOLS;C:\BIN;C:\UBNET&lt;br /&gt;
INCLUDE=\include&lt;br /&gt;
TERM=h19&lt;br /&gt;
INIT=c:\tmp&lt;br /&gt;
HOME=c:\tmp&lt;br /&gt;
USER=c:\tmp&lt;br /&gt;
TEMP=c:\tmp&lt;br /&gt;
TERM=ibmpc&lt;br /&gt;
LIB=c:\lib&lt;br /&gt;
PROMPT=($p)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Listing 4-4.  A typical environment string set.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The environment strings are normally used to customize a particular&lt;br /&gt;
execution environment. For example, suppose a user creates two screen&lt;br /&gt;
groups, each running a copy of CMD.EXE. Each CMD.EXE is a direct child of&lt;br /&gt;
the presentation manager, which manages and creates screen groups, and each&lt;br /&gt;
is also the ancestor of all processes that will run in its particular&lt;br /&gt;
screen group. If the user is running utility programs that use the&lt;br /&gt;
environment string &amp;quot;TEMP=&amp;lt;dirname&amp;gt;&amp;quot; to specify a directory to hold&lt;br /&gt;
temporary files, he or she may want to specify different TEMP= values for&lt;br /&gt;
each copy of CMD.EXE. As a result, the utilities that use TEMP= will access&lt;br /&gt;
the proper directory, depending on which screen group they are run in,&lt;br /&gt;
because they will have inherited the proper TEMP= environment string from&lt;br /&gt;
their CMD.EXE ancestor. See Chapter 10, Environment Strings, for a complete&lt;br /&gt;
discussion.&lt;br /&gt;
     Because of the inheritable nature of environment strings, parent&lt;br /&gt;
processes that edit the environment list should remove or edit only those&lt;br /&gt;
strings with which they are involved; any unrecognized strings should be&lt;br /&gt;
preserved.&lt;br /&gt;
&lt;br /&gt;
===4.4  DosCWait===&lt;br /&gt;
&lt;br /&gt;
When a process executes a child process, it usually wants to know when that&lt;br /&gt;
child process has completed and whether the process succeeded or failed.&lt;br /&gt;
DosCWait, the OS/2 companion function to DosExecPgm, returns such&lt;br /&gt;
information. Before we discuss DosCWait in detail, two observations are in&lt;br /&gt;
order. First, although each DosExecPgm call starts only a single process,&lt;br /&gt;
it's possible--and not uncommon--for that child process to create its own&lt;br /&gt;
children and perhaps they their own and so on. A program should not assume&lt;br /&gt;
that its child process won't create subchildren; instead, programs should&lt;br /&gt;
use the command-subtree forms of DosCWait. One return code from the direct&lt;br /&gt;
child process (that is, the root of the command subtree) is sufficient&lt;br /&gt;
because if that direct child process invokes other processes to do work for&lt;br /&gt;
it the direct child is responsible for monitoring their success via&lt;br /&gt;
DosCWait. In other words, if a child process farms out some of its work to&lt;br /&gt;
a grandchild process and that grandchild process terminates in error, then&lt;br /&gt;
the child process should also terminate with an error return.&lt;br /&gt;
     Second, although we discuss the process's child, in fact processes can&lt;br /&gt;
have multiple child processes and therefore multiple command subtrees at&lt;br /&gt;
any time. The parent process may have interconnected the child processes&lt;br /&gt;
via anonymous pipes, or they may be independent of one another. Issuing&lt;br /&gt;
separate DosCWaits for each process or subtree is unnecessary. The form of&lt;br /&gt;
the DosCWait call is shown in Listing 4-5.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
extern unsigned far pascal DOSCWAIT (&lt;br /&gt;
     unsigned                 ACTIONCODE,&lt;br /&gt;
     unsigned                 WAITOPTION,&lt;br /&gt;
     struct ResultCodes far   *RESULTWORD,&lt;br /&gt;
     unsigned far             *PIDRETURN,&lt;br /&gt;
     unsigned                 PID);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-5.  DosCWait function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Three of the arguments affect the scope of the command: ActionCode,&lt;br /&gt;
WaitOption, and PID. It's easiest to show how these interact by arranging&lt;br /&gt;
their possible values into tables.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Command Subtrees&lt;br /&gt;
&lt;br /&gt;
     These forms of DosCWait operate on the entire command subtree, which&lt;br /&gt;
     may, of course, consist of only one child process. We recommend these&lt;br /&gt;
     forms because they will continue to work correctly if the child&lt;br /&gt;
     process is changed to use more or fewer child processes of its own.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId   Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     1            0            n           Wait until the command subtree&lt;br /&gt;
                                           has completed and then return&lt;br /&gt;
                                           the direct child's termination&lt;br /&gt;
                                           code.&lt;br /&gt;
&lt;br /&gt;
     1            1            n           If the command subtree has&lt;br /&gt;
                                           completed, return the direct&lt;br /&gt;
                                           child's termination code.&lt;br /&gt;
                                           Otherwise, return the&lt;br /&gt;
                                           ERROR_CHILD_NOT_COMPLETE&lt;br /&gt;
                                           error code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Individual Processes&lt;br /&gt;
&lt;br /&gt;
     These forms of DosCWait are used to monitor individual child&lt;br /&gt;
     processes. The processes must be direct children; grandchild or&lt;br /&gt;
     unrelated processes cannot be DosCWaited. Use these forms only when&lt;br /&gt;
     the child process is part of the same application or software package&lt;br /&gt;
     as the parent process; the programmer needs to be certain that she or&lt;br /&gt;
     he can safely ignore the possibility that grandchild processes might&lt;br /&gt;
     still be running after the direct child has terminated.&lt;br /&gt;
1. It is in itself not an error to collect a child process's&lt;br /&gt;
termination code via DosCWait while that child still&lt;br /&gt;
has living descendant processes. However, such a case generally&lt;br /&gt;
means that the child's work, whatever that was, is not yet complete.&lt;br /&gt;
1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId   Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     0            0            0           DosCWait returns as soon as a&lt;br /&gt;
                                           direct child process terminates.&lt;br /&gt;
                                           If a child process had already&lt;br /&gt;
                                           terminated at the time of this&lt;br /&gt;
                                           call, it will return&lt;br /&gt;
                                           immediately.&lt;br /&gt;
&lt;br /&gt;
     0            0            N           DosCWait returns as soon as the&lt;br /&gt;
                                           direct child process N&lt;br /&gt;
                                           terminates. If it had already&lt;br /&gt;
                                           terminated at the time of the&lt;br /&gt;
                                           call, DosCWait returns&lt;br /&gt;
                                           immediately.&lt;br /&gt;
&lt;br /&gt;
     0            1            0           DosCWait checks for a terminated&lt;br /&gt;
                                           direct child process. If one is&lt;br /&gt;
                                           found, its status is returned.&lt;br /&gt;
                                           If none is found, an error code&lt;br /&gt;
                                           is returned.&lt;br /&gt;
&lt;br /&gt;
     0            1            N           DosCWait checks the status of&lt;br /&gt;
                                           the direct child process N. If&lt;br /&gt;
                                           it is terminated, its status is&lt;br /&gt;
                                           returned. If it is still&lt;br /&gt;
                                           running, an error code is&lt;br /&gt;
                                           returned.&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Not Recommended&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId    Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     1            0            0            DosCWait waits until a direct&lt;br /&gt;
                                            child has terminated and then&lt;br /&gt;
                                            waits until all of that child's&lt;br /&gt;
                                            descendants have terminated. It&lt;br /&gt;
                                            then returns the direct child's&lt;br /&gt;
                                            exit code. This form does not&lt;br /&gt;
                                            wait until the first command&lt;br /&gt;
                                            subtree has terminated; it&lt;br /&gt;
                                            selects a command subtree based&lt;br /&gt;
                                            on the first direct child that&lt;br /&gt;
                                            terminates, and then it waits&lt;br /&gt;
                                            as long as necessary for the&lt;br /&gt;
                                            remainder of that command&lt;br /&gt;
                                            subtree, even if other command&lt;br /&gt;
                                            subtrees meanwhile complete.&lt;br /&gt;
&lt;br /&gt;
     1            1            0            This form returns&lt;br /&gt;
                                            ERROR_CHILD_NOT_COMPLETE if any&lt;br /&gt;
                                            process in any of the caller's&lt;br /&gt;
                                            subtrees is still executing. If&lt;br /&gt;
                                            all subtrees have terminated,&lt;br /&gt;
                                            this form returns with a direct&lt;br /&gt;
                                            child's exit code. If no direct&lt;br /&gt;
                                            child processes have unwaited&lt;br /&gt;
                                            exit codes, the code&lt;br /&gt;
                                            ERROR_WAIT_NO_CHILDREN is&lt;br /&gt;
                                            returned.&lt;br /&gt;
&lt;br /&gt;
===4.5  Control of Child Tasks and Command Subtrees===&lt;br /&gt;
&lt;br /&gt;
A parent process has only limited control over its child processes because&lt;br /&gt;
the system is designed to minimize the side effects, or cross talk, between&lt;br /&gt;
processes. Specifically, a parent process can affect its command subtrees&lt;br /&gt;
in two ways: It can change their CPU priority, and it can terminate (kill)&lt;br /&gt;
them. Once again, the command subtree is the recommended form for both&lt;br /&gt;
commands because that form is insensitive to the operational details of the&lt;br /&gt;
child process.&lt;br /&gt;
&lt;br /&gt;
====4.5.1  DosKillProcess====&lt;br /&gt;
A parent process may initiate a child process or command subtree and then&lt;br /&gt;
decide to terminate that activity before the process completes normally.&lt;br /&gt;
Often this comes about because of a direct user command or because the user&lt;br /&gt;
typed Ctrl-Break. See Chapter 14, Interactive Programs, for special&lt;br /&gt;
techniques concerning Ctrl-Break and Ctrl-C.&lt;br /&gt;
     DosKillProcess flags each process in the command subtree (or the&lt;br /&gt;
direct child process if that form is used) for termination. A process&lt;br /&gt;
flagged for termination normally terminates as soon as all its threads&lt;br /&gt;
leave the system (that is, as soon as all its threads return from all&lt;br /&gt;
system calls). The system aborts calls that might block for more than a&lt;br /&gt;
second or two, such as those that read a keyboard character, so that the&lt;br /&gt;
process can terminate quickly. A process can intercept SIGKILL to delay&lt;br /&gt;
termination longer, even indefinitely. Delaying termination inordinately&lt;br /&gt;
via SetSignalHandler/SIGKILL is very bad practice and is considered a bug&lt;br /&gt;
rather than a feature.&lt;br /&gt;
&lt;br /&gt;
====4.5.2  DosSetPrty====&lt;br /&gt;
A child process inherits its parent's process priority when the DosExecPgm&lt;br /&gt;
call is issued. After the DosExecPgm call, the parent can still change the&lt;br /&gt;
process priority of the command subtree or of only the direct child&lt;br /&gt;
process. The command subtree form is recommended; if the child process's&lt;br /&gt;
work deserves priority N, then any child processes that it executes to help&lt;br /&gt;
in that work should also run at priority N.&lt;br /&gt;
&lt;br /&gt;
==5  Threads and Scheduler/Priorities==&lt;br /&gt;
&lt;br /&gt;
===5.1  Threads===&lt;br /&gt;
&lt;br /&gt;
Computers consist of a CPU (central processing unit) and RAM (random access&lt;br /&gt;
memory). A computer program consists of a sequence of instructions that are&lt;br /&gt;
placed, for the most part, one after the other in RAM. The CPU reads each&lt;br /&gt;
instruction in sequence and executes it. The passage of the CPU through the&lt;br /&gt;
instruction sequence is called a thread of execution. All versions of MS-&lt;br /&gt;
DOS executed programs, so they necessarily supported a thread of execution.&lt;br /&gt;
OS/2 is unique, however, in that it supports multiple threads of execution&lt;br /&gt;
within a single process. In other words, a program can execute in two or&lt;br /&gt;
more spots in its code at the same time.&lt;br /&gt;
     Obviously, a multitasking system needs to support multiple threads in&lt;br /&gt;
a systemwide sense. Each process necessarily must have a thread; so if&lt;br /&gt;
there are ten processes in the system, there must be ten threads. Such an&lt;br /&gt;
existence of multiple threads in the system is invisible to the programmer&lt;br /&gt;
because each program executes with only one thread. OS/2 is different from&lt;br /&gt;
this because it allows an individual program to execute with multiple&lt;br /&gt;
threads if it desires.&lt;br /&gt;
     Because threads are elements of processes and because the process is&lt;br /&gt;
the unit of resource ownership, all threads that belong to the same process&lt;br /&gt;
share that process's resources. Thus, if one thread opens a file on file&lt;br /&gt;
handle X, all threads in that process can issue DosReads or DosWrites to&lt;br /&gt;
that handle. If one thread allocates a memory segment, all threads in that&lt;br /&gt;
process can access that memory segment. Threads are analogous to the&lt;br /&gt;
employees of a company. A company may consist of a single employee, or it&lt;br /&gt;
may consist of two or more employees that divide the work among them. Each&lt;br /&gt;
employee has access to the company's resources--its office space and&lt;br /&gt;
equipment. The employees themselves, however, must coordinate their work so&lt;br /&gt;
that they cooperate and don't conflict. As far as the outside world is&lt;br /&gt;
concerned, each employee speaks for the company. Employees can terminate&lt;br /&gt;
and/or more can be hired without affecting how the company is seen from&lt;br /&gt;
outside. The only requirement is that the company have at least one&lt;br /&gt;
employee. When the last employee (thread) dies, the company (process) also&lt;br /&gt;
dies.&lt;br /&gt;
     Although the process is the unit of resource ownership, each thread&lt;br /&gt;
does &amp;quot;own&amp;quot; a small amount of private information. Specifically, each thread&lt;br /&gt;
has its own copy of the CPU's register contents. This is an obvious&lt;br /&gt;
requirement if each thread is to be able to execute different instruction&lt;br /&gt;
sequences. Furthermore, each thread has its own copy of the floating point&lt;br /&gt;
registers. OS/2 creates the process's first thread when the program begins&lt;br /&gt;
execution. Any additional threads are created by means of the&lt;br /&gt;
DosCreateThread call. Any thread can create another thread. All threads in&lt;br /&gt;
a process are considered siblings; there are no parent-child relationships.&lt;br /&gt;
The initial thread, thread 1, has some special characteristics and is&lt;br /&gt;
discussed below.&lt;br /&gt;
&lt;br /&gt;
====5.1.1  Thread Stacks====&lt;br /&gt;
Each thread has its own stack area, pointed to by that thread's SS and SP&lt;br /&gt;
values. Thread 1 is the process's primal thread. OS/2 allocates it stack&lt;br /&gt;
area in response to specifications in the .EXE file. If additional threads&lt;br /&gt;
are created via the DosCreateThread call, the caller specifies a stack area&lt;br /&gt;
for the new thread. Because the memory in which each thread's stack resides&lt;br /&gt;
is owned by the process, any thread can modify this memory; the programmer&lt;br /&gt;
must make sure that this does not happen. The size of the segment in which&lt;br /&gt;
the stack resides is explicitly specified; the size of a thread's stack is&lt;br /&gt;
not. The programmer can place a thread's stack in its own segment or in a&lt;br /&gt;
segment with other data values, including other thread stacks. In any case,&lt;br /&gt;
the programmer must ensure sufficient room for each thread's needs. Each&lt;br /&gt;
thread's stack must have at least 2 KB free in addition to the thread's&lt;br /&gt;
other needs at all times. This extra space is set aside for the needs of&lt;br /&gt;
dynamic link routines, some of which consume considerable stack space. All&lt;br /&gt;
threads must maintain this stack space reserve even if they are not used to&lt;br /&gt;
call dynamic link routines. Because of a bug in many 80286 processors,&lt;br /&gt;
stack segments must be preallocated to their full size. You cannot overrun&lt;br /&gt;
a stack segment and then assume that OS/2 will grow the segment;&lt;br /&gt;
overrunning a stack segment will cause a stack fault, and the process will&lt;br /&gt;
be terminated.&lt;br /&gt;
&lt;br /&gt;
====5.1.2  Thread Uses====&lt;br /&gt;
Threads have a great number of uses. This section describes four of them.&lt;br /&gt;
These examples are intended to be inspirations to the programmer; there are&lt;br /&gt;
many other uses for threads.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.1  Foreground and Background Work&lt;br /&gt;
Threads provide a form of multitasking within a single program; therefore,&lt;br /&gt;
one of their most obvious uses is to provide simultaneous foreground and&lt;br /&gt;
background&lt;br /&gt;
1. Here I mean foreground and background in the sense of directly&lt;br /&gt;
interacting with the user, not as in foreground and background&lt;br /&gt;
screen groups.&lt;br /&gt;
1 processing for a program. For example, a spreadsheet program&lt;br /&gt;
might use one thread to display menus and to read user input. A second&lt;br /&gt;
thread could execute user commands, update the spreadsheet, and so on.&lt;br /&gt;
     This arrangement generally increases the perceived speed of the&lt;br /&gt;
program by allowing the program to prompt for another command before the&lt;br /&gt;
previous command is complete. For example, if the user changes a cell in a&lt;br /&gt;
spreadsheet and then calls for recalculation, the &amp;quot;execute&amp;quot; thread can&lt;br /&gt;
recalculate while the &amp;quot;command&amp;quot; thread allows the user to move the cursor,&lt;br /&gt;
select new menus, and so forth. The spreadsheet should use RAM semaphores&lt;br /&gt;
to protect its structures so that one thread can't change a structure while&lt;br /&gt;
it is being manipulated by another thread. As far as the user can tell, he&lt;br /&gt;
or she is able to overlap commands without restriction. In actuality, the&lt;br /&gt;
previous command is usually complete before the user can finish entering&lt;br /&gt;
the new command. Occasionally, however, the new command is delayed until&lt;br /&gt;
the first has completed execution. This happens, for example, when the user&lt;br /&gt;
of a spreadsheet deletes a row right after saving the spreadsheet to disk.&lt;br /&gt;
Of course, the performance in this worst case situation is no worse than a&lt;br /&gt;
standard single-thread design is for all cases.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.2  Asynchronous Processing&lt;br /&gt;
Another common use of threads is to provide asynchronous elements in a&lt;br /&gt;
program's design. For example, as a protection against power failure, you&lt;br /&gt;
can design an editor so that it writes its RAM buffer to disk once every&lt;br /&gt;
minute. Threads make it unnecessary to scatter time checks throughout the&lt;br /&gt;
program or to sit in polling loops for input so that a time event isn't&lt;br /&gt;
missed while blocked on a read call. You can create a thread whose sole job&lt;br /&gt;
is periodic backup. The thread can call DosSleep to sleep for 60 seconds,&lt;br /&gt;
write the buffer, and then go back to sleep for another 60 seconds.&lt;br /&gt;
     The asynchronous event doesn't have to be time related. For example,&lt;br /&gt;
in a program that communicates over an asynchronous serial port, you can&lt;br /&gt;
dedicate a thread to wait for the modem carrier to come on or to wait for a&lt;br /&gt;
protocol time out. The main thread can continue to interact with the user.&lt;br /&gt;
     Programs that provide services to other programs via IPC can use&lt;br /&gt;
threads to simultaneously respond to multiple requests. For example, one&lt;br /&gt;
thread can watch the incoming work queue while one or more additional&lt;br /&gt;
threads perform the work.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.3  Speed Execution&lt;br /&gt;
You can use threads to speed the execution of single processes by&lt;br /&gt;
overlapping I/O and computation. A single-threaded process can perform&lt;br /&gt;
computations or call OS/2 for disk reads and writes, but not both at the&lt;br /&gt;
same time. A multithreaded process, on the other hand, can compute one&lt;br /&gt;
batch of data while reading the next batch from a device.&lt;br /&gt;
     Eventually, PCs containing multiple 80386 processors will become&lt;br /&gt;
available. An application that uses multiple threads may execute faster by&lt;br /&gt;
using more than one CPU simultaneously.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.4  Organizing Programs&lt;br /&gt;
Finally, you can use threads to organize and simplify the structure of a&lt;br /&gt;
program. For example, in a program for a turnkey security/alarm system, you&lt;br /&gt;
can assign a separate thread for each activity. One thread can watch the&lt;br /&gt;
status of the intrusion switches; a second can send commands to control the&lt;br /&gt;
lights; a third can run the telephone dialer; and a fourth can interface&lt;br /&gt;
with each control panel.&lt;br /&gt;
     This structure simplifies software design. The programmer needn't&lt;br /&gt;
worry that an intrusion switch is triggering unnoticed while the CPU is&lt;br /&gt;
executing the code that waits on the second key of a two-key command.&lt;br /&gt;
Likewise, the programmer doesn't have to worry about talking to two command&lt;br /&gt;
consoles at the same time; because each has its own thread and local&lt;br /&gt;
(stack) variables, multiple consoles can be used simultaneously without&lt;br /&gt;
conflict.&lt;br /&gt;
     Of course, you can write such a program without multiple threads; a&lt;br /&gt;
rat's nest of event flags and polling loops would do the job. Much better&lt;br /&gt;
would be a family of co-routines. But best, and simplest of all, is a&lt;br /&gt;
multithreaded design.&lt;br /&gt;
&lt;br /&gt;
====5.1.3  Interlocking====&lt;br /&gt;
The good news about threads is that they share a process's data, files, and&lt;br /&gt;
resources. The bad news is that they share a process's data, files, and&lt;br /&gt;
resources--and that sometimes these items must be protected against&lt;br /&gt;
simultaneous update by multiple threads. As we discussed earlier, most OS/2&lt;br /&gt;
machines have a single CPU; the &amp;quot;random&amp;quot; preemption of the scheduler,&lt;br /&gt;
switching the CPU among threads, gives the illusion of the simultaneous&lt;br /&gt;
execution of threads. Because the scheduler is deterministic and priority&lt;br /&gt;
based, scheduling a process's threads is certainly not random; but good&lt;br /&gt;
programming practice requires that it be considered so. Each time a program&lt;br /&gt;
runs, external events will perturb the scheduling of the threads. Perhaps&lt;br /&gt;
some other, higher priority task needs the CPU for a while. Perhaps the&lt;br /&gt;
disk arm is in a different position, and a disk read by one thread takes a&lt;br /&gt;
little longer this time than it did the last. You cannot even assume that&lt;br /&gt;
only the highest priority runnable thread is executing because a multiple-&lt;br /&gt;
CPU system may execute the N highest priority threads.&lt;br /&gt;
     The only safe assumption is that all threads are executing&lt;br /&gt;
simultaneously and that--in the absence of explicit interlocking or&lt;br /&gt;
semaphore calls--each thread is always doing the &amp;quot;worst possible thing&amp;quot; in&lt;br /&gt;
terms of simultaneously updating static data or structures. Writing to&lt;br /&gt;
looser standards and then testing the program &amp;quot;to see if it's OK&amp;quot; is&lt;br /&gt;
unacceptable. The very nature of such race conditions, as they are called,&lt;br /&gt;
makes them extremely difficult to find during testing. Murphy's law says&lt;br /&gt;
that such problems are rare during testing and become a plague only after&lt;br /&gt;
the program is released.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.1  Local Variables&lt;br /&gt;
The best way to avoid a collision of threads over static data is to write&lt;br /&gt;
your program to minimize static data. Because each thread has its own&lt;br /&gt;
stack, each thread has its own stack frame in which to store local&lt;br /&gt;
variables. For example, if one thread opens and reads a file and no other&lt;br /&gt;
thread ever manipulates that file, the memory location where that file's&lt;br /&gt;
handle is stored should be in the thread's stack frame, not in static&lt;br /&gt;
memory. Likewise, buffers and work areas that are private to a thread&lt;br /&gt;
should be on that thread's stack frame. Stack variables that are local to&lt;br /&gt;
the current procedure are easily referenced in high-level languages and in&lt;br /&gt;
assembly language. Data items that are referenced by multiple procedures&lt;br /&gt;
can still be located on the stack. Pascal programs can address such items&lt;br /&gt;
directly via the data scope mechanism. C and assembly language programs&lt;br /&gt;
will need to pass pointers to the items into the procedures that use them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.2  RAM Semaphores&lt;br /&gt;
Although using local variables on stack frames greatly reduces problems&lt;br /&gt;
among threads, there will always be at least a few cases in which more than&lt;br /&gt;
one thread needs to access a static data item or a static resource such as&lt;br /&gt;
a file handle. In this situation, write the code that manipulates the&lt;br /&gt;
static item as a critical section (a body of code that manipulates a data&lt;br /&gt;
resource in a nonreentrant way) and then use RAM semaphores to reserve each&lt;br /&gt;
critical section before it is executed. This procedure guarantees that only&lt;br /&gt;
one thread at a time is in a critical section. See 16.2 Data Integrity for&lt;br /&gt;
a more detailed discussion.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.3  DosSuspendThread&lt;br /&gt;
In some situations it may be possible to enumerate all threads that might&lt;br /&gt;
enter a critical section. In these cases, a process's thread can use&lt;br /&gt;
DosSuspendThread to suspend the execution of the other thread(s) that might&lt;br /&gt;
enter the critical section. The DosSuspendThread call can only be used to&lt;br /&gt;
suspend threads that belong to the process making the call; it cannot be&lt;br /&gt;
used to suspend a thread that belongs to another process. Multiple threads&lt;br /&gt;
can be suspended by making multiple DosSuspendThread calls, one per thread.&lt;br /&gt;
If a just-suspended thread is in the middle of a system call, work on that&lt;br /&gt;
system call may or may not proceed. In either case, there will be no&lt;br /&gt;
further execution of application (ring 3) code by a suspended thread.&lt;br /&gt;
     It is usually better to protect critical sections with a RAM semaphore&lt;br /&gt;
than to use DosSuspendThread. Using a semaphore to protect a critical&lt;br /&gt;
section is analogous to using a traffic light to protect an intersection&lt;br /&gt;
(an automotive &amp;quot;critical section&amp;quot; because conflicting uses must be&lt;br /&gt;
prevented). Using DosSuspendThread, on the other hand, is analogous to your&lt;br /&gt;
stopping the other cars each time you go through an intersection; you're&lt;br /&gt;
interfering with the operation of the other cars just in case they might be&lt;br /&gt;
driving through the same intersection as you, presumably an infrequent&lt;br /&gt;
situation. Furthermore, you need a way to ensure that another vehicle isn't&lt;br /&gt;
already in the middle of the intersection when you stop it. Getting back to&lt;br /&gt;
software, you need to ensure that the thread you're suspending isn't&lt;br /&gt;
already executing the critical section at the time that you suspend it. We&lt;br /&gt;
recommend that you avoid DosSuspendThread when possible because of its&lt;br /&gt;
adverse effects on process performance and because of the difficulty in&lt;br /&gt;
guaranteeing that all the necessary threads have been suspended, especially&lt;br /&gt;
when a program undergoes future maintenance and modification.&lt;br /&gt;
     A DosResumeThread call restores the normal operation of a suspended&lt;br /&gt;
thread.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.4  DosEnterCritSec/DosExitCritSec&lt;br /&gt;
DosSuspendThread suspends the execution of a single thread within a&lt;br /&gt;
process. DosEnterCritSec suspends all threads in a process except the one&lt;br /&gt;
making the DosEnterCritSec call. Except for the scope of their operation,&lt;br /&gt;
DosEnterCritSec and DosExitCritSec are similar to DosSuspendThead and&lt;br /&gt;
DosResumeThread, and the same caveats and observations apply.&lt;br /&gt;
     DosExitCritSec will not undo a DosSuspendThread that was already in&lt;br /&gt;
effect. It releases only those threads that were suspended by&lt;br /&gt;
DosEnterCritSec.&lt;br /&gt;
&lt;br /&gt;
====5.1.4  Thread 1====&lt;br /&gt;
Each thread in a process has an associated thread ID. A thread's ID is a&lt;br /&gt;
magic cookie. Its value has no intrinsic meaning to the application; it&lt;br /&gt;
has meaning only as a name for a thread in an operating system call. The&lt;br /&gt;
one exception to this is the process's first thread, whose thread ID is&lt;br /&gt;
always 1.&lt;br /&gt;
     Thread 1 is special: It is the thread that is interrupted when a&lt;br /&gt;
process receives a signal. See Chapter 12, Signals, for further details.&lt;br /&gt;
&lt;br /&gt;
====5.1.5  Thread Death====&lt;br /&gt;
A thread can die in two ways. First, it can terminate itself with the&lt;br /&gt;
DosExit call. Second, when any thread in a process calls DosExit with the&lt;br /&gt;
&amp;quot;exit entire process&amp;quot; argument, all threads belonging to that process are&lt;br /&gt;
terminated &amp;quot;as soon as possible.&amp;quot; If they were executing application code&lt;br /&gt;
at the time DosExit was called, they terminate immediately. If they were in&lt;br /&gt;
the middle of a system call, they terminate &amp;quot;very quickly.&amp;quot; If the system&lt;br /&gt;
call executes quickly enough, its function may complete (although the CPU&lt;br /&gt;
will not return from the system call itself); but if the system call&lt;br /&gt;
involves delays of more than 1 second, it will terminate without&lt;br /&gt;
completing. Whether a thread's last system call completes is usually moot,&lt;br /&gt;
but in a few cases, such as writes to some types of devices, it may be&lt;br /&gt;
noticed that the last write was only partially completed.&lt;br /&gt;
     When a process wants to terminate, it should use the &amp;quot;terminate entire&lt;br /&gt;
process&amp;quot; form of DosExit rather than the &amp;quot;terminate this thread&amp;quot; form.&lt;br /&gt;
Unbeknownst to the calling process, some dynlink packages, including some&lt;br /&gt;
OS/2 system calls, may create threads. These threads are called captive&lt;br /&gt;
threads because only the original calling thread returns from the dynlink&lt;br /&gt;
call; the created thread remains &amp;quot;captive&amp;quot; inside the dynlink package. If a&lt;br /&gt;
program attempts to terminate by causing all its known threads to use the&lt;br /&gt;
DosExit &amp;quot;terminate this thread&amp;quot; form, the termination may not be successful&lt;br /&gt;
because of such captive threads.&lt;br /&gt;
     Of course, if the last remaining thread of a process calls DosExit&lt;br /&gt;
&amp;quot;terminate this thread,&amp;quot; OS/2 terminates the process.&lt;br /&gt;
&lt;br /&gt;
====5.1.6  Performance Characteristics====&lt;br /&gt;
Threads are intended to be fast and cheap. In OS/2 version 1.0, each&lt;br /&gt;
additional thread that is created consumes about 1200 bytes of memory&lt;br /&gt;
inside the OS/2 kernel for its kernel mode stack. This is in addition to&lt;br /&gt;
the 2048 bytes of user mode stack space that we recommend you provide from&lt;br /&gt;
the process's data area. Terminating a thread does not release the kernel&lt;br /&gt;
stack memory, but subsequently creating another thread reuses this memory.&lt;br /&gt;
In other words, the system memory that a process's threads consume is the&lt;br /&gt;
maximum number of threads simultaneously alive times 1200 bytes. This&lt;br /&gt;
figure is exclusive of each thread's stack, which is provided by the&lt;br /&gt;
process from its own memory.&lt;br /&gt;
     The time needed to create a new thread depends on the process's&lt;br /&gt;
previous thread behavior. Creating a thread that will reuse the internal&lt;br /&gt;
memory area created for a previous thread that has terminated takes&lt;br /&gt;
approximately 3 milliseconds.&lt;br /&gt;
2. All timings in this book refer to a 6 mHz IBM AT with one&lt;br /&gt;
wait-state memory. This represents a worst case performance level.&lt;br /&gt;
2 A request to create a new thread that&lt;br /&gt;
extends the process's &amp;quot;thread count high-water mark&amp;quot; requires an internal&lt;br /&gt;
memory allocation operation. This operation may trigger a memory compaction&lt;br /&gt;
or even a segment swapout, so its time cannot be accurately predicted.&lt;br /&gt;
     It takes about 1 millisecond for the system to begin running an&lt;br /&gt;
unblocked thread. In other words, if a lower-priority thread releases a RAM&lt;br /&gt;
semaphore that is being waited on by a higher-priority thread,&lt;br /&gt;
approximately 1 millisecond passes between the lower-priority thread's call&lt;br /&gt;
to release the semaphore and the return of the higher-priority thread from&lt;br /&gt;
its DosSemRequest call.&lt;br /&gt;
     Threads are a key feature of OS/2; they will receive strong support in&lt;br /&gt;
future versions of OS/2 and will play an increasingly important&lt;br /&gt;
architectural role. You can, therefore, expect thread costs and performance&lt;br /&gt;
to be the same or to improve in future releases.&lt;br /&gt;
&lt;br /&gt;
===5.2  Scheduler/Priorities===&lt;br /&gt;
&lt;br /&gt;
A typical running OS/2 system contains a lot of threads. Frequently,&lt;br /&gt;
several threads are ready to execute at any one time. The OS/2 scheduler&lt;br /&gt;
decides which thread to run next and how long to run it before assigning&lt;br /&gt;
the CPU to another thread. OS/2's scheduler is a priority-based&lt;br /&gt;
scheduler; it assigns each thread a priority and then uses that priority to&lt;br /&gt;
decide which thread to run. The OS/2 scheduler is also a preemptive&lt;br /&gt;
scheduler. If a higher-priority thread is ready to execute, OS/2 does not&lt;br /&gt;
wait for the lower-priority thread to finish with the CPU before&lt;br /&gt;
reassigning the CPU; the lower-priority thread is preempted--the CPU is&lt;br /&gt;
summarily yanked away. Naturally, the state of the preempted thread is&lt;br /&gt;
recorded so that its execution can resume later without ill effect.&lt;br /&gt;
     The scheduler's dispatch algorithm is very straightforward: It&lt;br /&gt;
executes the highest-priority runnable thread for as long as the thread&lt;br /&gt;
wants the CPU. When that thread gives up the CPU--perhaps by waiting for an&lt;br /&gt;
I/O operation--that thread is no longer runnable, and the scheduler&lt;br /&gt;
executes the thread with the highest priority that is runnable. If a&lt;br /&gt;
blocked thread becomes runnable and it has a higher priority than the&lt;br /&gt;
thread currently running, the CPU is immediately preempted and assigned to&lt;br /&gt;
the higher-priority thread. In summary, the CPU is always running the&lt;br /&gt;
highest-priority runnable thread.&lt;br /&gt;
     The scheduler's dispatcher is simplicity itself: It's blindly priority&lt;br /&gt;
based. Although the usual focus for OS/2 activities is the process--a&lt;br /&gt;
process lives, dies, opens files, and so on--the scheduler components of&lt;br /&gt;
OS/2 know little about processes. Because the thread is the dispatchable&lt;br /&gt;
entity, the scheduler is primarily thread oriented. If you're not used to&lt;br /&gt;
thinking in terms of threads, you can mentally substitute the word process&lt;br /&gt;
for the word thread in the following discussion. In practice, all of a&lt;br /&gt;
process's threads typically share the same priority, so it's not too&lt;br /&gt;
inaccurate to view the system as being made up of processes that compete&lt;br /&gt;
for CPU resources.&lt;br /&gt;
     In OS/2 threads are classified and run in three categories: general&lt;br /&gt;
priority, time-critical priority, and low priority. These categories are&lt;br /&gt;
further divided into subcategories. Figure 5-1 shows the relationship of&lt;br /&gt;
the three priority categories and their subcategories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   High&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³        ³            /ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³        ³          /  ³   Foreground   ³&lt;br /&gt;
³ Force  ³        /    ³     screen     ³&lt;br /&gt;
³  run   ³      /      ³     group      ³&lt;br /&gt;
³        ³    /        ³                ³&lt;br /&gt;
³        ³  /          ³  interactive   ³&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄ´/            ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
³        ³             ³   Foreground   ³&lt;br /&gt;
³        ³             ³     screen     ³&lt;br /&gt;
³ Normal ³             ³     group      ³&lt;br /&gt;
³        ³             ³                ³&lt;br /&gt;
³        ³             ³ noninteractive ³&lt;br /&gt;
³        ³             ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄ´\            ³                ³&lt;br /&gt;
³        ³  \          ³   Background   ³&lt;br /&gt;
³        ³    \        ³     screen     ³&lt;br /&gt;
³  Idle  ³      \      ³     group      ³&lt;br /&gt;
³  time  ³        \    ³                ³&lt;br /&gt;
³        ³          \  ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³        ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
   Low&lt;br /&gt;
&lt;br /&gt;
Figure 5-1.  Priority categories.&lt;br /&gt;
&lt;br /&gt;
====5.2.1  General Priority Category====&lt;br /&gt;
The majority of threads in the system run in the general priority category&lt;br /&gt;
and belong to one of three subcategories: background, foreground, or&lt;br /&gt;
interactive. To a limited extent, OS/2 dynamically modifies the priorities&lt;br /&gt;
of threads in the general priority category.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.1  Background Subcategory&lt;br /&gt;
The purpose of the OS/2 priority design is to optimize response rather than&lt;br /&gt;
throughput. In other words, the system is not concerned about ensuring that&lt;br /&gt;
all runnable threads get at least some CPU time, and the system is not&lt;br /&gt;
primarily concerned about trying to keep the disks busy when the highest-&lt;br /&gt;
priority thread is compute bound. Instead, OS/2 is concerned about keeping&lt;br /&gt;
less important work from delaying or slowing more important work. This is&lt;br /&gt;
the reason for the background subcategory. The word background has been&lt;br /&gt;
used in many different ways to describe how tasks are performed in many&lt;br /&gt;
operating systems; we use the word to indicate processes that are&lt;br /&gt;
associated with a screen group not currently being displayed.&lt;br /&gt;
     For example, a user is working with a word-processing program but then&lt;br /&gt;
switches from that program to a spreadsheet program. The word-processing&lt;br /&gt;
program becomes background, and the spreadsheet program is promoted from&lt;br /&gt;
background to foreground. When the user selects different screen groups,&lt;br /&gt;
threads change from foreground to background or background to foreground.&lt;br /&gt;
Background threads have the lowest priority in the general priority&lt;br /&gt;
category. Background applications get the CPU (and, through it, the disks)&lt;br /&gt;
only when all foreground threads are idle. As soon as a foreground thread&lt;br /&gt;
is runnable, the CPU is preempted from the background thread. Background&lt;br /&gt;
threads can use leftover machine time, but they can never compete with&lt;br /&gt;
foreground threads.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.2  Foreground and Interactive Subcategories&lt;br /&gt;
All processes associated with the currently active screen group are made&lt;br /&gt;
members of the foreground subcategory. The process that is currently&lt;br /&gt;
interacting with the keyboard is promoted to the interactive subcategory.&lt;br /&gt;
This ensures that the user will get the fastest possible response to a&lt;br /&gt;
command. When the interactive process's threads release the CPU (via&lt;br /&gt;
blocking on some OS/2 call), the noninteractive foreground threads get the&lt;br /&gt;
next crack at it because those threads are usually doing work on behalf of&lt;br /&gt;
the interactive process or work that is in some way related. If no&lt;br /&gt;
foreground thread needs the CPU, background threads may run.&lt;br /&gt;
     Although the scheduler concerns itself with threads rather than&lt;br /&gt;
processes, it's processes that switch between categories--foreground,&lt;br /&gt;
background, and interactive. When a process changes category--for example,&lt;br /&gt;
when a process shows itself to be in the interactive subcategory by doing&lt;br /&gt;
keyboard I/O--the priorities of all its threads are adjusted&lt;br /&gt;
appropriately.&lt;br /&gt;
     Because background threads are the &amp;quot;low men on the totem pole&amp;quot; that is&lt;br /&gt;
composed of quite a few threads, it may seem that they'll never get to run.&lt;br /&gt;
This isn't the case, though, over a long enough period of time. Yes, a&lt;br /&gt;
background thread can be totally starved for CPU time during a 5-second&lt;br /&gt;
interval, but it would be very rare if it received no service during a 1-&lt;br /&gt;
minute interval. Interactive application commands that take more than a few&lt;br /&gt;
seconds of CPU time are rare. Commands involving disk transfer may take&lt;br /&gt;
longer, but the CPU is available for lower-priority threads while the&lt;br /&gt;
interactive process is waiting for disk operations. Finally, a user rarely&lt;br /&gt;
keeps an interactive application fully busy; the normal &amp;quot;type, look, and&lt;br /&gt;
think&amp;quot; cycle has lots of spare time in it for background threads to&lt;br /&gt;
run.&lt;br /&gt;
     But how does this apply to the presentation manager? The presentation&lt;br /&gt;
manager runs many independent interactive tasks within the same screen&lt;br /&gt;
group, so are they all foreground threads? How does OS/2 know which is the&lt;br /&gt;
interactive process? The answer is that the presentation manager advises&lt;br /&gt;
the scheduler. When the presentation manager screen group is displayed, all&lt;br /&gt;
threads within that screen group are placed in the foreground category.&lt;br /&gt;
When the user selects a particular window to receive keyboard or mouse&lt;br /&gt;
events, the presentation manager tells the scheduler that the process using&lt;br /&gt;
that window is now the interactive process. As a result, the system's&lt;br /&gt;
interactive performance is preserved in the presentation manager's screen&lt;br /&gt;
group.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.3  Throughput Balancing&lt;br /&gt;
We mentioned that some operating systems try to optimize system throughput&lt;br /&gt;
by trying to run CPU-bound and I/O-bound applications at the same time. The&lt;br /&gt;
theory is that the I/O-bound application ties up the disk but needs little&lt;br /&gt;
CPU time, so the disk work can be gotten out of the way while the CPU is&lt;br /&gt;
running the CPU-bound task. If the disk thread has the higher priority, the&lt;br /&gt;
tasks run in tandem. Each time the disk operation is completed, the I/O-&lt;br /&gt;
bound thread regains the CPU and issues another disk operation. Leftover&lt;br /&gt;
CPU time goes to the CPU-bound task that, in this case, has a lower&lt;br /&gt;
priority.&lt;br /&gt;
     This won't work, however, if the CPU-bound thread has a higher&lt;br /&gt;
priority than the I/O-bound thread. The CPU-bound thread will tend to hold&lt;br /&gt;
the CPU, and the I/O-bound thread won't get even the small amount of CPU&lt;br /&gt;
time that it needs to issue another I/O request. Traditionally, schedulers&lt;br /&gt;
have been designed to deal with this problem by boosting the priority of&lt;br /&gt;
I/O-bound tasks and lowering the priority of CPU-bound tasks so that,&lt;br /&gt;
eventually, the I/O-bound thread gets enough service to make its I/O&lt;br /&gt;
requests.&lt;br /&gt;
     The OS/2 scheduler incorporates this design to a limited extent. Each&lt;br /&gt;
time a thread issues a system call that blocks, the scheduler looks at the&lt;br /&gt;
period between the time the CPU was assigned to the thread and the time the&lt;br /&gt;
thread blocked itself with a system call.&lt;br /&gt;
3. We use &amp;quot;blocking&amp;quot; rather than &amp;quot;requesting an I/O operation&amp;quot; as&lt;br /&gt;
a test of I/O boundedness because nearly all blocking operations&lt;br /&gt;
wait for I/O. If a thread's data were all in the buffer cache,&lt;br /&gt;
the thread could issue many I/O requests and still be compute&lt;br /&gt;
bound. In other words, when we speak of I/O-bound threads, we&lt;br /&gt;
really mean device bound--not I/O request bound.&lt;br /&gt;
3 If that period of time is short,&lt;br /&gt;
the thread is considered I/O bound, and its priority receives a small&lt;br /&gt;
increment. If a thread is truly I/O bound, it soon receives several such&lt;br /&gt;
increments and, thus, a modest priority promotion. On the other hand, if&lt;br /&gt;
the thread held the CPU for a longer period of time, it is considered CPU&lt;br /&gt;
bound, and its priority receives a small decrement.&lt;br /&gt;
     The I/O boundedness priority adjustment is small. No background&lt;br /&gt;
thread, no matter how I/O bound, can have its priority raised to the point&lt;br /&gt;
where it has a higher priority than any foreground thread, no matter how&lt;br /&gt;
CPU bound. This throughput enhancing optimization applies only to &amp;quot;peer&amp;quot;&lt;br /&gt;
threads--threads with similar priorities. For example, the threads of a&lt;br /&gt;
single process generally have the same base priority, so this adjustment&lt;br /&gt;
helps optimize the throughput of that process.&lt;br /&gt;
&lt;br /&gt;
====5.2.2  Time-Critical Priority Category====&lt;br /&gt;
Foreground threads, particularly interactive foreground threads, receive&lt;br /&gt;
CPU service whenever they want it. Noninteractive foreground threads and,&lt;br /&gt;
particularly, background threads may not receive any CPU time for periods&lt;br /&gt;
of arbitrary length. This approach improves system response, but it's not&lt;br /&gt;
always a good thing. For example, you may be running a network or a&lt;br /&gt;
telecommunications application that drops its connection if it can't&lt;br /&gt;
respond to incoming packets in a timely fashion. Also, you may want to make&lt;br /&gt;
an exception to the principle of &amp;quot;response, not throughput&amp;quot; when it comes&lt;br /&gt;
to printers. Most printers are much slower than their users would like, and&lt;br /&gt;
most printer spooler programs require little in the way of CPU time; so the&lt;br /&gt;
OS/2 print spooler (the program that prints queued output on the printer)&lt;br /&gt;
would like to run at a high priority to keep the printer busy.&lt;br /&gt;
     Time-critical applications are so called because the ability to run in&lt;br /&gt;
a timely fashion is critical to their well-being. Time-critical&lt;br /&gt;
applications may or may not be interactive, and they may be in the&lt;br /&gt;
foreground or in a background screen group, but this should not affect&lt;br /&gt;
their high priority. The OS/2 scheduler contains a time-critical priority&lt;br /&gt;
category to deal with time-critical applications. A thread running in this&lt;br /&gt;
priority category has a higher priority than any non-time-critical thread&lt;br /&gt;
in the system, including interactive threads. Unlike priorities in the&lt;br /&gt;
general category, a time-critical priority is never adjusted; once given a&lt;br /&gt;
time-critical priority, a thread's priority remains fixed until a system&lt;br /&gt;
call changes it.&lt;br /&gt;
     Naturally, time-critical threads should consume only modest amounts of&lt;br /&gt;
CPU time. If an application has a time-critical thread that consumes&lt;br /&gt;
considerable CPU time--say, more than 20 percent--the foreground&lt;br /&gt;
interactive application will be noticeably slowed or even momentarily&lt;br /&gt;
stopped. System usability is severely affected when the interactive&lt;br /&gt;
application can't get service. The screen output stutters and stumbles,&lt;br /&gt;
characters are dropped when commands are typed, and, in general, the&lt;br /&gt;
computer becomes unusable.&lt;br /&gt;
     Not all threads in a process have to be of the same priority. An&lt;br /&gt;
application may need time-critical response for only some of its work; the&lt;br /&gt;
other work can run at a normal priority. For example, in a&lt;br /&gt;
telecommunications program a &amp;quot;receive incoming data&amp;quot; thread might run at a&lt;br /&gt;
time-critical priority but queue messages in memory for processing by a&lt;br /&gt;
normal-priority thread. If the time-critical thread finds that the normal-&lt;br /&gt;
priority thread has fallen behind, it can send a &amp;quot;wait for me&amp;quot; message to&lt;br /&gt;
the sending program.&lt;br /&gt;
     We strongly recommend that processes that use monitors run the monitor&lt;br /&gt;
thread, and only the monitor thread, at a time-critical priority. This&lt;br /&gt;
prevents delayed device response because of delays in processing the&lt;br /&gt;
monitor data stream. See 16.1 Device Monitors for more information.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.3  Low Priority Category&lt;br /&gt;
If you picture the general priority category as a range of priorities, with&lt;br /&gt;
the force run priority category as a higher range, there is a third range,&lt;br /&gt;
called the low priority category, that is lower in priority than the&lt;br /&gt;
general priority category. As a result, threads in this category get CPU&lt;br /&gt;
service only when no other thread in the other categories needs it. This&lt;br /&gt;
category is a mirror image of the time-critical priority category in that&lt;br /&gt;
the system call that sets the thread fixes the priority; OS/2 never changes&lt;br /&gt;
a low priority.&lt;br /&gt;
     I don't expect the low priority category to be particularly popular.&lt;br /&gt;
It's in the system primarily because it falls out for free, as a mirror&lt;br /&gt;
image of the time-critical category. Turnkey systems may want to run some&lt;br /&gt;
housekeeping processes at this priority. Some users enjoy computing PI,&lt;br /&gt;
doing cryptographic analysis, or displaying fractal images; these&lt;br /&gt;
recreations are good candidates for soaking up leftover CPU time. On a more&lt;br /&gt;
practical level, you could run a program that counts seconds of CPU time&lt;br /&gt;
and yields a histogram of CPU utilization during the course of a day.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.4  Setting Process/Thread Priorities&lt;br /&gt;
We've discussed at some length the effect of the various priorities, but we&lt;br /&gt;
haven't discussed how to set these priorities. Because inheritance is an&lt;br /&gt;
important OS/2 concept, how does a parent's priority affect that of the&lt;br /&gt;
child? Finally, although we said that priority is a thread issue rather&lt;br /&gt;
than a process one, we kept bringing up processes anyway. How does all this&lt;br /&gt;
work?&lt;br /&gt;
     Currently, whenever a thread is created, it inherits the priority of&lt;br /&gt;
its creator thread. In the case of DosCreateThread, the thread making the&lt;br /&gt;
call is the creator thread. In the case of thread 1, the thread in the&lt;br /&gt;
parent process that is making the DosExecPgm call is the creator thread.&lt;br /&gt;
When a process makes a DosSetPrty call to change the priority of one of its&lt;br /&gt;
own threads, the new priority always takes effect. When a process uses&lt;br /&gt;
DosSetPrty to change the priority of another process, only the threads in&lt;br /&gt;
that other process which have not had their priorities explicitly set from&lt;br /&gt;
within their own process are changed. This prevents a parent process from&lt;br /&gt;
inadvertently lowering the priority of, say, a time-critical thread by&lt;br /&gt;
changing the base priority of a child process.&lt;br /&gt;
     In a future release, we expect to improve this algorithm so that each&lt;br /&gt;
process has a base priority. A new thread will inherit its creator's base&lt;br /&gt;
priority. A process's thread priorities that are in the general priority&lt;br /&gt;
category will all be relative to the process's base priority so that a&lt;br /&gt;
change in the base priority will raise or lower the priority of all the&lt;br /&gt;
process's general threads while retaining their relative priority&lt;br /&gt;
relationships. Threads in the time-critical and low priority categories&lt;br /&gt;
will continue to be unaffected by their process's base priority.&lt;br /&gt;
&lt;br /&gt;
==6  The User Interface==&lt;br /&gt;
&lt;br /&gt;
OS/2 contains several important subsystems: the file system, the memory&lt;br /&gt;
management subsystem, the multitasking subsystem, and the user interface&lt;br /&gt;
subsystem--the presentation manager. MS-DOS does not define or support a&lt;br /&gt;
user interface subsystem; each application must provide its own. MS-DOS&lt;br /&gt;
utilities use a primitive line-oriented interface, essentially unchanged&lt;br /&gt;
from the interface provided by systems designed to interface with TTYs.&lt;br /&gt;
     OS/2 is intended to be a graphics-oriented operating system, and as&lt;br /&gt;
such it needs to provide a standard graphical user interface (GUI)&lt;br /&gt;
subsystem--for several reasons. First, because such systems are complex to&lt;br /&gt;
create, to expect that each application provide its own is unreasonable.&lt;br /&gt;
Second, a major benefit of a graphical user interface is that applications&lt;br /&gt;
can be intermingled. For example, their output windows can share the&lt;br /&gt;
screen, and the user can transfer data between applications using visual&lt;br /&gt;
metaphors. If each application had its own GUI package, such sharing would&lt;br /&gt;
be impossible. Third, a graphical user interface is supposed to make the&lt;br /&gt;
machine easier to use, but this will be so only if the user can learn one&lt;br /&gt;
interface that will work with all applications.&lt;br /&gt;
&lt;br /&gt;
===6.1  VIO User Interface===&lt;br /&gt;
&lt;br /&gt;
The full graphical user interface subsystem will not ship with OS/2 version&lt;br /&gt;
1.0, so the initial release will contain the character-oriented VIO/KBD/MOU&lt;br /&gt;
subsystem (see Chapter 13, The Presentation Manager and VIO, for more&lt;br /&gt;
details). Although VIO doesn't provide any graphical services, it does&lt;br /&gt;
allow applications to sidestep VIO and construct their own. The VIO&lt;br /&gt;
screen group interface is straightforward. When the machine is booted up,&lt;br /&gt;
the screen displays the screen group list. The user can select an existing&lt;br /&gt;
screen group or create a new one. From within a screen group, the user can&lt;br /&gt;
type a magic key sequence to return the screen to the screen group list.&lt;br /&gt;
Another magic key sequence allows the user to toggle through all existing&lt;br /&gt;
screen groups. One screen group is identified in the screen group list as&lt;br /&gt;
the real mode screen group.&lt;br /&gt;
&lt;br /&gt;
===6.2  The Presentation Manager User Interface===&lt;br /&gt;
&lt;br /&gt;
The OS/2 presentation manager is a powerful and flexible graphical user&lt;br /&gt;
interface. It supports such features as windowing, drop-down and pop-up&lt;br /&gt;
menus, and scroll bars. It works best with a graphical pointing device such&lt;br /&gt;
as a mouse, but it can be controlled exclusively from the keyboard.&lt;br /&gt;
     The presentation manager employs screen windows to allow multiple&lt;br /&gt;
applications to use the screen and keyboard simultaneously. Each&lt;br /&gt;
application uses one or more windows to display its information; the user&lt;br /&gt;
can size and position each window, overlapping some and perhaps shrinking&lt;br /&gt;
others to icons. Mouse and keyboard commands change the input focus between&lt;br /&gt;
windows; this allows the presentation manager to route keystrokes and mouse&lt;br /&gt;
events to the proper application.&lt;br /&gt;
     Because of its windowing capability, the presentation manager doesn't&lt;br /&gt;
need to use the underlying OS/2 screen group mechanism to allow the user to&lt;br /&gt;
switch between running applications. The user starts an application by&lt;br /&gt;
pointing to its name on a menu display; for most applications the&lt;br /&gt;
presentation manager creates a new window and assigns it to the new&lt;br /&gt;
process. Some applications may decline to use the presentation manager's&lt;br /&gt;
graphical user interface and prefer to take direct control of the display.&lt;br /&gt;
When such an application is initiated, the presentation manager creates a&lt;br /&gt;
private screen group for it and switches to that screen group. The user can&lt;br /&gt;
switch away by entering a special key sequence that brings up a menu which&lt;br /&gt;
allows the user to select any running program. If the selected program is&lt;br /&gt;
using the standard presentation manager GUI, the screen is switched to the&lt;br /&gt;
screen group shared by those programs. Otherwise, the screen is switched to&lt;br /&gt;
the private screen group that the specified application is using.&lt;br /&gt;
     To summarize, only the real mode application and applications that&lt;br /&gt;
take direct control of the display hardware need to run in their own screen&lt;br /&gt;
groups. The presentation manager runs all other processes in a single&lt;br /&gt;
screen group and uses its windowing facilities to share the screen among&lt;br /&gt;
them. The user can switch between applications via a special menu; if both&lt;br /&gt;
the previous and the new application are using the standard interface, the&lt;br /&gt;
user can switch the focus directly without going though the menu.&lt;br /&gt;
&lt;br /&gt;
===6.3  Presentation Manager and VIO Compatibility===&lt;br /&gt;
&lt;br /&gt;
In OS/2 version 1.1 and in all subsequent releases, the presentation&lt;br /&gt;
manager will replace and superset the VIO interface. Applications that use&lt;br /&gt;
the character mode VIO interface will continue to work properly as&lt;br /&gt;
windowable presentation manager applications, as will applications that use&lt;br /&gt;
the STDIN and STDOUT file handles for interactive I/O. Applications that&lt;br /&gt;
use the VIO interface to obtain direct access to the graphical display&lt;br /&gt;
hardware will also be supported; as described above, the presentation&lt;br /&gt;
manager will run such applications in their own screen group.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==7  Dynamic Linking==&lt;br /&gt;
&lt;br /&gt;
A central component of OS/2 is dynamic linking. Dynamic links play several&lt;br /&gt;
critical architectural roles. Before we can discuss them at such an&lt;br /&gt;
abstract level, however, we need to understand the nuts and bolts of their&lt;br /&gt;
workings.&lt;br /&gt;
&lt;br /&gt;
===7.1  Static Linking===&lt;br /&gt;
&lt;br /&gt;
A good preliminary to the study of dynamic links (called dynlinks, for&lt;br /&gt;
short) is a review of their relative, static links. Every programmer who&lt;br /&gt;
has gone beyond interpreter-based languages such as BASIC is familiar with&lt;br /&gt;
static links. You code a subroutine or a procedure call to a routine that&lt;br /&gt;
is not present in that compiland (or source file), which we'll call Foo.&lt;br /&gt;
The missing routine is declared external so that the assembler or compiler&lt;br /&gt;
doesn't flag it as an undefined symbol. At linktime, you present the linker&lt;br /&gt;
with the .OBJ file that you created from your compiland, and you also&lt;br /&gt;
provide a .OBJ file&lt;br /&gt;
1. Or a .LIB library file that contains the .OBJ file as a part of it.&lt;br /&gt;
1 that contains the missing routine Foo. The linker&lt;br /&gt;
combines the compilands into a final executable image--the .EXE file--that&lt;br /&gt;
contains the routine Foo as well as the routines that call it. During the&lt;br /&gt;
combination process, the linker adjusts the calls to Foo, which had been&lt;br /&gt;
undefined external references, to point to the place in the .EXE file where&lt;br /&gt;
the linker relocated the Foo routine. This process is diagramed in Figure&lt;br /&gt;
7-1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      .OBJ                            .LIB&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³               ³               ³  Foo  ÄÄÄÄÄÄ  ³&lt;br /&gt;
³   Call Foo    ³               ³       ÄÄÄÄÄÄ  ³&lt;br /&gt;
³               ³               ³       ÄÄÄÄÄÄ  ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ               ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ      .EXE file&lt;br /&gt;
        ³                               ³          ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ÀÄÄÄÄÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÙ          ³               ³&lt;br /&gt;
                  ³     +     ³                    ³   Call �ÄÄÄÄÄÄÅÄ¿&lt;br /&gt;
                ÚÄ�ÄÄÄÄÄÄÄÄÄÄÄ�Ä¿                  ³               ³ ³&lt;br /&gt;
                ³               ³                  ³               ³ ³&lt;br /&gt;
                ³    Linker     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³               ³ ³&lt;br /&gt;
                ³               ³                  ³               ³ ³&lt;br /&gt;
                ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ                  ³               ³ ³&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ �ÄÄÄÄÄÄÅÄÙ&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ        ³&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ        ³&lt;br /&gt;
                                                   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-1.  Static linking.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     In other words, with static linking you can write a program in pieces.&lt;br /&gt;
You can compile one piece at a time by having it refer to the other pieces&lt;br /&gt;
as externals. A program called a linker or a link editor combines these&lt;br /&gt;
pieces into one final .EXE image, fixing up the external references (that&lt;br /&gt;
is, references between one piece and another) that those pieces contain.&lt;br /&gt;
     Writing and compiling your program piecemeal is useful, but the&lt;br /&gt;
primary advantage of static linking is that you can use it to reference a&lt;br /&gt;
standard set of subroutines--a subroutine library--without compiling or&lt;br /&gt;
even possessing the source code for those subroutines. Nearly all high-&lt;br /&gt;
level language packages come with one or more standard runtime libraries&lt;br /&gt;
that contain various useful subroutines that the compiler can call&lt;br /&gt;
implicitly and that the programmer can call explicitly. Source for these&lt;br /&gt;
runtime libraries is rarely provided; the language supplier provides only&lt;br /&gt;
the .OBJ object files, typically in library format.&lt;br /&gt;
     To summarize, in traditional static linking the target code (that is,&lt;br /&gt;
the external subroutine) must be present at linktime and is built into the&lt;br /&gt;
final .EXE module. This makes the .EXE file larger, naturally, but more&lt;br /&gt;
important, the target code can't be changed or upgraded without relinking&lt;br /&gt;
to the main program's .OBJ files. Because the personal computer field is&lt;br /&gt;
built on commercial software whose authors don't release source or .OBJ&lt;br /&gt;
files, this relinking is out of the question for the typical end user.&lt;br /&gt;
Finally, the target code can't be shared among several (different)&lt;br /&gt;
applications that use the same library routines. This is true for two&lt;br /&gt;
reasons. First, the target code was relocated differently by the linker for&lt;br /&gt;
each client; so although the code remains logically the same for each &lt;br /&gt;
application, the address components of the binary instructions are&lt;br /&gt;
different in each .EXE file. Second, the operating system has no way of&lt;br /&gt;
knowing that these applications are using the same library, and it has no&lt;br /&gt;
way of knowing where that library is in each .EXE file. Therefore, it can't&lt;br /&gt;
avoid having duplicate copies of the library in memory.&lt;br /&gt;
&lt;br /&gt;
===7.2  Loadtime Dynamic Linking===&lt;br /&gt;
&lt;br /&gt;
The mechanical process of loadtime dynamic linking is the same as that of&lt;br /&gt;
static linking. The programmer makes an external reference to a subroutine&lt;br /&gt;
and at linktime specifies a library file (or a .OBJ file) that defines the&lt;br /&gt;
reference. The linker produces a .EXE file that OS/2 then loads and&lt;br /&gt;
executes. Behind the scenes, however, things are very much different.&lt;br /&gt;
     Step 1 is the same for both kinds of linking. The external reference&lt;br /&gt;
is compiled or assembled, resulting in a .OBJ file that contains an&lt;br /&gt;
external reference fixup record. The assembler or compiler doesn't know&lt;br /&gt;
about dynamic links; the .OBJ file that an assembler or a compiler produces&lt;br /&gt;
may be used for static links, dynamic links, or, more frequently, a&lt;br /&gt;
combination of both (some externals become dynamic links, others become&lt;br /&gt;
static links).&lt;br /&gt;
     In static linking, the linker finds the actual externally referenced&lt;br /&gt;
subroutine in the library file. In dynamic linking, the linker finds a&lt;br /&gt;
special record that defines a module name string and an entry point name&lt;br /&gt;
string. For example, in our hypothetical routine Foo, the library file&lt;br /&gt;
contains only these two name strings, not the code for Foo itself. (The&lt;br /&gt;
entry point name string doesn't have to be the name by which programs&lt;br /&gt;
called the routine.) The resultant .EXE file doesn't contain the code for&lt;br /&gt;
Foo; it contains a special dynamic link record that specifies these module&lt;br /&gt;
and entry point names for Foo. This is illustrated in Figure 7-2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿     ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     .OBJ      ³ Extern  ³     .LIB      ³     ³     .EXE      ³&lt;br /&gt;
³               ÃÄÄÄÄÄÄÄÄ�³ Foo:          ³     ³               ³&lt;br /&gt;
³   Call Foo    ³         ³ Module dlpack ³     ³      ÚÄÄÄÄ¿   ³&lt;br /&gt;
³               ³         ³ entry Foo     ³     ³ Call ³????ÃÄÄÄÅÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ         ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ     ³      ÀÄÄÄÄÙ   ³  ³&lt;br /&gt;
        ³                         ³         ÚÄÄ�ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³&lt;br /&gt;
        ³                         ³         ³   ³ Reference to  ³�ÄÙ&lt;br /&gt;
        ÀÄÄÄÄÄÄÄÄÄ�  +  �ÄÄÄÄÄÄÄÄÄÙ         ³   ³ dlpack: Foo   ³&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄ¿                 ³   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                ³  Link   ³                 ³&lt;br /&gt;
                ÀÄÄÄÄÂÄÄÄÄÙ                 ³&lt;br /&gt;
                     ³                      ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-2.  Dynamic linking.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When this .EXE file is run, OS/2 loads the code in the .EXE file into&lt;br /&gt;
memory and discovers the dynamic link record(s). For each dynamic link&lt;br /&gt;
module that is named, OS/2 locates the code in the system's dynamic link&lt;br /&gt;
library directory and loads it into memory (unless the module is already in&lt;br /&gt;
use; see below). The system then links the external references in the&lt;br /&gt;
application to the addresses of the called entry points. This process is&lt;br /&gt;
diagramed in Figure 7-3.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿                                    ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     .EXE      ³                                    ³     .DLL      ³&lt;br /&gt;
³               ³                                    ³  dlpack: Foo  ³&lt;br /&gt;
³     Call ÄÄÄÄÄÅÄÄ¿                                 ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³                                 ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³  dlpack: Foo  ³�ÄÙ Disk                            ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ                                    ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ&lt;br /&gt;
        ³                                                    ³&lt;br /&gt;
        ³                                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
        ³                                 ³&lt;br /&gt;
Ä Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä&lt;br /&gt;
        ³                                 ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄ�ÄÄÄÄÄÄÄ¿                 ÚÄÄÄÄÄÄÄ�ÄÄÄÄÄÄÄ¿&lt;br /&gt;
³               ³      RAM        ³               ³&lt;br /&gt;
³               ³      Fixup      ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³               ³     by OS/2     ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³     Call  ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³               ³                 ³               ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-3.  Loadtime dynlink fixups.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To summarize, instead of linking in the target code at linktime, the linker&lt;br /&gt;
places a module name and an entry point name into the .EXE file. When the&lt;br /&gt;
program is loaded (that is, executed), OS/2 locates the target code, loads&lt;br /&gt;
it, and does the necessary linking. Although all we're doing is postponing&lt;br /&gt;
the linkage until loadtime, this technique has several important&lt;br /&gt;
ramifications. First, the target code is not in the .EXE file but in a&lt;br /&gt;
separate dynamic link library (.DLL) file. Thus, the .EXE file is smaller&lt;br /&gt;
because it contains only the name of the target code, not the code itself.&lt;br /&gt;
You can change or upgrade the target code at any time simply by replacing&lt;br /&gt;
this .DLL file. The next time a referencing application is loaded,&lt;br /&gt;
2. With some restrictions. See 7.11.2 Dynlink Life, Death, and Sharing.&lt;br /&gt;
2 it is&lt;br /&gt;
linked to the new version of the target code. Finally, having the target&lt;br /&gt;
code in a .DLL file paves the way for automatic code sharing. OS/2 can&lt;br /&gt;
easily understand that two applications are using the same dynlink code&lt;br /&gt;
because it loaded and linked that code, and it can use this knowledge to&lt;br /&gt;
share the pure segments of that dynlink package rather than loading&lt;br /&gt;
duplicate copies.&lt;br /&gt;
     A final advantage of dynamic linking is that it's totally invisible to&lt;br /&gt;
the user, and it can even be invisible to the programmer. You need to&lt;br /&gt;
understand dynamic linking to create a dynamic link module, but you can use&lt;br /&gt;
one without even knowing that it's not an ordinary static link. The one&lt;br /&gt;
disadvantage of dynamic linking is that programs sometimes take longer to&lt;br /&gt;
load into memory than do those linked with static linking. The good news&lt;br /&gt;
about dynamic linking is that the target code(s) are separate from the main&lt;br /&gt;
.EXE file; this is also the bad news. Because the target code(s) are&lt;br /&gt;
separate from the main .EXE file, a few more disk operations may be&lt;br /&gt;
necessary to load them.&lt;br /&gt;
     The actual performance ramifications depend on the kind of dynlink&lt;br /&gt;
module that is referenced and whether this .EXE file is the first to&lt;br /&gt;
reference the module. This is discussed in more detail in 7.11&lt;br /&gt;
Implementation Details.&lt;br /&gt;
     Although this discussion has concentrated on processes calling dynlink&lt;br /&gt;
routines, dynlink routines can in fact be called by other dynlink routines.&lt;br /&gt;
When OS/2 loads a dynlink routine in response to a process's request, it&lt;br /&gt;
examines that routine to see if it has any dynlink references of its own.&lt;br /&gt;
Any such referenced dynlink routines are also loaded and so on until no&lt;br /&gt;
unsatisfied dynlink references remain.&lt;br /&gt;
&lt;br /&gt;
===7.3  Runtime Dynamic Linking===&lt;br /&gt;
&lt;br /&gt;
The dynamic linking that we have been describing is called load-time&lt;br /&gt;
dynamic linking because it occurs when the .EXE file is loaded. All dynamic&lt;br /&gt;
link names need not appear in the .EXE file at loadtime; a process can link&lt;br /&gt;
itself to a dynlink package at runtime as well. Runtime dynamic linking&lt;br /&gt;
works exactly like loadtime dynamic linking except that the process creates&lt;br /&gt;
the dynlink module and entry point names at runtime and then passes them to&lt;br /&gt;
OS/2 so that OS/2 can locate and load the specified dynlink code.&lt;br /&gt;
     Runtime linking takes place in four steps.&lt;br /&gt;
&lt;br /&gt;
     1.  The process issues a DosLoadModule call to tell OS/2 to locate and&lt;br /&gt;
         load the dynlink code into memory.&lt;br /&gt;
&lt;br /&gt;
     2.  The DosGetProcAddr call is used to obtain the addresses of the&lt;br /&gt;
         routines that the process wants to call.&lt;br /&gt;
&lt;br /&gt;
     3.  The process calls the dynlink library entry points by means of an&lt;br /&gt;
         indirect call through the address returned by DosGetProcAddr.&lt;br /&gt;
&lt;br /&gt;
     4.  When the process has no more use for the dynlink code, it can call&lt;br /&gt;
         DosFreeModule to release the dynlink code. After this call, the&lt;br /&gt;
         process will still have the addresses returned by DosGetProcAddr,&lt;br /&gt;
         but they will be illegal addresses; referencing them will cause a&lt;br /&gt;
         GP fault.&lt;br /&gt;
&lt;br /&gt;
     Runtime dynamic links are useful when a program knows that it will&lt;br /&gt;
want to call some dynlink routines but doesn't know which ones. For&lt;br /&gt;
example, a charting program may support four plotters, and it may want to&lt;br /&gt;
use dynlink plotter driver packages. It doesn't make sense for the&lt;br /&gt;
application to contain loadtime dynamic links to all four plotters because&lt;br /&gt;
only one will be used and the others will take up memory and swap space.&lt;br /&gt;
Instead, the charting program can wait until it learns which plotter is&lt;br /&gt;
installed and then use the runtime dynlink facility to load the appropriate&lt;br /&gt;
package. The application need not even call DosLoadModule when it&lt;br /&gt;
initializes; it can wait until the user issues a plot command before it&lt;br /&gt;
calls DosLoadModule, thereby reducing memory demands on the system.&lt;br /&gt;
     The application need not even be able to enumerate all the modules or&lt;br /&gt;
entry points that may be called. The application can learn the names of the&lt;br /&gt;
dynlink modules from another process or by looking in a configuration file.&lt;br /&gt;
This allows the user of our charting program, for example, to install&lt;br /&gt;
additional plotter drivers that didn't even exist at the time that the&lt;br /&gt;
application was written. Of course, in this example the calling sequences&lt;br /&gt;
of the dynlink plotter driver must be standardized, or the programmer must&lt;br /&gt;
devise a way for the application to figure out the proper way to call these&lt;br /&gt;
newly found routines.&lt;br /&gt;
     Naturally, a process is not limited to one runtime dynlink module;&lt;br /&gt;
multiple calls to DosLoadModule can be used to link to several dynlink&lt;br /&gt;
modules simultaneously. Regardless of the number of modules in use,&lt;br /&gt;
DosFreeModule should be used if the dynlink module will no longer be used&lt;br /&gt;
and the process intends to continue executing. Issuing DosFreeModules is&lt;br /&gt;
unnecessary if the process is about to terminate; OS/2 releases all dynlink&lt;br /&gt;
modules at process termination time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.4  Dynlinks, Processes, and Threads&lt;br /&gt;
&lt;br /&gt;
Simply put, OS/2 views dynlinks as a fancy subroutine package. Dynlinks&lt;br /&gt;
aren't processes, and they don't own any resources. A dynlink executes only&lt;br /&gt;
because a thread belonging to a client process called the dynlink code. The&lt;br /&gt;
dynlink code is executing as the client thread and process because, in the&lt;br /&gt;
eyes of the system, the dynlink is merely a subroutine that process has&lt;br /&gt;
called. Before the client process can call a dynlink package, OS/2 ensures&lt;br /&gt;
that the dynlink's segments are in the address space of the client. No ring&lt;br /&gt;
transition or context switching overhead occurs when a client calls a&lt;br /&gt;
dynlink routine; the far call to a dynlink entry point is just that--an&lt;br /&gt;
ordinary far call to a subroutine in the process's address space.&lt;br /&gt;
     One side effect is that dynlink calls are very fast; little CPU time&lt;br /&gt;
is spent getting to the dynlink package. Another side effect is no&lt;br /&gt;
separation between a client's segments and a dynlink package's segments&lt;br /&gt;
3. Subsystem dynlink packages may be sensitive to this. For&lt;br /&gt;
detailed information, see 7.11.1 Dynlink Data Security.&lt;br /&gt;
3&lt;br /&gt;
because segments belong to processes and only one process is running both&lt;br /&gt;
the client and the dynlink code. The same goes for file handles,&lt;br /&gt;
semaphores, and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5  Data&lt;br /&gt;
&lt;br /&gt;
The careful reader will have noticed something missing in this discussion&lt;br /&gt;
of dynamic linking: We've said nothing about how to handle a dynlink&lt;br /&gt;
routine's data. Subroutines linked with static links have no problem with&lt;br /&gt;
having their own static data; when the linker binds the external code with&lt;br /&gt;
the main code, it sees how much static data the external code needs and&lt;br /&gt;
allocates the necessary space in the proper data segment(s). References&lt;br /&gt;
that the external code makes to its data are then fixed up to point&lt;br /&gt;
to the proper location. Because the linker is combining all the .OBJ&lt;br /&gt;
files into a .EXE file, it can easily divide the static data segment(s)&lt;br /&gt;
among the various compilands.&lt;br /&gt;
     This technique doesn't work for dynamic link routines because their&lt;br /&gt;
code and therefore their data requirements aren't present at linktime. It's&lt;br /&gt;
possible to extend the special dynlink .OBJ file to describe the amount of&lt;br /&gt;
static data that the dynlink package will need, but it won't work.&lt;br /&gt;
4. And even if it did work, it would be a poor design because it&lt;br /&gt;
would restrict our ability to upgrade the dynlink code in the field.&lt;br /&gt;
4&lt;br /&gt;
Because the main code in each application uses different amounts of static&lt;br /&gt;
data, the data area reserved for the dynlink package would end up at a&lt;br /&gt;
different offset in each .EXE file that was built. When these .EXE files&lt;br /&gt;
were executed, the one set of shared dynlink code segments would need to&lt;br /&gt;
reference the data that resides at different addresses for each different&lt;br /&gt;
client. Relocating the static references in all dynlink code modules at&lt;br /&gt;
each occurrence of a context switch is clearly out of the question.&lt;br /&gt;
     An alternative to letting dynamic link routines have their own static&lt;br /&gt;
data is to require that their callers allocate the necessary data areas and&lt;br /&gt;
pass pointers to them upon every call. We easily rejected this scheme: It's&lt;br /&gt;
cumbersome; call statements must be written differently if they're for a&lt;br /&gt;
dynlink routine; and, finally, this hack wouldn't support subsystems, which&lt;br /&gt;
are discussed below.&lt;br /&gt;
     Instead, OS/2 takes advantage of the segmented architecture of the&lt;br /&gt;
80286. Each dynamic link routine can use one or more data segments to hold&lt;br /&gt;
its static data. Each client process has a separate set of these segments.&lt;br /&gt;
Because these segments hold only the dynlink routine's data and none of the&lt;br /&gt;
calling process's data, the offsets of the data items within that segment&lt;br /&gt;
will be the same no matter which client process is calling the dynlink&lt;br /&gt;
code. All we need do to solve our static data addressability problem is&lt;br /&gt;
ensure that the segment selectors of the dynlink routine's static data&lt;br /&gt;
segments are the same for each client process.&lt;br /&gt;
     OS/2 ensures that the dynlink library's segment selectors are the same&lt;br /&gt;
for each client process by means of a technique called the disjoint LDT&lt;br /&gt;
space. I won't attempt a general introduction to the segmented architecture&lt;br /&gt;
of the 80286, but a brief summary is in order. Each process in 80286&lt;br /&gt;
protect mode can have a maximum of 16,383 segments. These segments are&lt;br /&gt;
described in two tables: the LDT (Local Descriptor Table) and the GDT&lt;br /&gt;
(Global Descriptor Table). An application can't read from or write to these&lt;br /&gt;
tables. OS/2 manages them, and the 80286 microprocessor uses their contents&lt;br /&gt;
when a process loads selectors into its segment registers.&lt;br /&gt;
     In practice, the GDT is not used for application segments, which&lt;br /&gt;
leaves the LDT 8192 segments--or, more precisely, 8192 segment selectors,&lt;br /&gt;
which OS/2 can set up to point to memory segments. The 80286 does not&lt;br /&gt;
support efficient position-independent code, so 80286 programs contain&lt;br /&gt;
within them, as part of the instruction stream, the particular segment&lt;br /&gt;
selector needed to access a particular memory location, as well as an&lt;br /&gt;
offset within that segment. This applies to both code and data&lt;br /&gt;
references.&lt;br /&gt;
     When OS/2 loads a program into memory, the .EXE file describes the&lt;br /&gt;
number, type, and size of the program's segments. OS/2 creates these&lt;br /&gt;
segments and allocates a selector for each from the 8192 possible LDT&lt;br /&gt;
selectors. There isn't any conflict with other processes in the system, at&lt;br /&gt;
this point, because each process has its own LDT and its own private set of&lt;br /&gt;
8192 LDT selectors. After OS/2 chooses a selector for each segment, both&lt;br /&gt;
code and data, it uses a table of addresses provided in the .EXE file to&lt;br /&gt;
relocate each segment reference in the program, changing the place holder&lt;br /&gt;
value put there by the linker into the proper segment selector value. OS/2&lt;br /&gt;
never combines or splits segments, so it never has to relocate the offset&lt;br /&gt;
part of addresses, only the segment parts. Address offsets are more common&lt;br /&gt;
than segment references. Because the segment references are relatively few,&lt;br /&gt;
this relocation process is not very time-consuming.&lt;br /&gt;
     If OS/2 discovers that the process that it's loading references a&lt;br /&gt;
dynlink routine--say, our old friend Foo--the situation is more complex.&lt;br /&gt;
For example, suppose that the process isn't the first caller of Foo; Foo is&lt;br /&gt;
already in memory and already relocated to some particular LDT slots in the&lt;br /&gt;
LDT of the earlier client of Foo. OS/2 has to fill in those same slots in&lt;br /&gt;
the new process's LDT with pointers to Foo; it can't assign different LDT&lt;br /&gt;
slots because Foo's code and data have already been relocated to the&lt;br /&gt;
earlier process's slots. If the new process is already using Foo's slot&lt;br /&gt;
numbers for something else, then we are in trouble. This is a problem with&lt;br /&gt;
all of Foo's segments, both data segments and code segments.&lt;br /&gt;
     This is where the disjoint LDT space comes in. OS/2 reserves many of&lt;br /&gt;
each process's LDT slots&lt;br /&gt;
5. In version 1.0, more than half the LDT slots are reserved for&lt;br /&gt;
this disjoint area.&lt;br /&gt;
5 for the disjoint space. The same slot numbers are&lt;br /&gt;
reserved in every process's LDT. When OS/2 allocates an LDT selector for a&lt;br /&gt;
memory segment that may be shared between processes, it allocates an entry&lt;br /&gt;
from the disjoint LDT space. After a selector is allocated, that same slot&lt;br /&gt;
in all other LDTs in the system is reserved. The slot either remains empty&lt;br /&gt;
(that is, invalid) or points to this shared segment; it can have no other&lt;br /&gt;
use. This guarantees that a process that has been running for hours and&lt;br /&gt;
that has created dozens of segments can still call DosLoadModule to get&lt;br /&gt;
access to a dynlink routine; OS/2 will find that the proper slots in this&lt;br /&gt;
process's LDT are ready and waiting. The disjoint LDT space is used for all&lt;br /&gt;
shared memory objects, not just dynlink routines. Shared memory data&lt;br /&gt;
segments are also allocated from the disjoint LDT space. A process's code&lt;br /&gt;
segments are not allocated in the disjoint LDT space, yet they can still be&lt;br /&gt;
shared.&lt;br /&gt;
6. The sharing of pure segments between multiple copies of the&lt;br /&gt;
same program is established when the duplicate copies are loaded.&lt;br /&gt;
OS/2 will use the same selector to do segment mapping as it did&lt;br /&gt;
when it loaded the first copy, so these segments can be shared&lt;br /&gt;
even though their selectors are not in the disjoint space.&lt;br /&gt;
6 Figure 7-4 illustrates the disjoint LDT concept. Bullets in the&lt;br /&gt;
shaded selectors denote reserved but invalid disjoint selectors. These are&lt;br /&gt;
reserved in case that process later requests access to the shared memory&lt;br /&gt;
segments that were assigned those disjoint slots. Only process A is using&lt;br /&gt;
the dynlink package DLX, so its assigned disjoint LDT slots are reserved&lt;br /&gt;
for it in Process B's LDT as well as in the LDT of all other processes in&lt;br /&gt;
the system. Both processes are using the dynlink package DLY.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 Process A                         Process B&lt;br /&gt;
Segment table                     Segment table&lt;br /&gt;
 (LDT) for A                       (LDT) for B&lt;br /&gt;
  ÚÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄ¿ Process    ÚÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³       ÃÄÄÄ´        ³ A's        ³       ³ ÚÄ´        ³ Process&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ segments   ÃÄÄÄÄÄÄÄ´ ³ ³        ³ B's&lt;br /&gt;
  ³°°°°°°°³   ÀÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄ¿ ³°°°°°°°³ ³ ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ÚÄÄÄÄÄÄÄÄÄÄÄÄ´        ³ ÃÄÄÄÄÄÄÄ´ ³            ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³       ³ ³            ³        ³ ³       ÃÄÙ            ³        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³ ÚÄÄÄÄÄÄÄÄ¿ ÀÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄ´    ÚÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ³°°°°°°°³ ³Ú´        ³            ³°°°°°°°³    ³         ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³³³        ³            ÃÄÄÄÄÄÄÄ´    ³&lt;br /&gt;
  ³       ÃÄÙ³ÀÄÄÄÄÄÄÄÄÙ            ³       ÃÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´  ³                      ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°³  ³                      ³°°°°°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´  ³                      ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ÃÄÄÙ                      ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°�°°°³                         ³°°°°°°°ÃÄÄÄ´        ³ Dynlink&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´   ³        ³ DLZ's&lt;br /&gt;
  ³       ³                         ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°�°°°³                         ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´              ³        ³&lt;br /&gt;
  ³       ³                         ³       ³              ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿ Dynlink    ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄ´        ³ DLX's      ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ segments   ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³   ÀÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄ¿ ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´       ÚÄÄÄÄÄÄ´        ³ ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄÄÄÄÄÙ      ³        ³ ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿ ÀÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³ ÚÄ´        ³            ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³ ³        ³            ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÙ ÀÄÄÄÄÄÄÄÄÙ            ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³                         ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°³                         ³°°°°°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³                         ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿            ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄ´        ³ Dynlink    ³°°°°°°°ÃÄÄÄ´        ³ Dynlink&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ DLY's      ÃÄÄÄÄÄÄÄ´   ³        ³ DLY's&lt;br /&gt;
  ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments   ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿ ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³ ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´              ³        ³ ÃÄÄÄÄÄÄÄ´              ³        ³&lt;br /&gt;
  ³       ³              ÀÄÄÄÄÄÄÄÄÙ ³       ³              ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÀÄÄÄÄÄÄÄÙ                         ÀÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-4.  The disjoint LDT space.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5.1  Instance Data&lt;br /&gt;
OS/2 supports two types of data segments for dynlink routines--instance&lt;br /&gt;
and global. Instance data segments hold data specific to each instance of&lt;br /&gt;
the dynlink routine. In other words, a dynlink routine has a separate set&lt;br /&gt;
of instance data segments for each process using it. The dynlink code has&lt;br /&gt;
no difficulty addressing its data; the code can reference the data segment&lt;br /&gt;
selectors as immediate values. The linker and OS/2's loader conspire so&lt;br /&gt;
that the proper selector value is in place when the code executes.&lt;br /&gt;
     The use of instance data segments is nearly invisible both to the&lt;br /&gt;
client process and to the dynlink code. The client process simply calls the&lt;br /&gt;
dynlink routine, totally unaffected by the presence or absence of the&lt;br /&gt;
routine's instance data segment(s). A dynlink routine can even return&lt;br /&gt;
addresses of items in its data segments to the client process. The client&lt;br /&gt;
cannot distinguish between a dynlink routine and a statically linked one.&lt;br /&gt;
Likewise, the code that makes up the dynlink routine doesn't need to do&lt;br /&gt;
anything special to use its instance data segments. The dynlink code was&lt;br /&gt;
assembled or compiled with its static data in one or more segments; the&lt;br /&gt;
code itself references those segments normally. The linker and OS/2 handle&lt;br /&gt;
all details of allocating the disjoint LDT selectors, loading the segments,&lt;br /&gt;
fixing up the references, and so on.&lt;br /&gt;
     A dynlink routine that uses only instance data segments (or no data&lt;br /&gt;
segments at all) can be written as a single client package, as would be a&lt;br /&gt;
statically linked subroutine. Although such a dynlink routine may have&lt;br /&gt;
multiple clients, the presence of multiple clients is invisible to the&lt;br /&gt;
routine itself. Each client has a separate copy of the instance data&lt;br /&gt;
segment(s). When a new client is created, OS/2 loads virgin copies of the&lt;br /&gt;
instance data segments from the .DLL file. The fact that OS/2 is sharing&lt;br /&gt;
the pure code segments of the routine has no effect on the operation of the&lt;br /&gt;
routine itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5.2  Global Data&lt;br /&gt;
The second form of data segment available to a dynlink routine is a global&lt;br /&gt;
data segment. A global data segment, as the name implies, is not duplicated&lt;br /&gt;
for each client process. There is only one copy of each dynlink module's&lt;br /&gt;
global data segment(s); each client process is given shared access to that&lt;br /&gt;
segment. The segment is loaded only once--when the dynlink package is first&lt;br /&gt;
brought into memory to be linked with its first client process. Global data&lt;br /&gt;
segments allow a dynlink routine to be explicitly aware of its multiple&lt;br /&gt;
clients because changes to a global segment made by calls from one client&lt;br /&gt;
process are visible to the dynlink code when called from another client&lt;br /&gt;
process. Global data segments are provided to support subsystems, which are&lt;br /&gt;
discussed later. Figure 7-5 illustrates a dynlink routine with both&lt;br /&gt;
instance and global data segments.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³                                             ³&lt;br /&gt;
³               Code Segment(s)               ³&lt;br /&gt;
³                                             ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³              ³              ÚÁÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³&lt;br /&gt;
³    Global    ³             ÚÁÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³³&lt;br /&gt;
³     data     ³             ³              ³³³&lt;br /&gt;
³  segment(s)  ³             ³   Instance   ³³³&lt;br /&gt;
³              ³             ³     data     ³³³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ             ³  segment(s)  ³ÃÙ&lt;br /&gt;
                             ³              ÃÙ&lt;br /&gt;
                             ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-5.  Dynlink segments.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.6  Dynamic Link Packages As Subroutines&lt;br /&gt;
&lt;br /&gt;
Dynamic link subroutines (or packages) generally fall into two categories--&lt;br /&gt;
subroutines and subsystems. As we discussed earlier, a dynamic link&lt;br /&gt;
subroutine is written and executes in much the same way as a statically&lt;br /&gt;
linked subroutine. The only difference is in the preparation of the dynamic&lt;br /&gt;
link library file, which contains the actual subroutines, and in the&lt;br /&gt;
preparation of the special .OBJ file, to which client programs can link.&lt;br /&gt;
During execution, both the dynlink routines and the client routines can use&lt;br /&gt;
their own static data freely, and they can pass pointers to their data&lt;br /&gt;
areas back and forth to each other. The only difference between static&lt;br /&gt;
linking and dynamic linking, in this model, is that the dynlink routine&lt;br /&gt;
cannot reference any external symbols that the client code defines, nor can&lt;br /&gt;
the client externally reference any dynlink package symbols other than the&lt;br /&gt;
module entry points. Figure 7-6 illustrates a dynamic link routine being&lt;br /&gt;
used as a subroutine. The execution environment is nearly identical to that&lt;br /&gt;
of a traditional statically linked subroutine; the client and the&lt;br /&gt;
subroutine each reference their own static data areas, all of which are&lt;br /&gt;
contained in the process's address space. Note that a dynlink package can&lt;br /&gt;
reference the application's data and the application can reference the&lt;br /&gt;
dynlink package's data, but only if the application or the dynlink package&lt;br /&gt;
passes a pointer to its data to the other.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                         Process address space&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³            far calls                           far calls            ³&lt;br /&gt;
³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ far ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³&lt;br /&gt;
³ ³            ÃÄÄ�³            ³calls³  Dynlink   ÃÄÄ�³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP code  ³   ³  APP code  ÃÄÄÄÄ�³   code     ³   ³   code     ³ ³&lt;br /&gt;
³ ³ segment #1 ³�ÄÄ´ segment #n ³     ³ segment #1 ³�ÄÄ´ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ     ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ ³&lt;br /&gt;
³    ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³           ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³    ³&lt;br /&gt;
³    ³     ³ ³     references³           ³     ³ ³     references³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³           ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³    ³&lt;br /&gt;
³ ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÚÄÄÄ�ÄÄÄÄÄ�ÄÄ¿     ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÚÄÄÄ�ÄÄÄÄÄ�ÄÄ¿ ³&lt;br /&gt;
³ ³            ³   ³            ³     ³  Dynlink   ³   ³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP data  ³   ³  APP data  ³     ³   data     ³   ³   data     ³ ³&lt;br /&gt;
³ ³ segment #1 ³   ³ segment #n ³     ³ segment #1 ³   ³ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-6.  Dynamic link routines as subroutines.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.7  Subsystems&lt;br /&gt;
&lt;br /&gt;
The term dynlink subsystems refers to the design and intended function of a&lt;br /&gt;
particular style of dynlink package and is somewhat artificial. Although&lt;br /&gt;
OS/2 provides special features to help support subsystems, OS/2 does not&lt;br /&gt;
actually classify dynlink modules as subroutines or subsystems; subsystem&lt;br /&gt;
is merely a descriptive term.&lt;br /&gt;
     The term subsystem refers to a dynlink module that provides a set of&lt;br /&gt;
services built around a resource.&lt;br /&gt;
7. In the most general sense of the word. I don't mean a&lt;br /&gt;
&amp;quot;presentation manager resource object.&amp;quot;&lt;br /&gt;
7 For example, OS/2's VIO dynlink entry&lt;br /&gt;
points are considered a dynlink subsystem because they provide a set of&lt;br /&gt;
services to manage the display screen. A subsystem usually has to manage a&lt;br /&gt;
limited resource for an effectively unlimited number of clients; VIO does&lt;br /&gt;
this, managing a single physical display controller and a small number of&lt;br /&gt;
screen groups for an indefinite number of clients.&lt;br /&gt;
     Because subsystems generally manage a limited resource, they have one&lt;br /&gt;
or more global data segments that they use to keep information about the&lt;br /&gt;
state of the resource they're controlling; they also have buffers, flags,&lt;br /&gt;
semaphores, and so on. Per-client work areas are generally kept in instance&lt;br /&gt;
data segments; it's best to reserve the global data segment(s) for global&lt;br /&gt;
information. Figure 7-7 illustrates a dynamic link routine being used as a&lt;br /&gt;
subsystem. A dynlink subsystem differs from a dynlink being used as a&lt;br /&gt;
subroutine only by the addition of a static data segment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                         Process address space&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³            far calls                           far calls            ³&lt;br /&gt;
³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ far ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³&lt;br /&gt;
³ ³            ÃÄÄ�³            ³calls³  Dynlink   ÃÄÄ�³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP code  ³   ³  APP code  ÃÄÄÄÄ�³   code     ³   ³   code     ³ ³&lt;br /&gt;
³ ³ segment #1 ³�ÄÄ´ segment #n ³     ³ segment #1 ³�ÄÄ´ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ     ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ ³&lt;br /&gt;
³    ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³           ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³    ³&lt;br /&gt;
³    ³     ³ ³     references³           ³     ³ ³     references³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³           ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³    ³&lt;br /&gt;
³ ÚÍÍ�ÍÍÍÍÍ�ÍÍÍ»   ÚÍÍÍ�ÍÍÍÍÍ�ÍÍ»     ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÖÄÄÄÁÄÄÄÄÄÁÄÄ¿ ³&lt;br /&gt;
³ ³            º   ³            º     ³  global    ³   º instance   ³ ³&lt;br /&gt;
³ ³  APP data  º   ³  APP data  º     ³   data     ³   º   data     ³ ³&lt;br /&gt;
³ ³ segment #1 º   ³ segment #n º     ³ segment    ³   º segment    ³ ³&lt;br /&gt;
³ ÀÄÄÄÄÄÄÄÄÄÄÄÄ½   ÀÄÄÄÄÄÄÄÄÄÄÄÄ½     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÈÍÍÍÍÍÍÍÍÍÍÍÍÙ ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-7.  Dynamic link routines as subsystems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.7.1  Special Subsystem Support&lt;br /&gt;
Two OS/2 features are particularly valuable to subsystems: global data&lt;br /&gt;
segments (which we've already discussed) and special client initialization&lt;br /&gt;
and termination support. Clearly, if a subsystem is going to manage a&lt;br /&gt;
resource, keeping track of its clients in a global data segment, it needs&lt;br /&gt;
to know when new clients arrive and when old clients terminate. The simple&lt;br /&gt;
dynlink subroutine model doesn't provide this information in a reliable&lt;br /&gt;
fashion. A subsystem undoubtedly has initialize and terminate entry points,&lt;br /&gt;
but client programs may terminate without having called a subsystem's&lt;br /&gt;
terminate entry point. Such a failure may be an error on the part of the&lt;br /&gt;
client, but the system architecture decrees that errors should be&lt;br /&gt;
localized; it's not acceptable for a bug in a client process to be able to&lt;br /&gt;
hang up a subsystem and thus all its clients as well.&lt;br /&gt;
     The two forms of subsystem initialization are global and instance. A&lt;br /&gt;
subsystem can specify either service but not both. If global initialization&lt;br /&gt;
is specified, the initialization entry point is called only once per&lt;br /&gt;
activation of the subsystem. When the subsystem dynlink package is first&lt;br /&gt;
referenced, OS/2 allocates the subsystem's global data segment(s), taking&lt;br /&gt;
their initial values from the .DLL file. OS/2 then calls the subsystem's&lt;br /&gt;
global initialization entry point so that the module can do its one-time&lt;br /&gt;
initialization. The thread that is used to call the initialization entry&lt;br /&gt;
point belongs to that first client process,&lt;br /&gt;
8. The client process doesn't explicitly call a dynlink package's&lt;br /&gt;
initialization entry points. OS/2 uses its godlike powers to&lt;br /&gt;
borrow a thread for the purpose. The mechanism is invisible to the&lt;br /&gt;
client program. It goes without saying, we hope, that it would be&lt;br /&gt;
extremely rude to the client process, not to say damaging, were&lt;br /&gt;
the dynlink package to refuse to return that initialization thread&lt;br /&gt;
or if it were to damage it in some way, such as lowering its&lt;br /&gt;
priority or calling DosExit with it!&lt;br /&gt;
8 so the first client's instance&lt;br /&gt;
data segments are also set up and may be used by the global initialization&lt;br /&gt;
process. This means that although the dynlink subsystem is free to open&lt;br /&gt;
files, read and write their contents, and close them again, it may not open&lt;br /&gt;
a handle to a file, store the handle number in a global data segment, and&lt;br /&gt;
expect to use that handle in the future.&lt;br /&gt;
     Remember, subsystems don't own resources; processes own resources.&lt;br /&gt;
When a dynlink package opens a file, that file is open only for that one&lt;br /&gt;
client process. That handle has meaning only when that particular client is&lt;br /&gt;
calling the subsystem code. If a dynlink package were to store process A's&lt;br /&gt;
handle number in a global data segment and then attempt to do a read from&lt;br /&gt;
that handle when running as process B, at best the read would fail with&lt;br /&gt;
&amp;quot;invalid handle&amp;quot;; at worst some unrelated file of B's would be molested.&lt;br /&gt;
And, of course, when client process A eventually terminates, the handle&lt;br /&gt;
becomes invalid for all clients.&lt;br /&gt;
     The second form of initialization is instance initialization. The&lt;br /&gt;
instance initialization entry point is called in the same way as the global&lt;br /&gt;
initialization entry point except that it is called for every new client&lt;br /&gt;
when that client first attaches to the dynlink package. Any instance data&lt;br /&gt;
segments that exist will already be allocated and will have been given&lt;br /&gt;
their initial values from the .DLL file. The initialization entry point for&lt;br /&gt;
a loadtime dynlink is called before the client's code begins executing. The&lt;br /&gt;
initialization entry point for a runtime dynlink is called when the client&lt;br /&gt;
calls the DosLoadModule function. A dynlink package may not specify both&lt;br /&gt;
global and instance initialization; if it desires both, it should specify&lt;br /&gt;
instance initialization and use a counter in one of its global data&lt;br /&gt;
segments to detect the first instance initialization.&lt;br /&gt;
     Even more important than initialization control is termination&lt;br /&gt;
control. In its global data area, a subsystem may have records, buffers, or&lt;br /&gt;
semaphores on behalf of a client process. It may have queued-up requests&lt;br /&gt;
from that client that it needs to purge when the client terminates. The&lt;br /&gt;
dynlink package need not release instance data segments; because these&lt;br /&gt;
belong to the client process, they are destroyed when the client&lt;br /&gt;
terminates. The global data segments themselves are released if this is the&lt;br /&gt;
dynlink module's last client, so the module may want to take this last&lt;br /&gt;
chance to update a log file, release a system semaphore, and so on.&lt;br /&gt;
     Because a dynlink routine runs as the calling client process, it could&lt;br /&gt;
use DosSetSigHandler to intercept the termination signal. This should never&lt;br /&gt;
be done, however, because the termination signal is not activated for all&lt;br /&gt;
causes of process termination. For example, if the process calls DosExit,&lt;br /&gt;
the termination signal is not sent. Furthermore, there can be only one&lt;br /&gt;
handler per signal type per process. Because client processes don't and&lt;br /&gt;
shouldn't know what goes on inside a dynlink routine, the client process&lt;br /&gt;
and a dynlink routine may conflict in the use of the signal. Such a&lt;br /&gt;
conflict may also occur between two dynlink packages.&lt;br /&gt;
     Using DosExitList service prevents such a collision. DosExitList&lt;br /&gt;
allows a process to specify one or more subroutine addresses that will be&lt;br /&gt;
called when the process terminates. Addresses can be added to and removed&lt;br /&gt;
from the list. DosExitList is ideally suited for termination control. There&lt;br /&gt;
can be many such addresses, and the addresses are called under all&lt;br /&gt;
termination conditions. Both the client process and the subsystem dynlinks&lt;br /&gt;
that it calls can have their own termination routine or routines.&lt;br /&gt;
DosExitList is discussed in more detail in 16.2 Data Integrity.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.8  Dynamic Links As Interfaces to Other Processes&lt;br /&gt;
&lt;br /&gt;
Earlier, I mentioned that dynlink subsystems have difficulty dealing with&lt;br /&gt;
resources--other than global memory--because resource ownership and access&lt;br /&gt;
are on a per-process basis. Life as a dynlink subsystem can be&lt;br /&gt;
schizophrenic. Which files are open, which semaphores are owned and so on&lt;br /&gt;
depends on which client is running your code at the moment. Global memory&lt;br /&gt;
is different; it's the one resource that all clients own jointly. The&lt;br /&gt;
memory remains as long as the client count doesn't go to zero.&lt;br /&gt;
     One way to deal with resource issues is for a dynlink package to act&lt;br /&gt;
as a front end for a server process. During module initialization, the&lt;br /&gt;
dynlink module can check a system semaphore to see whether the server&lt;br /&gt;
process is already running and, if not, start it up. It needs to do this&lt;br /&gt;
with the &amp;quot;detach&amp;quot; form of DosExecPgm so that the server process doesn't&lt;br /&gt;
appear to the system as a child of the subsystem's first client. Such a&lt;br /&gt;
mistake could mean that the client's parent thinks that the command subtree&lt;br /&gt;
it founded by running the client never terminates because the server&lt;br /&gt;
process appears to be part of the command subtree (see Figure 7-8).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
   ³   Grandparent   ³       ³   Grandparent   ³&lt;br /&gt;
   ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
            ³                         ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿      ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³°°°°°°°°°°°³°°°°°°°°°°°³ ³°°°°°°°°°°°³°°°°°°°°°°°³   Ú �³  Daemon  ³&lt;br /&gt;
³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³ ³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³   |  ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³°°³     Process     ³°°³ ³°°³     Process     ³Ä Å Ä Ù   &lt;br /&gt;
³°°ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ°°³ ³°°ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ°°³&lt;br /&gt;
³°°°°°°°°°°°³°°°°°°°°°°°³ ³°°° Command subtree °°°³&lt;br /&gt;
³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°³      Daemon     ³°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°° Command subtree °°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°°°°°°°°°°°°°°°°°°°°°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-8.  Dynlink daemon initiation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When the server process is running, the dynlink subsystem can forward&lt;br /&gt;
some or all requests to it by one of the many IPC facilities. For example,&lt;br /&gt;
a database subsystem might want to use a dedicated server process to hold&lt;br /&gt;
open the database file and do reads and writes to it. It might keep buffers&lt;br /&gt;
and ISAM directories in a shared memory segment to which the dynlink&lt;br /&gt;
subsystem requests access for each of its clients; then requests that can&lt;br /&gt;
be satisfied by data from these buffers won't require the IPC to the server&lt;br /&gt;
process.&lt;br /&gt;
     The only function of some dynlink packages is to act as a procedural&lt;br /&gt;
interface to another process. For example, a spreadsheet program might&lt;br /&gt;
provide an interface through which other applications can retrieve data&lt;br /&gt;
values from a spreadsheet. The best way to do this is for the spreadsheet&lt;br /&gt;
package to contain a dynamic link library that provides clients a&lt;br /&gt;
procedural interface to the spreadsheet process. The library routine itself&lt;br /&gt;
will invoke a noninteractive copy (perhaps a special subset .EXE) of the&lt;br /&gt;
spreadsheet to recover the information, passing it back to the client via&lt;br /&gt;
IPC. Alternatively, the retrieval code that understands the spreadsheet&lt;br /&gt;
data formats could be in the dynlink package itself because that package&lt;br /&gt;
ships with the spreadsheet and will be upgraded when the spreadsheet is. In&lt;br /&gt;
this case, the spreadsheet itself could use the package instead of&lt;br /&gt;
duplicating the functionality in its own .EXE file. In any case, the&lt;br /&gt;
implementation details are hidden from the client process; the client&lt;br /&gt;
process simply makes a procedure call that returns the desired data.&lt;br /&gt;
     Viewed from the highest level, this arrangement is simple: A client&lt;br /&gt;
process uses IPC to get service from a server process via a subroutine&lt;br /&gt;
library. From the programmer's point of view, though, the entire mechanism&lt;br /&gt;
is encapsulated in the dynlink subsystem's interface. A future upgrade to&lt;br /&gt;
the dynlink package may use an improved server process and different forms&lt;br /&gt;
of IPC to talk to it but retain full binary compatibility with the existing&lt;br /&gt;
client base. Figure 7-9 illustrates a dynlink package being used as an&lt;br /&gt;
interface to a daemon process. The figure shows the dynlink package&lt;br /&gt;
interfacing with the daemon process by means of a shared memory segment and&lt;br /&gt;
some other form of IPC, perhaps a named pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                                   client process   |   daemon process&lt;br /&gt;
                                                    |&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿    Far call     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿       |        ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³    APP     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³  Dynlink   ³�ÄÄÄÄÄÄ|ÄÄÄÄÄÄÄÄ´   Daemon   ³&lt;br /&gt;
³    code    ³                 ³    code    ³      IPC       ³    code    ³&lt;br /&gt;
³ segment(s) ³                 ³ segment(s) ÃÄÄÄÄÄÄÄ|ÄÄÄÄÄÄÄ�³ segment(s) ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÂÄÄÄÂÄÄÙ       |        ÀÄÄÂÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
      ³                              ³   ³          |           ³   ³&lt;br /&gt;
      ³                              ³   ÀÄÄÄÄÄÄÄ¿  |   ÚÄÄÄÄÄÄÄÙ   ³&lt;br /&gt;
      ³                              ³           ³  |   ³           ³&lt;br /&gt;
ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿                 ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿ ÚÄÄ�ÄÄ|ÄÄÄ�ÄÄ¿ ÚÄÄÄÄÄÄ�ÄÄÄÄÄ¿&lt;br /&gt;
³    APP     ³                 ³  Dynlink   ³ ³     |      ³ ³   Daemon   ³&lt;br /&gt;
³    data    ³                 ³    data    ³ ³   Shared   ³ ³    data    ³&lt;br /&gt;
³ segment(s) ³                 ³ segment(s) ³ ³   memory   ³ ³ segment(s) ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄ|ÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                                    |&lt;br /&gt;
&lt;br /&gt;
Figure 7-9.  Dynamic link routines as daemon interfaces.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.9  Dynamic Links As Interfaces to the Kernel&lt;br /&gt;
&lt;br /&gt;
We've seen how dynlink libraries can serve as simple subroutine libraries,&lt;br /&gt;
how they can serve as subsystems, and how they can serve as interfaces to&lt;br /&gt;
other processes. OS/2 has one more trick up its sleeve: Dynlink libraries&lt;br /&gt;
can also serve as interfaces to OS/2 itself.&lt;br /&gt;
     Some OS/2 calls are actually implemented as simple library routines.&lt;br /&gt;
For example, DosErrClass is implemented in OS/2 version 1.0 as a simple&lt;br /&gt;
library routine. It takes an error code and locates, in a table, an&lt;br /&gt;
explanatory text string, an error classification, and a recommended action.&lt;br /&gt;
Services such as these were traditionally part of the kernel of operating&lt;br /&gt;
systems, not because they needed to use privileged instructions, but&lt;br /&gt;
because their error tables needed to be changed each time an upgrade to the&lt;br /&gt;
operating system was released. If the service has been provided as a&lt;br /&gt;
statically linked subroutine, older applications running on newer releases&lt;br /&gt;
would receive new error codes that would not be in the library code's&lt;br /&gt;
tables.&lt;br /&gt;
     Although OS/2 implements DosErrClass as a library routine, it's a&lt;br /&gt;
dynlink library routine, and the .DLL file is bundled with the operating&lt;br /&gt;
system itself. Any later release of the system will contain an upgraded&lt;br /&gt;
version of the DosErrClass routine, one that knows about new error codes.&lt;br /&gt;
Consequently, the dynlink facility provides OS/2 with a great deal of&lt;br /&gt;
flexibility in packaging its functionality.&lt;br /&gt;
     Some functions, such as &amp;quot;open file&amp;quot; or &amp;quot;allocate memory,&amp;quot; can't be&lt;br /&gt;
implemented as ordinary subroutines. They need access to key internal data&lt;br /&gt;
structures, and these structures are of course protected so that they can't&lt;br /&gt;
be changed by unprivileged code. To get these services, the processor must&lt;br /&gt;
make a system call, entering the kernel code in a very controlled fashion&lt;br /&gt;
and there running with sufficient privilege to do its work. This privilege&lt;br /&gt;
transition is via a call gate--a feature of the 80286/80386 hardware. A&lt;br /&gt;
program calls a call gate exactly as it performs an ordinary far call;&lt;br /&gt;
special flags in the GDT and LDT tell the processor that this is a call&lt;br /&gt;
gate rather than a regular call.&lt;br /&gt;
     In OS/2, system calls are indistinguishable from ordinary dynlink&lt;br /&gt;
calls. All OS/2 system calls are defined in a dynlink module called&lt;br /&gt;
DosCalls. When OS/2 fixes up dynlink references to this module, it consults&lt;br /&gt;
a special table, built into OS/2, of resident functions. If the function is&lt;br /&gt;
not listed in this table, then an ordinary dynlink is set up. If the&lt;br /&gt;
function is in the table, OS/2 sets up a call gate call in place of the&lt;br /&gt;
ordinary dynlink call. The transparency between library and call gate&lt;br /&gt;
functions explains why passing an invalid address to an OS/2 system call&lt;br /&gt;
causes the calling process to GP fault. Because the OS/2 kernel code&lt;br /&gt;
controls and manages the GP fault mechanism, OS/2 calls that are call gates&lt;br /&gt;
could easily return an error code if an invalid address causes a GP fault.&lt;br /&gt;
If this were done, however, the behavior of OS/2 calls would differ&lt;br /&gt;
depending on their implementation: Dynlink entry points would GP fault for&lt;br /&gt;
invalid addresses;&lt;br /&gt;
9. The LAR and LSL instructions are not sufficient to prevent&lt;br /&gt;
this because another thread in that process may free a segment&lt;br /&gt;
after the LAR but before the reference.&lt;br /&gt;
9 call gate entries would return an error code. OS/2&lt;br /&gt;
prevents this dichotomy and preserves its freedom to, in future releases,&lt;br /&gt;
move function between dynlink and call gate entries by providing a uniform&lt;br /&gt;
reaction to invalid addresses. Because non-call-gate dynlink routines must&lt;br /&gt;
generate GP faults, call gate routines produce them as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.10  The Architectural Role of Dynamic Links&lt;br /&gt;
&lt;br /&gt;
Dynamic links play three major roles in OS/2: They provide the system&lt;br /&gt;
interface; they provide a high-bandwidth device interface; and they support&lt;br /&gt;
open architecture nonkernel service packages.&lt;br /&gt;
     The role of dynamic links as the system interface is clear. They&lt;br /&gt;
provide a uniform, high-efficiency interface to the system kernel as well&lt;br /&gt;
as a variety of nonkernel services. The interface is directly compatible&lt;br /&gt;
with high-level languages, and it takes advantage of special speed-&lt;br /&gt;
enhancing features of the 80286 and 80386 microprocessors.&lt;br /&gt;
10. Specifically, automatic argument passing on calls to the ring&lt;br /&gt;
0 kernel code.&lt;br /&gt;
10 It provides a&lt;br /&gt;
wide and convenient name space, and it allows the distribution of function&lt;br /&gt;
between library code and kernel code. Finally, it provides an essentially&lt;br /&gt;
unlimited expansion capability.&lt;br /&gt;
     But dynamic links do much more than act as system calls. You'll recall&lt;br /&gt;
that in the opening chapters I expressed a need for a device interface that&lt;br /&gt;
was as device independent as device drivers but without their attendant&lt;br /&gt;
overhead. Dynamic links provide this interface because they allow&lt;br /&gt;
applications to make a high-speed call to a subroutine package that can&lt;br /&gt;
directly manipulate the device (see Chapter 18, I/O Privilege Mechanism&lt;br /&gt;
and Debugging/Ptrace). The call itself is fast, and the package can specify&lt;br /&gt;
an arbitrarily wide set of parameters. No privilege or ring transition is&lt;br /&gt;
needed, and the dynlink package can directly access its client's data&lt;br /&gt;
areas. Finally, the dynlink package can use subsystem support features to&lt;br /&gt;
virtualize the device or to referee its use among multiple clients. Device&lt;br /&gt;
independence is provided because a new version of the dynlink interface can&lt;br /&gt;
be installed whenever new hardware is installed. VIO and the presentation&lt;br /&gt;
manager are examples of this kind of dynlink use. Dynlink packages have an&lt;br /&gt;
important drawback when they are being used as device driver replacements:&lt;br /&gt;
They cannot receive hardware interrupts. Some devices, such as video&lt;br /&gt;
displays, do not generate interrupts. Interrupt-driven devices, though,&lt;br /&gt;
require a true device driver. That driver can contain all of the device&lt;br /&gt;
interface function, or the work can be split between a device driver and a&lt;br /&gt;
dynlink package that acts as a front end for that device driver. See&lt;br /&gt;
Chapters 17 and 18 for further discussion of this.&lt;br /&gt;
     Dynlink routines can also act as nonkernel service packages--as an&lt;br /&gt;
open system architecture for software. Most operating systems&lt;br /&gt;
are like the early versions of the Apple Macintosh computer: They are&lt;br /&gt;
closed systems; only their creators can add features to them. Because of&lt;br /&gt;
OS/2's open system architecture, third parties and end users can add system&lt;br /&gt;
services simply by plugging in dynlink modules, just as hardware cards plug&lt;br /&gt;
into an open hardware system. The analogy extends further: Some hardware&lt;br /&gt;
cards become so popular that their interface defines a standard. Examples&lt;br /&gt;
are the Hayes modem and the Hercules Graphics Card. Third-party dynlink&lt;br /&gt;
packages will, over time, establish similar standards. Vendors will offer,&lt;br /&gt;
for example, improved database dynlink routines that are advertised as plug&lt;br /&gt;
compatible with the standard database dynlink interface, but better,&lt;br /&gt;
cheaper, and faster.&lt;br /&gt;
     Dynlinks allow third parties to add interfaces to OS/2; they also&lt;br /&gt;
allow OS/2's developers to add future interfaces. The dynlink interface&lt;br /&gt;
model allows additional functionality to be implemented as subroutines or&lt;br /&gt;
processes or even to be distributed across a network environment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11  Implementation Details&lt;br /&gt;
&lt;br /&gt;
Although dynlink routines often act very much like traditional static&lt;br /&gt;
subroutines, a programmer must be aware of some special considerations&lt;br /&gt;
involved. This section discusses some issues that must be dealt with to&lt;br /&gt;
produce a good dynlink package.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.1  Dynlink Data Security&lt;br /&gt;
We have discussed how a dynlink package runs as a subroutine of the client&lt;br /&gt;
process and that the client process has access to the dynlink package's&lt;br /&gt;
instance and global data segments.&lt;br /&gt;
11. A client process has memory access (addressability) to all of&lt;br /&gt;
the package's global segments but only to those instance data&lt;br /&gt;
segments associated with that process.&lt;br /&gt;
11 This use of the dynlink interface is&lt;br /&gt;
efficient and thus advantageous, but it's also disadvantageous because&lt;br /&gt;
aberrant client processes can damage the dynlink package's global data&lt;br /&gt;
segments.&lt;br /&gt;
     In most circumstances, accidental damage to a dynlink package's data&lt;br /&gt;
segments is rare. Unless the dynlink package returns pointers into its data&lt;br /&gt;
segments to the client process, the client doesn't &amp;quot;know&amp;quot; the dynlink&lt;br /&gt;
package's data segment selectors. The only way such a process could access&lt;br /&gt;
the dynlink's segments would be to accidentally create a random selector&lt;br /&gt;
value that matched one belonging to a dynlink package. Because the&lt;br /&gt;
majority of selector values are illegal, a process would have to be&lt;br /&gt;
very &amp;quot;lucky&amp;quot; to generate a valid dynlink package data selector before it&lt;br /&gt;
generated an unused or code segment selector.&lt;br /&gt;
12.Because if a process generates and writes with a selector that&lt;br /&gt;
is invalid or points to a code segment, the process will be&lt;br /&gt;
terminated immediately with a GP fault.&lt;br /&gt;
12 Naturally, dynlink packages&lt;br /&gt;
shouldn't use global data segments to hold sensitive data because a&lt;br /&gt;
malicious application can figure out the proper selector values.&lt;br /&gt;
     The measures a programmer takes to deal with the security issue depend&lt;br /&gt;
on the nature and sensitivity of the dynlink package. Dynlink packages that&lt;br /&gt;
don't have global data segments are at no risk; an aberrant program can&lt;br /&gt;
damage its instance data segments and thereby fail to run correctly, but&lt;br /&gt;
that's the expected outcome of a program bug. A dynlink package with global&lt;br /&gt;
data segments can minimize the risk by never giving its callers pointers&lt;br /&gt;
into its (the dynlink package's) global data segment. If the amount of&lt;br /&gt;
global data is small and merely detecting damage is sufficient, the global&lt;br /&gt;
data segments could be checksummed.&lt;br /&gt;
     Finally, if accidental damage would be grave, a dynlink package can&lt;br /&gt;
work in conjunction with a special dedicated process, as described above.&lt;br /&gt;
The dedicated process can keep the sensitive data and provide it on a per-&lt;br /&gt;
client basis to the dynlink package in response to an IPC request. Because&lt;br /&gt;
the dedicated process is a separate process, its segments are fully&lt;br /&gt;
protected from the client process as well as from all others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.2  Dynlink Life, Death, and Sharing&lt;br /&gt;
Throughout this discussion, I have referred to sharing pure segments. The&lt;br /&gt;
ability to share pure segments is an optimization that OS/2 makes for all&lt;br /&gt;
memory segments whether they are dynlink segments or an application's .EXE&lt;br /&gt;
file segments. A pure segment is one that is never modified during its&lt;br /&gt;
lifetime. All code segments (except for those created by DosCreateCSAlias)&lt;br /&gt;
are pure; read-only data segments are also pure. When OS/2 notices that&lt;br /&gt;
it's going to load two copies of the same pure segment, it performs a&lt;br /&gt;
behind-the-scenes optimization and gives the second client access to the&lt;br /&gt;
earlier copy of the segment instead of wasting memory with a duplicate&lt;br /&gt;
version.&lt;br /&gt;
     For example, if two copies of a program are run, all code segments are&lt;br /&gt;
pure; at most, only one copy of each code segment will be in memory. OS/2&lt;br /&gt;
flags these segments as &amp;quot;internally shared&amp;quot; and doesn't release them until&lt;br /&gt;
the last user has finished with the segment. This is not the same as&lt;br /&gt;
&amp;quot;shared memory&amp;quot; as it is generally defined in OS/2. Because pure segments&lt;br /&gt;
can only be read, never written, no process can tell that pure segments are&lt;br /&gt;
being shared or be affected by that sharing. Although threads from two or&lt;br /&gt;
more processes may execute the same shared code segment at the same time,&lt;br /&gt;
this is not the same as a multithreaded process. Each copy of a program has&lt;br /&gt;
its own data areas, its own stack, its own file handles, and so on. They&lt;br /&gt;
are totally independent of one another even if OS/2 is quietly sharing&lt;br /&gt;
their pure code segments among them. Unlike multiple threads within a&lt;br /&gt;
single process, threads from different processes cannot affect one another;&lt;br /&gt;
the programmer can safely ignore their possible existence in shared code&lt;br /&gt;
segments.&lt;br /&gt;
     Because the pure segments of a dynlink package are shared, the second&lt;br /&gt;
and subsequent clients of a dynlink package can load much more quickly&lt;br /&gt;
(because these pure segments don't have to be loaded from the .DLL disk&lt;br /&gt;
file). This doesn't mean that OS/2 doesn't have to &amp;quot;hit the disk&amp;quot; at all:&lt;br /&gt;
Many dynlink packages use instance data segments, and OS/2 loads a fresh&lt;br /&gt;
copy of the initial values for these segments from the .DLL file.&lt;br /&gt;
     A dynlink package's second client is its second simultaneous client.&lt;br /&gt;
Under OS/2, only processes have a life of their own. Objects such as&lt;br /&gt;
dynlink packages and shared memory segments exist only as possessions of&lt;br /&gt;
processes. When the last client process of such an object dies or otherwise&lt;br /&gt;
releases the object, OS/2 destroys it and frees up the memory. For example,&lt;br /&gt;
when the first client (since bootup) of a dynlink package references it,&lt;br /&gt;
OS/2 loads the package's code and data segments. Then OS/2 calls the&lt;br /&gt;
package's initialization routine--if the package has one. OS/2 records in&lt;br /&gt;
an internal data structure that this dynlink package has one client. If&lt;br /&gt;
additional clients come along while the first is still using the dynlink&lt;br /&gt;
package, OS/2 increments the package's user count appropriately. Each time&lt;br /&gt;
a client disconnects or dies, the user count is decremented. As long as the&lt;br /&gt;
user count remains nonzero, the package remains in existence, each client&lt;br /&gt;
sharing the original global data segments. When the client count goes to&lt;br /&gt;
zero, OS/2 discards the dynlink package's code and global data segments and&lt;br /&gt;
in effect forgets all about the package. When another client comes along,&lt;br /&gt;
OS/2 reloads the package and reloads its global data segment as if the&lt;br /&gt;
earlier use had never occurred.&lt;br /&gt;
     This mechanism affects a dynlink package only in the management of the&lt;br /&gt;
package's global data segment. The package's code segments are pure, so it&lt;br /&gt;
doesn't matter if they are reloaded from the .DLL file. The instance data&lt;br /&gt;
segments are always reinitialized for each new client, but the data in a&lt;br /&gt;
package's global data segment remains in existence only as long as the&lt;br /&gt;
package has at least one client process. When the last client releases the&lt;br /&gt;
package, the global data segment is discarded. If this is a problem for a&lt;br /&gt;
dynlink package, an associated &amp;quot;dummy&amp;quot; process (which the dynlink package&lt;br /&gt;
could start during its loadtime initialization) can reference the dynlink&lt;br /&gt;
package. As long as this process stays alive, the dynlink package and its&lt;br /&gt;
global data segments stay alive.&lt;br /&gt;
13. If you use this technique, be sure to use the detached form&lt;br /&gt;
of DosExec; see the warning in 7.8 Dynamic Links As&lt;br /&gt;
Interfaces to Other Processes.&lt;br /&gt;
13&lt;br /&gt;
     An alternative is for the dynlink package to keep track of the count&lt;br /&gt;
of its clients and save the contents of its global data segments to a disk&lt;br /&gt;
file when the last client terminates, but this is tricky. Because a process&lt;br /&gt;
may fail to call a dynlink package's &amp;quot;I'm finished&amp;quot; entry point (presumably&lt;br /&gt;
part of the dynlink package's interface) before it terminates, the dynlink&lt;br /&gt;
package must get control to write its segment via DosExitList. If the&lt;br /&gt;
client process is connected to the dynlink package via DosLoadModule (that&lt;br /&gt;
is, via runtime dynamic linking), it cannot disconnect from the package via&lt;br /&gt;
DosFreeModule as long as a DosExitList address points into the dynlink&lt;br /&gt;
package. An attempt to do so returns an error code. Typically, one would&lt;br /&gt;
expect the application to ignore this error code; but because the dynlink&lt;br /&gt;
package is still attached to the client process, it will receive&lt;br /&gt;
DosExitList service when the client eventually terminates. It's important&lt;br /&gt;
that dynlink packages which maintain client state information and therefore&lt;br /&gt;
need DosExitList also offer an &amp;quot;I'm finished&amp;quot; function. When a client calls&lt;br /&gt;
this function, the package should close it out and then remove its&lt;br /&gt;
processing address from DosExitList so that DosFreeModule can take effect&lt;br /&gt;
if the client wishes.&lt;br /&gt;
     Note that OS/2's habit of sharing in-use dynlink libraries has&lt;br /&gt;
implications for the replacement of dynlink packages. Specifically, OS/2&lt;br /&gt;
holds the dynlink .DLL file open for as long as that library has any&lt;br /&gt;
clients. To replace a dynlink library with an upgraded version,&lt;br /&gt;
you must first ensure that all clients of the old package have been&lt;br /&gt;
terminated.&lt;br /&gt;
     While we're on the subject, I'll point out that dynlink segments, like&lt;br /&gt;
.EXE file segments, can be marked (by the linker) as &amp;quot;preload&amp;quot; or &amp;quot;load on&lt;br /&gt;
demand.&amp;quot; When a dynlink module or a .EXE file is loaded, OS/2 immediately&lt;br /&gt;
loads all segments marked &amp;quot;preload&amp;quot; but usually&lt;br /&gt;
14. Segments that are loaded from removable media will be fully&lt;br /&gt;
loaded, regardless of the &amp;quot;load on demand&amp;quot; bit.&lt;br /&gt;
14 does not load any&lt;br /&gt;
segments marked &amp;quot;load on demand.&amp;quot; These segments are loaded only when (and&lt;br /&gt;
if) they are referenced. This mechanism speeds process and library loading&lt;br /&gt;
and reduces swapping by leaving infrequently used segments out of memory&lt;br /&gt;
until they are needed. Once a segment is loaded, its &amp;quot;preload&amp;quot; or &amp;quot;load on&lt;br /&gt;
demand&amp;quot; status has no further bearing; the segment will be swapped or&lt;br /&gt;
discarded without consideration for these bits.&lt;br /&gt;
     Finally, special OS/2 code keeps track of dynamic link &amp;quot;circular&lt;br /&gt;
references.&amp;quot; Because dynlink packages can call other dynlink packages,&lt;br /&gt;
package A can call package B, and package B can call package A. Even if the&lt;br /&gt;
client process C terminates, packages A and B might appear to be in use by&lt;br /&gt;
each other, and they would both stay in memory. OS/2 keeps a graph of&lt;br /&gt;
dynlink clients, both processes and other dynlink packages. When a process&lt;br /&gt;
can no longer reach a dynlink package over this graph--in other words, when&lt;br /&gt;
a package doesn't have a process for a client and when none of its client&lt;br /&gt;
packages have processes for clients and so on--the dynlink package is&lt;br /&gt;
released. Figure 7-10 illustrates a dynamic link circular reference. PA&lt;br /&gt;
and PB are two processes, and LA through LG are dynlink library routines.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿                        ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³°°°°°°°°°°°°³                        ³°°°°°°°°°°°°³&lt;br /&gt;
³°°°°°PA°°°°°³                        ³°°°°°PB°°°°°³&lt;br /&gt;
³°°°°°°°°°°°°³                        ³°°°°°°°°°°°°³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ                        ÀÄÂÄÄÄÄÄÄÄÄÂÄÙ&lt;br /&gt;
      ³                                 ³        ³&lt;br /&gt;
ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿                          ³  ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿&lt;br /&gt;
³     LA     ³�ÄÄÄÄÄÄÄ¿                 ³  ³     LF     ³&lt;br /&gt;
ÀÄÄÂÄÄÄÄÄÄÂÄÄÙ        ³                 ³  ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
   ³   ÚÄÄÁÄÄÄÄÄÄÄÄÄ¿ ³                 ³        ³&lt;br /&gt;
   ³   ³     LB     ÃÄÙ                 ³  ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿&lt;br /&gt;
   ³   ÀÄÂÄÄÄÄÄÄÄÄÄÄÙ                   ³  ³     LG     ³&lt;br /&gt;
   ³     ³  ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ  ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄ�ÄÄÄÄÄ�ÄÄ�¿           ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿          ³&lt;br /&gt;
³     LC     ÃÄÄÄÄÄÄÄÄÄÄ�³     LD     ³�ÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÄ�ÄÄÄÄÄÄÙ           ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
      ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-10.  Dynamic link circular references.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.3  Dynlink Side Effects&lt;br /&gt;
A well-written dynlink library needs to adhere to the OS/2 religious tenet&lt;br /&gt;
of zero side effects. A dynlink library should export to the client process&lt;br /&gt;
only its functional interface and not accidentally export side effects that&lt;br /&gt;
may interfere with the consistent execution of the client.&lt;br /&gt;
     Some possible side effects are obvious: A dynlink routine shouldn't&lt;br /&gt;
close any file handles that it didn't itself open. The same applies to&lt;br /&gt;
other system resources that the client process may be accessing, and it&lt;br /&gt;
applies in the inverse, as well: A dynlink routine that obtains resources&lt;br /&gt;
for itself, in the guise of the client process, should do so in a way that&lt;br /&gt;
doesn't affect the client code. For example, consuming many of the&lt;br /&gt;
available file handles would be a side effect because the client would then&lt;br /&gt;
unexpectedly be short of available file handles. A dynlink package with a&lt;br /&gt;
healthy file handle appetite should be sure to call OS/2 to raise the&lt;br /&gt;
maximum number of file handles so that the client process isn't&lt;br /&gt;
constrained. Finally, the amount of available stack space is a resource&lt;br /&gt;
that a dynlink package must not exhaust. A dynlink routine should try to&lt;br /&gt;
minimize its stack needs, and an upgrade to an existing dynlink package&lt;br /&gt;
must not consume much more stack space than did the earlier version, lest&lt;br /&gt;
the upgrade cause existing clients to fail in the field.&lt;br /&gt;
     Dynlink routines can also cause side effects by issuing some kinds of&lt;br /&gt;
system calls. Because a dynlink routine runs as a subroutine of the client&lt;br /&gt;
process, it must be sure that calls that it makes to OS/2 on behalf of the&lt;br /&gt;
client process don't affect the client application. For example, each&lt;br /&gt;
signal event can have only one handler address; if a dynlink routine&lt;br /&gt;
establishes a signal handler, then that signal handler preempts any handler&lt;br /&gt;
set up by the client application. Likewise, if a dynlink routine changes&lt;br /&gt;
the priority of the thread with which it was called, the dynlink routine&lt;br /&gt;
must be sure to restore that priority before it returns to its caller.&lt;br /&gt;
Several other system functions such as DosError and DosSetVerify also cause&lt;br /&gt;
side effects that can affect the client process.&lt;br /&gt;
     Enumerating all forms of side effects is not possible; it's up to the&lt;br /&gt;
programmer to take the care needed to ensure that a dynlink module is&lt;br /&gt;
properly house-trained. A dynlink module should avoid the side effects&lt;br /&gt;
mentioned as well as similar ones, and, most important, it should behave&lt;br /&gt;
consistently so that if a client application passes its acceptance tests in&lt;br /&gt;
the lab it won't mysteriously fail in the field. This applies doubly to&lt;br /&gt;
upgrades for existing dynlink routines. Upgrades must be written so that if&lt;br /&gt;
a client application works with the earlier release of the dynlink package&lt;br /&gt;
it will work with the new release; obviously the author of the application&lt;br /&gt;
will not have an opportunity to retest existing copies of the application&lt;br /&gt;
against the new release of the dynlink module.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.12  Dynlink Names&lt;br /&gt;
&lt;br /&gt;
Each dynlink entry point has three names associated with it: an external&lt;br /&gt;
name, a module name, and an entry point name. The name the client program&lt;br /&gt;
calls as an external reference is the external name. The programmer works&lt;br /&gt;
with this name, and its syntax and form must be compatible with the&lt;br /&gt;
assembler or compiler being used. The name should be simple and explanatory&lt;br /&gt;
yet unlikely to collide with another external name in the client code or in&lt;br /&gt;
another library. A name such as READ or RESET is a poor choice because of&lt;br /&gt;
the collision possibilities; a name such as XR23P11 is obviously hard to&lt;br /&gt;
work with.&lt;br /&gt;
     The linker replaces the external name with a module name and an entry&lt;br /&gt;
point name, which are embedded in the resultant .EXE file. OS/2 uses the&lt;br /&gt;
module name to locate the dynlink .DLL file; the code for module modname is&lt;br /&gt;
in file MODNAME.DLL. The entry point name specifies the entry point in the&lt;br /&gt;
module; the entry point name need not be the same as the external name. For&lt;br /&gt;
modules with a lot of entry points, the client .EXE file size can be&lt;br /&gt;
minimized and the loading speed maximized by using entry ordinals in place&lt;br /&gt;
of entry point names. See the OS/2 technical reference literature for&lt;br /&gt;
details.&lt;br /&gt;
     Runtime dynamic links are established by using the module name and the&lt;br /&gt;
entry point name; the external name is not used.&lt;br /&gt;
&lt;br /&gt;
==8  File System Name Space==&lt;br /&gt;
&lt;br /&gt;
File system name space is a fancy term for how names of objects are defined&lt;br /&gt;
in OS/2. The words file system are a hint that OS/2 uses one naming scheme&lt;br /&gt;
both for files and for everything else with a name in ASCII format--system&lt;br /&gt;
semaphores, named shared memory, and so forth. First, we'll discuss the&lt;br /&gt;
syntax of names and how to manipulate them; we'll wind up with a discussion&lt;br /&gt;
of how and why we use one naming scheme for all named objects.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.1  Filenames&lt;br /&gt;
&lt;br /&gt;
Before we discuss OS/2 filenames, let's review the format of filenames&lt;br /&gt;
under MS-DOS. In MS-DOS, filenames are required to fit the 8.3 format: a&lt;br /&gt;
name field (which can contain a maximum of 8 characters) and an extension&lt;br /&gt;
field (which can contain a maximum of 3 characters).&lt;br /&gt;
1. As an aside, these sizes date from a tradition established&lt;br /&gt;
many years ago by Digital Equipment Corporation. Digital's very&lt;br /&gt;
early computers used a technique called RAD50 to store 3&lt;br /&gt;
uppercase letters in one 16-bit word, so their file systems&lt;br /&gt;
allowed a 6-character filename and a 3-character extension. CP/M&lt;br /&gt;
later picked up this filename structure. CP/M didn't use RAD50,&lt;br /&gt;
so, in a moment of generosity, it allowed 8-character filenames;&lt;br /&gt;
but the 3-character extension was kept.&lt;br /&gt;
1 The period character&lt;br /&gt;
(.) between the name and the extension is not part of the filename; it's a&lt;br /&gt;
separator character. The filename can consist of uppercase characters only.&lt;br /&gt;
If a user or an application creates a filename that contains lowercase&lt;br /&gt;
characters or a mixture of uppercase and lowercase, MS-DOS converts the&lt;br /&gt;
filename to all uppercase. If an application presents a filename whose name&lt;br /&gt;
or extension field exceeds the allotted length, MS-DOS silently truncates&lt;br /&gt;
the name to the 8.3 format before using it. MS-DOS establishes and enforces&lt;br /&gt;
these rules and maintains the file system structure on the disks. The file&lt;br /&gt;
system that MS-DOS version 3.x supports is called the FAT (File Allocation&lt;br /&gt;
Table) file system. The following are typical MS-DOS and OS/2 filenames:&lt;br /&gt;
&lt;br /&gt;
   \FOOTBALL\SRC\KERNEL\SCHED.ASM&lt;br /&gt;
&lt;br /&gt;
Football is a development project, so this name describes the source for&lt;br /&gt;
the kernel scheduler for the football project.&lt;br /&gt;
&lt;br /&gt;
   \MEMOS\286\MODESWIT.DOC&lt;br /&gt;
&lt;br /&gt;
is a memo discussing 80286 mode switching.&lt;br /&gt;
&lt;br /&gt;
   \\HAGAR\SCRATCH\GORDONL\FOR_MARK&lt;br /&gt;
&lt;br /&gt;
is a file in my scratch directory on the network server HAGAR, placed there&lt;br /&gt;
for use by Mark.&lt;br /&gt;
     The OS/2 architecture views file systems quite differently. As&lt;br /&gt;
microcomputers become more powerful and are used in more and more ways,&lt;br /&gt;
file system characteristics will be needed that might not be met by a&lt;br /&gt;
built-in OS/2 file system. Exotic peripherals, such as WORM&lt;br /&gt;
2. Write Once, Read Many disks. These are generally laser disks of&lt;br /&gt;
very high capacity, but once a track is written, it cannot be erased.&lt;br /&gt;
These disks can appear to be erasable by writing new copies of files&lt;br /&gt;
and directories each time a change is made, abandoning the old ones.&lt;br /&gt;
2 drives,&lt;br /&gt;
definitely require special file systems to meet their special&lt;br /&gt;
characteristics. For this reason, the file system is not built into OS/2&lt;br /&gt;
but is a closely allied component--an installable file system (IFS). An IFS&lt;br /&gt;
is similar to a device driver; it's a body of code that OS/2 loads at boot&lt;br /&gt;
time. The code talks to OS/2 via a standard interface and provides the&lt;br /&gt;
software to manage a file system on a storage device, including the ability&lt;br /&gt;
to create and maintain directories, to allocate disk space, and so&lt;br /&gt;
on.&lt;br /&gt;
     If you are familiar with OS/2 version 1.0, this information may be&lt;br /&gt;
surprising because you have seen no mention of an IFS in the reference&lt;br /&gt;
manuals. That's because the implementation hasn't yet caught up with the&lt;br /&gt;
architecture. We designed OS/2, from the beginning, to support installable&lt;br /&gt;
file systems, one of which would of course be the familiar FAT file system.&lt;br /&gt;
We designed the file system calls, such as DosOpen and DosClose, with this&lt;br /&gt;
in mind. Although scheduling pressures forced us to ship OS/2 version 1.0&lt;br /&gt;
with only the FAT file system--still built in--a future release will&lt;br /&gt;
include the full IFS package. Although at this writing the IFS release of&lt;br /&gt;
OS/2 has not been announced, this information is included here so that you&lt;br /&gt;
can understand the basis for the system name architecture. Also, this&lt;br /&gt;
information will help you write programs that work well under the new&lt;br /&gt;
releases of OS/2 that contain the IFS.&lt;br /&gt;
     Because the IFS will interpret filenames and pathnames and because&lt;br /&gt;
installable file systems can vary considerably, OS/2&lt;br /&gt;
3. Excluding the IFS part.&lt;br /&gt;
3 doesn't contain much&lt;br /&gt;
specific information about the format and meaning of filenames and&lt;br /&gt;
pathnames. In general, the form and meaning of filenames and pathnames are&lt;br /&gt;
private matters between the user and the IFS; both the application and OS/2&lt;br /&gt;
are simply go-betweens. Neither should attempt to parse or understand&lt;br /&gt;
filenames and pathnames. Applications shouldn't parse names because some&lt;br /&gt;
IFSs will support names in formats other than the 8.3 format. Applications&lt;br /&gt;
shouldn't even assume a specific length for a filename or a pathname. All&lt;br /&gt;
OS/2 filename and pathname interfaces, such as DosOpen, DosFindNext, and so&lt;br /&gt;
on, are designed to take name strings of arbitrary length. Applications&lt;br /&gt;
should use name buffers of at least 256 characters to ensure that a long&lt;br /&gt;
name is not truncated.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.2  Network Access&lt;br /&gt;
&lt;br /&gt;
Two hundred and fifty-six characters may seem a bit extreme for the length&lt;br /&gt;
of a filename, and perhaps it is. But OS/2 filenames are often pathnames,&lt;br /&gt;
and pathnames can be quite lengthy. To provide transparent access to files&lt;br /&gt;
on a LAN (local area network), OS/2 makes the network part of the file&lt;br /&gt;
system name space. In other words, a file's pathname can specify a machine&lt;br /&gt;
name as well as a directory path. An application can issue an open to a&lt;br /&gt;
name string such as \WORK\BOOK.DAT or \\VOGON\TEMP\RECALC.ASM. The first&lt;br /&gt;
name specifies the file BOOK.DAT in the directory WORK on the current drive&lt;br /&gt;
of the local machine; the second name specifies the file RECALC.ASM in the&lt;br /&gt;
directory TEMP on the machine VOGON.&lt;br /&gt;
4. Network naming is a bit more complex than this; the name TEMP&lt;br /&gt;
on the machine VOGON actually refers to an offered network&lt;br /&gt;
resource and might appear in any actual disk directory.&lt;br /&gt;
4 Future releases of the Microsoft LAN&lt;br /&gt;
Manager will make further use of the file system name space, so filenames,&lt;br /&gt;
especially program-generated filenames, can easily become very long.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.3  Name Generation and Compatibility&lt;br /&gt;
&lt;br /&gt;
Earlier, I said that applications should pass on filenames entered by the&lt;br /&gt;
user, ignoring their form. This is, of course, a bit unrealistic. Programs&lt;br /&gt;
often need to generate filenames--to hold scratch files, to hold derivative&lt;br /&gt;
filenames (for example, FOO.OBJ derived from FOO.ASM), and so forth. How&lt;br /&gt;
can an application generate or permute such filenames and yet ensure&lt;br /&gt;
compatibility with all installable file systems? The answer is, of course:&lt;br /&gt;
Use the least common denominator approach. In other words, you can safely&lt;br /&gt;
assume that a new IFS must accept the FAT file system's names (the 8.3&lt;br /&gt;
format) because otherwise it would be incompatible with too many programs.&lt;br /&gt;
So if an application sticks to the 8.3 rules when it creates names, it can&lt;br /&gt;
be sure that it is compatible with future file systems. Unlike MS-DOS,&lt;br /&gt;
OS/2&lt;br /&gt;
5. More properly, the FAT installable file system installed in OS/2.&lt;br /&gt;
5 will not truncate name or extension fields that are too long;&lt;br /&gt;
instead, an error will be returned. The case of a filename will continue to&lt;br /&gt;
be insignificant. Some operating systems, such as UNIX, are case sensitive;&lt;br /&gt;
for example, in UNIX the names &amp;quot;foo&amp;quot; and &amp;quot;Foo&amp;quot; refer to different files.&lt;br /&gt;
This works fine for a system used primarily by programmers, who know that a&lt;br /&gt;
lowercase f is ASCII 66 (SUB16) and that an uppercase F is ASCII&lt;br /&gt;
46 (SUB 16). Nonprogrammers, on the other hand, tend to see f and F as the&lt;br /&gt;
same character. Because most OS/2 users are nonprogrammers, OS/2&lt;br /&gt;
installable file systems will continue to be case insensitive.&lt;br /&gt;
     I said that it was safe if program-generated names adhered to the 8.3&lt;br /&gt;
rule. Program-permuted names are likewise safe if they only substitute&lt;br /&gt;
alphanumeric characters for other alphanumeric characters, for example,&lt;br /&gt;
FOO.OBJ for FOO.ASM. Lengthening filenames is also safe (for example,&lt;br /&gt;
changing FOO.C to FOO.OBJ) if your program checks for &amp;quot;invalid name&amp;quot; error&lt;br /&gt;
codes for the new name and has some way to deal with that possibility. In&lt;br /&gt;
any case, write your program so that it isn't confused by enhanced&lt;br /&gt;
pathnames; in the above substitution cases, the algorithm should work from&lt;br /&gt;
the end of the path string and ignore what comes before.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.4  Permissions&lt;br /&gt;
&lt;br /&gt;
Future releases of OS/2 will use the file system name space for more than&lt;br /&gt;
locating a file; it will also contain the permissions for the file. A&lt;br /&gt;
uniform mechanism will associate an access list with every entry in the&lt;br /&gt;
file system name space. This list will prevent unauthorized access--&lt;br /&gt;
accidental or deliberate--to the named file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.5  Other Objects in the File System Name Space&lt;br /&gt;
&lt;br /&gt;
As we've seen, the file system name space is a valuable device in several&lt;br /&gt;
aspects. First, it allows the generation of a variety of names. You can&lt;br /&gt;
group names together (by putting them in the same directory), and you can&lt;br /&gt;
generate entire families of unique names (by creating a new subdirectory).&lt;br /&gt;
Second, the name space can encompass all files and devices on the local&lt;br /&gt;
machine as well as files and devices on remote machines. Finally, file&lt;br /&gt;
system names will eventually support a flexible access and protection&lt;br /&gt;
mechanism.&lt;br /&gt;
     Thus, it comes as no surprise that when the designers of OS/2 needed a&lt;br /&gt;
naming mechanism to deal with nonfile objects, such as shared memory,&lt;br /&gt;
system semaphores, and named pipes, we chose to use the file system name&lt;br /&gt;
space. One small disadvantage to this decision is that a shared memory&lt;br /&gt;
object cannot have a name identical to that of a system semaphore, a named&lt;br /&gt;
pipe, or a disk file. This drawback is trivial, however, compared with the&lt;br /&gt;
benefits of sharing the file system name space. And, of course, you can use&lt;br /&gt;
separate subdirectory names for each type of object, thus preventing name&lt;br /&gt;
collision.&lt;br /&gt;
     Does this mean that system semaphores, shared memory, and pipes have&lt;br /&gt;
actual file system entries on a disk somewhere? Not yet. The FAT file&lt;br /&gt;
system does not support special object names in its directories. Although&lt;br /&gt;
changing it to do so would be easy, the file system would no longer be&lt;br /&gt;
downward compatible with MS-DOS. (MS-DOS 3.x could not read such disks&lt;br /&gt;
written under OS/2.) Because only the FAT file system is available with&lt;br /&gt;
OS/2 version 1.0, that release keeps special RAM-resident pseudo&lt;br /&gt;
directories to hold the special object names. These names must start with&lt;br /&gt;
\SEM\, \SHAREMEM\, \QUEUES\, and \DEV\ to minimize the chance of name&lt;br /&gt;
collision with a real file when they do become special pseudo files in a&lt;br /&gt;
future release of OS/2.&lt;br /&gt;
     Although all file system name space features--networking and (in the&lt;br /&gt;
future) permissions--apply to all file system name space objects from an&lt;br /&gt;
architectural standpoint, not all permutations may be supported.&lt;br /&gt;
Specifically, supporting named shared memory across the network is very&lt;br /&gt;
costly&lt;br /&gt;
6. The entire shared memory segment must be transferred across&lt;br /&gt;
the network each time any byte within it is changed. Some clever&lt;br /&gt;
optimizations can reduce this cost, but none works well enough to&lt;br /&gt;
be feasible.&lt;br /&gt;
6 and won't be implemented.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==9  Memory Management==&lt;br /&gt;
&lt;br /&gt;
A primary function of any multitasking operating system is to allocate&lt;br /&gt;
system resources to each process according to its need. The scheduler&lt;br /&gt;
allocates CPU time among processes (actually, among threads); the memory&lt;br /&gt;
manager allocates both physical memory and virtual memory.&lt;br /&gt;
&lt;br /&gt;
===9.1  Protection Model===&lt;br /&gt;
&lt;br /&gt;
Although MS-DOS provided a simple form of memory management, OS/2 provides&lt;br /&gt;
memory protection. Under MS-DOS 3.x, for example, a program should ask the&lt;br /&gt;
operating system to allocate a memory area before the program uses it.&lt;br /&gt;
Under OS/2, a program must ask the operating system to allocate a memory&lt;br /&gt;
area before the program uses it. As we discussed earlier, the 80286&lt;br /&gt;
microprocessor contains special memory protection hardware. Each memory&lt;br /&gt;
reference that a program makes explicitly or implicitly references a&lt;br /&gt;
segment selector. The segment selector, in turn, references an entry in the&lt;br /&gt;
GDT or the LDT, depending on the form of the selector. Before any program,&lt;br /&gt;
including OS/2 itself, can reference a memory location, that memory&lt;br /&gt;
location must be described in an LDT or a GDT entry, and the selector for&lt;br /&gt;
that entry must be loaded into one of the four segment registers.&lt;br /&gt;
     This hardware design places some restrictions on how programs can use&lt;br /&gt;
addresses.&lt;br /&gt;
&lt;br /&gt;
     þ  A program cannot address memory not set up for it in the LDT or&lt;br /&gt;
        GDT. The only way to address memory is via the LDT and GDT.&lt;br /&gt;
&lt;br /&gt;
     þ  Each segment descriptor in the LDT and GDT contains the physical&lt;br /&gt;
        address and the length of that segment. A program cannot reference&lt;br /&gt;
        an offset into a segment beyond that segment's length.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't put garbage (arbitrary values) into a segment&lt;br /&gt;
        register. Each time a segment register is loaded, the hardware&lt;br /&gt;
        examines the corresponding LDT and GDT to see if the entry is&lt;br /&gt;
        valid. If a program puts an arbitrary value--for example, the lower&lt;br /&gt;
        half of a floating point number--into a segment register, the&lt;br /&gt;
        arbitrary value will probably point to an invalid LDT or GDT entry,&lt;br /&gt;
        causing a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't execute instructions from within a data segment.&lt;br /&gt;
        Attempting to load a data segment selector into the CS register&lt;br /&gt;
        (usually via a far call or a far jump) causes a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't write into a code segment. Attempting to do so&lt;br /&gt;
        causes a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't perform segment arithmetic. Segment arithmetic&lt;br /&gt;
        refers to activities made possible by the addressing mechanism of&lt;br /&gt;
        the 8086 and 8088 microprocessors. Although they are described as&lt;br /&gt;
        having a segment architecture, they are actually linear address&lt;br /&gt;
        space machines that use offset registers--the so-called segment&lt;br /&gt;
        registers. An 8086 can address 1 MB of memory, which requires a 20-&lt;br /&gt;
        bit address. The processor creates this address by multiplying the&lt;br /&gt;
        16-bit segment value by 16 and adding it to the 16-bit offset&lt;br /&gt;
        value. The result is an address between 0 and 1,048,575 (that is, 1&lt;br /&gt;
        MB).&lt;br /&gt;
1. Actually, it's possible to produce addresses beyond 1 MB&lt;br /&gt;
(2^20) by this method if a large enough segment and offset value&lt;br /&gt;
are chosen. The 8086 ignores the carry into the nonexistent 21st&lt;br /&gt;
address bit, effectively wrapping around such large addresses&lt;br /&gt;
into the first 65 KB-16 bytes of physical memory.&lt;br /&gt;
1 The reason these are not true segments is that they don't&lt;br /&gt;
        have any associated length and their names (that is, their&lt;br /&gt;
        selectors) aren't names at all but physical addresses divided by&lt;br /&gt;
        16. These segment values are actually scaled offsets. An address&lt;br /&gt;
        that has a segment value of 100 and an offset value of 100 (shown&lt;br /&gt;
        as 100(SUB10):100(SUB10)), and the address (99(SUB10):116(SUB10))&lt;br /&gt;
        both refer to the same memory location.&lt;br /&gt;
           Many real mode programs take advantage of this situation. Some&lt;br /&gt;
        programs that keep a great many pointers store them as 20-bit&lt;br /&gt;
        values, decomposing those values into the segment:offset form only&lt;br /&gt;
        when they need to de-reference the pointer. To ensure that certain&lt;br /&gt;
        objects have a specific offset value, other programs choose a&lt;br /&gt;
        matching segment value so that the resultant 20-bit address is&lt;br /&gt;
        correct. Neither technique works in OS/2 protect mode. Each segment&lt;br /&gt;
        selector describes its own segment, a segment with a length and an&lt;br /&gt;
        address that are independent of the numeric value of the segment&lt;br /&gt;
        selector. The memory described by segment N has nothing in common&lt;br /&gt;
        with the memory described by segment N+4 or by any other segment&lt;br /&gt;
        unless OS/2 explicitly sets it up that way.&lt;br /&gt;
&lt;br /&gt;
     The segmentation and protection hardware allows OS/2 to impose further&lt;br /&gt;
restrictions on processes.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot edit or examine the contents of the LDT or the&lt;br /&gt;
        GDT. OS/2 simply declines to build an LDT or GDT selector that a&lt;br /&gt;
        process can use to access the contents of those tables. Certain LDT&lt;br /&gt;
        and GDT selectors describe the contents of those tables themselves,&lt;br /&gt;
        but OS/2 sets them up so that they can only be used by ring 0 (that&lt;br /&gt;
        is, privileged) code.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot hook interrupt vectors. MS-DOS version 3.x&lt;br /&gt;
        programs commonly hook interrupt vectors by replacing the address&lt;br /&gt;
        of the interrupt handler with an address from their own code. Thus,&lt;br /&gt;
        these programs can monitor or intercept system calls made via INT&lt;br /&gt;
        21h, BIOS calls also made via interrupts, and hardware interrupts&lt;br /&gt;
        such as the keyboard and the system clock. OS/2 programs cannot do&lt;br /&gt;
        this. OS/2 declines to set up a segment selector that processes can&lt;br /&gt;
        use to address the interrupt vector table.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot call the ROM BIOS code because no selector&lt;br /&gt;
        addresses the ROM BIOS code. Even if such a selector were&lt;br /&gt;
        available, it would be of little use. The ROM BIOS is coded for&lt;br /&gt;
        real mode execution and performs segment arithmetic operations that&lt;br /&gt;
        are no longer legal. If OS/2 provided a ROM BIOS selector, calls to&lt;br /&gt;
        the ROM BIOS would usually generate GP faults.&lt;br /&gt;
&lt;br /&gt;
     þ  Finally, processes cannot run in ring 0, that is, in privileged&lt;br /&gt;
        mode. Both OS/2 and the 80286 hardware are designed to prevent an&lt;br /&gt;
        application program from ever executing in ring 0. Code running in&lt;br /&gt;
        ring 0 can manipulate the LDT and GDT tables as well as other&lt;br /&gt;
        hardware protection features. If OS/2 allowed processes to run in&lt;br /&gt;
        ring 0, the system could never be stable or secure. OS/2 obtains&lt;br /&gt;
        its privileged (literally) state by being the first code loaded at&lt;br /&gt;
        boot time. The boot process takes place in ring 0 and grants ring 0&lt;br /&gt;
        permission to OS/2 by transferring control to OS/2 while remaining&lt;br /&gt;
        in ring 0. OS/2 does not, naturally, extend this favor to the&lt;br /&gt;
        application programs it loads; it ensures that applications can&lt;br /&gt;
        only run in ring 3 user mode.&lt;br /&gt;
2. Applications can also run in ring 2 (see 18.1 I/O Privilege&lt;br /&gt;
Mechanism).&lt;br /&gt;
2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2  Memory Management API&lt;br /&gt;
&lt;br /&gt;
OS/2 provides an extensive memory management API. This book is not a&lt;br /&gt;
reference manual, so I won't cover all the calls. Instead, I'll focus on&lt;br /&gt;
areas that may not be completely self-explanatory.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.1  Shared Memory&lt;br /&gt;
OS/2 supports two kinds of shared memory--named shared memory and giveaway&lt;br /&gt;
shared memory. In both, the memory object shared is a segment. Only an&lt;br /&gt;
entire segment can be shared; sharing part of a segment is not possible.&lt;br /&gt;
Named shared memory is volatile because neither the name of the named&lt;br /&gt;
shared memory nor the memory itself can exist on the FAT file system. When&lt;br /&gt;
the number of processes using a shared memory segment goes to zero, the&lt;br /&gt;
memory is released. Shared memory can't stay around in the absence of&lt;br /&gt;
client processes; it must be reinitialized via DosAllocShrSeg after a&lt;br /&gt;
period of nonuse.&lt;br /&gt;
     Giveaway shared memory allows processes to share access to the same&lt;br /&gt;
segment. Giveaway shared memory segments don't have names because processes&lt;br /&gt;
can't ask to have access to them; a current user of the segment has to give&lt;br /&gt;
access to the segment to a new client process. The term giveaway is a bit&lt;br /&gt;
of a misnomer because the giving process retains access to the memory--the&lt;br /&gt;
access is &amp;quot;given&amp;quot; but not especially &amp;quot;away.&amp;quot; Giveaway shared memory is not&lt;br /&gt;
as convenient as named shared memory. The owner&lt;br /&gt;
3. One of the owners. Anyone with access to a giveaway shared&lt;br /&gt;
segment can give it away itself.&lt;br /&gt;
3 has to know the PID of the&lt;br /&gt;
recipient and  then communicate the recipient's segment selector (returned&lt;br /&gt;
by DosGiveSeg) to that recipient process via some form of IPC.&lt;br /&gt;
     Despite its limitations, giveaway shared memory has important virtues.&lt;br /&gt;
It's a fast and efficient way for one process to transfer data to another;&lt;br /&gt;
and because access is passed &amp;quot;hand to hand,&amp;quot; the wrong process cannot&lt;br /&gt;
accidentally or deliberately gain access to the segment. Most clients of&lt;br /&gt;
giveaway shared memory don't retain access to the segment once they've&lt;br /&gt;
passed it off; they typically call DosFreeSeg on their handle after they've&lt;br /&gt;
called DosGiveSeg. For example, consider the design of a database dynlink&lt;br /&gt;
subsystem that acts as a front end for a database serving process. As part&lt;br /&gt;
of the dynlink initialization process, the package arranged for its client&lt;br /&gt;
process to share a small named shared memory segment with the database&lt;br /&gt;
process. It might be best to use a named pipe or named shared memory--&lt;br /&gt;
created by the database process--to establish initial communication and&lt;br /&gt;
then use this interface only to set up a private piece of giveaway shared&lt;br /&gt;
memory for all further transactions between the client process (via the&lt;br /&gt;
dynlink subsystem) and the database process. Doing it this way, rather than&lt;br /&gt;
having one named shared segment hold service requests from all clients,&lt;br /&gt;
provides greater security. Because each client has its own separate shared&lt;br /&gt;
memory communications area, an amok client can't damage the communications&lt;br /&gt;
of other clients.&lt;br /&gt;
     When a client process asks the database process to read it a record,&lt;br /&gt;
the database process must use a form of IPC to transfer the data to the&lt;br /&gt;
client. Pipes are too slow for the volume of data that our example&lt;br /&gt;
anticipates; shared memory is the best technique. If we were to use named&lt;br /&gt;
shared memory, the database package would have to create a unique shared&lt;br /&gt;
memory name for each record, allocate the memory, and then communicate the&lt;br /&gt;
name to the client (actually, to the dynlink subsystem called by the&lt;br /&gt;
client) so that it can request access. This process has some drawbacks:&lt;br /&gt;
&lt;br /&gt;
     þ  A new unique shared memory name must be created for each request.&lt;br /&gt;
        We could reuse a single shared memory segment, but this would force&lt;br /&gt;
        the client to copy the data out of the segment before it could make&lt;br /&gt;
        another request--too costly a process for an application that must&lt;br /&gt;
        handle a high volume of data.&lt;br /&gt;
&lt;br /&gt;
     þ  Creating named shared memory segments is generally slower than&lt;br /&gt;
        creating giveaway shared memory segments, especially if a large&lt;br /&gt;
        number of named shared memory objects exist, as would be the case&lt;br /&gt;
        in this scenario. The client spends more time when it then requests&lt;br /&gt;
        access to the segment. Creating named shared memory segments is&lt;br /&gt;
        plenty fast enough when it's done once in a while, but in a high-&lt;br /&gt;
        frequency application such as our example, it could become a&lt;br /&gt;
        bottleneck.&lt;br /&gt;
&lt;br /&gt;
     Instead, the database process can create a giveaway shared memory&lt;br /&gt;
segment, load the data into it, and then give it to the client process. The&lt;br /&gt;
database process can easily learn the client's PID; the dynlink interface,&lt;br /&gt;
which runs as the client process, can include it as part of the data&lt;br /&gt;
request. Likewise, the database process can easily return the new client&lt;br /&gt;
selector to the client. This process is fast and efficient and doesn't bog&lt;br /&gt;
down the system by forcing it to deal with a great many name strings.&lt;br /&gt;
     Note that you must specify, at the time of the DosAllocSeg, that the&lt;br /&gt;
segment might be &amp;quot;given away.&amp;quot; Doing so allows OS/2 to allocate the&lt;br /&gt;
selector in the disjoint space, as we discussed earlier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.2  Huge Memory&lt;br /&gt;
The design of the 80286 microprocessor specifies the maximum size of a&lt;br /&gt;
memory segment as 64 KB. For many programs, this number is far too small.&lt;br /&gt;
For example, the internal representation of a large spreadsheet commonly&lt;br /&gt;
takes up 256 KB or more. OS/2 can do nothing to set up a segment that is&lt;br /&gt;
truly larger than 64 KB, but the OS/2 facility called huge segments&lt;br /&gt;
provides a reasonable emulation of segments larger than 64 KB. The trick is&lt;br /&gt;
that a huge segment of, for example, 200 KB is not a single segment but a&lt;br /&gt;
group of four segments, three of which are 64 KB and a fourth of 8 KB. With&lt;br /&gt;
minimal programming burden, OS/2 allows an application to treat the group&lt;br /&gt;
of four segments as a single huge segment.&lt;br /&gt;
     When a process calls DosAllocHuge to allocate a huge segment, OS/2&lt;br /&gt;
allocates several physical segments, the sum of whose size equals the size&lt;br /&gt;
of the virtual huge segment. All component segments are 64 KB, except&lt;br /&gt;
possibly the last one. Unlike an arbitrary collection of segment selectors,&lt;br /&gt;
DosAllocHuge guarantees that the segment selectors it returns are spaced&lt;br /&gt;
uniformly from each other. The selector of the N+1th component segment is&lt;br /&gt;
that of the Nth segment plus i, where i is a power of two. The value of i&lt;br /&gt;
is constant for any given execution of OS/2, but it may vary between&lt;br /&gt;
releases of OS/2 or as a result of internal configuration during bootup. In&lt;br /&gt;
other words, a program must learn the factor i every time it executes; it&lt;br /&gt;
must not hard code the value. There are three ways to learn this value.&lt;br /&gt;
First, a program can call DosGetHugeShift; second, it can read this value&lt;br /&gt;
from the global infoseg; and third, it can reference this value as the&lt;br /&gt;
undefined absolute externals DOSHUGESHIFT (log(SUB2)(i)) or&lt;br /&gt;
DOSHUGEINCR (i). OS/2 will insert the proper value for these&lt;br /&gt;
externals at loadtime. This last method is the most efficient and is&lt;br /&gt;
recommended. Family API programs should call DosGetHugeShift. Figure 9-1&lt;br /&gt;
illustrates the layout of a 200 KB huge memory object. Selectors n + 4i and&lt;br /&gt;
n + 5i are currently invalid but are reserved for future growth of the&lt;br /&gt;
huge object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ÚÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´                              ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³                              ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+0i ³       ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³               ³       ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³      ³               ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³               ³&lt;br /&gt;
n+1i ³       ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄ¿       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³       ³       ³       ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³      ³       ³       ³       ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³       ³       ÀÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+2i ³       ÃÄÄÄÄÄÄÙ       ³               ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
n+3i ³       ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³&lt;br /&gt;
n+4i ³   �   ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³              ³               ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+5i ³   �   ³                              ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´                              ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÀÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 9-1.  Huge memory objects.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Once an application has the first segment selector of the huge segment&lt;br /&gt;
group, called the base segment, and the value log(SUB2)(i), computing the&lt;br /&gt;
address of the Nth byte in the huge segment is easy. Take the high-order&lt;br /&gt;
word of the value of N (that is, N/64 KB), shift it left by log(SUB2)(i)&lt;br /&gt;
(that is, by the DosHugeShift value), and add the base segment selector&lt;br /&gt;
returned by DosAllocHuge. The resultant value is the segment selector for&lt;br /&gt;
the proper component segment; the low-order 16 bits of i are the offset&lt;br /&gt;
into that segment. This computation is reasonably quick to perform since it&lt;br /&gt;
involves only a shift and an addition.&lt;br /&gt;
     Huge segments can be shrunk or grown via DosReallocHuge. If the huge&lt;br /&gt;
segment is to be grown, creating more component physical segments may be&lt;br /&gt;
necessary. Because the address generation rules dictate which selector this&lt;br /&gt;
new segment may have, growing the huge segment may not be possible if that&lt;br /&gt;
selector has already been allocated for another purpose. DosAllocHuge takes&lt;br /&gt;
a maximum growth parameter; it uses this value to reserve sufficient&lt;br /&gt;
selectors to allow the huge segment to grow that big. Applications should&lt;br /&gt;
not provide an unrealistically large number for this argument because doing&lt;br /&gt;
so will waste LDT selectors.&lt;br /&gt;
     The astute reader will notice that the segment arithmetic of the 8086&lt;br /&gt;
environment is not dead; in a sense, it's been resurrected by the huge&lt;br /&gt;
segment mechanism. Applications written for the 8086 frequently use this&lt;br /&gt;
technique to address memory regions greater than 64 KB, using a shift value&lt;br /&gt;
of 12. In other words, if you add 2^12 to an 8086 segment register value,&lt;br /&gt;
the segment register will point to an address 2^12*16, or 64 KB, further in&lt;br /&gt;
physical memory. The offset value between the component segment values was&lt;br /&gt;
always 4096 because of the way the 8086 generated addresses. Although the&lt;br /&gt;
steps involved in computing the segment value are the same in protect mode,&lt;br /&gt;
what's actually happening is considerably different. When you do this&lt;br /&gt;
computation in protect mode, the segment selector value has no inherent&lt;br /&gt;
relationship to the other selectors that make up the huge object. The trick&lt;br /&gt;
only works because OS/2 has arranged for equally spaced-out selectors to&lt;br /&gt;
exist and for each to point to an area of physical memory of the&lt;br /&gt;
appropriate size. Figure 9-2 illustrates the similarities and differences&lt;br /&gt;
between huge model addressing in real and protect modes. The application&lt;br /&gt;
code sequence is identical: A segment selector is computed by adding N*i to&lt;br /&gt;
the base selector. In real mode i is always 4096; in protect mode OS/2&lt;br /&gt;
provides i.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
          REAL MODE        |             PROTECT MODE&lt;br /&gt;
                           |&lt;br /&gt;
           Physical        |&lt;br /&gt;
            Memory         |            Segment&lt;br /&gt;
           ÚÄÄÄÄÄÄ¿        |            Selector         Physical&lt;br /&gt;
           ³      ³        |             Table            Memory&lt;br /&gt;
           ³      ³        |            ÚÄÄÄÄÄÄ¿         ÚÄÄÄÄÄÄ¿&lt;br /&gt;
n+0i ÄÄÄÄÄ�³      ³±       |            ³      ³  ÚÄÄÄÄÄ�³      ³&lt;br /&gt;
           ³      ³±�64 KB | n+0i ÄÄÄÄÄ�³      ÃÄÄÅÄÄÄ¿  ÀÄÄÄÄÄÄÙ&lt;br /&gt;
n+1i ÄÄÄÄÄ�³      ³±       |            ³      ³  ³   ³&lt;br /&gt;
           ³      ³        | n+1i ÄÄÄÄÄ�³      ÃÄÄÅÄ¿ ³  ÚÄÄÄÄÄÄ¿&lt;br /&gt;
n+2i ÄÄÄÄÄ�³      ³        |            ³      ³  ³ ³ ÀÄ�³      ³&lt;br /&gt;
           ³      ³        | n+2i ÄÄÄÄÄ�³      ÃÄÄÙ ³    ÀÄÄÄÄÄÄÙ&lt;br /&gt;
n+3i ÄÄÄÄÄ�³      ³        |            ³      ³    ³    ÚÄÄÄÄÄÄ¿&lt;br /&gt;
           ³      ³        | n+3i ÄÄÄÄÄ�³      ÃÄÄÄÄÅÄÄÄ�³      ³&lt;br /&gt;
           ³      ³        |            ³      ³    ³    ÀÄÄÄÄÄÄÙ&lt;br /&gt;
           ³      ³        |            ³      ³    ³    ÚÄÄÄÄÄÄ¿&lt;br /&gt;
           ³      ³        |            ÀÄÄÄÄÄÄÙ    ÀÄÄÄ�³      ³&lt;br /&gt;
           ³      ³        |        i is set by OS/2     ÀÄÄÄÄÄÄÙ&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ÀÄÄÄÄÄÄÙ        |&lt;br /&gt;
           i = 4096        |&lt;br /&gt;
&lt;br /&gt;
Figure 9-2.  Huge model addressing in real mode and in protect mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Although the similarity between 8086 segment arithmetic and OS/2 huge&lt;br /&gt;
segments is only apparent, it does make it easy to write a program as a&lt;br /&gt;
dual mode application. By using the shift value of 12 in real mode and&lt;br /&gt;
using the OS/2 supplied value in protect mode, the same code functions&lt;br /&gt;
correctly in either mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.3  Executing from Data Segments&lt;br /&gt;
We saw that OS/2 provides the huge segment mechanism to get around the&lt;br /&gt;
segment size restriction imposed by the hardware. OS/2 likewise circumvents&lt;br /&gt;
another hardware restriction--the inability to execute code from data&lt;br /&gt;
segments. Although the demand loading, the discarding, and the swapping of&lt;br /&gt;
code segments make one use of running code from data segments--code&lt;br /&gt;
overlays--obsolete, the capability is still needed. Some high-performance&lt;br /&gt;
programs--the presentation manager, for example--compile &amp;quot;on the fly&amp;quot;&lt;br /&gt;
special code to perform time-critical tasks, such as flipping bits in EGA&lt;br /&gt;
display memory. The optimal sequence may differ depending on several&lt;br /&gt;
factors, so a program may need to compile such code and execute it, gaining&lt;br /&gt;
a significant increase in efficiency over some other approach. OS/2&lt;br /&gt;
supports this need by means of the DosCreateCSAlias call.&lt;br /&gt;
     When DosCreateCSAlias is called with a selector for a data segment, it&lt;br /&gt;
creates a totally different code segment selector (in the eyes of 80286&lt;br /&gt;
hardware) that by some strange coincidence points to exactly the same&lt;br /&gt;
memory locations as does the data segment selector. As a result, code is&lt;br /&gt;
not actually executing from a data segment but from a code segment. Because&lt;br /&gt;
the code segment exactly overlaps that other data segment, the desired&lt;br /&gt;
effect is achieved. The programmer need only be careful to use the data&lt;br /&gt;
selector when writing the segment and to use the code selector when&lt;br /&gt;
executing it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.4  Memory Suballocation&lt;br /&gt;
All memory objects discussed so far have been segments. OS/2 provides a&lt;br /&gt;
facility called memory suballocation that allocates pieces of memory from&lt;br /&gt;
within an application's segment. Pieces of memory can be suballocated from&lt;br /&gt;
within a segment, grown, shrunk, and released. OS/2 uses a classic heap&lt;br /&gt;
algorithm to do this. The DosSubAlloc call uses space made available from&lt;br /&gt;
earlier DosSubFrees when possible, growing the segment as necessary when&lt;br /&gt;
the free heap space is insufficient. We will call the pieces of memory&lt;br /&gt;
returned by DosSubAlloc heap objects.&lt;br /&gt;
     The memory suballocation package works within the domain of a process.&lt;br /&gt;
The suballocation package doesn't allocate the memory from some &amp;quot;system&lt;br /&gt;
pool&amp;quot; outside the process's address space, as does the segment allocator.&lt;br /&gt;
The suballocation package doesn't even allocate segments; it manages only&lt;br /&gt;
segments supplied by and owned by (or at least accessible to) the caller.&lt;br /&gt;
This is a feature because memory protection is on a per-segment basis. If&lt;br /&gt;
the suballocation package were to get its space from some system global&lt;br /&gt;
segment, a process that overwrote its heap object could damage one&lt;br /&gt;
belonging to another process. Figure 9-3 illustrates memory suballocation.&lt;br /&gt;
It shows a segment j being suballocated. H is the suballocation header; the&lt;br /&gt;
shaded areas are free space.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       ÚÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
       ³  Segment  ³       ³  Segment  ³       ³  Segment  ³&lt;br /&gt;
       ³     i     ³       ³     j     ³       ³     k     ³&lt;br /&gt;
       ÀÄÄÄÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                          /            \&lt;br /&gt;
                        /                \&lt;br /&gt;
                      /                    \&lt;br /&gt;
                    /                        \&lt;br /&gt;
                  /                            \&lt;br /&gt;
                /                                \&lt;br /&gt;
              /                                    \&lt;br /&gt;
            /                                        \&lt;br /&gt;
          /                                            \&lt;br /&gt;
        /                                                \&lt;br /&gt;
      /                                                    \&lt;br /&gt;
    /                                                        \&lt;br /&gt;
  /                                                            \&lt;br /&gt;
ÚÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÂÂÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÂÄÄÄÄÄÄÄÂÄÄÄÂÄÄÄÄÂÄÄÄÂÄÄÄÄÄÄÄ¿&lt;br /&gt;
³H ³  ³     ³°°°³³°°°°³           ³ ³°°°°°°°³   ³    ³   ³°°°°°°°³&lt;br /&gt;
³  ³  ³     ³°°°³³°°°°³           ³ ³°°°°°°°³   ³    ³   ³°°°°°°°³&lt;br /&gt;
ÀÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÁÁÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÁÄÄÄÁÄÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 9-3.  Memory suballocation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     We said that the memory suballocator subdivides segments that are&lt;br /&gt;
accessible to the client process. This means that you can use it to&lt;br /&gt;
subdivide space in a shared memory segment. Such a technique can be handy&lt;br /&gt;
when two or more processes are using a shared memory segment for&lt;br /&gt;
intercommunication, but there is risk because an error in one process can&lt;br /&gt;
easily corrupt the heap objects of another.&lt;br /&gt;
     Earlier, in the discussion of dynamic link subsystems, we described&lt;br /&gt;
facilities and techniques for writing a reliable subsystem. The OS/2 memory&lt;br /&gt;
suballocation package is a good example of such a subsystem, so let's look&lt;br /&gt;
at its workings more closely. The first try at the suballocation package&lt;br /&gt;
produced a straightforward heap allocator, much like the one in a C or&lt;br /&gt;
Pascal runtime library. It maintained a free chain of heap objects and&lt;br /&gt;
allocated them at its client's request. If the closest-size free heap&lt;br /&gt;
object was still bigger than the request, it was split into an allocated&lt;br /&gt;
part and a free part. Freed heap objects were coalesced with any adjacent&lt;br /&gt;
free objects. The suballocation package took a segment pointer and some&lt;br /&gt;
other arguments and returned some values--an offset and the changed data in&lt;br /&gt;
the segment itself where the heap headers were stored. If we stretch things&lt;br /&gt;
a little and consider the changed state of the supplied data segment as a&lt;br /&gt;
returned value, then the suballocation package at this stage is much like a&lt;br /&gt;
function: It has no state of its own; it merely returns values computed&lt;br /&gt;
only from the input arguments. This simple suballocation dynlink routine&lt;br /&gt;
uses no global data segments and doesn't even need an instance data&lt;br /&gt;
segment.&lt;br /&gt;
     This simple implementation has an important drawback: More than one&lt;br /&gt;
process can't safely use it to manage a shared memory segment; likewise,&lt;br /&gt;
multiple threads within one process can't use it. The heap free list is a&lt;br /&gt;
critical section; if multiple threads call the suballocator on the same&lt;br /&gt;
segment, the heap free list can become corrupted. This problem necessitated&lt;br /&gt;
upgrading the suballocation package to use a semaphore to protect the&lt;br /&gt;
critical section. If we didn't want to support suballocation of shared&lt;br /&gt;
memory and were only worried about multiple threads within a task, we could&lt;br /&gt;
use RAM semaphores located in the managed segment itself to protect the&lt;br /&gt;
critical section. The semaphore might be left set if the process died&lt;br /&gt;
unexpectedly, but the managed segment isn't shared. It's going to be&lt;br /&gt;
destroyed in any case, so we don't care.&lt;br /&gt;
     But, even in this simple situation of managing only privately owned&lt;br /&gt;
segments, we must concern ourselves with some special situations. One&lt;br /&gt;
problem is signals: What if the suballocator is called with thread 1, and a&lt;br /&gt;
signal (such as SIGINT, meaning that the user pressed Ctrl-C) comes in?&lt;br /&gt;
Thread 1 is interrupted from the suballocation critical section to execute&lt;br /&gt;
the signal handler. Often signal handlers return to the interrupted code,&lt;br /&gt;
and all is well. But what if the signal handler does not return but jumps&lt;br /&gt;
to the application's command loop? Or what if it does return, but before it&lt;br /&gt;
does so calls the memory suballocator? In these two cases, we'd have a&lt;br /&gt;
deadlock on the critical section. We can solve these problems by using the&lt;br /&gt;
DosHoldSignal function. DosHoldSignal does for signals what the CLI&lt;br /&gt;
instruction does for hardware interrupts: It holds them off for a short&lt;br /&gt;
time. Actually, it holds them off forever unless the application releases&lt;br /&gt;
them, but holding signals for more than a second or two is poor practice.&lt;br /&gt;
If you precede the critical section's semaphore claim call with a signal&lt;br /&gt;
hold and follow the critical section's semaphore release call with a signal&lt;br /&gt;
release, you're protected from deadlocks caused by signal handling.&lt;br /&gt;
     Note that unlike the CLI instruction, DosHoldSignal calls nest. OS/2&lt;br /&gt;
counts the number of DosHoldSignal &amp;quot;hold&amp;quot; calls made and holds signals off&lt;br /&gt;
until an equal number of &amp;quot;release&amp;quot; calls are issued. This means that a&lt;br /&gt;
routine can safely execute a hold/release pair without affecting the state&lt;br /&gt;
of its calling code. If the caller had signals held at the time of the&lt;br /&gt;
call, they will remain held. If signals were free at the time of the call,&lt;br /&gt;
the callee's &amp;quot;release&amp;quot; call restores them to that state.&lt;br /&gt;
     Whenever dynlink packages make any call that changes the state of the&lt;br /&gt;
process or the thread, they must be sure to restore that state before they&lt;br /&gt;
return to their caller. Functions that nest, such as DosHoldSignal,&lt;br /&gt;
accomplish this automatically. For other functions, the dynlink package&lt;br /&gt;
should explicitly discover and remember the previous state so that it can&lt;br /&gt;
be restored.&lt;br /&gt;
     Our problems aren't over though. A second problem is brought about by&lt;br /&gt;
the DosExitList facility. If a client process's thread is in the&lt;br /&gt;
suballocation package's critical section and the client terminates&lt;br /&gt;
suddenly--it could be killed externally or have a GP fault--the process&lt;br /&gt;
might not die immediately. If any DosExitList handlers are registered, they&lt;br /&gt;
will be called. They might call the memory suballocator, and once again we&lt;br /&gt;
face deadlock. We could solve this situation with the classic approach of&lt;br /&gt;
making a bug into a feature: Document that the suballocator can't be called&lt;br /&gt;
at exitlist time. This may make sense for some dynlink subsystems, but it's&lt;br /&gt;
too restrictive for an important OS/2 facility. We've got to deal with this&lt;br /&gt;
problem too.&lt;br /&gt;
     The DosHoldSignal trick won't help us here. It would indeed prevent&lt;br /&gt;
external kills, but it would not prevent GP faults and the like. We could&lt;br /&gt;
say, &amp;quot;A program that GP faults is very sick, so all bets are off.&amp;quot; This&lt;br /&gt;
position is valid, except that if the program or one of its dynlink&lt;br /&gt;
subsystems uses DosExitList and the DosExitList handler tries to allocate&lt;br /&gt;
or release a heap object, the process will hang and never terminate&lt;br /&gt;
correctly. This is unacceptable because the user would be forced to reboot&lt;br /&gt;
to get rid of the moribund application. The answer is to use a system&lt;br /&gt;
semaphore rather than a RAM semaphore to protect the memory segment. System&lt;br /&gt;
semaphores are a bit slower than RAM semaphores, but they have some extra&lt;br /&gt;
features. One is that they can be made exclusive; only the thread that owns&lt;br /&gt;
the semaphore can release it. Coupled with this is an &amp;quot;owner death&amp;quot;&lt;br /&gt;
notification facility that allows a process's DosExitList handler an&lt;br /&gt;
opportunity to determine that one of its threads has orphaned a semaphore&lt;br /&gt;
(see 16.2 Data Integrity for details). Our suballocation package can now&lt;br /&gt;
protect itself by using exclusive system semaphores to protect its critical&lt;br /&gt;
section and by registering a DosExitList handler to release that semaphore.&lt;br /&gt;
The exitlist code can discover if a thread in its process has orphaned the&lt;br /&gt;
semaphore and, if so, can release it. Of course, releasing the semaphore&lt;br /&gt;
won't help if the heap headers are in an inconsistent state. You can write&lt;br /&gt;
the suballocation package so that the heap is never in an inconsistent&lt;br /&gt;
state, or you can write it to keep track of the modified state so that the&lt;br /&gt;
exitlist handler can repair the heap structure.&lt;br /&gt;
     In this later case, be sure the DosExitList handler you establish to&lt;br /&gt;
clean up the heap is called first (see DosExitList documentation).&lt;br /&gt;
     Finally, even if we decide that the client application won't be&lt;br /&gt;
allowed to issue suballocation requests during its own exitlist processing,&lt;br /&gt;
we want the memory suballocator to support allocating a shared segment&lt;br /&gt;
among many different processes. Because of this, the actual OS/2&lt;br /&gt;
suballocation package makes use of DosExitList so that the suballocation&lt;br /&gt;
structure and semaphores can be cleaned up should a client thread terminate&lt;br /&gt;
while in the suballocation critical section.&lt;br /&gt;
     The suballocation dynlink package does more than illustrate subsystem&lt;br /&gt;
design; it also illustrates the value of a system architecture that uses&lt;br /&gt;
dynlinks as a standard system interface, regardless of the type of code&lt;br /&gt;
that provides the service. As you have seen, the memory suballocation&lt;br /&gt;
package released with OS/2 version 1.0 doesn't reside in the kernel; it's&lt;br /&gt;
effectively a subroutine package. OS/2 in an 80286 environment will&lt;br /&gt;
undoubtedly preserve this approach in future releases, but a forthcoming&lt;br /&gt;
80386 version of OS/2 may not. The 80386 architecture supports paged&lt;br /&gt;
virtual memory, so memory swapping (actually, paging) can take place on&lt;br /&gt;
part of a segment. This future paging environment may precipitate some&lt;br /&gt;
changes in the memory suballocator. Perhaps we'll want to rearrange the&lt;br /&gt;
heap for better efficiency with paging, or perhaps the OS/2 kernel will&lt;br /&gt;
want to become involved so that it can better anticipate paging demands. In&lt;br /&gt;
any case, any future release of OS/2 has complete flexibility to upgrade&lt;br /&gt;
the memory suballocation package in any externally compatible fashion,&lt;br /&gt;
thanks to the standard interface provided by dynamic links.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.3  Segment Swapping&lt;br /&gt;
&lt;br /&gt;
One of the most important features of the 80286 memory management hardware&lt;br /&gt;
is swapping support. Swapping is a technique by which some code or data&lt;br /&gt;
segments in memory are written to a disk file, thus allowing the memory&lt;br /&gt;
they were using to be reclaimed for another purpose. Later, the swapped-out&lt;br /&gt;
code or data is reloaded into memory. This technique lets you run more&lt;br /&gt;
programs than can simultaneously fit in memory; all you need is enough&lt;br /&gt;
memory to hold the programs that are running at that particular moment.&lt;br /&gt;
Quiescent programs can be swapped to disk to make room for active ones.&lt;br /&gt;
Later, when the swapped programs become active, OS/2 reads them in and&lt;br /&gt;
resumes them. If necessary OS/2 first makes memory available by swapping&lt;br /&gt;
out another quiescent program.&lt;br /&gt;
     Although I used the word program above, swapping is actually done on a&lt;br /&gt;
segment basis. Segments are swapped out individually and completely; the&lt;br /&gt;
OS/2 swapping code doesn't pay attention to relationships between segments&lt;br /&gt;
(they aren't swapped in groups), and the 80286 hardware does not allow only&lt;br /&gt;
part of a segment to be swapped. I simplified the concept a bit in the&lt;br /&gt;
above paragraph. You need not swap out an entire process; you can swap out&lt;br /&gt;
some segments and leave others in memory. OS/2 can and commonly does run a&lt;br /&gt;
process when some of its segments are swapped out. As long as a process&lt;br /&gt;
does not try to use the swapped-out segments, it runs unhindered. If a&lt;br /&gt;
process references a swapped-out segment, the 80286 hardware generates a&lt;br /&gt;
special trap that OS/2 intercepts. The segment fault trap handler swaps in&lt;br /&gt;
the missing segment, first swapping out some other if need be, and then the&lt;br /&gt;
process resumes where it left off. Segment faulting is invisible to a&lt;br /&gt;
process; the process executes normally, except that a segment load&lt;br /&gt;
instruction takes on the order of 30 milliseconds instead of the usual 3&lt;br /&gt;
microseconds.&lt;br /&gt;
     When memory is depleted and a segment must be swapped, OS/2 has to&lt;br /&gt;
choose one to swap out. Making the right choice is important; for example,&lt;br /&gt;
consider a process that alternates references between segment A and segment&lt;br /&gt;
B. If A is swapped out, a poorly designed system might choose B to swap out&lt;br /&gt;
to make room for A. After a few instructions are executed, B has to be&lt;br /&gt;
swapped in. If A is in turn swapped out to make room for B, the system&lt;br /&gt;
would soon spend all its time swapping A and B to and from the disk. This&lt;br /&gt;
is called thrashing, and thrashing can destroy system performance. In other&lt;br /&gt;
words, the effect of swapping is to make some segment loads take 10,000&lt;br /&gt;
times longer than they would if the segment were in memory. Although the&lt;br /&gt;
number 10,000 seems very large, the actual time of about 30 milliseconds is&lt;br /&gt;
not, as long as we don't have to pay those 30 milliseconds very often.&lt;br /&gt;
     A lot hinges on choosing segments to swap out that won't be referenced&lt;br /&gt;
in the near future. OS/2 uses the LRU (Least Recently Used) scheme to&lt;br /&gt;
determine which segment it will swap out. The ideal choice is the segment--&lt;br /&gt;
among those currently in memory--that will be referenced last because this&lt;br /&gt;
postpones the swap-in of that segment as long as possible. Unfortunately,&lt;br /&gt;
it's mathematically provable that no operating system can predict the&lt;br /&gt;
behavior of arbitrary processes. Instead, operating systems try to make an&lt;br /&gt;
educated guess as to which segment in memory is least likely to be&lt;br /&gt;
referenced in the immediate future. The LRU scheme is precisely that--a&lt;br /&gt;
good guess. OS/2 figures that if a segment hasn't been used in a long time&lt;br /&gt;
then it probably won't be used for a long time yet, so it swaps out the&lt;br /&gt;
segment that was last used the longest time ago--in other words, the least&lt;br /&gt;
recently used segment.&lt;br /&gt;
     Of course, it's easy to construct an example where the LRU decision is&lt;br /&gt;
the wrong one or even the worst one. The classic example is a program that&lt;br /&gt;
references, round robin, N segments when there is room in memory for only&lt;br /&gt;
N-1. When you attempt to make room for segment I, the least recently used&lt;br /&gt;
segment will be I+1, which in fact is the segment that will next be used. A&lt;br /&gt;
discussion of reference locality and working set problems, as these are&lt;br /&gt;
called, is beyond the scope of this book. Authors of programs that will&lt;br /&gt;
make repetitious accesses to large bodies of data or code should study the&lt;br /&gt;
available literature on virtual memory systems. Remember, on an 80286, OS/2&lt;br /&gt;
swaps only on a segment basis. A future 80386 release of OS/2 will swap, or&lt;br /&gt;
page, on a 4 KB page basis.&lt;br /&gt;
     The swapping algorithm is strictly LRU among all swap-eligible&lt;br /&gt;
segments in the system. Thread/process priority is not considered; system&lt;br /&gt;
segments that are marked swappable get no special treatment. Some system&lt;br /&gt;
segments are marked nonswappable, however. For example, swapping out the&lt;br /&gt;
OS/2 code that performs swap-ins would be embarrassing. Likewise, the disk&lt;br /&gt;
driver code for the swapping disk must not be swapped out. Some kernel and&lt;br /&gt;
device driver code is called at interrupt time; this is never swapped&lt;br /&gt;
because of the swap-in delay and because of potential interference between&lt;br /&gt;
the swapped-out interrupt handling code and the interrupt handling code of&lt;br /&gt;
the disk driver that will do the swap-in. Finally, some kernel code is&lt;br /&gt;
called in real mode in response to requests from the 3x box. No real mode&lt;br /&gt;
code can be swapped because the processor does not support segment faults&lt;br /&gt;
when running in real mode.&lt;br /&gt;
     The technique of running more programs then there is RAM to hold them&lt;br /&gt;
is called memory overcommit. OS/2 has to keep careful track of the degree&lt;br /&gt;
of overcommit so that it doesn't find itself with too much of a good thing-&lt;br /&gt;
-not enough free RAM, even with swapping, to swap in a swapped-out process.&lt;br /&gt;
Such a situation is doubly painful: Not only can the user not access or&lt;br /&gt;
save the data that he or she has spent the last four hours working on, but&lt;br /&gt;
OS/2 can't even tell the program what's wrong because it can't get the&lt;br /&gt;
program into memory to run it. To prevent this, OS/2 keeps track of its&lt;br /&gt;
commitments and overcommitments in two ways. First, before it starts a&lt;br /&gt;
process, OS/2 ensures that there is enough swap space to run it. Second, it&lt;br /&gt;
ensures that there is always enough available RAM to execute a swapped-out&lt;br /&gt;
process.&lt;br /&gt;
     At first glance, knowing if RAM is sufficient to run a process seems&lt;br /&gt;
simple--either the process fits into memory or it doesn't. Life is a bit&lt;br /&gt;
more complicated than that under OS/2 because the segments of a program or&lt;br /&gt;
a dynlink library may be marked for demand loading. This means that they&lt;br /&gt;
won't come in when the program starts executing but may be called in later.&lt;br /&gt;
Obviously, once a program starts executing, it can make nearly unlimited&lt;br /&gt;
demands for memory. When a program requests a memory allocation, however,&lt;br /&gt;
OS/2 can return an error code if available memory is insufficient. The&lt;br /&gt;
program can then deal with the problem: make do with less, refuse the&lt;br /&gt;
user's command, and so forth.&lt;br /&gt;
     OS/2 isn't concerned about a program's explicit memory requests&lt;br /&gt;
because they can always be refused; the implicit memory requests are the&lt;br /&gt;
problem--faulting in a demand load segment, for example. Not only is there&lt;br /&gt;
no interface to give the program an error code,&lt;br /&gt;
4. A demand load segment is faulted in via a &amp;quot;load segment register&amp;quot;&lt;br /&gt;
instruction. These CPU instructions don't return error codes!&lt;br /&gt;
4 but the program may be&lt;br /&gt;
unable to proceed without the segment. As a result, when a program is first&lt;br /&gt;
loaded (via a DosExecPgm call), OS/2 sums the size of all its impure&lt;br /&gt;
segments even if they are marked for &amp;quot;load on demand.&amp;quot; The same computation&lt;br /&gt;
is done for all the loadtime dynlink libraries it references and for all&lt;br /&gt;
the libraries they reference and so on. This final number, plus the&lt;br /&gt;
internal system per-process overhead, is the maximum implicit memory demand&lt;br /&gt;
of the program. If that much free swap space is available, the program can&lt;br /&gt;
start execution.&lt;br /&gt;
     You have undoubtedly noticed that I said we could run the program if&lt;br /&gt;
there was enough swap space. But a program must be in RAM to execute,&lt;br /&gt;
so why don't we care about the amount of available RAM space? We&lt;br /&gt;
do care. Not about the actual amount of free RAM when we start a program,&lt;br /&gt;
but about the amount of RAM that can be made free--by swapping--if needed.&lt;br /&gt;
If some RAM contains a swappable segment, then we can swap it&lt;br /&gt;
because we set aside enough swap space for the task. Pure segments, by the&lt;br /&gt;
way, are not normally swapped. In lieu of a swap-out, OS/2 simply discards&lt;br /&gt;
them. When it's time to swap them in, OS/2 reloads them from their original&lt;br /&gt;
.EXE or .DLL files.&lt;br /&gt;
5. An exception to this is programs that were executed from removable&lt;br /&gt;
media. OS/2 preloads all pure segments from such .EXE and .DLL files&lt;br /&gt;
and swaps them as necessary. This prevents certain deadlock problems&lt;br /&gt;
involving the hard error daemon and the volume management code.&lt;br /&gt;
5&lt;br /&gt;
     Because not all segments of a process need to be in memory for the&lt;br /&gt;
process to execute, we don't have to ensure enough free RAM for the entire&lt;br /&gt;
process, just enough so that we can simultaneously load six 64 KB segments-&lt;br /&gt;
-the maximum amount of memory needed to run any process. The numbers 6 and&lt;br /&gt;
64 KB are derived from the design of the 80286. To execute even a single&lt;br /&gt;
instruction of a process, all the segments selected by the four segment&lt;br /&gt;
registers must be in memory. The other two necessary segments come from the&lt;br /&gt;
worst case scenario of a program trying to execute a far return instruction&lt;br /&gt;
from a ring 2 segment (see 18.1 I/O Privilege Mechanism). The four&lt;br /&gt;
segments named in the registers must be present for the instruction to&lt;br /&gt;
start, and the two new segments--CS and SS--that the far return instruction&lt;br /&gt;
will reference must be present for the instruction to complete. That makes&lt;br /&gt;
six; the 64 KB comes from the maximum size a segment can reach. As a&lt;br /&gt;
result, as long as OS/2 can free up those six 64 KB memory regions, by&lt;br /&gt;
swapping and discarding if necessary, any swapped-out program can execute.&lt;br /&gt;
     Naturally, if that were the only available memory and it had to be&lt;br /&gt;
shared by all running processes, system response would be very poor.&lt;br /&gt;
Normally, much more RAM space is available. The memory overcommit code is&lt;br /&gt;
concerned only that all processes can run; it won't refuse to start a&lt;br /&gt;
process because it might execute slowly. It could be that the applications&lt;br /&gt;
that a particular user runs and their usage pattern are such that the user&lt;br /&gt;
finds the performance acceptable and thus hasn't bought more memory. Or&lt;br /&gt;
perhaps the slowness is a rare occurrence, and the user is willing to&lt;br /&gt;
accept it just this once. In general, if the system thrashes--&lt;br /&gt;
spends too much time swapping--it's a soft failure: The user knows what's&lt;br /&gt;
wrong, the user knows what to do to make it get better (run fewer programs&lt;br /&gt;
or buy more memory), and the user can meanwhile continue to work.&lt;br /&gt;
     Clearly, because all segments of the applications are swappable and&lt;br /&gt;
because we've ensured that the swap space is sufficient for all of them,&lt;br /&gt;
initiating a new process doesn't consume any of our free or freeable RAM.&lt;br /&gt;
It's the device drivers and their ability to allocate nonswappable segments&lt;br /&gt;
that can drain the RAM pool. For this reason, OS/2 may refuse to load a&lt;br /&gt;
device driver or to honor a device driver's memory allocation request if to&lt;br /&gt;
do so would leave less than six 64 KB areas of RAM available.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.3.1  Swapping Miscellany&lt;br /&gt;
The system swap space consists of a special file, called SWAPPER.DAT,&lt;br /&gt;
created at boot time. The location of the file is described in the&lt;br /&gt;
CONFIG.SYS file. OS/2 may not allocate the entire maximum size of the swap&lt;br /&gt;
file initially; instead, it may allocate a smaller size and grow the swap&lt;br /&gt;
file to its maximum size if needed. The swap file may grow, but in OS/2&lt;br /&gt;
version 1.0 it never shrinks.&lt;br /&gt;
     The available swap space in the system is more than the maximum size&lt;br /&gt;
of the swap file; it also includes extra RAM. Clearly, a system with 8 MB&lt;br /&gt;
of RAM and a 200 KB swap file should be able to run programs that consume&lt;br /&gt;
more than 200 KB. After setting aside the memory consumed by nonswappable&lt;br /&gt;
segments and our six 64 KB reserved areas, the remaining RAM is considered&lt;br /&gt;
part of the swap file for memory overcommit accounting purposes.&lt;br /&gt;
     We mentioned in passing that memory used in real mode can't be&lt;br /&gt;
swapped. This means that the entire 3x box memory area is nonswappable. In&lt;br /&gt;
fact, the casual attitude of MS-DOS applications toward memory allocation&lt;br /&gt;
forces OS/2 to keep a strict boundary between real mode and protect mode&lt;br /&gt;
memory. Memory below the RMSIZE value specified in CONFIG.SYS belongs&lt;br /&gt;
exclusively to the real mode program, minus that consumed by the device&lt;br /&gt;
drivers and the parts of the OS/2 kernel that run in real mode.&lt;br /&gt;
     Early in the development of OS/2, attempts were made to put protect&lt;br /&gt;
mode segments into any unused real mode memory, but we abandoned this&lt;br /&gt;
approach. First, because the risk was great that the real mode program&lt;br /&gt;
might overwrite part of the segment. Although this is technically a bug on&lt;br /&gt;
the part of the real mode application, such bugs generally do not affect&lt;br /&gt;
program execution in an MS-DOS environment because that memory is unused at&lt;br /&gt;
the time. Thus, such bugs undoubtedly exist unnoticed in today's MS-DOS&lt;br /&gt;
applications, waiting to wreak havoc in the OS/2 environment.&lt;br /&gt;
     A second reason concerns existing real mode applications having been&lt;br /&gt;
written for a single-tasking environment. Such an application commonly asks&lt;br /&gt;
for 1 MB of memory, a request that must be refused. The refusal, however,&lt;br /&gt;
also specifies the amount of memory available at the time of the call. Real&lt;br /&gt;
mode applications then turn around and ask for that amount, but they don't&lt;br /&gt;
check to see if an &amp;quot;insufficient memory&amp;quot; error code was returned from the&lt;br /&gt;
second call. After all, how could such a code be returned? The operating&lt;br /&gt;
system has just said that the memory was available. This coding sequence&lt;br /&gt;
can cause disaster in a multitasking environment where the memory might&lt;br /&gt;
have been allocated elsewhere between the first and second call from the&lt;br /&gt;
application. This is another reason OS/2 sets aside a fixed region of&lt;br /&gt;
memory for the 3x box and never uses it for other purposes, even if it&lt;br /&gt;
appears to be idle.&lt;br /&gt;
     We mentioned that OS/2's primary concern is that programs be able to&lt;br /&gt;
execute at all; whether they execute well is the user's problem. This&lt;br /&gt;
approach is acceptable because OS/2 is a single-user system. Multiuser&lt;br /&gt;
systems need to deal with thrashing situations because the users that&lt;br /&gt;
suffer from thrashing may not be the ones who created it and may be&lt;br /&gt;
powerless to alleviate it. In a single-user environment, however, the user&lt;br /&gt;
is responsible for the load that caused the thrashing, the user is the one&lt;br /&gt;
who is suffering from it, and the user is the one who can fix the situation&lt;br /&gt;
by buying more RAM or terminating a few applications. Nevertheless,&lt;br /&gt;
applications with considerable memory needs should be written so as to&lt;br /&gt;
minimize their impact on the system swapper.&lt;br /&gt;
     Fundamentally, all swapping optimization techniques boil down to one&lt;br /&gt;
issue: locality of reference. This means keeping the memory locations that&lt;br /&gt;
are referenced near one another in time and in space. If your program&lt;br /&gt;
supports five functions, put the code of each function in a separate&lt;br /&gt;
segment, with another segment holding common code. The user can then work&lt;br /&gt;
with one function, and the other segments can be swapped. If each function&lt;br /&gt;
had some code in each of five segments, all segments would have to be in&lt;br /&gt;
memory at all times.&lt;br /&gt;
     A large body of literature deals with these issues because of the&lt;br /&gt;
prevalence of virtual memory systems in the mainframe environment. Most of&lt;br /&gt;
this work was done when RAM was very expensive. To precisely determine&lt;br /&gt;
which segments or pages should be resident and which should be swapped was&lt;br /&gt;
worth a great deal of effort. Memory was costly, and swapping devices were&lt;br /&gt;
fast, so algorithms were designed to &amp;quot;crank the screws down tight&amp;quot; and free&lt;br /&gt;
up as much memory as possible. After all, if they misjudged and swapped&lt;br /&gt;
something that was needed soon, it could be brought back in quickly. The&lt;br /&gt;
OS/2 environment is inverted: RAM is comparatively cheap, and the swapping&lt;br /&gt;
disk, being the regular system hard disk, is comparatively slow.&lt;br /&gt;
Consequently, OS/2's swapping strategy is to identify segments that are&lt;br /&gt;
clearly idle and swap them (because cheap RAM doesn't mean free RAM) but&lt;br /&gt;
not to judge things so closely that segments are frequently swapped when&lt;br /&gt;
they should not be.&lt;br /&gt;
     A key concept derived from this classic virtual memory work is that of&lt;br /&gt;
the working set. A thread's working set is the set of segments it will&lt;br /&gt;
reference &amp;quot;soon&amp;quot;--in the next several seconds or few minutes. Programmers&lt;br /&gt;
should analyze their code to determine its working sets; obviously the set&lt;br /&gt;
of segments in the working set will vary with the work the application is&lt;br /&gt;
doing. Code and data should be arranged between segments so that the size&lt;br /&gt;
of each common working set consists of a minimum amount of memory. For&lt;br /&gt;
example, if a program contains extensive code and data to deal with&lt;br /&gt;
uncommon error situations, these items should reside in separate segments&lt;br /&gt;
so that they aren't resident except when needed. You don't want to burden&lt;br /&gt;
the system with too many segments; two functions that are frequently used&lt;br /&gt;
together should occupy the same segment, but large unrelated bodies of code&lt;br /&gt;
and data should have their own segments or be grouped with other items that&lt;br /&gt;
are in their working set. Consider segment size when packing items into&lt;br /&gt;
segments. Too many small segments increase system overhead; large segments&lt;br /&gt;
decrease the efficiency of the swap mechanism. Splitting a segment in two&lt;br /&gt;
doesn't make sense if all code in the segment belongs to the same working&lt;br /&gt;
set, but it does make sense to split large bodies of unrelated code and&lt;br /&gt;
data.&lt;br /&gt;
     As we said before, an exhaustive discussion of these issues is beyond&lt;br /&gt;
the scope of this book. Programmers writing memory-intensive applications&lt;br /&gt;
should study the literature and their programs to optimize their&lt;br /&gt;
performance in an OS/2 environment. Minimizing an application's memory&lt;br /&gt;
requirements is more than being a &amp;quot;good citizen&amp;quot;; the smaller a program's&lt;br /&gt;
working set, the better it will run when the system load picks up.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.4  Status and Information&lt;br /&gt;
&lt;br /&gt;
OS/2 takes advantage of the 80286 LDT and GDT architecture in providing two&lt;br /&gt;
special segments, called infosegs, that contain system information. OS/2&lt;br /&gt;
updates these segments when changes take place, so their information is&lt;br /&gt;
always current. One infoseg is global, and the other is local. The global&lt;br /&gt;
infoseg contains information about the system as a whole; the local infoseg&lt;br /&gt;
contains process specific data. Naturally, the global infoseg is read only&lt;br /&gt;
and is shared among all processes. Local infosegs are also read only, but&lt;br /&gt;
each process has its own.&lt;br /&gt;
     The global infoseg contains time and date information. The &amp;quot;seconds&lt;br /&gt;
elapsed since 1970&amp;quot; field is particularly useful for time-stamping events&lt;br /&gt;
because calculating the interval between two times is easy. Simply subtract&lt;br /&gt;
and then divide by the number of seconds in the unit of time in which&lt;br /&gt;
you're interested. It's important that you remember that the date/time&lt;br /&gt;
fields are 32-bit fields but the 80286 reads data 16 bits at a time. Thus,&lt;br /&gt;
if an application reads the two time-stamp words at the same time as they&lt;br /&gt;
are being updated, it may read a bad value--not a value off by 1, but a&lt;br /&gt;
value that is off by 63335. The easiest way to deal with this is to read&lt;br /&gt;
the value and then compare the just read value with the infoseg contents.&lt;br /&gt;
If they are the same, your read value is correct. If they differ, continue&lt;br /&gt;
reading and comparing until the read and infoseg  values agree. The RAS&lt;br /&gt;
6. Reliability, Availability, and Serviceability. A buzzword that refers&lt;br /&gt;
to components intended to aid field diagnosis of system malfunctions.&lt;br /&gt;
6&lt;br /&gt;
information is used for field system diagnosis and is not of general&lt;br /&gt;
interest to programmers.&lt;br /&gt;
     The local infoseg segment contains process and thread information. The&lt;br /&gt;
information is accurate for the currently executing thread. The subscreen&lt;br /&gt;
group value is used by the presentation manager subsystem and is not of&lt;br /&gt;
value to applications. For more information on global and local infosegs,&lt;br /&gt;
see the OS/2 reference manual.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==10  Environment Strings==&lt;br /&gt;
&lt;br /&gt;
A major requirement of OS/2 is the ability to support logical device and&lt;br /&gt;
directory names. For example, a program needs to write a temporary scratch&lt;br /&gt;
file to the user's fastest disk. Which disk is that? Is it drive C, the&lt;br /&gt;
hard disk? Some machines don't have a hard disk. Is it drive B, the floppy&lt;br /&gt;
drive? Some machines don't have a drive B. And even if a hard disk on drive&lt;br /&gt;
C exists, maybe drive D also exists and has more free space. Or perhaps&lt;br /&gt;
drive E is preferred because it's a RAM disk. Perhaps it's not, though,&lt;br /&gt;
because the user wants the scratch file preserved when the machine is&lt;br /&gt;
powered down. This program needs the ability to specify a logical&lt;br /&gt;
directory--the scratch file directory--rather than a physical drive and&lt;br /&gt;
directory such as A:\ or C:\TEMP. The user could then specify the physical&lt;br /&gt;
location (drive and directory) that corresponds to the logical&lt;br /&gt;
directory.&lt;br /&gt;
     Another example is a spell-checker program that stores two&lt;br /&gt;
dictionaries on a disk. Presumably, the dictionary files were copied to a&lt;br /&gt;
hard disk when the program was installed, but on which drive and directory?&lt;br /&gt;
The checker's author could certainly hard code a directory such as&lt;br /&gt;
C:\SPELLCHK\DICT1. But what if the user doesn't have a C drive, or what if&lt;br /&gt;
drive C is full and the user wants to use drive D instead? How can this&lt;br /&gt;
program offer the user the flexibility of putting the dictionary files&lt;br /&gt;
where they best fit and yet still find them when it needs them?&lt;br /&gt;
     The answer to these problems is a logical device and directory name&lt;br /&gt;
facility. Such a facility should have three characteristics:&lt;br /&gt;
&lt;br /&gt;
     þ  It should allow the user to map the logical directories onto the&lt;br /&gt;
        actual (physical) devices and directories at will. It should be&lt;br /&gt;
        possible to change these mappings without changing the programs&lt;br /&gt;
        that use them.&lt;br /&gt;
&lt;br /&gt;
     þ  The set of possible logical devices and directories should be very&lt;br /&gt;
        large and arbitrarily expandable. Some new program, such as our&lt;br /&gt;
        spelling checker, will always need a new logical directory.&lt;br /&gt;
&lt;br /&gt;
     þ  The name set should be large and collision free. Many programs will&lt;br /&gt;
        want to use logical directory names. If all names must come from a&lt;br /&gt;
        small set of possibilities, such as X1, X2, X3, and so on, two&lt;br /&gt;
        applications, written independently, may each choose the same name&lt;br /&gt;
        for conflicting uses.&lt;br /&gt;
&lt;br /&gt;
     The original version of MS-DOS did not provide for logical devices and&lt;br /&gt;
directories. In those days a maximum PC configuration consisted of two&lt;br /&gt;
floppy disks. Operating the machine entailed playing a lot of &amp;quot;disk jockey&amp;quot;&lt;br /&gt;
as the user moved system, program, and data disks in and out of the drives.&lt;br /&gt;
The user was the only one who could judge which drive should contain which&lt;br /&gt;
floppy and its associated data, and data files moved from drive to drive&lt;br /&gt;
dynamically. A logical device mechanism would have been of little use.&lt;br /&gt;
Logical directories were not needed because MS-DOS version 1.0 didn't&lt;br /&gt;
support directories. MS-DOS versions 2.x and 3.x propagated the &amp;quot;physical&lt;br /&gt;
names only&amp;quot; architecture because of memory limitations and because of the&lt;br /&gt;
catch-22 of new operating system features: Applications won't take&lt;br /&gt;
advantage of the new feature because many machines are running older&lt;br /&gt;
versions of MS-DOS without that new feature.&lt;br /&gt;
     None of these reasons holds true for OS/2. All OS/2 protect mode&lt;br /&gt;
applications will be rewritten. OS/2 has access to plenty of memory.&lt;br /&gt;
Finally, OS/2 needs a logical drive/directory mechanism: All OS/2 machines&lt;br /&gt;
have hard disks or similar facilities, and all OS/2 machines will run a&lt;br /&gt;
variety of sophisticated applications that need access to private files and&lt;br /&gt;
work areas. As a result, the environment string mechanism in MS-DOS has&lt;br /&gt;
been expanded to serve as the logical name in OS/2.&lt;br /&gt;
     Because of the memory allocation techniques employed by MS-DOS&lt;br /&gt;
programs and because of the lack of segment motion and swapping in real&lt;br /&gt;
mode, the MS-DOS environment list was very limited in size. The size of the&lt;br /&gt;
environment segment was easily exceeded. OS/2 allows environment segments&lt;br /&gt;
to be grown arbitrarily, at any time, subject only to the hardware's 64 KB&lt;br /&gt;
length limitation. In keeping with the OS/2 architecture, each process has&lt;br /&gt;
its own environment segment. By default, the child inherits a copy of the&lt;br /&gt;
parent's segment, but the parent can substitute other environment values at&lt;br /&gt;
DosExecPgm time.&lt;br /&gt;
     Using the environment string facility to provide logical names is&lt;br /&gt;
straightforward. If a convention for the logical name that you need doesn't&lt;br /&gt;
already exist, you must choose a meaningful name. Your installation&lt;br /&gt;
instructions or software should document how to use the environment string;&lt;br /&gt;
the application should display an error message or use an appropriate&lt;br /&gt;
default if the logical names do not appear in the environment string.&lt;br /&gt;
Because each process has its own environment segment that it inherited from&lt;br /&gt;
its parent, batch files, startup scripts, and initiator programs that load&lt;br /&gt;
applications can conveniently set up the necessary strings. This also&lt;br /&gt;
allows several applications or multiple copies of the same application to&lt;br /&gt;
define the same logical name differently.&lt;br /&gt;
     The existing conventions are:&lt;br /&gt;
&lt;br /&gt;
     PATH=&lt;br /&gt;
     PATH defines a list of directories that CMD.EXE searches when it has&lt;br /&gt;
     been instructed to execute a program. The directories are searched&lt;br /&gt;
     from left to right and are separated by semicolons. For example,&lt;br /&gt;
&lt;br /&gt;
        PATH=C:\BIN;D:\TOOLS;.&lt;br /&gt;
&lt;br /&gt;
     means search C:\BIN first, D:\TOOLS second, and the current working&lt;br /&gt;
     directory third.&lt;br /&gt;
&lt;br /&gt;
     DPATH=&lt;br /&gt;
     DPATH defines a list of directories that programs may search to locate&lt;br /&gt;
     a data file. The directories are searched from left to right and are&lt;br /&gt;
     separated by semicolons. For example:&lt;br /&gt;
&lt;br /&gt;
        DPATH=C:\DBM;D:\TEMP;.&lt;br /&gt;
&lt;br /&gt;
Applications use DPATH as a convenience to the user: A user can work from&lt;br /&gt;
one directory and reference data files in another directory, named in the&lt;br /&gt;
DPATH string, without specifying the full path names of the data files.&lt;br /&gt;
Obviously, applications and users must use this technique with care.&lt;br /&gt;
Searching too widely for a filename is extremely dangerous; the wrong file&lt;br /&gt;
may be found because filenames themselves are often duplicated in different&lt;br /&gt;
directories. To use the DPATH string, an application must first use&lt;br /&gt;
DosScanEnv to locate the DPATH string, and then it must use DosSearchPath&lt;br /&gt;
to locate the data file.&lt;br /&gt;
&lt;br /&gt;
     INCLUDE=&lt;br /&gt;
     The INCLUDE name defines the drive and directory where compiler and&lt;br /&gt;
     assembler standard include files are located.&lt;br /&gt;
&lt;br /&gt;
     INIT=&lt;br /&gt;
     The INIT name defines the drive and directory that contains&lt;br /&gt;
     initialization and configuration information for the application. For&lt;br /&gt;
     example, some applications define files that contain the user's&lt;br /&gt;
     preferred defaults. These files might be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
     LIB=&lt;br /&gt;
     The LIB name defines the drive and directory where the standard&lt;br /&gt;
     language library modules are kept.&lt;br /&gt;
&lt;br /&gt;
     PROMPT=&lt;br /&gt;
     The PROMPT name defines the CMD.EXE prompt string. Special character&lt;br /&gt;
     sequences are defined so that the CMD.EXE prompt can contain the&lt;br /&gt;
     working directory, the date and time, and so on. See CMD.EXE&lt;br /&gt;
     documentation for details.&lt;br /&gt;
&lt;br /&gt;
     TEMP=&lt;br /&gt;
     The TEMP name defines the drive and directory for temporary files.&lt;br /&gt;
     This directory is on a device that is relatively fast and has&lt;br /&gt;
     sufficient room for scratch files. The TEMP directory should be&lt;br /&gt;
     considered volatile; its contents can be lost during a reboot&lt;br /&gt;
     operation.&lt;br /&gt;
&lt;br /&gt;
     The environment segment is a very flexible tool that you can use to&lt;br /&gt;
customize the environment of an application or a group of applications. For&lt;br /&gt;
example, you can use environment strings to specify default options for&lt;br /&gt;
applications. Users can use the same systemwide default or change that&lt;br /&gt;
value for a particular screen group or activation of the application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==11  Interprocess Communication==&lt;br /&gt;
&lt;br /&gt;
Interprocess Communication (IPC) is central to OS/2. As we discussed&lt;br /&gt;
earlier, effective IPC is needed to support both the tool-based&lt;br /&gt;
architecture and the dynlink interface for interprocess services. Because&lt;br /&gt;
IPC is so important, OS/2 provides several forms to fulfill a variety of&lt;br /&gt;
needs.&lt;br /&gt;
&lt;br /&gt;
===11.1  Shared Memory===&lt;br /&gt;
&lt;br /&gt;
Shared memory has already been discussed in some detail. To summarize, the&lt;br /&gt;
two forms are named shared memory (access is requested by the client by&lt;br /&gt;
name) and giveaway shared memory (a current owner gives access to another&lt;br /&gt;
process). Shared memory is the most efficient form of IPC because no data&lt;br /&gt;
copying or calls to the operating system kernel are involved once the&lt;br /&gt;
shared memory has been set up. Shared memory does require more effort on&lt;br /&gt;
the part of the client processes; a protocol must be established,&lt;br /&gt;
semaphores and flags are usually needed, and exposure to amok programs and&lt;br /&gt;
premature termination must be considered. Applications that expect to deal&lt;br /&gt;
with a low volume of data may want to consider using named pipes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2  Semaphores&lt;br /&gt;
&lt;br /&gt;
A semaphore is a flag or a signal. In its basic form a semaphore has only&lt;br /&gt;
two states--on and off or stop and go. A railroad semaphore, for example,&lt;br /&gt;
is either red or green--stop or go. In computer software, a semaphore is a&lt;br /&gt;
flag or a signal used by one thread of execution to flag or signal&lt;br /&gt;
another. Often it's for purposes of mutual exclusion: &amp;quot;I'm in here, stay&lt;br /&gt;
out.&amp;quot; But sometimes it can be used to indicate other events: &amp;quot;Your data is&lt;br /&gt;
ready.&amp;quot;&lt;br /&gt;
     OS/2 supports two kinds of semaphores, each of which can be used in&lt;br /&gt;
two different ways. The two kinds of semaphores--RAM semaphores and system&lt;br /&gt;
semaphores--have a lot in common, and the same system API is used to&lt;br /&gt;
manipulate both. A RAM semaphore, as its name implies, uses a 4-byte data&lt;br /&gt;
structure kept in a RAM location that must be accessible to all threads&lt;br /&gt;
that use it. The system API that manipulates RAM semaphores is located in a&lt;br /&gt;
dynlink subsystem. This code claims semaphores with an atomic test-and-set&lt;br /&gt;
operation,&lt;br /&gt;
1. An atomic operation is one that is indivisible and therefore&lt;br /&gt;
cannot be interrupted in the middle.&lt;br /&gt;
1 so it need not enter the kernel (ring 0) to protect itself&lt;br /&gt;
against preemption. As a result, the most common tasks--claiming a free&lt;br /&gt;
semaphore and freeing a semaphore that has no waiters--are very fast, on&lt;br /&gt;
the order of 100 microseconds on a 6-MHz 1-wait-state IBM PC/AT.&lt;br /&gt;
2. This is the standard environment when quoting speeds; it's&lt;br /&gt;
both the common case and the worst case. Machines that don't run&lt;br /&gt;
at this speed run faster.&lt;br /&gt;
2 If the&lt;br /&gt;
semaphore is already claimed and the caller must block or if another thread&lt;br /&gt;
is waiting on the semaphore, the semaphore dynlink package must enter&lt;br /&gt;
kernel mode.&lt;br /&gt;
     System semaphores, on the other hand, use a data structure that is&lt;br /&gt;
kept in system memory outside the address space of any process. Therefore,&lt;br /&gt;
system semaphore operations are slower than RAM semaphore operations, on&lt;br /&gt;
the order of 350 microseconds for an uncontested semaphore claim. Some&lt;br /&gt;
important advantages offset this operating speed however. System semaphores&lt;br /&gt;
support mechanisms that prevent deadlock by crashing programs, and system&lt;br /&gt;
semaphores support exclusivity and counting features. As a general rule,&lt;br /&gt;
you should use RAM semaphores when the requirement is wholly contained&lt;br /&gt;
within one process. When multiple processes may be involved, use system&lt;br /&gt;
semaphores.&lt;br /&gt;
     The first step, regardless of the type or use of the semaphore, is to&lt;br /&gt;
create it. An application creates RAM semaphores simply by allocating a 4-&lt;br /&gt;
byte area of memory initialized to zero. The far address of this area is&lt;br /&gt;
the RAM semaphore handle. The DosCreateSem call creates system semaphores.&lt;br /&gt;
(The DosCreateSem call takes an exclusivity argument, which we'll discuss&lt;br /&gt;
later.) Although semaphores control thread execution, semaphore handles&lt;br /&gt;
are owned by the process. Once a semaphore is created and its handle&lt;br /&gt;
obtained, all threads in that process can use that handle. Other&lt;br /&gt;
processes must open the semaphore via DosOpenSem. There is no explicit&lt;br /&gt;
open for a RAM semaphore. To be useful for IPC, the RAM semaphore must&lt;br /&gt;
be in a shared memory segment so that another process can access it;&lt;br /&gt;
the other process simply learns the far address of the RAM semaphore. A RAM&lt;br /&gt;
semaphore is initialized by zeroing out its 4-byte memory area.&lt;br /&gt;
     Except for opening and closing, RAM and system semaphores use exactly&lt;br /&gt;
the same OS/2 semaphore calls. Each semaphore call takes a semaphore handle&lt;br /&gt;
as an argument. A RAM semaphore's handle is its address; a system&lt;br /&gt;
semaphore's handle was returned by the create or open call. The OS/2&lt;br /&gt;
semaphore routines can distinguish between RAM and system semaphores by&lt;br /&gt;
examining the handle they are passed. Because system semaphores and their&lt;br /&gt;
names are kept in an internal OS/2 data area, they are a finite resource;&lt;br /&gt;
the number of RAM semaphores is limited only by the amount of available RAM&lt;br /&gt;
to hold them.&lt;br /&gt;
     The most common use of semaphores is to protect critical sections. To&lt;br /&gt;
reiterate, a critical section is a body of code that manipulates a data&lt;br /&gt;
resource in a nonreentrant way. In other words, a critical section will&lt;br /&gt;
screw up if two threads call it at the same time on the same data resource.&lt;br /&gt;
A critical section can cover more than one section of code; if one&lt;br /&gt;
subroutine adds entries to a table and another subroutine removes entries,&lt;br /&gt;
both subroutines are in the table's critical section. A critical section is&lt;br /&gt;
much like an airplane washroom, and the semaphore is like the sign that&lt;br /&gt;
says &amp;quot;Occupied.&amp;quot; The first user sets the semaphore and starts manipulating&lt;br /&gt;
the resource; meanwhile others arrive, see that the semaphore is set, and&lt;br /&gt;
block (that is, wait) outside. When the critical section becomes available&lt;br /&gt;
and the semaphore is cleared, only one of the waiting threads gets to claim&lt;br /&gt;
it; the others keep on waiting.&lt;br /&gt;
     Using semaphores to protect critical sections is straightforward. At&lt;br /&gt;
the top of a section of code that will manipulate the critical resource,&lt;br /&gt;
insert a call to DosSemRequest. When this call returns, the semaphore is&lt;br /&gt;
claimed, and the code can proceed. When the code is finished and the&lt;br /&gt;
critical section is &amp;quot;clean,&amp;quot; call DosSemClear. DosSemClear releases the&lt;br /&gt;
semaphore and reactivates any thread waiting on it.&lt;br /&gt;
     System semaphores are different from RAM semaphores in this&lt;br /&gt;
application in one critical respect. If a system semaphore is created for&lt;br /&gt;
exclusive use, it can be used as a counting semaphore. Exclusive use means&lt;br /&gt;
that only the thread that set the semaphore can clear it;&lt;br /&gt;
3. This is a departure from the principle of resource ownership&lt;br /&gt;
by process, not by thread. The thread, not the process, owns the&lt;br /&gt;
privilege to clear a set &amp;quot;exclusive use&amp;quot; semaphore.&lt;br /&gt;
3 this is expected&lt;br /&gt;
when protecting critical sections. A counting semaphore can be set many&lt;br /&gt;
times but must be released an equal number of times before it becomes free.&lt;br /&gt;
For example, an application contains function A and function B, each of&lt;br /&gt;
which manipulates the same critical section. Each claims the semaphore at&lt;br /&gt;
its beginning and releases it at its end. However, under some&lt;br /&gt;
circumstances, function A may need to call function B. Function A can't&lt;br /&gt;
release the semaphore before it calls B because it's still in the critical&lt;br /&gt;
section and the data is in an inconsistent state. But when B issues&lt;br /&gt;
DosSemRequest on the semaphore, it blocks because the semaphore was already&lt;br /&gt;
set by A.&lt;br /&gt;
     A counting semaphore solves this problem. When function B makes the&lt;br /&gt;
second, redundant DosSemRequest call, OS/2 recognizes it as the same thread&lt;br /&gt;
that already owns the semaphore, and instead of blocking the thread, it&lt;br /&gt;
increments a counter to show that the semaphore has been claimed twice.&lt;br /&gt;
Later, when function B releases the semaphore, OS/2 decrements the counter.&lt;br /&gt;
Because the counter is not at zero, the semaphore is not really clear and&lt;br /&gt;
thus not released. The semaphore is truly released only after function B&lt;br /&gt;
returns to function A, and A, finishing its work, releases the semaphore a&lt;br /&gt;
second time.&lt;br /&gt;
     A second major use of semaphores is signaling (unrelated to the signal&lt;br /&gt;
facility of OS/2). Signaling is using semaphores to notify threads that&lt;br /&gt;
certain events or activities have taken place. For example, consider a&lt;br /&gt;
multithreaded application that uses one thread to communicate over a serial&lt;br /&gt;
port and another thread to compute with the results of that communication.&lt;br /&gt;
The computing thread tells the communication thread to send a message and&lt;br /&gt;
get a reply, and then it goes about its own business. Later, the computing&lt;br /&gt;
thread wants to block until the reply is received but only if the reply&lt;br /&gt;
hasn't already been received--it may have already arrived, in which case&lt;br /&gt;
the computing thread doesn't want to block.&lt;br /&gt;
     You can handle this by using a semaphore as a flag. The computing&lt;br /&gt;
thread sets the semaphore via DosSemSet before it gives the order to the&lt;br /&gt;
communications thread. When the computing thread is ready to wait for the&lt;br /&gt;
reply, it does a DosSemWait on the semaphore it set earlier. When the&lt;br /&gt;
communications thread receives the reply, it clears the semaphore. When the&lt;br /&gt;
computing thread calls DosSemWait, it will continue without&lt;br /&gt;
delay if the semaphore is already clear. Otherwise, the computing thread&lt;br /&gt;
blocks until the semaphore is cleared. In this example, we aren't&lt;br /&gt;
protecting a critical section; we're using the semaphore transition&lt;br /&gt;
from set to clear to flag an event between multiple threads. Our needs are&lt;br /&gt;
the opposite of a critical section semaphore: We don't want the semaphore&lt;br /&gt;
to be exclusively owned; if it were, the communications thread couldn't&lt;br /&gt;
release it. We also don't want the semaphore to be counting. If it counts,&lt;br /&gt;
the computing thread won't block when it does the DosSemWait; OS/2 would&lt;br /&gt;
recognize that it did the DosSemSet earlier and would increment the&lt;br /&gt;
semaphore counter.&lt;br /&gt;
     OS/2 itself uses semaphore signaling in this fashion when asynchronous&lt;br /&gt;
communication is needed. For example, asynchronous I/O uses semaphores in&lt;br /&gt;
the signaling mode to indicate that an I/O operation has completed. The&lt;br /&gt;
system timer services use semaphores in the signaling mode to indicate that&lt;br /&gt;
the specified time has elapsed. OS/2 supports a special form of semaphore&lt;br /&gt;
waiting, called DosMuxSemWait, which allows a thread to wait on more than&lt;br /&gt;
one semaphore at one time. As soon as any specified semaphore becomes&lt;br /&gt;
clear, DosMuxSemWait returns. DosMuxSemWait, like DosSemWait, only waits&lt;br /&gt;
for a semaphore to become clear; it doesn't set or claim the semaphore as&lt;br /&gt;
does DosSemRequest. DosMuxSemWait allows a thread to wait on a variety of&lt;br /&gt;
events and to wake up whenever one of those events occurs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2.1  Semaphore Recovery&lt;br /&gt;
We discussed earlier some difficulties that can arise if a semaphore is&lt;br /&gt;
left set &amp;quot;orphaned&amp;quot; when its owner terminates unexpectedly. We'll review&lt;br /&gt;
the topic because it's critical that applications handle the situation&lt;br /&gt;
correctly and because that correctness generally has to be demonstrable by&lt;br /&gt;
inspection. It's very difficult to demonstrate and fix timing-related bugs&lt;br /&gt;
by just testing a program.&lt;br /&gt;
     Semaphores can become orphaned in at least four ways:&lt;br /&gt;
&lt;br /&gt;
     1.  An incoming signal can divert the CPU, and the signal handler can&lt;br /&gt;
         fail to return to the point of interruption.&lt;br /&gt;
&lt;br /&gt;
     2.  A process can kill another process without warning.&lt;br /&gt;
&lt;br /&gt;
     3.  A process can incur a GP fault, which is fatal.&lt;br /&gt;
&lt;br /&gt;
     4.  A process can malfunction because of a coding error and fail to&lt;br /&gt;
         release a semaphore.&lt;br /&gt;
&lt;br /&gt;
     The action to take in such events depends on how semaphores are being&lt;br /&gt;
used. In some situations, no action is needed. Our example of the computing&lt;br /&gt;
and communications threads is such a situation. If the process dies, the&lt;br /&gt;
semaphore and all its users die. Special treatment is necessary only if the&lt;br /&gt;
application uses DosExitList to run code that needs to use the semaphore.&lt;br /&gt;
This should rarely be necessary because semaphores are used within a&lt;br /&gt;
process to coordinate multiple threads and only one thread remains&lt;br /&gt;
when the exitlist is activated. Likewise, a process can receive signals&lt;br /&gt;
only if it has asked for them, so an application that does not use signals&lt;br /&gt;
need not worry about their interrupting its critical sections. An&lt;br /&gt;
application that does use signals can use DosHoldSignal, always return&lt;br /&gt;
from a signal handler, or prevent thread 1 (the signal-handling thread)&lt;br /&gt;
from entering critical sections.&lt;br /&gt;
     In other situations, the semaphore can protect a recoverable resource.&lt;br /&gt;
For example, you can use a system semaphore to protect access to a printer&lt;br /&gt;
that for some reason is being dealt with directly by applications rather&lt;br /&gt;
than by the system spooler. If the owner of the &amp;quot;I'm using the printer&amp;quot;&lt;br /&gt;
system semaphore dies unexpectedly, the next thread that tries to claim the&lt;br /&gt;
semaphore will be able to do so but will receive a special error code that&lt;br /&gt;
says, &amp;quot;The owner of this semaphore died while holding it.&amp;quot; In such a case,&lt;br /&gt;
the application can simply write a form feed or two to the printer and&lt;br /&gt;
continue. Other possible actions are to clean up the protected resource or&lt;br /&gt;
to execute a process that will do so. Finally, an application can display a&lt;br /&gt;
message to the user saying, &amp;quot;Gee, this database is corrupt! You better do&lt;br /&gt;
something,&amp;quot; and then terminate. In this case, the application should&lt;br /&gt;
deliberately terminate while holding the semaphore so that any other&lt;br /&gt;
threads waiting on it will receive the &amp;quot;owner died&amp;quot; message. Once the&lt;br /&gt;
&amp;quot;owner died&amp;quot; code is received, that state is cleared; so if the recipient&lt;br /&gt;
of the code releases the semaphore without fixing the inconsistencies in&lt;br /&gt;
the critical section, problems will result.&lt;br /&gt;
     Additional matters must be considered if a process intends to clean up&lt;br /&gt;
its own semaphores by means of a DosExitList handler. First, exclusive&lt;br /&gt;
(that is, counting) semaphores must be used. Although an exitlist routine&lt;br /&gt;
can tell that a RAM or nonexclusive system semaphore is reserved, it cannot&lt;br /&gt;
tell whether it is the process that reserved it. You may be tempted simply&lt;br /&gt;
to keep a flag byte that is set each time the semaphore is claimed and&lt;br /&gt;
cleared each time the semaphore is released, but that solution contains a&lt;br /&gt;
potentially deadly window of failure. If the thread sets the &amp;quot;I own it&amp;quot;&lt;br /&gt;
flag before it calls DosSemRequest, the thread could terminate between&lt;br /&gt;
setting the flag and receiving the semaphore. In that case, the exitlist&lt;br /&gt;
routine would believe, wrongly, that it owns the semaphore and would&lt;br /&gt;
therefore release it--a very unpleasant surprise for the true owner of the&lt;br /&gt;
semaphore. Conversely, if the thread claims the semaphore and then sets the&lt;br /&gt;
flag, a window exists in which the semaphore is claimed but the flag does&lt;br /&gt;
not say so. This is also disastrous.&lt;br /&gt;
     Using exclusive system semaphores solves these problems. As I&lt;br /&gt;
mentioned earlier, when the thread that has set a system semaphore dies&lt;br /&gt;
with the semaphore set, the semaphore is placed into a special &amp;quot;owner died&amp;quot;&lt;br /&gt;
state so that the next thread to attempt to claim the semaphore is informed&lt;br /&gt;
of its orphan status. There is an extra twist to this for exclusive-use&lt;br /&gt;
system semaphores. Should the process die due to an external cause or due&lt;br /&gt;
to a DosExit call and that process has a DosExitList handler, all orphaned&lt;br /&gt;
system semaphores are placed in a special &amp;quot;owner died&amp;quot; state so that only&lt;br /&gt;
that process's remaining thread--the one executing the DosExitList&lt;br /&gt;
handlers--can claim the semaphore. When it does so, it still receives the&lt;br /&gt;
special &amp;quot;owner died&amp;quot; code. The exitlist handler can use DosSemWait with a&lt;br /&gt;
timeout value of 0 to see if the semaphore is set. If the &amp;quot;owner died&amp;quot; code&lt;br /&gt;
is returned, then the DosExitList handler cleans up the resource and then&lt;br /&gt;
issues DosSemClear to clear the semaphore. If a thread terminates by&lt;br /&gt;
explicitly calling DosExit with the &amp;quot;terminate this thread&amp;quot; subcode, any&lt;br /&gt;
exclusive-use system semaphores that it has set will not enter this special&lt;br /&gt;
&amp;quot;owner died&amp;quot; state but will instead assume the general &amp;quot;owner died&amp;quot; state&lt;br /&gt;
that allows any thread in the system to claim the semaphore and receive the&lt;br /&gt;
&amp;quot;owner died&amp;quot; code. Likewise, any semaphores in the special &amp;quot;owner died&amp;quot;&lt;br /&gt;
state that are not cleared by the DosExitList handlers become normal &amp;quot;owner&lt;br /&gt;
died&amp;quot; semaphores when the process completely terminates.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2.2  Semaphore Scheduling&lt;br /&gt;
Although multiple threads can wait for a semaphore, only one thread gets&lt;br /&gt;
the semaphore when it becomes available. OS/2 schedules semaphore grants&lt;br /&gt;
based on CPU priority: The highest-priority waiting thread claims the&lt;br /&gt;
semaphore. If several waiting threads are at the highest priority, OS/2&lt;br /&gt;
distributes the grants among them evenly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.3  Named Pipes&lt;br /&gt;
&lt;br /&gt;
We've already discussed anonymous pipes--stream oriented IPC mechanisms&lt;br /&gt;
that work via the DosRead and DosWrite calls. Two processes can communicate&lt;br /&gt;
via anonymous pipes only if one is a descendant of the other and if the&lt;br /&gt;
descendant has inherited the parent's handle to the pipe. Anonymous pipes&lt;br /&gt;
are used almost exclusively to transfer input and output data to and from a&lt;br /&gt;
child process or to and from a subtree of child processes.&lt;br /&gt;
     OS/2 supports another form of pipes called named pipes. Named pipes&lt;br /&gt;
are not available in OS/2 version 1.0; they will be available in a later&lt;br /&gt;
release. I discuss them here because of their importance in the system&lt;br /&gt;
architecture. Also, because of the extensible nature of OS/2, it's possible&lt;br /&gt;
that named pipe functionality will be added to the system by including the&lt;br /&gt;
function in some other Microsoft system software package that, when it runs&lt;br /&gt;
under OS/2, installs the capability. In such a case, application programs&lt;br /&gt;
will be unable to distinguish the &amp;quot;add-on&amp;quot; named pipe facility from the&lt;br /&gt;
&amp;quot;built-in&amp;quot; version that will eventually be included in OS/2.&lt;br /&gt;
     Named pipes are much like anonymous pipes in that they're a serial&lt;br /&gt;
communications channel between two processes and they use the DosRead and&lt;br /&gt;
DosWrite interface. They are different, however, in several important&lt;br /&gt;
ways.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes have names in the file system name space. Users of a&lt;br /&gt;
        named pipe need not be related; they need only know the name of a&lt;br /&gt;
        pipe to access it.&lt;br /&gt;
&lt;br /&gt;
     þ  Because named pipes use the file system name space and because that&lt;br /&gt;
        name space can describe machines on a network, named pipes work&lt;br /&gt;
        both locally (within a single machine) and remotely (across a&lt;br /&gt;
        network).&lt;br /&gt;
&lt;br /&gt;
     þ  An anonymous pipe is a byte-stream mechanism. The system considers&lt;br /&gt;
        the data sent through an anonymous pipe as an undifferentiated&lt;br /&gt;
        stream of bytes. The writer can write a 100-byte block of data, and&lt;br /&gt;
        the reader can read the data with two 30-byte reads and one 40-byte&lt;br /&gt;
        read. If the byte stream contains individual messages, the&lt;br /&gt;
        recipient must determine where they start and stop. Named pipes can&lt;br /&gt;
        be used in this byte-stream mode, but named pipes also support&lt;br /&gt;
        support message mode, in which processes read and write streams&lt;br /&gt;
        of messages. When the named pipe is in message mode, OS/2&lt;br /&gt;
        (figuratively!) separates the messages from each other with pieces&lt;br /&gt;
        of waxed paper so that the reader can ask for &amp;quot;the next message&amp;quot;&lt;br /&gt;
        rather than for &amp;quot;the next 100 bytes.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes are full duplex, whereas anonymous pipes are actually a&lt;br /&gt;
        pair of pipes, each half duplex. When an anonymous pipe is created,&lt;br /&gt;
        two handles are returned--a read handle and a write handle. An open&lt;br /&gt;
        of a named pipe returns a single handle, which may (depending on&lt;br /&gt;
        the mode of the DosOpen) be both read and written. Although a full&lt;br /&gt;
        duplex named pipe is accessed via a single handle, the data moving&lt;br /&gt;
        in each direction is kept totally separate. A named pipe should be&lt;br /&gt;
        viewed as two separate pipes between the reader and the writer--one&lt;br /&gt;
        holds data going in, the other holds data coming back. For example,&lt;br /&gt;
        if a thread writes to a named pipe handle and then reads from that&lt;br /&gt;
        handle, the thread will not read back the data it just wrote. The&lt;br /&gt;
        data the thread just wrote in is in the outgoing side; the read&lt;br /&gt;
        reads from the incoming side.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes are frequently used to communicate with processes that&lt;br /&gt;
        provide a service to one or more clients, usually simultaneously.&lt;br /&gt;
        The named pipe API contains special functions to facilitate such&lt;br /&gt;
        use: pipe reusability, multiple pipes with identical names, and so&lt;br /&gt;
        on. These are discussed below.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes support transaction I/O calls that provide an efficient&lt;br /&gt;
        way to implement local and remote procedure call dialogs between&lt;br /&gt;
        processes.&lt;br /&gt;
&lt;br /&gt;
     þ  Programs running on MS-DOS version 3.x workstations can access&lt;br /&gt;
        named pipes on an OS/2 server to conduct dialogs with server&lt;br /&gt;
        applications because, to a client, a named pipe looks exactly like&lt;br /&gt;
        a file.&lt;br /&gt;
&lt;br /&gt;
     You'll recall that the creator of an anonymous pipe uses a special&lt;br /&gt;
interface (DosMakePipe) to create the pipe but that the client process&lt;br /&gt;
can use the DosRead and DosWrite functions, remaining ignorant of the&lt;br /&gt;
nature of the handle. The same holds true for named pipes when they&lt;br /&gt;
are used in stream mode. The creator of a named pipe uses a special API&lt;br /&gt;
to set it up, but its clients can use the pipe while remaining ignorant&lt;br /&gt;
of its nature as long as that use is serial.&lt;br /&gt;
4. Random access, using DosSeek, is not supported for&lt;br /&gt;
pipes and will cause an error code to be returned.&lt;br /&gt;
4 Named pipes are created by&lt;br /&gt;
the DosMakeNmPipe call. Once the pipe is created, one of the serving&lt;br /&gt;
process's threads must wait via the DosConnectNmPipe call for the client&lt;br /&gt;
to open the pipe. The client cannot successfully open the pipe until a&lt;br /&gt;
DosConnectNmPipe has been issued to it by the server process.&lt;br /&gt;
     Although the serving process understands that it's using a named pipe&lt;br /&gt;
and can therefore call a special named pipe API, the client process need&lt;br /&gt;
not be aware that it's using a named pipe because the normal DosOpen call&lt;br /&gt;
is used to open the pipe. Because named pipes appear in the file system&lt;br /&gt;
name space, the client can, for example, open a file called \PIPE\STATUS,&lt;br /&gt;
unaware that it's a named pipe being managed by another process. The&lt;br /&gt;
DosMakeNmPipe call returns a handle to the serving end of the pipe; the&lt;br /&gt;
DosOpen call returns a handle to the client end. As soon as the client&lt;br /&gt;
opens a pipe, the DosConnectNmPipe call returns to the serving process.&lt;br /&gt;
     Communication over a named pipe is similar to that over an anonymous&lt;br /&gt;
pipe: The client and server each issue reads and writes to the handle, as&lt;br /&gt;
appropriate for the mode of the open. When a process at one end of the pipe&lt;br /&gt;
closes it, the process at the other end gets an error code in response to&lt;br /&gt;
write operations and an EOF indication in response to read operations.&lt;br /&gt;
     The scenario just described is simple enough, but that's the problem:&lt;br /&gt;
It's too simple. In real life, a serving process probably stays around so&lt;br /&gt;
that it can serve the next client. This is the purpose behind the&lt;br /&gt;
DosConnectNmPipe call. After the first client closes its end of the named&lt;br /&gt;
pipe and the server end sees the EOF on the pipe, the server end issues a&lt;br /&gt;
DosDisconnectNmPipe call to acknowledge that the client has closed the pipe&lt;br /&gt;
(either explicitly or via termination). It can then issue another&lt;br /&gt;
DosConnectNmPipe call to reenable that pipe for reopening by another client&lt;br /&gt;
or by the same client. In other words, the connect and disconnect&lt;br /&gt;
operations allow a server to let clients, one by one, connect to it via a&lt;br /&gt;
single named pipe. The DosDisconnectNmPipe call can be used to forcibly&lt;br /&gt;
disconnect a client. This action is appropriate if a client makes an&lt;br /&gt;
invalid request or otherwise shows signs of ill health.&lt;br /&gt;
     We can serve multiple clients, one at a time, but what about serving&lt;br /&gt;
them in parallel? As we've described it so far, our serving process handles&lt;br /&gt;
only one client. A client's DosOpen call fails if the named pipe already&lt;br /&gt;
has a client user or if the server process hasn't issued the&lt;br /&gt;
DosConnectNmPipe call. This is where the instancing parameter, supplied to&lt;br /&gt;
DosMakeNmPipe, comes in.&lt;br /&gt;
     When a named pipe is first opened,&lt;br /&gt;
5. Like other non-file-system resident named objects, a named pipe&lt;br /&gt;
remains known to the system only as long as a process has it open.&lt;br /&gt;
When all handles to a named pipe are closed, OS/2 forgets all&lt;br /&gt;
information concerning the named pipe. The next DosMakeNmPipe&lt;br /&gt;
call recreates the named pipe from ground zero.&lt;br /&gt;
5 the instance count parameter is&lt;br /&gt;
specified in the pipe flag's word. If this count is greater than 1, the&lt;br /&gt;
pipe can be opened by a server process more than once. Additional opens are&lt;br /&gt;
done via DosMakeNmPipe, which returns another handle to access the new&lt;br /&gt;
instance of the pipe. Obviously the pipe isn't being &amp;quot;made&amp;quot; for the second&lt;br /&gt;
and subsequent calls to DosMakeNmPipe, but the DosOpen call can't be used&lt;br /&gt;
instead because it opens the client end of the named pipe, not the server&lt;br /&gt;
end. The instance count argument is ignored for the second and subsequent&lt;br /&gt;
DosMakeNmPipe calls. Extra instances of a named pipe can be created by the&lt;br /&gt;
same process that created the first instance, or they can be created by&lt;br /&gt;
other processes. Figure 11-1 illustrates multiple instances of a named&lt;br /&gt;
pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿              \\pipe\pipename      &lt;br /&gt;
³  Client  ÃÄÄÄÄÄ¿               �                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     A    ³     ³     °°°°°°°°°°°°°°°°°°°°°°     ³           ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ     ³               ÚÄÄÄÄÄÄÄÄÄÄ¿     ³           ³&lt;br /&gt;
                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´          ³     ³           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿                  ÚÄÄÁÄÄÄÄÄÄÄ¿  ³     ³           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´          ³  ³     ³           ³&lt;br /&gt;
³     B    ³               ÚÄÄÁÄÄÄÄÄÄÄ¿  ³  ³     ³  Server   ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ     ÚÄÄÄÄÄÄÄÄÄ´          ³  ³  ÃÄÄÄÄÄ´  Process  ³&lt;br /&gt;
                 ³      ÚÄÄÁÄÄÄÄÄÄÄ¿  ³  ÃÄÄÙ     ³           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿     ³  ÚÄÄÄ´          ³  ³  ÃÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÙ  ³   ³          ³  ÃÄÄÙ        ³           ³&lt;br /&gt;
³     C    ³        ³   ³          ³  ÃÄÄÄÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ        ³   ³          ÃÄÄÙ           ³           ³&lt;br /&gt;
                    ³   ³          ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿        ³   ÀÄÄÄÄÄÄÄÄÄÄÙ              ³           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÄÄÄÙ                             ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³     D    ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 11-1.  Multiple instances of a named pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When a client process does a DosOpen on a named pipe that has multiple&lt;br /&gt;
instances, OS/2 connects it to any server instance of the pipe that has&lt;br /&gt;
issued a DosConnectNmPipe call. If no instances are available and enabled,&lt;br /&gt;
the client receives an error code. OS/2 makes no guarantees about&lt;br /&gt;
distributing the incoming work evenly across all server instances; it&lt;br /&gt;
assumes that all server threads that issued a DosConnectNmPipe call are&lt;br /&gt;
equal.&lt;br /&gt;
     The multiple instance capability allows a single server process or&lt;br /&gt;
perhaps multiple server processes to handle many clients simultaneously.&lt;br /&gt;
One process using four threads can serve four clients as rapidly as four&lt;br /&gt;
processes, each with one thread, can do the job. As long as threads don't&lt;br /&gt;
interfere with one another by blocking on critical sections, a multiprocess&lt;br /&gt;
server has no inherent efficiency advantage over a multithread server.&lt;br /&gt;
     The OS/2 named pipe package includes some composite operations for&lt;br /&gt;
client processes: DosTransactNmPipe and DosCallNmPipe. DosTransactNmPipe is&lt;br /&gt;
much like a DosWrite followed by a DosRead: It sends a message to the&lt;br /&gt;
server end of the named pipe and then reads a reply. DosCallNmPipe does&lt;br /&gt;
the same on an unopened named pipe: It has the combined effect of a&lt;br /&gt;
DosOpen, a DosTransactNmPipe, and a DosClose. These calls are of little&lt;br /&gt;
value if the client and server processes are on the same machine; the&lt;br /&gt;
client could easily build such subroutines itself by appropriately&lt;br /&gt;
combining DosOpen, DosClose, DosRead, and DosWrite. These calls are in the&lt;br /&gt;
named pipe package because they provide significant performance savings in&lt;br /&gt;
a networked environment. If the server process is on a different machine&lt;br /&gt;
from the client process, OS/2 and the network transport can use a&lt;br /&gt;
datagramlike mechanism to implement these calls in a network-efficient&lt;br /&gt;
fashion. Because named pipes work invisibly across the network, any client&lt;br /&gt;
process that performs these types of operations should use these composite&lt;br /&gt;
calls, even if the author of the program didn't anticipate the program&lt;br /&gt;
being used in a networked environment. Using the composite calls will&lt;br /&gt;
 ensure the performance gains if a user decides to use a server process&lt;br /&gt;
located across the network. Readers familiar with network architecture will&lt;br /&gt;
recognize the DosCallNmPipe function as a form of remote procedure call. In&lt;br /&gt;
effect, it allows a process to make a procedure call to another process,&lt;br /&gt;
even a process on another machine.&lt;br /&gt;
     The OS/2 named pipe facility contains a great many features, as befits&lt;br /&gt;
its importance in realizing the OS/2 tool-based architecture. This book is&lt;br /&gt;
not intended to provide an exhaustive coverage of features, but a few other&lt;br /&gt;
miscellaneous items merit mention.&lt;br /&gt;
     Our above discussion concentrated on stream-based communications,&lt;br /&gt;
which can be convenient because they allow a client process to use a named&lt;br /&gt;
pipe while ignorant of its nature. For example, you can write a spooler&lt;br /&gt;
package for a device not supported by the system spoolers--say, for a&lt;br /&gt;
plotter device. Input to the spooler can be via a named pipe, perhaps&lt;br /&gt;
\PIPE\PLOTOUT. An application could then be told to write its plotter&lt;br /&gt;
output to a file named \PIPE\PLOTOUT or even \\PLOTMACH\PIPE\PLOTOUT&lt;br /&gt;
(across a network). The application will then use the spooler at the&lt;br /&gt;
other end of the named pipe.&lt;br /&gt;
     Sometimes, though, the client process does understand that it's&lt;br /&gt;
talking to a named pipe, and the information exchanged is a series of&lt;br /&gt;
messages rather than a long stream of plotter data. In this case, the named&lt;br /&gt;
pipe can be configured as a message stream in which each message is&lt;br /&gt;
indivisible and atomic at the interface. In other words, when a process&lt;br /&gt;
reads from a named pipe, it gets only one message per read, and it gets the&lt;br /&gt;
entire message. Messages can queue up in the pipe, but OS/2 remembers the&lt;br /&gt;
message boundaries so that it can split them apart as they are read.&lt;br /&gt;
Message streams can be used effectively in a networking environment because&lt;br /&gt;
the network transport can better judge how to assemble packets.&lt;br /&gt;
     Although our examples have shown the client and server processes&lt;br /&gt;
issuing calls and blocking until they are done, named pipes can be&lt;br /&gt;
configured to operate in a nonblocking fashion. This allows a server or a&lt;br /&gt;
client to test a pipe to see if it's ready for a particular operation,&lt;br /&gt;
thereby guaranteeing that the process won't be held up for some period&lt;br /&gt;
waiting for a request to complete. Processes can also use DosPeekNmPipe, a&lt;br /&gt;
related facility that returns a peek at any data (without consuming the&lt;br /&gt;
data) currently waiting to be read in the pipe interface. Servers can use&lt;br /&gt;
this to scan a client's request to see if they're interested in handling it&lt;br /&gt;
at that time.&lt;br /&gt;
     Finally, we mentioned that a process that attempts a DosOpen to a&lt;br /&gt;
named pipe without any available instances is returned an error code.&lt;br /&gt;
Typically, a client in this situation wants to wait for service to become&lt;br /&gt;
available, and it doesn't want to sit in a polling loop periodically &lt;br /&gt;
testing for server availability. The DosWaitNmPipe call is provided for&lt;br /&gt;
this situation; it allows a client to block until an instance of the named&lt;br /&gt;
pipe becomes available. When DosWaitNmPipe returns, the client must still&lt;br /&gt;
do a DosOpen. The DosOpen can fail, however, if another process has taken&lt;br /&gt;
the pipe instance in the time between the &amp;quot;wait&amp;quot; and the &amp;quot;open&amp;quot; calls. But&lt;br /&gt;
because multiple waiters for a named pipe are serviced in priority order,&lt;br /&gt;
such a &amp;quot;race&amp;quot; condition is uncommon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.4  Queues&lt;br /&gt;
&lt;br /&gt;
Queues are another form of IPC. In many ways they are similar to named&lt;br /&gt;
pipes, but they are also significantly different. Like named pipes, they&lt;br /&gt;
use the file system name space, and they pass messages rather than byte&lt;br /&gt;
streams. Unlike named pipes, queues allow multiple writes to a single queue&lt;br /&gt;
because the messages bring with them information about their sending&lt;br /&gt;
process that enables the queue reader to distinguish between messages from&lt;br /&gt;
different senders. Named pipes are strictly FIFO, whereas queue messages&lt;br /&gt;
can be read in a variety of orders. Finally, queues use shared memory as a&lt;br /&gt;
transfer mechanism; so although they're faster than named pipes for higher&lt;br /&gt;
volume data transfers on a single machine, they don't work across the&lt;br /&gt;
network.&lt;br /&gt;
     The interface to the queue package is similar but not identical to&lt;br /&gt;
that of the named pipe interface. Like named pipes, each queue has a single&lt;br /&gt;
owner that creates it. Clients open and close the queue while the owner,&lt;br /&gt;
typically, lives on. Unlike named pipes, the client process must use a&lt;br /&gt;
special queue API (DosReadQueue, DosWriteQueue, and so on) and thus must be&lt;br /&gt;
written especially to use the queue package. Although each queue has a&lt;br /&gt;
single owner, each queue can have multiple clients; so the queue mechanism&lt;br /&gt;
doesn't need a facility to have multiple queues of the same name, nor does&lt;br /&gt;
it need a DosWaitNmPipe equivalent.&lt;br /&gt;
     Queue messages are somewhat different from named pipe messages. In&lt;br /&gt;
addition to carrying the body of the message, each queue message carries&lt;br /&gt;
two additional pieces of information. One is the PID of the sender; OS/2&lt;br /&gt;
provides this information, and the sender cannot affect it. The other is a&lt;br /&gt;
word value that the sender supplied and that OS/2 and the queue package do&lt;br /&gt;
not interpret. Queue servers and clients can use this information as they&lt;br /&gt;
wish to facilitate communication.&lt;br /&gt;
     The queue package also contains a peek facility, similar to that of&lt;br /&gt;
named pipes but with an interesting twist. If a process peeks the named&lt;br /&gt;
pipe and then later reads from it, it can be sure that the message it reads&lt;br /&gt;
is the same one that it peeked because named pipes are always FIFO. Queues,&lt;br /&gt;
however, allow records to be read in different orders of priority. If a&lt;br /&gt;
queue is being read in priority order, a process might well peek a message,&lt;br /&gt;
but by the time the process issues the queue read, some other message of&lt;br /&gt;
higher priority may have arrived and thus be at the front of the list. To&lt;br /&gt;
get around this problem, when the queue package peeks a message, it returns&lt;br /&gt;
a magic cookie to the caller along with the message. The caller can supply&lt;br /&gt;
this cookie to a subsequent DosReadQueue call to ensure that the peeked&lt;br /&gt;
message is the one read, overriding the normal message-ranking process.&lt;br /&gt;
This magic cookie can also be supplied to the DosPeekQueue call to peek the&lt;br /&gt;
second and subsequent records in the queue.&lt;br /&gt;
     Finally, one extremely important difference between queues and named&lt;br /&gt;
pipes is that named pipes transfer (that is, copy) the data from the client&lt;br /&gt;
to the server process. Queues transfer only the address of the data; the&lt;br /&gt;
queue package does not touch the data itself. Thus, the data body of the&lt;br /&gt;
queue message must be addressable to both the client and the serving&lt;br /&gt;
process. This is straightforward if both the client and serving threads&lt;br /&gt;
belong to the same process. If the client and serving threads are from&lt;br /&gt;
different processes, however, the data body of the queue message must be in&lt;br /&gt;
a shared memory segment that is addressable to both the client and the&lt;br /&gt;
server.&lt;br /&gt;
     A related issue is buffer reusability. An application can reuse a&lt;br /&gt;
memory area immediately after its thread returns from the named pipe call&lt;br /&gt;
that wrote the data from that area; but when using a queue, the sender must&lt;br /&gt;
not overwrite the message area until it's sure the reading process is&lt;br /&gt;
finished with the message.&lt;br /&gt;
     One way to kill both these birds--the shared memory and the memory&lt;br /&gt;
reuse problems--with one stone is to use the memory suballocation package.&lt;br /&gt;
Both the client and the queue server need to have shared access to a memory&lt;br /&gt;
segment that is then managed by the memory suballocation package. The&lt;br /&gt;
client allocates a memory object to hold the queue message and write it to&lt;br /&gt;
the queue. The queue server can address that queue message because it's in&lt;br /&gt;
the shared memory segment. When the queue manager is finished with the&lt;br /&gt;
message, it calls the memory suballocator to release the memory object. The&lt;br /&gt;
client need not worry about when the server is finished with the message&lt;br /&gt;
because the client allocates a new message buffer for each new message,&lt;br /&gt;
relying on the server to return the messages fast enough so that the memory&lt;br /&gt;
suballocator doesn't run out of available space.&lt;br /&gt;
     A similar technique on a segment level is to use giveaway shared&lt;br /&gt;
memory. The client allocates a giveaway segment for each message content,&lt;br /&gt;
creates the message, gives away a shared addressability to the segment to&lt;br /&gt;
the server process, and then writes the message (actually, the message's&lt;br /&gt;
address) to the queue. Note that the sender uses the recipient's selector&lt;br /&gt;
as the data address in this case, not its own selector. When the thread&lt;br /&gt;
returns from that DosWriteQueue call, the client releases its access to the&lt;br /&gt;
segment via DosFreeSeg. When the server process is finished with the&lt;br /&gt;
message, it also releases the memory segment. Because the queue server is&lt;br /&gt;
the last process with access to that segment, the segment is then returned&lt;br /&gt;
to the free pool.&lt;br /&gt;
     Software designers need to consider carefully the tradeoffs between&lt;br /&gt;
queues, named pipes, and other forms of IPC. Queues are potentially very&lt;br /&gt;
fast because only addresses are copied, not the data itself; but the work&lt;br /&gt;
involved in managing and reusing the shared memory may consume the time&lt;br /&gt;
savings if the messages are small. In general, small messages that are&lt;br /&gt;
always read FIFO should go by named pipes, as should applications that&lt;br /&gt;
communicate with clients and servers across a network. Very large or high&lt;br /&gt;
data rate messages may be better suited to queues.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.5  Dynamic Data Exchange (DDE)&lt;br /&gt;
&lt;br /&gt;
DDE is a form of IPC available to processes that use the presentation&lt;br /&gt;
manager API. The presentation manager's interface is message oriented; that&lt;br /&gt;
is, the primary means of communication between a process and the&lt;br /&gt;
presentation manager is the passing of messages. The presentation manager&lt;br /&gt;
message interface allows applications to define private messages that have&lt;br /&gt;
a unique meaning throughout the PC. DDE is, strictly speaking, a protocol&lt;br /&gt;
that defines new messages for communication between applications that use&lt;br /&gt;
it.&lt;br /&gt;
     DDE messages can be directed at a particular recipient or broadcast to&lt;br /&gt;
all presentation manager applications on a particular PC. Typically, a&lt;br /&gt;
client process broadcasts a message that says, &amp;quot;Does anyone out there have&lt;br /&gt;
this information?&amp;quot; or &amp;quot;Does anyone out there provide this service?&amp;quot; If no&lt;br /&gt;
response is received, the answer is taken to be no. If a response is&lt;br /&gt;
received, it contains an identifying code&lt;br /&gt;
6.A window handle.&lt;br /&gt;
6 that allows the two processes to&lt;br /&gt;
communicate privately.&lt;br /&gt;
     DDE's broadcast mechanism and message orientation gives it a lot of&lt;br /&gt;
flexibility in a multiprocessing environment. For example, a specialized&lt;br /&gt;
application might be scanning stock quotes that are arriving via a special&lt;br /&gt;
link. A spreadsheet program could use DDE to tell this scanner application&lt;br /&gt;
to notify it whenever the quotes change for certain stocks that are&lt;br /&gt;
mentioned in its spreadsheet. Another application, perhaps called Market&lt;br /&gt;
Alert, might ask the scanner to notify it of trades in a different set of&lt;br /&gt;
stocks so that the alert program can flash a banner if those stocks trade&lt;br /&gt;
outside a prescribed range. DDEs can be used only by presentation manager&lt;br /&gt;
applications to communicate with the same.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.6  Signaling&lt;br /&gt;
&lt;br /&gt;
Signals are asynchronous notification mechanisms that operate in a fashion&lt;br /&gt;
analogous to hardware interrupts. Like hardware interrupts, when a signal&lt;br /&gt;
arrives at a process, that process's thread 1 stops after the instruction&lt;br /&gt;
it is executing and begins executing at a specified handler address. The&lt;br /&gt;
many special considerations to take into account when using signals are&lt;br /&gt;
discussed in Chapter 12. This section discusses their use as a form of&lt;br /&gt;
IPC.&lt;br /&gt;
     Processes typically receive signals in response to external events&lt;br /&gt;
that must be serviced immediately. Examples of such events are the user&lt;br /&gt;
pressing Ctrl-C or a process being killed. Three signals (flag A, flag B,&lt;br /&gt;
and flag C), however, are caused by another process&lt;br /&gt;
7. This is the typical case; but like all other system calls that affect&lt;br /&gt;
processes, a thread can make such a call to affect its own process.&lt;br /&gt;
7 issuing an explicit&lt;br /&gt;
DosFlagProcess API. DosFlagProcess is a unique form of IPC because it's&lt;br /&gt;
asynchronous. The recipient doesn't have to block or poll waiting for the&lt;br /&gt;
event; it finds out about it (by discovering itself to be executing the&lt;br /&gt;
signal handler) as soon as the scheduler gives it CPU time.&lt;br /&gt;
     DosFlagProcess, however, has some unique drawbacks. First, a signal&lt;br /&gt;
carries little information with it: only the number of the signal and a&lt;br /&gt;
single argument word. Second, signals can interrupt and interfere with some&lt;br /&gt;
system calls. Third, OS/2 views signals more as events than as messages; so&lt;br /&gt;
if signals are sent faster than the recipient can process them, OS/2&lt;br /&gt;
discards some of the overrun. These disadvantages (discussed in Chapter 12)&lt;br /&gt;
restrict signals to a rather specialized role as an IPC mechanism.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.7  Combining IPC Forms&lt;br /&gt;
&lt;br /&gt;
We've discussed each form of IPC, listing its strengths and weaknesses. If&lt;br /&gt;
you use forms in conjunction, however, you benefit from their combined&lt;br /&gt;
strengths. For example, a process can use named pipes or DDE to establish&lt;br /&gt;
contact with another process and then agree with it to send a high volume&lt;br /&gt;
of data via shared memory. An application that provides an IPC interface&lt;br /&gt;
should also provide a dynlink package to hide the details of the IPC. This&lt;br /&gt;
gives designers the flexibility to improve the IPC component of their&lt;br /&gt;
package in future releases while still maintaining interface compatibility&lt;br /&gt;
with their clients.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==12  Signals==&lt;br /&gt;
&lt;br /&gt;
The OS/2 signal mechanism is similar, but not identical, to the UNIX signal&lt;br /&gt;
mechanism. A signal is much like a hardware interrupt except that it is&lt;br /&gt;
initiated and implemented in software. Just as a hardware interrupt causes&lt;br /&gt;
the CS, IP, and Flags registers to be saved on the stack and execution to&lt;br /&gt;
begin at a handler address, a signal causes the application's CS, IP, and&lt;br /&gt;
Flags registers to be saved on the stack and execution to begin at a&lt;br /&gt;
signal-handler address. An IRET instruction returns control to the&lt;br /&gt;
interrupted address in both cases. Signals are different from hardware&lt;br /&gt;
interrupts in that they are a software construct and don't involve&lt;br /&gt;
privilege transitions, stack switches, or ring 0 code.&lt;br /&gt;
     OS/2 supports six signals--three common signals (Ctrl-C, Ctrl-Break,&lt;br /&gt;
and program termination) and three general-purpose signals. The Ctrl-C and&lt;br /&gt;
Ctrl-Break signals occur in response to keyboard activity; the program&lt;br /&gt;
termination signal occurs when a process is killed via the DosKill call.&lt;br /&gt;
1. The process termination signal handler is not called under&lt;br /&gt;
all conditions of process termination, only in response to&lt;br /&gt;
DosKill. Normal exits, GP faults, and so on do not&lt;br /&gt;
activate the process termination signal handler.&lt;br /&gt;
1&lt;br /&gt;
The three general-purpose signals are generated by an explicit call from a&lt;br /&gt;
thread, typically a thread from another process. A signal handler is in the&lt;br /&gt;
form of a far subroutine, that is, a subroutine that returns with a far&lt;br /&gt;
return instruction. When a signal arrives, the process's thread 1 is&lt;br /&gt;
interrupted from its current location and made to call the signal-handler&lt;br /&gt;
procedure with an argument provided by the signal generator. The signal-&lt;br /&gt;
handler code can return,&lt;br /&gt;
2. OS/2 interposes a code thunk, so the signal handler need not&lt;br /&gt;
concern itself with executing an IRET instruction, which language&lt;br /&gt;
compilers usually won't generate. When the signal handler is&lt;br /&gt;
entered, its return address points to a piece of OS/2 code that&lt;br /&gt;
contains the IRET instruction.&lt;br /&gt;
2 in which case the CPU returns from where it was&lt;br /&gt;
interrupted, or the signal handler can clean its stack and jump into the&lt;br /&gt;
process's code at some other spot, as in the C language's longjmp facility.&lt;br /&gt;
     The analogy between signals and hardware interrupts holds still&lt;br /&gt;
further. As it does in a hardware interrupt, the system blocks further&lt;br /&gt;
interrupts from the same source so that the signal handler won't be&lt;br /&gt;
arbitrarily reentered. The signal handler must issue a special form of&lt;br /&gt;
DosSetSigHandler to dismiss the signal and allow further signals to occur.&lt;br /&gt;
Typically this is done at the end of the signal-handling routine unless the&lt;br /&gt;
signal handler is reentrant. Also, like hardware interrupts, the equivalent&lt;br /&gt;
of the CLI instruction--the DosHoldSignal call--is used to protect critical&lt;br /&gt;
sections from being interrupted via a signal.&lt;br /&gt;
     Unlike hardware interrupts, signals have no interrupt priority. As&lt;br /&gt;
each enabled signal occurs, the signal handler is entered, even if another&lt;br /&gt;
signal handler must be interrupted. New signal events that come in while&lt;br /&gt;
that signal is still being processed from an earlier event--before the&lt;br /&gt;
signal has been dismissed by the handler--are held until the previous&lt;br /&gt;
signal event has been dismissed. Like hardware interrupts, this is a&lt;br /&gt;
pending-signal flag, not a counter. If three signals of the same kind are&lt;br /&gt;
held off, only one signal event occurs when that signal becomes&lt;br /&gt;
reenabled.&lt;br /&gt;
     A signal event occurs in the context of a process whose thread of&lt;br /&gt;
execution is interrupted for the signal handler; a signal doesn't cause the&lt;br /&gt;
CPU to stop executing another process in order to execute the first&lt;br /&gt;
process's signal handler. When OS/2 &amp;quot;posts&amp;quot; a signal to a process, it&lt;br /&gt;
simply makes a mark that says, &amp;quot;The next time we run this guy, store his&lt;br /&gt;
CS, IP, and Flags values on the stack and start executing here instead.&amp;quot;&lt;br /&gt;
The system uses its regular priority rules to assign the CPU to threads;&lt;br /&gt;
when the scheduler next runs the signaled thread, the dispatcher code that&lt;br /&gt;
sends the CPU into the application's code reads the &amp;quot;posted signal&amp;quot; mark&lt;br /&gt;
and does the required work.&lt;br /&gt;
     Because a signal &amp;quot;pseudo interrupt&amp;quot; is merely a trick of the&lt;br /&gt;
dispatcher, signal handlers don't run in ring 0 as do hardware interrupt&lt;br /&gt;
handlers; they run in ring 3 as do all application threads. In general, as&lt;br /&gt;
far as OS/2 is concerned, the process isn't in any sort of special state&lt;br /&gt;
when it's executing a signal handler, and no special rules govern what a&lt;br /&gt;
thread can and cannot do in a signal handler.&lt;br /&gt;
     Receiving a signal when thread 1 is executing an application or&lt;br /&gt;
dynlink code is straightforward: The system saves CS, IP, and Flags, and&lt;br /&gt;
the signal handler saves the rest. The full register complement can be&lt;br /&gt;
restored after the signal has been processed, and thread 1's normal&lt;br /&gt;
execution resumes without incident. If thread 1 is executing a system call&lt;br /&gt;
that takes the CPU inside the kernel, the situation is more complex. OS/2&lt;br /&gt;
can't emulate an interrupt from the system ring 0 code to the application's&lt;br /&gt;
ring 3 code, nor can OS/2 take the chance that the signal handler never&lt;br /&gt;
returns from the signal&lt;br /&gt;
3.It's acceptable for a signal handler to clean up thread 1's&lt;br /&gt;
stack, dismiss the signal, jump to another part of the&lt;br /&gt;
application, and never return from the signal. For example, an&lt;br /&gt;
application can jump into its &amp;quot;prompt and command loop&amp;quot; in&lt;br /&gt;
response to the press of Ctrl-C.&lt;br /&gt;
3 and therefore leaves OS/2's internals in an&lt;br /&gt;
intermediate state. Instead, when a signal is posted and thread 1 is&lt;br /&gt;
executing ring 0 OS/2 code, the system either completes its operations&lt;br /&gt;
before recognizing the signal or aborts the operation and then recognizes&lt;br /&gt;
the signal. If the operation is expected to take place &amp;quot;quickly,&amp;quot; the&lt;br /&gt;
system completes the operation, and the signal is recognized at the point&lt;br /&gt;
where the CPU resumes executing the application's ring 3 code.&lt;br /&gt;
     All non-I/O operations are deemed to complete &amp;quot;quickly,&amp;quot; with the&lt;br /&gt;
exception of the explicit blocking operations such as DosSleep, DosSemWait,&lt;br /&gt;
and so on. I/O operations depend on the specific device. Disk I/O completes&lt;br /&gt;
quickly, but keyboard and serial I/O generally do not. Clearly, if we wait&lt;br /&gt;
for the user to finish typing a line before we recognize a signal, we might&lt;br /&gt;
never recognize it--especially if the signal is Ctrl-C! In the case of&lt;br /&gt;
&amp;quot;slow devices,&amp;quot; OS/2 or the device driver terminates the operation and&lt;br /&gt;
returns to the application with an error code. The signal is recognized&lt;br /&gt;
when the CPU is about to resume executing the ring 3 application code that&lt;br /&gt;
follows the system call that was interrupted.&lt;br /&gt;
     Although the application is given an error code to explain that the&lt;br /&gt;
system call was interrupted, the application may be unable to reissue the&lt;br /&gt;
system call to complete the work. In the case of device I/O, the&lt;br /&gt;
application typically can't tell how much, if any, of the requested output&lt;br /&gt;
or input took place before the signal interrupted the operation. If an&lt;br /&gt;
output operation is not reissued, some data at the end of the write may be&lt;br /&gt;
missing. If an output operation is restarted, then some data at the&lt;br /&gt;
beginning of the write may be written twice. In the case of DosSleep, the&lt;br /&gt;
application cannot tell how much of the requested sleep has elapsed. These&lt;br /&gt;
issues are not usually a problem; it's typically keyboard input that is&lt;br /&gt;
interrupted. In the case of the common signals (Ctrl-C, Ctrl-Break, and&lt;br /&gt;
process killed) the application typically flushes partial keyboard input&lt;br /&gt;
anyway. Applications that use other &amp;quot;slow&amp;quot; devices or the IPC flag signals&lt;br /&gt;
need to deal with this, however.&lt;br /&gt;
     Although a process can have multiple threads, only thread 1 is used to&lt;br /&gt;
execute the signal handler.&lt;br /&gt;
4. For this reason, a process should not terminate thread 1 and&lt;br /&gt;
continue executing with others; then it cannot receive signals.&lt;br /&gt;
4 This leads to an obvious solution to the&lt;br /&gt;
interrupted system call problem: Applications that will be inconvenienced&lt;br /&gt;
by interrupted system calls due to signals should dedicate thread 1 to work&lt;br /&gt;
that doesn't make interruptible system calls and use other thread(s) for&lt;br /&gt;
that work. In the worst case, thread 1 can be totally dedicated to waiting&lt;br /&gt;
for signals: It can block on a RAM semaphore that is never released, or it&lt;br /&gt;
can execute a DosSleep loop.&lt;br /&gt;
     A couple of practical details about signals are worth noting. First,&lt;br /&gt;
the user of a high-level language such as C need not worry about saving the&lt;br /&gt;
registers inside the signal-handler routine. The language runtimes&lt;br /&gt;
typically provide code to handle all these details; as far as the&lt;br /&gt;
application program is concerned, the signal handler is asynchronously far&lt;br /&gt;
called, and it can return from the signal by the return() statement. Also,&lt;br /&gt;
no application can receive a signal without first requesting it, so you&lt;br /&gt;
need not worry about setting up signal handlers if your application doesn't&lt;br /&gt;
explicitly ask to use them. A process can have only one signal-handling&lt;br /&gt;
address for each signal, so general-purpose dynlink routines (ones that&lt;br /&gt;
might be called by applications that aren't bundled with the dynlink&lt;br /&gt;
package) should never set a signal handler; doing so might override a&lt;br /&gt;
handler established by the client program code.&lt;br /&gt;
     Signals interact with critical sections in much the same way as&lt;br /&gt;
interrupts do. If a signal arrives while thread 1 is executing a critical&lt;br /&gt;
section that is protected by a semaphore and if that signal handler never&lt;br /&gt;
returns to the interrupted location, the critical section's semaphore will&lt;br /&gt;
be left jammed on. Even if the signal handler eventually returns, deadlock&lt;br /&gt;
occurs if it attempts to enter the critical section during processing of&lt;br /&gt;
the signal (perhaps it called a dynlink package, unaware that the package&lt;br /&gt;
contained a critical section). Dynlink packages must deal with this problem&lt;br /&gt;
by means of the DosHoldSignal call, which is analogous to the CLI/STI&lt;br /&gt;
instructions for hardware interrupts: The DosHoldSignal holds off arriving&lt;br /&gt;
signals until they are released. Held-off signals should be released within&lt;br /&gt;
a second or two so that the user won't be pounding Ctrl-C and thinking that&lt;br /&gt;
the application has crashed. Applications can use DosHoldSignal, or they&lt;br /&gt;
can simply ensure that thread 1 never enters critical sections, perhaps by&lt;br /&gt;
reserving it for signal handling, as discussed above.&lt;br /&gt;
     Ctrl-C and Ctrl-Break are special, device-specific operations. Setting&lt;br /&gt;
a signal handler for these signals is a form of I/O to the keyboard device;&lt;br /&gt;
applications must never do this until they have verified that they have&lt;br /&gt;
been assigned the keyboard device. See Chapter 14, Interactive Programs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==13  The Presentation Manager and VIO==&lt;br /&gt;
&lt;br /&gt;
In the early chapters of this book, I emphasized the importance of a high-&lt;br /&gt;
powered, high-bandwidth graphical user interface. It's a lot of work for an&lt;br /&gt;
application to manage graphical rendition, windowing, menus, and so on, and&lt;br /&gt;
it's hard for the user to learn a completely different interface for each&lt;br /&gt;
application. Therefore, OS/2 contains a subsystem called the presentation&lt;br /&gt;
manager (PM) that provides these services and more. The presentation&lt;br /&gt;
manager is implemented as a dynlink subsystem and daemon process&lt;br /&gt;
combination, and it provides:&lt;br /&gt;
&lt;br /&gt;
* High-performance graphical windowing.&lt;br /&gt;
* A powerful user interface model, including drop-down menus, scroll bars, icons, and mouse and keyboard interfaces. Most of these facilities are optional to the application; it can choose the standard services or &amp;quot;roll its own.&amp;quot;&lt;br /&gt;
* Device independence. The presentation manager contains a sophisticated multilevel device interface so that as much work as possible is pushed down to &amp;quot;smart&amp;quot; graphics cards to optimize performance.&lt;br /&gt;
&lt;br /&gt;
     Interfacing an application with the presentation manager involves a&lt;br /&gt;
degree of effort that not all programmers may want to put forth. The&lt;br /&gt;
interface to the application may be so simple that the presentation&lt;br /&gt;
manager's features are of little value, or the programmer may want to port&lt;br /&gt;
an MS-DOS application to OS/2 with the minimum degree of change. For these&lt;br /&gt;
reasons, OS/2 provides a second interface package called VIO,&lt;br /&gt;
1. VIO is a convenience term that encompasses three dynlink&lt;br /&gt;
subsystems: KBD (keyboard), VIO (Video I/O; the display adapter),&lt;br /&gt;
and MOU (mouse).&lt;br /&gt;
1 which is&lt;br /&gt;
primarily character oriented and looks much like the MS-DOS ROM BIOS video&lt;br /&gt;
interface. The initial release of OS/2 contains only VIO, implemented as a&lt;br /&gt;
separate package. The next release will contain the presentation manager,&lt;br /&gt;
and VIO will then become an alternate interface to the presentation&lt;br /&gt;
manager.&lt;br /&gt;
     Fundamentally, the presentation manager and VIO are the equivalent of&lt;br /&gt;
device drivers. They are implemented as dynlink packages because they are&lt;br /&gt;
device dependent and need to be replaced if different devices are used.&lt;br /&gt;
Dynlinks are used instead of true device drivers because they can provide&lt;br /&gt;
high throughput for the screen device: A simple call is made directly to&lt;br /&gt;
the code that paints the pixels on the screen. Also, the dynlink interface&lt;br /&gt;
allows the presentation manager to be implemented partially as a dynlink&lt;br /&gt;
subsystem and partially as a daemon process accessed by that subsystem.&lt;br /&gt;
     These packages are complex; explaining them in detail is beyond the&lt;br /&gt;
scope of this book. Instead, I will discuss from a general perspective the&lt;br /&gt;
special issues for users of this package.&lt;br /&gt;
     VIO is essentially character oriented. It supports graphics-based&lt;br /&gt;
applications, but only to the extent of allowing them to manipulate the&lt;br /&gt;
display controller directly so that they can &amp;quot;go around&amp;quot; VIO and provide&lt;br /&gt;
special interfaces related to screen switching of graphics applications&lt;br /&gt;
(see below). The base VIO package plays a role similar to that of the ROM&lt;br /&gt;
BIOS INT 10/INT 16 interface used in MS-DOS. It contains some useful&lt;br /&gt;
enhancements but in general is a superset of the ROM BIOS functions, so INT&lt;br /&gt;
10-based real mode applications can be quickly adjusted to use VIO instead.&lt;br /&gt;
VIO is replaceable, in whole or in part, to allow applications being run&lt;br /&gt;
with VIO to be managed later by the presentation manager package.&lt;br /&gt;
     The presentation manager is entirely different from VIO. It offers an&lt;br /&gt;
extremely rich and powerful set of functions that support windowing, and it&lt;br /&gt;
offers a full, device-independent graphics facility. Its message-oriented&lt;br /&gt;
architecture is well suited to interactive applications. Once the&lt;br /&gt;
presentation manager programming model is learned and the key elements of&lt;br /&gt;
its complex interface are understood, a programmer can take advantage of a&lt;br /&gt;
very sexy user interface with comparatively little effort. The presentation&lt;br /&gt;
manager also replaces the existing VIO/KBD/MOU calls in order to support&lt;br /&gt;
older programs that use these interfaces.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.1  Choosing Between PM and VIO&lt;br /&gt;
&lt;br /&gt;
The roles of VIO and the presentation manager sometimes cause confusion:&lt;br /&gt;
Which should you use for your application? The default interface for a new&lt;br /&gt;
application should be the presentation manager. It's not in OS/2 version&lt;br /&gt;
1.0 because of scheduling restrictions; owners of version 1.0 will receive&lt;br /&gt;
the presentation manager as soon as it is available, and all future&lt;br /&gt;
releases will be bundled with the presentation manager. The presentation&lt;br /&gt;
manager will be present on essentially all personal computer OS/2&lt;br /&gt;
installations, so you are not restricting the potential market for an&lt;br /&gt;
application if you write it for the presentation manager.&lt;br /&gt;
     The presentation manager interface allows an application to utilize a&lt;br /&gt;
powerful, graphical user interface. In general form it's standardized for&lt;br /&gt;
ease of use, but it can be customized in a specific implementation so that&lt;br /&gt;
an application can provide important value-added features. On the other&lt;br /&gt;
hand, if you are porting an application from the real mode environment, you&lt;br /&gt;
will find it easier to use the VIO interface. Naturally, such programs run&lt;br /&gt;
well under the presentation manager, but they forgo the ability to use&lt;br /&gt;
graphics and to interact with the presentation manager. The user can still&lt;br /&gt;
&amp;quot;window&amp;quot; the VIO application's screen image, but without the application's&lt;br /&gt;
knowledge or cooperation. To summarize, you have three choices when writing&lt;br /&gt;
an application:&lt;br /&gt;
&lt;br /&gt;
     1.  Only use the VIO interface in character mode. This works well in a&lt;br /&gt;
         presentation manager environment and is a good choice for ported&lt;br /&gt;
         real mode applications. The VIO interface is also supported by the&lt;br /&gt;
         Family API mechanism. This mode is compatible with the Family API&lt;br /&gt;
         facility.&lt;br /&gt;
&lt;br /&gt;
     2.  Use the special VIO interface facilities to sidestep VIO and&lt;br /&gt;
         directly manipulate the display screen in either character or &lt;br /&gt;
         graphics mode. This also works in a presentation manager&lt;br /&gt;
         environment, but the application will not be able to run in a &lt;br /&gt;
         window. This approach can be compatible with the Family API if it &lt;br /&gt;
         is carefully implemented.&lt;br /&gt;
&lt;br /&gt;
     3.  Use the presentation manager interface--the most sophisticated&lt;br /&gt;
         interface for the least effort. The presentation manager interface&lt;br /&gt;
         provides a way to &amp;quot;operate&amp;quot; applications that will become a widely&lt;br /&gt;
         known user standard because of the capabilities of the interface,&lt;br /&gt;
         because of the support it receives from key software vendors, and&lt;br /&gt;
         because it's bundled with OS/2. The user is obviously at an&lt;br /&gt;
         advantage if he or she does not have to spend time learning a new&lt;br /&gt;
         interface and operational metaphors to use your application.&lt;br /&gt;
         Finally, Microsoft is a strong believer in the power of a&lt;br /&gt;
         graphical user interface; future releases of OS/2 will contain&lt;br /&gt;
         &amp;quot;more-faster-better&amp;quot; presentation manager features. Many of these&lt;br /&gt;
         improvements will apply to existing presentation manager&lt;br /&gt;
         applications; others will expand the interface API. The standard&lt;br /&gt;
         of performance for application interfaces, as well as for&lt;br /&gt;
         application performance, continues to evolve. The rudimentary&lt;br /&gt;
         interfaces and function of the first-generation PC software are no&lt;br /&gt;
         longer considered competitive. Although OS/2 can do nothing to&lt;br /&gt;
         alleviate the developer's burden of keeping an application's&lt;br /&gt;
         function competitive, the presentation manager is a great help in&lt;br /&gt;
         keeping the application's interface state of the art.&lt;br /&gt;
&lt;br /&gt;
     Clearly, using the presentation manager interface is the best&lt;br /&gt;
strategy for new or extensively reworked applications. The presentation&lt;br /&gt;
manager API will be expanded and improved; the INT 10-like VIO functions&lt;br /&gt;
and the VIO direct screen access capabilities will be supported for the&lt;br /&gt;
foreseeable future, but they're an evolutionary dead end. Given that, you&lt;br /&gt;
may want to use the VIO mechanism or the Family API facilities to quickly&lt;br /&gt;
port an application from a real mode version and then use the presentation&lt;br /&gt;
manager in a product upgrade release.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.2  Background I/O&lt;br /&gt;
&lt;br /&gt;
A process is in the background when it is no longer interacting directly&lt;br /&gt;
with the user. In a presentation manager environment, this means that none&lt;br /&gt;
of the process's windows are the keyboard focus. The windows themselves may&lt;br /&gt;
still be visible, or they may be obscured or iconic. In a VIO environment,&lt;br /&gt;
a process is in the background when the user has selected another screen&lt;br /&gt;
group. In this case, the application's screen display is not visible.&lt;br /&gt;
     A presentation manager application can easily continue to update its&lt;br /&gt;
window displays when it is in the background; the application can continue&lt;br /&gt;
to call the presentation manager to change its window contents in any way&lt;br /&gt;
it wishes. A presentation manager application can arrange to be informed&lt;br /&gt;
when it enters and leaves the background (actually, receives and loses the&lt;br /&gt;
keyboard focus), or it can simply carry on with its work, oblivious to the&lt;br /&gt;
issue. Background I/O can continue regardless of whether I/O form is&lt;br /&gt;
character or graphics based.&lt;br /&gt;
     VIO applications can continue to do I/O in background mode as well.&lt;br /&gt;
The VIO package maintains a logical video buffer for each screen group;&lt;br /&gt;
when VIO calls are made to update the display of a screen group that is in&lt;br /&gt;
the background, VIO makes the requested changes to the logical video&lt;br /&gt;
buffer. When the screen group is restored to the foreground, the updated&lt;br /&gt;
contents of the logical video buffer are copied to the display's physical&lt;br /&gt;
video buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.3  Graphics Under VIO&lt;br /&gt;
&lt;br /&gt;
VIO is a character-oriented package and provides character mode&lt;br /&gt;
applications with a variety of services. As we have just seen, when a&lt;br /&gt;
screen switch takes place, VIO automatically handles saving the old screen&lt;br /&gt;
image and restoring the new. VIO does provide a mechanism to allow an&lt;br /&gt;
application to sidestep VIO and directly manipulate the physical video&lt;br /&gt;
buffer, where it is then free to use any graphical capability of the&lt;br /&gt;
hardware. There are two major disadvantages to sidestepping VIO for&lt;br /&gt;
graphics rather than using the presentation manager services:&lt;br /&gt;
&lt;br /&gt;
     1.  The application is device dependent because it must manipulate the&lt;br /&gt;
         video display hardware directly.&lt;br /&gt;
&lt;br /&gt;
     2.  VIO can no longer save or restore the state of the physical video&lt;br /&gt;
         buffer during screen switch operations. The application must use a&lt;br /&gt;
         special VIO interface to provide these functions itself.&lt;br /&gt;
&lt;br /&gt;
     The following discussion applies only to applications that want to&lt;br /&gt;
sidestep the presentation manager and VIO interfaces and interact directly&lt;br /&gt;
with the display hardware.&lt;br /&gt;
     Gaining access to the video hardware is easy; the VIO call VioGetBuf&lt;br /&gt;
provides a selector to the video buffer and also gives the application's&lt;br /&gt;
ring 2 code segments, if any, permission to program the video controller's&lt;br /&gt;
registers. The complication arises from the screen-switching capabilities&lt;br /&gt;
of OS/2. When the user switches the application into a background screen&lt;br /&gt;
group, the contents of the video memory belong to someone else; the&lt;br /&gt;
application's video memory is stored somewhere in RAM. It is disastrous&lt;br /&gt;
when an application doesn't pay attention to this process and accidentally&lt;br /&gt;
updates the video RAM or the video controller while they are assigned to&lt;br /&gt;
another screen group.&lt;br /&gt;
     Two important issues are connected with screen switching: (1) How does&lt;br /&gt;
an application find out that it's in background mode? (2) Who saves and&lt;br /&gt;
restores its screen image and where? The VioScrLock call handles screen&lt;br /&gt;
access. Before every access to the display memory or the display&lt;br /&gt;
controller, an application must first issue the VioScrLock call. While the&lt;br /&gt;
call is in effect, OS/2 cannot perform any screen switches. Naturally, the&lt;br /&gt;
application must do its work and quickly release the screen switch lockout.&lt;br /&gt;
Failure to release the lock in a timely fashion has the effect of hanging&lt;br /&gt;
the system, not only for a user's explicit screen switch commands, but also&lt;br /&gt;
for other facilities that use the screen switch mechanism, such as the hard&lt;br /&gt;
error handler. Hard errors can't be presented to the user while the screen&lt;br /&gt;
lock is in effect. If OS/2 needs to switch screens, and an aberrant&lt;br /&gt;
application has the screen lock set, OS/2 will cancel the lock and perform&lt;br /&gt;
the screen switch after a period (currently 30 seconds). This is still a&lt;br /&gt;
disaster scenario, although a mollified one, because the application that&lt;br /&gt;
was summarily &amp;quot;delocked&amp;quot; will probably end up with a trashed screen image.&lt;br /&gt;
The screen lock and unlock calls execute relatively rapidly, so they can be&lt;br /&gt;
called frequently to protect only the actual write-to-screen operation,&lt;br /&gt;
leaving the screen unlocked during computation. Basically, an application&lt;br /&gt;
should use VioScrLock to protect a block of I/O that can be written, in its&lt;br /&gt;
entirety, without significant recomputation. Examples of such blocks are a&lt;br /&gt;
screen scroll, a screen erase, and a write to a cell in a spreadsheet&lt;br /&gt;
program.&lt;br /&gt;
     VioScrLock must be used to protect code sequences that program the&lt;br /&gt;
display hardware as well as code sequences that write to video memory. Some&lt;br /&gt;
peripheral programming sequences are noninterruptible. For example, a two-&lt;br /&gt;
step programming sequence in which the first I/O write selects a&lt;br /&gt;
multiplexed register and the second write modifies that register is&lt;br /&gt;
uninterruptible because the first write placed the peripheral device into a&lt;br /&gt;
special state. Such sequences must be protected within one lock/unlock&lt;br /&gt;
pair.&lt;br /&gt;
     Sometimes when an application calls VioScrLock, it receives a special&lt;br /&gt;
error code that says, &amp;quot;The screen is unavailable.&amp;quot; This means that the&lt;br /&gt;
screen has been switched into the background and that the application may&lt;br /&gt;
not--and must not--manipulate the display hardware. Typically, the program&lt;br /&gt;
issues a blocking form of VioScrLock that suspends the thread until the&lt;br /&gt;
screen is again in the foreground and the video display buffers contain&lt;br /&gt;
that process's image.&lt;br /&gt;
     An application that directly manipulates the video hardware must do&lt;br /&gt;
more than simply lay low when it is in the background. It must also save&lt;br /&gt;
and restore the entire video state--the contents of the display buffer and&lt;br /&gt;
the modes, palates, cursors, and so on of the display controller. VIO does&lt;br /&gt;
not provide this service to direct-screen manipulation processes for two&lt;br /&gt;
reasons. First, the process is very likely using the display in a graphics&lt;br /&gt;
mode. Some display cards contain a vast amount of video memory, and VIO&lt;br /&gt;
would be forced to save it all just in case the application was using it&lt;br /&gt;
all. Second, many popular display controllers such as the EGA and&lt;br /&gt;
compatibles contain many write-only control registers. This means that VIO&lt;br /&gt;
cannot read the controller state back from the card in order to save it for&lt;br /&gt;
a later restoration. The only entity that understands the state of the&lt;br /&gt;
card, and therefore the only entity that can restore that state, is the&lt;br /&gt;
code that programmed it--the application itself.&lt;br /&gt;
     But how does the system notify the process when it's time to save or&lt;br /&gt;
restore? Processes can call the system in many ways, but the system can't&lt;br /&gt;
call processes. OS/2 deals with this situation by inverting the usual&lt;br /&gt;
meaning of call and return. When a process first decides to refresh its own&lt;br /&gt;
screen, it creates an extra thread and uses that thread to call the&lt;br /&gt;
VioSavRedrawWait function. The thread doesn't return from this call right&lt;br /&gt;
away; instead, VIO holds the thread &amp;quot;captive&amp;quot; until it's time for a screen&lt;br /&gt;
switch. To notify the process that it must now save its screen image, VIO&lt;br /&gt;
allows the captive thread to return from the VioSavRedrawWait call. The&lt;br /&gt;
process then saves the display state and screen contents, typically using&lt;br /&gt;
the returned thread. When the save operation is complete, VioSavRedrawWait&lt;br /&gt;
is called again. This notifies VIO that the save is complete and that the&lt;br /&gt;
screen can now be switched; it also resets the cycle so that the process&lt;br /&gt;
can again be notified when it's time to restore its saved screen image. In&lt;br /&gt;
effect, this mechanism makes the return from VioSavRedrawWait analogous to&lt;br /&gt;
a system-to-process call, and it makes the later call to VioSavRedrawWait&lt;br /&gt;
analogous to a return from process to system.&lt;br /&gt;
     The design of OS/2 generally avoids features in which the system calls&lt;br /&gt;
a process to help a system activity such as screen switching. This is&lt;br /&gt;
because a tenet of the OS/2 design religion is that an aberrant process&lt;br /&gt;
should not be able to crash the system. Clearly, we're vulnerable to that&lt;br /&gt;
in this case. VIO postpones the screen switch until the process saves its&lt;br /&gt;
screen image, but what if the process somehow hangs up and doesn't complete&lt;br /&gt;
the save? The screen is in an indeterminate state, and no process can use&lt;br /&gt;
the screen and keyboard. As far as the user is concerned, the system has&lt;br /&gt;
crashed. True, other processes in the system are alive and well, but if the&lt;br /&gt;
user can't get to them, even to save his or her work, their continued&lt;br /&gt;
health is of little comfort.&lt;br /&gt;
     The designers of OS/2 were stuck here, between a rock and a hard&lt;br /&gt;
place: Applications had to be able to save their screen image if they were&lt;br /&gt;
to have direct video access, but such a facility violated the &amp;quot;no crashing&amp;quot;&lt;br /&gt;
tenet of the design religion. Because the video access had to be supported&lt;br /&gt;
and the system had to be crash resistant, we found a two-part workaround.&lt;br /&gt;
     The first part concerns the most common cause of a process hanging up&lt;br /&gt;
in its screen-save operation: hard errors. When a hard error occurs, the&lt;br /&gt;
hard error daemon uses the screen switch mechanism to take control of the&lt;br /&gt;
screen and the keyboard. The hard error daemon saves the existing screen&lt;br /&gt;
image and keeps the application that was in the foreground at the time of&lt;br /&gt;
the hard error from fighting with the daemon over control of the screen and&lt;br /&gt;
the keyboard. However, if the hard error daemon uses the screen-switching&lt;br /&gt;
mechanism and if the screen-switching mechanism allows the foreground&lt;br /&gt;
process to save its own screen image, that process might, while saving its&lt;br /&gt;
screen image, try to use the device that has the hard error and thus&lt;br /&gt;
deadlock the system. The device in error won't service more requests until&lt;br /&gt;
the hard error is cleared, but the hard error can't be cleared until the&lt;br /&gt;
daemon takes control. The daemon can't take control until the foreground&lt;br /&gt;
process is through saving, and the foreground process can't complete saving&lt;br /&gt;
until the device services its request. Note that this deadlock doesn't&lt;br /&gt;
require an explicit I/O operation on the part of the foreground process;&lt;br /&gt;
simply allocating memory or referencing a segment might cause swapping or&lt;br /&gt;
loading activity on the device that is experiencing the hard error.&lt;br /&gt;
     A two-part approach is used to solve this problem. First, deadlocks&lt;br /&gt;
involving the hard error daemon are managed by having the hard error screen&lt;br /&gt;
switch do a partial screen save. When I said earlier that VIO would not&lt;br /&gt;
save the video memory of direct access screen groups, I was lying a bit.&lt;br /&gt;
When the system is doing a hard error screen switch, VIO will save the&lt;br /&gt;
first part (typically 4 KB) of the video memory--enough to display a page&lt;br /&gt;
of text. We don't have to worry about how much video RAM the application&lt;br /&gt;
was using because the video display will be switched to character mode and&lt;br /&gt;
the hard error daemon will overwrite only a small part of video memory.&lt;br /&gt;
Naturally, this means that the hard error daemon must always restore the&lt;br /&gt;
original screen group; it can't switch to a third screen group because the&lt;br /&gt;
first one's video memory wasn't fully saved.&lt;br /&gt;
     VIO and the hard error daemon keep enough free RAM around to save this&lt;br /&gt;
piece of the video memory so that a hard error screen switch can always&lt;br /&gt;
take place without the need for memory swapping. When the hard error daemon&lt;br /&gt;
is finished with the screen, the overwritten video memory is restored from&lt;br /&gt;
the buffer. As we discussed above, however, VIO can't restore the state of&lt;br /&gt;
the video controller itself; only the application can do that. The&lt;br /&gt;
VioModeWait function is used to notify the application that it must restore&lt;br /&gt;
the screen state.&lt;br /&gt;
     In summary, any application that directly accesses the video hardware&lt;br /&gt;
must provide captive threads to VioSavRedrawWait and to VioModeWait.&lt;br /&gt;
VioSavRedrawWait will return when the application is to save or to restore&lt;br /&gt;
the video memory. VioModeWait will return when the application is to&lt;br /&gt;
restore the state of the video controller from the application's own record&lt;br /&gt;
of the controller's state.&lt;br /&gt;
     The second part of the &amp;quot;application hangs while saving screen and&lt;br /&gt;
hangs system&amp;quot; solution is unfortunately ad hoc: If the application does not&lt;br /&gt;
complete its screen save operation within approximately 30 seconds, the&lt;br /&gt;
system considers it hung and switches the screen anyway. The hung process&lt;br /&gt;
is suspended while it's in background so that it won't suddenly &amp;quot;come&lt;br /&gt;
alive&amp;quot; and manipulate the screen. When the process is again in the&lt;br /&gt;
foreground, the system unsuspends it and hopes that it will straighten&lt;br /&gt;
itself out. In such a case, the application's screen image may be trashed.&lt;br /&gt;
At best, the user can enter a &amp;quot;repaint screen&amp;quot; command to the application&lt;br /&gt;
and all will be well; at worst, the application is hung up, but the system&lt;br /&gt;
itself is alive and well. Building a system that can detect and correct&lt;br /&gt;
errors on the part of an application is impossible; the best that we can&lt;br /&gt;
hope to do is to keep an aberrant application from damaging the rest of the&lt;br /&gt;
system.&lt;br /&gt;
     I hope that this long and involved discussion of rules, regulations,&lt;br /&gt;
doom, and disaster has not frightened you into contemplating programs that&lt;br /&gt;
communicate by Morse code. You need be concerned with these issues only if&lt;br /&gt;
you write applications that manipulate the display device directly,&lt;br /&gt;
circumventing either VIO or the presentation manager interfaces. These&lt;br /&gt;
concerns are not an issue when you are writing ordinary text applications&lt;br /&gt;
that use VIO or the presentation manager or graphics applications that use&lt;br /&gt;
the presentation manager. VIO and the presentation manager handle screen&lt;br /&gt;
saving and support background display writing. Finally, I'll point out, as&lt;br /&gt;
a curiosity, that even processes that use handle operations only to write&lt;br /&gt;
to STDOUT use VIO or the presentation manager. When STDOUT points to the&lt;br /&gt;
screen device, the operating system routes STDOUT writes to the&lt;br /&gt;
VIO/presentation manager packages. This is, of course, invisible to the&lt;br /&gt;
application; it need not concern itself with foreground/background, EGA&lt;br /&gt;
screen modes, hard error screen restorations, and the like.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==14  Interactive Programs==&lt;br /&gt;
&lt;br /&gt;
A great many applications interact with the user via the screen and the&lt;br /&gt;
keyboard. Because the primary function of most desktop computers is to run&lt;br /&gt;
interactive applications, OS/2 contains a variety of services that make&lt;br /&gt;
interaction powerful and efficient.&lt;br /&gt;
     As we've seen in earlier chapters, interactive programs can use the&lt;br /&gt;
presentation manager to manage their interface, or they can do it&lt;br /&gt;
themselves, using VIO or direct video access for their I/O. The&lt;br /&gt;
presentation manager provides a great deal of function and automatically&lt;br /&gt;
solves a great many problems. For example, a presentation manager&lt;br /&gt;
application doesn't have to concern itself with the sharing of the single&lt;br /&gt;
keyboard among all processes in its screen group. The presentation manager&lt;br /&gt;
takes care of that by handling the keyboard and by simply sending keyboard&lt;br /&gt;
events to each process, as appropriate.&lt;br /&gt;
     If you're writing an application that uses the presentation manager,&lt;br /&gt;
then you can skip this chapter. If you're writing an application that does&lt;br /&gt;
not use the presentation manager but that may be used in an interactive&lt;br /&gt;
fashion, it's very important that you understand the issues discussed in&lt;br /&gt;
this chapter. They apply to all programs that use VIO or the STDIN/STDOUT&lt;br /&gt;
handles to do interactive I/O, even if such programs are being run via the&lt;br /&gt;
presentation manager.&lt;br /&gt;
&lt;br /&gt;
===14.1  I/O Architecture===&lt;br /&gt;
&lt;br /&gt;
Simply put, the system I/O architecture says that all programs read their&lt;br /&gt;
main input from the STDIN handle and write their main output to the STDOUT&lt;br /&gt;
handle. This applies to all non-presentation manager applications, but&lt;br /&gt;
especially to interactive applications. The reason is that OS/2 and&lt;br /&gt;
program-execution utilities such as CMD.EXE (shell programs) cooperate to&lt;br /&gt;
use the STDIN/STDOUT mechanism to control access to the screen and&lt;br /&gt;
keyboard. For example, if two processes read from the keyboard at the same&lt;br /&gt;
time, some keys go to one process, and the rest go to the other in an&lt;br /&gt;
unpredictable fashion. Likewise, it is a bad idea for more than one process&lt;br /&gt;
to write to the screen at the same time.&lt;br /&gt;
1. Within the same screen group and/or window, of course.&lt;br /&gt;
Applications that use different virtual screens can each write to&lt;br /&gt;
their own screen without regard for other virtual screens.&lt;br /&gt;
1 Clearly, you don't want too many&lt;br /&gt;
processes doing keyboard/screen I/O within a single screen group, but you&lt;br /&gt;
also don't want too few. It would be embarrassing if a user terminated one&lt;br /&gt;
interactive application in a screen group, such as a program run from&lt;br /&gt;
CMD.EXE, and CMD.EXE failed to resume use of the keyboard/screen to print a&lt;br /&gt;
prompt.&lt;br /&gt;
     So how will we handle this? We might be running a great many processes&lt;br /&gt;
in a screen group. For example, you could use CMD.EXE to execute a&lt;br /&gt;
spreadsheet program, which was told to execute a subshell--another copy of&lt;br /&gt;
CMD.EXE. The user could then execute a program to interpret a special batch&lt;br /&gt;
script, which in turn executes an editor. And this editor was told to run a&lt;br /&gt;
copy of a C compiler to scan the source being edited for errors. Oh, yes,&lt;br /&gt;
and we forgot to mention that the top level CMD.EXE was told to run a copy&lt;br /&gt;
of the assembler in parallel with all these other operations (similar to&lt;br /&gt;
the UNIX &amp;quot;&amp;amp;&amp;quot; operation).&lt;br /&gt;
     Many processes are running in this screen group; some of them are&lt;br /&gt;
interactive, and some are not, and at any time only one is using the&lt;br /&gt;
keyboard and the screen. Although it would be handy to declare that the&lt;br /&gt;
most recently executed process will use the keyboard and the screen, you&lt;br /&gt;
can't: The most recently executed program was the C compiler, and it's not&lt;br /&gt;
even interactive. OS/2 cannot decide which process should be using the&lt;br /&gt;
screen and keyboard because OS/2 lacks any knowledge of the function of&lt;br /&gt;
each process. OS/2 knows only their child-parent relationships, and the&lt;br /&gt;
situation can be far too complex for that information to be sufficient.&lt;br /&gt;
     Because OS/2 can't determine which process should be using the screen&lt;br /&gt;
and the keyboard, it doesn't try. The processes themselves make the&lt;br /&gt;
determination. The rule is simple: The process that is currently using the&lt;br /&gt;
screen and the keyboard can grant access to a child process, or it can keep&lt;br /&gt;
access for itself. If a process grants access to a child process, then it&lt;br /&gt;
must keep off the screen and the keyboard until that child terminates. Once&lt;br /&gt;
the child process is granted use of the screen and the keyboard, the child&lt;br /&gt;
process is free to do as it wishes, perhaps granting access to its own&lt;br /&gt;
children. Until that child process terminates, the parent must avoid device&lt;br /&gt;
conflict by staying quiet.&lt;br /&gt;
     Let's look at how this works in real life. For example, CMD.EXE, the&lt;br /&gt;
first process in the screen group, starts up with STDIN open on the&lt;br /&gt;
keyboard and STDOUT open on the screen. (The system did this by magic.)&lt;br /&gt;
When this copy of CMD.EXE is told to execute the spreadsheet program,&lt;br /&gt;
CMD.EXE doesn't know if the spreadsheet program is interactive or not, so&lt;br /&gt;
it lets the child process--the spreadsheet program--inherit its STDIN and&lt;br /&gt;
STDOUT handles, which point to the keyboard and to the screen. Because&lt;br /&gt;
CMD.EXE granted access to the screen and the keyboard to the child, CMD.EXE&lt;br /&gt;
can't use STDIN or STDOUT until that child process terminates. Typically,&lt;br /&gt;
at this point CMD.EXE would DosCWait on its child process.&lt;br /&gt;
     Now the spreadsheet program comes alive. It writes to STDOUT, which is&lt;br /&gt;
the screen, and it reads from STDIN, which is the keyboard. When the&lt;br /&gt;
spreadsheet program is instructed to run CMD.EXE, it does so, presuming, as&lt;br /&gt;
did its parent, that CMD.EXE is interactive and therefore letting CMD.EXE&lt;br /&gt;
inherit its STDIN and STDOUT handles. Now the spreadsheet must avoid any&lt;br /&gt;
STDIN/STDOUT I/O until its child--CMD.EXE--terminates. As long as these&lt;br /&gt;
processes continue to run interactive children, things are going to work&lt;br /&gt;
out OK. When the children start to die and execution starts popping back up&lt;br /&gt;
the tree, applications restart, using the screen and the keyboard in the&lt;br /&gt;
proper order.&lt;br /&gt;
     But what about the detached assembly that CMD.EXE started before it&lt;br /&gt;
ran the spreadsheet? In this case, the user has explicitly told CMD.EXE&lt;br /&gt;
that it wants the application run &amp;quot;detached&amp;quot; from the keyboard. If the user&lt;br /&gt;
specified a STDIN for the assembler--perhaps a file--then CMD.EXE sets that&lt;br /&gt;
up for the child's STDIN. If the user didn't specify an alternate STDIN,&lt;br /&gt;
CMD.EXE opens STDIN on the  NULL device so that an application that reads&lt;br /&gt;
it will receive EOF. In this way, CMD.EXE (which knew that the application&lt;br /&gt;
wasn't to use the keyboard because the user gave explicit instructions) did&lt;br /&gt;
not let the child process inherit STDIN, so CMD.EXE continues to use it,&lt;br /&gt;
printing a new prompt and reading a new command. Figure 14-1 shows a&lt;br /&gt;
typical process tree. The shaded processes have inherited a STDIN, which&lt;br /&gt;
points to the keyboard, and a STDOUT, which points to the screen. All such&lt;br /&gt;
processes must lie on a single path if the rules are followed because each&lt;br /&gt;
process has the option of allowing a maximum of one child to inherit its&lt;br /&gt;
STDIN and STDOUT handles unchanged.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ÀÄÄÂÄÄÄÄÄÄÄÄÂÄÄÙ                 ÚÄÄÄÄ¿    Processes&lt;br /&gt;
             ÚÄÄÄÄÄÄÙ        ÀÄÄÄÄÄÄ¿             ³°°°°³  = using the&lt;br /&gt;
             ³                      ³             ÀÄÄÄÄÙ    keyboard &lt;br /&gt;
         ÚÄÄÄÁÄÄ¿                ÚÄÄÁÄÄÄ¿&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ÀÄÂÄÄÄÄÙ                ÀÄÂÄÄÂÄÙ&lt;br /&gt;
        ÚÄÄÙ                    ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
    ÚÄÄÄÁÄÄ¿                 ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
    ³      ³                 ³°°°°³    ³    ³&lt;br /&gt;
    ³      ³                 ÀÂÄÄÂÙ    ÀÄÄÄÂÙ&lt;br /&gt;
    ÀÄÂÄÄÂÄÙ               ÚÄÄÙ  ÀÄÄ¿      ÀÄÄ¿&lt;br /&gt;
   ÚÄÄÙ  ÀÄÄ¿           ÚÄÄÁÄ¿    ÚÄÁÄÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
ÚÄÄÁÄ¿    ÚÄÁÄÄ¿        ³°°°°³    ³    ³    ³    ³&lt;br /&gt;
³    ³    ³    ³        ÀÂÄÄÂÙ    ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÙ    ÀÄÄÄÄÙ      ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
                   ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
                   ³    ³    ³°°°°³&lt;br /&gt;
                   ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Figure 14-1.  Processes using the keyboard.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     You are undoubtedly becoming a bit concerned at this point: &amp;quot;Does this&lt;br /&gt;
mean I'm forced to use the limited, serial STDIN/STDOUT interface for my&lt;br /&gt;
high-resolution graphics output?&amp;quot; I'm glad you asked. What we've been&lt;br /&gt;
discussing is the architectural model that must be followed because it's&lt;br /&gt;
used systemwide to avoid screen and keyboard conflicts. However,&lt;br /&gt;
applications can and should use special services to optimize their&lt;br /&gt;
interactive I/O as long as they do so according to the architectural model.&lt;br /&gt;
Specifically, OS/2 provides the KBD, VIO, and MOU dynlink packages. These&lt;br /&gt;
high-performance programs interface directly with the hardware, avoiding&lt;br /&gt;
the STDIN/STDOUT limited interfaces. The key is &amp;quot;directly with the&lt;br /&gt;
hardware&amp;quot;: A process is welcome to use hardware-specific interfaces&lt;br /&gt;
to optimize performance, but only after it has ensured that the&lt;br /&gt;
architectural model grants it access to that device.&lt;br /&gt;
     In practice, this is straightforward. Any interactive program that&lt;br /&gt;
wants to use KBD first ensures (via DosQHandType) that its STDIN handle is&lt;br /&gt;
open on the keyboard. If STDIN is not open on the keyboard, the keyboard&lt;br /&gt;
belongs to another program, and the interactive program must not burst in&lt;br /&gt;
on the rightful owner by using KBD. All dynlink device interfaces are&lt;br /&gt;
trusting souls and won't check your bona fides before they do their stuff,&lt;br /&gt;
so the application must look before it leaps. The same applies to STDOUT,&lt;br /&gt;
to the keyboard device, and to the VIO package. All applications must&lt;br /&gt;
verify that STDIN and STDOUT point to the keyboard and the screen before&lt;br /&gt;
they use any device-direct interface, which includes VIO, KBD, MOU, and&lt;br /&gt;
direct device access.&lt;br /&gt;
     What's an interactive program to do if it finds that STDIN or STDOUT&lt;br /&gt;
doesn't point to the keyboard and screen devices? I don't know, but the&lt;br /&gt;
author of the application does. Some applications might not be truly&lt;br /&gt;
interactive and therefore would work fine. For example, Microsoft Macro&lt;br /&gt;
Assembler (MASM) can prompt the user for the names of source, object, and&lt;br /&gt;
listing files. Although MASM is technically interacting with the user, MASM&lt;br /&gt;
is not an interactive application because it doesn't depend on the ability&lt;br /&gt;
to interact to do its work. If STDIN points to a file, MASM is perfectly&lt;br /&gt;
happy reading the filenames from that file. MASM doesn't need to see if&lt;br /&gt;
STDIN points to the keyboard because MASM doesn't need to use the KBD&lt;br /&gt;
package. Instead, MASM reads its names from STDIN and takes what it&lt;br /&gt;
gets.&lt;br /&gt;
     Other programs may not require an interactive interface, but when they&lt;br /&gt;
are interacting, they may want to use KBD or VIO to improve performance.&lt;br /&gt;
Such applications should test STDIN and STDOUT to see if they point to&lt;br /&gt;
the appropriate devices. If they do, applications can circumvent the&lt;br /&gt;
STDIN/STDOUT limitations and use KBD and VIO. If they don't, the&lt;br /&gt;
applications are stuck with STDIN and STDOUT. Finally, many interactive&lt;br /&gt;
applications make no sense at all in a noninteractive environment. These&lt;br /&gt;
applications need to check STDIN and STDOUT, and, if they don't point to&lt;br /&gt;
the devices, the applications should write an error message to STDERR and&lt;br /&gt;
terminate. Admittedly, the user is in error if he or she attempts to run an&lt;br /&gt;
interactive application, such as a WYSIWYG editor, detached, but printing&lt;br /&gt;
an error message is far better than trashing the display screen and&lt;br /&gt;
fighting with CMD.EXE over the keyboard. The screen group would then be&lt;br /&gt;
totally unusable, and the user might not even be able to terminate the&lt;br /&gt;
editor if he or she can't get the terminate command through the keyboard&lt;br /&gt;
contention.&lt;br /&gt;
     It's technically possible, although highly unusual, for an application&lt;br /&gt;
to inherit access to the keyboard yet not have access to the screen. More&lt;br /&gt;
commonly, an application has access to the screen but not to the keyboard.&lt;br /&gt;
Although most users would find it confusing, power users can detach&lt;br /&gt;
programs such as compilers so that any output summary or error messages&lt;br /&gt;
they produce appear on the screen. Although the user may end up with&lt;br /&gt;
intermingled output, he or she may like the instant notification. Each&lt;br /&gt;
application that wants to use VIO, KBD, or the environment manager needs to&lt;br /&gt;
check STDIN and STDOUT individually for access to the appropriate device.&lt;br /&gt;
     Earlier in this section, we talked about how applications work when&lt;br /&gt;
they create children that inherit the screen and the keyboard, and it&lt;br /&gt;
probably sounded complicated. In practice, it can be simple. For example,&lt;br /&gt;
the technique used to DosExecPgm a child that will inherit the keyboard can&lt;br /&gt;
be used when the parent itself doesn't have the keyboard and thus can't&lt;br /&gt;
bequeath it. Therefore, the parent doesn't need to check its STDIN status&lt;br /&gt;
during the DosExecPgm. To summarize, here are the rules:&lt;br /&gt;
&lt;br /&gt;
     Executing Programs&lt;br /&gt;
&lt;br /&gt;
     þ  If the child process is to inherit the STDIN handle, the parent&lt;br /&gt;
        process must not access that handle any further until the child&lt;br /&gt;
        process terminates.&lt;br /&gt;
&lt;br /&gt;
     þ  If the child process is not to inherit the STDIN handle (so that&lt;br /&gt;
        your program can continue to interact), then the child process&lt;br /&gt;
        STDIN must be opened on a file or on the NULL device. Don't rely on&lt;br /&gt;
        the child not to use the handle; the child might DosExecPgm a&lt;br /&gt;
        grandchild that is not so well mannered.&lt;br /&gt;
&lt;br /&gt;
     þ  A process can let only one child at a time inherit its STDIN. If a&lt;br /&gt;
        process is going to run multiple child processes in parallel, only&lt;br /&gt;
        one can inherit STDIN; the others must use alternative STDIN&lt;br /&gt;
        sources.&lt;br /&gt;
&lt;br /&gt;
     þ  All these rules apply to STDINs open on pipes and files as well as&lt;br /&gt;
        to KBD, so your application needn't check the source of STDIN.&lt;br /&gt;
&lt;br /&gt;
     All Processes&lt;br /&gt;
&lt;br /&gt;
     þ  Verify that the STDIN handle points to the keyboard before using&lt;br /&gt;
        KBD, SIGBRK, or SIGCTLC (see below). You must not use these direct&lt;br /&gt;
        device facilities if STDIN is not open on the keyboard.&lt;br /&gt;
&lt;br /&gt;
     þ  Verify that the STDOUT handle points to the screen before using&lt;br /&gt;
        VIO. You must not use direct device facilities if STDOUT is not&lt;br /&gt;
        open on the screen.&lt;br /&gt;
&lt;br /&gt;
     þ  If the process executes any child that inherits its STDIN, it must&lt;br /&gt;
        not terminate itself until that child process terminates. This is&lt;br /&gt;
        because the parent will assume that the termination of the direct&lt;br /&gt;
        child means that the STDIN handle is now available.&lt;br /&gt;
&lt;br /&gt;
===14.2  Ctrl-C and Ctrl-Break Handling===&lt;br /&gt;
&lt;br /&gt;
Just when you think that it's safe to go back into the operating system,&lt;br /&gt;
one more device and process tree issue needs to be discussed: the handling&lt;br /&gt;
of Ctrl-C and Ctrl-Break. (Once again, this discussion applies only to&lt;br /&gt;
programs that don't explicitly use the presentation manager facility. Those&lt;br /&gt;
applications that do use the presentation manager have all these issues&lt;br /&gt;
handled for them automatically.) These two events are tied to the keyboard&lt;br /&gt;
hardware, so their routing has a great deal in common with the above&lt;br /&gt;
discussion. The fundamental problem is simple: When the user presses Ctrl-C&lt;br /&gt;
or Ctrl-Break, what's the operating system to do? Clearly, a process or&lt;br /&gt;
processes or perhaps an entire subtree of processes must be killed or&lt;br /&gt;
signaled. But do we kill or signal? And which one(s)?&lt;br /&gt;
     OS/2 defines a convention that allows the processes themselves to&lt;br /&gt;
decide. Consider a type of application--a &amp;quot;command application&amp;quot;--that runs&lt;br /&gt;
in command mode. In command mode, a command application reads a command,&lt;br /&gt;
executes the command, and then typically returns to command mode.&lt;br /&gt;
Furthermore, when the user presses Ctrl-Break, the command application&lt;br /&gt;
doesn't want to terminate but to stop what it's doing and return to command&lt;br /&gt;
mode. This is the style of most interactive applications but not that of&lt;br /&gt;
most noninteractive applications. For example, if the user types MASM to&lt;br /&gt;
CMD.EXE, the CMD.EXE program runs MASM as a child process. CMD.EXE is a&lt;br /&gt;
command application, but MASM is not. The distinction between &amp;quot;command&lt;br /&gt;
application&amp;quot; and &amp;quot;noncommand application&amp;quot; is not made by OS/2 but is merely&lt;br /&gt;
descriptive terminology that is useful in this discussion.&lt;br /&gt;
     The system convention is that Ctrl-C and Ctrl-Break mean &amp;quot;Stop what&lt;br /&gt;
you're doing.&amp;quot; OS/2 generates signals in response to Ctrl-C and Ctrl-Break;&lt;br /&gt;
it never directly kills a process. OS/2 can easily decide which process to&lt;br /&gt;
signal when Ctrl-C or Ctrl-Break is pressed: It signals the lowest command&lt;br /&gt;
process in the process tree in that screen group. At first glance, this may&lt;br /&gt;
not seem easy. How can OS/2 distinguish command processes, and how can it&lt;br /&gt;
determine the &amp;quot;lowest&amp;quot;? The total process tree in a screen group may be&lt;br /&gt;
very complex; some processes in it may have died, creating multiple now-&lt;br /&gt;
independent &amp;quot;treelets.&amp;quot;&lt;br /&gt;
     The process tree may be complex, but the tree of processes using the&lt;br /&gt;
keyboard is simpler because a process can't let multiple children&lt;br /&gt;
simultaneously inherit its STDIN. A process can only inherit the&lt;br /&gt;
keyboard,&lt;br /&gt;
2. Actually, a process should only inherit the&lt;br /&gt;
keyboard. The keyboard device can be opened explicitly, but doing&lt;br /&gt;
so when a process's inherited STDIN doesn't point to the keyboard&lt;br /&gt;
device would be a serious error.&lt;br /&gt;
2 not open it explicitly; so a single path down the tree must&lt;br /&gt;
intersect (or contain) all command processes. This single path can't be&lt;br /&gt;
fragmented because of missing processes due to child death because a&lt;br /&gt;
process that has let a child inherit its STDIN must not terminate until the&lt;br /&gt;
child does. So, all OS/2 needs is to find any command process in the&lt;br /&gt;
command subtree and then look at its descendants for another command&lt;br /&gt;
process and so on. The bottommost process receives the signal.&lt;br /&gt;
3. Actually, OS/2 uses a more efficient algorithm than this; I'm&lt;br /&gt;
merely illustrating that finding the lowest command process is&lt;br /&gt;
not difficult.&lt;br /&gt;
3&lt;br /&gt;
     Figure 14-2 illustrates a possible process tree. The shaded processes&lt;br /&gt;
have inherited handles to the keyboard and screen; those marked with C are&lt;br /&gt;
command processes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ³°°°°°°c°°°°°°°³&lt;br /&gt;
                 ÀÄÄÂÄÄÄÄÄÄÄÄÂÄÄÙ                ÚÄÄÄÄ¿    Processes&lt;br /&gt;
             ÚÄÄÄÄÄÄÙ        ÀÄÄÄÄÄÄ¿            ³°°°°³  = using the&lt;br /&gt;
             ³                      ³            ÀÄÄÄÄÙ    keyboard &lt;br /&gt;
         ÚÄÄÄÁÄÄ¿                ÚÄÄÁÄÄÄ¿&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ³      ³                ³°°c°°°³&lt;br /&gt;
         ÀÄÂÄÄÄÄÙ                ÀÄÂÄÄÂÄÙ&lt;br /&gt;
        ÚÄÄÙ                    ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
    ÚÄÄÄÁÄÄ¿                 ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
    ³      ³                 ³°c°°³    ³    ³&lt;br /&gt;
    ³      ³                 ÀÂÄÄÂÙ    ÀÄÄÄÂÙ&lt;br /&gt;
    ÀÄÂÄÄÂÄÙ               ÚÄÄÙ  ÀÄÄ¿      ÀÄÄ¿&lt;br /&gt;
   ÚÄÄÙ  ÀÄÄ¿           ÚÄÄÁÄ¿    ÚÄÁÄÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
ÚÄÄÁÄ¿    ÚÄÁÄÄ¿        ³°c°°³    ³    ³    ³    ³&lt;br /&gt;
³    ³    ³    ³        ÀÂÄÄÂÙ    ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÙ    ÀÄÄÄÄÙ      ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
                   ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
                   ³    ³    ³°°°°³&lt;br /&gt;
                   ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Figure 14-2.  Ctrl-C routing in a process tree.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This now begs the final question: How can OS/2 tell if an application&lt;br /&gt;
is a command process or not? It can tell because all command&lt;br /&gt;
processes/command applications do something that other processes never do.&lt;br /&gt;
By definition, a command process doesn't want to be summarily killed when&lt;br /&gt;
the user presses Ctrl-C or Ctrl-Break, so all command processes establish&lt;br /&gt;
signal handlers for Ctrl-C and Ctrl-Break. Because all command processes&lt;br /&gt;
intercept Ctrl-C and Ctrl-Break, all we need now is to establish the&lt;br /&gt;
convention that only command processes intercept Ctrl-C and Ctrl-Break.&lt;br /&gt;
This hearkens back to our earlier discussion of checking STDIN before&lt;br /&gt;
directly using the keyboard device. Telling the keyboard device that you&lt;br /&gt;
want the Ctrl-C or Ctrl-Break signals routed to your process is a form of&lt;br /&gt;
I/O with the keyboard device, and it must only be done if your program has&lt;br /&gt;
verified that STDIN points to the keyboard device. Furthermore,&lt;br /&gt;
intercepting Ctrl-C or Ctrl-Break just so that your program can clean up&lt;br /&gt;
during unexpected termination is unnecessary and insufficient. The SIGTERM&lt;br /&gt;
signal or, better, the exitlist mechanism provides this capability and&lt;br /&gt;
covers causes of death other than the keyboard. So all processes that&lt;br /&gt;
intercept Ctrl-C and Ctrl-Break have access to the keyboard, and they want&lt;br /&gt;
to do something other than die when the user presses Ctrl-C or Ctrl-Break.&lt;br /&gt;
They fit the command process definition.&lt;br /&gt;
     Now that we've exhaustively shown how OS/2 finds which process to send&lt;br /&gt;
a Ctrl-C signal, what should the process do when it gets the signal? Obey&lt;br /&gt;
the system convention and stop what it's doing as quickly as is wise. If&lt;br /&gt;
the application isn't working on a command, the application typically&lt;br /&gt;
flushes the keyboard type-ahead buffer and reprompts. If the application is&lt;br /&gt;
working on a command that is implemented in code within the application,&lt;br /&gt;
the application jumps from the signal handler to its command loop or, more&lt;br /&gt;
commonly, sets a flag to terminate the current command prematurely.&lt;br /&gt;
4. Occasionally, terminating a command halfway through could&lt;br /&gt;
leave the user's work trashed. In such a case, finishing the&lt;br /&gt;
command is prudent.&lt;br /&gt;
4&lt;br /&gt;
     Finally, if the application is running a child process, it typically&lt;br /&gt;
stops what it's doing by issuing a DosKill on that child command subtree.&lt;br /&gt;
This, then, is how Ctrl-C can kill a program such as MASM. Ctrl-C is sent&lt;br /&gt;
to MASM's closest ancestor that is a command process,&lt;br /&gt;
5. Often, CMD.EXE is MASM's direct ancestor, but other programs, such&lt;br /&gt;
as a build utility, could have come between CMD.EXE and MASM.&lt;br /&gt;
5 which in turn issues&lt;br /&gt;
a DosKill on MASM's subtree. MASM does any exitlist cleanup that it&lt;br /&gt;
wishes (probably deleting scratch files) and then terminates. When the&lt;br /&gt;
command process that ran MASM, typically CMD.EXE, sees that MASM has&lt;br /&gt;
terminated, it prints ^C on the screen, followed by a new prompt.&lt;br /&gt;
&lt;br /&gt;
==15  The File System==&lt;br /&gt;
&lt;br /&gt;
The file system in OS/2 version 1.0 is little changed from that of MS-DOS,&lt;br /&gt;
partially in an effort to preserve compatibility with MS-DOS programs and&lt;br /&gt;
partially due to limitations imposed by the project's schedule. When the&lt;br /&gt;
schedule for an &amp;quot;all singing, all dancing&amp;quot; OS/2 was shown to be too long,&lt;br /&gt;
planned file system improvements were moved to a future release. To explain&lt;br /&gt;
the rationale for postponing something so useful, I'll digress a little.&lt;br /&gt;
     The microcomputer industry developed around the dual concepts of mass&lt;br /&gt;
market software and standards. Because software is mass marketed, you can&lt;br /&gt;
buy some very sophisticated and useful programs for a modest sum of money--&lt;br /&gt;
at least modest in comparison to the development cost, which is often&lt;br /&gt;
measured in millions of dollars. Mass marketing encourages standards&lt;br /&gt;
because users don't want to buy machines, peripherals, and systems that&lt;br /&gt;
don't run these programs. Likewise, the acceptance of the standards&lt;br /&gt;
encourages the development of mass market software because standards make&lt;br /&gt;
it possible for a single binary program to execute correctly on a great&lt;br /&gt;
many machines and thus provide a market big enough to repay the development&lt;br /&gt;
costs of a major application.&lt;br /&gt;
     This synergy, or positive feedback, between standards and mass market&lt;br /&gt;
software affected the process of developing operating systems. At first&lt;br /&gt;
glance, adding new features to an operating system seems straightforward.&lt;br /&gt;
The developers create new features in a new release, and then applications&lt;br /&gt;
are written to use those new features. However, with mass market software,&lt;br /&gt;
it doesn't work that way. Microsoft could indeed release a new version of&lt;br /&gt;
OS/2 with new features (and, in fact, we certainly will do so), but&lt;br /&gt;
initially few new applications will use said new features. This is&lt;br /&gt;
because of the initial limited market penetration of the new release. For&lt;br /&gt;
example, let's assume that at a certain time after the availability of a&lt;br /&gt;
new release, 10 percent of OS/2 users have upgraded. An ISV (Independent&lt;br /&gt;
Software Vendor) is planning its next new product--one it hopes will be a&lt;br /&gt;
bestseller. The ISV must decide whether to use the new feature and&lt;br /&gt;
automatically lock itself out of 90 percent of the potential market or to&lt;br /&gt;
use the common subset of features contained in the earlier OS/2 release and&lt;br /&gt;
be able to run on all machines, including the 10 percent running the OS/2&lt;br /&gt;
upgrade. In general, ISVs won't use a nonvital feature until the great&lt;br /&gt;
majority of existing systems support that feature.&lt;br /&gt;
     The key to introducing new features in an operating system isn't that&lt;br /&gt;
they be available and useful; it's that the release which contains those&lt;br /&gt;
new features sees widespread use as quickly as possible. If this doesn't&lt;br /&gt;
happen, then the new feature pretty much dies stillborn. This is why each&lt;br /&gt;
MS-DOS release that contained major new functionality coincided with a&lt;br /&gt;
release that was required to use new hardware. MS-DOS version 2.0 was&lt;br /&gt;
required for the IBM XT product line; MS-DOS version 3.0 was required for&lt;br /&gt;
the IBM AT product line. And OS/2 is no exception: It wasn't required for a&lt;br /&gt;
new &amp;quot;box,&amp;quot; but it was required to bring out the protect mode machine lying&lt;br /&gt;
fallow inside 80286-based machines. If a new release of a system doesn't&lt;br /&gt;
provide a new feature that makes people want it or need it badly, then&lt;br /&gt;
market penetration will be slow. People will pay the cost and endure the&lt;br /&gt;
hassle of upgrading only if their applications require it, and those&lt;br /&gt;
applications dare require it only if most people have already upgraded.&lt;br /&gt;
     Because of this, the initial release of OS/2 is &amp;quot;magical&amp;quot; in the eyes&lt;br /&gt;
of its developers. It provides a window of opportunity in which to&lt;br /&gt;
introduce new features into the PC operating system standard, a window that&lt;br /&gt;
won't be open quite as wide again for a long time. And this postponed major&lt;br /&gt;
file system enhancements: The file system can be enhanced in a later&lt;br /&gt;
release and benefit existing applications without any change on their part,&lt;br /&gt;
whereas many other OS/2 features needed to be in the first release or they&lt;br /&gt;
might never be available.&lt;br /&gt;
&lt;br /&gt;
===15.1  The OS/2 File System===&lt;br /&gt;
&lt;br /&gt;
Although OS/2 version 1.0 contains little in the way of file system&lt;br /&gt;
improvements, it does contain two that are significant. The first is&lt;br /&gt;
asynchronous I/O. Asynchronous I/O consists of two functions, DosReadAsync&lt;br /&gt;
and DosWriteAsync. These functions are identical to DosRead and DosWrite&lt;br /&gt;
except that they return to the caller immediately, usually before the I/O&lt;br /&gt;
operation has completed. Each takes the handle of a semaphore that is&lt;br /&gt;
cleared when the I/O operation completes. The threads of the calling&lt;br /&gt;
process can use this semaphore to poll for operation complete, or they can&lt;br /&gt;
wait for the operation to complete. The DosMuxSemWait call is particularly&lt;br /&gt;
useful in this regard because it allows a process to wait for several&lt;br /&gt;
semaphore events, which can be asynchronous I/O events, IPC events, and&lt;br /&gt;
timer events, intermingled as the programmer wishes.&lt;br /&gt;
     The second file system feature is extended partitioning; it supports&lt;br /&gt;
dividing large physical disks into multiple sections, several of which may&lt;br /&gt;
contain FAT file systems. In effect, it causes OS/2 to treat a large hard&lt;br /&gt;
disk as two or more smaller ones, each of which meets the file system's&lt;br /&gt;
size limits. It's widely believed that MS-DOS is limited to disks less than&lt;br /&gt;
32 MB in size. This isn't strictly true. The limitation is that a disk can&lt;br /&gt;
have no more than 65,535 sectors; the standard sector size is 512 bytes,&lt;br /&gt;
which gives the 32 MB value. Furthermore, each disk is limited to 32,768&lt;br /&gt;
clusters. A sector is the unit of disk storage; disks can read and write&lt;br /&gt;
only integral sectors. A sector's size is established when the disk is&lt;br /&gt;
formatted. A cluster is the unit of disk space allocation for files and&lt;br /&gt;
directories. It may be as small as one sector, or it may be four sectors,&lt;br /&gt;
eight sectors, or some other size. Because the MS-DOS file system supports&lt;br /&gt;
a maximum of 65 KB sectors but only 32 KB clusters, a 32 MB disk must be&lt;br /&gt;
allocated in two-sector (or bigger) clusters. It's possible to write a&lt;br /&gt;
device driver that uses a sector size that is a multiple of 512 bytes,&lt;br /&gt;
which gets around the 65 KB sector restriction and allows the use of a disk&lt;br /&gt;
greater than 32 MB. This trick works for MS-DOS and for OS/2, but it's not&lt;br /&gt;
optimal because it doesn't do anything to increase the maximum number of&lt;br /&gt;
allocation clusters from the existing 32 KB value,&lt;br /&gt;
1. The FAT file system can deal with a maximum of 32 KB allocation&lt;br /&gt;
units, or clusters. No matter what the size of the disk, all files&lt;br /&gt;
must consume disk space in increments of no smaller than 1/32Kth of&lt;br /&gt;
the total disk size. This means that a 60 MB disk, using 1024 byte&lt;br /&gt;
sectors, allocates space in 2048-byte increments.&lt;br /&gt;
1 which means that&lt;br /&gt;
because many disk files are small a lot of space is wasted due to internal&lt;br /&gt;
fragmentation.&lt;br /&gt;
     The OS/2 version 1.0 extended partitioning feature provides an interim&lt;br /&gt;
solution that is not quite as convenient as large sectors but that reduces&lt;br /&gt;
the wastage from internal fragmentation: It allows more than one disk&lt;br /&gt;
partition to contain a FAT file system. Multipartitioned disks are possible&lt;br /&gt;
under MS-DOS, but only one partition can be an MS-DOS (that is, FAT) file&lt;br /&gt;
system. This restriction has been relaxed in OS/2 so that, for example, a&lt;br /&gt;
60 MB disk can be partitioned into two separate logical disks (for example,&lt;br /&gt;
C and D), each 30 MB.&lt;br /&gt;
&lt;br /&gt;
===15.2  Media Volume Management===&lt;br /&gt;
&lt;br /&gt;
The multitasking capability of OS/2 necessitated major file system&lt;br /&gt;
enhancements in the area of volume management. A disk volume is the name&lt;br /&gt;
given to the file system and files on a particular disk medium. A disk&lt;br /&gt;
drive that contains a fixed medium always contains the same volume, but a&lt;br /&gt;
disk drive from which the media (such as floppy disks) can be removed will&lt;br /&gt;
contain whatever disk--whatever volume--the user has in it at the time.&lt;br /&gt;
That volumes can change becomes a problem in a multitasking environment.&lt;br /&gt;
For example, suppose a user is using a word processor to edit a file on a&lt;br /&gt;
floppy disk in drive A. The editor has opened the file and is keeping it&lt;br /&gt;
open for the duration of the edit. Without closing the file or terminating&lt;br /&gt;
the editor, the user can switch to a screen group in which a spreadsheet&lt;br /&gt;
program is running. The user might then need to insert a different disk&lt;br /&gt;
into drive A--one that contains data needed by the spreadsheet. If the user&lt;br /&gt;
then switches back to the word processor without remembering to change the&lt;br /&gt;
floppy disk, disaster will strike. Pressing the Page Down key will cause&lt;br /&gt;
the editor to try to read another sector from its already open disk file.&lt;br /&gt;
The operating system knows--because of FAT information stored in RAM&lt;br /&gt;
buffers_ that the next sector in the text file is sector N, and it will&lt;br /&gt;
issue a read to sector N on the wrong medium--the spreadsheet floppy disk--&lt;br /&gt;
and return that to the word processor program as the next sector of the&lt;br /&gt;
text file. And, at that, the user is getting off lightly; he or she might&lt;br /&gt;
just as easily have given the word processor a command that caused it to&lt;br /&gt;
write a sector to the disk, which would do double damage. A file on the&lt;br /&gt;
spreadsheet floppy would be destroyed by the &amp;quot;random&amp;quot; write, and the text&lt;br /&gt;
file would be corrupted as well because it's missing a sector write that it&lt;br /&gt;
should have received.&lt;br /&gt;
     We can't solve this problem by admonishing the user to be careful;&lt;br /&gt;
many programs read from and write to disk without direct user intervention.&lt;br /&gt;
For example, the word processor might save work in progress to disk every&lt;br /&gt;
two minutes. If this time interval elapses while the user is still working&lt;br /&gt;
with the spreadsheet program on the spreadsheet floppy disk, our&lt;br /&gt;
hypothetical &amp;quot;flawless&amp;quot; user is still S.O.L.&lt;br /&gt;
2. Severely out of luck.&lt;br /&gt;
2&lt;br /&gt;
     OS/2 resolves these problems by recognizing that when an application&lt;br /&gt;
does I/O to an open file the I/O is not really aimed at drive A; it's aimed&lt;br /&gt;
at a particular floppy disk volume--the one containing the open file. Each&lt;br /&gt;
disk volume, removable or not, has a volume name stored in its root&lt;br /&gt;
directory and a unique 32-bit volume identifier stored in its boot sector.&lt;br /&gt;
Figure 15-1 illustrates the two volume names--one for computer use and one&lt;br /&gt;
for human use. Each file handle is associated with a particular 32-bit&lt;br /&gt;
volume ID. When an I/O request is made for a file handle, OS/2 checks to&lt;br /&gt;
see if the proper volume is in the drive by comparing the 32-bit value of&lt;br /&gt;
the request with that of the medium currently spinning. If they match, the&lt;br /&gt;
operation completes. If the mounted volume is different from the requested&lt;br /&gt;
volume, OS/2 uses the hard error daemon mechanism to prompt the user to&lt;br /&gt;
insert the correct volume in the drive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                            VOLUME LABELS&lt;br /&gt;
Sector                                                          Sector&lt;br /&gt;
  0                                                               N&lt;br /&gt;
  ÚÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³     ³             ³      ³                                    ³&lt;br /&gt;
  ³     ³             ³      ³                                    ³&lt;br /&gt;
  ÀÄÄ�ÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄ�ÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ÀÄ 32-bit volume ID  ÀÄÄÄÄÄÄÄ volume name in&lt;br /&gt;
        in boot sector             home directory&lt;br /&gt;
&lt;br /&gt;
Figure 15-1.  Volume ID and volume name location.&lt;br /&gt;
&lt;br /&gt;
     Three problems must be overcome to make this scheme practical. First,&lt;br /&gt;
checking the volume ID of the medium must be fast. You can't afford to read&lt;br /&gt;
the boot sector each time you do an I/O operation if doing so halves the&lt;br /&gt;
speed of disk I/O. Second, you need assurance that volume IDs are unique.&lt;br /&gt;
Third, you need a plan to deal with a volume that doesn't have a volume ID.&lt;br /&gt;
     Keeping down the cost of volume verification is easy if you know when&lt;br /&gt;
a volume is changed; obviously, the ID is read from the boot sector only&lt;br /&gt;
when a medium has been changed. But how can OS/2 tell that a media change&lt;br /&gt;
has occurred? It can't; that's a device driver issue.&lt;br /&gt;
     For starters, if the device contains a nonremovable medium, rechecking&lt;br /&gt;
its volume ID is never necessary. The device driver understands this, and&lt;br /&gt;
when it is asked the status of the medium, it responds, &amp;quot;Unchanged.&amp;quot; Some&lt;br /&gt;
removable media drives have a flag bit that warns the driver that the door&lt;br /&gt;
has been opened. In this case, when asked, the device driver tells OS/2&lt;br /&gt;
that the medium is &amp;quot;uncertain.&amp;quot; The driver doesn't know for sure that it&lt;br /&gt;
was really changed, but it may have been; so OS/2 rechecks the volume ID.&lt;br /&gt;
Rechecking the volume ID is more difficult when a removable media device&lt;br /&gt;
has no such indicator.&lt;br /&gt;
     In this case, the author of the device driver uses device-specific&lt;br /&gt;
knowledge to decide on a minimum possible time to effect a media change. If&lt;br /&gt;
the device is ready, yet less than the minimum possible time has elapsed&lt;br /&gt;
since the last operation, the driver knows that the same medium must be in&lt;br /&gt;
the drive. If more than the minimum possible time has elapsed, the driver&lt;br /&gt;
returns &amp;quot;medium uncertain,&amp;quot; and OS/2 rechecks the volume label. This time&lt;br /&gt;
interval is typically 2 seconds for floppy disk drives, so effectively an&lt;br /&gt;
extra disk read is done after every idle period; for any given episode of&lt;br /&gt;
disk I/O, however, no extra reads are needed.&lt;br /&gt;
     Ensuring that a volume ID is unique is another problem. Simply&lt;br /&gt;
lecturing the user on the wisdom of unique IDs is inadequate; the user will&lt;br /&gt;
still label three disks &amp;quot;temp&amp;quot; or number them all as &amp;quot;10.&amp;quot; And even the&lt;br /&gt;
hypothetical perfect user might borrow from a neighbor a disk whose name is&lt;br /&gt;
the same as one the user already owns. OS/2 deals with this problem by&lt;br /&gt;
using a 32-bit randomized value for disk volume IDs. When a disk is&lt;br /&gt;
formatted, the user enters a supposedly unique name. This name is&lt;br /&gt;
checksummed, and the result, combined with the number of seconds between&lt;br /&gt;
the present and 1980, is used to seed a random number generator. This&lt;br /&gt;
generator returns a 32-bit volume ID. Although accidentally duplicating a&lt;br /&gt;
volume ID is obviously possible, the four billion possible codes make it&lt;br /&gt;
quite unlikely.&lt;br /&gt;
     The name the user enters is used only to prompt the user to insert the&lt;br /&gt;
volume when necessary, so it need not be truly unique for the volume&lt;br /&gt;
management system to work. If the user names several disks WORK, OS/2 still&lt;br /&gt;
sees them as independent volumes because their volume IDs are different. If&lt;br /&gt;
the user inserts the wrong WORK disk in response to a prompt, OS/2&lt;br /&gt;
recognizes it as the wrong disk and reissues the &amp;quot;Insert disk WORK&amp;quot; prompt.&lt;br /&gt;
After trying each WORK volume in turn, the user will probably decide to&lt;br /&gt;
relabel the disks!&lt;br /&gt;
     The thorniest problem arises from unlabeled disks--disks formatted&lt;br /&gt;
with MS-DOS. Forcing the user to label these disks is unacceptable, as is&lt;br /&gt;
having OS/2 automatically label them with volume IDs: The disk may be read-&lt;br /&gt;
only, perhaps permanently so. Even if the disk is not read-only, the&lt;br /&gt;
problem of low density and high density raises its ugly head. Low-density&lt;br /&gt;
disks can be read in a high-density drive, but writes made to a low-density&lt;br /&gt;
disk from a high-density drive can only be read on high-density drives. If&lt;br /&gt;
a low-density disk is placed in a high-density drive and then labeled by&lt;br /&gt;
OS/2, its boot sector is no longer readable when the disk is placed in a&lt;br /&gt;
low-density drive.&lt;br /&gt;
     For volumes without a proper volume ID, OS/2 attempts to create a&lt;br /&gt;
unique substitute volume ID by checksumming parts of the volume's root&lt;br /&gt;
directory and its FAT table. OS/2 uses the existing volume name if one&lt;br /&gt;
exists; if there is no volume name, OS/2 attempts to describe the disk.&lt;br /&gt;
None of these techniques is foolproof, and they require extra disk&lt;br /&gt;
operations every time the medium is identified. Therefore, software&lt;br /&gt;
distributors and users should make every effort to label disks that OS/2&lt;br /&gt;
systems are to use. OS/2 labels are backward compatible with MS-DOS version&lt;br /&gt;
3.x labels.&lt;br /&gt;
     The OS/2 DISKCOPY command makes a byte-by-byte verbatim copy of a&lt;br /&gt;
floppy disk, except that the duplicate disk has a different volume ID value&lt;br /&gt;
in the boot sector (the volume label name is not changed). OS/2 users can't&lt;br /&gt;
tell this, however, because the DISKCOMP utility lies, and if two disks are&lt;br /&gt;
identical in every byte except for the volume ID, it reports that the disks&lt;br /&gt;
are identical. However, if the user uses DISKCOPY to duplicate the disk&lt;br /&gt;
under OS/2 and then compares the two with DISKCOMP under MS-DOS 3.x, a&lt;br /&gt;
difference is reported.&lt;br /&gt;
     Our discussion so far has centered on file reads and writes to an open&lt;br /&gt;
handle. Reads and writes are volume-oriented operations because they're&lt;br /&gt;
aimed at the volume on which the file resides. DosOpens, on the other hand,&lt;br /&gt;
are drive oriented because they search the default or specified drive for&lt;br /&gt;
the file in question (or create it) regardless of the volume in the drive.&lt;br /&gt;
All handle operations are volume oriented, and all name-based calls are&lt;br /&gt;
drive oriented. Currently, you cannot specify that a given file is to be&lt;br /&gt;
opened on or created on a specific volume. To ensure that a scratch or&lt;br /&gt;
output file is created on a certain volume, arrange to have a file open on&lt;br /&gt;
that volume and issue a write to that file immediately before doing the&lt;br /&gt;
file open. The write operation followed by a DosBufReset will ensure that&lt;br /&gt;
the particular medium is in the drive at that time.&lt;br /&gt;
&lt;br /&gt;
===15.3  I/O Efficiency===&lt;br /&gt;
&lt;br /&gt;
OS/2 provides full blocking and deblocking services for all disk I/O&lt;br /&gt;
requests. A program can read or write any number of bytes, and OS/2 will&lt;br /&gt;
read the proper sectors into internal buffers so that only the specified&lt;br /&gt;
bytes are affected. Naturally, every DosRead or DosWrite call takes time to&lt;br /&gt;
execute, so if your program makes few I/O calls, each for large amounts of&lt;br /&gt;
data, it will execute faster.&lt;br /&gt;
     I/O performance can be further improved by making sector aligned&lt;br /&gt;
calls, that is, by requesting a transfer of an integral multiple of 512&lt;br /&gt;
bytes to or from a file seek position that is itself a multiple of 512.&lt;br /&gt;
OS/2 reads and writes entire disk sectors directly from and to the device&lt;br /&gt;
hardware without an intermediate copy step through system buffers. Because&lt;br /&gt;
the file system keeps logically adjacent sectors physically adjacent on the&lt;br /&gt;
disk, disk seek times and rotational latency are such that one can read or&lt;br /&gt;
write four sectors of data (2048 bytes) in essentially the same time needed&lt;br /&gt;
to read or write one sector (512 bytes).&lt;br /&gt;
     Even if the length or the file position of the request isn't a&lt;br /&gt;
multiple of 512, OS/2 performs the initial fraction of the request via its&lt;br /&gt;
buffers, directly transfers any whole sectors out of the middle of the&lt;br /&gt;
request, and uses the buffers for the fractional remainder. Even if your&lt;br /&gt;
requests aren't sector aligned, making them as large as feasible is&lt;br /&gt;
beneficial.&lt;br /&gt;
     To summarize, I/O is most efficient when requests are large and sector&lt;br /&gt;
aligned. Even misaligned requests can be almost optimally serviced if they&lt;br /&gt;
are large. Programs that cannot naturally make aligned requests and that&lt;br /&gt;
are not I/O intensive should take advantage of the blocking and deblocking&lt;br /&gt;
services that OS/2 provides. Likewise, programs that need to make large,&lt;br /&gt;
unaligned requests should use OS/2's blocking management. Programs that&lt;br /&gt;
need to make frequent, small, nonaligned requests will perform best if they&lt;br /&gt;
read blocks of sectors into internal buffers and deblock the data&lt;br /&gt;
themselves, avoiding the overhead of frequent DosRead or DosWrite calls.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==16  Device Monitors, Data Integrity, and Timer Services==&lt;br /&gt;
&lt;br /&gt;
In discussing the design goals of OS/2, I mentioned continuing to support&lt;br /&gt;
the kinds of functionality found in MS-DOS, even when that functionality&lt;br /&gt;
was obtained by going around the operating system. A good example of such&lt;br /&gt;
functionality is device data manipulation, a technique that usually&lt;br /&gt;
involves hooking interrupt vectors and that is used by many application&lt;br /&gt;
programs. For example, pop-up programs such as SideKick have become very&lt;br /&gt;
popular. These programs get into memory via the terminate and stay resident&lt;br /&gt;
mechanism and then edit the keyboard interrupt vector to point to their&lt;br /&gt;
code. These programs examine each keystroke to see if it is their special&lt;br /&gt;
activate key. If not, they transfer control to the original interrupt&lt;br /&gt;
handler. If the keystroke is their special activate key, they retain&lt;br /&gt;
control of the CPU and display, or &amp;quot;pop up,&amp;quot; a message or a menu on the&lt;br /&gt;
screen. Other programs hook the keyboard vector to provide spell checking&lt;br /&gt;
or keyboard macro expansion. Some programs also hook the BIOS entry vector&lt;br /&gt;
that commands the printer, either to substitute alternate printer driver&lt;br /&gt;
code or to manipulate the data sent to the printer. Programs that turn a&lt;br /&gt;
spreadsheet's output sideways are an example of this.&lt;br /&gt;
     In general, these programs edit, or hook, the interrupt vectors that&lt;br /&gt;
receive device interrupts and communicate with device driver routines in&lt;br /&gt;
the ROM BIOS. The functions provided by such programs and their evident&lt;br /&gt;
popularity among users demonstrate a need for programs to be able to&lt;br /&gt;
monitor and/or modify device data streams. The OS/2 mechanism that does&lt;br /&gt;
this is called a device monitor.&lt;br /&gt;
&lt;br /&gt;
===16.1  Device Monitors===&lt;br /&gt;
&lt;br /&gt;
The design of device monitors had to meet the general requirements and&lt;br /&gt;
religion of OS/2. Specifically, the MS-DOS technique of letting&lt;br /&gt;
applications receive interrupts by editing the interrupt vectors could not&lt;br /&gt;
be allowed because doing so would destroy the system's ability to provide a&lt;br /&gt;
stable environment. Furthermore, unlike MS-DOS, OS/2 doesn't use the ROM&lt;br /&gt;
BIOS as a form of device driver, so hooking the BIOS communication vectors&lt;br /&gt;
would not provide access to the device data stream. In addition, allowing&lt;br /&gt;
an application to arbitrarily interfere with a device driver's operation is&lt;br /&gt;
contrary to OS/2 design principles; the device driver is the architectural&lt;br /&gt;
embodiment of knowledge about the device, and it must be involved in and&lt;br /&gt;
&amp;quot;aware&amp;quot; of any external manipulation of the data stream. The result is an&lt;br /&gt;
OS/2 device monitor mechanism that allows processes, running in their&lt;br /&gt;
normal ring 3 state, to monitor and edit device data streams with the prior&lt;br /&gt;
permission and knowledge of the appropriate device driver.&lt;br /&gt;
     Specifically, a process registers itself as a device monitor by&lt;br /&gt;
calling the appropriate device driver via a DosMonReg call.&lt;br /&gt;
1. Which is a dynlink package that eventually calls the device&lt;br /&gt;
driver via a DosDevIOCtl call.&lt;br /&gt;
1 The process&lt;br /&gt;
also provides two data buffers, one for incoming monitor data and another&lt;br /&gt;
for outgoing monitor data. Processes can easily call OS/2, but OS/2 has no&lt;br /&gt;
way to call processes.&lt;br /&gt;
2. Signals are a partial exception to this, but signals have&lt;br /&gt;
limitations, as discussed earlier.&lt;br /&gt;
2 OS/2 gets around this by inverting the normal&lt;br /&gt;
sense of a call and return sequence. When OS/2 needs to &amp;quot;call&amp;quot; a process,&lt;br /&gt;
it requires that process to call OS/2 beforehand with one of its threads.&lt;br /&gt;
OS/2 holds this thread captive until the callback event takes place. OS/2&lt;br /&gt;
then accomplishes a call to the process by releasing the thread so that it&lt;br /&gt;
returns from the holding system call and resumes execution within the&lt;br /&gt;
process. When the process is ready to &amp;quot;return&amp;quot; to OS/2, it recalls the&lt;br /&gt;
holding entry point (see Figure 16-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      Process calls OS/2     |        OS/2 &amp;quot;calls&amp;quot; Process&lt;br /&gt;
                             | &lt;br /&gt;
Process  call                |  Process  call           FCN    call&lt;br /&gt;
    ÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄ    |      ÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
          �       �          |            �       �             �&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
  Ä Ä Ä Ä Å Ä Ä Ä Å Ä Ä Ä    |    Ä Ä Ä Ä Å Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Å Ä Ä&lt;br /&gt;
 OS/2     ³       ³          |   OS/2     ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          �       �          |            �       �             �&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³  FCN  ³          |            ³       ³             ³&lt;br /&gt;
          ÀÄÄÄÄÄÄÄÙ          |            ÀÄÄ&amp;lt; &amp;lt;ÄÄÙ             ÀÄÄÄ&amp;lt;&lt;br /&gt;
                Return       |                &amp;gt; &amp;gt; Return             &amp;gt;&lt;br /&gt;
&lt;br /&gt;
Figure 16-1.  &amp;quot;Calling&amp;quot; a process from OS/2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 uses this technique for monitors as well. A monitoring process is&lt;br /&gt;
required to call the OS/2 entry point directly after registering itself as&lt;br /&gt;
a device monitor. OS/2 notifies the monitor process of the presence of data&lt;br /&gt;
in the incoming buffer by allowing this thread to return to the process.&lt;br /&gt;
Figure 16-2 illustrates a device with two monitoring processes, X and Y.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿              ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³         Monitor          ³              ³         Monitor          ³&lt;br /&gt;
³        Process X         ³              ³        Process Y         ³&lt;br /&gt;
³                          ³              ³                          ³&lt;br /&gt;
³ DosMonRead   DosMonWrite ³              ³ DosMonRead   DosMonWrite ³&lt;br /&gt;
ÀÄÄÄÄÄ�ÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ              ÀÄÄÄÄÄ�ÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ&lt;br /&gt;
      ÀÄ¿          ÀÄÄÄÄÄÄÄÄÄÄÄ¿      ÚÄÄÄÄÄÄÄÄÄÙ            ³&lt;br /&gt;
        ³                      ³      ³                      ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄ¿&lt;br /&gt;
³       ³                      ³      ³                      ³       ³&lt;br /&gt;
³   ÚÄÄÄÁÄÄÄÄ¿                Ú�ÄÄÄÄÄÄÁ¿                ÚÄÄÄÄ�ÄÄÄ¿   ³&lt;br /&gt;
³   ³ Buffer ³                ³ Buffer ³                ³ Buffer ³   ³&lt;br /&gt;
³   ³   1    ³                ³   2    ³                ³   3    ³   ³&lt;br /&gt;
³   ÀÄÄÄ�ÄÄÄÄÙ                ÀÄÄÄÄÄÄÄÄÙ                ÀÄÄÄÄÂÄÄÄÙ   ³&lt;br /&gt;
³  ÚÄÄÄÄÙ                        OS/2                        ÀÄÄÄÄ¿  ³&lt;br /&gt;
ÀÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÙ&lt;br /&gt;
   ³ Data in                                             Data out ³&lt;br /&gt;
   ³                                                              ³&lt;br /&gt;
ÚÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄ¿&lt;br /&gt;
³  ³                        Device driver                         �  ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 16-2.  Device monitors.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     But we need to discuss a few additional details. First, because OS/2&lt;br /&gt;
strives to make processes see a consistent environment regardless of the&lt;br /&gt;
presence of other processes, each device can have as many monitors as the&lt;br /&gt;
device driver allows. OS/2 connects multiple device monitors into a chain&lt;br /&gt;
so that the device data stream is passed through the first monitor in the&lt;br /&gt;
chain, then through the second monitor, and so on. When a process registers&lt;br /&gt;
itself as a monitor, it specifies whether it wants to be first in the chain&lt;br /&gt;
or last in the chain; some applications are sensitive to this. The first&lt;br /&gt;
monitor to register itself as first is truly first; the next monitor to ask&lt;br /&gt;
for first actually becomes second, and so forth. The same algorithm applies&lt;br /&gt;
to monitors that want to be last: The first to so request becomes the last,&lt;br /&gt;
the second to request last becomes next to last, and so forth.&lt;br /&gt;
     The actual format of the monitor data stream is device specific; the&lt;br /&gt;
device driver decrees the format. Some device drivers have special rules&lt;br /&gt;
and requirements. For example, the keyboard device driver allows monitoring&lt;br /&gt;
processes to insert the &amp;quot;screen switch&amp;quot; key sequence into the data stream,&lt;br /&gt;
whereupon it is recognized as if the user had typed it at the physical&lt;br /&gt;
keyboard. But the device driver will not pass such sequences that really&lt;br /&gt;
were typed through the monitor chain; they are directly obeyed instead.&lt;br /&gt;
     This approach prevents an amok keyboard monitor from effectively&lt;br /&gt;
crashing the system by intercepting and consuming all attempts by the user&lt;br /&gt;
to switch screen groups. The screen device driver does not allow device&lt;br /&gt;
monitors, not because the performance impact would be too big (as it, in&lt;br /&gt;
fact, would be) but because the VIO and presentation manager dynlink&lt;br /&gt;
packages totally circumvent the screen device driver so that it never sees&lt;br /&gt;
any screen data being written.&lt;br /&gt;
     The DosMonRead call holds the device's thread until incoming data is&lt;br /&gt;
available. The DosMonWrite call returns the CPU to the process as soon as&lt;br /&gt;
it is able. The same thread that calls DosMonRead need not be the one to&lt;br /&gt;
call DosMonWrite (see below).&lt;br /&gt;
     Because monitors are an important component of OS/2, an application&lt;br /&gt;
must be very careful to use them properly; therefore, some caveats are in&lt;br /&gt;
order. First, monitors are inserted into the device data chain, with&lt;br /&gt;
obvious effects on the data throughput rate of the device. Each time the&lt;br /&gt;
user presses a key, for example, a packet must pass through every monitor&lt;br /&gt;
in the keyboard chain before the application can read the key and obey or&lt;br /&gt;
echo it. Clearly, any sluggishness on the part of a monitor or the presence&lt;br /&gt;
of too many monitors in a chain will adversely affect system response. The&lt;br /&gt;
thread involved in reading, processing, and writing monitor data should be&lt;br /&gt;
set at a high priority. We recommend the lowest of the force run priority&lt;br /&gt;
categories. Furthermore, the monitor component of a monitoring application&lt;br /&gt;
must contain no critical sections or other events that could slow or&lt;br /&gt;
suspend its operation. In addition, if a monitor data stream will be&lt;br /&gt;
extensively processed, a normal-priority thread must be used to handle that&lt;br /&gt;
processing so that the high-priority thread can continue to transfer&lt;br /&gt;
monitor data in and out without impediment. For example, an auxiliary&lt;br /&gt;
thread and buffer must be used if a keyboard monitor is to write all&lt;br /&gt;
keystrokes to a disk buffer.&lt;br /&gt;
     Finally, if a monitor process terminates abnormally although OS/2&lt;br /&gt;
properly unlinks it from the monitor chain, the data in the process's&lt;br /&gt;
monitor buffers is lost. Clearly, losing an unspecified amount of data&lt;br /&gt;
without warning from the keyboard data stream or perhaps from printer&lt;br /&gt;
output will upset the user no little amount. Monitoring processes must be&lt;br /&gt;
written carefully so that they minimize this risk.&lt;br /&gt;
     The device monitor feature threatens OS/2's fundamental architectural&lt;br /&gt;
principles more than any other. Thus, its presence in the system testifies&lt;br /&gt;
to its importance. Specifically, device monitors violate the design&lt;br /&gt;
principle of minimizing interference between processes, a.k.a.&lt;br /&gt;
encapsulation. Clearly, a process that is monitoring a device's data stream&lt;br /&gt;
can affect the output of or input to a great many processes other than&lt;br /&gt;
itself. This is sometimes called a feature, not a bug. For example, the&lt;br /&gt;
printer spooler uses monitors to intercept output aimed at the printer,&lt;br /&gt;
storing it on disk, and to feed data from those disk files to the actual&lt;br /&gt;
printer device. Clearly, spooling printer output interferes with another&lt;br /&gt;
process, but the interference is valuable. Designers of monitoring&lt;br /&gt;
applications must ensure that their applications damage neither the&lt;br /&gt;
system's performance nor its stability.&lt;br /&gt;
&lt;br /&gt;
===16.2  Data Integrity===&lt;br /&gt;
&lt;br /&gt;
I've discussed data integrity in a multitasking environment several times.&lt;br /&gt;
This section does not review that material in detail but brings together&lt;br /&gt;
all the elements and introduces a few related system facilities.&lt;br /&gt;
     The first problem in a multitasking system is that multiple processes,&lt;br /&gt;
or multiple threads within a process, may try to simultaneously manipulate&lt;br /&gt;
the same resource--a file, a device, a data structure in memory, or even a&lt;br /&gt;
single byte of memory. When the manipulation of a resource must be&lt;br /&gt;
serialized to work correctly, that manipulation is called a critical&lt;br /&gt;
section. This term refers to the act of manipulating the resource, but not&lt;br /&gt;
particularly to the code that does so. Clearly, if any of four subroutines&lt;br /&gt;
can manipulate a particular resource, entering any of the four is entering&lt;br /&gt;
the critical section.&lt;br /&gt;
     The problem is more pervasive than a programmer unfamiliar with the&lt;br /&gt;
issue might assume. For example, even the simple act of testing a word to&lt;br /&gt;
see if it holds the value 4 and incrementing it if it doesn't is a critical&lt;br /&gt;
section. If only one thread in one process can access this word, then the&lt;br /&gt;
critical section is serialized. But if more than one thread can access the&lt;br /&gt;
word, then more than one thread could be in the critical section at the&lt;br /&gt;
same time, with disastrous results. Specifically, consider the assembly&lt;br /&gt;
language sequence shown in Listing 16-1. It looks simple enough: Test to&lt;br /&gt;
see if COUNT holds 4; if it doesn't, increment it; if it does, jump to the&lt;br /&gt;
label COMPLETE. Listing 16-2 shows what might go wrong in a multithreaded&lt;br /&gt;
environment: Thread A checks the value to see if it's 4, but it's 3. Right&lt;br /&gt;
after the compare instruction, a context switch takes place, and thread B&lt;br /&gt;
is executed. Thread B also performs the compare, sees the value as 3, and&lt;br /&gt;
increments it. Later, thread A resumes execution, after the compare&lt;br /&gt;
instruction, at a location where it believes the COUNT value to be 3; so it&lt;br /&gt;
also increments the value of COUNT. The value is now 5 and will continue to&lt;br /&gt;
be incremented way past the value of 4 that was supposed to be its upper&lt;br /&gt;
limit. The label COMPLETE may never be reached.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
COUNT        DW      0               ; Event counter&lt;br /&gt;
&lt;br /&gt;
        .&lt;br /&gt;
        .&lt;br /&gt;
        CMP     COUNT,4        ; is this the 4th?&lt;br /&gt;
        JE      COMPLETE       ; yes, we're done&lt;br /&gt;
        INC     COUNT          ; count event&lt;br /&gt;
        .&lt;br /&gt;
        .&lt;br /&gt;
&lt;br /&gt;
Listing 16-1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        Thread A                                     Thread B&lt;br /&gt;
&lt;br /&gt;
             .&lt;br /&gt;
             .&lt;br /&gt;
             CMP     COUNT,4  [count is now 3]&lt;br /&gt;
             -------------------context switch---&amp;gt;&lt;br /&gt;
                                    CMP     COUNT,4 [count is 3]&lt;br /&gt;
                                    JE      COMPLETE&lt;br /&gt;
                                    INC     COUNT   [count is 4]&lt;br /&gt;
                                              .&lt;br /&gt;
                                              .&lt;br /&gt;
             &amp;lt;-----------context switch--------&lt;br /&gt;
             JE      COMPLETE [jmp not taken]&lt;br /&gt;
             INC     COUNT    [count is now 5]&lt;br /&gt;
&lt;br /&gt;
Listing 16-2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     I apologize for again lecturing on this topic, but such problems are&lt;br /&gt;
very nonobvious, rarely turn up in testing, are nearly impossible to find&lt;br /&gt;
in the field, and the very possibility of their existence is new with OS/2.&lt;br /&gt;
Thus, &amp;quot;too much is not enough,&amp;quot; caveat-wise. Now that we've reviewed the&lt;br /&gt;
problems, let's look at the solutions.&lt;br /&gt;
     A programmer inexperienced with a multitasking environment might&lt;br /&gt;
protest that this scenario is unlikely, and indeed it is. Maybe the chances&lt;br /&gt;
are only 1 in 1 million that it would happen. But because a microprocessor&lt;br /&gt;
executes 1 million instructions a second, it might not be all that long&lt;br /&gt;
before the 1-in-1-million unlucky chance comes true. Furthermore, an&lt;br /&gt;
incorrect program normally has multiple unprotected critical sections, many&lt;br /&gt;
of which are larger than the 2-instruction window in our simple example.&lt;br /&gt;
     The program must identify and protect all critical sections; a program&lt;br /&gt;
that fails to do so will randomly fail. You can't take solace in there&lt;br /&gt;
being only one CPU and assuming that OS/2 probably won't context switch in&lt;br /&gt;
the critical section. OS/2 can context switch at any time, and because&lt;br /&gt;
context switching can be triggered by unpredictable external events, such&lt;br /&gt;
as serial port I/O and rotational latency on a disk, no amount of testing&lt;br /&gt;
can prove that an unprotected critical section is safe. In reality, a test&lt;br /&gt;
environment is often relatively simple; context switching tends to occur at&lt;br /&gt;
consistent intervals, which means that such problems tend not to turn up&lt;br /&gt;
during program test. Instead, they turn up in the real world, and give your&lt;br /&gt;
program a reputation for instability.&lt;br /&gt;
     Naturally, testing has its place, but the only sure way to deal with&lt;br /&gt;
critical sections is to examine your code carefully while assuming that all&lt;br /&gt;
threads in the system are executing simultaneously.&lt;br /&gt;
3. This is more than a Gedankenexperiment. Multiple&lt;br /&gt;
processor machines will be built, and when they are, OS/2 will execute&lt;br /&gt;
multiple threads, even within one process, truly simultaneously.&lt;br /&gt;
3 Furthermore, when&lt;br /&gt;
examining a code sequence, always assume that the CPU will reschedule in&lt;br /&gt;
the worst way. If there is any possible window, reality will find it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.1  Semaphores&lt;br /&gt;
The traditional solution for protecting critical sections is the semaphore.&lt;br /&gt;
The two OS/2 semaphores--RAM and system--each have advantages, and the&lt;br /&gt;
operation of each is guaranteed to be completely immune to critical section&lt;br /&gt;
problems. In the jargon, their operation is guaranteed atomic. Whenever a&lt;br /&gt;
thread is going to manipulate a critical resource, it first claims the&lt;br /&gt;
semaphore that protects the resource. Only after it controls the semaphore&lt;br /&gt;
does it look at the resource because the resource's values may have changed&lt;br /&gt;
between the time the semaphore was requested and the time it was granted.&lt;br /&gt;
After the thread completes its manipulation of the resource, it releases&lt;br /&gt;
the semaphore.&lt;br /&gt;
     The semaphore mechanism protects well against all cooperating&lt;br /&gt;
4. Obviously, if some thread refuses to claim the semaphore,&lt;br /&gt;
nothing can be done.&lt;br /&gt;
4&lt;br /&gt;
threads, whether they belong to the same process or to different processes.&lt;br /&gt;
Another OS/2 mechanism, called DosEnterCritSec, can be used to protect a&lt;br /&gt;
critical section that is accessed only by threads belonging to a single&lt;br /&gt;
process. When a thread issues the DosEnterCritSec call, OS/2 suspends&lt;br /&gt;
execution of all other threads in that process until a subsequent&lt;br /&gt;
DosEnterCritSec call is issued. Naturally, only threads executing in&lt;br /&gt;
application mode are suspended; threads executing inside the OS/2 kernel&lt;br /&gt;
are not suspended until they attempt to return to application mode.&lt;br /&gt;
5. I leave as an exercise to the reader to explain why the&lt;br /&gt;
DosEnterCritSec call is not safe unless all other threads&lt;br /&gt;
in the process make use of it for that critical section as well.&lt;br /&gt;
5 The&lt;br /&gt;
use of DosEnterCritSec is dangerous because the process's other threads may&lt;br /&gt;
be suspended while they are holding a critical section. If the thread that&lt;br /&gt;
issued the DosEnterCritSec then also tries to enter that critical section,&lt;br /&gt;
the process will deadlock. If a dynlink package is involved, it may have&lt;br /&gt;
created extra threads unbeknownst to the client process so that the client&lt;br /&gt;
may not even be aware that such a critical section exists and might be in&lt;br /&gt;
use. For this reason, DosEnterCritSec is safe only when used to protect&lt;br /&gt;
short sections of code that can't block or deadlock and that don't call any&lt;br /&gt;
dynlink modules.&lt;br /&gt;
     Still another OS/2 critical section facility is file sharing and&lt;br /&gt;
record locking, which can be used to protect critical sections when they&lt;br /&gt;
consist of files or parts of files. For example, a database program&lt;br /&gt;
certainly considers its master database file a critical section, and it&lt;br /&gt;
doesn't want anyone messing with it while the database application has it&lt;br /&gt;
open. It can open the file with the file-sharing mode set to &amp;quot;allow no&lt;br /&gt;
(other) readers, allow no writers.&amp;quot; As long as the database application&lt;br /&gt;
keeps the file open, OS/2 prevents any other process from opening (or&lt;br /&gt;
deleting!) that file.&lt;br /&gt;
     The record-locking mechanism can be used to provide a smaller&lt;br /&gt;
granularity of protection. A process can lock a range of bytes within a&lt;br /&gt;
file, and while that lock is in effect, OS/2 prevents any other process&lt;br /&gt;
from reading or writing those bytes. These two specialized forms of&lt;br /&gt;
critical section protection are unique in that they protect a process&lt;br /&gt;
against all other processes, even &amp;quot;uncooperating&amp;quot; ones that don't protect&lt;br /&gt;
their own access to the critical section. Unfortunately, the file-sharing&lt;br /&gt;
and record-locking mechanisms don't contain any provision for blocking&lt;br /&gt;
until the conflict is released. Applications that want to wait for the&lt;br /&gt;
conflict to clear must use a polling loop. Use DosSleep to block for at&lt;br /&gt;
least a half second between each poll.&lt;br /&gt;
     Unfortunately, although semaphores protect critical sections well,&lt;br /&gt;
sometimes they bring problems of their own. Specifically, what happens if&lt;br /&gt;
an asynchronous event, such as program termination or a signal, pulls the&lt;br /&gt;
CPU away from inside a critical section and the CPU never returns to&lt;br /&gt;
release the semaphore? The answers range from &amp;quot;moot&amp;quot; to &amp;quot;disaster,&amp;quot;&lt;br /&gt;
depending on the circumstances. The possibilities are so manifold that&lt;br /&gt;
I'll group some of them.&lt;br /&gt;
     What can you do if the CPU is pulled away inside a critical&lt;br /&gt;
section?&lt;br /&gt;
&lt;br /&gt;
     þ  Ignore it. This is fine if the critical section is wholly accessed&lt;br /&gt;
        by a single process and that process doesn't use signals to modify&lt;br /&gt;
        the normal path of execution and if neither the process nor its&lt;br /&gt;
        dynlink routines attempt to enter the critical section during&lt;br /&gt;
        DosExitList processing.&lt;br /&gt;
&lt;br /&gt;
     þ  Clear the semaphore. This is an option if you know that the&lt;br /&gt;
        resource protected by the semaphore has no state, such as a&lt;br /&gt;
        semaphore that protects the right to be writing to the screen. The&lt;br /&gt;
        trick is to ensure that the interrupted thread set the semaphore&lt;br /&gt;
        and that you don't accidentally clear the semaphore when you don't&lt;br /&gt;
        set it. For example, if the semaphore is wholly used within a&lt;br /&gt;
        single process but that process's DosExitList handlers may use it,&lt;br /&gt;
        they can force the semaphore clear when they are entered.&lt;br /&gt;
&lt;br /&gt;
     þ  Detect the situation and repair the critical section. This&lt;br /&gt;
        detection can be made for RAM semaphores only during process&lt;br /&gt;
        termination and only if the semaphore is solely used by that&lt;br /&gt;
        process. In such a case, you know that a thread in the process set&lt;br /&gt;
        the semaphore, and you know that the thread is no longer executing&lt;br /&gt;
        the critical section because all threads are terminated. You can&lt;br /&gt;
        test the semaphore by using a nonblocking DosSemSet; if it's set,&lt;br /&gt;
        &amp;quot;recover&amp;quot; the resource.&lt;br /&gt;
&lt;br /&gt;
        System semaphores are generally better suited for this. When the&lt;br /&gt;
        owning thread of a system semaphore dies, the semaphore is given a&lt;br /&gt;
        special mark. The next attempt to set the semaphore returns with a&lt;br /&gt;
        code that tells the new owner that the previous owner died within&lt;br /&gt;
        the critical section. The new owner has the option of cleaning up&lt;br /&gt;
        the resource.&lt;br /&gt;
&lt;br /&gt;
     Another possibility is to try to prevent the CPU from being yanked out&lt;br /&gt;
of a critical section. Signals can be momentarily delayed with the&lt;br /&gt;
DosHoldSignal mechanism. Process termination that results from an external&lt;br /&gt;
kill can be postponed by setting up a signal handler for the KILL signal&lt;br /&gt;
and then using DosHoldSignal. This last technique doesn't protect you&lt;br /&gt;
against termination due to GP fault and the like however.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.2  DosBufReset&lt;br /&gt;
One remaining data integrity issue--disk data synchronization--is not&lt;br /&gt;
related to critical sections. Often, when a DosWrite call is made, OS/2&lt;br /&gt;
holds the data in a buffer rather than writing it immediately to disk.&lt;br /&gt;
Naturally, any subsequent calls made to read this data are satisfied&lt;br /&gt;
correctly, so an application cannot see that the data has not yet been&lt;br /&gt;
written unless the reading application uses direct physical access to the &lt;br /&gt;
volume (that is, raw media reads). This case explains why CHKDSK may&lt;br /&gt;
erroneously report errors that run on a volume that has open files.&lt;br /&gt;
     OS/2 eventually writes the data to the disk, so this buffering is of&lt;br /&gt;
concern only when the system crashes with unwritten buffered data.&lt;br /&gt;
Naturally, such crashes are expected to be rare, but some applications may&lt;br /&gt;
find the possibility so threatening that they want to take protective&lt;br /&gt;
steps. The two OS/2 functions for this purpose are flushing and&lt;br /&gt;
writethroughs. The flush operation--DosBufReset--writes all dirty buffers--&lt;br /&gt;
those with changed but unwritten data in them--to the disk. When the call&lt;br /&gt;
returns, the data is on the disk. Use this call sparingly; although its&lt;br /&gt;
specification promises only that it will flush buffers associated with the&lt;br /&gt;
specified file handle(s), for most file systems it writes all dirty buffers&lt;br /&gt;
in the system to disk. Moreover, if file handles are open to a server&lt;br /&gt;
machine on the network, most or all of that server's buffers get flushed,&lt;br /&gt;
even those that were used by other client machines on the network. Because&lt;br /&gt;
of these costs, applications should use this operation judiciously.&lt;br /&gt;
     Note that it's not true that a flush operation simply causes a write&lt;br /&gt;
to be done sooner rather than later. A flush operation may also cause extra&lt;br /&gt;
disk writes. For example, consider an application that is writing data 10&lt;br /&gt;
bytes at a time. In this case, OS/2 buffers the data until it has a full&lt;br /&gt;
sector's worth. A series of buffer flush operations arriving at this time&lt;br /&gt;
would cause the assembly buffer to be written to the disk many extra and&lt;br /&gt;
unnecessary times.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.3  Writethroughs&lt;br /&gt;
Buffer flushes are expensive, and unless they are used frequently, they&lt;br /&gt;
don't guarantee a particular write ordering. Some applications, such as&lt;br /&gt;
database managers, may want to guarantee that data be written to the disk&lt;br /&gt;
in exactly the same order in which it was given to OS/2 via DosWrite. For&lt;br /&gt;
example, an application may want to guarantee that the data is in place in&lt;br /&gt;
a database before the allocation chain is written and that the chain be&lt;br /&gt;
written before the database directory is updated. Such an ordering may make&lt;br /&gt;
it easy for the package to recover the database in case of a crash.&lt;br /&gt;
     The OS/2 mechanism for doing this is called writethrough--a status bit&lt;br /&gt;
that can be set for individual file handles. If a writethrough is in effect&lt;br /&gt;
for a handle to which the write is issued, OS/2 guarantees that the data&lt;br /&gt;
will be written to the disk before the DosWrite operation returns.&lt;br /&gt;
Obviously, applications using writethrough should write their data in large&lt;br /&gt;
chunks; writing many small chunks of data to a file marked for writethrough&lt;br /&gt;
is very inefficient.&lt;br /&gt;
&lt;br /&gt;
     Three caveats are associated with writethroughs:&lt;br /&gt;
&lt;br /&gt;
     þ  If writethrough is set on a file after it is open, all subsequent&lt;br /&gt;
        writes are written through, but data from previous writes may still&lt;br /&gt;
        be in dirty buffers.&lt;br /&gt;
&lt;br /&gt;
     þ  If a writethrough file is being shared by multiple processes or is&lt;br /&gt;
        open on multiple handles, all instances of that file should be&lt;br /&gt;
        marked writethrough. Data written to a handle not marked&lt;br /&gt;
        writethrough may go into the buffers.&lt;br /&gt;
&lt;br /&gt;
     þ  The operation of data writethroughs has some nonintuitive surprises&lt;br /&gt;
        when used with the current FAT file system. Specifically, although&lt;br /&gt;
        this feature works as advertised to place the file's data sectors&lt;br /&gt;
        on the disk, it does not update the directory entry that specifies&lt;br /&gt;
        the size of the file. Thus, if you extend a file by 10 sectors and&lt;br /&gt;
        the system crashes before you close the file, the data in those 10&lt;br /&gt;
        sectors is lost. If you had writethrough set, then those 10 sectors&lt;br /&gt;
        of data were indeed written to the disk; but because the directory&lt;br /&gt;
        entry wasn't updated, CHKDSK will return those sectors to the free&lt;br /&gt;
        list.&lt;br /&gt;
&lt;br /&gt;
        The writethrough operation protects the file's data but not the&lt;br /&gt;
        directory or allocation information. This is not a concern as long&lt;br /&gt;
        as you write over a portion of the file that has been already&lt;br /&gt;
        extended, but any writes that extend the file are not protected.&lt;br /&gt;
        The good news is that the data will be on the disk, as guaranteed,&lt;br /&gt;
        but the bad news is that the directory entry won't be updated; if&lt;br /&gt;
        the system crashes, file extensions cannot be recovered. The&lt;br /&gt;
        recommended solution is to use DosNewSize to extend the file as&lt;br /&gt;
        needed, followed by DosBufReset to update the directory information&lt;br /&gt;
        on the disk, and then to writethrough the data as needed.&lt;br /&gt;
        Overextending the file size is better than doing too many&lt;br /&gt;
        NewSize/BufReset combinations; if you overextend, you can always&lt;br /&gt;
        shrink the file before closing it with a final DosNewSize.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.3  Timer Services&lt;br /&gt;
&lt;br /&gt;
Frequently, applications want to keep track of the passage of real time. A&lt;br /&gt;
game program may want events to occur asynchronously with the user's input;&lt;br /&gt;
a telecommunications program may want to track how long a response takes&lt;br /&gt;
and perhaps declare a link timed-out after some interval. Other programs&lt;br /&gt;
may need to pace the display of a demonstration or assume a default action&lt;br /&gt;
if the user doesn't respond in a reasonable amount of time. OS/2 provides&lt;br /&gt;
several facilities to track the passage of real time; applications should&lt;br /&gt;
use these facilities and shun polling and timing loops because the timing&lt;br /&gt;
of such loops depends on the system's workload and the CPU's speed and&lt;br /&gt;
because they totally lock out from execution any thread of a lower&lt;br /&gt;
priority.&lt;br /&gt;
     Time intervals in OS/2 are discussed in terms of milliseconds to&lt;br /&gt;
isolate the concept of a time interval from the physical mechanism&lt;br /&gt;
(periodic clock interrupts) that measures time intervals. Although you can&lt;br /&gt;
specify a time interval down to the millisecond, the system does not&lt;br /&gt;
guarantee any such accuracy.&lt;br /&gt;
     On most hardware, OS/2 version 1.0 uses a periodic system clock&lt;br /&gt;
interrupt of 32 Hz (32 times a second). This means that OS/2 measures time&lt;br /&gt;
intervals with a quantum size of 31.25 milliseconds. As a result, any&lt;br /&gt;
timeout value is subject to quantization error of this order. For example,&lt;br /&gt;
if a process asks to sleep for 25 milliseconds, OS/2 knows that the request&lt;br /&gt;
was made at some time after the most recent clock tick, but it cannot tell&lt;br /&gt;
how long after, other than that less than 31.25 milliseconds had elapsed&lt;br /&gt;
between the previous clock tick and the sleep request. After the sleep&lt;br /&gt;
request is made, another clock tick occurs. Once again, OS/2 can't tell how&lt;br /&gt;
much time has elapsed since the sleep request and the new clock tick, other&lt;br /&gt;
than that it was less than 31.25 milliseconds. Lacking this knowledge, OS/2&lt;br /&gt;
uses a simple algorithm: At each clock tick, OS/2 decrements each timeout&lt;br /&gt;
value in the system by the clock tick interval (generally 31.25&lt;br /&gt;
milliseconds). Thus, our 25-millisecond sleep request may come back in 1&lt;br /&gt;
millisecond or less or in 31.25 milliseconds. A request to block for 33&lt;br /&gt;
milliseconds could come back in 32 milliseconds or in 62.5 milliseconds.&lt;br /&gt;
     Clearly, the OS/2 timer functions are intended for human-scale timing,&lt;br /&gt;
in which the 1/32-second quantization error is not noticeable, and not for&lt;br /&gt;
high-precision timing of fast events. Regardless of the resolution of the&lt;br /&gt;
timer, the system's preemptive scheduler prevents the implementation of&lt;br /&gt;
high-accuracy short-interval timing. Even if a timer system call were to&lt;br /&gt;
time out after a precise interval, the calling thread might not resume&lt;br /&gt;
execution immediately because a higher-priority thread might be executing&lt;br /&gt;
elsewhere.&lt;br /&gt;
     One form of OS/2 timer services is built into some system calls. For&lt;br /&gt;
example, all semaphore blocking calls support an argument that allows the&lt;br /&gt;
caller to specify a timeout value. When the specified time has elapsed, the&lt;br /&gt;
call returns with a &amp;quot;call timed out&amp;quot; error code. Some threads use this&lt;br /&gt;
facility to guard against being indefinitely locked out; if the semaphore&lt;br /&gt;
call times out, the thread can give up, display an error message, or try&lt;br /&gt;
another tactic. Other threads may use the facility expecting to be timed&lt;br /&gt;
out: They use the timeout facility to perform periodic tasks and use the&lt;br /&gt;
semaphore just as an emergency flag. Another thread in the system can&lt;br /&gt;
provide an emergency wakeup for the timer thread simply by clearing the&lt;br /&gt;
semaphore.&lt;br /&gt;
     Blocking on a semaphore merely to delay for a specific interval is&lt;br /&gt;
unnecessary; the DosSleep call allows a thread to block unconditionally for&lt;br /&gt;
an arbitrary length of time, subject, of course, to the timer's&lt;br /&gt;
quantization error.&lt;br /&gt;
6. And subject to the fact that if thread 1 is doing the DosSleeping&lt;br /&gt;
the sleep will be interrupted if a signal is taken.&lt;br /&gt;
6 DosSleep measures time intervals in a synchronous&lt;br /&gt;
fashion: The thread is held inside the operating system until the time&lt;br /&gt;
interval has elapsed. OS/2 provides an asynchronous timer service that&lt;br /&gt;
allows timing to take place in parallel with a thread's normal execution.&lt;br /&gt;
Specifically, the DosTimerAsync call is made with a timeout interval, such&lt;br /&gt;
as DosSleep, and also with the handle of a system semaphore.&lt;br /&gt;
7. Unlike most semaphore applications, the timer functions work&lt;br /&gt;
only with system semaphores. RAM semaphores may not be used&lt;br /&gt;
because of the difficulty in posting a RAM semaphore at interrupt&lt;br /&gt;
time; the RAM that contains the semaphore may be swapped out.&lt;br /&gt;
7 The&lt;br /&gt;
DosTimerAsync call returns immediately; later, when the time interval has&lt;br /&gt;
elapsed, the system semaphore is cleared. The process can poll the&lt;br /&gt;
semaphore to see if the time is up, and/or it can block on the semaphore to&lt;br /&gt;
wait for the time to elapse. Of course, if a process contains multiple&lt;br /&gt;
threads, some can poll and others can block.&lt;br /&gt;
     The DosTimerStart call is identical to the DosTimerAsync call except&lt;br /&gt;
that the semaphore is repeatedly cleared at the specified interval until a&lt;br /&gt;
corresponding DosTimerStop call is made. DosTimerStart clears the&lt;br /&gt;
semaphore; the process must set it again after it's been cleared.&lt;br /&gt;
     None of the above-mentioned facilities is completely accurate for&lt;br /&gt;
tracking the time of day or the amount of elapsed time. As we mentioned, if&lt;br /&gt;
a higher-priority thread is consuming enough CPU time, unpredictable delays&lt;br /&gt;
occur. Even DosTimerStart is susceptible to losing ticks because if the CPU&lt;br /&gt;
is unavailable for a long enough period the process won't be able to reset&lt;br /&gt;
the semaphore soon enough to prevent missing its next clearing.&lt;br /&gt;
Applications that want a precise measurement of elapsed time should use the&lt;br /&gt;
time values stored in the global infoseg. We also recommend that&lt;br /&gt;
applications with a critical need to manage timeouts, even if they are&lt;br /&gt;
executing in the lower-priority background, dedicate a thread to managing&lt;br /&gt;
the time-critical work and elevate that thread to a higher priority. This&lt;br /&gt;
will ensure that time-critical events aren't missed because a high-priority&lt;br /&gt;
foreground thread is going through a period of intensive CPU usage. Of&lt;br /&gt;
course, such an application must be designed so that the high-priority&lt;br /&gt;
timer event thread does not itself consume significant CPU time; it should&lt;br /&gt;
simply log the timer events and rely on its fellow normal-priority threads&lt;br /&gt;
to handle the major work involved.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==17  Device Drivers and Hard Errors==&lt;br /&gt;
&lt;br /&gt;
The multitasking nature of OS/2 makes OS/2 device drivers considerably more&lt;br /&gt;
complex than MS-DOS device drivers. Furthermore, whenever you have devices,&lt;br /&gt;
you must deal with device failures--the infamous hard errors. The handling&lt;br /&gt;
of hard errors in a multitasking environment is likewise considerably more&lt;br /&gt;
complex than it was under MS-DOS.&lt;br /&gt;
&lt;br /&gt;
===17.1  Device Drivers===&lt;br /&gt;
&lt;br /&gt;
This section gives an overview of device drivers, paying special attention&lt;br /&gt;
to their key architectural elements. Writing a device driver is a complex&lt;br /&gt;
task that must be undertaken with considerable care; a great many caveats&lt;br /&gt;
and &amp;quot;gotchas&amp;quot; lie in wait for the unsuspecting programmer. Many of these&lt;br /&gt;
&amp;quot;gotchas&amp;quot; are of that most favorite breed: ones that never show up in&lt;br /&gt;
testing, only in the field. This section is by no means an exhaustive&lt;br /&gt;
discussion of device drivers, nor is it a how-to guide. Study the OS/2&lt;br /&gt;
device driver reference documentation carefully before setting out to write&lt;br /&gt;
your own.&lt;br /&gt;
     In Chapter 2 I briefly discussed device independence and the role&lt;br /&gt;
that device drivers play in bringing it about. I said that a device driver&lt;br /&gt;
is a package of code that transforms I/O requests made in standard, device-&lt;br /&gt;
independent fashion into the operations necessary to make a specific piece&lt;br /&gt;
of hardware fulfill that request. A device driver takes data and status&lt;br /&gt;
information from the hardware, in the hardware-specific format, and&lt;br /&gt;
massages that information into the form that the operating system expects&lt;br /&gt;
to receive.&lt;br /&gt;
     The device driver architecture has two key elements. First, each&lt;br /&gt;
hardware device has its own device driver to hide the specific details of&lt;br /&gt;
the device from the operating system. Second, device drivers are not hard-&lt;br /&gt;
wired into the operating system when it is manufactured; they are&lt;br /&gt;
dynamically installed at boot time. This second point is the interesting&lt;br /&gt;
one. If all device drivers were hard-wired into OS/2, the technique of&lt;br /&gt;
encapsulating device-dependent code into specific packages would be good&lt;br /&gt;
engineering practice but of little interest to the user. OS/2 would run&lt;br /&gt;
only on a system configured with a certain magic set of peripheral devices.&lt;br /&gt;
But because device drivers are dynamically installable at boot time, OS/2&lt;br /&gt;
can work with a variety of devices, even ones that didn't exist when OS/2&lt;br /&gt;
was written, as long as a proper device driver for that device is installed&lt;br /&gt;
at boot time. Note that device drivers can be installed only at boot time;&lt;br /&gt;
they cannot be installed after the system has completed booting up. This is&lt;br /&gt;
because in a future secure environment the ability to dynamically install a&lt;br /&gt;
device driver would give any application the ability to violate system&lt;br /&gt;
security.&lt;br /&gt;
     Saying that device drivers merely translate between the operating&lt;br /&gt;
system and the device is a bit of oversimplification; in reality, they are&lt;br /&gt;
responsible for encapsulating, or owning, nearly all device-specific&lt;br /&gt;
knowledge about the device. Device drivers service the interrupts that&lt;br /&gt;
their devices generate, and they work at task time (that is, at&lt;br /&gt;
noninterrupt time). If a device monitor is necessary for a device, the&lt;br /&gt;
device driver writer decides that and provides the necessary support. If an&lt;br /&gt;
application needs direct access to a device's I/O ports or to its special&lt;br /&gt;
mapped memory,&lt;br /&gt;
1. For example, the display memory of a CGA or an EGA card.&lt;br /&gt;
1 the driver offers those services to processes. The device&lt;br /&gt;
driver also knows whether multiple processes should simultaneously use the&lt;br /&gt;
device and either allows or disallows this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.1  Device Drivers and OS/2 Communication&lt;br /&gt;
Because device drivers need to call and be called by OS/2 efficiently and&lt;br /&gt;
because device drivers must handle hardware interrupts efficiently, they&lt;br /&gt;
must run at ring 0. This means that device drivers must be trusted and must&lt;br /&gt;
be trustworthy. A flaky device driver--or worse, a malicious one--can do&lt;br /&gt;
unlimited and nearly untraceable damage to any application or data file in&lt;br /&gt;
the system.&lt;br /&gt;
     OS/2 can easily call a device driver. Because OS/2 loaded the device&lt;br /&gt;
driver into memory, it knows the address of its entry point and can call it&lt;br /&gt;
directly. For the device driver to call OS/2 is trickier because the driver&lt;br /&gt;
doesn't know the memory locations that OS/2 occupies nor does it have any&lt;br /&gt;
control over the memory descriptor tables (LDT and GDT). When the device&lt;br /&gt;
driver is initialized, OS/2 supplies the device driver with the address of&lt;br /&gt;
the OS/2 DevHlp entry point. Device drivers call this address to access a&lt;br /&gt;
variety of OS/2 services, called DevHlp services. The OS/2 DevHlp address&lt;br /&gt;
references a GDT selector so that the DevHlp address is valid at all times--&lt;br /&gt;
in protected mode, in real mode,&lt;br /&gt;
2. A GDT selector cannot literally be valid in real mode because the&lt;br /&gt;
GDT is not in use. OS/2 uses a technique called tiling so that the&lt;br /&gt;
selector, when used as a segment address in real mode, addresses&lt;br /&gt;
the same physical memory as does the protect mode segment.&lt;br /&gt;
2 at interrupt time, and during device&lt;br /&gt;
driver initialization. Some DevHlp functions are only valid in certain&lt;br /&gt;
modes, but the DevHlp facility is always available.&lt;br /&gt;
     Why don't device drivers simply use dynamic links to access OS/2&lt;br /&gt;
services, the way that applications do? The OS/2 kernel dynlink interface&lt;br /&gt;
is designed for processes running in user mode, at ring 3, to call the ring&lt;br /&gt;
0 kernel. In other words, it's designed for outsiders to call in, but&lt;br /&gt;
device drivers are already inside. They run at ring 0, in kernel mode, and&lt;br /&gt;
at interrupt time. One, of course, could kludge things so that device&lt;br /&gt;
drivers make dynlink calls, and then special code at those OS/2 entry&lt;br /&gt;
points would recognize a device driver request and do all the special&lt;br /&gt;
handling. But every system call from a normal application would be slowed&lt;br /&gt;
by this extra code, and every service call from a device driver would&lt;br /&gt;
likewise be slowed. As a result, device drivers have their own private,&lt;br /&gt;
high-efficiency &amp;quot;backdoor&amp;quot; entry into OS/2. Figure 17-1 illustrates&lt;br /&gt;
the call linkages between OS/2 and a device driver. OS/2 calls only one&lt;br /&gt;
entry point in the device driver, providing a function code that the device&lt;br /&gt;
driver uses to address a dispatch table. OS/2 learns the address of&lt;br /&gt;
thisentry point when it loads the device driver. The device driver in turn&lt;br /&gt;
calls only one OS/2 address, the DevHlp entry point. It also supplies a&lt;br /&gt;
function code that is used to address a dispatch table. The device driver&lt;br /&gt;
is told this address when it receives its initialize call from OS/2. Not&lt;br /&gt;
shown is the device driver's interrupt entry point.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
           OS/2                                   Device driver&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Far call (FCN)   ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³                        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄ¿    ³  Function              ³&lt;br /&gt;
³               DevHlp   ³             ³    ³   Table                ³&lt;br /&gt;
³         Function Table ³             ³    ³  ÚÄÄÄÄÄÄ¿  ÚÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄ¿  ÚÄÄÄÄÄÄ¿  ³             ÀÄÄÄÄÅÄ�³      ÃÄÄÙ           ³&lt;br /&gt;
³           ÀÄÄ´      ³�ÄÅÄÄÄÄÄÄÄÄÄÄ¿       ³  ³      ÃÄÄÄÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄÄÄÄ´      ³  ³ DevHlp   ³       ³  ³      ÃÄÄ¿           ³&lt;br /&gt;
³           ÚÄÄ´      ³  ³ address  ³       ³  ÀÄÄÄÄÄÄÙ  ÀÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄÙ  ÀÄÄÄÄÄÄÙ  ³          ³       ³                        ³&lt;br /&gt;
³                        ³          ÀÄÄÄÄÄÄÄ´                        ³&lt;br /&gt;
³                        ³       Far call   ³                        ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ     (DevHlp FCN) ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 17-1.  Device driver call linkages.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.2  Device Driver Programming Model&lt;br /&gt;
The programming model for device drivers under MS-DOS is simple. Device&lt;br /&gt;
drivers are called to perform a function, and they return when that&lt;br /&gt;
function is complete or they encounter an unrecoverable error. If the&lt;br /&gt;
device is interrupt driven, the CPU hangs in a loop inside the device&lt;br /&gt;
driver while waiting for the driver's interrupt handler to be entered; when&lt;br /&gt;
the operation is complete, the interrupt handler sets a private flag to&lt;br /&gt;
break the task-time CPU out of its wait loop.&lt;br /&gt;
     The OS/2 device driver model is considerably more complicated because&lt;br /&gt;
OS/2 is a multitasking system. Even if the thread that calls the device&lt;br /&gt;
driver with a request has nothing better to do than wait for the operation&lt;br /&gt;
to complete, other threads in the system could make good use of the time.&lt;br /&gt;
Another effect of the OS/2 multitasking architecture is that two or more&lt;br /&gt;
threads can simultaneously call a device driver. To explore this last issue&lt;br /&gt;
fully, we'll digress for a moment and discuss the OS/2 internal execution&lt;br /&gt;
model.&lt;br /&gt;
     By design, OS/2 acts more like a subroutine library than like a&lt;br /&gt;
process. The only dispatchable entities in the system are threads, and all&lt;br /&gt;
threads belong to processes. When a process's thread calls OS/2, that&lt;br /&gt;
thread executes OS/2's code.&lt;br /&gt;
3. The few exceptions to this don't affect the issues discussed here.&lt;br /&gt;
3 It's like walking up to the counter at a&lt;br /&gt;
fast-food restaurant and placing your order. You then slip on an apron, run&lt;br /&gt;
around behind the counter, and prepare your own order. When the food is&lt;br /&gt;
ready, you take off the apron, run back around to the front of the counter,&lt;br /&gt;
and pick up the food. The counter represents the boundary between ring 0&lt;br /&gt;
(kernel mode) and ring 3 (application mode), and the apron represents the&lt;br /&gt;
privileged state necessary to work behind the counter.&lt;br /&gt;
     Naturally, OS/2 is reentrant; at any one time many threads are&lt;br /&gt;
executing inside OS/2, but each is doing work for only one process--the&lt;br /&gt;
process to whom that thread belongs. Behind the counter are several folks&lt;br /&gt;
wearing aprons, but each is working only on his or her own order. This&lt;br /&gt;
approach simplifies the internals of OS/2: Each instance of a section of&lt;br /&gt;
code is doing only one thing for one client. If a section of code must wait&lt;br /&gt;
for something, it simply blocks (analogous to a semaphore wait) as long as&lt;br /&gt;
it has to and resumes when it can. Threads within the kernel that are&lt;br /&gt;
competing for a single resource do so by internal semaphores, and they are&lt;br /&gt;
given access to these semaphores on a priority basis, just as they are when&lt;br /&gt;
executing in application mode.&lt;br /&gt;
     OS/2 makes little distinction between a thread running inside the&lt;br /&gt;
kernel and one running outside, in the application's code itself: The&lt;br /&gt;
process's LDT remains valid, and the thread, while inside the kernel, can&lt;br /&gt;
access any memory location that was accessible to the process in&lt;br /&gt;
application mode, in addition to being able to access restricted ring 0&lt;br /&gt;
memory. The only distinction the scheduler makes between threads inside and&lt;br /&gt;
those outside the kernel is that the scheduler never preempts a thread&lt;br /&gt;
running inside the kernel. This greatly relaxes the rigor with which kernel&lt;br /&gt;
code needs to protect its critical sections: When the CPU is executing&lt;br /&gt;
kernel code, the scheduler performs a context switch only when the CPU&lt;br /&gt;
voluntarily blocks itself. As long as kernel code doesn't block itself,&lt;br /&gt;
wait on a semaphore, or call a subroutine that waits on a semaphore, it&lt;br /&gt;
needn't worry about any other thread entering its critical section.&lt;br /&gt;
4. Although hardware interrupts still occur; any critical section&lt;br /&gt;
modified at interrupt time is still vulnerable.&lt;br /&gt;
4&lt;br /&gt;
     When OS/2 calls a device driver at task time, it does so with the&lt;br /&gt;
thread that was executing OS/2--the thread that belongs to the client&lt;br /&gt;
process and that made the original service call. Thus, the task-time part&lt;br /&gt;
of a device driver is running, at ring 0, in the client's context. The&lt;br /&gt;
client's LDT is active, all the client's addresses are active, and the&lt;br /&gt;
device driver is immune from being preempted by other task-time threads&lt;br /&gt;
(but not by interrupt service) until it blocks via a DevHlp&lt;br /&gt;
function or returns to OS/2.&lt;br /&gt;
     OS/2 device drivers are divided into two general categories: those for&lt;br /&gt;
character mode devices and those for block mode devices. This terminology&lt;br /&gt;
is traditional, but don't take it too literally because character mode&lt;br /&gt;
operations can be done to block mode devices. The actual distinction is&lt;br /&gt;
that character mode device drivers do I/O synchronously; that is, they do&lt;br /&gt;
operations in first in, first out order. Block mode device drivers can be&lt;br /&gt;
asynchronous; they can perform I/O requests in an order different from the&lt;br /&gt;
one in which they received them. A traditional serial character device,&lt;br /&gt;
such as a printer, must not change the order of its requests; doing so&lt;br /&gt;
scrambles the output. A block device, such as a disk, can reverse the order&lt;br /&gt;
of two sector reads without problems.&lt;br /&gt;
     Figure 17-2 shows an algorithm for character mode device drivers.&lt;br /&gt;
5. See the device driver reference manual for more details.&lt;br /&gt;
5&lt;br /&gt;
OS/2 calls the device driver with a request, as shown at the top of the&lt;br /&gt;
figure. If the device driver is busy with another request, the new&lt;br /&gt;
requesting thread should block on a RAM semaphore until the device is&lt;br /&gt;
available. When the device is free, the task-time thread does the requested&lt;br /&gt;
operation. Sometimes the work can be done at task time (such as an IOCTL&lt;br /&gt;
call asking about the number of characters in an input buffer), but more&lt;br /&gt;
frequently the task-time thread initiates the operation, and the work is&lt;br /&gt;
completed at interrupt time. If the task-time thread needs to wait for&lt;br /&gt;
interrupt service, it should block on a RAM semaphore&lt;br /&gt;
6. Located in the device driver data area.&lt;br /&gt;
6 that the interrupt-&lt;br /&gt;
time code will clear. When the last associated device interrupt takes place&lt;br /&gt;
and the operation is complete, the interrupt code releases the RAM&lt;br /&gt;
semaphore. The task-time thread awakens and returns to OS/2 with the status&lt;br /&gt;
bits properly set in the request block.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       OS/2 code            Device driver           Device interrupt&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Issue request to&lt;br /&gt;
device driver. ÄÄÄÄÄÄ� Block until device is&lt;br /&gt;
                       available.&lt;br /&gt;
&lt;br /&gt;
                       Peform request. Block&lt;br /&gt;
                       until interrupts&lt;br /&gt;
                       complete if necessary.  ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ Device interrupt (if any)&lt;br /&gt;
                                               ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ Perform next step in I/O&lt;br /&gt;
                                               ³ operation.&lt;br /&gt;
                                               ³&lt;br /&gt;
                                               ³ When done, use DevHlp&lt;br /&gt;
                                               ³ ProcRun to unblock task&lt;br /&gt;
                                               ³ time thread.&lt;br /&gt;
                                               ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ End of interrupt&lt;br /&gt;
                       Complete request and    ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                       return to OS/2&lt;br /&gt;
Request now complete.&lt;br /&gt;
Continue.&lt;br /&gt;
&lt;br /&gt;
Figure 17-2.  Simplified character mode device driver model.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Figure 17-3 shows an algorithm for block devices. The general outline&lt;br /&gt;
is the same as that for character devices but more complicated because of&lt;br /&gt;
the asynchronous nature of random-access devices. Because requests can be&lt;br /&gt;
processed in any order, most block device drivers maintain an internal work&lt;br /&gt;
queue to which they add each new request. They usually use a special DevHlp&lt;br /&gt;
function to sort the work queue in sector number order so that disk head&lt;br /&gt;
motion is minimized.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       OS/2 code             Device driver           Device interrupt&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Issue request to&lt;br /&gt;
device driver. ÄÄÄÄÄÄÄ� Add request to device&lt;br /&gt;
                        request list. Fire up&lt;br /&gt;
                        device if not active.&lt;br /&gt;
&lt;br /&gt;
                        Return to OS/2 with&lt;br /&gt;
                        DONE clear. ÄÄ¿&lt;br /&gt;
           ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
           �                                    ³ Device interrupt&lt;br /&gt;
OS/2 thread(s) block on                         ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
incomplete request when                         ³ Perform next step in&lt;br /&gt;
they can proceed no                             ³ I/O operation.&lt;br /&gt;
further without the                             ³&lt;br /&gt;
data.                                           ³ If request is done&lt;br /&gt;
                                                ³    Pull request from&lt;br /&gt;
                                                ³    list&lt;br /&gt;
Any threads blocked ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
on this request are                             ³    Use DevHlp DevDone&lt;br /&gt;
awakened.                                       ³    to tell OS/2 that&lt;br /&gt;
                                                ³    the I/O is done.&lt;br /&gt;
                                                ³&lt;br /&gt;
                                                ³    If further work on&lt;br /&gt;
                                                ³    queue, start work&lt;br /&gt;
                                                ³    on next item.&lt;br /&gt;
                                                ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                                ³ End of interrupt&lt;br /&gt;
Request now complete.                           ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Continue.&lt;br /&gt;
&lt;br /&gt;
Figure 17-3.  Block mode device driver model.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The easiest way to understand this figure is to think of a block mode&lt;br /&gt;
device driver as being made up of N threads: one interrupt-time thread does&lt;br /&gt;
the actual work, and all the others are task-time threads that queue the&lt;br /&gt;
work. As each request comes into the driver via a task-time thread, that&lt;br /&gt;
thread simply puts the request on the queue and returns to OS/2. Later, the&lt;br /&gt;
device driver's interrupt service routine calls the DevHlp DevDone function&lt;br /&gt;
to tell OS/2 that the operation is complete. Returning to OS/2 with the&lt;br /&gt;
operation incomplete is permissible because the request block status bits&lt;br /&gt;
show that the operation is incomplete.&lt;br /&gt;
     Sometimes, OS/2 needs to wait for the operation (such as a read from a&lt;br /&gt;
directory); so when the device driver returns with the operation&lt;br /&gt;
incomplete, OS/2 simply waits for it to finish. In other circumstances,&lt;br /&gt;
such as flushing the cache buffers, OS/2 may not wait around for the&lt;br /&gt;
operation to complete. It may go on about its business or even issue a new&lt;br /&gt;
request to the driver, using, of course, a new request block because the&lt;br /&gt;
old one is still in use. This design gives the system a great deal of&lt;br /&gt;
parallelism and thereby improves throughput.&lt;br /&gt;
     I said that a block mode device driver consists of several task-time&lt;br /&gt;
threads and one interrupt-time thread. The term interrupt-time thread is a&lt;br /&gt;
bit misleading, however, because it's not a true thread managed by the&lt;br /&gt;
scheduler but a pseudo thread created by the hardware interrupt mechanism.&lt;br /&gt;
For example, a disk device driver has four requests queued up, and the READ&lt;br /&gt;
operation for the first request is in progress. When it completes, the&lt;br /&gt;
driver's interrupt service routine is entered by the hardware interrupt&lt;br /&gt;
generated by the disk controller. That interrupt-time thread, executing the&lt;br /&gt;
driver's interrupt service routine, checks the status, verifies that all is&lt;br /&gt;
OK, and calls various DevHlp routines to post the request as complete and&lt;br /&gt;
to remove it from the queue. It then notes that requests remain on the&lt;br /&gt;
queue and starts work on the next one, which involves a seek operation. The&lt;br /&gt;
driver's interrupt-time code issues the seek command to the hardware and&lt;br /&gt;
then returns from the interrupt. When the disk stops seeking, another&lt;br /&gt;
interrupt is generated; the interrupt-time code notes the successful seek,&lt;br /&gt;
issues the read or write operation to the controller, and exits.&lt;br /&gt;
     As you can see, the repeated activation of the device driver's&lt;br /&gt;
interrupt service routine is much like a thread, but with two major&lt;br /&gt;
differences. First, every time an interrupt service routine is entered, it&lt;br /&gt;
has a fresh stack. A task-time thread has register contents and a stack&lt;br /&gt;
that are preserved by the system; neither is preserved for an interrupt&lt;br /&gt;
service routine between interrupts. A task-time thread keeps track of what&lt;br /&gt;
it was doing by its CS:IP address, its register contents, and its stack&lt;br /&gt;
contents. An interrupt service routine must keep track of its work by means&lt;br /&gt;
of static values stored in the device driver's data segment. Typically,&lt;br /&gt;
interrupt service routines implement a state machine and maintain the&lt;br /&gt;
current state in the driver's data segment. Second, a true thread remains&lt;br /&gt;
in existence until explicitly terminated; an interrupt service thread is an&lt;br /&gt;
illusion of a thread that is maintained by repeated interrupts. If any one&lt;br /&gt;
execution of the interrupt service routine fails to give the hardware a&lt;br /&gt;
command that will generate another interrupt, the interrupt pseudo thread&lt;br /&gt;
will no longer exist after the interrupt service routine returns.&lt;br /&gt;
     The block mode driver algorithm description left out a detail. If the&lt;br /&gt;
disk is idle when a new request comes in, the request is put on the queue,&lt;br /&gt;
but there is no interrupt-time pseudo thread to service the request. Thus,&lt;br /&gt;
both the task-time and interrupt-time parts of a device driver must be able&lt;br /&gt;
to initiate an operation. The recommended approach is to use a software&lt;br /&gt;
state machine to control the hardware and to ensure that the state machine,&lt;br /&gt;
at least the start operation part of it, is callable at both task and&lt;br /&gt;
interrupt time. The algorithm above is then modified so that after the&lt;br /&gt;
task-time part of a block device driver puts its request on the driver's&lt;br /&gt;
internal queue it verifies that the device (or state machine or interrupt&lt;br /&gt;
pseudo thread) is active. If the device has been idle, the task-time thread&lt;br /&gt;
in the device driver initiates the operation by calling the initial state&lt;br /&gt;
of the state machine; it then returns to OS/2. This primes the pump;&lt;br /&gt;
the interrupt pseudo thread now continues to run until the request queue is&lt;br /&gt;
empty.&lt;br /&gt;
     Figure 17-4 shows an overview of the OS/2 device driver architecture.&lt;br /&gt;
Each device driver consists of a task-time part, an interrupt-time part (if&lt;br /&gt;
the device generates interrupts), and the start-operation code that is&lt;br /&gt;
executed in either mode. The driver's data area typically contains state&lt;br /&gt;
information, flags, and semaphores to handle communication between the&lt;br /&gt;
task-time part and the interrupt. Figure 17-4 also shows that the task-time&lt;br /&gt;
part of a device driver can have multiple instances. It can be called by&lt;br /&gt;
several threads at the same time, just as a shared dynlink library routine&lt;br /&gt;
might be. Unlike a dynlink library, a device driver has no instance data&lt;br /&gt;
segment; the device driver's data segment is a global data segment,&lt;br /&gt;
accessible to all execution instances of the device driver's task-time&lt;br /&gt;
component. Just as a dynlink package uses semaphores to protect critical&lt;br /&gt;
data areas in its global data segment, a device driver uses semaphores to&lt;br /&gt;
protect the critical data values in its data segment. Unlike dynlink&lt;br /&gt;
routines, the device driver has an additional special thread--the interrupt&lt;br /&gt;
service thread. A device driver can't protect critical sections that are&lt;br /&gt;
accessed at interrupt time by using semaphores because an interrupt service&lt;br /&gt;
thread cannot block. It must complete the interrupt service and exit--&lt;br /&gt;
quickly, at that. When you write device drivers, you must minimize the&lt;br /&gt;
critical sections that are entered by the interrupt service thread and&lt;br /&gt;
protect them via the CLI/STI instruction sequence.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
         ³              &amp;quot;D&amp;quot;³&lt;br /&gt;
      ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ³                ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
      ³              &amp;quot;C&amp;quot;³  ³                ³   Device driver   ³&lt;br /&gt;
   ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ÃÄÄÙ                ³ interrupt service ³&lt;br /&gt;
   ³              &amp;quot;B&amp;quot;³  ³                   ÀÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ÃÄÄÙ                     ³           ³&lt;br /&gt;
³  Instance &amp;quot;A&amp;quot;   ³  ³                        ³           ³&lt;br /&gt;
³  device driver  ÃÄÄÙ                ÚÄÄÄÄÄÄÄ�ÄÄÄ¿       ³&lt;br /&gt;
³  task-time code ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³ Start I/O ³       ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ                   ³   code    ³       ³&lt;br /&gt;
         ³                            ÀÄÄÄÄÄÂÄÄÄÄÄÙ       ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                         ÚÄÄÄÄÄÄÄÄ�ÄÄÄÄÄÄ¿      ³&lt;br /&gt;
         ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³ Device driver ³�ÄÄÄÄÄÙ&lt;br /&gt;
                                   ³     data      ³&lt;br /&gt;
                                   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 17-4.  Device driver code structure.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.3  Device Management&lt;br /&gt;
Device drivers do more than talk to the device; they also manage it for the&lt;br /&gt;
system. Device drivers are called each time a process opens or closes a&lt;br /&gt;
device; device drivers determine whether a device can be used by more than&lt;br /&gt;
one process simultaneously. Likewise, device drivers receive device monitor&lt;br /&gt;
requests from applications via the IOCTL interface and, when appropriate,&lt;br /&gt;
call OS/2 via the DevHlp interface to perform the bulk of the monitor work.&lt;br /&gt;
Finally, device drivers can grant processes access to the device's I/O&lt;br /&gt;
ports, to the device's mapped memory, and/or to special control areas in&lt;br /&gt;
the device driver's data area itself. Once again, processes ask for these&lt;br /&gt;
features via IOCTL; the device driver grants the requests via a DevHlp&lt;br /&gt;
dialog with OS/2. Some device drivers are degenerate; they don't actually&lt;br /&gt;
transfer data but exist solely to manage these other tasks. The screen&lt;br /&gt;
device driver is an example. Screen data is always written directly to the&lt;br /&gt;
display buffer by VIO, the application, or the presentation manager. The&lt;br /&gt;
screen device driver exists to grant direct access, manage screen groups,&lt;br /&gt;
and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.4  Dual Mode&lt;br /&gt;
The last key architectural feature of device drivers is that they are&lt;br /&gt;
written in dual mode: The driver code, both task time and interrupt time,&lt;br /&gt;
must be able to execute in protected mode and real mode. The process of&lt;br /&gt;
mode switching between protected mode and real mode is quite slow--about&lt;br /&gt;
800 microseconds. If we decreed that all device drivers run only in&lt;br /&gt;
protected mode and that service interrupts run only in protected mode, a&lt;br /&gt;
disk request from a real mode program might require six or more mode&lt;br /&gt;
switches--one for the request, and five for the interrupts--for a penalty&lt;br /&gt;
of almost 5 milliseconds. Consequently, device drivers must run in whatever&lt;br /&gt;
mode the CPU is in when the request comes along or the interrupt arrives.&lt;br /&gt;
     At first glance, this seems easy enough: As long as the device driver&lt;br /&gt;
refrains from computing its own segment selectors, it can execute in either&lt;br /&gt;
mode. The catch is that OS/2 may switch between modes at every call and/or&lt;br /&gt;
interrupt, and the addresses of code and data items are different in each&lt;br /&gt;
mode. A device driver might be called in protected mode with an address in&lt;br /&gt;
the client process's address space. When the &amp;quot;data ready&amp;quot; interrupt&lt;br /&gt;
arrives, however, the CPU may be running in real mode, and that client's&lt;br /&gt;
address is no longer valid--for two reasons. One, the segment selector part&lt;br /&gt;
of a memory address has a different meaning in real mode than it does in&lt;br /&gt;
protected mode; and, two, the client's selector was in the LDT, and the LDT&lt;br /&gt;
is invalid at interrupt time.&lt;br /&gt;
7. See the device driver reference manual for more details.&lt;br /&gt;
7 OS/2 helps device drivers deal with&lt;br /&gt;
addressing in a dual mode environment in three ways:&lt;br /&gt;
&lt;br /&gt;
     1.  Some addresses are the same in both modes and in either protected&lt;br /&gt;
         mode or real mode. The DevHlp entry point, the global infoseg&lt;br /&gt;
         address, the request packet address, and any addresses returned&lt;br /&gt;
         via the DevHlp GetDosVar function are valid at all times and in&lt;br /&gt;
         both modes.&lt;br /&gt;
&lt;br /&gt;
     2.  Although the segment selector value for the device driver's code&lt;br /&gt;
         and data segments is different in each mode, OS/2 loads the proper&lt;br /&gt;
         values into CS and DS before it calls the device driver's task-&lt;br /&gt;
         time or interrupt-time entry points. As long as a device driver is&lt;br /&gt;
         careful not to &amp;quot;remember&amp;quot; and reuse these values, it won't notice&lt;br /&gt;
         that they (possibly) change at every call.&lt;br /&gt;
&lt;br /&gt;
     3.  OS/2 provides a variety of DevHlp functions that allow a device&lt;br /&gt;
         driver to convert a selector:offset pair into a physical address&lt;br /&gt;
         and then later convert this physical address back into a&lt;br /&gt;
         selector:offset pair that is valid at that particular time. This&lt;br /&gt;
         allows device drivers to convert addresses that are outside their&lt;br /&gt;
         own segments into physical addresses and then, upon each task-time&lt;br /&gt;
         or interrupt-time call to the driver, convert that physical&lt;br /&gt;
         address back into one that is usable in the current mode. This&lt;br /&gt;
         avoids the problem of recording a selector:offset pair in protect&lt;br /&gt;
         mode and then trying to use it as a segment:offset pair in real&lt;br /&gt;
         mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2  Hard Errors&lt;br /&gt;
&lt;br /&gt;
Sometimes the system encounters an error that it can neither ignore nor&lt;br /&gt;
correct but which the user can correct. A classic example is the user&lt;br /&gt;
leaving ajar the door to the floppy drive; the system can do nothing to&lt;br /&gt;
access that floppy disk until someone closes the door. Such an error is&lt;br /&gt;
called a hard error. The term originated to describe an error that won't go&lt;br /&gt;
away when the operation is retried, but it also aptly describes the effort&lt;br /&gt;
involved in the design of OS/2 to deal with such errors.&lt;br /&gt;
     The manner in which MS-DOS handles hard errors is straightforward. In&lt;br /&gt;
our drive door example, MS-DOS discovers the problem when it is deep inside&lt;br /&gt;
the bowels of the system, communicating with the disk driver. The driver&lt;br /&gt;
reports the problem, and MS-DOS displays some text on the screen--the&lt;br /&gt;
infamous &amp;quot;Abort, Retry, Ignore?&amp;quot; message. Typically, the user fixes the&lt;br /&gt;
problem and replies; MS-DOS then takes the action specified by the user,&lt;br /&gt;
finishes its work, and returns to the application. Often, applications&lt;br /&gt;
didn't want the system to handle hard errors automatically. Perhaps they&lt;br /&gt;
were concerned about data integrity and wanted to be aware of a disk&lt;br /&gt;
writing problem, or they wanted to prevent the user from specifying&lt;br /&gt;
&amp;quot;Ignore,&amp;quot;&lt;br /&gt;
8. This response is classic. Sophisticated users understand the likely&lt;br /&gt;
consequences of such a reply, but most users would interpret &amp;quot;Ignore&amp;quot;&lt;br /&gt;
as &amp;quot;Make the problem go away&amp;quot;--an apparently ideal solution!&lt;br /&gt;
8 or they didn't want MS-DOS to write over their screen display&lt;br /&gt;
without their knowing. To handle these situations, MS-DOS lets applications&lt;br /&gt;
store the address of a hard error handler in the INT 24 vector; if a&lt;br /&gt;
handler is present, MS-DOS calls it instead of its own handler.&lt;br /&gt;
     The system is in an unusual state while processing an MS-DOS hard&lt;br /&gt;
error. The application originally calls MS-DOS via the INT 21 vector. MS-&lt;br /&gt;
DOS then calls several levels deep within itself, whereupon an internal MS-&lt;br /&gt;
DOS routine calls the hard error handler back in the application. Because&lt;br /&gt;
MS-DOS is not generally reentrant, the application cannot recall MS-DOS via&lt;br /&gt;
INT 21 at this point; doing so would mean that it has called MS-DOS twice&lt;br /&gt;
at the same time. The application probably needs to do screen and keyboard&lt;br /&gt;
I/O when handling the hard error, so MS-DOS was made partially reentrant.&lt;br /&gt;
The original call involves disk I/O, so MS-DOS can be reentered via a&lt;br /&gt;
screen/keyboard I/O call without problem.&lt;br /&gt;
     Several problems prevented us from adopting a similar scheme for OS/2.&lt;br /&gt;
First, unlike the single-tasking MS-DOS, OS/2 cannot suspend operations&lt;br /&gt;
while the operating system calls an application--a call that might not&lt;br /&gt;
return for a long time. Second, major technical and security problems are&lt;br /&gt;
involved with calling from ring 0 (the privileged kernel mode) to ring 3&lt;br /&gt;
(the application mode). Also, in the MS-DOS environment, deciding which&lt;br /&gt;
process was responsible for the operation that triggered the hard error is&lt;br /&gt;
easy: Only one application is running. OS/2 may have a hard time&lt;br /&gt;
determining which process to alert because more than one process may have&lt;br /&gt;
caused a disk FAT sector or a disk directory to be edited. The improved&lt;br /&gt;
buffering techniques employed by OS/2 may cause a hard error to occur at a&lt;br /&gt;
time when no process is doing any I/O. Finally, even if we solve all these&lt;br /&gt;
problems, the application that triggers the hard error may be running in a&lt;br /&gt;
background screen group and be unable to display a message or use the&lt;br /&gt;
keyboard. Even if the application is in the foreground screen group, it&lt;br /&gt;
can't use the screen and keyboard if it's not the process currently&lt;br /&gt;
controlling them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2.1  The Hard Error Daemon&lt;br /&gt;
This last problem yields a clue to the solution. OS/2 supports multiple&lt;br /&gt;
screen groups, and its screen group mechanism manages multiple simultaneous&lt;br /&gt;
use of the screen and the keyboard, keeping the current users--one in each&lt;br /&gt;
screen group--isolated from one another. Clearly, we need to use screen&lt;br /&gt;
groups to allow a hard error dialog to be completed with the user without&lt;br /&gt;
interfering with the current foreground application. Doing so solves the&lt;br /&gt;
problem of writing on another application's screen image and therefore&lt;br /&gt;
removes most of the need for notifying an application that a hard error has&lt;br /&gt;
occurred.&lt;br /&gt;
     Specifically, OS/2 always has running a process called the hard error&lt;br /&gt;
daemon. When a hard error occurs, OS/2 doesn't attempt to figure out which&lt;br /&gt;
process caused it; instead, it notifies the hard error daemon. The hard&lt;br /&gt;
error daemon performs a special form of screen group switch to the reserved&lt;br /&gt;
hard error screen group and then displays its message and reads its input.&lt;br /&gt;
Because the hard error daemon is the only process in this screen group,&lt;br /&gt;
screen and keyboard usage do not conflict. The previous foreground process&lt;br /&gt;
is now temporarily in the background; the screen group mechanism keeps it&lt;br /&gt;
at bay.&lt;br /&gt;
     Meanwhile, the process thread that encountered the hard error in the&lt;br /&gt;
kernel is blocked there, waiting for the hard error daemon to get a&lt;br /&gt;
response from the user. The thread that handles the hard error is never the&lt;br /&gt;
thread that caused the hard error, and the kernel is already fully&lt;br /&gt;
reentrant for different threads; so the hard error daemon thread is free to&lt;br /&gt;
call OS/2 at will. When the user corrects the problem and responds to the&lt;br /&gt;
hard error daemon, the hard error daemon sends the response back to the&lt;br /&gt;
kernel, which allows the thread that encountered the error to take the&lt;br /&gt;
specified action. That thread either retries the operation or produces an&lt;br /&gt;
error code; the hard error daemon returns the system to the original&lt;br /&gt;
screen group. The screen group code then does its usual trick of&lt;br /&gt;
restoring the screen image to its previous state. Figure 17-5 illustrates&lt;br /&gt;
the hard error handling sequence. A process thread encounters a hard error&lt;br /&gt;
while in the OS/2 kernel. The thread blocks at that point while the hard&lt;br /&gt;
error daemon's previously captured thread is released. The hard error&lt;br /&gt;
daemon performs a special modified screen switch at (1), displays its&lt;br /&gt;
message, gets the user's response, restores the application screen group at&lt;br /&gt;
(2), and reenters the OS/2 kernel. The response code is then passed to the&lt;br /&gt;
blocked application thread, which then resumes execution.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
             DosCall&lt;br /&gt;
   Process  ÄÄÄÄ¿                                        ÚÄÄÄÄÄÄÄÄ&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³  OS/2 kernel  ³                                        ³  Return   ³&lt;br /&gt;
³               ³ Hard                                   ³  to       ³&lt;br /&gt;
³               ³ error                                  ³  process  ³&lt;br /&gt;
³               ÀÄÄÄÄÄÄ´                      ÃÄÄÄ&amp;gt;Ä&amp;gt;ÄÄÄÄÙ           ³&lt;br /&gt;
³                                                &amp;lt; &amp;lt;                 ³&lt;br /&gt;
³                      ÃÄ¿                  ÚÄ´                      ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                         �                  �&lt;br /&gt;
                 returns ³                  ³ calls&lt;br /&gt;
                         ³                  ³&lt;br /&gt;
   Hard error daemon     ³ Display message  ³&lt;br /&gt;
                         ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                      (1)    Get response    (2)&lt;br /&gt;
 &lt;br /&gt;
                                    TimeÄÄÄÄÄÄ�&lt;br /&gt;
&lt;br /&gt;
Figure 17-5.  Hard error handling.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Although the most common cause of hard errors is a disk problem for&lt;br /&gt;
example, an open drive door or a medium error--other events that require&lt;br /&gt;
user intervention or user notification use the hard error mechanism. For&lt;br /&gt;
example, the volume management package (see 15.2 Media Volume Management)&lt;br /&gt;
uses the hard error mechanism to display its &amp;quot;Insert volume &amp;lt;name&amp;gt;&amp;quot;&lt;br /&gt;
messages. As I mentioned earlier, MS-DOS applications running in the&lt;br /&gt;
compatibility box can encounter problems, such as locked files, that they&lt;br /&gt;
can't understand. Rather than have these applications fail mysteriously,&lt;br /&gt;
OS/2 uses the hard error daemon mechanism to inform the user of the cause&lt;br /&gt;
of the real mode application's difficulties. Although the application&lt;br /&gt;
running in the compatibility box sees an operating system that acts like&lt;br /&gt;
MS-DOS, the operating system is actually OS/2. Because of this, hard errors&lt;br /&gt;
encountered by a real mode process are handled by an amalgam of the MS-DOS&lt;br /&gt;
INT 24 mechanism and the OS/2 hard error daemon. See Chapter 19, The 3X&lt;br /&gt;
Box.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2.2  Application Hard Error Handling&lt;br /&gt;
In some cases an application doesn't want the system to handle its hard&lt;br /&gt;
errors. For example, an application designed for unattended or remote&lt;br /&gt;
operation, such as a network server, may want to pass notification of hard&lt;br /&gt;
errors to a remote correspondent rather than hanging up forever with a&lt;br /&gt;
message on a screen that might not be read for hours. Another example is a&lt;br /&gt;
database program concerned about the integrity of its master file; it may&lt;br /&gt;
want to know about hard errors so that it can take some special action or&lt;br /&gt;
perhaps use an alternative master file on another device. OS/2 allows a&lt;br /&gt;
process to disable automatic hard error handling on a per file basis. Our&lt;br /&gt;
network example will want to disable hard error pop-ups for anything the&lt;br /&gt;
process does; our database example may want to disable hard error pop-ups&lt;br /&gt;
only for its master file, keeping their convenience for any other files&lt;br /&gt;
that it might access. When a hard error occurs on behalf of a process or a&lt;br /&gt;
handle that has hard error pop-ups disabled, OS/2 assumes that a FAIL&lt;br /&gt;
response was entered to a hypothetical hard error pop-up and returns to the&lt;br /&gt;
application with a special error code. The application must analyze the&lt;br /&gt;
code and take the necessary actions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==18  I/O Privilege Mechanism and Debugging/Ptrace==&lt;br /&gt;
&lt;br /&gt;
The earlier chapters of this book focused on the &amp;quot;captains and kings&amp;quot; of&lt;br /&gt;
the operating system world, the major architectural features. But like any&lt;br /&gt;
real world operating system, OS/2 contains a variety of miscellaneous&lt;br /&gt;
facilities that have to be there to get the work done. Although these&lt;br /&gt;
facilities may not be major elements in some architectural grand scheme,&lt;br /&gt;
they still have to obey the principles of the design religion. Two of them&lt;br /&gt;
are the I/O privilege mechanism and the debugging facility.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
18.1  I/O Privilege Mechanism&lt;br /&gt;
&lt;br /&gt;
Earlier I discussed the need for a mechanism that allows applications high-&lt;br /&gt;
speed direct access to devices. But the mechanism must control access in&lt;br /&gt;
such a way that the system's stability isn't jeopardized and in such a way&lt;br /&gt;
that applications don't fight over device control. OS/2 meets this&lt;br /&gt;
requirement with its I/O privilege mechanism. This facility allows a&lt;br /&gt;
process to ask a device driver for direct access to the device's I/O&lt;br /&gt;
ports and any dedicated or mapped memory locations it has. The I/O&lt;br /&gt;
privilege mechanism can be used directly by an application, which&lt;br /&gt;
necessarily makes it device dependent, or indirectly by a dynlink package.&lt;br /&gt;
The dynlink package can act as a kind of device driver; a new version can&lt;br /&gt;
be shipped with new hardware to maintain application compatibility. This&lt;br /&gt;
pseudo device driver is normally much faster than a true device driver&lt;br /&gt;
because of the customized procedural interface; not entering ring 0 and the&lt;br /&gt;
OS/2 kernel code saves much time.&lt;br /&gt;
     Unfortunately, this isn't a free lunch. Dynlink pseudo device drivers&lt;br /&gt;
can do everything that true device drivers can except handle interrupts.&lt;br /&gt;
Because hardware interrupts must be handled at ring 0, the handler must be&lt;br /&gt;
part of a true device driver. Frequently, a compromise is in order: Both a&lt;br /&gt;
dynlink package and a true device driver are provided. The true device&lt;br /&gt;
driver handles the interrupts, and the dynlink package does the rest of the&lt;br /&gt;
work. The two typically communicate via shared memory and/or private&lt;br /&gt;
IOCTLs. An example of such a compromise is the system KBD dynlink package.&lt;br /&gt;
The system VIO package doesn't need a device driver to handle interrupts&lt;br /&gt;
because the display device doesn't generate any.&lt;br /&gt;
     The two components in the OS/2 I/O access model are access to the&lt;br /&gt;
device's memory and access to its I/O ports. Granting and controlling&lt;br /&gt;
access to a device's mapped memory is easy because the 80286 protect mode&lt;br /&gt;
supports powerful memory management facilities. First, a process asks the&lt;br /&gt;
device driver for access to the device's memory, for example, to the memory&lt;br /&gt;
buffer of a CGA board. Typically, a dynlink package, rather than an&lt;br /&gt;
application, does this via the DosDevIOCtl call. If the device driver&lt;br /&gt;
approves the request, it asks OS/2 via the DevHlp interface to set up an&lt;br /&gt;
LDT memory descriptor to the proper physical memory locations. OS/2 returns&lt;br /&gt;
the resultant selector to the device driver, which returns it to the&lt;br /&gt;
calling process. This technique isn't limited to memory-mapped device&lt;br /&gt;
memory; device drivers can use it to allow their companion dynlink packages&lt;br /&gt;
direct access to a piece of the device driver's data segment. In this way,&lt;br /&gt;
a combination dynlink/device driver device interface can optimize&lt;br /&gt;
communication between the dynlink package and the device driver.&lt;br /&gt;
     Providing I/O port access to a process is more difficult because it is&lt;br /&gt;
supported more modestly by the 80286 processor. The 80286 uses its ring&lt;br /&gt;
protection mechanism to control I/O access; the system can grant code&lt;br /&gt;
running at a certain ring privilege access to all I/O ports, but it can't&lt;br /&gt;
grant access to only some I/O ports. It's too dangerous to grant an&lt;br /&gt;
application access to all I/O ports simply because it uses VIO and VIO&lt;br /&gt;
needs direct port access for the display adapter. This solution would mean&lt;br /&gt;
that OS/2's I/O space is effectively unprotected because almost all&lt;br /&gt;
programs use VIO or the presentation manager directly or indirectly.&lt;br /&gt;
     Instead, OS/2 was designed to allow, upon request from the device&lt;br /&gt;
driver, any code segments marked&lt;br /&gt;
1. This is done via a special command to the linker.&lt;br /&gt;
1 to execute at ring 2 to have I/O access.&lt;br /&gt;
The bad news is that access to all I/O ports must be granted&lt;br /&gt;
indiscriminately, but the good news is that the system is vulnerable to&lt;br /&gt;
program bugs only when those ring 2 segments are being executed. The&lt;br /&gt;
capabilities of ring 2 code, as it's called, are restricted: Ring 2 code&lt;br /&gt;
cannot issue dynlink calls to the system. This is partly a result of ring&lt;br /&gt;
architecture (supporting ring 2 system calls would require significant&lt;br /&gt;
additional overhead) and partly to discourage lazy programmers from&lt;br /&gt;
flagging their entire process as ring 2 to avoid sequestering their I/O&lt;br /&gt;
routines.&lt;br /&gt;
     As I said, in OS/2 version 1.0 the ring mechanism can restrict I/O&lt;br /&gt;
access only to a limited degree. Any malicious program and some buggy&lt;br /&gt;
programs can still damage system stability by manipulating the system's&lt;br /&gt;
peripherals. Furthermore, a real mode application can issue any I/O&lt;br /&gt;
instruction at any time. A future release of OS/2 that runs only on the&lt;br /&gt;
80386 processor will solve these problems. The 80386 hardware is&lt;br /&gt;
specifically designed to allow processes access to some I/O ports but not&lt;br /&gt;
to others through a bit map the system maintains. This map, which of course&lt;br /&gt;
the application cannot directly change, tells the 80386 which port&lt;br /&gt;
addresses may be accessed and which must be refused. This map applies&lt;br /&gt;
equally to protect mode and real mode applications.&lt;br /&gt;
2. Actually, to &amp;quot;virtual real mode&amp;quot; applications. This is&lt;br /&gt;
functionally the same as real mode on earlier processors.&lt;br /&gt;
2 OS/2 will use the&lt;br /&gt;
port addresses supplied by the device driver to allow access only to the&lt;br /&gt;
I/O ports associated with the device(s) to which the process has been&lt;br /&gt;
granted access. This release will not support application code segments&lt;br /&gt;
running at ring 2; any segments so marked will be loaded and run at ring 3.&lt;br /&gt;
The change will be invisible to all applications that use only the&lt;br /&gt;
proper I/O ports. Applications that request access to one device and then&lt;br /&gt;
use their I/O permissions to program another device will fail.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
18.2  Debugging/Ptrace&lt;br /&gt;
&lt;br /&gt;
Because OS/2 goes to a great deal of effort to keep one application from&lt;br /&gt;
interfering with another, special facilities were built to allow debugging&lt;br /&gt;
programs to manipulate and examine a debuggee (the process being debugged).&lt;br /&gt;
Because a debugger is available for OS/2 and writing your own is laborious,&lt;br /&gt;
we expect few programmers to write debuggers. This discussion is included,&lt;br /&gt;
nevertheless, because it further illuminates the OS/2 architectural&lt;br /&gt;
approach.&lt;br /&gt;
     The first concern of a debugger is that it be able to read and write&lt;br /&gt;
the debuggee's code and data segments as well as intercept traps, signals,&lt;br /&gt;
breakpoints, and the like. All these capabilities are strictly in the&lt;br /&gt;
domain of OS/2, so OS/2 must &amp;quot;export&amp;quot; them to the debugger program. A&lt;br /&gt;
second concern is system security: Obviously, the debug interface provides&lt;br /&gt;
a golden opportunity for &amp;quot;cracker&amp;quot; programs to manipulate any other&lt;br /&gt;
program, thereby circumventing passwords, encryption, or any other&lt;br /&gt;
protection scheme. OS/2 prevents this by requiring that the debuggee&lt;br /&gt;
process be flagged as a debug target when it is initially executed; a&lt;br /&gt;
debugger can't latch onto an already-running process. Furthermore, when&lt;br /&gt;
secure versions of OS/2 are available, processes executed under control of&lt;br /&gt;
a debugger will be shorn of any permissions they might have that are in&lt;br /&gt;
excess of those owned by the debugger.&lt;br /&gt;
     Before we examine the debugging interface, we should digress for a&lt;br /&gt;
moment and discuss the OS/2 approach to forcing actions upon threads and&lt;br /&gt;
processes. Earlier I described the process of kernel execution. I mentioned&lt;br /&gt;
that when a process thread makes a kernel request that thread itself enters&lt;br /&gt;
kernel mode and services its own request. This arrangement simplified the&lt;br /&gt;
design of the kernel because a function is coded to perform one action for&lt;br /&gt;
one client in a serial, synchronous fashion. Furthermore, nothing is ever&lt;br /&gt;
forced on a thread that is in kernel mode; any action taken on a thread in&lt;br /&gt;
kernel mode is taken by that thread itself. For example, if a process is to&lt;br /&gt;
be killed and one of its threads is in kernel mode, OS/2 doesn't terminate&lt;br /&gt;
that thread; it sets a flag that says, &amp;quot;Please kill yourself at your&lt;br /&gt;
earliest convenience.&amp;quot; Consequently, OS/2 doesn't need special code to&lt;br /&gt;
enumerate and release any internal flags or resources that a killed kernel&lt;br /&gt;
mode thread might leave orphaned, and in general no thread need&lt;br /&gt;
&amp;quot;understand&amp;quot; the state of any other. The thread to be killed cleans itself&lt;br /&gt;
up, releasing resources, flags, and whatever before it obligingly commits&lt;br /&gt;
suicide.&lt;br /&gt;
     But when is the thread's &amp;quot;earliest convenience&amp;quot;? Thread termination is&lt;br /&gt;
a forced event, and all threads check for any pending forced events&lt;br /&gt;
immediately before they leave kernel mode and reenter application mode.&lt;br /&gt;
This transition takes place frequently: not only when a system call returns&lt;br /&gt;
to the calling application, but also each time a context switch takes&lt;br /&gt;
place.&lt;br /&gt;
     Although it may appear that forced events might languish unprocessed,&lt;br /&gt;
they are serviced rapidly. For example, when a process issues a DosKill&lt;br /&gt;
function on its child process, each thread in the child process is marked&lt;br /&gt;
&amp;quot;kill yourself.&amp;quot; Because the parent process had the CPU, obviously, when it&lt;br /&gt;
issued the DosKill, each of the child's threads is in kernel mode, either&lt;br /&gt;
because the thread is working on a system call or because it was&lt;br /&gt;
artificially placed in kernel mode when the scheduler preempted it. Before&lt;br /&gt;
any of those now-marked threads can execute even a single instruction of&lt;br /&gt;
the child application's code, they must go through OS/2's dispatch routine.&lt;br /&gt;
The &amp;quot;kill yourself&amp;quot; flag is noted, and the thread terminates itself instead&lt;br /&gt;
of returning to application mode. As you can see, the final effect of this&lt;br /&gt;
approach is far from slow: The DosKill takes effect immediately--not one&lt;br /&gt;
more instruction of the child process is executed.&lt;br /&gt;
3. Excepting any SIGKILL handlers and&lt;br /&gt;
DosExitList handlers, of course.&lt;br /&gt;
3 The only significant&lt;br /&gt;
delay in recognizing a forced event occurs when a system call takes a long&lt;br /&gt;
time to process. OS/2 is not very CPU bound, so any call that takes a &amp;quot;long&lt;br /&gt;
time&amp;quot; (1 second or more) must be blocked for most of that time.&lt;br /&gt;
     When a kernel thread issues a block call for an event that might take&lt;br /&gt;
a long time--such as waiting for a keystroke or waiting for a semaphore to&lt;br /&gt;
clear--it uses a special form of block called an interruptible block. When&lt;br /&gt;
OS/2 posts a force flag against a thread, it checks to see if that thread&lt;br /&gt;
is blocking interruptibly. If it is, that thread is released from its block&lt;br /&gt;
with a special code that says, &amp;quot;You were awakened not because the event has&lt;br /&gt;
come to pass but because a forced event was posted.&amp;quot; That thread must then&lt;br /&gt;
finish the system call quickly (generally by declaring an error) so that&lt;br /&gt;
the thread can go through the dispatch routine and recognize the force&lt;br /&gt;
flag. I described this mechanism in Chapter 12 when I talked about another&lt;br /&gt;
kind of forced event--the OS/2 signal mechanism. An incoming signal is a&lt;br /&gt;
forced event for a process's thread 1; it therefore receives the same&lt;br /&gt;
timely response and has the same effect of aborting a slow system call.&lt;br /&gt;
     I've gone through this long discussion of forced events and how&lt;br /&gt;
they're processed because the internal debugging facility is based on one&lt;br /&gt;
giant special forced event. When a process is placed in debug state, a&lt;br /&gt;
trace force flag is permanently set for the initial thread of that process&lt;br /&gt;
and for any other threads it creates. When any of those threads are in&lt;br /&gt;
kernel mode--and they enter kernel mode whenever anything of interest takes&lt;br /&gt;
place--they execute the debuggee half of the OS/2 trace code. The debugger&lt;br /&gt;
half is executed by a debugger thread that issues special DosPtrace calls;&lt;br /&gt;
the two halves of the package communicate through a shared memory area&lt;br /&gt;
built into OS/2.&lt;br /&gt;
     When the debuggee encounters a special event (for example, a Ctrl-C&lt;br /&gt;
signal or a GP fault), the trace force event takes precedence over any&lt;br /&gt;
other, and the debuggee's thread executes the debuggee half of the&lt;br /&gt;
DosPtrace code. This code writes a record describing the event into a&lt;br /&gt;
communications buffer, wakes up the debugger thread, which is typically&lt;br /&gt;
blocked in the debugger's part of the DosPtrace code, and blocks, awaiting&lt;br /&gt;
a reply. The debugger's thread wakes up and returns to the debugger with&lt;br /&gt;
the event information. When the debugger recalls DosPtrace with a command,&lt;br /&gt;
the command is written into the communications area, and the debuggee is&lt;br /&gt;
awakened to read and obey. The command might be &amp;quot;Resume normal execution,&amp;quot;&lt;br /&gt;
&amp;quot;Process the event as you normally would,&amp;quot; or &amp;quot;Give me the contents of&lt;br /&gt;
these locations in your address space,&amp;quot; whereupon the debuggee thread&lt;br /&gt;
replies and remains in the DosPtrace handler.&lt;br /&gt;
     This approach is simple to implement, does the job well, and takes&lt;br /&gt;
advantage of existing OS/2 features. For example, no special code is needed&lt;br /&gt;
to allow the debugger access to the debuggee's address space because the&lt;br /&gt;
debuggee itself, unwittingly in the DosPtrace code, reads and writes its&lt;br /&gt;
own address space. Credit goes to the UNIX ptrace facility, upon which this&lt;br /&gt;
facility was closely modeled.&lt;br /&gt;
     Finally, here are a few incidental facts that the readers of this&lt;br /&gt;
book, being likely users of debugging facilities, should know. OS/2&lt;br /&gt;
maintains a linkage between the debugger process and the debuggee process.&lt;br /&gt;
When the debugger process terminates, the debuggee process also terminates&lt;br /&gt;
if it has not already done so. The debuggee program need not be a direct&lt;br /&gt;
child of the debugger; when the debugger process makes its initial&lt;br /&gt;
DosPtrace call, OS/2 connects it to the last process that was executed with&lt;br /&gt;
the special tracing option. If a process is executed with the tracing&lt;br /&gt;
option but no debugger process subsequently issues a DosPtrace function,&lt;br /&gt;
the jilted debuggee process is terminated in about two minutes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==19  The 3x Box==&lt;br /&gt;
&lt;br /&gt;
It's of critical importance that OS/2 do a good job of running existing MS-&lt;br /&gt;
DOS applications, but as we've discussed, this is a difficult task. To&lt;br /&gt;
offer the official MS-DOS interfaces under OS/2 and therefore claim upward&lt;br /&gt;
compatibility would be easy; unfortunately, few popular applications would&lt;br /&gt;
run successfully in such an environment. Most sophisticated applications&lt;br /&gt;
take direct control of the machine environment and use MS-DOS for tasks the&lt;br /&gt;
application doesn't want to bother with, such as file I/O, keyboard&lt;br /&gt;
buffering, and so forth. If we're to run existing applications&lt;br /&gt;
successfully, we must provide a close facsimile to a real mode PC running&lt;br /&gt;
MS-DOS in all respects, not just the INT 21 program interface.&lt;br /&gt;
     OS/2 provides such a highly compatible environment, called the real&lt;br /&gt;
mode screen group, the compatibility box, or simply the 3x box. The 3x box&lt;br /&gt;
is an environment that emulates an 8086-based PC running MS-DOS version&lt;br /&gt;
3.3.&lt;br /&gt;
1. For OS/2 version 1.0, the 3x box is compatible with MS-DOS&lt;br /&gt;
version 3.3.&lt;br /&gt;
1 MS-DOS programs execute in real mode, and because emulating real&lt;br /&gt;
mode from within protected mode is prohibitively slow, OS/2 physically&lt;br /&gt;
switches into real mode to execute MS-DOS applications. Because MS-DOS&lt;br /&gt;
programs are well aware of the MS-DOS memory layout, this layout is&lt;br /&gt;
replicated for the OS/2 3x box. The first N bytes (typically 640 KB) are&lt;br /&gt;
reserved for the exclusive use of the low-memory parts of OS/2 and the 3x&lt;br /&gt;
box; protected mode applications never use any of this memory. Thus,&lt;br /&gt;
programs that are careless about memory allocation or that make single-&lt;br /&gt;
tasking assumptions about the availability of memory can run in a&lt;br /&gt;
multitasking environment. Figure 19-1 illustrates the OS/2 memory layout.&lt;br /&gt;
The low bytes of memory are reserved for the device drivers and portions of&lt;br /&gt;
OS/2 that must run in real mode. The remainder of the space, up to the&lt;br /&gt;
RMSIZE value, is dedicated to the 3x box. Memory from 640 KB to 1 MB is&lt;br /&gt;
reserved for ROMs and video display buffers. Memory above 1 MB holds the&lt;br /&gt;
remainder of OS/2 and all protect mode applications. Nonswappable, fixed&lt;br /&gt;
segments are kept at one end of this memory to reduce fragmentation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      N ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ³    Protect     ³&lt;br /&gt;
        ³      mode      ³&lt;br /&gt;
        ³  applications  ³&lt;br /&gt;
        ³   (movable)    ³&lt;br /&gt;
        &amp;lt;                &amp;lt;&lt;br /&gt;
         &amp;gt;                &amp;gt;&lt;br /&gt;
        &amp;lt;                &amp;lt;&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³     Fixed      ³&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³      OS/2      ³&lt;br /&gt;
   1 MB ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
 640 KB ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ³      Real      ³&lt;br /&gt;
        ³      mode      ³&lt;br /&gt;
        ³  application   ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³   Additional   ³&lt;br /&gt;
        ³ device drivers ³&lt;br /&gt;
  ~100k ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ³    Low OS/2    ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
   90:0 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³    Bios ROM    ³&lt;br /&gt;
      0 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 19-1.  System memory layout.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 uses the screen group mechanism to provide a user interface to&lt;br /&gt;
the 3x box. One screen group is designated the real mode screen group;&lt;br /&gt;
automatically, OS/2 executes COMMAND.COM in that screen group when it is&lt;br /&gt;
first selected. The user accesses the real mode environment by selecting&lt;br /&gt;
that screen group and returns to the protected mode environment by&lt;br /&gt;
selecting another screen group. OS/2 version 1.0 supports a single real&lt;br /&gt;
mode screen group because the real mode compatibility is provided by&lt;br /&gt;
actually running the application in real mode. Thus, only one 640 KB area&lt;br /&gt;
is reserved for all real mode applications, and adjudicating between the&lt;br /&gt;
conflicting hardware manipulations of multiple real mode applications&lt;br /&gt;
without any assistance from the 80286 microprocessor hardware would be&lt;br /&gt;
prohibitively difficult. The 80386 microprocessor, however, provides a&lt;br /&gt;
special hardware facility called virtual 8086 mode that will allow a future&lt;br /&gt;
release of OS/2 to support multiple real mode screen groups, but only on an&lt;br /&gt;
80386-based machine.&lt;br /&gt;
     The operating system that services the 3x application's INT 21&lt;br /&gt;
requests is not an exact copy of MS-DOS; it's actually a low-memory&lt;br /&gt;
extension of OS/2 itself. Because OS/2 is derived from MS-DOS, OS/2&lt;br /&gt;
executes MS-DOS functions in a manner identical to that of the real MS-DOS.&lt;br /&gt;
OS/2 supports the non-MS-DOS functions mentioned above by staying out of&lt;br /&gt;
the way as much as possible and letting the 3x application &amp;quot;party hearty&amp;quot;&lt;br /&gt;
with the hardware. For example, hooking most interrupt vectors is&lt;br /&gt;
supported, as is hooking INT 21 and the ROM BIOS INT vectors. The ROM BIOS&lt;br /&gt;
calls themselves are fully supported. Frequently, staying out of the way is&lt;br /&gt;
not as easy as it may sound. For example, OS/2 must intercept and monitor&lt;br /&gt;
real mode calls made to the disk driver part of the ROM BIOS so that it can&lt;br /&gt;
prevent conflict with ongoing, asynchronous protect-mode disk I/O. OS/2 may&lt;br /&gt;
find it necessary to momentarily block a real mode application's BIOS call&lt;br /&gt;
until the protect mode device driver can release the hardware. Once the&lt;br /&gt;
real mode application is in the BIOS, the same interlock mechanism prevents&lt;br /&gt;
the protect mode device driver from entering the disk I/O critical&lt;br /&gt;
section.&lt;br /&gt;
     Hard errors encountered by the real mode application are handled by a&lt;br /&gt;
hybrid of the OS/2 hard error daemon and the 3x box INT 24 mechanism in a&lt;br /&gt;
three-step process, as follows:&lt;br /&gt;
&lt;br /&gt;
     1:  Hard error codes caused by events unique to the OS/2 environment--&lt;br /&gt;
         such as a volume manager media change request--activate the hard&lt;br /&gt;
         error daemon so that the user can get an accurate explanation&lt;br /&gt;
         of the problem. The user's response to the hard error is saved&lt;br /&gt;
         but is not yet acted upon. Hard error codes, which are also&lt;br /&gt;
         present in MS-DOS version 3.3, skip this step and start at&lt;br /&gt;
         step 2.&lt;br /&gt;
&lt;br /&gt;
     2:  If the real mode application has installed its own hard error&lt;br /&gt;
         handler via the INT 24 vector, it is called. If step 1 was&lt;br /&gt;
         skipped, the code should be known to the application, and it is&lt;br /&gt;
         presented unchanged. If step 1 was taken, the error code is&lt;br /&gt;
         transformed to ERROR_I24_GEN_FAILURE for this step. The response&lt;br /&gt;
         returned by the program, if valid for this class of hard error, is&lt;br /&gt;
         acted upon. This means that hard errors new to OS/2 can actually&lt;br /&gt;
         generate two pop-ups--one from the hard error daemon with an&lt;br /&gt;
         accurate message and one from the application itself with a&lt;br /&gt;
         General Failure message. This allows the user to understand the&lt;br /&gt;
         true cause of the hard error and yet notifies the application that&lt;br /&gt;
         a hard error has occurred. In such a case, the action specified by&lt;br /&gt;
         the application when it returned from its own hard error handler&lt;br /&gt;
         is the one taken, not the action specified by the user to the&lt;br /&gt;
         initial hard error daemon pop-up.&lt;br /&gt;
&lt;br /&gt;
3:       If the real mode application has not registered its hard error&lt;br /&gt;
         handler via the INT 24 mechanism, OS/2 provides a default handler&lt;br /&gt;
         that uses the hard error daemon. If step 1 was taken and the hard&lt;br /&gt;
         error daemon has already run, it is not run again; OS/2 takes the&lt;br /&gt;
         action specified in response to the hard error pop-up that was&lt;br /&gt;
         displayed. If step 1 was not taken because the hard error code is&lt;br /&gt;
         MS-DOS 3.x compatible and if step 2 was not taken because the&lt;br /&gt;
         application did not provide its own handler, then OS/2 activates&lt;br /&gt;
         the hard error daemon in step 3 to present the message and receive&lt;br /&gt;
         a reply.&lt;br /&gt;
&lt;br /&gt;
     The 3x box supports only MS-DOS functionality; no new OS/2 features&lt;br /&gt;
are available to 3x box applications--no new API, no multiple threads, no&lt;br /&gt;
IPC, no semaphores, and so on.&lt;br /&gt;
2. There are two exceptions. The OPEN function was&lt;br /&gt;
extended, and an INT 2F multiplex function was added to notify&lt;br /&gt;
real mode applications of screen switches.&lt;br /&gt;
2 This decision was made for two reasons.&lt;br /&gt;
First, although any real mode application can damage the system's&lt;br /&gt;
stability, allowing real mode applications to access some protect mode&lt;br /&gt;
features may aggravate the problem. For example, terminate and stay&lt;br /&gt;
resident programs may manipulate the CPU in such a way as to make it&lt;br /&gt;
impossible for a real mode application to protect a critical section with&lt;br /&gt;
semaphores and yet guarantee that it won't leave the semaphore orphaned.&lt;br /&gt;
Second, because OS/2 has only one real mode box and it labors under a 640&lt;br /&gt;
KB memory ceiling, it doesn't make sense to develop new real mode&lt;br /&gt;
applications that use new OS/2 functions and thus require OS/2.&lt;br /&gt;
     The 3x box emulation extends to interrupts. OS/2 continues to context&lt;br /&gt;
switch the CPU when the 3x box is active; that is, the 3x box application&lt;br /&gt;
is the foreground application. Because the foreground process receives a&lt;br /&gt;
favorable priority, its CPU is preempted only when a time-critical protect&lt;br /&gt;
mode application needs to run or when the real mode application blocks. If&lt;br /&gt;
the CPU is running a protect mode application when a device interrupt comes&lt;br /&gt;
in, OS/2 switches to real mode so that a real mode application that is&lt;br /&gt;
hooking the interrupt vectors can receive the interrupt in real mode. When&lt;br /&gt;
the interrupt is complete, OS/2 switches back to protected mode and resumes&lt;br /&gt;
the protected application.&lt;br /&gt;
     Although protected mode applications can continue to run when the 3x&lt;br /&gt;
box is in the foreground, the reverse is not true. When the 3x box screen&lt;br /&gt;
group is in the background, all 3x box execution is suspended, including&lt;br /&gt;
interrupts. Unlike protected mode applications, real mode applications&lt;br /&gt;
cannot be trusted to refrain from manipulating the screen hardware when&lt;br /&gt;
they are in a background screen group. Normally, a real mode application&lt;br /&gt;
doesn't notice its suspension when it's in background mode; the only thing&lt;br /&gt;
it might notice is that the system time-of-day has apparently &amp;quot;jumped&lt;br /&gt;
forward.&amp;quot; Because mode switching is a slow process and leaves interrupts&lt;br /&gt;
disabled for almost 1 millisecond, mode switching can cause interrupt&lt;br /&gt;
overruns on fast devices such as serial ports. The best way to deal with&lt;br /&gt;
this is to switch the real mode application into a background screen group;&lt;br /&gt;
with no more real mode programs to execute, OS/2 does no further mode&lt;br /&gt;
switching.&lt;br /&gt;
     Some OS/2 utility programs such as FIND are packaged as Family API&lt;br /&gt;
applications. A single binary can run in both protected mode and real mode,&lt;br /&gt;
and the user is saved the inconvenience of switching from real mode to&lt;br /&gt;
protected mode to do simple utility functions. This works well for simple&lt;br /&gt;
utility programs without full screen or graphical interfaces and for&lt;br /&gt;
programs that have modest memory demands and that in other ways have little&lt;br /&gt;
need of OS/2's extended capabilities. Obviously, if an application can make&lt;br /&gt;
good use of OS/2's protect mode features, it should be written to be&lt;br /&gt;
protect mode only so that it can take advantage of those features.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==20  Family API==&lt;br /&gt;
&lt;br /&gt;
When a new release of a PC operating system is announced, application&lt;br /&gt;
writers face a decision: Should they write a new application to use some of&lt;br /&gt;
the new features or should they use only the features in earlier releases?&lt;br /&gt;
If they go for the sexy new features, their product might do more, be&lt;br /&gt;
easier to write, or be more efficient; but when the program hits the&lt;br /&gt;
market, only 10 percent of existing PCs may be running the new release. Not&lt;br /&gt;
all of the existing machines have the proper processor to be able to run&lt;br /&gt;
the new system, and, of those, many of their users haven't seen the need to&lt;br /&gt;
go to the expense and endure the hassle of upgrading their operating&lt;br /&gt;
system. If it's viable to write the new application so that it requires&lt;br /&gt;
only the old operating system (and therefore runs in compatibility mode&lt;br /&gt;
under the new operating system), then it's tempting to do so. Even though&lt;br /&gt;
the product is not as good as it might be, it can sell to 100 percent of&lt;br /&gt;
the installed base of machines--10 times as many as it would if it required&lt;br /&gt;
the new operating system.&lt;br /&gt;
     And here you have the classic &amp;quot;catch-22&amp;quot; of software standards: If&lt;br /&gt;
users don't see a need, they won't use the new system. If they don't use&lt;br /&gt;
the new system, applications will not be written explicitly for it; so the&lt;br /&gt;
users never see a need. Without some way to prime the pump, it will be a&lt;br /&gt;
long time before a comprehensive set of applications are available that use&lt;br /&gt;
the new system's features.&lt;br /&gt;
     OS/2 tackles this problem in several ways. The software bundled with&lt;br /&gt;
OS/2 runs in protected mode, and OS/2 attempts to include as much&lt;br /&gt;
additional user function as possible to increase its value to a user who&lt;br /&gt;
initially owns no protected mode applications. The most important user&lt;br /&gt;
acceptance feature of OS/2, however, is called Family API. Family API is a&lt;br /&gt;
special subset of the OS/2 protected mode API. Using special tools included&lt;br /&gt;
in the OS/2 developer's kit, you can build applications that use only the&lt;br /&gt;
Family API. The resultant .EXE file(s) run unchanged in OS/2 protect mode&lt;br /&gt;
or on an 8086 running MS-DOS 2.x or 3.x.&lt;br /&gt;
1. Of course, they also run in the MS-DOS 3.x compatible screen group&lt;br /&gt;
under OS/2; but except for convenience utilities, it's generally a&lt;br /&gt;
waste to dedicate the one real mode screen group to running an appl-&lt;br /&gt;
cation that could run in any of the many protect mode screen groups.&lt;br /&gt;
1 Thus, developers don't have to&lt;br /&gt;
choose between writing applications that are OS/2 protect mode and writing&lt;br /&gt;
applications that are MS-DOS compatible; they can use the Family API&lt;br /&gt;
mechanism and do both. Your applications will run as protected mode&lt;br /&gt;
applications under OS/2 and as MS-DOS applications under a true MS-DOS&lt;br /&gt;
system.&lt;br /&gt;
     Clearly, the Family API is a noteworthy feature. It offers some OS/2&lt;br /&gt;
functions, together with the dynamic link system interface, to programs&lt;br /&gt;
that run under MS-DOS without a copy of OS/2 anywhere in sight. It does&lt;br /&gt;
this by providing an OS/2 compatibility library that accepts the OS/2&lt;br /&gt;
system interface calls and implements them itself, calling the underlying&lt;br /&gt;
MS-DOS system via INT 21 as necessary. This information should give you a&lt;br /&gt;
big head start in figuring out which OS/2 functions are included in the&lt;br /&gt;
Family API: Clearly all functions that have similar INT 21 functions--such&lt;br /&gt;
as DosOpen, DosRead, and DosAllocSeg--are supported. Also present are&lt;br /&gt;
functions, such as DosSubAlloc, that can be supported directly by the&lt;br /&gt;
special Family API library. Features that are extremely difficult to&lt;br /&gt;
support in a true MS-DOS environment, such as multiple threads and&lt;br /&gt;
asynchronous I/O, are not present in the Family API.&lt;br /&gt;
     Where does this library come from? And how does it get loaded by MS-&lt;br /&gt;
DOS to satisfy the OS/2 executable's dynlink requests? It's all done with&lt;br /&gt;
mirrors, as the expression goes, and the &amp;quot;mirrors&amp;quot; must be built into the&lt;br /&gt;
application's .EXE file because that file is all that's present when a&lt;br /&gt;
Family API application is executed under MS-DOS. Figure 20-1 shows the&lt;br /&gt;
layout of a Family API .EXE file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         Family API&lt;br /&gt;
            .EXE&lt;br /&gt;
     ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³&lt;br /&gt;
     ³°°°MS-DOS 3.x°°°°³&lt;br /&gt;
     ³°°°.EXE header°ÄÄÅÄÄ¿&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³  ³&lt;br /&gt;
     ³°°°Family API°°°°³  ³ Shaded area is&lt;br /&gt;
     ³°°°°°loader°°°°°°³  ³ read by MS-DOS as&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³ a real mode&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³  ³ application&lt;br /&gt;
     ³°°°Family API°°°°³  ³&lt;br /&gt;
     ³°°°°°library°°°°°³  ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´�ÄÙ&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³      OS/2       ³&lt;br /&gt;
     ³   .EXE header   ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³      OS/2       ³&lt;br /&gt;
     ³   application   ³&lt;br /&gt;
     ³    segments     ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³     Dynlink     ³&lt;br /&gt;
     ³      names      ³&lt;br /&gt;
     ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 20-1.  Family API executable (.EXE) format.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 needed to define a new .EXE file because the existing MS-DOS .EXE&lt;br /&gt;
file format contained too little information for the OS/2 protect mode&lt;br /&gt;
segmented environment. Because, as we've discussed, the 8086 memory&lt;br /&gt;
architecture is--despite the terminology normally used--a linear memory&lt;br /&gt;
architecture, the MS-DOS .EXE format described only a single hunk of memory&lt;br /&gt;
that was to be loaded contiguously. OS/2 needs each segment described&lt;br /&gt;
separately, with information on its status: read only, code or data, demand&lt;br /&gt;
load or preload, and so on. Naturally, OS/2 also needs a .EXE format with&lt;br /&gt;
special records to describe loadtime dynamic links. This new .EXE format&lt;br /&gt;
was defined so that its initial bytes look exactly like those of the old&lt;br /&gt;
MS-DOS .EXE file header. A special flag bit is set in this fake .EXE&lt;br /&gt;
 header that is ignored by all releases of MS-DOS but that OS/2 recognizes&lt;br /&gt;
to mean &amp;quot;It's not true. I'm really an OS/2 .EXE file. Seek to this location&lt;br /&gt;
to find the true, new-style .EXE header.&amp;quot;&lt;br /&gt;
     When an MS-DOS system is told to load this .EXE file, it sees and&lt;br /&gt;
believes the old .EXE file header. This header does not describe the&lt;br /&gt;
application itself but a body of special code built into the .EXE file&lt;br /&gt;
before the actual application's code: the Family API loader and library. In&lt;br /&gt;
other words, to MS-DOS this .EXE file looks like a valid, executable&lt;br /&gt;
program, and that program is the Family API loader and library. The Family&lt;br /&gt;
API loader and library are loaded into memory, and execution begins. MS-DOS&lt;br /&gt;
doesn't load in the body of the application itself because it wasn't&lt;br /&gt;
described as part of the load image in the special MS-DOS .EXE file header.&lt;br /&gt;
As soon as it starts to execute, the Family API loader begins reading in&lt;br /&gt;
the application's segments, performs a loader's general relocation chores,&lt;br /&gt;
and fixes up dynlink references to the proper entry points in the Family&lt;br /&gt;
API library package. When the application is loaded, the Family API loader&lt;br /&gt;
block moves the application to its final execution address, which overlays&lt;br /&gt;
most of the Family API loader to reclaim that space, and execution&lt;br /&gt;
begins.&lt;br /&gt;
     All OS/2 .EXE files have this fake MS-DOS .EXE format header. In non-&lt;br /&gt;
Family API executables, the Family API loader and library are missing, and&lt;br /&gt;
by default the header describes an impossibly big MS-DOS executable. Should&lt;br /&gt;
the application be accidentally run under a non-OS/2 system or in the OS/2&lt;br /&gt;
compatibility screen group, MS-DOS will refuse to load the program.&lt;br /&gt;
Optionally, the programmer can link in a small stub program that goes where&lt;br /&gt;
the Family API loader would and that prints a more meaningful error&lt;br /&gt;
message. As we said earlier, the old-style .EXE headers on the front of the&lt;br /&gt;
file contain a flag bit to alert OS/2 to the presence of a new-style .EXE&lt;br /&gt;
header further into the file. Because this header doesn't describe the&lt;br /&gt;
Family API loader and library parts of the file, OS/2 ignores their&lt;br /&gt;
presence when it loads a Family API application in protected mode; the&lt;br /&gt;
application's dynlink references are fixed up to the normal dynlink&lt;br /&gt;
libraries, and the Family API versions of those libraries are ignored.&lt;br /&gt;
     There Ain't No Such Thing As A Free Lunch, and the same unfortunately&lt;br /&gt;
applies to the Family API mechanism. First, although the Family API allows&lt;br /&gt;
dynlink calls to be used in an MS-DOS environment, this is not true&lt;br /&gt;
dynlinking; it's quasi dynlinking. Obviously, runtime dynlinking is not&lt;br /&gt;
supported, but even loadtime dynlinking is special because the dynlink&lt;br /&gt;
target library is bound into the .EXE file. One of the advantages of&lt;br /&gt;
dynlinks is that the target code is not part of the .EXE file and can&lt;br /&gt;
therefore be changed and upgraded without changing the .EXE file. This is&lt;br /&gt;
not true of the dynlink emulation library used by the Family API because&lt;br /&gt;
it is built into the .EXE file. Fortunately, this disadvantage isn't&lt;br /&gt;
normally a problem. Dynlink libraries are updated either to improve&lt;br /&gt;
their implementation or to add new features. The Family API library can't&lt;br /&gt;
be improved very much because its environment--MS-DOS--is limited and&lt;br /&gt;
unchanging. If new Family API features were added, loading that new library&lt;br /&gt;
with preexisting Family API .EXE files would make no sense; those programs&lt;br /&gt;
wouldn't be calling the new features.&lt;br /&gt;
     A more significant drawback is the size and speed hit that the Family&lt;br /&gt;
API introduces. Clearly, the size of a Family API .EXE file is extended by&lt;br /&gt;
the size of the Family API loader and the support library. The tools used&lt;br /&gt;
to build Family API executables include only those library routines used by&lt;br /&gt;
the program, but even so the library and the loader add up to a nontrivial&lt;br /&gt;
amount of memory--typically 10 KB to 14 KB in the .EXE file and perhaps&lt;br /&gt;
9KB (the loader is not included) in RAM. Finally, loading a Family API&lt;br /&gt;
application under MS-DOS is slower than loading a true MS-DOS .EXE file.&lt;br /&gt;
Comparing loadtime against the loadtime of MS-DOS is tough for any&lt;br /&gt;
operating system because loading faster than MS-DOS is difficult. The .EXE&lt;br /&gt;
file consists of a single lump of contiguous data that can be read into&lt;br /&gt;
memory in a single disk read operation. A relocation table must also be&lt;br /&gt;
read, but it's typically very small. It's hard for any system to be faster&lt;br /&gt;
than this. Clearly, loading a Family API application is slower because the&lt;br /&gt;
loader and library must be loaded, and then they must, a segment at a time,&lt;br /&gt;
bring in the body of the application.&lt;br /&gt;
     Although the Family API makes dual environment applications possible,&lt;br /&gt;
it can't totally hide from an application the difference between the MS-DOS&lt;br /&gt;
3.x and the OS/2 execution environment. For example, the Family API&lt;br /&gt;
supports only the DosFindFirst function for a single search handle at a&lt;br /&gt;
time. An application that wants to perform multiple directory searches&lt;br /&gt;
simultaneously should use DosGetMachineMode to determine its environment&lt;br /&gt;
and then use the unrestricted DosFindFirst function if running in protect&lt;br /&gt;
mode or use the INT 21 functions if running in real mode. Likewise, an&lt;br /&gt;
application that wants to manipulate printer data needs to contain version-&lt;br /&gt;
specific code to hook INT 17 or to use device monitors, depending on the&lt;br /&gt;
environment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Part III  The Future&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==21  The Future==&lt;br /&gt;
&lt;br /&gt;
This chapter is difficult to write because describing the second version of&lt;br /&gt;
OS/2 becomes uninteresting when that version is released. Furthermore,&lt;br /&gt;
preannouncing products is bad practice; the trade-offs between schedule and&lt;br /&gt;
customer demand can accelerate the inclusion of some features and postpone&lt;br /&gt;
others, often late in the development cycle. If we talk explicitly about&lt;br /&gt;
future features, developers may plan their work around the availability of&lt;br /&gt;
those features and be left high and dry if said features are postponed. As&lt;br /&gt;
a result, this chapter is necessarily vague about both the functional&lt;br /&gt;
details and the release schedule, discussing future goals for features&lt;br /&gt;
rather than the features themselves. Design your application, not so that&lt;br /&gt;
it depends on the features described here, but so that it is compatible&lt;br /&gt;
with them.&lt;br /&gt;
     OS/2 version 1.0 is the first standard MS-DOS-compatible operating&lt;br /&gt;
system that unlocks the memory-addressing potential of the 80286--a &amp;quot;train&amp;quot;&lt;br /&gt;
that will &amp;quot;pull&amp;quot; a great many APIs into the standard. On the other hand,&lt;br /&gt;
foreseeable future releases cannot expect such penetration, so the&lt;br /&gt;
designers of OS/2 version 1.0 focused primarily on including a full set of&lt;br /&gt;
APIs. Major performance improvements were postponed for future releases&lt;br /&gt;
simply because such improvements can be easily added later, whereas new&lt;br /&gt;
APIs cannot. Most of the planned work is to take further advantage of&lt;br /&gt;
existing interfaces, not to create new ones.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.1  File System&lt;br /&gt;
&lt;br /&gt;
Clearly, the heart of the office automation environment is data--lots of&lt;br /&gt;
data--searching for it, reading it, and, less frequently, writing it. A&lt;br /&gt;
machine's raw number-crunching capacity is relatively uninteresting in this&lt;br /&gt;
milieu; the important issue is how fast the machine can get the data and&lt;br /&gt;
manipulate it. Certainly, raw CPU power is advantageous; it allows the use&lt;br /&gt;
of a relatively compute bound graphical user interface, for example. But&lt;br /&gt;
I/O performance is becoming the limiting factor, especially in a&lt;br /&gt;
multitasking environment. Where does this data come from? If it's from the&lt;br /&gt;
keyboard, no problem; human typing speeds are glacially slow to a computer.&lt;br /&gt;
If the data is from a non-mass-storage device, OS/2's direct device access&lt;br /&gt;
facilities should provide sufficient throughput. That leaves the file&lt;br /&gt;
system for local disks and the network for remote data. The file system is&lt;br /&gt;
a natural for a future release upgrade. Its interface is generic so that&lt;br /&gt;
applications written for the first release will work compatibly with new&lt;br /&gt;
file systems in subsequent releases.&lt;br /&gt;
     Talking about pending file system improvements is relatively easy&lt;br /&gt;
because the weaknesses in the current FAT file system are obvious.&lt;br /&gt;
&lt;br /&gt;
     þ  Large Disk Support&lt;br /&gt;
        Clearly, a new file system will support arbitrarily large&lt;br /&gt;
        disks without introducing prohibitive allocation fragmentation.&lt;br /&gt;
        Allocation fragmentation refers to the minimum amount of disk space&lt;br /&gt;
        that a file system can allocate to a small file--the allocation&lt;br /&gt;
        unit. If the allocation unit is size N, the average file on the&lt;br /&gt;
        disk is expected to waste N/2 bytes of disk space because each file&lt;br /&gt;
        has a last allocation unit and on the average that unit will be&lt;br /&gt;
        only half filled. Actually, if the allocation unit is large, say&lt;br /&gt;
        more than 2 KB, the average fragmentation loss is greater than this&lt;br /&gt;
        estimate because a disproportionate number of files are small.&lt;br /&gt;
           The existing MS-DOS FAT file system can handle large disks,&lt;br /&gt;
        but at the cost of using very large allocation units. Depending on&lt;br /&gt;
        the number and the size of the files, a 100 MB disk might be as&lt;br /&gt;
        much as 50 percent wasted by this fragmentation. The new Microsoft&lt;br /&gt;
        file system will support a very small allocation unit--probably 512&lt;br /&gt;
        bytes--to reduce this fragmentation, and this small allocation unit&lt;br /&gt;
        size will not adversely affect the performance of the file system.&lt;br /&gt;
&lt;br /&gt;
     þ  File Protection&lt;br /&gt;
        A new file system also must support file access protection as part&lt;br /&gt;
        of the move toward a fully secure environment. File protection is&lt;br /&gt;
        typically a feature of multiuser operating systems; the MS-DOS FAT&lt;br /&gt;
        file system was designed for a single-user environment and contains&lt;br /&gt;
        no protection facilities. So why do we need them now? One reason is&lt;br /&gt;
        that a networked PC is physically a single-user machine, but&lt;br /&gt;
        logically it's a multiuser machine because multiple users can&lt;br /&gt;
        access the same files over the network. Also, as we shall see, it&lt;br /&gt;
        is sometimes useful to be able to protect your own files from&lt;br /&gt;
        access by yourself.&lt;br /&gt;
           Today, most network installations consist of server machines&lt;br /&gt;
        and client machines, with client machines able to access only files&lt;br /&gt;
        on server machines. MSNET and PCNET servers have a rudimentary form&lt;br /&gt;
        of file protection, but it needs improvement (see below). In the&lt;br /&gt;
        future, as machines become bigger and as products improve, files on&lt;br /&gt;
        client machines will also be available across the network. Clearly,&lt;br /&gt;
        a strong protection mechanism is needed to eliminate risks to a&lt;br /&gt;
        client machine's files. Finally, a file protection mechanism can be&lt;br /&gt;
        useful even on a single-user machine that is not accessible from a&lt;br /&gt;
        network. Today a variety of &amp;quot;Trojan&amp;quot; programs claim to be one thing&lt;br /&gt;
        but actually are another. In a nonnetworked environment, these&lt;br /&gt;
        programs are generally examples of mindless vandalism; typically,&lt;br /&gt;
        they purge the contents of the victim's hard disk. In a future&lt;br /&gt;
        office environment, they might edit payroll files or send sensitive&lt;br /&gt;
        data to someone waiting across the network. If you, as a user, can&lt;br /&gt;
        put sensitive files under password protection, they are safe even&lt;br /&gt;
        from yourself when you unwittingly run a Trojan program. That&lt;br /&gt;
        program doesn't know the password, and you certainly will decline&lt;br /&gt;
        to supply it. Self-protection also prevents someone from sitting&lt;br /&gt;
        down at your PC while you are at lunch or on vacation and wreaking&lt;br /&gt;
        havoc with your files.&lt;br /&gt;
           Protection mechanisms take two general forms:&lt;br /&gt;
        capability tokens and access lists. capability token gives access&lt;br /&gt;
        to an object if the requestor can supply the proper token,&lt;br /&gt;
        which itself can take a variety of forms. A per-file or per-&lt;br /&gt;
        directory password, such as is available on existing MSNET&lt;br /&gt;
        and PCNET products, is a kind of  capability token: If you&lt;br /&gt;
        can present the password, you can access the file. Note that&lt;br /&gt;
        the password is associated with the item, not the user. The&lt;br /&gt;
        front door key to your house is a good example of a&lt;br /&gt;
        capability token, and it shows the features and limitations&lt;br /&gt;
        of the approach very well. Access to your house depends on&lt;br /&gt;
        owning the capability token--the key--and not on who you&lt;br /&gt;
        are. If you don't have your key, you can't get in, even if&lt;br /&gt;
        it's your own house. Anybody that does have the key can get&lt;br /&gt;
        in, no matter who they are. A key can sometimes be handy:&lt;br /&gt;
        You can loan it to someone for a day and then get it back.&lt;br /&gt;
        You can give it to the plumber's office, for example, and&lt;br /&gt;
        the office can give it to the plumber, who can in turn give&lt;br /&gt;
        it to an assistant. Capability tokens are flexible because&lt;br /&gt;
        you can pass them around without notifying the owner of the&lt;br /&gt;
        protected object.&lt;br /&gt;
           This benefit is also the major drawback of capability token&lt;br /&gt;
        systems: The capabilities can be passed around willy-nilly&lt;br /&gt;
        and, like a key, can be duplicated. Once you give your key&lt;br /&gt;
        out, you never know if you've gotten &amp;quot;them&amp;quot; back again. You&lt;br /&gt;
        can't enumerate who has access to your house, and if they&lt;br /&gt;
        refuse to return a key or if they've duplicated it, you&lt;br /&gt;
        can't withdraw access to your house. The only way to regain&lt;br /&gt;
        control over your house is to change the lock, which means&lt;br /&gt;
        that you have to reissue keys to everybody who should get&lt;br /&gt;
        access. In the world of houses and keys, this isn't much of&lt;br /&gt;
        a problem because keys aren't given out that much and it's&lt;br /&gt;
        easy to contact the few people who should have them.&lt;br /&gt;
        Changing the capability &amp;quot;lock&amp;quot; on a computer file is much&lt;br /&gt;
        more difficult, however, because it may mean updating a&lt;br /&gt;
        great many programs that are allowed access, and they all&lt;br /&gt;
        have to be updated simultaneously so that none is&lt;br /&gt;
        accidentally locked out. And, of course, the distribution of&lt;br /&gt;
        the new capability token must be carried out securely; you&lt;br /&gt;
        must ensure that no &amp;quot;bad guy&amp;quot; gets a chance to see and copy&lt;br /&gt;
        the token.&lt;br /&gt;
           And, finally, because a separate capability token, or&lt;br /&gt;
        password, needs to be kept for each file or directory, you can't&lt;br /&gt;
        possibly memorize them all. Instead, they get built into programs,&lt;br /&gt;
        stored in files, entered into batch scripts, and so on. All these&lt;br /&gt;
        passwords--the ones that are difficult to change because of the&lt;br /&gt;
        hassle of updating everybody--are being kept around in &amp;quot;plain text&amp;quot;&lt;br /&gt;
        in standardized locations, an invitation for pilferage. And just as&lt;br /&gt;
        the lock on your door won't tell you how many keys exist, a&lt;br /&gt;
        capability token system won't be able to warn you that someone has&lt;br /&gt;
        stolen a copy of the capability token.&lt;br /&gt;
           An alternative approach is the access list mechanism. It is&lt;br /&gt;
        equivalent to the guard at the movie studio gate who has a&lt;br /&gt;
        list of people on his clipboard. Each protected object is&lt;br /&gt;
        associated with a list of who is allowed what kind of access. It's&lt;br /&gt;
        easy to see who has access--simply look at the list. It's easy to&lt;br /&gt;
        give or take away access--simply edit the list. Maintaining the&lt;br /&gt;
        list is easy because no change is made unless someone is to be&lt;br /&gt;
        added or removed, and the list can contain group names, such as&lt;br /&gt;
        &amp;quot;anyone from the production department&amp;quot; or &amp;quot;all vice presidents.&amp;quot;&lt;br /&gt;
           The fly in this particular ointment--and the reason that&lt;br /&gt;
        MSNET didn't use this approach--is in authenticating the&lt;br /&gt;
        identification of the person who wants access. In our movie studio,&lt;br /&gt;
        a picture badge is probably sufficient.&lt;br /&gt;
1. Note that the photo on the badge, together with a hard-to duplicate&lt;br /&gt;
design, keeps the badge from being just another capability token.&lt;br /&gt;
1 With the computer, we use&lt;br /&gt;
        a personal password. This password doesn't show that you have&lt;br /&gt;
        access to a particular file; it shows that you are who you claim to&lt;br /&gt;
        be. Also, because you have only one password, you can memorize it;&lt;br /&gt;
        it needn't be written on any list. Finally, you can change the&lt;br /&gt;
        password frequently because only one person--the one changing it,&lt;br /&gt;
        you--needs to be notified. Once the computer system knows that&lt;br /&gt;
        you're truly Hiram G. Hornswoggle, it grants or refuses access&lt;br /&gt;
        based on whether you're on an access list or belong to a group that&lt;br /&gt;
        is on an access list. MS-DOS can't use this approach because it's&lt;br /&gt;
        an unprotected system; whatever flag it sets in memory to say that&lt;br /&gt;
        you have properly authenticated yourself can be set by a cheater&lt;br /&gt;
        program. OS/2 is a protect mode operating system and is secure from&lt;br /&gt;
        such manipulation provided that no untrusted real mode applications&lt;br /&gt;
        are executed.&lt;br /&gt;
2. A future OS/2 release will take advantage of the 80386&lt;br /&gt;
processor's virtual real mode facility to make it safe to run&lt;br /&gt;
untrusted real mode programs on an 80386.&lt;br /&gt;
2 A networking environment provides an extra&lt;br /&gt;
        challenge because you can write a program--perhaps running&lt;br /&gt;
        on an MS-DOS machine to avoid protection mechanisms--that &amp;quot;sniffs&amp;quot;&lt;br /&gt;
        the network, examining every communication. A client machine can't&lt;br /&gt;
        send a plain-text password over the network to authenticate its&lt;br /&gt;
        user because a sniffer could see it. And it certainly can't send a&lt;br /&gt;
        message saying, &amp;quot;I'm satisfied that this is really Hiram.&amp;quot; The&lt;br /&gt;
        client machine may be running bogus software that will lie and say&lt;br /&gt;
        that when it isn't true. In other words, a network authentication&lt;br /&gt;
        protocol must assume that &amp;quot;bad guys&amp;quot; can read all net transmissions&lt;br /&gt;
        and can generate any transmission they wish.&lt;br /&gt;
           As should be clear by now, a future OS/2 file system will&lt;br /&gt;
        support per-object permission lists. OS/2 will be enhanced&lt;br /&gt;
        to support users' identifying themselves by means of personal&lt;br /&gt;
        passwords. Future network software will support a secure network&lt;br /&gt;
        authentication protocol.&lt;br /&gt;
&lt;br /&gt;
     A new file system will do more than support access lists; it will also&lt;br /&gt;
support filenames longer than the FAT 8.3 convention, and it will support&lt;br /&gt;
extended file attributes. The FAT file system supports a very limited set&lt;br /&gt;
of attributes, each of which are binary flags--system, hidden, read-only,&lt;br /&gt;
and so on. Extended attributes allow an arbitrary set of attributes,&lt;br /&gt;
represented as text strings, to be associated with each file. Individual&lt;br /&gt;
applications will be able to define specific attributes, set them on files,&lt;br /&gt;
and later query their values. Extended attributes can be used, for example,&lt;br /&gt;
to name the application that created the file. This would allow a user to&lt;br /&gt;
click the mouse over the filename on a directory display and have the&lt;br /&gt;
presentation manager bring up the proper application on that file.&lt;br /&gt;
     Finally, although this file system wish list looks pretty good, how do&lt;br /&gt;
we know that we've covered all the bases? And will our new file system work&lt;br /&gt;
well with CD-ROM&lt;br /&gt;
3. Special versions of compact discs that contain digital data instead&lt;br /&gt;
of digitized music. When accessed via a modified CD player, they&lt;br /&gt;
provide approximately 600 MB of read-only storage.&lt;br /&gt;
3 disks and WORM drives? The answers are &amp;quot;We don't&amp;quot; and&lt;br /&gt;
&amp;quot;It doesn't,&amp;quot; so a future OS/2 release will support installable file&lt;br /&gt;
systems. An installable file system is similar to an installable device&lt;br /&gt;
driver. When the system is initialized, not only device drivers but new&lt;br /&gt;
file system management packages can be installed into OS/2. This will allow&lt;br /&gt;
specialized file systems to handle specialized devices such as CD-ROMs and&lt;br /&gt;
WORM, as well as providing an easy interface to media written on foreign&lt;br /&gt;
file systems that are on non-MS-DOS or non-OS/2 systems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2  The 80386&lt;br /&gt;
&lt;br /&gt;
Throughout this book, the name 80386 keeps cropping up, almost as a kind of&lt;br /&gt;
magical incantation. To a system designer, it is a magical device. It&lt;br /&gt;
provides the protection facilities of the 80286, but it also provides three&lt;br /&gt;
other key features.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.1  Large Segments&lt;br /&gt;
The 80386 has a segmented architecture very much like that of the 80286,&lt;br /&gt;
but 80286 segments are limited to 64 KB. On the 80386, segments can be as&lt;br /&gt;
large as 4 million KB; segments can be so large that an entire program can&lt;br /&gt;
run in 2 segments (one code and one data) and essentially ignore the&lt;br /&gt;
segmentation facilities of the processor. This is called flat model.&lt;br /&gt;
Writing programs that deal with large structures is easier using flat&lt;br /&gt;
model, and because compilers have a hard time generating optimal segmented&lt;br /&gt;
code, converting 8086/80286 large model programs to 80386 flat model can&lt;br /&gt;
produce dramatic increases in execution speed.&lt;br /&gt;
     Although a future release of OS/2 will certainly support large&lt;br /&gt;
segments and applications that use flat model internally, OS/2 will not&lt;br /&gt;
necessarily provide a flat model API. The system API for 32-bit&lt;br /&gt;
applications may continue to use segmented (that is, 48-bit) addresses.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.2  Multiple Real Mode Boxes&lt;br /&gt;
The 80386 provides a mode of execution called virtual real mode. Processes&lt;br /&gt;
that run in this mode execute instructions exactly as they would in real&lt;br /&gt;
mode, but they are not truly in real mode; they are in a special 8086-&lt;br /&gt;
compatible protected mode. The additional memory management and protection&lt;br /&gt;
facilities that this mode provides allow a future version of OS/2 to&lt;br /&gt;
support more than one real mode box at the same time; multiple real mode&lt;br /&gt;
applications will be able to execute simultaneously and to continue&lt;br /&gt;
executing while in background mode. The virtual real mode eliminates the&lt;br /&gt;
need for mode switching; thus, the user can execute real mode applications&lt;br /&gt;
while running communications applications that have a high interrupt&lt;br /&gt;
rate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.3  Full Protection Capability&lt;br /&gt;
The virtual real mode capability, coupled with the 80386's ability to&lt;br /&gt;
allow/disallow I/O access on a port-by-port basis, provides the hardware&lt;br /&gt;
foundation for a future OS/2 that is fully secure. In a fully secure OS/2,&lt;br /&gt;
the modules loaded in during bootup--the operating system itself, device&lt;br /&gt;
drivers, installable file systems, and so on--must be trusted, but no other&lt;br /&gt;
program can accidentally or deliberately damage others, read protected&lt;br /&gt;
files, or otherwise access or damage restricted data. The only damage an&lt;br /&gt;
aberrant or malicious program will be able to do is to slow down the&lt;br /&gt;
machine by hogging the resources, such as consuming most of the RAM or CPU&lt;br /&gt;
time. This is relatively harmless; the user can simply kill the offending&lt;br /&gt;
program and not run it anymore.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.4  Other Features&lt;br /&gt;
The 80386 contains other significant features besides speed, such as paged&lt;br /&gt;
virtual memory, that don't appear in an API or in a specific user benefit.&lt;br /&gt;
For this reason, we won't discuss them here other than to state the&lt;br /&gt;
obvious: An 80386 machine is generally considerably faster than an 80286-&lt;br /&gt;
based one.&lt;br /&gt;
     So what do these 80386 features mean for the 80286? What role will it&lt;br /&gt;
play in the near and far future? Should a developer write for the 80286 or&lt;br /&gt;
the 80386? First, OS/2 for the 80386&lt;br /&gt;
4. A product still under development at the time of this writing.&lt;br /&gt;
All releases of OS/2 will run on the 80386, but the initial OS/2&lt;br /&gt;
release treats the 80386 as a &amp;quot;fast 80286.&amp;quot; The only 80386&lt;br /&gt;
feature it uses is the faster mode-switching capability.&lt;br /&gt;
4 is the same operating system,&lt;br /&gt;
essentially, as OS/2 for the 80286. The only new API in 80386 OS/2 will be&lt;br /&gt;
the 32-bit wide one for 32-bit mode 80386-only binaries. The other&lt;br /&gt;
features--such as virtual memory, I/O permission mapping, and multiple real&lt;br /&gt;
mode boxes--are of value to the user but don't present any new APIs and&lt;br /&gt;
therefore are compatible with all applications. Certainly, taking advantage&lt;br /&gt;
of the 80386's new instruction order codes and 2^32-byte-length segments&lt;br /&gt;
will require a new API; in fact, a program must be specially written and&lt;br /&gt;
compiled for that environment. Only applications that can't function at all&lt;br /&gt;
using the smaller 80286-compatible segments need to become 80386 dependent;&lt;br /&gt;
80286 protect mode programs will run without change and without any&lt;br /&gt;
disadvantage on the 80386, taking advantage of its improved speed.&lt;br /&gt;
     To summarize, there is only one operating system, OS/2. OS/2 supports&lt;br /&gt;
16-bit protected mode applications that run on all machines, and OS/2 will&lt;br /&gt;
support 32-bit protected mode applications that will run only on 80386&lt;br /&gt;
machines. A developer should consider writing an application for the&lt;br /&gt;
32-bit model&lt;br /&gt;
5. When it is announced and documented.&lt;br /&gt;
5 only if the application performs so poorly in the 16-bit&lt;br /&gt;
model that a 16-bit version is worthless. Otherwise, one should develop&lt;br /&gt;
applications for the 16-bit model; such applications will run well on all&lt;br /&gt;
existing OS/2-compatible machines and on all OS/2 releases. Later, when the&lt;br /&gt;
80386 and OS/2-386 have sufficient market penetration, you may want to&lt;br /&gt;
release higher-performance upgrades to products that require the 80386.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.3  The Next Ten Years&lt;br /&gt;
&lt;br /&gt;
Microsoft believes that OS/2 will be a major influence in the personal&lt;br /&gt;
computer industry for roughly the next ten years. The standardization of&lt;br /&gt;
computing environments that mass market software brings about gives such&lt;br /&gt;
standards abnormal longevity, while the incredible rate of hardware&lt;br /&gt;
improvements brings on great pressure to change. As a result, we expect&lt;br /&gt;
OS/2 to live long and prosper, where long is a relative term in an industry&lt;br /&gt;
in which nothing can survive more than a decade. What might OS/2's&lt;br /&gt;
successor system look like? If we could answer that today, a successor&lt;br /&gt;
system would be unnecessary. Clearly, the increases in CPU performance will&lt;br /&gt;
continue. Personal computers will undoubtedly follow in the footsteps of&lt;br /&gt;
their supercomputer brethren and become used for more than calculation, but&lt;br /&gt;
also for simulation, modeling, and expert systems, not only in the&lt;br /&gt;
workplace but also in the home. The future will become clearer, over time,&lt;br /&gt;
as this most wonderful of tools continues to change its users.&lt;br /&gt;
     The development of OS/2 is, to date, the largest project that&lt;br /&gt;
Microsoft has ever taken on. From an initially very small group of&lt;br /&gt;
Microsoft engineers, to a still small joint Microsoft-IBM design team, to&lt;br /&gt;
finally a great many developers, builders, testers, and documenters from&lt;br /&gt;
both Microsoft and IBM, the project became known affectionately as the&lt;br /&gt;
&amp;quot;Black Hole.&amp;quot;&lt;br /&gt;
     As I write this, OS/2 is just weeks away from retail sale. It's been a&lt;br /&gt;
great pleasure for me and for the people who worked with me to see our&lt;br /&gt;
black hole begin to give back to our customers the fruits of the labors&lt;br /&gt;
that were poured into it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Glossary&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
anonymous pipe&lt;br /&gt;
a data storage buffer that OS/2 maintains in RAM; used for interprocess&lt;br /&gt;
communications.&lt;br /&gt;
&lt;br /&gt;
Applications Program Interface (API)&lt;br /&gt;
the set of calls a program uses to obtain services from the operating&lt;br /&gt;
system. The term API denotes a service interface, whatever its form.&lt;br /&gt;
&lt;br /&gt;
background category&lt;br /&gt;
a classification of processes that consists of those associated with a&lt;br /&gt;
screen group not currently being displayed.&lt;br /&gt;
&lt;br /&gt;
call gate&lt;br /&gt;
a special LDT or GDT entry that describes a subroutine entry point rather&lt;br /&gt;
than a memory segment. A far call to a call gate selector will cause a&lt;br /&gt;
transfer to the entry point specified in the call gate. This is a feature&lt;br /&gt;
of the 80286/80386 hardware and is normally used to provide a transition&lt;br /&gt;
from a lower privilege state to a higher one.&lt;br /&gt;
&lt;br /&gt;
captive thread&lt;br /&gt;
a thread that has been created by a dynlink package and that stays within&lt;br /&gt;
the dynlink code, never transferring back to the client process's code;&lt;br /&gt;
also a thread that is used to call a service entry point and that will&lt;br /&gt;
never return or that will return only if some specific event occurs.&lt;br /&gt;
&lt;br /&gt;
child process&lt;br /&gt;
a process created by another process (its parent process).&lt;br /&gt;
&lt;br /&gt;
closed system&lt;br /&gt;
hardware or software design that cannot be enhanced in the field by third-&lt;br /&gt;
party suppliers.&lt;br /&gt;
&lt;br /&gt;
command subtree&lt;br /&gt;
a process and all its descendants.&lt;br /&gt;
&lt;br /&gt;
context switch&lt;br /&gt;
the act of switching the CPU from the execution of one thread to another,&lt;br /&gt;
which may belong to the same process or to a different one.&lt;br /&gt;
&lt;br /&gt;
cooked mode&lt;br /&gt;
a mode established by programs for keyboard input. In cooked mode, OS/2&lt;br /&gt;
handles the line-editing characters such as the back space.&lt;br /&gt;
&lt;br /&gt;
critical section&lt;br /&gt;
a body of code that manipulates a data resource in a non-reentrant way.&lt;br /&gt;
&lt;br /&gt;
daemon program&lt;br /&gt;
a process that performs a utility function without interaction with the&lt;br /&gt;
user. For example, the swapper process is a daemon program.&lt;br /&gt;
&lt;br /&gt;
debuggee&lt;br /&gt;
the program being debugged.&lt;br /&gt;
&lt;br /&gt;
debugger&lt;br /&gt;
a program that helps the programmer locate the source of problems found&lt;br /&gt;
during runtime testing of a program.&lt;br /&gt;
&lt;br /&gt;
device driver&lt;br /&gt;
a program that transforms I/O requests made in a standard, device-&lt;br /&gt;
independent fashion into the operations necessary to make a specific piece&lt;br /&gt;
of hardware fulfill that request.&lt;br /&gt;
&lt;br /&gt;
device monitor&lt;br /&gt;
a mechanism that allows processes to track and/or modify device data&lt;br /&gt;
streams.&lt;br /&gt;
&lt;br /&gt;
disjoint LDT space&lt;br /&gt;
the LDT selectors reserved for memory objects that are shared or that may&lt;br /&gt;
be shared among processes.&lt;br /&gt;
&lt;br /&gt;
dynamic link&lt;br /&gt;
a method of postponing the resolution of external references until loadtime&lt;br /&gt;
or runtime. A dynamic link allows the called subroutines to be packaged,&lt;br /&gt;
dist ributed, and maintained independently of their callers. OS/2 extends&lt;br /&gt;
the dynamic link (or dynlink) mechanism to serve as the primary method by&lt;br /&gt;
which all system and nonsystem services are obtained.&lt;br /&gt;
&lt;br /&gt;
dynlink&lt;br /&gt;
see dynamic link.&lt;br /&gt;
&lt;br /&gt;
dynlink library&lt;br /&gt;
a file, in a special format, that contains the binary code for a group of&lt;br /&gt;
dynamically linked subroutines.&lt;br /&gt;
&lt;br /&gt;
dynlink routine&lt;br /&gt;
see dynamic link.&lt;br /&gt;
&lt;br /&gt;
dynlink subsystem&lt;br /&gt;
a dynlink module that provides a set of services built around a resource.&lt;br /&gt;
&lt;br /&gt;
encapsulation&lt;br /&gt;
the principle of hiding the internal implementation of a program, function,&lt;br /&gt;
or service so that its clients can tell what it does but not how it does&lt;br /&gt;
it.&lt;br /&gt;
&lt;br /&gt;
environment strings&lt;br /&gt;
a series of user-definable and program-definable strings that are&lt;br /&gt;
associated with each process. The initial values of environment strings are&lt;br /&gt;
established by a process's parent.&lt;br /&gt;
&lt;br /&gt;
exitlist&lt;br /&gt;
a list of subroutines that OS/2 calls when a process has terminated. The&lt;br /&gt;
exitlist is executed after process termination but before the process is&lt;br /&gt;
actually destroyed.&lt;br /&gt;
&lt;br /&gt;
Family Applications Program Interface (Family API)&lt;br /&gt;
a standard execution environment under MS-DOS versions 2.x and 3.x and&lt;br /&gt;
OS/2. The programmer can use the Family API to create an application that&lt;br /&gt;
uses a subset of OS/2 functions (but a superset of MS-DOS 3.x functions)&lt;br /&gt;
and that runs in a binary-compatible fashion under MS-DOS versions 2.x and&lt;br /&gt;
3.x and OS/2.&lt;br /&gt;
&lt;br /&gt;
file handle&lt;br /&gt;
a binary value that represents an open file; used in all file I/O calls.&lt;br /&gt;
&lt;br /&gt;
file locking&lt;br /&gt;
an OS/2 facility that allows one program to temporarily prevent other&lt;br /&gt;
programs from reading and/or writing a particular file.&lt;br /&gt;
&lt;br /&gt;
file system name space&lt;br /&gt;
names that have the format of filenames. All such names will eventually&lt;br /&gt;
represent disk &amp;quot;files&amp;quot;--data or special. Initially, some of these names are&lt;br /&gt;
kept in internal OS/2 RAM tables and are not present on any disk volume.&lt;br /&gt;
&lt;br /&gt;
forced event&lt;br /&gt;
an event or action that is forced upon a thread or a process from an&lt;br /&gt;
external source; for example, a Ctrl-C or a DosKill command.&lt;br /&gt;
&lt;br /&gt;
foreground category&lt;br /&gt;
a classification of processes that consists of those associated with the&lt;br /&gt;
currently active screen group.&lt;br /&gt;
&lt;br /&gt;
GDT&lt;br /&gt;
see global descriptor table.&lt;br /&gt;
&lt;br /&gt;
general priority category&lt;br /&gt;
the OS/2 classification of threads that consists of three subcategories:&lt;br /&gt;
background, foreground, and interactive.&lt;br /&gt;
&lt;br /&gt;
general protection (GP) fault&lt;br /&gt;
an error that occurs when a program accesses invalid memory locations or&lt;br /&gt;
accesses valid locations in an invalid way (such as writing into read-only&lt;br /&gt;
memory areas).&lt;br /&gt;
&lt;br /&gt;
giveaway shared memory&lt;br /&gt;
a shared memory mechanism in which a process that already has access to the&lt;br /&gt;
segment can grant access to another process. Processes cannot obtain access&lt;br /&gt;
for themselves; access must be granted by another process that already has&lt;br /&gt;
access.&lt;br /&gt;
&lt;br /&gt;
global data segment&lt;br /&gt;
a data segment that is shared among all instances of a dynlink routine; in&lt;br /&gt;
other words, a single segment that is accessible to all processes that call&lt;br /&gt;
a particular dynlink routine.&lt;br /&gt;
&lt;br /&gt;
global descriptor table (GDT)&lt;br /&gt;
an element of the 80286/80386 memory management hardware. The GDT holds the&lt;br /&gt;
descriptions of as many as 4095 global segments. A global segment is&lt;br /&gt;
accessible to all processes.&lt;br /&gt;
&lt;br /&gt;
global subsystem initialization&lt;br /&gt;
a facility that allows a dynlink routine to specify that its initialize&lt;br /&gt;
entry point should be called when the dynlink package is loaded on behalf&lt;br /&gt;
of its first client.&lt;br /&gt;
&lt;br /&gt;
grandparent process&lt;br /&gt;
the parent process of a process that created a process.&lt;br /&gt;
&lt;br /&gt;
handle&lt;br /&gt;
an arbitrary integer value that OS/2 returns to a process so that the&lt;br /&gt;
process can return it to OS/2 on subsequent calls; known to programmers as&lt;br /&gt;
a magic cookie.&lt;br /&gt;
&lt;br /&gt;
hard error&lt;br /&gt;
an error that the system detects but which it cannot correct without user&lt;br /&gt;
intervention.&lt;br /&gt;
&lt;br /&gt;
hard error daemon&lt;br /&gt;
a daemon process that services hard errors. The hard error daemon may be an&lt;br /&gt;
independent process, or it may be a thread that belongs to the session&lt;br /&gt;
manager or to the presentation manager.&lt;br /&gt;
&lt;br /&gt;
huge segments&lt;br /&gt;
a software technique that allows the creation and use of pseudo segments&lt;br /&gt;
larger than 65 KB.&lt;br /&gt;
&lt;br /&gt;
installable file system (IFS)&lt;br /&gt;
a body of code that OS/2 loads at boot time and that provides the software&lt;br /&gt;
to manage a file system on a storage device, including the ability to&lt;br /&gt;
create and maintain directories, allocate disk space, and so on.&lt;br /&gt;
&lt;br /&gt;
instance data segment&lt;br /&gt;
a memory segment that holds data specific to each instance of the dynlink&lt;br /&gt;
routine.&lt;br /&gt;
&lt;br /&gt;
instance subsystem initialization&lt;br /&gt;
a service that dynlink routines can request. A dynlink routine's initialize&lt;br /&gt;
entry point is called each time a new client is linked to the routine.&lt;br /&gt;
&lt;br /&gt;
interactive category&lt;br /&gt;
a classification of processes that consists of the process currently&lt;br /&gt;
interacting with the keyboard.&lt;br /&gt;
&lt;br /&gt;
interactive program&lt;br /&gt;
a program whose function is to obey commands from a user, such as an editor&lt;br /&gt;
or a spreadsheet program. Programs such as compilers may literally interact&lt;br /&gt;
by asking for filenames and compilation options, but they are considered&lt;br /&gt;
noninteractive because their function is to compile a source program, not&lt;br /&gt;
to provide answers to user-entered commands.&lt;br /&gt;
&lt;br /&gt;
interprocess communications (IPC)&lt;br /&gt;
the ability of processes and threads to transfer data and messages among&lt;br /&gt;
themselves; used to offer services to and receive services from other&lt;br /&gt;
programs.&lt;br /&gt;
&lt;br /&gt;
interruptible block&lt;br /&gt;
a special form of a blocking operation used inside the OS/2  kernel so that&lt;br /&gt;
events such as process kill and Ctrl-C can interrupt a thread that is&lt;br /&gt;
waiting, inside OS/2, for an event.&lt;br /&gt;
&lt;br /&gt;
I/O privilege mechanism&lt;br /&gt;
a facility that allows a process to ask a device driver for direct access&lt;br /&gt;
to the device's I/O ports and any dedicated or mapped memory locations it&lt;br /&gt;
has. The I/O privilege mechanism can be used directly by an application or&lt;br /&gt;
indirectly by a dynlink package.&lt;br /&gt;
&lt;br /&gt;
IPC&lt;br /&gt;
see interprocess communications.&lt;br /&gt;
&lt;br /&gt;
KBD&lt;br /&gt;
an abbreviated name for the dynlink package that manages the keyboard&lt;br /&gt;
device. All its entry points start with Kbd.&lt;br /&gt;
&lt;br /&gt;
kernel&lt;br /&gt;
the central part of OS/2. It resides permanently in fixed memory locations&lt;br /&gt;
and executes in the privileged ring 0 state.&lt;br /&gt;
&lt;br /&gt;
LDT&lt;br /&gt;
see local descriptor table.&lt;br /&gt;
&lt;br /&gt;
loadtime dynamic linking&lt;br /&gt;
the act of connecting a client process to dynamic link libraries when the&lt;br /&gt;
process is first loaded into memory.&lt;br /&gt;
&lt;br /&gt;
local descriptor table (LDT)&lt;br /&gt;
an element of the 80286/80386 memory management hardware. The LDT holds the&lt;br /&gt;
descriptions of as many as 4095 local segments. Each process has its own&lt;br /&gt;
LDT and cannot access the LDTs of other processes.&lt;br /&gt;
&lt;br /&gt;
logical device&lt;br /&gt;
a symbolic name for a device that the user can cause to be mapped to any&lt;br /&gt;
physical (actual) device.&lt;br /&gt;
&lt;br /&gt;
logical directory&lt;br /&gt;
a symbolic name for a directory that the user can cause to be mapped to any&lt;br /&gt;
actual drive and directory.&lt;br /&gt;
&lt;br /&gt;
low priority category&lt;br /&gt;
a classification of processes that consists of processes that get CPU time&lt;br /&gt;
only when no other thread in the other categories needs it; this category&lt;br /&gt;
is lower in priority than the general priority category.&lt;br /&gt;
&lt;br /&gt;
magic cookie&lt;br /&gt;
see handle.&lt;br /&gt;
&lt;br /&gt;
memory manager&lt;br /&gt;
the section of OS/2 that allocates both physical memory and virtual memory.&lt;br /&gt;
&lt;br /&gt;
memory overcommit&lt;br /&gt;
allocating more memory to the running program than physically exists.&lt;br /&gt;
&lt;br /&gt;
memory suballocation&lt;br /&gt;
the OS/2 facility that allocates pieces of memory from within an&lt;br /&gt;
application's segment.&lt;br /&gt;
&lt;br /&gt;
MOU&lt;br /&gt;
an abbreviated name for the dynlink package that manages the mouse device.&lt;br /&gt;
All its entry points start with Mou.&lt;br /&gt;
&lt;br /&gt;
multitasking operating system&lt;br /&gt;
an operating system in which two or more programs/threads can execute&lt;br /&gt;
simultaneously.&lt;br /&gt;
&lt;br /&gt;
named pipe&lt;br /&gt;
a data storage buffer that OS/2 maintains in RAM; used for interprocess&lt;br /&gt;
communication.&lt;br /&gt;
&lt;br /&gt;
named shared memory&lt;br /&gt;
a memory segment that can be accessed simultaneously by more than one&lt;br /&gt;
process. Its name allows processes to request access to it.&lt;br /&gt;
&lt;br /&gt;
open system&lt;br /&gt;
hardware or software design that allows third-party additions and upgrades&lt;br /&gt;
in the field.&lt;br /&gt;
&lt;br /&gt;
object name buffer&lt;br /&gt;
the area in which OS/2 returns a character string if the DosExecPgm&lt;br /&gt;
function fails.&lt;br /&gt;
&lt;br /&gt;
parallel multitasking&lt;br /&gt;
the process whereby programs execute simultaneously.&lt;br /&gt;
&lt;br /&gt;
parent process&lt;br /&gt;
a process that creates another process, which is called the child process.&lt;br /&gt;
&lt;br /&gt;
physical memory&lt;br /&gt;
the RAM (Random Access Memory) physically present inside the machine.&lt;br /&gt;
&lt;br /&gt;
PID (Process Identification Number)&lt;br /&gt;
a unique code that OS/2 assigns to a process when the process is created.&lt;br /&gt;
The PID may be any value except 0.&lt;br /&gt;
&lt;br /&gt;
pipe&lt;br /&gt;
see anonymous pipe; named pipe.&lt;br /&gt;
&lt;br /&gt;
presentation manager&lt;br /&gt;
the graphical user interface for OS/2.&lt;br /&gt;
&lt;br /&gt;
priority&lt;br /&gt;
(also known as CPU priority) the numeric value assigned to each runnable&lt;br /&gt;
thread in the system. Threads with a higher priority are assigned the CPU&lt;br /&gt;
in preference to those with a lower priority.&lt;br /&gt;
&lt;br /&gt;
privilege mode&lt;br /&gt;
a special execution mode (also known as ring 0) supported by the&lt;br /&gt;
80286/80386 hardware. Code executing in this mode can execute restricted&lt;br /&gt;
instructions that are used to manipulate key system structures and tables.&lt;br /&gt;
Only the OS/2 kernel and device drivers run in this mode.&lt;br /&gt;
&lt;br /&gt;
process&lt;br /&gt;
the executing instance of a binary file. In OS/2, the terms task and&lt;br /&gt;
process are used interchangeably. A process is the unit of ownership, and&lt;br /&gt;
processes own resources such as memory, open files, dynlink libraries, and&lt;br /&gt;
semaphores.&lt;br /&gt;
&lt;br /&gt;
protect mode&lt;br /&gt;
the operating mode of the 80286 microprocessor that allows the operating&lt;br /&gt;
system to use features that protect one application from another; also&lt;br /&gt;
called protected mode.&lt;br /&gt;
&lt;br /&gt;
queue&lt;br /&gt;
an orderly list of elements waiting for processing.&lt;br /&gt;
&lt;br /&gt;
RAM semaphore&lt;br /&gt;
a kind of semaphore that is based in memory accessible to a thread; fast,&lt;br /&gt;
but with limited functionality. See system semaphore.&lt;br /&gt;
&lt;br /&gt;
raw mode&lt;br /&gt;
a mode established by programs for keyboard input. In raw mode OS/2 passes&lt;br /&gt;
to the caller each character typed immediately as it is typed. The caller&lt;br /&gt;
is responsible for handling line-editing characters such as the back space.&lt;br /&gt;
&lt;br /&gt;
real mode&lt;br /&gt;
the operating mode of the 80286 microprocessor that runs programs designed&lt;br /&gt;
for the 8086/8088 microprocessor.&lt;br /&gt;
&lt;br /&gt;
record locking&lt;br /&gt;
the mechanism that allows a process to lock a range of bytes within a file.&lt;br /&gt;
While the lock is in effect, no other process can read or write those&lt;br /&gt;
bytes.&lt;br /&gt;
&lt;br /&gt;
ring 3&lt;br /&gt;
the privilege level that is used to run applications. Code executing at&lt;br /&gt;
this level cannot modify critical system structures.&lt;br /&gt;
&lt;br /&gt;
runtime dynamic linking&lt;br /&gt;
the act of establishing a dynamic link after a process has begun execution.&lt;br /&gt;
This is done by providing OS/2 with the module and entry point names; OS/2&lt;br /&gt;
returns the address of the routine.&lt;br /&gt;
&lt;br /&gt;
scheduler&lt;br /&gt;
the part of OS/2 that decides which thread to run and how long to run it&lt;br /&gt;
before assigning the CPU to another thread; also, the part of OS/2 that&lt;br /&gt;
determines the priority value for each thread.&lt;br /&gt;
&lt;br /&gt;
screen group&lt;br /&gt;
a group of one or more processes that share (generally in a serial fashion)&lt;br /&gt;
a single logical screen and keyboard.&lt;br /&gt;
&lt;br /&gt;
semaphore&lt;br /&gt;
a software flag or signal used to coordinate the activities of two or more&lt;br /&gt;
threads; commonly used to protect a critical section.&lt;br /&gt;
&lt;br /&gt;
serial multitasking&lt;br /&gt;
the process whereby multiple programs execute, but only one at a time.&lt;br /&gt;
&lt;br /&gt;
session manager&lt;br /&gt;
a system utility that manages screen group switching. The session manager&lt;br /&gt;
is used only in the absence of the presentation manager; the presentation&lt;br /&gt;
manager replaces the session manager.&lt;br /&gt;
&lt;br /&gt;
shared memory&lt;br /&gt;
a memory segment that can be accessed simultaneously by more than one&lt;br /&gt;
process.&lt;br /&gt;
&lt;br /&gt;
signaling&lt;br /&gt;
using semaphores to notify threads that certain events or activities have&lt;br /&gt;
taken place.&lt;br /&gt;
&lt;br /&gt;
signals&lt;br /&gt;
notification mechanisms implemented in software that operate in a fashion&lt;br /&gt;
analogous to hardware interrupts.&lt;br /&gt;
&lt;br /&gt;
software tools approach&lt;br /&gt;
a design philosophy in which each program and application in a package is&lt;br /&gt;
dedicated to performing a specific task and doing that task very well. See&lt;br /&gt;
also encapsulation.&lt;br /&gt;
&lt;br /&gt;
stack frame&lt;br /&gt;
a portion of a thread's stack that contains a procedure's local variables&lt;br /&gt;
and parameters.&lt;br /&gt;
&lt;br /&gt;
static linking&lt;br /&gt;
the combining of multiple compilands into a single executable file, thereby&lt;br /&gt;
resolving undefined external references.&lt;br /&gt;
&lt;br /&gt;
single-tasking&lt;br /&gt;
a computer environment in which only one program runs at a time.&lt;br /&gt;
&lt;br /&gt;
swapping&lt;br /&gt;
the technique by which some code or data in memory is written to a disk&lt;br /&gt;
file, thus allowing the memory it was using to be reused for another&lt;br /&gt;
purpose.&lt;br /&gt;
&lt;br /&gt;
system semaphore&lt;br /&gt;
a semaphore that is implemented in OS/2's internal memory area; somewhat&lt;br /&gt;
slower than RAM semaphores, but providing more features.&lt;br /&gt;
&lt;br /&gt;
System File Table (SFT)&lt;br /&gt;
an internal OS/2 table that contains an entry for every file currently&lt;br /&gt;
open.&lt;br /&gt;
&lt;br /&gt;
task&lt;br /&gt;
see process.&lt;br /&gt;
&lt;br /&gt;
thread&lt;br /&gt;
the OS/2 mechanism that allows more than one path of execution through the&lt;br /&gt;
same instance of an application program.&lt;br /&gt;
&lt;br /&gt;
thread ID&lt;br /&gt;
the handle of a particular thread within a process.&lt;br /&gt;
&lt;br /&gt;
thread of execution&lt;br /&gt;
the passage of the CPU through the instruction sequence.&lt;br /&gt;
&lt;br /&gt;
time-critical priority&lt;br /&gt;
a classification of processes that may be interactive or noninteractive, in&lt;br /&gt;
the foreground or background screen group, which have a higher priority&lt;br /&gt;
than any non-time-critical thread in the system.&lt;br /&gt;
&lt;br /&gt;
time slice&lt;br /&gt;
the amount of execution time that the scheduler will give a thread before&lt;br /&gt;
reassigning the CPU to another thread of equal priority.&lt;br /&gt;
&lt;br /&gt;
VIO&lt;br /&gt;
an abbreviated name of the dynlink package that manages the display device.&lt;br /&gt;
All its entry points start with Vio.&lt;br /&gt;
&lt;br /&gt;
virtual memory&lt;br /&gt;
the memory space allocated to and used by a process. At the time it is&lt;br /&gt;
being referenced, the virtual memory must be present in physical memory,&lt;br /&gt;
but otherwise it may be swapped to a disk file.&lt;br /&gt;
&lt;br /&gt;
virtualization&lt;br /&gt;
the general technique of hiding a complicated actual situation behind a&lt;br /&gt;
simple, standard interface.&lt;br /&gt;
&lt;br /&gt;
writethrough&lt;br /&gt;
an option available when a file write operation is performed which&lt;br /&gt;
specifies that the normal caching mechanism is to be sidestepped and the&lt;br /&gt;
data is to be written through to the disk surface immediately.&lt;br /&gt;
&lt;br /&gt;
3x box&lt;br /&gt;
the OS/2 environment that emulates an 8086-based PC running MS-DOS versions&lt;br /&gt;
2.x or 3.x.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Index&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Symbols&lt;br /&gt;
----&lt;br /&gt;
3x box&lt;br /&gt;
80286 processor&lt;br /&gt;
   bugs in&lt;br /&gt;
   I/O access control in&lt;br /&gt;
   segmented architecture of&lt;br /&gt;
   size of segments&lt;br /&gt;
80386 processor&lt;br /&gt;
   I/O access control in&lt;br /&gt;
   key features of&lt;br /&gt;
8080 processor&lt;br /&gt;
8086/8088 processor&lt;br /&gt;
   memory limitations of&lt;br /&gt;
   real mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A&lt;br /&gt;
----&lt;br /&gt;
Abort, Retry, Ignore message (MS-DOS)&lt;br /&gt;
access lists&lt;br /&gt;
addresses&lt;br /&gt;
   invalid&lt;br /&gt;
   subroutine&lt;br /&gt;
addressing, huge model&lt;br /&gt;
address offsets&lt;br /&gt;
address space, linear&lt;br /&gt;
allocation. See also memory&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
anonymous pipes&lt;br /&gt;
API&lt;br /&gt;
   80386 processor&lt;br /&gt;
   Family&lt;br /&gt;
   memory management&lt;br /&gt;
Apple Macintosh&lt;br /&gt;
application environment. See environment&lt;br /&gt;
application mode&lt;br /&gt;
applications&lt;br /&gt;
   ``combo''&lt;br /&gt;
   command&lt;br /&gt;
   communicating between&lt;br /&gt;
   compatibility with MS-DOS&lt;br /&gt;
   designing for both OS/2 and MS-DOS&lt;br /&gt;
   device monitors and&lt;br /&gt;
   dual mode&lt;br /&gt;
   I/O-bound&lt;br /&gt;
   protecting&lt;br /&gt;
   real mode&lt;br /&gt;
   running MS-DOS&lt;br /&gt;
   time-critical&lt;br /&gt;
Applications Program Interface. See API&lt;br /&gt;
   Family&lt;br /&gt;
architecture&lt;br /&gt;
   80386 processor&lt;br /&gt;
   design concepts of OS/2&lt;br /&gt;
   device driver&lt;br /&gt;
   I/O&lt;br /&gt;
   segmented&lt;br /&gt;
arguments&lt;br /&gt;
   DosCWait&lt;br /&gt;
   DosExecPgm&lt;br /&gt;
ASCII text strings&lt;br /&gt;
ASCIIZ strings&lt;br /&gt;
asynchronous I/O&lt;br /&gt;
asynchronous processing&lt;br /&gt;
atomic operation&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
B&lt;br /&gt;
----&lt;br /&gt;
background&lt;br /&gt;
   category&lt;br /&gt;
   I/O&lt;br /&gt;
   processing&lt;br /&gt;
   threads and applications&lt;br /&gt;
base segment&lt;br /&gt;
BAT files, MS-DOS&lt;br /&gt;
BIOS entry vector, hooking the&lt;br /&gt;
blocking services&lt;br /&gt;
block mode&lt;br /&gt;
   device drivers&lt;br /&gt;
   driver algorithm&lt;br /&gt;
blocks, interruptible&lt;br /&gt;
boot process&lt;br /&gt;
boot time, installing device drivers at&lt;br /&gt;
breakthroughs, technological&lt;br /&gt;
buffer reusability&lt;br /&gt;
buffers&lt;br /&gt;
   flushing&lt;br /&gt;
   monitor&lt;br /&gt;
bugs&lt;br /&gt;
   80286 processor&lt;br /&gt;
   program&lt;br /&gt;
byte-stream mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
C&lt;br /&gt;
----&lt;br /&gt;
call gate&lt;br /&gt;
call and return sequence, OS/2&lt;br /&gt;
call statements, writing for dynamic link routines&lt;br /&gt;
call timed out error&lt;br /&gt;
capability tokens&lt;br /&gt;
captive threads&lt;br /&gt;
categories, priority&lt;br /&gt;
Central Processing Unit. See CPU&lt;br /&gt;
character mode&lt;br /&gt;
   device driver model for&lt;br /&gt;
child processes&lt;br /&gt;
   controlling&lt;br /&gt;
CHKDSK&lt;br /&gt;
circular references&lt;br /&gt;
CLI instruction&lt;br /&gt;
client processes&lt;br /&gt;
closed system&lt;br /&gt;
clusters&lt;br /&gt;
CMD.EXE&lt;br /&gt;
   I/O architecture and&lt;br /&gt;
   logical device and directory names&lt;br /&gt;
code, swapping&lt;br /&gt;
CodeView&lt;br /&gt;
command application&lt;br /&gt;
COMMAND.COM&lt;br /&gt;
command mode&lt;br /&gt;
command processes&lt;br /&gt;
command subtrees&lt;br /&gt;
   controlling&lt;br /&gt;
command threads&lt;br /&gt;
communication, interprocess&lt;br /&gt;
compatibility&lt;br /&gt;
   downward&lt;br /&gt;
   functional&lt;br /&gt;
   levels of&lt;br /&gt;
   MS-DOS&lt;br /&gt;
   name generation and&lt;br /&gt;
   VIO and presentation manager&lt;br /&gt;
compatibility box&lt;br /&gt;
compatibility issues, OS/2&lt;br /&gt;
compatibility mode&lt;br /&gt;
computers&lt;br /&gt;
   mental work and&lt;br /&gt;
   multiple CPU&lt;br /&gt;
   networked (see also networks)&lt;br /&gt;
   Von Neumann&lt;br /&gt;
CONFIG.SYS file&lt;br /&gt;
consistency, design&lt;br /&gt;
context switching&lt;br /&gt;
conventions, system&lt;br /&gt;
cooked mode&lt;br /&gt;
CP/M, compatibility with&lt;br /&gt;
CP/M-80&lt;br /&gt;
CPU See also 8086/8088 processor, 80286 processor, 80386 processor&lt;br /&gt;
   priority&lt;br /&gt;
crashes, system&lt;br /&gt;
critical sections&lt;br /&gt;
   protecting&lt;br /&gt;
   signals and&lt;br /&gt;
CS register&lt;br /&gt;
Ctrl-Break and Ctrl-C&lt;br /&gt;
customized environment&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
D&lt;br /&gt;
----&lt;br /&gt;
daemon, hard error&lt;br /&gt;
daemon interfaces, dynamic links as&lt;br /&gt;
daemon program&lt;br /&gt;
data&lt;br /&gt;
   global&lt;br /&gt;
   handling in dynamic linking&lt;br /&gt;
   instance&lt;br /&gt;
   instructions and&lt;br /&gt;
   swapped-out&lt;br /&gt;
data integrity&lt;br /&gt;
data segments, executing from&lt;br /&gt;
data streams, monitoring&lt;br /&gt;
debuggee&lt;br /&gt;
debugger&lt;br /&gt;
   system&lt;br /&gt;
debugging&lt;br /&gt;
demand loading&lt;br /&gt;
demand load segment&lt;br /&gt;
densities, disk&lt;br /&gt;
design, concepts of OS/2&lt;br /&gt;
design goals&lt;br /&gt;
DevHlp&lt;br /&gt;
device data streams&lt;br /&gt;
device drivers&lt;br /&gt;
   architecture of&lt;br /&gt;
   block mode&lt;br /&gt;
   character mode&lt;br /&gt;
   code structure&lt;br /&gt;
   definition of&lt;br /&gt;
   dynamic link pseudo&lt;br /&gt;
   OS/2 communication and&lt;br /&gt;
   programming model for&lt;br /&gt;
device independence&lt;br /&gt;
   definition of&lt;br /&gt;
device management&lt;br /&gt;
device monitors&lt;br /&gt;
device names&lt;br /&gt;
devices&lt;br /&gt;
   direct access of&lt;br /&gt;
   logical&lt;br /&gt;
device-specific code, encapsulating&lt;br /&gt;
Digital Research&lt;br /&gt;
direct device access&lt;br /&gt;
directories&lt;br /&gt;
   ISAM&lt;br /&gt;
   logical&lt;br /&gt;
   working&lt;br /&gt;
directory names&lt;br /&gt;
directory tree hierarchy&lt;br /&gt;
disjoint LDT space&lt;br /&gt;
disk data synchronization&lt;br /&gt;
disk I/O requests&lt;br /&gt;
disk seek times&lt;br /&gt;
disk space, allocation of&lt;br /&gt;
DISKCOMP&lt;br /&gt;
DISKCOPY&lt;br /&gt;
disks&lt;br /&gt;
   laser&lt;br /&gt;
   unlabeled&lt;br /&gt;
dispatcher&lt;br /&gt;
display device, manipulating the&lt;br /&gt;
display memory&lt;br /&gt;
.DLL files&lt;br /&gt;
DosAllocHuge&lt;br /&gt;
DosAllocSeg&lt;br /&gt;
DosAllocShrSeg&lt;br /&gt;
DosBufReset&lt;br /&gt;
DosCallNmPipe&lt;br /&gt;
DosCalls&lt;br /&gt;
DosClose&lt;br /&gt;
DosConnectNmPipe&lt;br /&gt;
DosCreateCSAlias&lt;br /&gt;
DosCreateSem&lt;br /&gt;
DosCreateThread&lt;br /&gt;
DosCWait&lt;br /&gt;
DosDevIOCtl&lt;br /&gt;
DosDupHandle&lt;br /&gt;
DosEnterCritSec&lt;br /&gt;
DosErrClass&lt;br /&gt;
DosError&lt;br /&gt;
DosExecPgm&lt;br /&gt;
DosExit&lt;br /&gt;
DosExitCritSec&lt;br /&gt;
DosExitList&lt;br /&gt;
DosFindFirst&lt;br /&gt;
DosFindNext&lt;br /&gt;
DosFlagProcess&lt;br /&gt;
DosFreeModule&lt;br /&gt;
DosFreeSeg&lt;br /&gt;
DosGetHugeShift&lt;br /&gt;
DosGetMachineMode&lt;br /&gt;
DosGetProcAddr&lt;br /&gt;
DosGiveSeg&lt;br /&gt;
DosHoldSignal&lt;br /&gt;
DosKill&lt;br /&gt;
DosKillProcess&lt;br /&gt;
DosLoadModule&lt;br /&gt;
DosMakeNmPipe&lt;br /&gt;
DosMakePipe&lt;br /&gt;
DosMonRead&lt;br /&gt;
DosMonReq&lt;br /&gt;
DosMonWrite&lt;br /&gt;
DosMuxSemWait&lt;br /&gt;
DosNewSize&lt;br /&gt;
DosOpen&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosPeekNmPipe&lt;br /&gt;
DosPtrace&lt;br /&gt;
DosRead&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosReadQueue&lt;br /&gt;
DosReallocHuge&lt;br /&gt;
DosResumeThread&lt;br /&gt;
DosScanEnv&lt;br /&gt;
DosSearchPath&lt;br /&gt;
DosSemClear&lt;br /&gt;
DosSemRequest&lt;br /&gt;
DosSemSet&lt;br /&gt;
DosSemWait&lt;br /&gt;
DosSetFHandState&lt;br /&gt;
DosSetPrty&lt;br /&gt;
DosSetSigHandler&lt;br /&gt;
DosSetVerify&lt;br /&gt;
DosSleep&lt;br /&gt;
DosSubAlloc&lt;br /&gt;
DosSubFrees&lt;br /&gt;
DosSuspendThread&lt;br /&gt;
DosTimerAsync&lt;br /&gt;
DosTimerStart&lt;br /&gt;
DosTimerStop&lt;br /&gt;
DosTransactNmPipe&lt;br /&gt;
DosWrite&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosWriteQueue&lt;br /&gt;
downward compatibility&lt;br /&gt;
DPATH&lt;br /&gt;
drive-oriented operations&lt;br /&gt;
drives, high- and low-density&lt;br /&gt;
dual mode&lt;br /&gt;
Dynamic Data Exchange (DDE)&lt;br /&gt;
dynamic linking&lt;br /&gt;
   Family API use of&lt;br /&gt;
   loadtime&lt;br /&gt;
   runtime&lt;br /&gt;
dynamic link libraries&lt;br /&gt;
dynamic link routines, calling&lt;br /&gt;
dynamic links&lt;br /&gt;
   architectural role of&lt;br /&gt;
   circular references in&lt;br /&gt;
   details on implementing&lt;br /&gt;
   device drivers and&lt;br /&gt;
   interfaces to other processes&lt;br /&gt;
   naming&lt;br /&gt;
   side effects of&lt;br /&gt;
   using for pseudo device drivers&lt;br /&gt;
dynlink See also dynamic links&lt;br /&gt;
   routines&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
E&lt;br /&gt;
----&lt;br /&gt;
encapsulation&lt;br /&gt;
   device driver&lt;br /&gt;
entry ordinals&lt;br /&gt;
entry point name&lt;br /&gt;
environment&lt;br /&gt;
   customized&lt;br /&gt;
   dual mode&lt;br /&gt;
   protected&lt;br /&gt;
   single-tasking&lt;br /&gt;
   single-tasking versus multitasking&lt;br /&gt;
   stable&lt;br /&gt;
   stand-alone&lt;br /&gt;
   virtualizing the&lt;br /&gt;
environment block&lt;br /&gt;
environment strings&lt;br /&gt;
EnvPointer&lt;br /&gt;
EnvString&lt;br /&gt;
error codes, compatibility mode&lt;br /&gt;
errors&lt;br /&gt;
   general protection fault&lt;br /&gt;
   hard&lt;br /&gt;
   localization of&lt;br /&gt;
   program&lt;br /&gt;
   timed out&lt;br /&gt;
.EXE files&lt;br /&gt;
   executing&lt;br /&gt;
   Family API&lt;br /&gt;
EXEC function (MS-DOS)&lt;br /&gt;
execute threads&lt;br /&gt;
executing programs&lt;br /&gt;
execution speed&lt;br /&gt;
exitlist&lt;br /&gt;
expandability&lt;br /&gt;
extended file attributes&lt;br /&gt;
extended partitioning&lt;br /&gt;
extension, filename&lt;br /&gt;
external name, dynamic link&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
F&lt;br /&gt;
----&lt;br /&gt;
Family API&lt;br /&gt;
   drawbacks of&lt;br /&gt;
Family Applications Program Interface (Family API)&lt;br /&gt;
far return instruction&lt;br /&gt;
fault, memory not present&lt;br /&gt;
fault errors, general protection&lt;br /&gt;
faults, GP&lt;br /&gt;
features&lt;br /&gt;
   additional&lt;br /&gt;
   introducing new operating system&lt;br /&gt;
file&lt;br /&gt;
   allocation, improving&lt;br /&gt;
   attributes, extended&lt;br /&gt;
   handles&lt;br /&gt;
   I/O&lt;br /&gt;
   limits, floppy disk&lt;br /&gt;
   locking&lt;br /&gt;
   protection&lt;br /&gt;
   seek position&lt;br /&gt;
   sharing&lt;br /&gt;
   system&lt;br /&gt;
File Allocation Table&lt;br /&gt;
filenames, OS/2 and MS-DOS&lt;br /&gt;
files&lt;br /&gt;
   .DLL&lt;br /&gt;
   .EXE&lt;br /&gt;
   linking&lt;br /&gt;
   naming&lt;br /&gt;
   .OBJ&lt;br /&gt;
file system&lt;br /&gt;
   future of&lt;br /&gt;
   hierarchical&lt;br /&gt;
   installable&lt;br /&gt;
file system name space&lt;br /&gt;
   named pipes and&lt;br /&gt;
file utilization&lt;br /&gt;
FIND utility program&lt;br /&gt;
flags register&lt;br /&gt;
flat model, 80386 processor&lt;br /&gt;
flexibility, maximum&lt;br /&gt;
flush operation, buffer&lt;br /&gt;
forced events&lt;br /&gt;
foreground&lt;br /&gt;
   category&lt;br /&gt;
   processing&lt;br /&gt;
   threads and applications&lt;br /&gt;
fprintf&lt;br /&gt;
fragmentation&lt;br /&gt;
   disk&lt;br /&gt;
   internal&lt;br /&gt;
functions&lt;br /&gt;
   addition of&lt;br /&gt;
   resident&lt;br /&gt;
future versions of OS/2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
G&lt;br /&gt;
----&lt;br /&gt;
garbage collection&lt;br /&gt;
garbage handles&lt;br /&gt;
general failure error&lt;br /&gt;
general priority category&lt;br /&gt;
general protection fault (GP fault)&lt;br /&gt;
   errors&lt;br /&gt;
GetDosVar&lt;br /&gt;
giveaway shared memory&lt;br /&gt;
global data&lt;br /&gt;
global data segments&lt;br /&gt;
Global Descriptor Table (GDT)&lt;br /&gt;
global subsystem initialization&lt;br /&gt;
glossary&lt;br /&gt;
goals&lt;br /&gt;
   design&lt;br /&gt;
   OS/2&lt;br /&gt;
GP faults&lt;br /&gt;
grandparent processes&lt;br /&gt;
graphical user interface&lt;br /&gt;
   standard&lt;br /&gt;
graphics, VIO and&lt;br /&gt;
graphics devices&lt;br /&gt;
graphics drivers, device-independent&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
H&lt;br /&gt;
----&lt;br /&gt;
handles&lt;br /&gt;
   closing&lt;br /&gt;
   closing with dynamic links&lt;br /&gt;
   duplicated and inherited&lt;br /&gt;
   garbage&lt;br /&gt;
   semaphore&lt;br /&gt;
hard error daemon&lt;br /&gt;
hard errors&lt;br /&gt;
   handling in real mode&lt;br /&gt;
hardware, nonstandard&lt;br /&gt;
hardware devices. See devices&lt;br /&gt;
hardware interrupts&lt;br /&gt;
   device drivers and&lt;br /&gt;
hardware-specific interfaces&lt;br /&gt;
Hayes modems&lt;br /&gt;
heap algorithm&lt;br /&gt;
heap objects&lt;br /&gt;
Hercules Graphics Card&lt;br /&gt;
hierarchical file system&lt;br /&gt;
hooking the keyboard vector&lt;br /&gt;
huge memory&lt;br /&gt;
huge model addressing&lt;br /&gt;
huge segments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I&lt;br /&gt;
----&lt;br /&gt;
IBM PC/AT&lt;br /&gt;
INCLUDE&lt;br /&gt;
Independent Software Vendors (ISVs)&lt;br /&gt;
industrial revolution, second&lt;br /&gt;
infosegs&lt;br /&gt;
inheritance&lt;br /&gt;
INIT&lt;br /&gt;
initialization&lt;br /&gt;
   device driver&lt;br /&gt;
   global subsystem&lt;br /&gt;
   instance subsystem&lt;br /&gt;
initialization entry points, dynamic link&lt;br /&gt;
input/output. See I/O&lt;br /&gt;
installable file system (IFS)&lt;br /&gt;
instance data&lt;br /&gt;
   segment&lt;br /&gt;
instance subsystem initialization&lt;br /&gt;
instructions&lt;br /&gt;
   sequence of&lt;br /&gt;
insufficient memory&lt;br /&gt;
INT 21&lt;br /&gt;
INT 2F multiplex function&lt;br /&gt;
integrated applications&lt;br /&gt;
integrity, data&lt;br /&gt;
Intel. See also 8086/8088 processor, 80286 processor, 80386 processor&lt;br /&gt;
   80286 processor&lt;br /&gt;
   80386 processor&lt;br /&gt;
   8080 processor&lt;br /&gt;
   8086/8088 processor&lt;br /&gt;
interactive&lt;br /&gt;
   category&lt;br /&gt;
   processes&lt;br /&gt;
   programs&lt;br /&gt;
interface, user&lt;br /&gt;
interlocking&lt;br /&gt;
internal fragmentation&lt;br /&gt;
interprocess communication (IPC)&lt;br /&gt;
interrupt handling code&lt;br /&gt;
interruptible block&lt;br /&gt;
interrupts&lt;br /&gt;
   3x box emulation&lt;br /&gt;
   hardware&lt;br /&gt;
   signals and&lt;br /&gt;
interrupt service routine, device driver&lt;br /&gt;
interrupt-time thread&lt;br /&gt;
interrupt vectors&lt;br /&gt;
   hooking&lt;br /&gt;
I/O&lt;br /&gt;
   architecture&lt;br /&gt;
   asynchronous&lt;br /&gt;
   background&lt;br /&gt;
   disk requests&lt;br /&gt;
   efficiency&lt;br /&gt;
   file&lt;br /&gt;
   port access&lt;br /&gt;
   privilege mechanism&lt;br /&gt;
   requesting an operation&lt;br /&gt;
   signals and&lt;br /&gt;
   video&lt;br /&gt;
I/O-bound applications&lt;br /&gt;
IOCTL call&lt;br /&gt;
IP register&lt;br /&gt;
IPC See also interprocess communication&lt;br /&gt;
   combining forms of&lt;br /&gt;
   using with dynamic links&lt;br /&gt;
IRET instruction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
K&lt;br /&gt;
----&lt;br /&gt;
KBD&lt;br /&gt;
kernel&lt;br /&gt;
   interfacing with dynamic links&lt;br /&gt;
   mode&lt;br /&gt;
keyboard, processes using the&lt;br /&gt;
keyboard mode, cooked and raw&lt;br /&gt;
keyboard vector, hooking the&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&lt;br /&gt;
----&lt;br /&gt;
label names, volume&lt;br /&gt;
large disk support&lt;br /&gt;
LAR instruction&lt;br /&gt;
latency, rotational&lt;br /&gt;
Least Recently Used (LRU) scheme&lt;br /&gt;
levels, compatibility&lt;br /&gt;
LIB&lt;br /&gt;
libraries&lt;br /&gt;
   dynamic link&lt;br /&gt;
   sharing dynamic link&lt;br /&gt;
   subroutine and runtime&lt;br /&gt;
linear address space&lt;br /&gt;
linking&lt;br /&gt;
   dynamic&lt;br /&gt;
   static&lt;br /&gt;
loadtime dynamic linking&lt;br /&gt;
local area networks. See networks&lt;br /&gt;
Local Descriptor Table (LDT)&lt;br /&gt;
locality of reference&lt;br /&gt;
localization of errors&lt;br /&gt;
local variables&lt;br /&gt;
locking, file. See file and record locking&lt;br /&gt;
logical&lt;br /&gt;
   device and directory name facility&lt;br /&gt;
   devices&lt;br /&gt;
   directories&lt;br /&gt;
   disks, partitioning&lt;br /&gt;
low priority category&lt;br /&gt;
LSL instruction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
M&lt;br /&gt;
----&lt;br /&gt;
magic cookie&lt;br /&gt;
mass marketing, software and&lt;br /&gt;
media volume management&lt;br /&gt;
memory&lt;br /&gt;
   display&lt;br /&gt;
   huge&lt;br /&gt;
   insufficient&lt;br /&gt;
   layout of system&lt;br /&gt;
   limitations of in 8088 processor&lt;br /&gt;
   named shared&lt;br /&gt;
   physical&lt;br /&gt;
   protection&lt;br /&gt;
   shared&lt;br /&gt;
   shared giveaway&lt;br /&gt;
   swapping&lt;br /&gt;
   swapping segments in&lt;br /&gt;
   swapped-out&lt;br /&gt;
   utilization&lt;br /&gt;
   video&lt;br /&gt;
   virtual&lt;br /&gt;
memory management&lt;br /&gt;
   API&lt;br /&gt;
   hardware&lt;br /&gt;
memory manager&lt;br /&gt;
   definition of&lt;br /&gt;
memory not present fault&lt;br /&gt;
memory objects, tracking&lt;br /&gt;
memory overcommit&lt;br /&gt;
memory segments, allocating&lt;br /&gt;
memory suballocation&lt;br /&gt;
memory unit&lt;br /&gt;
mental work, mechanizing&lt;br /&gt;
message mode&lt;br /&gt;
Microsoft, vision of&lt;br /&gt;
Microsoft Macro Assembler (MASM)&lt;br /&gt;
model, architectural&lt;br /&gt;
modes&lt;br /&gt;
   incompatible&lt;br /&gt;
   keyboard&lt;br /&gt;
modular tools&lt;br /&gt;
module name&lt;br /&gt;
monitors, device&lt;br /&gt;
MOU&lt;br /&gt;
MS-DOS&lt;br /&gt;
   compatibility with&lt;br /&gt;
   environment list&lt;br /&gt;
   expandability of&lt;br /&gt;
   filenames in&lt;br /&gt;
   history of&lt;br /&gt;
   memory allocation in&lt;br /&gt;
   running applications in OS/2&lt;br /&gt;
   version 1.0&lt;br /&gt;
   version 2.0&lt;br /&gt;
   version 3.0&lt;br /&gt;
   version 4.0&lt;br /&gt;
   version 5.0&lt;br /&gt;
   versions of&lt;br /&gt;
multitasking&lt;br /&gt;
   data integrity and&lt;br /&gt;
   definition of&lt;br /&gt;
   device drivers and&lt;br /&gt;
   full&lt;br /&gt;
   MS-DOS version&lt;br /&gt;
   parallel&lt;br /&gt;
   serial&lt;br /&gt;
multiuser systems, thrashing and&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
N&lt;br /&gt;
----&lt;br /&gt;
named pipes&lt;br /&gt;
   multiple instances of&lt;br /&gt;
named shared memory&lt;br /&gt;
name generation, compatibility and&lt;br /&gt;
names, dynamic link&lt;br /&gt;
name set&lt;br /&gt;
network piggybacking&lt;br /&gt;
networks&lt;br /&gt;
   access to&lt;br /&gt;
   compatibility on&lt;br /&gt;
   file protection on&lt;br /&gt;
   importance of security on&lt;br /&gt;
network virtual circuit&lt;br /&gt;
NUMADD program&lt;br /&gt;
NUMARITH application&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
O&lt;br /&gt;
----&lt;br /&gt;
.OBJ files&lt;br /&gt;
obj namebuf&lt;br /&gt;
object name buffer&lt;br /&gt;
objects&lt;br /&gt;
   defining&lt;br /&gt;
   file system name space and&lt;br /&gt;
office automation&lt;br /&gt;
   objective of&lt;br /&gt;
   operating system for&lt;br /&gt;
offset registers&lt;br /&gt;
OPEN function&lt;br /&gt;
open system&lt;br /&gt;
operating systems&lt;br /&gt;
   multitasking&lt;br /&gt;
   other&lt;br /&gt;
ordinals, entry&lt;br /&gt;
orphaned semaphores&lt;br /&gt;
overlays, code&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
P&lt;br /&gt;
----&lt;br /&gt;
packet, request&lt;br /&gt;
paged virtual memory&lt;br /&gt;
paperless office&lt;br /&gt;
parallel multitasking&lt;br /&gt;
parent processes&lt;br /&gt;
partitioning, extended&lt;br /&gt;
passwords, file&lt;br /&gt;
Paterson, Tim&lt;br /&gt;
PATH&lt;br /&gt;
pathnames&lt;br /&gt;
peripherals, high-bandwidth&lt;br /&gt;
permissions&lt;br /&gt;
physical memory&lt;br /&gt;
PID&lt;br /&gt;
piggybacking&lt;br /&gt;
pipes&lt;br /&gt;
   anonymous&lt;br /&gt;
   named&lt;br /&gt;
pointer, seek&lt;br /&gt;
preemptive scheduler&lt;br /&gt;
presentation manager&lt;br /&gt;
   choosing between VIO and&lt;br /&gt;
   DDE and&lt;br /&gt;
priority&lt;br /&gt;
   categories of&lt;br /&gt;
   time-critical&lt;br /&gt;
priority scheduler&lt;br /&gt;
   semaphore&lt;br /&gt;
privilege mechanism, I/O&lt;br /&gt;
privilege mode&lt;br /&gt;
privilege transition&lt;br /&gt;
process termination signal handler&lt;br /&gt;
processes&lt;br /&gt;
   calling from OS/2&lt;br /&gt;
   child&lt;br /&gt;
   client&lt;br /&gt;
   command&lt;br /&gt;
   daemon&lt;br /&gt;
   dynamic linking and&lt;br /&gt;
   foreground and background&lt;br /&gt;
   grandparent&lt;br /&gt;
   interactive&lt;br /&gt;
   interfacing with dynamic links&lt;br /&gt;
   parent&lt;br /&gt;
   problems with multiple&lt;br /&gt;
   threads and&lt;br /&gt;
processing&lt;br /&gt;
   asynchronous&lt;br /&gt;
   foreground and background&lt;br /&gt;
processing unit&lt;br /&gt;
process tree&lt;br /&gt;
programming model, device drivers&lt;br /&gt;
programs&lt;br /&gt;
   debugger&lt;br /&gt;
   executing&lt;br /&gt;
   interactive&lt;br /&gt;
   running on OS/2&lt;br /&gt;
program termination signal&lt;br /&gt;
PROMPT&lt;br /&gt;
protected environment&lt;br /&gt;
protection&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
   model&lt;br /&gt;
   side-effects&lt;br /&gt;
protect mode&lt;br /&gt;
   huge model addressing in&lt;br /&gt;
   real mode versus&lt;br /&gt;
protocol, DDE&lt;br /&gt;
pseudo interrupts&lt;br /&gt;
Ptrace&lt;br /&gt;
pure segment&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Q, R&lt;br /&gt;
----&lt;br /&gt;
queues&lt;br /&gt;
RAM See also memory&lt;br /&gt;
   available&lt;br /&gt;
   semaphores&lt;br /&gt;
Random Access Memory. See RAM&lt;br /&gt;
random selector values&lt;br /&gt;
RAS information&lt;br /&gt;
raw mode&lt;br /&gt;
real mode&lt;br /&gt;
   80386 processor&lt;br /&gt;
   8086 processor&lt;br /&gt;
   applications&lt;br /&gt;
   compatibility box&lt;br /&gt;
   device drivers in&lt;br /&gt;
   hard errors in&lt;br /&gt;
   huge model addressing in&lt;br /&gt;
   protect mode versus&lt;br /&gt;
   screen group&lt;br /&gt;
   swapping in&lt;br /&gt;
real time, tracking passage of&lt;br /&gt;
record locking&lt;br /&gt;
redirector code (MS-DOS)&lt;br /&gt;
reference locality&lt;br /&gt;
registers&lt;br /&gt;
   offset&lt;br /&gt;
   segment&lt;br /&gt;
   signals and&lt;br /&gt;
Reliability, Availability, and Serviceability (RAS)&lt;br /&gt;
religion, OS/2&lt;br /&gt;
remote procedure call&lt;br /&gt;
request packet&lt;br /&gt;
resident functions&lt;br /&gt;
resources, manipulating&lt;br /&gt;
return()&lt;br /&gt;
ring 3&lt;br /&gt;
ring transition&lt;br /&gt;
ROM BIOS&lt;br /&gt;
root directory&lt;br /&gt;
rotational latency&lt;br /&gt;
runtime dynamic linking&lt;br /&gt;
runtime library&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
S&lt;br /&gt;
----&lt;br /&gt;
scheduler&lt;br /&gt;
   preemptive&lt;br /&gt;
   priority&lt;br /&gt;
   semaphore&lt;br /&gt;
SCP-DOS&lt;br /&gt;
screen device, handle for&lt;br /&gt;
screen groups&lt;br /&gt;
   multiple&lt;br /&gt;
   using in 3x box&lt;br /&gt;
screen images, manipulating&lt;br /&gt;
screen-save operation&lt;br /&gt;
screen switch&lt;br /&gt;
screen updates, requirements for&lt;br /&gt;
Seattle Computer Products&lt;br /&gt;
sector aligned calls&lt;br /&gt;
sectors&lt;br /&gt;
   blocks of&lt;br /&gt;
security, dynamic link&lt;br /&gt;
seek pointer&lt;br /&gt;
segment arithmetic&lt;br /&gt;
segment fault&lt;br /&gt;
segments&lt;br /&gt;
   80286 architecture&lt;br /&gt;
   80386 architecture&lt;br /&gt;
   data&lt;br /&gt;
   dynamic link&lt;br /&gt;
   executing from data&lt;br /&gt;
   global data&lt;br /&gt;
   huge&lt;br /&gt;
   internally shared&lt;br /&gt;
   nonswappable&lt;br /&gt;
   pure&lt;br /&gt;
   sharing&lt;br /&gt;
   stack&lt;br /&gt;
   status and information&lt;br /&gt;
segment selectors&lt;br /&gt;
segment swapping&lt;br /&gt;
semaphore handles&lt;br /&gt;
semaphores&lt;br /&gt;
   data integrity and&lt;br /&gt;
   file system name space and&lt;br /&gt;
   orphaned&lt;br /&gt;
   RAM&lt;br /&gt;
   recovering&lt;br /&gt;
   scheduling&lt;br /&gt;
   system&lt;br /&gt;
serial multitasking&lt;br /&gt;
session manager&lt;br /&gt;
SetSignalHandler&lt;br /&gt;
shared memory&lt;br /&gt;
   giveaway&lt;br /&gt;
   named&lt;br /&gt;
sharing segments&lt;br /&gt;
side effects&lt;br /&gt;
   controlling&lt;br /&gt;
   dynamic link&lt;br /&gt;
   protection&lt;br /&gt;
signal handler address&lt;br /&gt;
signal handlers&lt;br /&gt;
signaling&lt;br /&gt;
signals&lt;br /&gt;
   holding&lt;br /&gt;
SIGTERM signal&lt;br /&gt;
single-tasking&lt;br /&gt;
single-tasking environment, containing errors in&lt;br /&gt;
software design, OS/2 concepts of&lt;br /&gt;
software tools approach&lt;br /&gt;
speed execution&lt;br /&gt;
stable environment&lt;br /&gt;
stack frames&lt;br /&gt;
stacks, thread&lt;br /&gt;
stack segments&lt;br /&gt;
standard error. See STDERR&lt;br /&gt;
standard file handles&lt;br /&gt;
standard input. See STDIN&lt;br /&gt;
standard output. See STDOUT&lt;br /&gt;
standards, software&lt;br /&gt;
static data segments, adding&lt;br /&gt;
static links&lt;br /&gt;
status and information, segment&lt;br /&gt;
STDERR&lt;br /&gt;
STDIN&lt;br /&gt;
   compatibility with presentation manager&lt;br /&gt;
STDIN/STDOUT mechanism, I/O&lt;br /&gt;
STDOUT&lt;br /&gt;
   compatibility with presentation manager&lt;br /&gt;
strings, environment&lt;br /&gt;
suballocation, memory&lt;br /&gt;
subroutine library&lt;br /&gt;
subroutines, dynamic link packages as&lt;br /&gt;
subsystems&lt;br /&gt;
   dynamic link&lt;br /&gt;
   special support&lt;br /&gt;
subtask model&lt;br /&gt;
subtrees, command&lt;br /&gt;
   controlling&lt;br /&gt;
swapped-out memory&lt;br /&gt;
SWAPPER.DAT&lt;br /&gt;
swapping&lt;br /&gt;
   segment&lt;br /&gt;
system&lt;br /&gt;
   conventions&lt;br /&gt;
   crashes&lt;br /&gt;
   diagnosis&lt;br /&gt;
   File Table (SFT)&lt;br /&gt;
   memory layout&lt;br /&gt;
   security, debuggers and&lt;br /&gt;
   semaphores&lt;br /&gt;
system, hanging the&lt;br /&gt;
systems&lt;br /&gt;
   closed and open&lt;br /&gt;
   file&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
T&lt;br /&gt;
----&lt;br /&gt;
task See also processes&lt;br /&gt;
task-time thread&lt;br /&gt;
technological breakthroughs&lt;br /&gt;
TEMP&lt;br /&gt;
terminate and stay resident mechanism&lt;br /&gt;
thrashing&lt;br /&gt;
thread 1&lt;br /&gt;
thread death&lt;br /&gt;
thread of execution&lt;br /&gt;
thread ID&lt;br /&gt;
threads&lt;br /&gt;
   background&lt;br /&gt;
   captive&lt;br /&gt;
   collisions of&lt;br /&gt;
   dynamic linking and&lt;br /&gt;
   foreground and background processing with&lt;br /&gt;
   foreground and interactive&lt;br /&gt;
   from different processes&lt;br /&gt;
   I/O-bound&lt;br /&gt;
   interrupt-time&lt;br /&gt;
   low priority&lt;br /&gt;
   multiple&lt;br /&gt;
   organizing programs using&lt;br /&gt;
   performance characteristics of&lt;br /&gt;
   priority categories of&lt;br /&gt;
   problems with multiple&lt;br /&gt;
   switching the CPU among&lt;br /&gt;
   task-time&lt;br /&gt;
   working sets and&lt;br /&gt;
thread stacks&lt;br /&gt;
throughput balancing&lt;br /&gt;
time-critical priority category&lt;br /&gt;
time intervals&lt;br /&gt;
timer services&lt;br /&gt;
time slice&lt;br /&gt;
timing, thread&lt;br /&gt;
tools, software&lt;br /&gt;
TOPS-10&lt;br /&gt;
TREE&lt;br /&gt;
tree, process&lt;br /&gt;
tree-structured file system&lt;br /&gt;
Trojan programs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
U&lt;br /&gt;
----&lt;br /&gt;
UNIX&lt;br /&gt;
   naming conventions in&lt;br /&gt;
   ptrace facility&lt;br /&gt;
upper- and lowercase&lt;br /&gt;
upward compatibility&lt;br /&gt;
user interface&lt;br /&gt;
   graphical&lt;br /&gt;
   presentation manager&lt;br /&gt;
   VIO&lt;br /&gt;
utilization&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
V&lt;br /&gt;
----&lt;br /&gt;
variables, local&lt;br /&gt;
versions, future OS/2&lt;br /&gt;
video hardware, accessing&lt;br /&gt;
VIO,&lt;br /&gt;
   choosing between presentation manager and&lt;br /&gt;
   dynamic link entry points&lt;br /&gt;
   graphics under&lt;br /&gt;
   subsystem&lt;br /&gt;
   user interface&lt;br /&gt;
VioGetBuf&lt;br /&gt;
VioModeWait&lt;br /&gt;
VioSavRedrawWait&lt;br /&gt;
VioScrLock&lt;br /&gt;
virtual circuit, network&lt;br /&gt;
virtual display device&lt;br /&gt;
virtualization&lt;br /&gt;
   device&lt;br /&gt;
virtualizing the environment&lt;br /&gt;
virtual memory&lt;br /&gt;
   concepts of&lt;br /&gt;
   paged&lt;br /&gt;
virtual real mode (80386)&lt;br /&gt;
volume, disk&lt;br /&gt;
volume ID&lt;br /&gt;
volume-oriented operations&lt;br /&gt;
Von Neumann, John&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
W&lt;br /&gt;
----&lt;br /&gt;
windowing interface&lt;br /&gt;
working directories&lt;br /&gt;
working set&lt;br /&gt;
WORM drives&lt;br /&gt;
writethrough&lt;br /&gt;
WYSIWYG&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
X&lt;br /&gt;
----&lt;br /&gt;
XENIX&lt;br /&gt;
&lt;br /&gt;
Z&lt;br /&gt;
----&lt;br /&gt;
zero-terminated strings&lt;br /&gt;
&lt;br /&gt;
[[Category: OS/2]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=Inside_OS/2&amp;diff=36400</id>
		<title>Inside OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=Inside_OS/2&amp;diff=36400"/>
				<updated>2025-06-13T12:40:29Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* 4  Multitasking */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the text from the book.&lt;br /&gt;
&lt;br /&gt;
INSIDE OS/2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
INSIDE OS/2 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Gordon Letwin &lt;br /&gt;
Chief Architect, Systems Software, Microsoft(R)&lt;br /&gt;
&lt;br /&gt;
Foreword by Bill Gates &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
PUBLISHED BY&lt;br /&gt;
Microsoft Press&lt;br /&gt;
A Division of Microsoft Corporation&lt;br /&gt;
16011 NE 36th Way, Box 97017, Redmond, Washington 98073-9717&lt;br /&gt;
&lt;br /&gt;
Copyright (C) 1988 by Microsoft Press&lt;br /&gt;
All rights reserved. No part of the contents of this book may be&lt;br /&gt;
reproduced or transmitted in any form or by any means without the written&lt;br /&gt;
permission of the publisher.&lt;br /&gt;
&lt;br /&gt;
Library of Congress Cataloging in Publication Data&lt;br /&gt;
Letwin, Gordon.&lt;br /&gt;
Inside OS/2.&lt;br /&gt;
&lt;br /&gt;
Includes index.&lt;br /&gt;
1. MS OS/2 (Computer operating system) I. Title.&lt;br /&gt;
II. Title: Inside OS/Two.&lt;br /&gt;
QA76.76.063L48    1988    005.4'46    87-31579&lt;br /&gt;
ISBN 1-55615-117-9&lt;br /&gt;
&lt;br /&gt;
Printed and bound in the United States of America&lt;br /&gt;
&lt;br /&gt;
1 2 3 4 5 6 7 8 9 MLML 8 9 0 9 8&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade in the United States by Harper &amp;amp; Row.&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade in Canada by General Publishing Company, Ltd.&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade outside the United States and Canada by&lt;br /&gt;
Penguin Books Ltd.&lt;br /&gt;
&lt;br /&gt;
Penguin Books Ltd., Harmondsworth, Middlesex, England&lt;br /&gt;
Penguin Books Australia Ltd., Ringwood, Victoria, Australia&lt;br /&gt;
Penguin Books N.Z. Ltd., 182-190 Wairau Road, Auckland 10, New Zealand&lt;br /&gt;
&lt;br /&gt;
British Cataloging in publication Data available&lt;br /&gt;
&lt;br /&gt;
Editor: Patricia Pratt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                              Dedication&lt;br /&gt;
&lt;br /&gt;
                               To R.P.W.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Contents&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Foreword by Bill Gates&lt;br /&gt;
&lt;br /&gt;
Introduction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part I: The Project&lt;br /&gt;
&lt;br /&gt;
Chapter 1.  History of the Project&lt;br /&gt;
             1.1  MS-DOS version 1.0&lt;br /&gt;
             1.2  MS-DOS version 2.0&lt;br /&gt;
             1.3  MS-DOS version 3.0&lt;br /&gt;
             1.4  MS-DOS version 4.0&lt;br /&gt;
&lt;br /&gt;
Chapter 2.  Goals and Compatibility Issues&lt;br /&gt;
             2.1  Goals&lt;br /&gt;
                   2.1.1  Graphical User Interface&lt;br /&gt;
                   2.1.2  Multitasking&lt;br /&gt;
                   2.1.3  Memory Management&lt;br /&gt;
                   2.1.4  Protection&lt;br /&gt;
                   2.1.5  Encapsulation&lt;br /&gt;
                   2.1.6  Interprocess Communication (IPC)&lt;br /&gt;
                   2.1.7  Direct Device Access&lt;br /&gt;
             2.2  Compatibility Issues&lt;br /&gt;
                   2.2.1  Real Mode vs Protect Mode&lt;br /&gt;
                   2.2.2  Running Applications in Real (Compatibility) Mode&lt;br /&gt;
                           2.2.2.1  Memory Utilization&lt;br /&gt;
                           2.2.2.2  File Locking&lt;br /&gt;
                           2.2.2.3  Network Piggybacking&lt;br /&gt;
                   2.2.3  Popular Function Compatibility&lt;br /&gt;
                   2.2.4  Downward Compatibility&lt;br /&gt;
                           2.2.4.1  Family API&lt;br /&gt;
                           2.2.4.2  Network Server-Client Compatibility&lt;br /&gt;
&lt;br /&gt;
Chapter 3.  The OS/2 Religion&lt;br /&gt;
             3.1  Maximum Flexibility&lt;br /&gt;
             3.2  Stable Environment&lt;br /&gt;
                   3.2.1  Memory Protection&lt;br /&gt;
                   3.2.2  Side-Effects Protection&lt;br /&gt;
             3.3  Localization of Errors&lt;br /&gt;
             3.4  Software Tools Approach&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part II: The Architecture&lt;br /&gt;
&lt;br /&gt;
Chapter 4.  Multitasking&lt;br /&gt;
             4.1  Subtask Model&lt;br /&gt;
                   4.1.1  Standard File Handles&lt;br /&gt;
                   4.1.2  Anonymous Pipes&lt;br /&gt;
                   4.1.3  Details, Details&lt;br /&gt;
             4.2  PIDs and Command Subtrees&lt;br /&gt;
             4.3  DosExecPgm&lt;br /&gt;
             4.4  DosCWait&lt;br /&gt;
             4.5  Control of Child Tasks and Command Subtrees&lt;br /&gt;
                   4.5.1  DosKillProcess&lt;br /&gt;
                   4.5.2  DosSetPriority&lt;br /&gt;
&lt;br /&gt;
Chapter 5.  Threads and Scheduler/Priorities&lt;br /&gt;
             5.1  Threads&lt;br /&gt;
                   5.1.1  Thread Stacks&lt;br /&gt;
                   5.1.2  Thread Uses&lt;br /&gt;
                           5.1.2.1  Foreground and Background Work&lt;br /&gt;
                           5.1.2.2  Asynchronous Processing&lt;br /&gt;
                           5.1.2.3  Speed Execution&lt;br /&gt;
                           5.1.2.4  Organizing Programs&lt;br /&gt;
                   5.1.3  Interlocking&lt;br /&gt;
                           5.1.3.1  Local Variables&lt;br /&gt;
                           5.1.3.2  RAM Semaphores&lt;br /&gt;
                           5.1.3.3  DosSuspendThread&lt;br /&gt;
                           5.1.3.4  DosEnterCritSec/DosExitCritSec&lt;br /&gt;
                   5.1.4  Thread 1&lt;br /&gt;
                   5.1.5  Thread Death&lt;br /&gt;
                   5.1.6  Performance Characteristics&lt;br /&gt;
             5.2  Scheduler/Priorities&lt;br /&gt;
                   5.2.1  General Priority Category&lt;br /&gt;
                           5.2.1.1  Background Subcategory&lt;br /&gt;
                           5.2.1.2  Foreground and Interactive&lt;br /&gt;
                                     Subcategories&lt;br /&gt;
                           5.2.1.3  Throughput Balancing&lt;br /&gt;
                   5.2.2  Time-Critical Priority Category&lt;br /&gt;
                   5.2.3  Force Background Priority Category&lt;br /&gt;
                   5.2.4  Setting Process/Thread Priorities&lt;br /&gt;
&lt;br /&gt;
Chapter 6.  The User Interface&lt;br /&gt;
             6.1  VIO User Interface&lt;br /&gt;
             6.2  The Presentation Manager User Interface&lt;br /&gt;
             6.3  Presentation Manager and VIO Compatibility&lt;br /&gt;
&lt;br /&gt;
Chapter 7.  Dynamic Linking&lt;br /&gt;
             7.1  Static Linking&lt;br /&gt;
             7.2  Loadtime Dynamic Linking&lt;br /&gt;
             7.3  Runtime Dynamic Linking&lt;br /&gt;
             7.4  Dynlinks, Processes, and Threads&lt;br /&gt;
             7.5  Data&lt;br /&gt;
                   7.5.1  Instance Data&lt;br /&gt;
                   7.5.2  Global Data&lt;br /&gt;
             7.6  Dynamic Link Packages As Subroutines&lt;br /&gt;
             7.7  Subsystems&lt;br /&gt;
                   7.7.1  Special Subsystem Support&lt;br /&gt;
             7.8  Dynamic Links As Interfaces to Other Processes&lt;br /&gt;
             7.9  Dynamic Links As Interfaces to the Kernel&lt;br /&gt;
             7.10 The Architectural Role of Dynamic Links&lt;br /&gt;
             7.11 Implementation Details&lt;br /&gt;
                   7.11.1  Dynlink Data Security&lt;br /&gt;
                   7.11.2  Dynlink Life, Death, and Sharing&lt;br /&gt;
                   7.11.3  Dynlink Side Effects&lt;br /&gt;
             7.12 Dynlink Names&lt;br /&gt;
&lt;br /&gt;
Chapter 8.  File System Name Space&lt;br /&gt;
             8.1  Filenames&lt;br /&gt;
             8.2  Network Access&lt;br /&gt;
             8.3  Name Generation and Compatibility&lt;br /&gt;
             8.4  Permissions&lt;br /&gt;
             8.5  Other Objects in the File System Name Space&lt;br /&gt;
&lt;br /&gt;
Chapter 9.  Memory Management&lt;br /&gt;
             9.1  Protection Model&lt;br /&gt;
             9.2  Memory Management API&lt;br /&gt;
                   9.2.1  Shared Memory&lt;br /&gt;
                   9.2.2  Huge Memory&lt;br /&gt;
                   9.2.3  Executing from Data Segments&lt;br /&gt;
                   9.2.4  Memory Suballocation&lt;br /&gt;
             9.3  Segment Swapping&lt;br /&gt;
                   9.3.1  Swapping Miscellany&lt;br /&gt;
             9.4  Status and Information&lt;br /&gt;
&lt;br /&gt;
Chapter 10. Environment Strings&lt;br /&gt;
&lt;br /&gt;
Chapter 11. Interprocess Communication (IPC)&lt;br /&gt;
             11.1  Shared Memory&lt;br /&gt;
             11.2  Semaphores&lt;br /&gt;
                    11.2.1  Semaphore Recovery&lt;br /&gt;
                    11.2.2  Semaphore Scheduling&lt;br /&gt;
             11.3  Named Pipes&lt;br /&gt;
             11.4  Queues&lt;br /&gt;
             11.5  Dynamic Data Exchange (DDE)&lt;br /&gt;
             11.6  Signals&lt;br /&gt;
             11.7  Combining IPC Forms&lt;br /&gt;
&lt;br /&gt;
Chapter 12. Signals&lt;br /&gt;
&lt;br /&gt;
Chapter 13. The Presentation Manager and VIO&lt;br /&gt;
             13.1  Choosing Between PM and VIO&lt;br /&gt;
             13.2  Background I/O&lt;br /&gt;
             13.3  Graphics Under VIO&lt;br /&gt;
&lt;br /&gt;
Chapter 14. Interactive Programs&lt;br /&gt;
             14.1  I/O Architecture&lt;br /&gt;
             14.2  Ctrl-C and Ctrl-Break Handling&lt;br /&gt;
&lt;br /&gt;
Chapter 15. The File System&lt;br /&gt;
             15.1  The OS/2 File System&lt;br /&gt;
             15.2  Media Volume Management&lt;br /&gt;
             15.3  I/O Efficiency&lt;br /&gt;
&lt;br /&gt;
Chapter 16. Device Monitors, Data Integrity, and Timer Services&lt;br /&gt;
             16.1  Device Monitors&lt;br /&gt;
             16.2  Data Integrity&lt;br /&gt;
                    16.2.1  Semaphores&lt;br /&gt;
                    16.2.2  DosBufReset&lt;br /&gt;
                    16.2.3  Writethroughs&lt;br /&gt;
             16.3  Timer Services&lt;br /&gt;
&lt;br /&gt;
Chapter 17. Device Drivers and Hard Errors&lt;br /&gt;
             17.1  Device Drivers&lt;br /&gt;
                    17.1.1  Device Drivers and OS/2 Communication&lt;br /&gt;
                    17.1.2  Device Driver Programming Model&lt;br /&gt;
                    17.1.3  Device Management&lt;br /&gt;
                    17.1.4  Dual Mode&lt;br /&gt;
             17.2  Hard Errors&lt;br /&gt;
                    17.2.1  The Hard Error Daemon&lt;br /&gt;
                    17.2.2  Application Hard Error Handling&lt;br /&gt;
&lt;br /&gt;
Chapter 18. I/O Privilege Mechanism and Debugging/Ptrace&lt;br /&gt;
             18.1  I/O Privilege Mechanism&lt;br /&gt;
             18.2  Debugging/Ptrace&lt;br /&gt;
&lt;br /&gt;
Chapter 19. The 3x Box&lt;br /&gt;
&lt;br /&gt;
Chapter 20. Family API&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part III: The Future&lt;br /&gt;
&lt;br /&gt;
Chapter 21. The Future&lt;br /&gt;
             21.1  File System&lt;br /&gt;
             21.2  The 80386&lt;br /&gt;
                    21.2.1  Large Segments&lt;br /&gt;
                    21.2.2  Multiple Real Mode Boxes&lt;br /&gt;
                    21.2.3  Full Protection Capability&lt;br /&gt;
                    21.2.4  Other Features&lt;br /&gt;
             21.3  The Next Ten Years&lt;br /&gt;
&lt;br /&gt;
Glossary&lt;br /&gt;
&lt;br /&gt;
Index&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Acknowledgments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Although a book can have a single author, a work such as OS/2 necessarily&lt;br /&gt;
owes its existence to the efforts of a great many people. The architecture&lt;br /&gt;
described herein was hammered out by a joint Microsoft/ IBM design team:&lt;br /&gt;
Ann, Anthony, Carolyn, Ed, Gordon, Jerry, Mark, Mike, Ray, and Ross. This&lt;br /&gt;
team accomplished a great deal of work in a short period of time.&lt;br /&gt;
     The bulk of the credit, and my thanks, go to the engineers who&lt;br /&gt;
designed and implemented the code and made it work. The size of the teams&lt;br /&gt;
involved throughout the project prevents me from listing all the names&lt;br /&gt;
here. It's hard for someone who has not been involved in a software project&lt;br /&gt;
of this scope to imagine the problems, pressure, chaos, and &amp;quot;reality&lt;br /&gt;
shifts&amp;quot; that arise in a never-ending stream. These people deserve great&lt;br /&gt;
credit for their skill and determination in making OS/2 come to pass.&lt;br /&gt;
     Thanks go to the OS/2 development staffers who found time, in the heat&lt;br /&gt;
of the furnace, to review and critique this book: Ian Birrell, Ross Cook,&lt;br /&gt;
Rick Dewitt, Dave Gilman, Vic Heller, Mike McLaughlin, Jeff Parsons, Ray&lt;br /&gt;
Pedrizetti, Robert Reichel, Rajen Shah, Anthony Short, Ben Slivka, Pete&lt;br /&gt;
Stewart, Indira Subramanian, Bryan Willman, and Mark Zbikowski.&lt;br /&gt;
     I'd like to give special thanks to Mark Zbikowski and Aaron Reynolds,&lt;br /&gt;
the &amp;quot;gurus of DOS.&amp;quot; Without their successes there would never have been an&lt;br /&gt;
opportunity for a product such as OS/2.&lt;br /&gt;
     And finally I'd like to thank Bill Gates for creating and captaining&lt;br /&gt;
one hell of a company, thereby making all this possible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Foreword&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 is destined to be a very important piece of software. During the&lt;br /&gt;
next 10 years, millions of programmers and users will utilize this system.&lt;br /&gt;
From time to time they will come across a feature or a limitation and&lt;br /&gt;
wonder why it's there. The best way for them to understand the overall&lt;br /&gt;
philosophy of the system will be to read this book. Gordon Letwin is&lt;br /&gt;
Microsoft's architect for OS/2. In his very clear and sometimes humorous&lt;br /&gt;
way, Gordon has laid out in this book why he included what he did and why&lt;br /&gt;
he didn't include other things.&lt;br /&gt;
     The very first generation of microcomputers were 8-bit machines, such&lt;br /&gt;
as the Commodore Pet, the TRS-80, the Apple II, and the CPM 80 based&lt;br /&gt;
machines. Built into almost all of them was Microsoft's BASIC Interpreter.&lt;br /&gt;
I met Gordon Letwin when I went to visit Heath's personal computer group&lt;br /&gt;
(now part of Zenith). Gordon had written his own BASIC as well as an&lt;br /&gt;
operating system for the Heath system, and he wasn't too happy that his&lt;br /&gt;
management was considering buying someone else's. In a group of about 15&lt;br /&gt;
people, he bluntly pointed out the limitations of my BASIC versus his.&lt;br /&gt;
After Heath licensed my BASIC, I convinced Gordon that Microsoft was the&lt;br /&gt;
place to be if you wanted your great software to be popular, and so he&lt;br /&gt;
became one of Microsoft's first 10 programmers. His first project was to&lt;br /&gt;
single-handedly write a compiler for Microsoft BASIC. He put a sign on his&lt;br /&gt;
door that read&lt;br /&gt;
&lt;br /&gt;
        Do not disturb, feed, poke, tease...the animal&lt;br /&gt;
&lt;br /&gt;
and in 5 months wrote a superb compiler that is still the basis for all our&lt;br /&gt;
BASIC compilers. Unlike the code that a lot of superstar programmers write,&lt;br /&gt;
Gordon's source code is a model of readability and includes precise&lt;br /&gt;
explanations of algorithms and why they were chosen.&lt;br /&gt;
     When the Intel 80286 came along, with its protected mode completely&lt;br /&gt;
separate from its compatible real mode, we had no idea how we were going to&lt;br /&gt;
get at its new capabilities. In fact, we had given up until Gordon came up&lt;br /&gt;
with the patented idea described in this book that has been referred to as&lt;br /&gt;
&amp;quot;turning the car off and on at 60 MPH.&amp;quot; When we first explained the idea to&lt;br /&gt;
Intel and many of its customers, they were sure it wouldn't work. Even&lt;br /&gt;
Gordon wasn't positive it would work until he wrote some test programs that&lt;br /&gt;
proved it did.&lt;br /&gt;
     Gordon's role as an operating systems architect is to overview our&lt;br /&gt;
designs and approaches and make sure they are as simple and as elegant as&lt;br /&gt;
possible. Part of this job includes reviewing people's code. Most&lt;br /&gt;
programmers enjoy having Gordon look over their code and point out how it&lt;br /&gt;
could be improved and simplified. A lot of programs end up about half as&lt;br /&gt;
big after Gordon has explained a better way to write them. Gordon doesn't&lt;br /&gt;
mince words, however, so in at least one case a particularly sensitive&lt;br /&gt;
programmer burst into tears after reading his commentary. Gordon isn't&lt;br /&gt;
content to just look over other people's code. When a particular project&lt;br /&gt;
looks very difficult, he dives in. Currently, Gordon has decided to&lt;br /&gt;
personally write most of our new file system, which will be dramatically&lt;br /&gt;
faster than our present one. On a recent &amp;quot;vacation&amp;quot; he wrote more than 50&lt;br /&gt;
pages of source code.&lt;br /&gt;
     This is Gordon's debut as a book author, and like any good designer he&lt;br /&gt;
has already imagined what bad reviews might say. I think this book is both&lt;br /&gt;
fun and important. I hope you enjoy it as much as I have.&lt;br /&gt;
&lt;br /&gt;
Bill Gates&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Introduction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technological breakthroughs develop in patterns that are distinct from&lt;br /&gt;
patterns of incremental advancements. An incremental advancement--an&lt;br /&gt;
improvement to an existing item--is straightforward and unsurprising. An&lt;br /&gt;
improvement is created; people see the improvement, know what it will do&lt;br /&gt;
for them, and start using it.&lt;br /&gt;
     A major advance without closely related antecedents--a technological&lt;br /&gt;
breakthrough--follows a different pattern. The field of communication is a&lt;br /&gt;
good example. Early in this century, a large infrastructure existed to&lt;br /&gt;
facilitate interpersonal communication. Mail was delivered twice a day, and&lt;br /&gt;
a variety of efficient services relayed messages. A businessman dictated a&lt;br /&gt;
message to his secretary, who gave it to a messenger service. The service&lt;br /&gt;
carried the message to its nearby destination, where a secretary delivered&lt;br /&gt;
it to the recipient.&lt;br /&gt;
     Into this environment came a technological breakthrough--the&lt;br /&gt;
telephone. The invention of the telephone was a breakthrough, not an&lt;br /&gt;
incremental advance, because it provided an entirely new way to communicate.&lt;br /&gt;
It wasn't an improvement over an existing method. That it was&lt;br /&gt;
a breakthrough development impeded its acceptance. Most business people&lt;br /&gt;
considered it a newfangled toy, of little practical use. &amp;quot;What good does it&lt;br /&gt;
do me? By the time I dictate the message, and my secretary writes it down&lt;br /&gt;
and gives it to the mailroom, and they phone the addressee's mailroom, and&lt;br /&gt;
the message is copied--perhaps incorrectly--and delivered to the&lt;br /&gt;
addressee's secretary, it would have been as fast to have it delivered by&lt;br /&gt;
messenger! All my correspondents are close by, and, besides, with&lt;br /&gt;
messengers I don't have to pay someone to sit by the telephone all day in&lt;br /&gt;
case a message comes in.&amp;quot;&lt;br /&gt;
     This is a classic example of the earliest stages of breakthrough&lt;br /&gt;
technology--potential users evaluate it by trying to fit it into present&lt;br /&gt;
work patterns. Our example businessman has not yet realized that he needn't&lt;br /&gt;
write the message down anymore and that it needn't be copied down at the&lt;br /&gt;
destination. He also doesn't realize that the reason his recipients are&lt;br /&gt;
close by is that they have to be for decent messenger delivery. The&lt;br /&gt;
telephone relaxed this requirement, allowing more efficient locations near&lt;br /&gt;
factories and raw materials or where office space was cheaper. But it&lt;br /&gt;
was necessary for the telephone to be accepted before these advantages &lt;br /&gt;
could be realized.&lt;br /&gt;
     Another impedance to the acceptance of a breakthrough technology is&lt;br /&gt;
that the necessary new infrastructure is not in place. A telephone did&lt;br /&gt;
little good if your intended correspondent didn't have one. The nature of&lt;br /&gt;
telephones required a standard; until that standard was set, your&lt;br /&gt;
correspondent might own a phone, but it could be connected to a network&lt;br /&gt;
unreachable by you. Furthermore, because the technology was in its infancy,&lt;br /&gt;
the facilities were crude.&lt;br /&gt;
     These obstacles were not insurmountable. The communications&lt;br /&gt;
requirements of some people were so critical that they were willing to&lt;br /&gt;
invent new procedures and to put up with the problems of the early stages.&lt;br /&gt;
Some people, because of their daring or ambition, used the new system to&lt;br /&gt;
augment their existing system. And finally, because the new technology was&lt;br /&gt;
so powerful, some used it to enhance the existing technology. For example,&lt;br /&gt;
a messenger service might establish several offices with telephone linkage&lt;br /&gt;
between them and use the telephones to speed delivery of short messages by&lt;br /&gt;
phoning them to the office nearest the destination, where they were copied&lt;br /&gt;
down and delivered normally. Using the telephone in this fashion was&lt;br /&gt;
wasteful, but where demand for the old service was high enough, any&lt;br /&gt;
improvement, however &amp;quot;wasteful,&amp;quot; was welcome.&lt;br /&gt;
     After it has a foot in the door, a breakthrough technology is&lt;br /&gt;
unstoppable. After a time, standards are established, the bugs are worked&lt;br /&gt;
out, and, most important, the tool changes its users. Once the telephone&lt;br /&gt;
became available, business and personal practices developed in new&lt;br /&gt;
patterns, patterns that were not considered before because they were not&lt;br /&gt;
possible. Messenger services used to be fast enough, but only because,&lt;br /&gt;
before the telephone, the messenger service was the fastest technology&lt;br /&gt;
available. The telephone changed the life-style of its users.&lt;br /&gt;
     This change in the structure of human activity explains why an&lt;br /&gt;
intelligent person could say, &amp;quot;Telephones are silly gadgets,&amp;quot; and a few&lt;br /&gt;
years later say, &amp;quot;Telephones are indispensable.&amp;quot; This change in the tool&lt;br /&gt;
user--caused by the tool itself--also makes predicting the ultimate effect&lt;br /&gt;
of the new technology difficult. Extrapolating from existing trends is&lt;br /&gt;
wildly inaccurate because the new tool destroys many practices and creates&lt;br /&gt;
wholly unforeseen ones. It's great fun to read early, seemingly silly&lt;br /&gt;
predictions of life in the future and to laugh at the predictors, but the&lt;br /&gt;
predictors were frequently intelligent and educated. Their only mistake was&lt;br /&gt;
in treating the new development as an incremental advance rather than as a&lt;br /&gt;
breakthrough technology. They saw how the new development would improve&lt;br /&gt;
their current practices, but they couldn't see how it would replace those&lt;br /&gt;
practices.&lt;br /&gt;
     Digital computers are an obvious breakthrough technology, and they've&lt;br /&gt;
shared the classic three-stage pattern: &amp;quot;exotic toys,&amp;quot; &amp;quot;limited use,&amp;quot; and&lt;br /&gt;
&amp;quot;indispensable.&amp;quot; Mainframe computers have gone the full route, in the&lt;br /&gt;
milieu of business and scientific computing. IBM's initial estimate of the&lt;br /&gt;
computer market was a few dozen machines. But, as the technology and the&lt;br /&gt;
support infrastructure grew, and as people's ways of working adapted to&lt;br /&gt;
computers, the use of computers grew--from the census bureau, to life&lt;br /&gt;
insurance companies, to payroll systems, and finally to wholly new&lt;br /&gt;
functions such as MIS (Management Information Sciences) systems and airline&lt;br /&gt;
reservation networks.&lt;br /&gt;
     Microcomputers are in the process of a similar development. The&lt;br /&gt;
&amp;quot;exotic toy&amp;quot; stage has already given way to the &amp;quot;limited use&amp;quot; stage. We're&lt;br /&gt;
just starting to develop standards and infrastructure and are only a few&lt;br /&gt;
years from the &amp;quot;indispensable&amp;quot; stage. In anticipation of this stage,&lt;br /&gt;
Microsoft undertook the design and the development of OS/2.&lt;br /&gt;
     Although studying the mainframe computer revolution helps in trying to&lt;br /&gt;
predict the path of the microcomputer revolution, microcomputers are more&lt;br /&gt;
than just &amp;quot;cheap mainframes.&amp;quot; The microcomputer revolution will follow the&lt;br /&gt;
tradition of breakthroughs, creating new needs and new uses that cannot be&lt;br /&gt;
anticipated solely by studying what happened with mainframe systems.&lt;br /&gt;
     This book was written because of the breakthrough nature of the&lt;br /&gt;
microcomputer and the impact of the coming second industrial revolution.&lt;br /&gt;
The designers of OS/2 tried to anticipate, to the greatest extent possible,&lt;br /&gt;
the demands that would be placed on the system when the tool--the personal&lt;br /&gt;
computer--and the tool user reached their new equilibrium. A knowledge of&lt;br /&gt;
MS-DOS and a thorough reading of the OS/2 reference manuals will not, in&lt;br /&gt;
themselves, clarify the key issues of the programming environment that OS/2&lt;br /&gt;
was written to support. This is true not only because of the complexity of&lt;br /&gt;
the product but because many design elements were chosen to provide&lt;br /&gt;
services that from a prebreakthrough perspective--don't seem needed and&lt;br /&gt;
solve problems that haven't yet arisen.&lt;br /&gt;
     Other books provide reference information and detailed how-to&lt;br /&gt;
instructions for writing OS/2 programs. This book describes the underlying&lt;br /&gt;
architectural models that make up OS/2 and discusses how those models are&lt;br /&gt;
expected to meet the foreseen and unforeseen requirements of the oncoming&lt;br /&gt;
office automation revolution. It focuses on the general issues, problems,&lt;br /&gt;
and solutions that all OS/2 programs encounter regardless of the&lt;br /&gt;
programming and interface models that a programmer may employ.&lt;br /&gt;
     As is often the case in a technical discussion, everything in OS/2 is&lt;br /&gt;
interconnected in some fashion to everything else. A discussion on the&lt;br /&gt;
shinbone naturally leads to a discussion of the thighbone and so on. The&lt;br /&gt;
author and the editor of this book have tried hard to group the material&lt;br /&gt;
into a logical progression without redundancy, but the very nature of the&lt;br /&gt;
material makes complete success at this impossible. It's often desirable,&lt;br /&gt;
in fact, to repeat material, perhaps from a different viewpoint or with a&lt;br /&gt;
different emphasis. For these reasons, the index references every mention&lt;br /&gt;
of an item or a topic, however peripheral. Having too many references&lt;br /&gt;
(including a few worthless ones) is far better than having too few&lt;br /&gt;
references. When you're looking for information about a particular subject,&lt;br /&gt;
I recommend that you first consult the contents page to locate the major&lt;br /&gt;
discussion and then peruse the index to pick up references that may appear&lt;br /&gt;
in unexpected places.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part I  The Project&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==1  History of the Project==&lt;br /&gt;
&lt;br /&gt;
Microsoft was founded to realize a vision of a microcomputer on every&lt;br /&gt;
desktop--a vision of the second industrial revolution. The first industrial&lt;br /&gt;
revolution mechanized physical work. Before the eighteenth century, nearly&lt;br /&gt;
all objects were created and constructed by human hands, one at a time.&lt;br /&gt;
With few exceptions, such as animal-powered plowing and cartage, all power&lt;br /&gt;
was human muscle power. The second industrial revolution will mechanize&lt;br /&gt;
routine mental work. Today, on the verge of the revolution, people are&lt;br /&gt;
still doing &amp;quot;thought work,&amp;quot; one piece at a time.&lt;br /&gt;
     Certain tasks--those massive in scope and capable of being rigidly&lt;br /&gt;
described, such as payroll calculations--have been automated, but the&lt;br /&gt;
majority of &amp;quot;thought work&amp;quot; is still done by people, not by computers. We&lt;br /&gt;
have the computer equivalent of the plow horse, but we don't have the&lt;br /&gt;
computer equivalent of the electric drill or the washing machine.&lt;br /&gt;
     Of course, computers cannot replace original thought and creativity&lt;br /&gt;
(at least, not in the near future) any more than machines have replaced&lt;br /&gt;
design and creativity in the physical realm. But the bulk of the work in a&lt;br /&gt;
white-collar office involves routine manipulation of information. The&lt;br /&gt;
second industrial revolution will relieve us of the &amp;quot;grunt work&amp;quot;--routine&lt;br /&gt;
data manipulation, analysis, and decisions--freeing us to deal only with&lt;br /&gt;
those situations that require human judgment.&lt;br /&gt;
     Most people do not recognize the inevitability of the second&lt;br /&gt;
industrial revolution. They can't see how a computer could do 75 percent of&lt;br /&gt;
their work because their work was structured in the absence of computers.&lt;br /&gt;
But, true to the pattern for technological breakthroughs, the tremendous&lt;br /&gt;
utility of the microcomputer will transform its users and the way they do&lt;br /&gt;
their work.&lt;br /&gt;
     For example, a great deal of work is hard to computerize because the&lt;br /&gt;
input information arrives on paper and it would take too long to type it&lt;br /&gt;
all in. Ten years ago, computer proponents envisioned the &amp;quot;paperless&lt;br /&gt;
office&amp;quot; as a solution for this problem: All material would be generated by&lt;br /&gt;
computer and then transferred electronically or via disk to other&lt;br /&gt;
computers. Offices are certainly becoming more paperless, and the arrival&lt;br /&gt;
of powerful networking systems will accelerate this, but paper continues to&lt;br /&gt;
be a very useful medium. As a result, in recent years growth has occurred&lt;br /&gt;
in another direction--incorporating paper as a computer input and output&lt;br /&gt;
device. Powerful laser printers, desktop publishing systems, and optical&lt;br /&gt;
scanners and optical character recognition will make it more practical to&lt;br /&gt;
input from and output to paper.&lt;br /&gt;
     Although the founders of Microsoft fully appreciate the impact of the&lt;br /&gt;
second industrial revolution, nobody can predict in detail how the&lt;br /&gt;
revolution will unfold. Instead, Microsoft bases its day-to-day decisions&lt;br /&gt;
on dual sets of goals: short-term goals, which are well known, and a long-&lt;br /&gt;
term goal--our vision of the automated office. Each decision has to meet&lt;br /&gt;
our short-term goals, and it must be consonant with our long-term vision, a&lt;br /&gt;
vision that becomes more precise as the revolution progresses.&lt;br /&gt;
     When 16-bit microprocessors were first announced, Microsoft knew that&lt;br /&gt;
the &amp;quot;iron&amp;quot; was now sufficiently powerful to begin to realize this vision.&lt;br /&gt;
But a powerful computer environment requires both strong iron and a&lt;br /&gt;
sophisticated operating system. The iron was becoming available, but the&lt;br /&gt;
operating system that had been standard for 8-bit microprocessors was&lt;br /&gt;
inadequate. This is when and why Microsoft entered the operating system&lt;br /&gt;
business: We knew that we needed a powerful operating system to realize our&lt;br /&gt;
vision and that the only way to guarantee its existence and suitability was&lt;br /&gt;
to write it ourselves.&lt;br /&gt;
&lt;br /&gt;
===1.1  MS-DOS version 1.0===&lt;br /&gt;
&lt;br /&gt;
MS-DOS got its start when IBM asked Microsoft to develop a disk operating&lt;br /&gt;
system for a new product that IBM was developing, the IBM Personal Computer&lt;br /&gt;
(PC). Microsoft's only operating system product at that time was XENIX, a&lt;br /&gt;
licensed version of AT&amp;amp;T's UNIX  operating system. XENIX/UNIX requires a&lt;br /&gt;
processor with memory management and protection facilities. Because the&lt;br /&gt;
8086/8088 processors had neither and because XENIX/UNIX memory&lt;br /&gt;
requirements--modest by minicomputer standards of the day--were nonetheless&lt;br /&gt;
large by microcomputer standards, a different operating system had to be&lt;br /&gt;
developed.&lt;br /&gt;
     CP/M-80, developed by Digital Research, Incorporated (DRI), had been&lt;br /&gt;
the standard 8-bit operating system, and the majority of existing&lt;br /&gt;
microcomputer software had been written to run on CP/M-80. For this reason,&lt;br /&gt;
Microsoft decided to make MS-DOS version 1.0 as compatible as possible with&lt;br /&gt;
CP/M-80. The 8088 processor would not run the existing CP/M-80 programs,&lt;br /&gt;
which were written for the 8080 processor, but because 8080 programs could&lt;br /&gt;
be easily and semiautomatically converted to run on the 8088, Microsoft&lt;br /&gt;
felt that minimizing adaptation hassles by minimizing operating system&lt;br /&gt;
incompatibility would hasten the acceptance of MS-DOS on the IBM PC.&lt;br /&gt;
     A major software product requires a great deal of development time,&lt;br /&gt;
and IBM was in a hurry to introduce its PC. Microsoft, therefore, looked&lt;br /&gt;
around for a software product to buy that could be built onto to create MS-&lt;br /&gt;
DOS version 1.0. Such a product was found at Seattle Computer Products. Tim&lt;br /&gt;
Paterson, an engineer there, had produced a CP/M-80 &amp;quot;clone,&amp;quot; called SCP-&lt;br /&gt;
DOS, that ran on the 8088 processor. Microsoft purchased full rights to&lt;br /&gt;
this product and to its source code and used the product as a starting&lt;br /&gt;
point in the development of MS-DOS version 1.0.&lt;br /&gt;
     MS-DOS version 1.0 was released in August 1981. Available only for the&lt;br /&gt;
IBM PC, it consisted of 4000 lines of assembly-language source code and ran&lt;br /&gt;
in 8 KB of memory. MS-DOS version 1.1 was released in 1982 and worked with&lt;br /&gt;
double-sided 320 KB floppy disks.&lt;br /&gt;
     Microsoft's goal was that MS-DOS version 1.0 be highly CP/M&lt;br /&gt;
compatible, and it was. Ironically, it was considerably more compatible&lt;br /&gt;
than DRI's own 8088 product, CP/M-86. As we shall see later, this CP/M&lt;br /&gt;
compatibility, necessary at the time, eventually came to cause Microsoft&lt;br /&gt;
engineers a great deal of difficulty.&lt;br /&gt;
&lt;br /&gt;
===1.2  MS-DOS version 2.0===&lt;br /&gt;
&lt;br /&gt;
In early 1982, IBM disclosed to Microsoft that it was developing a hard&lt;br /&gt;
disk-based personal computer, the IBM XT. Microsoft began work on MS-DOS&lt;br /&gt;
version 2.0 to provide support for the new disk hardware. Changes were&lt;br /&gt;
necessary because MS-DOS, in keeping with its CP/M-80 compatible heritage,&lt;br /&gt;
had been designed for a floppy disk environment. A disk could contain only&lt;br /&gt;
one directory, and that directory could contain a maximum of 64 files. This&lt;br /&gt;
decision was reasonable when first made because floppy disks held only&lt;br /&gt;
about 180 KB of data.&lt;br /&gt;
     For the hard disk, however, the 64-file limit was much too small, and&lt;br /&gt;
using a single directory to manage perhaps hundreds of files was&lt;br /&gt;
clumsy. Therefore, the MS-DOS version 2.0 developers--Mark Zbikowski, Aaron&lt;br /&gt;
Reynolds, Chris Peters, and Nancy Panners--added a hierarchical file&lt;br /&gt;
system. In a hierarchical file system a directory can contain other&lt;br /&gt;
directories and files. In turn, those directories can con- tain a mixture&lt;br /&gt;
of files and directories and so on. A hierarchically designed system starts&lt;br /&gt;
with the main, or &amp;quot;root,&amp;quot; directory, which itself can contain (as seen in&lt;br /&gt;
Figure 1-1) a tree-structured collection of files and directories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                directory WORK&lt;br /&gt;
                           ADMIN&lt;br /&gt;
               Ú────────── BUDGET&lt;br /&gt;
               ³           CALENDAR&lt;br /&gt;
               ³           LUNCH.DOC&lt;br /&gt;
               ³           PAYROLL ─────────¿&lt;br /&gt;
               ³           PHONE.LST        ³&lt;br /&gt;
               ³           SCHED.DOC        ³&lt;br /&gt;
               ³                            ³&lt;br /&gt;
           directory BUDGET              directory PAYROLL&lt;br /&gt;
                      MONTH                         ADDRESSES&lt;br /&gt;
                      QUARTER                       MONTHLY&lt;br /&gt;
                      YEAR                          NAMES&lt;br /&gt;
                      1986 ────────¿                RETIRED ─────¿&lt;br /&gt;
            Ú──────── 1985         ³                VACATION     ³&lt;br /&gt;
            ³                      ³                WEEKLY       ³&lt;br /&gt;
            ³                      ³                             ³&lt;br /&gt;
     directory 1985         directory 1986             directory RETIRED&lt;br /&gt;
            ³                      ³                             ³&lt;br /&gt;
&lt;br /&gt;
Figure 1-1.  A directory tree hierarchy. Within the WORK directory&lt;br /&gt;
are five files (ADMIN, CALENDAR, LUNCH.DOC, PHONE.LST, SCHED.DOC) and two&lt;br /&gt;
subdirectories (BUDGET, PAYROLL). Each subdirectory has its own&lt;br /&gt;
subdirectories.&lt;br /&gt;
&lt;br /&gt;
===1.3  MS-DOS version 3.0====&lt;br /&gt;
&lt;br /&gt;
MS-DOS version 3.0 was introduced in August 1984, when IBM announced the&lt;br /&gt;
IBM PC/AT. The AT contains an 80286 processor, but, when running DOS, it&lt;br /&gt;
uses the 8086 emulation mode built into the chip and runs as a &amp;quot;fast 8086.&amp;quot;&lt;br /&gt;
The chip's extended addressing range and its protected mode architecture&lt;br /&gt;
sit unused.&lt;br /&gt;
1. Products such as Microsoft XENIX/UNIX run on the PC/AT and&lt;br /&gt;
compatibles, using the processor's protected mode. This is possible&lt;br /&gt;
because XENIX/UNIX and similar systems had no preexisting real mode&lt;br /&gt;
applications that needed to be supported.&lt;br /&gt;
1&lt;br /&gt;
     MS-DOS version 3.1 was released in November 1984 and contained&lt;br /&gt;
networking support. In January 1986, MS-DOS version 3.2--a minor revision--&lt;br /&gt;
was released. This version supported 3-1/2-inch floppy disks and contained&lt;br /&gt;
the formatting function for a device in the device driver. In 1987, MS-DOS&lt;br /&gt;
version 3.3 followed; the primary enhancement of this release was support&lt;br /&gt;
for the IBM PS/2 and compatible hardware.&lt;br /&gt;
&lt;br /&gt;
===1.4  MS-DOS version 4.0====&lt;br /&gt;
&lt;br /&gt;
Microsoft started work on a multitasking version of MS-DOS in January 1983.&lt;br /&gt;
At the time, it was internally called MS-DOS version 3.0. When a new&lt;br /&gt;
version of the single-tasking MS-DOS was shipped under the name MS-DOS&lt;br /&gt;
version 3.0, the multitasking version was renamed, internally, to MS-DOS&lt;br /&gt;
version 4.0. A version of this product--a multitasking, real-mode only MS-&lt;br /&gt;
DOS--was shipped as MS-DOS version 4.0. Because MS-DOS version 4.0 runs&lt;br /&gt;
only in real mode, it can run on 8088 and 8086 machines as well as on 80286&lt;br /&gt;
machines. The limitations of the real mode environment make MS-DOS version&lt;br /&gt;
4.0 a specialized product. Although MS-DOS version 4.0 supports full&lt;br /&gt;
preemptive multitasking, system memory is limited to the 640 KB available&lt;br /&gt;
in real mode, with no swapping.&lt;br /&gt;
2. It is not feasible to support general purpose swapping without&lt;br /&gt;
memory management hardware that is unavailable in 8086 real mode.&lt;br /&gt;
2 This means that all processes have to fit&lt;br /&gt;
into the single 640 KB memory area. Only one MS-DOS version 3.x compatible&lt;br /&gt;
real mode application can be run; the other processes must be special MS-&lt;br /&gt;
DOS version 4.0 processes that understand their environment and cooperate&lt;br /&gt;
with the operating system to coexist peacefully with the single MS-DOS&lt;br /&gt;
version 3.x real mode application.&lt;br /&gt;
     Because of these restrictions, MS-DOS version 4.0 was not intended for&lt;br /&gt;
general release, but as a platform for specific OEMs to support extended PC&lt;br /&gt;
architectures. For example, a powerful telephone management system could be&lt;br /&gt;
built into a PC by using special MS-DOS version 4.0 background processes to&lt;br /&gt;
control the telephone equipment. The resulting machine could then be&lt;br /&gt;
marketed as a &amp;quot;compatible MS-DOS 3 PC with a built-in superphone.&amp;quot;&lt;br /&gt;
     Although MS-DOS version 4.0 was released as a special OEM product, the&lt;br /&gt;
project--now called MS-DOS version 5.0--continued. The goal was to take&lt;br /&gt;
advantage of the protected mode of the 80286 to provide full general&lt;br /&gt;
purpose multitasking without the limitations--as seen in MS-DOS version&lt;br /&gt;
4.0--of a real-mode only environment. Soon, Microsoft and IBM signed a&lt;br /&gt;
Joint Development Agreement that provided for the design and development of&lt;br /&gt;
MS-DOS version 5.0 (now called CP/DOS). The agreement is complex, but it&lt;br /&gt;
basically provides for joint development and then subsequent joint&lt;br /&gt;
ownership, with both companies holding full rights to the resulting&lt;br /&gt;
product.&lt;br /&gt;
     As the project neared completion, the marketing staffs looked at&lt;br /&gt;
CP/DOS, nee DOS 5, nee DOS 4, nee DOS 3, and decided that it needed...you&lt;br /&gt;
guessed it...a name change. As a result, the remainder of this book will&lt;br /&gt;
discuss the design and function of an operating system called OS/2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==2  Goals and Compatibility Issues===&lt;br /&gt;
&lt;br /&gt;
OS/2 is similar to traditional multitasking operating systems in many ways:&lt;br /&gt;
It provides multitasking, scheduling, disk management, memory management,&lt;br /&gt;
and so on. But it is also different in many ways, because a personal&lt;br /&gt;
computer is very different from a multiuser minicomputer. The designers of&lt;br /&gt;
OS/2 worked from two lists: a set of goals and a set of compatibility&lt;br /&gt;
issues. This chapter describes those goals and compatibility issues and&lt;br /&gt;
provides the context for a later discussion of the design itself.&lt;br /&gt;
&lt;br /&gt;
===2.1  Goals===&lt;br /&gt;
&lt;br /&gt;
The primary goal of OS/2 is to be the ideal office automation operating&lt;br /&gt;
system. The designers worked toward this goal by defining the following&lt;br /&gt;
intermediate and, seemingly, contradictory goals:&lt;br /&gt;
&lt;br /&gt;
* To provide device-independent graphics drivers without introducing any significant overhead.&lt;br /&gt;
* To allow applications direct access to high-bandwidth peripherals but maintain the ability to virtualize or apportion the usage of those peripherals.&lt;br /&gt;
* To provide multitasking without reducing the performance and response available from a single-tasking system.&lt;br /&gt;
* To provide a fully customized environment for each program and its descendants yet also provide a standard environment that is unaffected by other programs in the system.&lt;br /&gt;
* To provide a protected environment to ensure system stability yet one that will not constrain applications from the capabilities they have under nonprotected systems.&lt;br /&gt;
&lt;br /&gt;
====2.1.1  Graphical User Interface====&lt;br /&gt;
By far the fastest and easiest way people receive information is through&lt;br /&gt;
the eye. We are inherently visual creatures. Our eyes receive information&lt;br /&gt;
rapidly; they can &amp;quot;seek&amp;quot; to the desired information and &amp;quot;zoom&amp;quot; their&lt;br /&gt;
attention in and out with small, rapid movements of the eye muscles. A&lt;br /&gt;
large part of the human brain is dedicated to processing visual&lt;br /&gt;
information. People abstract data and meaning from visual material--from&lt;br /&gt;
text to graphics to motion pictures--hundreds of times faster than from any&lt;br /&gt;
other material.&lt;br /&gt;
     As a result, if an office automation system is to provide quantities&lt;br /&gt;
of information quickly and in a form in which it can be easily absorbed, a&lt;br /&gt;
powerful graphics capability is essential. Such capabilities were rare in&lt;br /&gt;
earlier minicomputer operating systems because of the huge memory and&lt;br /&gt;
compute power costs of high-resolution displays. Today's microcomputers&lt;br /&gt;
have the memory to contain the display information, they have the CPU power&lt;br /&gt;
to create and manipulate that information, and they have no better use for&lt;br /&gt;
those capabilities than to support powerful, easy-to-use graphical&lt;br /&gt;
applications.&lt;br /&gt;
     Graphics can take many forms--pictures, tables, drawings, charts--&lt;br /&gt;
perhaps incorporating color and even animation. All are powerful adjuncts&lt;br /&gt;
to the presentation of alphanumeric text. Graphical applications don't&lt;br /&gt;
necessarily employ charts and pictures. A WYSIWYG (What You See Is What You&lt;br /&gt;
Get) typesetting program may display only text, but if that text is drawn&lt;br /&gt;
in graphics mode, the screen can show any font, in any type size, with&lt;br /&gt;
proportional spacing, kerning, and so on.&lt;br /&gt;
     The screen graphics components of OS/2 need to be device independent;&lt;br /&gt;
that is, an application must display the proper graphical &amp;quot;picture&amp;quot; without&lt;br /&gt;
relying on the specific characteristics of any particular graphical display&lt;br /&gt;
interface board. Each year the state of the art in displays gets better; it&lt;br /&gt;
would be extremely shortsighted to tie applications to a particular display&lt;br /&gt;
board, for no matter how good it is, within a couple of years it will be&lt;br /&gt;
obsolete.&lt;br /&gt;
     The idea is to encapsulate device-specific code by requiring that each&lt;br /&gt;
device come with a software package called a device driver. The application&lt;br /&gt;
program issues commands for a generic device, and the device driver then&lt;br /&gt;
translates those commands to fit the characteristics of the actual device.&lt;br /&gt;
The result is that the manufacturer of a new graphics display board needs&lt;br /&gt;
to write an appropriate device driver and supply it with the board. The&lt;br /&gt;
application program doesn't need to know anything about the device, and the&lt;br /&gt;
device driver doesn't need to know anything about the application, other&lt;br /&gt;
than the specification of the common interface they share. This common&lt;br /&gt;
interface describes a virtual display device; the general technique of&lt;br /&gt;
hiding a complicated actual situation behind a simple, standard interface&lt;br /&gt;
is called &amp;quot;virtualization.&amp;quot;&lt;br /&gt;
     Figure 2-1 shows the traditional operating system device driver&lt;br /&gt;
architecture. Applications don't directly call device drivers because&lt;br /&gt;
device drivers need to execute in the processor's privilege mode to&lt;br /&gt;
manipulate their device; the calling application must run in normal mode.&lt;br /&gt;
In the language of the 80286/80386 family of processors, privilege mode is&lt;br /&gt;
called ring 0, and normal mode is called ring 3. The operating system&lt;br /&gt;
usually acts as a middleman: It receives the request, validates it, deals&lt;br /&gt;
with issues that arise when there is only one device but multiple&lt;br /&gt;
applications are using it, and then passes the request to the device&lt;br /&gt;
driver. The device driver's response or return of data takes the reverse&lt;br /&gt;
path, winding its way through the operating system and back to the&lt;br /&gt;
application program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         Application                      Kernel             Device driver&lt;br /&gt;
          (ring 3)                       (ring 0)               (ring 0)&lt;br /&gt;
──────────────────────────────────────────────────────────────────────────&lt;br /&gt;
                                   Request&lt;br /&gt;
                             ³     packet:                  ³ Ú──────────¿&lt;br /&gt;
                                Ú──────────¿    Device        ³  Device  ³&lt;br /&gt;
                             ³  ³          ³    descriptor ÄÅÄ´  driver  ³&lt;br /&gt;
Call deviceio (arg 1...argn)    ³ function ³    #1            À──────────Ù&lt;br /&gt;
                             ³  ³   arg1   ³          ú     ³&lt;br /&gt;
        ───────────────────────�³    ù     ÃÄ¿        ú&lt;br /&gt;
                  Ring       ³  ³   argn   ³ ³        ú     ³&lt;br /&gt;
                  transition    ³          ³ ³        ú       Ú──────────¿&lt;br /&gt;
                             ³  À──────────Ù À� Device      ³ ³  Device  ³&lt;br /&gt;
                                                descriptor ÄÄÄ´  driver  ³&lt;br /&gt;
                             ³                  #N          ³ À──────────Ù&lt;br /&gt;
&lt;br /&gt;
Figure 2-1.  Traditional device driver architecture. When an application&lt;br /&gt;
wants to do device I/0, it calls the operating system, which builds a&lt;br /&gt;
device request packet, determines the target device, and delivers the&lt;br /&gt;
packet. The device driver's response follows the opposite route through the&lt;br /&gt;
kernel back to the application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This approach solves the device virtualization problem, but at a cost&lt;br /&gt;
in performance. The interface between the application and the device driver&lt;br /&gt;
is narrow; that is, the form messages can take is usually restricted.&lt;br /&gt;
Commonly, the application program is expected to build a request block that&lt;br /&gt;
contains all the information and data that the device driver needs to&lt;br /&gt;
service the request; the actual call to the operating system is simply&lt;br /&gt;
&amp;quot;pass this request block to the device driver.&amp;quot; Setting up this block takes&lt;br /&gt;
time, and breaking it down in the device driver again takes time. More time&lt;br /&gt;
is spent on the reply; the device driver builds, the operating system&lt;br /&gt;
copies, and the application breaks down. Further time is spent calling down&lt;br /&gt;
through the internal layers of the operating system, examining and copying&lt;br /&gt;
the request block, routing to the proper device driver, and so forth.&lt;br /&gt;
Finally, the transition between rings (privilege and normal mode) is also&lt;br /&gt;
time-consuming, and two such transitions occur--to privilege mode and back&lt;br /&gt;
again.&lt;br /&gt;
     Such a cost in performance was acceptable in nongraphics-based systems&lt;br /&gt;
because, typically, completely updating a screen required only 1920 (or&lt;br /&gt;
fewer) bytes of data. Today's graphics devices can require 256,000 bytes or&lt;br /&gt;
more per screen update, and future devices will be even more demanding.&lt;br /&gt;
Furthermore, applications may expect to update these high-resolution&lt;br /&gt;
screens several times a second.&lt;br /&gt;
1. It's not so much the amount of data that slows the traditional&lt;br /&gt;
device driver model, but the number of requests and replies. Disk&lt;br /&gt;
devices work well through the traditional model because disk&lt;br /&gt;
requests tend to be large (perhaps 40,000 bytes). Display devices&lt;br /&gt;
tend to be written piecemeal--a character, a word, or a line at a&lt;br /&gt;
time. It is the high rate of these individual calls that slows the&lt;br /&gt;
device driver model, not the number of bytes written to the screen.&lt;br /&gt;
1&lt;br /&gt;
     OS/2 needed powerful, device-independent graphical display support&lt;br /&gt;
that had a wide, efficient user interface--one that did not involve ring&lt;br /&gt;
transitions, the operating system, or other unnecessary overhead. As we'll&lt;br /&gt;
see later, OS/2 meets this requirement by means of a mechanism called&lt;br /&gt;
dynamic linking.&lt;br /&gt;
&lt;br /&gt;
====2.1.2  Multitasking====&lt;br /&gt;
To be really useful, a personal computer must be able to do more than one&lt;br /&gt;
chore at a time--an ability called multitasking. We humans multitask all&lt;br /&gt;
the time. For example, you may be involved in three projects at work, be&lt;br /&gt;
halfway through a novel, and be taking Spanish lessons. You pick up each&lt;br /&gt;
task in turn, work on it for a while, and then put it down and work on&lt;br /&gt;
something else. This is called serial multitasking. Humans can also do some&lt;br /&gt;
tasks simultaneously, such as driving a car and talking. This is called&lt;br /&gt;
parallel multitasking.&lt;br /&gt;
     In a serial multitasking computer environment, a user can switch&lt;br /&gt;
activities at will, working for a while at each. For example, a user can&lt;br /&gt;
leave a word-processing program without terminating it, consult a&lt;br /&gt;
spreadsheet, and then return to the waiting word-processing program. Or, if&lt;br /&gt;
someone telephones and requests an appointment, the user can switch from a&lt;br /&gt;
spreadsheet to a scheduling program, consult the calendar, and then return&lt;br /&gt;
to the spreadsheet.&lt;br /&gt;
     The obvious value of multitasking makes it another key requirement for&lt;br /&gt;
OS/2: Many programs or applications can run at the same time. But&lt;br /&gt;
multitasking is useful for more than just switching between applications:&lt;br /&gt;
Parallel multitasking allows an application to do work by itself--perhaps&lt;br /&gt;
print a large file or recalculate a large spreadsheet--while the user&lt;br /&gt;
works with another application. Because OS/2 supports full multitasking,&lt;br /&gt;
it can execute programs in addition to the application(s) the user is&lt;br /&gt;
running, providing advanced services such as network mail without&lt;br /&gt;
interrupting or interfering with the user's work.&lt;br /&gt;
2. Present-day machines contain only one CPU, so at any instant&lt;br /&gt;
only one program can be executing. At this microscopic level,&lt;br /&gt;
OS/2 is a serial multitasking system. It is not considered serial&lt;br /&gt;
multitasking, however, because it performs preemptive scheduling.&lt;br /&gt;
At any time, OS/2 can remove the CPU from the currently running&lt;br /&gt;
program and assign it to another program. Because these&lt;br /&gt;
rescheduling events may occur many times a second at totally&lt;br /&gt;
unpredictable places within the running programs, it is accurate&lt;br /&gt;
to view the system as if each program truly runs simultaneously&lt;br /&gt;
with other programs.&lt;br /&gt;
&lt;br /&gt;
====2.1.3  Memory Management====&lt;br /&gt;
Multitasking is fairly easy to achieve. All that's necessary is a source of&lt;br /&gt;
periodic hardware interrupts, such as a clock circuit, to enable the&lt;br /&gt;
operating system to effect a &amp;quot;context switch,&amp;quot; or to reschedule. To be&lt;br /&gt;
useful, however, a multitasking system needs an effective memory management&lt;br /&gt;
system. For example, a user wants to run two applications on a system. Each&lt;br /&gt;
starts at as low a memory location as possible to maximize the amount of&lt;br /&gt;
memory it can use. Unfortunately, if the system supports multitasking and&lt;br /&gt;
the user tries to run both applications simultaneously, each attempts to&lt;br /&gt;
use the same memory cells, and the applications destroy each other.&lt;br /&gt;
     A memory management system solves this problem by using special&lt;br /&gt;
hardware facilities built into 80286/80386 processors (for example, IBM&lt;br /&gt;
PC/AT machines and compatibles and 80386-based machines).&lt;br /&gt;
3. Earlier 8086/8088 processors used in PCs, PC/XTs, and similar&lt;br /&gt;
machines lack this hardware. This is why earlier versions of MS-DOS&lt;br /&gt;
didn't support multitasking and why OS/2 won't run on such machines.&lt;br /&gt;
3 The memory&lt;br /&gt;
management system uses the hardware to virtualize the memory of the machine&lt;br /&gt;
so that each program appears to have all memory to itself.&lt;br /&gt;
     Memory management is more than keeping programs out of each other's&lt;br /&gt;
way. The system must track the owner or user(s) of each piece of memory so&lt;br /&gt;
that the memory space can be reclaimed when it is no longer needed, even if&lt;br /&gt;
the owner of the memory neglects to explicitly release it. Some operating&lt;br /&gt;
systems avoid this work by assuming that no application will ever fail to&lt;br /&gt;
return its memory when done or by examining the contents of memory and&lt;br /&gt;
ascertaining from those contents whether the memory is still being used.&lt;br /&gt;
(This is called &amp;quot;garbage collection.&amp;quot;) Neither alternative was acceptable&lt;br /&gt;
for OS/2. Because OS/2 will run a variety of programs written by many&lt;br /&gt;
vendors, identifying free memory by inspection is impossible, and assuming&lt;br /&gt;
perfection from the applications themselves is unwise. Tracking the&lt;br /&gt;
ownership and usage of memory objects can be complex, as we shall see in&lt;br /&gt;
our discussion on dynamic link libraries.&lt;br /&gt;
     Finally, the memory management system must manage memory overcommit.&lt;br /&gt;
The multitasking capability of OS/2 allows many applications to be run&lt;br /&gt;
simultaneously; thus, RAM must hold all these programs and their data.&lt;br /&gt;
Although RAM becomes cheaper every year, buying enough to hold all of one's&lt;br /&gt;
applications at one time is still prohibitive. Furthermore, although RAM&lt;br /&gt;
prices continue to drop, the memory requirements of applications will&lt;br /&gt;
continue to rise. Consequently, OS/2 must contain an effective mechanism to&lt;br /&gt;
allocate more memory to the running programs than in fact physically&lt;br /&gt;
exists. This is called memory overcommit.&lt;br /&gt;
     OS/2 accomplishes this magic with the classic technique of swapping.&lt;br /&gt;
OS/2 periodically examines each segment of memory to see if it has been&lt;br /&gt;
used recently. When a request is made for RAM and none is available, the&lt;br /&gt;
least recently used segment of memory (the piece that has been unused for&lt;br /&gt;
the longest time) is written to a disk file, and the RAM it occupied is&lt;br /&gt;
made available. Later, if a program attempts to use the swapped-out memory,&lt;br /&gt;
a &amp;quot;memory not present&amp;quot; fault occurs. OS/2 intercepts the fault and reloads&lt;br /&gt;
the memory information from the disk into memory, swapping out some other&lt;br /&gt;
piece of memory, if necessary, to make room. This whole process is&lt;br /&gt;
invisible to the application that uses the swapped memory area; the only&lt;br /&gt;
impact is a small delay while the needed memory is read back from the&lt;br /&gt;
disk.&lt;br /&gt;
     The fundamental concepts of memory overcommit and swapping are simple,&lt;br /&gt;
but a good implementation is not. OS/2 must choose the right piece of&lt;br /&gt;
memory to swap out, and it must swap it out efficiently. Not only must care&lt;br /&gt;
be taken that the swap file doesn't grow too big and consume all the free&lt;br /&gt;
disk space but also that deadlocks don't occur. For example, if all the&lt;br /&gt;
disk swap space is filled, it may be impossible to swap into RAM a piece of&lt;br /&gt;
memory because no free RAM is available, and OS/2 can't free up RAM because&lt;br /&gt;
no swap space exists to write it out to. Naturally, the greater the load on&lt;br /&gt;
the system, the slower the system will be, but the speed degradation must&lt;br /&gt;
be gradual and acceptable, and the system must never deadlock.&lt;br /&gt;
     The issues involved in memory management and the memory management&lt;br /&gt;
facilities that OS/2 provides are considerably more complex than this&lt;br /&gt;
overview. We'll return to the subject of memory management in detail in&lt;br /&gt;
Chapter 9.&lt;br /&gt;
&lt;br /&gt;
====2.1.4  Protection====&lt;br /&gt;
I mentioned earlier that OS/2 cannot trust applications to behave&lt;br /&gt;
correctly. I was talking about memory management, but this concern&lt;br /&gt;
generalizes into the next key requirement: OS/2 must protect applications &lt;br /&gt;
from the proper or improper actions of other applications that may be&lt;br /&gt;
running on the system.&lt;br /&gt;
     Because OS/2 will run applications and programs from a variety of&lt;br /&gt;
vendors, every user's machine will execute a different set of applications,&lt;br /&gt;
running in different ways on different data. No software vendor can fully&lt;br /&gt;
test a product in all possible environments. This makes it critical that an&lt;br /&gt;
error on the part of one program does not crash the system or some other&lt;br /&gt;
program or, worse, corrupt data and not bring down the system. Even if no&lt;br /&gt;
data is damaged, system crashes are unacceptable. Few users have the&lt;br /&gt;
background or equipment even to diagnose which application caused the&lt;br /&gt;
problem.&lt;br /&gt;
     Furthermore, malice, as well as accident, is a concern. Microsoft's&lt;br /&gt;
vision of the automated office cannot be realized without a system that is&lt;br /&gt;
secure from deliberate attack. No corporation will be willing to base its&lt;br /&gt;
operations on a computer network when any person in that company--with the&lt;br /&gt;
help of some &amp;quot;cracker&amp;quot; programs bought from the back of a computer&lt;br /&gt;
magazine--can see and change personnel or payroll files, billing notices,&lt;br /&gt;
or strategic planning memos.&lt;br /&gt;
     Today, personal computers are being used as a kind of super-&lt;br /&gt;
sophisticated desk calculator. As such, data is secured by traditional&lt;br /&gt;
means--physical locks on office doors, computers, or file cabinets that&lt;br /&gt;
store disks. Users don't see a need for a protected environment because&lt;br /&gt;
their machine is physically protected. This lack of interest in protection&lt;br /&gt;
is another example of the development of a breakthrough technology.&lt;br /&gt;
Protection is not needed because the machine is secure and operates on data&lt;br /&gt;
brought to it by traditional office channels. In the future, however,&lt;br /&gt;
networked personal computers will become universal and will act both as the&lt;br /&gt;
processors and as the source (via the network) of the data. Thus, in this&lt;br /&gt;
role, protection is a key requirement and is indeed a prerequisite for&lt;br /&gt;
personal computers to assume that central role.&lt;br /&gt;
&lt;br /&gt;
====2.1.5  Encapsulation====&lt;br /&gt;
When a program runs in a single-tasking system such as MS-DOS version 3.x,&lt;br /&gt;
its environment is always constant--consisting of the machine and MS-DOS.&lt;br /&gt;
The program can expect to get the same treatment from the system and to&lt;br /&gt;
provide exactly the same interaction with the user each time it runs. In a&lt;br /&gt;
multitasking environment, however, many programs can be running. Each&lt;br /&gt;
program can be using files and devices in different ways; each program can&lt;br /&gt;
be using the mouse, each program can have the screen display in a different&lt;br /&gt;
mode, and so on. OS/2 must encapsulate, or isolate, each program so that it&lt;br /&gt;
&amp;quot;sees&amp;quot; a uniform environment each time it runs, even though the computer&lt;br /&gt;
environment itself may be different each time.&lt;br /&gt;
&lt;br /&gt;
====2.1.6  Interprocess Communication (IPC)====&lt;br /&gt;
In a single-tasking environment such as MS-DOS version 3.x, each program&lt;br /&gt;
stands alone. If it needs a particular service not provided by the&lt;br /&gt;
operating system, it must provide that service itself. For example, every&lt;br /&gt;
application that needs a sort facility must contain its own.&lt;br /&gt;
     Likewise, if a spreadsheet needs to access values from a database, it&lt;br /&gt;
must contain the code to do so. This extra code complicates the spreadsheet&lt;br /&gt;
program, and it ties the program to a particular database product or&lt;br /&gt;
format. A user might be unable to switch to a better product because the&lt;br /&gt;
spreadsheet is unable to understand the new database's file formats.&lt;br /&gt;
     A direct result of such a stand-alone environment is the creation of&lt;br /&gt;
very large and complex &amp;quot;combo&amp;quot; packages such as Lotus Symphony. Because&lt;br /&gt;
every function that the user may want must be contained within one program,&lt;br /&gt;
vendors supply packages that attempt to contain everything.&lt;br /&gt;
     In practice, such chimeric programs tend to be large and cumbersome,&lt;br /&gt;
and their individual functional components (spreadsheets, word processors,&lt;br /&gt;
and databases, for example) are generally more difficult to use and less&lt;br /&gt;
sophisticated than individual applications that specialize in a single&lt;br /&gt;
function.&lt;br /&gt;
     The stand-alone environment forces the creation of larger and more&lt;br /&gt;
complex programs, each of which typically understands only its own file&lt;br /&gt;
formats and works poorly, if at all, with data produced by other programs.&lt;br /&gt;
This vision of personal computer software growing monstrous until&lt;br /&gt;
collapsing from its own weight brings about another OS/2 requirement:&lt;br /&gt;
Applications must be able to communicate, easily and efficiently, with&lt;br /&gt;
other applications.&lt;br /&gt;
     More specifically, an application must be able to find (or name) the&lt;br /&gt;
application that provides the information or service that the client needs,&lt;br /&gt;
and it must be able to establish efficient communication with the provider&lt;br /&gt;
program without requiring that either application have specific knowledge&lt;br /&gt;
of the internal workings of the other. Thus, a spreadsheet program must be&lt;br /&gt;
able to communicate with a database program and access the values it needs.&lt;br /&gt;
The spreadsheet program is therefore not tied to any particular database&lt;br /&gt;
program but can work with any database system that recognizes OS/2 IPC&lt;br /&gt;
requests.&lt;br /&gt;
     Applications running under OS/2 not only retain their full power as&lt;br /&gt;
individual applications but also benefit from cross-application&lt;br /&gt;
communication. Furthermore, the total system can be enhanced by upgrading&lt;br /&gt;
an application that provides services to others. When a new, faster, or&lt;br /&gt;
more fully featured database package is installed, not only is the user's&lt;br /&gt;
database application improved but the database functions of the spreadsheet&lt;br /&gt;
program are improved as well.&lt;br /&gt;
     The OS/2 philosophy is that no program should reinvent the wheel.&lt;br /&gt;
Programs should be written to offer their services to other programs and to&lt;br /&gt;
take advantage of the offered services of other programs. The result is a&lt;br /&gt;
maximally effective and efficient system.&lt;br /&gt;
&lt;br /&gt;
====2.1.7  Direct Device Access====&lt;br /&gt;
Earlier, we discussed the need for a high-performance graphical interface&lt;br /&gt;
and the limitations of the traditional device driver architecture. OS/2&lt;br /&gt;
contains a built-in solution for the screen graphical interface, but what&lt;br /&gt;
about other, specialized devices that may require a higher bandwidth&lt;br /&gt;
interface than device drivers provide? The one sure prediction about the&lt;br /&gt;
future of a technological breakthrough is that you can't fully predict it.&lt;br /&gt;
For this reason, the final key requirement for OS/2 is that it contain an&lt;br /&gt;
&amp;quot;escape hatch&amp;quot; in anticipation of devices that have performance needs too&lt;br /&gt;
great for a device driver model.&lt;br /&gt;
     OS/2 provides this expandability by allowing applications direct&lt;br /&gt;
access to hardware devices--both the I/O ports and any device memory. This&lt;br /&gt;
must be done, of course, in such a way that only devices which are intended&lt;br /&gt;
to be used in this fashion can be so accessed. Applications are prevented&lt;br /&gt;
from using this access technique on devices that are being managed by the&lt;br /&gt;
operating system or by a device driver. This facility gives applications&lt;br /&gt;
the ability to take advantage of special nonstandard hardware such as OCRs&lt;br /&gt;
(Optical Character Readers), digitizer tablets, Fax equipment, special&lt;br /&gt;
purpose graphics cards, and the like.&lt;br /&gt;
&lt;br /&gt;
===2.2  Compatibility Issues===&lt;br /&gt;
&lt;br /&gt;
But OS/2 has to do more than meet the goals we've discussed: It must be&lt;br /&gt;
compatible with 8086/8088 and 80286 architecture, and it must be compatible&lt;br /&gt;
with MS-DOS. By far the easiest solution would have been to create a new&lt;br /&gt;
multitasking operating system that would not be compatible with MS-DOS, but&lt;br /&gt;
such a system is unacceptable. Potential users may be excited about the new&lt;br /&gt;
system, but they won't buy it until applications are available. Application&lt;br /&gt;
writers may likewise be excited, but they won't adapt their products for it&lt;br /&gt;
until the system has sold enough copies to gain significant market share.&lt;br /&gt;
This &amp;quot;catch 22&amp;quot; means that the only people who will buy the new operating&lt;br /&gt;
system are the developers' mothers, and they probably get it at a discount&lt;br /&gt;
anyway.&lt;br /&gt;
&lt;br /&gt;
====2.2.1  Real Mode vs Protect Mode====&lt;br /&gt;
The first real mode compatibility issue relates to the design of the 80286&lt;br /&gt;
microprocessor--the &amp;quot;brain&amp;quot; of an MS-DOS computer. This chip has two&lt;br /&gt;
incompatible modes--real (compatibility) mode and protect mode. Real mode&lt;br /&gt;
is designed to run programs in exactly the same manner as they run on the&lt;br /&gt;
8086/8088 processor. In other words, when the 80286 is in real mode, it&lt;br /&gt;
&amp;quot;looks&amp;quot; to the operating system and programs exactly like a fast&lt;br /&gt;
8088.&lt;br /&gt;
     But the designers of the 80286 wanted it to be more than a fast 8088.&lt;br /&gt;
They wanted to add such features as memory management, memory protection,&lt;br /&gt;
and the ring protection mechanism, which allows the operating system to&lt;br /&gt;
protect one application from another. They weren't able to do this while&lt;br /&gt;
remaining fully compatible with the earlier 8088 chip, so they added a&lt;br /&gt;
second mode to the 80286--protect mode. When the processor is running in&lt;br /&gt;
protect mode, it provides these important new features, but it will not run&lt;br /&gt;
most programs written for the 8086/8088.&lt;br /&gt;
     In effect, an 80286 is two separate microprocessors in one package. It&lt;br /&gt;
can act like a very fast 8088--compatible, but with no new capabilities--or&lt;br /&gt;
it can act like an 80286--incompatible, but providing new features.&lt;br /&gt;
Unfortunately, the designers of the chip didn't appreciate the importance&lt;br /&gt;
of compatibility in the MS-DOS marketplace, and they designed the 80286 so&lt;br /&gt;
that it can run in either mode but can't switch back and forth at will.&lt;br /&gt;
4. The 80286 initializes itself in real mode. There is a command&lt;br /&gt;
to switch from real mode to protect mode, but there is no command&lt;br /&gt;
to switch back.&lt;br /&gt;
&lt;br /&gt;
In other words, an 80286 was designed to run only old 8086/8088 programs,&lt;br /&gt;
or it can run only new 80286 style programs, but never both at the same&lt;br /&gt;
time.&lt;br /&gt;
     In summary, OS/2 was required to do something that the 80286 was not&lt;br /&gt;
designed for--execute both 8086/8088 style (real mode) and 80286 style&lt;br /&gt;
(protect) mode programs at the same time. The existence of this book should&lt;br /&gt;
lead you to believe that this problem was solved, and indeed it was.&lt;br /&gt;
&lt;br /&gt;
====2.2.2  Running Applications in Real (Compatibility) Mode====&lt;br /&gt;
Solving the real mode vs protect mode problem, however, presented other&lt;br /&gt;
problems. In general, the problems came about because the real mode&lt;br /&gt;
programs were written for MS-DOS versions 2.x or 3.x, both of which are&lt;br /&gt;
single-tasking environments.&lt;br /&gt;
     Although MS-DOS is normally spoken of as an operating system, it could&lt;br /&gt;
just as accurately be called a &amp;quot;system executive.&amp;quot; Because it runs in an&lt;br /&gt;
unprotected environment, applications are free to edit interrupt vectors,&lt;br /&gt;
manipulate peripherals, and in general take over from MS-DOS wherever they&lt;br /&gt;
wish. This flexibility is one reason for the success of MS-DOS; if MS-DOS&lt;br /&gt;
doesn't offer the service your program needs, you can always help yourself.&lt;br /&gt;
Developers were free to explore new possibilities, often with great&lt;br /&gt;
success. Most applications view MS-DOS as a program loader and as a set of&lt;br /&gt;
file system subroutines, interfacing directly with the hardware for all&lt;br /&gt;
their other needs, such as intercepting interrupt vectors, editing disk&lt;br /&gt;
controller parameter tables, and so on.&lt;br /&gt;
     It may seem that if a popular application &amp;quot;pokes&amp;quot; the operating system&lt;br /&gt;
and otherwise engages in unsavory practices that the authors or users of&lt;br /&gt;
the application will suffer because a future release, such as OS/2, may not&lt;br /&gt;
run the application correctly. To the contrary, the market dynamics state&lt;br /&gt;
that the application has now set a standard, and it's the operating system&lt;br /&gt;
developers who suffer because they must support that standard. Usually,&lt;br /&gt;
that &amp;quot;standard&amp;quot; operating system interface is not even known; a great deal&lt;br /&gt;
of experimentation is necessary to discover exactly which undocumented side&lt;br /&gt;
effects, system internals, and timing relationships the application is&lt;br /&gt;
dependent on.&lt;br /&gt;
     Offering an MS-DOS-compatible Applications Program Interface (API)&lt;br /&gt;
provides what we call level 1 compatibility. Allowing applications to&lt;br /&gt;
continue to manipulate system hardware provides level 2 compatibility.&lt;br /&gt;
Level 3 issues deal with providing an execution environment that supports&lt;br /&gt;
the hidden assumptions that programs written for a single-tasking&lt;br /&gt;
environment may make. Three are discussed below by way of illustration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.1  Memory Utilization&lt;br /&gt;
The existing real mode applications that OS/2 must support were written for&lt;br /&gt;
an environment in which no other programs are running. As a result,&lt;br /&gt;
programs typically consume all available memory in the system in the belief&lt;br /&gt;
that, since no other program is around to use any leftover memory, they&lt;br /&gt;
might as well use it all. If a program doesn't ask for all available memory&lt;br /&gt;
at first, it may ask for the remainder at some later time. Such a&lt;br /&gt;
subsequent request could never be refused under MS-DOS versions 2.x and&lt;br /&gt;
3.x, and applications were written to depend on this. Therefore, such a&lt;br /&gt;
request must be satisfied under OS/2 to maintain full compatibility.&lt;br /&gt;
     Even the manner of a memory request depends on single-tasking&lt;br /&gt;
assumptions. Programs typically ask for all memory in two steps. First,&lt;br /&gt;
they ask for the maximum amount of memory that an 8088 can provide--1 MB.&lt;br /&gt;
The application's programmer knew that the request would be refused because&lt;br /&gt;
1 MB is greater than the 640 KB maximum supported by MS-DOS; but when MS-&lt;br /&gt;
DOS refuses the request, it tells the application exactly how much memory&lt;br /&gt;
is available. Programs then ask for that amount of memory. The programmer&lt;br /&gt;
knew that MS-DOS would not refuse the second memory request for&lt;br /&gt;
insufficient memory because when MS-DOS responded to the first request it&lt;br /&gt;
told the application exactly how much memory was available. Consequently,&lt;br /&gt;
programmers rarely included a check for an &amp;quot;insufficient memory&amp;quot; error from&lt;br /&gt;
the second call.&lt;br /&gt;
     This shortcut introduces problems in the OS/2 multitasking&lt;br /&gt;
environment. When OS/2 responded to the first too-large request, it would&lt;br /&gt;
return the amount of memory available at that exact moment. Other programs&lt;br /&gt;
are simultaneously executing; by the time our real mode program makes its&lt;br /&gt;
second request, some more memory may have been given out, and the second&lt;br /&gt;
request may also be too large. It won't do any good for OS/2 to respond&lt;br /&gt;
with an error code, however, because the real mode application does not&lt;br /&gt;
check for one (it was written in the belief that it is impossible to get&lt;br /&gt;
such a code on the second call). The upshot is that even if OS/2 refused&lt;br /&gt;
the second call the real mode application would assume that it had been&lt;br /&gt;
given the memory, would use it, and in the process would destroy the other&lt;br /&gt;
program(s) that were the true owners of that memory.&lt;br /&gt;
     Obviously, OS/2 must resolve this and similar issues to support the&lt;br /&gt;
existing base of real mode applications.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.2  File Locking&lt;br /&gt;
Because multitasking systems run more than one program at the same time,&lt;br /&gt;
two programs may try to write or to modify the same file at the same time.&lt;br /&gt;
Or one may try to read a file while another is changing that file's&lt;br /&gt;
contents. Multitasking systems usually solve this problem by means of a&lt;br /&gt;
file-locking mechanism, which allows one program to temporarily prevent&lt;br /&gt;
other programs from reading and/or writing a particular file.&lt;br /&gt;
     An application may find that a file it is accessing has been locked by&lt;br /&gt;
some other application in the system. In such a situation, OS/2 normally&lt;br /&gt;
returns a &amp;quot;file locked&amp;quot; error code, and the application typically gives up&lt;br /&gt;
or waits and retries the operation later. OS/2 cannot return a &amp;quot;file&lt;br /&gt;
locked&amp;quot; error to an old-style real mode application, though, because when&lt;br /&gt;
the application was written (for MS-DOS versions 2.x or 3.x) no such error&lt;br /&gt;
code existed because no such error was possible. Few real mode applications&lt;br /&gt;
even bother to check their read and write operations for error codes, and&lt;br /&gt;
those that do wouldn't &amp;quot;understand&amp;quot; the error code and wouldn't handle it&lt;br /&gt;
correctly.&lt;br /&gt;
     OS/2 cannot compromise the integrity of the file-locking mechanism by&lt;br /&gt;
allowing the real mode application to ignore locks, but it cannot report&lt;br /&gt;
that the file is locked to the application either. OS/2 must determine the&lt;br /&gt;
proper course of action and then take that action on behalf of the real&lt;br /&gt;
mode application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.3  Network Piggybacking&lt;br /&gt;
Running under MS-DOS version 3.1, an application can use an existing&lt;br /&gt;
network virtual circuit to communicate with an application running on the&lt;br /&gt;
server machine to which the virtual circuit is connected. This is called&lt;br /&gt;
&amp;quot;piggybacking&amp;quot; the virtual circuit because the applications on each end are&lt;br /&gt;
borrowing a circuit that the network redirector established for other&lt;br /&gt;
purposes. The two sets of programs can use a single circuit for two&lt;br /&gt;
different purposes without confusion under MS-DOS version 3.1 because of&lt;br /&gt;
its single-tasking nature. The redirector only uses the circuit when the&lt;br /&gt;
application calls MS-DOS to perform a network function. Because the CPU is&lt;br /&gt;
inside MS-DOS, it can't be executing the application software that sends&lt;br /&gt;
private messages, which leaves the circuit free for use by the&lt;br /&gt;
redirector.&lt;br /&gt;
     Conversely, if the application is sending its own private messages--&lt;br /&gt;
piggybacking--then it can't be executing MS-DOS, and therefore the&lt;br /&gt;
redirector code (which is built into MS-DOS) can't be using the virtual&lt;br /&gt;
circuit.&lt;br /&gt;
     This is no longer the case in OS/2. OS/2 is a multitasking system, and&lt;br /&gt;
one application can use the redirector at the same time that the real mode&lt;br /&gt;
application is piggybacking the circuit. OS/2 must somehow interlock access&lt;br /&gt;
to network virtual circuits so that multiple users of a network virtual&lt;br /&gt;
circuit do not conflict.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.3  Popular Function Compatibility&lt;br /&gt;
We've discussed some issues of binary compatibility, providing applications&lt;br /&gt;
the internal software interfaces they had in MS-DOS. This is because it is&lt;br /&gt;
vitally important that existing applications run correctly, unchanged,&lt;br /&gt;
under the new operating system.&lt;br /&gt;
5. An extremely high degree of compatibility is required for&lt;br /&gt;
virtually any application to run because a typical application&lt;br /&gt;
uses a great many documented and undocumented interfaces and&lt;br /&gt;
features of the earlier system. If any one of those interfaces&lt;br /&gt;
is not supplied, the application will not run correctly.&lt;br /&gt;
Consequently, we cannot provide 90 percent compatibility and&lt;br /&gt;
expect to run 90 percent of existing applications; 99.9 percent&lt;br /&gt;
compatibility is required for such a degree of success.&lt;br /&gt;
5 OS/2 also needs to provide functional&lt;br /&gt;
compatibility; it has to allow the creation of protect mode applications&lt;br /&gt;
that provide the functions that users grew to know and love in real mode&lt;br /&gt;
applications.&lt;br /&gt;
     This can be difficult because many popular applications (for example,&lt;br /&gt;
&amp;quot;terminate and stay resident loadable helper&amp;quot; routines such as SideKick)&lt;br /&gt;
were written for a single-tasking, unprotected environment without regard&lt;br /&gt;
to the ease with which their function could be provided in a protected&lt;br /&gt;
environment. For example, a popular application may implement some of its&lt;br /&gt;
features by patching (that is, editing) MS-DOS itself. This cannot be&lt;br /&gt;
allowed in OS/2 (the reason is discussed in Chapter 4), so OS/2 must&lt;br /&gt;
provide alternative mechanisms for protect mode applications to provide&lt;br /&gt;
services that users have grown to expect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4  Downward Compatibility&lt;br /&gt;
So far, our discussion on compatibility has focused exclusively on upward&lt;br /&gt;
compatibility--old programs must run in the new system but not vice versa.&lt;br /&gt;
Downward compatibility--running new programs under MS-DOS--is also&lt;br /&gt;
important. Developers are reluctant to write OS/2-only applications until&lt;br /&gt;
OS/2 has achieved major penetration of the market, yet this very&lt;br /&gt;
unavailability of software slows such penetration. If it's possible to&lt;br /&gt;
write applications that take advantage of OS/2's protect mode yet also run&lt;br /&gt;
unchanged under MS-DOS version 3.x, ISVs (Independent Software Vendors) can&lt;br /&gt;
write their products for OS/2 without locking themselves out of the&lt;br /&gt;
existing MS-DOS market.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4.1  Family API&lt;br /&gt;
To provide downward compatibility for applications, OS/2 designers&lt;br /&gt;
integrated a Family&lt;br /&gt;
6.Family refers to the MS-DOS/OS/2 family of&lt;br /&gt;
operating systems.&lt;br /&gt;
6 Applications Program Interface (Family API) into the&lt;br /&gt;
OS/2 project. The Family API provides a standard execution environment&lt;br /&gt;
under MS-DOS version 3.x and OS/2. Using the Family API, a programmer can&lt;br /&gt;
create an application that uses a subset of OS/2 functions (but a superset&lt;br /&gt;
of MS-DOS version 3.x functions) and that runs in a binary compatible&lt;br /&gt;
fashion under MS-DOS version 3.x and OS/2. In effect, some OS/2 functions&lt;br /&gt;
can be retrofitted into an MS-DOS version 3.x environment by means of the&lt;br /&gt;
Family API.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4.2  Network Server-Client Compatibility&lt;br /&gt;
Another important form of upward and downward compatibility is the network&lt;br /&gt;
system. You can expect any OS/2 system to be on a network, communicating&lt;br /&gt;
not only with MS-DOS 3.x systems but, one day, with a new version of OS/2&lt;br /&gt;
as well. The network interface must be simultaneously upwardly and&lt;br /&gt;
downwardly compatible with all past and future versions of networking MS-&lt;br /&gt;
DOS.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==3  The OS/2 Religion==&lt;br /&gt;
&lt;br /&gt;
Religion, in the context of software design, is a body of beliefs about&lt;br /&gt;
design rights and design wrongs. A particular design is praised or&lt;br /&gt;
criticized on the basis of fact--it is small or large, fast or slow--and&lt;br /&gt;
also on the basis of religion--it is good or bad, depending on how well it&lt;br /&gt;
obeys the religious precepts. Purpose and consistency underlie the design&lt;br /&gt;
religion as a whole; its influence is felt in every individual judgment.&lt;br /&gt;
     The purpose of software design religion is to specify precepts that&lt;br /&gt;
designers can follow when selecting an approach from among the many&lt;br /&gt;
possibilities before them. A project of the size and scope of OS/2 needed a&lt;br /&gt;
carefully thought out religion because OS/2 will dramatically affect this&lt;br /&gt;
and future generations of operating systems. It needed a strong religion&lt;br /&gt;
for another reason: to ensure consistency among wide-ranging features&lt;br /&gt;
implemented by a large team of programmers. Such consistency is very&lt;br /&gt;
important; if one programmer optimizes design to do A well, at the expense&lt;br /&gt;
of doing B less well, and another programmer--in the absence of religious&lt;br /&gt;
guidance--does the opposite, the end result is a product that does neither&lt;br /&gt;
A nor B well.&lt;br /&gt;
     This chapter discusses the major architectural dogmas of the OS/2&lt;br /&gt;
religion: maximum flexibility, a stable environment, localization of&lt;br /&gt;
errors, and the software tools approach.&lt;br /&gt;
&lt;br /&gt;
===3.1  Maximum Flexibility===&lt;br /&gt;
&lt;br /&gt;
The introduction to this book discusses the process of technological&lt;br /&gt;
breakthroughs. I have pointed out that one of the easiest predictions about&lt;br /&gt;
breakthroughs is that fully predicting their course is impossible. For&lt;br /&gt;
example, the 8088 microprocessor is designed to address 1 MB of memory, but&lt;br /&gt;
the IBM PC and compatible machines are designed so that addressable memory&lt;br /&gt;
is limited to 640 KB. When this decision was made, 640 KB was ten times the&lt;br /&gt;
memory that the then state-of-the-art 8080 machines could use; the initial&lt;br /&gt;
PCs were going to ship with 16 KB in them, and it seemed to all concerned&lt;br /&gt;
that 640 KB was overly generous. Yet it took only a few years before 640 KB&lt;br /&gt;
became the typical memory complement of a machine, and within another year&lt;br /&gt;
that amount of memory was viewed as pitifully small.&lt;br /&gt;
     OS/2's design religion addresses the uncertain future by decreeing&lt;br /&gt;
that--to the extent compatible with other elements in the design religion--&lt;br /&gt;
OS/2 shall be as flexible as possible. The tenet of flexibility is that&lt;br /&gt;
each component of OS/2 should be designed as if massive changes will occur&lt;br /&gt;
in that area in a future release. In other words, the current component&lt;br /&gt;
should be designed in a way that does not restrict new features and in a&lt;br /&gt;
way that can be easily supported by a new version of OS/2, one that might&lt;br /&gt;
differ dramatically in internal design.&lt;br /&gt;
     Several general principles result from a design goal of flexibility.&lt;br /&gt;
All are intended to facilitate change, which is inevitable in the general&lt;br /&gt;
yet unpredictable in the specific:&lt;br /&gt;
&lt;br /&gt;
     1.  All OS/2 features should be sufficiently elemental (simple) that&lt;br /&gt;
         they can be easily supported in any future system, including&lt;br /&gt;
         systems fundamentally different in design from OS/2. Either the&lt;br /&gt;
         features themselves are this simple, or the features are built&lt;br /&gt;
         using base features that are this simple. The adjective simple&lt;br /&gt;
         doesn't particularly refer to externals--a small number of&lt;br /&gt;
         functions and options--but to internals. The internal operating&lt;br /&gt;
         system infrastructure necessary to provide a function should be&lt;br /&gt;
         either very simple or so fundamental to the nature of operating&lt;br /&gt;
         systems that it is inevitable in future releases.&lt;br /&gt;
            By way of analogy, as time travelers we may be able to guess&lt;br /&gt;
         very little about the twenty-first century, but we do know that &lt;br /&gt;
         people will still need to eat. The cuisine of the twenty-first&lt;br /&gt;
         century may be unguessable, but certainly future kitchens will&lt;br /&gt;
         contain facilities to cut and heat food. If we bring food that&lt;br /&gt;
         needs only those two operations, we'll find that even if there's&lt;br /&gt;
         nothing to our liking on the twenty-first-century standard menu&lt;br /&gt;
         the kitchen can still meet our needs.&lt;br /&gt;
            We've seen how important upward compatibility is for computer&lt;br /&gt;
         operating systems, so we can rest assured that the future MS-DOS&lt;br /&gt;
         &amp;quot;kitchen&amp;quot; will be happy to make the necessary effort to support&lt;br /&gt;
         old programs. All we have to do today is to ensure that such&lt;br /&gt;
         support is possible. Producing a compatible line of operating&lt;br /&gt;
         system releases means more than looking backward; it also means&lt;br /&gt;
         looking forward.&lt;br /&gt;
&lt;br /&gt;
     2.  All system interfaces need to support expansion in a future&lt;br /&gt;
         release. For example, if a call queries the status of a disk file,&lt;br /&gt;
         then in addition to passing the operating system a pointer to a&lt;br /&gt;
         structure to fill in with the information, the application must&lt;br /&gt;
         also pass in the length of that structure. Although the current&lt;br /&gt;
         release of the operating system returns N bytes of information, a&lt;br /&gt;
         future release may support new kinds of disk files and may return&lt;br /&gt;
         M bytes of information. Because the application tells the&lt;br /&gt;
         operating system, via the buffer length parameter, which version&lt;br /&gt;
         of the information structure that the application understands (the&lt;br /&gt;
         old short version or the new longer version), the operating system&lt;br /&gt;
         can support both old programs and new programs simultaneously.&lt;br /&gt;
            In general, all system interfaces should be designed to support&lt;br /&gt;
         the current feature set without restraining the addition of&lt;br /&gt;
         features to the interfaces in future releases. Extra room should&lt;br /&gt;
         be left in count and flag arguments for future expansion, and all&lt;br /&gt;
         passed and returned structures need either to be self-sizing or to&lt;br /&gt;
         include a size argument.&lt;br /&gt;
            One more interface deserves special mention--the file system&lt;br /&gt;
         interface. Expanding the capabilities of the file system, such as&lt;br /&gt;
         allowing filenames longer than eight characters, is difficult&lt;br /&gt;
         because many old applications don't know how to process filenames&lt;br /&gt;
         that are longer than eight characters or they regard the longer&lt;br /&gt;
         names as illegal and reject them. OS/2 solves this and similar&lt;br /&gt;
         problems by specifying that all filenames supplied to or returned&lt;br /&gt;
         from the operating system be zero-terminated strings (ASCIIZ&lt;br /&gt;
         strings) of arbitrary length.&lt;br /&gt;
            Programmers are specifically cautioned against parsing or&lt;br /&gt;
         otherwise &amp;quot;understanding&amp;quot; filenames. Programs should consider file&lt;br /&gt;
         system pathnames as &amp;quot;magic cookies&amp;quot; to be passed to and from the&lt;br /&gt;
         operating system, but not to be parsed by the program. The details&lt;br /&gt;
         of this interface and other expandable interfaces are discussed in&lt;br /&gt;
         later chapters.&lt;br /&gt;
&lt;br /&gt;
     3.  OS/2 needs to support the addition of functions at any time. The&lt;br /&gt;
         implementation details of these functions need to be hidden from&lt;br /&gt;
         the client applications so that those details can be changed at&lt;br /&gt;
         any time. Indeed, OS/2 should disguise even the source of a&lt;br /&gt;
         feature. Some APIs are serviced by kernel code, others are&lt;br /&gt;
         serviced by subroutine libraries, and still others may be serviced&lt;br /&gt;
         by other processes running in the system. Because a client&lt;br /&gt;
         application can't tell the difference, the system designers are&lt;br /&gt;
         free to change the implementation of an API as necessary. For&lt;br /&gt;
         example, an OS/2 kernel API might be considerably changed in a&lt;br /&gt;
         future release. The old API can continue to be supported by the&lt;br /&gt;
         creation of a subroutine library routine. This routine would take&lt;br /&gt;
         the old form of the API, convert it to the new form, call the OS/2&lt;br /&gt;
         kernel, and then backconvert the result. Such a technique allows&lt;br /&gt;
         future versions of OS/2 to support new features while continuing&lt;br /&gt;
         to provide the old features to existing programs. These techniques&lt;br /&gt;
         are discussed in detail in Chapter 7, Dynamic Linking.&lt;br /&gt;
&lt;br /&gt;
     4.  Finally, to provide maximum flexibility, the operating system&lt;br /&gt;
         should be extensible and expandable in a piecemeal fashion out in&lt;br /&gt;
         the field. In other words, a user should be able to add functions&lt;br /&gt;
         to the system--for example, a database engine--or to upgrade or&lt;br /&gt;
         replace system components--such as a new graphics display driver--&lt;br /&gt;
         without a new release from Microsoft. A microcomputer design that&lt;br /&gt;
         allows third-party hardware additions and upgrades in the field is&lt;br /&gt;
         called an open system. The IBM PC line is a classic example of an&lt;br /&gt;
         open system. A design that contains no provisions for such&lt;br /&gt;
         enhancements is called a closed system. The earliest version of&lt;br /&gt;
         the Apple Macintosh is an example. At first glance, MS-DOS appears&lt;br /&gt;
         to be a closed software system because it contains no provisions&lt;br /&gt;
         for expansion. In practice, its unprotected environment makes MS-&lt;br /&gt;
         DOS the king of the open software systems because every&lt;br /&gt;
         application is free to patch the system and access the hardware as&lt;br /&gt;
         it sees fit. Keeping a software system open is as important as&lt;br /&gt;
         keeping a hardware system open. Because OS/2 is a protected&lt;br /&gt;
         operating system, explicit features, such as dynamic linking, are&lt;br /&gt;
         provided to allow system expansion by Microsoft, other software&lt;br /&gt;
         vendors, and users themselves. The topic of open systems is&lt;br /&gt;
         discussed more fully in Chapter 7.&lt;br /&gt;
&lt;br /&gt;
===3.2  A Stable Environment===&lt;br /&gt;
&lt;br /&gt;
An office automation operating system has to provide its users--the&lt;br /&gt;
application programs and the human operator--with a stable environment.&lt;br /&gt;
Every application should work the same way each time it's run; and each&lt;br /&gt;
time an application is given the same data, it should produce the same&lt;br /&gt;
result. The normal operation of one application should not affect any other&lt;br /&gt;
application. Even a program error (bug) should not affect other programs in&lt;br /&gt;
the system. Finally, if a program has bugs, the operating system should&lt;br /&gt;
detect those bugs whenever possible and report them to the user. These&lt;br /&gt;
certainly are obvious goals, but the nature of present-day computers makes&lt;br /&gt;
them surprisingly difficult to achieve.&lt;br /&gt;
&lt;br /&gt;
====3.2.1  Memory Protection====&lt;br /&gt;
Modern computers are based on the Von Neumann design--named after John Von&lt;br /&gt;
Neumann, the pioneering Hungarian-born American mathematician and computer&lt;br /&gt;
scientist. A Von Neumann computer consists of only two parts: a memory unit&lt;br /&gt;
and a processing unit. The memory unit contains both the data to be&lt;br /&gt;
operated on and the instructions (or program) that command the processing&lt;br /&gt;
unit. The processing unit reads instructions from memory; these&lt;br /&gt;
instructions may tell it to issue further reads to memory to retrieve data,&lt;br /&gt;
to operate on data retrieved earlier, or to store data back into&lt;br /&gt;
memory.&lt;br /&gt;
     A Von Neumann computer does not distinguish between instructions and&lt;br /&gt;
data; both are stored in binary code in the computer's memory. Individual&lt;br /&gt;
programs are responsible for keeping track of which memory locations hold&lt;br /&gt;
instructions and which hold data, and each program uses the memory in a&lt;br /&gt;
different way. Because the computer does not distinguish between&lt;br /&gt;
instructions and data, a program may operate on its own instructions&lt;br /&gt;
exactly as it operates on data. A program can read, modify, and write&lt;br /&gt;
computer instructions at will.&lt;br /&gt;
1. This is a simplification. OS/2 and the 80286 CPU contain&lt;br /&gt;
features that do distinguish somewhat between instructions and&lt;br /&gt;
data and that limit the ability of programs to modify their own&lt;br /&gt;
instructions. See 9.1 Protection Model for more information.&lt;br /&gt;
1&lt;br /&gt;
     This is exactly what OS/2 does when it is commanded to run a program:&lt;br /&gt;
It reads the program into memory by treating it as data, and then it causes&lt;br /&gt;
the data in those locations to be executed. It is even possible for a&lt;br /&gt;
program to dynamically &amp;quot;reprogram&amp;quot; itself by manipulating its own&lt;br /&gt;
instructions.&lt;br /&gt;
     Computer programs are extremely complex, and errors in their logic can&lt;br /&gt;
cause the program to unintentionally modify data or instructions in memory.&lt;br /&gt;
For example, a carelessly written program might contain a command buffer 80&lt;br /&gt;
bytes in size because it expects no commands longer than 80 bytes. If a&lt;br /&gt;
user types a longer command, perhaps in error, and the program does not&lt;br /&gt;
contain a special check for this circumstance, the program will overwrite&lt;br /&gt;
the memory beyond the 80-byte command buffer, destroying the data or&lt;br /&gt;
instructions placed there.&lt;br /&gt;
2. This is a simplified example. Rarely would a present-day,&lt;br /&gt;
well-tested application contain such a naive error, but errors of&lt;br /&gt;
this type--albeit in a much more complex form--exist in nearly&lt;br /&gt;
all software.&lt;br /&gt;
2&lt;br /&gt;
     In a single-tasking environment such as MS-DOS, only one application&lt;br /&gt;
runs at a time. An error such as our example could damage memory belonging&lt;br /&gt;
to MS-DOS, the application, or memory that is not in use. In practice (due&lt;br /&gt;
to memory layout conventions) MS-DOS is rarely damaged. An aberrant program&lt;br /&gt;
typically damages itself or modifies memory not in use. In any case, the&lt;br /&gt;
error goes undetected, the program produces an incorrect result, or the&lt;br /&gt;
system crashes. In the last two cases, the user loses work, but it is clear&lt;br /&gt;
which application is in error--the one executing at the time of the crash.&lt;br /&gt;
(For completeness, I'll point out that it is possible for an aberrant&lt;br /&gt;
application to damage MS-DOS subtly enough so that the application itself&lt;br /&gt;
completes correctly, but the next time an application runs, it fails. This&lt;br /&gt;
is rare, and the new application generally fails immediately upon startup;&lt;br /&gt;
so after a few such episodes with different applications, the user&lt;br /&gt;
generally identifies the true culprit.)&lt;br /&gt;
     As we have seen, errors in programs are relatively well contained in a&lt;br /&gt;
single-tasking system. MS-DOS cannot, unfortunately, correct the error, nor&lt;br /&gt;
can it very often detect the error (these tasks can be shown to be&lt;br /&gt;
mathematically impossible, in the general case). But at least the errors&lt;br /&gt;
are contained within the aberrant application; and should errors in data or&lt;br /&gt;
logic become apparent, the user can identify the erring application. When&lt;br /&gt;
we execute a second program in memory alongside the first, the situation&lt;br /&gt;
becomes more complicated.&lt;br /&gt;
     The first difficulty arises because the commonest error for a program&lt;br /&gt;
to make is to use memory that MS-DOS has not allocated to it. In a single-&lt;br /&gt;
tasking environment these memory locations are typically unused, but in a&lt;br /&gt;
multitasking environment the damaged location(s) probably belong to some&lt;br /&gt;
other program. That program will then either give incorrect results, damage&lt;br /&gt;
still other memory locations, crash, or some combination of these. In&lt;br /&gt;
summary, a memory addressing error is more dangerous because there is more&lt;br /&gt;
in memory to damage and that damage will have a more severe effect.&lt;br /&gt;
     The second difficulty arises, not from explicit programming errors,&lt;br /&gt;
but from conflicts in the normal operation of two or more co-resident&lt;br /&gt;
programs that are in some fashion incompatible. A simple example is called&lt;br /&gt;
&amp;quot;hooking the keyboard vector&amp;quot; (see Figure 3-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
BEFORE&lt;br /&gt;
                      Vector table                    Keyboard device&lt;br /&gt;
                     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿                   driver routine&lt;br /&gt;
  Device interrupt   ³            ³        ÚÄÄÄÄÄÄÄÄ� x x x x: ÄÄÄÄÄÄ&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÃÄÄÄÄÄÄÄÄÄÄÄÄ´        ³                   ÄÄÄÄÄÄ&lt;br /&gt;
             ÀÄÄÄÄÄ� ³  x x x x   ÃÄÄÄÄÄÄÄÄÙ                   ÄÄÄÄÄÄ&lt;br /&gt;
                     ÃÄÄÄÄÄÄÄÄÄÄÄÄ´                            ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³                            ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³&lt;br /&gt;
                     ³            ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
AFTER                                 Application      Keyboard device&lt;br /&gt;
                      Vector table    edits table      driver&lt;br /&gt;
                     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿    ³            Ú� x x x x: ÄÄÄÄÄÄ&lt;br /&gt;
  Device interrupt   ³            ³    ³            ³           ÄÄÄÄÄÄ&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÃÄÄÄÄÄÄÄÄÄÄÄÄ´ �ÄÄÙ            ÀÄÄÄÄÄÄÄÄÄ¿ ÄÄÄÄÄÄ&lt;br /&gt;
             ÀÄÄÄÄÄ� ³  y y y y   ÃÄ¿   Application           ³ ÄÄÄÄÄÄ&lt;br /&gt;
                     ÃÄÄÄÄÄÄÄÄÄÄÄÄ´ ³   routine               ³ ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³ ÀÄ� y y y y: ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ³            ³              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ³            ³              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                                                 ÄÄÄÄÄÄ       ³&lt;br /&gt;
                                                 jmp x x x x ÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 3-1.  Hooking the keyboard vector.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     In this case, an application modifies certain MS-DOS memory locations&lt;br /&gt;
so that when a key is pressed the application code, instead of the MS-DOS&lt;br /&gt;
code, is notified by the hardware. Applications do this because it allows&lt;br /&gt;
them to examine certain keyboard events, such as pressing the shift key&lt;br /&gt;
without pressing any other key, that MS-DOS does not pass on to&lt;br /&gt;
applications which ask MS-DOS to read the keyboard for them. It works fine&lt;br /&gt;
for one application to &amp;quot;hook&amp;quot; the keyboard vector; although hooking the&lt;br /&gt;
keyboard vector modifies system memory locations that don't belong to the&lt;br /&gt;
application, the application generally gets away with it successfully. In a&lt;br /&gt;
multitasking environment, however, a second application may want to do the&lt;br /&gt;
same trick, and the system probably won't function correctly. The result is&lt;br /&gt;
that a stable environment requires memory protection. An application must&lt;br /&gt;
not be allowed to modify, accidentally or deliberately, memory that isn't&lt;br /&gt;
assigned to that application.&lt;br /&gt;
&lt;br /&gt;
====3.2.2  Side-Effects Protection====&lt;br /&gt;
A stable environment requires more than memory protection; it also requires&lt;br /&gt;
that the system be designed so that the execution of one application&lt;br /&gt;
doesn't cause side effects for any other application. Side effects can be&lt;br /&gt;
catastrophic or they can be unremarkable, but in all cases they violate the&lt;br /&gt;
tenet of a stable environment.&lt;br /&gt;
     For example, consider the practice of hooking the keyboard interrupt&lt;br /&gt;
vector. If one application uses this technique to intercept keystrokes, it&lt;br /&gt;
will intercept all keystrokes, even those intended for some other&lt;br /&gt;
application. The side effects in this case are catastrophic--the hooking&lt;br /&gt;
application sees keystrokes that aren't intended for it, and the other&lt;br /&gt;
applications don't get any keystrokes at all.&lt;br /&gt;
     Side effects can plague programs even when they are using official&lt;br /&gt;
system features if those features are not carefully designed. For example,&lt;br /&gt;
a mainframe operating system called TOPS-10 contains a program that&lt;br /&gt;
supports command files similar to MS-DOS .BAT files, and it also contains a&lt;br /&gt;
program that provides delayed offline execution of commands. Unfortunately,&lt;br /&gt;
both programs use the same TOPS-10 facility to do their work. If you&lt;br /&gt;
include a .BAT file in a delayed command list, the two programs will&lt;br /&gt;
conflict, and the .BAT file will not execute.&lt;br /&gt;
     OS/2 deals with side effects by virtualizing to the greatest extent&lt;br /&gt;
possible each application's operating environment. This means that OS/2&lt;br /&gt;
tries to make each application &amp;quot;see&amp;quot; a standard environment that is&lt;br /&gt;
unaffected by changes in another application's environment. The effect is&lt;br /&gt;
like that of a building of identical apartments. When each tenant moves in,&lt;br /&gt;
he or she gets a standard environment, a duplicate of all the apartments.&lt;br /&gt;
Each tenant can customize his or her environment, but doing so doesn't&lt;br /&gt;
affect the other tenants or their environments.&lt;br /&gt;
     Following are some examples of application environment issues that&lt;br /&gt;
OS/2 virtualizes.&lt;br /&gt;
&lt;br /&gt;
     þ  Working Directories. Each application has a working (or current)&lt;br /&gt;
        directory for each disk drive. Under MS-DOS version 3.x, if a child&lt;br /&gt;
        process changes the working directory for drive C and then exits,&lt;br /&gt;
        the working directory for drive C remains changed when the parent&lt;br /&gt;
        process regains control. OS/2 eliminates this side effect by&lt;br /&gt;
        maintaining a separate list of working directories for each process&lt;br /&gt;
        in the system. Thus, when an application changes its working&lt;br /&gt;
        directories, the working directories of other applications in the&lt;br /&gt;
        system remain unchanged.&lt;br /&gt;
&lt;br /&gt;
     þ  Memory Utilization. The simple act of memory consumption produces&lt;br /&gt;
        side effects. If one process consumes all available RAM, none is&lt;br /&gt;
        left for the others. The OS/2 memory management system uses memory&lt;br /&gt;
        overcommit (swapping) so that the memory needs of each application&lt;br /&gt;
        can be met.&lt;br /&gt;
&lt;br /&gt;
     þ  Priority. OS/2 uses a priority-based scheduler to assign the CPU to&lt;br /&gt;
        the processes that need it. Applications can adjust their priority&lt;br /&gt;
        and that of their child processes as they see fit. However, the&lt;br /&gt;
        very priority of a task causes side effects. Consider a process&lt;br /&gt;
        that tells OS/2 that it must run at a higher priority than any&lt;br /&gt;
        other task in the system. If a second process makes the same&lt;br /&gt;
        request, a conflict occurs: Both processes cannot be the highest&lt;br /&gt;
        priority in the system. In general, the priority that a process&lt;br /&gt;
        wants for itself depends on the priorities of the other processes&lt;br /&gt;
        in the system. The OS/2 scheduler contains a sophisticated&lt;br /&gt;
        absolute/relative mechanism to deal with these conflicts.&lt;br /&gt;
&lt;br /&gt;
     þ  File Utilization. As discussed earlier, one application may modify&lt;br /&gt;
        the files that another application is using, causing an unintended&lt;br /&gt;
        side effect. The OS/2 file-locking mechanism prevents unintended&lt;br /&gt;
        modifications, and the OS/2 record-locking mechanism coordinates&lt;br /&gt;
        intentional parallel updates to a single file.&lt;br /&gt;
&lt;br /&gt;
     þ  Environment Strings. OS/2 retains the MS-DOS concept of environment&lt;br /&gt;
        strings: Each process has its own set. A child process inherits a&lt;br /&gt;
        copy of the parent's environment strings, but changing the strings&lt;br /&gt;
        in this copy will not affect the original strings in the parent's&lt;br /&gt;
        environment.&lt;br /&gt;
&lt;br /&gt;
     þ  Keyboard Mode. OS/2 applications can place the keyboard in one of&lt;br /&gt;
        two modes--cooked or raw. These modes tell OS/2 whether the&lt;br /&gt;
        application wants to handle, for example, the backspace character&lt;br /&gt;
        (raw mode) or whether it wants OS/2 to handle the backspace&lt;br /&gt;
        character for it (cooked mode). The effect of these calls on&lt;br /&gt;
        subsequent keyboard read operations would cause side effects for&lt;br /&gt;
        other applications reading from the keyboard, so OS/2 maintains a&lt;br /&gt;
        record of the cooked/raw status of each application and silently&lt;br /&gt;
        switches the mode of the keyboard when an application issues a&lt;br /&gt;
        keyboard read request.&lt;br /&gt;
&lt;br /&gt;
===3.3  Localization of Errors===&lt;br /&gt;
&lt;br /&gt;
A key element in creating a stable environment is localizing errors. Humans&lt;br /&gt;
always make errors, and human creations such as computer programs always&lt;br /&gt;
contain errors. Before the development of computers, routine human errors&lt;br /&gt;
were usually limited in scope. Unfortunately, as the saying goes, a&lt;br /&gt;
computer can make a mistake in 60 seconds that it would take a whole office&lt;br /&gt;
force a year to make. Although OS/2 can do little to prevent such errors,&lt;br /&gt;
it needs to do its best to localize the errors.&lt;br /&gt;
     Localizing errors consists of two activities: minimizing as much as&lt;br /&gt;
possible the impact of the error on other applications in the system, and&lt;br /&gt;
maximizing the opportunity for the user to understand which of the many&lt;br /&gt;
programs running in the computer caused the error. These two activities are&lt;br /&gt;
interrelated in that the more successful the operating system is in&lt;br /&gt;
restricting the damage to the domain of a single program, the easier it is&lt;br /&gt;
for the user to know which program is at fault.&lt;br /&gt;
     The most important aspect of error localization has already been&lt;br /&gt;
discussed at length--memory management and protection. Other error&lt;br /&gt;
localization principles include the following:&lt;br /&gt;
&lt;br /&gt;
     þ  No program can crash or hang the system. A fundamental element of&lt;br /&gt;
        the OS/2 design religion is that no application program can,&lt;br /&gt;
        accidentally or even deliberately, crash or hang the system. If a&lt;br /&gt;
        failing application could crash the system, obviously the system&lt;br /&gt;
        did not localize the error! Furthermore, the user would be unable&lt;br /&gt;
        to identify the responsible application because the entire system&lt;br /&gt;
        would be dead.&lt;br /&gt;
&lt;br /&gt;
     þ  No program can make inoperable any screen group other than its own.&lt;br /&gt;
        As we'll see in later chapters of this book, sometimes design&lt;br /&gt;
        goals, design religions, or both conflict. For example, the precept&lt;br /&gt;
        of no side effects conflicts with the requirement of supporting&lt;br /&gt;
        keyboard macro expander applications. The sole purpose of such an&lt;br /&gt;
        application is to cause a side effect--specifically to translate&lt;br /&gt;
        certain keystroke sequences into other sequences. OS/2 resolves&lt;br /&gt;
        this conflict by allowing applications to examine and modify the&lt;br /&gt;
        flow of data to and from devices (see Chapter 16) but in a&lt;br /&gt;
        controlled fashion. Thus, an aberrant keyboard macro application&lt;br /&gt;
        that starts to &amp;quot;eat&amp;quot; all keys, passing none through to the&lt;br /&gt;
        application, can make its current screen group unusable, but it&lt;br /&gt;
        can't affect the user's ability to change screen groups.&lt;br /&gt;
           Note that keyboard monitors can intercept and consume any&lt;br /&gt;
        character or character sequence except for the keystrokes that OS/2&lt;br /&gt;
        uses to switch screen groups (Ctrl-Esc and Alt-Esc). This is to&lt;br /&gt;
        prevent aberrant keyboard monitor applications from accidentally&lt;br /&gt;
        locking the user into his or her screen group by consuming and&lt;br /&gt;
        discarding the keyboard sequences that are used to switch from&lt;br /&gt;
        screen groups.&lt;br /&gt;
&lt;br /&gt;
     þ  Applications cannot intercept general protection (GP) fault errors.&lt;br /&gt;
        A GP fault occurs when a program accesses invalid memory locations&lt;br /&gt;
        or accesses valid locations in an invalid way (such as writing into&lt;br /&gt;
        read-only memory areas). OS/2 always terminates the operation and&lt;br /&gt;
        displays a message for the user. A GP fault is evidence that the&lt;br /&gt;
        program's logic is incorrect, and therefore it cannot be expected&lt;br /&gt;
        to fix itself or trusted to notify the user of its ill health.&lt;br /&gt;
           The OS/2 design does allow almost any other error on the part of&lt;br /&gt;
        an application to be detected and handled by that application. For&lt;br /&gt;
        example, &amp;quot;Illegal filename&amp;quot; is an error caused by user input, not&lt;br /&gt;
        by the application. The application can deal with this error as it&lt;br /&gt;
        sees fit, perhaps correcting and retrying the operation. An error&lt;br /&gt;
        such as &amp;quot;Floppy disk drive not ready&amp;quot; is normally handled by OS/2&lt;br /&gt;
        but can be handled by the application. This is useful for&lt;br /&gt;
        applications that are designed to operate unattended; they need to&lt;br /&gt;
        handle errors themselves rather than waiting for action to be taken&lt;br /&gt;
        by a nonexistent user.&lt;br /&gt;
&lt;br /&gt;
===3.4  Software Tools Approach===&lt;br /&gt;
&lt;br /&gt;
In Chapter 2 we discussed IPC and the desirability of having separate&lt;br /&gt;
functions contained in separate programs. We discussed the flexibility of&lt;br /&gt;
such an approach over the &amp;quot;one man band&amp;quot; approach of an all-in-one&lt;br /&gt;
application. We also touched on the value of being able to upgrade the&lt;br /&gt;
functionality of the system incrementally by replacing individual programs.&lt;br /&gt;
All these issues are software tools issues.&lt;br /&gt;
     Software tools refers to a design philosophy which says that&lt;br /&gt;
individual programs and applications should be like tools: Each should do&lt;br /&gt;
one job and do it very well. A person who wants to turn screws and also&lt;br /&gt;
drive nails should get a screwdriver and a hammer rather than a single tool&lt;br /&gt;
that does neither job as well.&lt;br /&gt;
     The tools approach is used routinely in nonsoftware environments and&lt;br /&gt;
is taken for granted. For example, inside a standard PC the hardware and&lt;br /&gt;
electronics are isolated into functional components that communicate via&lt;br /&gt;
interfaces. The power supply is in a box by itself; its interface is the&lt;br /&gt;
line cord and some power connectors. The disk drives are separate from the&lt;br /&gt;
rest of the electronics; they interface via more connectors. Each component&lt;br /&gt;
is the equivalent of a software application: It does one job and does it&lt;br /&gt;
well. When the disk drive needs power, it doesn't build in a power supply;&lt;br /&gt;
it uses the standard interface to the power supply module--the power&lt;br /&gt;
&amp;quot;specialist&amp;quot; in the system.&lt;br /&gt;
     Occasionally, the software tools approach is criticized for being&lt;br /&gt;
inefficient. People may argue that space is wasted and time is lost by&lt;br /&gt;
packaging key functions separately; if they are combined, the argument&lt;br /&gt;
goes, nothing is wasted. This argument is correct in that some RAM and CPU&lt;br /&gt;
time is spent on interface issues, but it ignores the gains involved in&lt;br /&gt;
&amp;quot;sending out the work&amp;quot; to a specialist rather than doing it oneself. One&lt;br /&gt;
could argue, for example, that if I built an all-in-one PC system I'd save&lt;br /&gt;
money because I wouldn't have to buy connectors to plug everything&lt;br /&gt;
together. I might also save a little by not having to buy buffer chips to&lt;br /&gt;
drive signals over those connectors. But in doing so, I'd lose the&lt;br /&gt;
advantage of being able to buy my power supply from a very high-volume and&lt;br /&gt;
high-efficiency supplier--someone who can make a better, cheaper supply,&lt;br /&gt;
even with the cost of connectors, than my computer company can.&lt;br /&gt;
     Finally, the user gains from the modular approach. If you need more&lt;br /&gt;
disk capability, you can buy one and plug it in. You are not limited to one&lt;br /&gt;
disk maker but rather can choose the one that's right for your needs--&lt;br /&gt;
expensive and powerful or cheap and modest. You can buy third-party&lt;br /&gt;
hardware, such as plug-in cards, that the manufacturer of your computer&lt;br /&gt;
doesn't make. All in all, the modest cost of a few connectors and driver&lt;br /&gt;
chips is paid back manyfold, both in direct system costs (due to the&lt;br /&gt;
efficiency of specialization) and in the additional capability and&lt;br /&gt;
flexibility of the machine.&lt;br /&gt;
     As I said earlier, the software tools approach is the software&lt;br /&gt;
equivalent of an open system. It's an important part of the OS/2 religion:&lt;br /&gt;
Although the system doesn't require a modular tools approach from&lt;br /&gt;
applications programs, it should do everything in its power to facilitate&lt;br /&gt;
such systems, and it should itself be constructed in that fashion.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Part II  The Architecture&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==4  Multitasking==&lt;br /&gt;
&lt;br /&gt;
I have discussed the goals and compatibility issues that OS/2 is intended&lt;br /&gt;
to meet, and I have described the design religion that was established for&lt;br /&gt;
OS/2. The following chapters discuss individual design elements in some&lt;br /&gt;
detail, emphasizing not only how the elements work and are used but the&lt;br /&gt;
role they play in the system as a whole.&lt;br /&gt;
     In a multitasking operating system, two or more programs can execute&lt;br /&gt;
at the same time. Some benefits of such a feature are obvious: You (the&lt;br /&gt;
user) can switch between several application programs without saving work&lt;br /&gt;
and exiting one program to start another. When the telephone rings, for&lt;br /&gt;
example, you can switch from the word processor application you are using&lt;br /&gt;
to write a memo and go to the application that is managing your appointment&lt;br /&gt;
calendar or to the spreadsheet application that contains the figures that&lt;br /&gt;
are necessary to answer your caller's query.&lt;br /&gt;
     This type of multitasking is similar to what people do when they're&lt;br /&gt;
not working with a computer. You may leave a report half read on your desk&lt;br /&gt;
to address a more pressing need, such as answering the phone. Later,&lt;br /&gt;
perhaps after other tasks intervene, you return to the report. You don't&lt;br /&gt;
terminate a project and return your reference materials to the bookshelf,&lt;br /&gt;
the files, and the library to answer the telephone; you merely switch your&lt;br /&gt;
attention for a while and later pick up where you left off.&lt;br /&gt;
     This kind of multitasking is called serial multitasking because&lt;br /&gt;
actions are performed one at a time. Although you probably haven't thought&lt;br /&gt;
of it this way, you've spent much of your life serially multitasking. Every&lt;br /&gt;
day when you leave for work, you suspend your home life and resume your&lt;br /&gt;
work life. That evening, you reverse the process. You serially multitask a&lt;br /&gt;
hobby--each time picking it up where you left off last time and then&lt;br /&gt;
leaving off again. Reading the comics in a daily newspaper is a prodigious&lt;br /&gt;
feat of human serial multitasking--you switch from one to another of&lt;br /&gt;
perhaps 20 strips, remembering for each what has gone on before and then&lt;br /&gt;
waiting until tomorrow for the next installment. Although serial&lt;br /&gt;
multitasking is very useful, it is not nearly as useful as full&lt;br /&gt;
multitasking--the kind of multitasking built into OS/2.&lt;br /&gt;
     Full multitasking on the computer involves doing more than one thing--&lt;br /&gt;
running more than one application--at the same time. Humans do a little of&lt;br /&gt;
this, but not too much. People commonly talk while they drive cars, eat&lt;br /&gt;
while watching television, and walk while chewing gum. None of these&lt;br /&gt;
activities requires one's full concentration though. Humans generally can't&lt;br /&gt;
fully multitask activities that require a significant amount of&lt;br /&gt;
concentration because they have only one brain.&lt;br /&gt;
     For that matter, a personal computer has only one &amp;quot;brain&amp;quot;--one CPU.&lt;br /&gt;
1. Although multiple-CPU computers are well known, personal computers&lt;br /&gt;
with multiple CPUs are uncommon. In any case, this discussion applies,&lt;br /&gt;
with the obvious extensions, to multiple-CPU systems.&lt;br /&gt;
1&lt;br /&gt;
But OS/2 can switch this CPU from one activity to another very rapidly--&lt;br /&gt;
dozens or even hundreds of times a second. All executing programs seem to&lt;br /&gt;
be running at the same time, at least on the human scale of time. For&lt;br /&gt;
example, if five programs are running and each in turn gets 0.01 second of&lt;br /&gt;
CPU time (that is, 10 milliseconds), in 1 second each program receives 20&lt;br /&gt;
time slices. To most observers, human or other computer software, all five&lt;br /&gt;
programs appear to be running simultaneously but each at one-fifth its&lt;br /&gt;
maximum speed. We'll return to the topic of time slicing later; for now,&lt;br /&gt;
it's easiest--and, as we shall see, best--to pretend that all executing&lt;br /&gt;
programs run simultaneously.&lt;br /&gt;
     The full multitasking capabilities of OS/2 allow the personal computer&lt;br /&gt;
to act as more than a mere engine to run applications; the personal&lt;br /&gt;
computer can now be a system of services. The user can interact with a&lt;br /&gt;
spreadsheet program, for example, while a mail application is receiving&lt;br /&gt;
network messages that the user can read later. At the same time, other&lt;br /&gt;
programs may be downloading data from a mainframe computer or spooling&lt;br /&gt;
output to a printer or a plotter. The user may have explicitly initiated&lt;br /&gt;
some of these activities; a program may have initiated others. Regardless,&lt;br /&gt;
they all execute simultaneously, and they all do their work without&lt;br /&gt;
requiring the user's attention or intervention.&lt;br /&gt;
     Full multitasking is useful to programs themselves. Earlier, we&lt;br /&gt;
discussed the advantages of a tools approach--writing programs so that&lt;br /&gt;
they can offer their services to other programs. The numerous advantages&lt;br /&gt;
of this technique are possible only because of full&lt;br /&gt;
multitasking. For&lt;br /&gt;
example, if a program is to be able to invoke another program to sort a &lt;br /&gt;
data file, the sort program must execute at the same time as its client &lt;br /&gt;
program. It wouldn't be very useful if the client program had to &lt;br /&gt;
terminate in order  for the sort program to run.&lt;br /&gt;
     Finally, full multitasking is useful within a program itself. A thread&lt;br /&gt;
is an OS/2 mechanism that allows more than one path of execution through a&lt;br /&gt;
particular application. (Threads are discussed in detail later; for now it&lt;br /&gt;
will suffice to imagine that several CPUs can be made to execute the same&lt;br /&gt;
program simultaneously.) This allows individual applications to perform&lt;br /&gt;
more than one task at a time. For example, if the user tells a spreadsheet&lt;br /&gt;
program to recalculate a large budget analysis, the program can use one&lt;br /&gt;
thread to do the calculating and another to prompt for, read, and obey the&lt;br /&gt;
user's next command. In effect, multiple operations overlap during&lt;br /&gt;
execution and thereby increase the program's responsiveness to the user.&lt;br /&gt;
     OS/2 uses a time-sliced, priority-based preemptive scheduler to&lt;br /&gt;
provide full multitasking. In other words, the OS/2 scheduler preempts--&lt;br /&gt;
takes away--the CPU from one application at any time the scheduler desires&lt;br /&gt;
and assigns the CPU another application. Programs don't surrender the CPU&lt;br /&gt;
when they feel like it; OS/2 preempts it. Each program in the system (more&lt;br /&gt;
precisely, each thread in the system) has its own priority. When a thread&lt;br /&gt;
of a higher priority than the one currently running wants to run, the&lt;br /&gt;
scheduler preempts the running thread in favor of the higher priority one.&lt;br /&gt;
If two or more runnable threads have the same highest priority, OS/2 runs&lt;br /&gt;
each in turn for a fraction of a second--a time slice.&lt;br /&gt;
     The OS/2 scheduler does not periodically look around to see if the&lt;br /&gt;
highest priority thread is running. Such an approach wastes CPU time and&lt;br /&gt;
slows response time because a higher priority thread must wait to run until&lt;br /&gt;
the next scheduler scan. Instead, other parts of the system call the&lt;br /&gt;
scheduler when they think that a thread other than the one running should&lt;br /&gt;
be executed.&lt;br /&gt;
&lt;br /&gt;
===4.1  Subtask Model===&lt;br /&gt;
&lt;br /&gt;
The terms task and process are used interchangeably to describe the direct&lt;br /&gt;
result of executing a binary (.EXE) file. A process is the unit of&lt;br /&gt;
ownership under OS/2, and processes own resources such as memory, open&lt;br /&gt;
files, connections to dynlink libraries, and semaphores. Casual users would&lt;br /&gt;
call a process a &amp;quot;program&amp;quot;; and, in fact, under MS-DOS all programs and&lt;br /&gt;
applications consist of a single process. OS/2 uses the terms task or&lt;br /&gt;
process because a single application program under OS/2 may consist of more&lt;br /&gt;
than one process. This section describes how this is done.&lt;br /&gt;
     First, some more terminology. When a process creates, or execs,&lt;br /&gt;
another process, the creator process is called the parent process, and the&lt;br /&gt;
created process is called the child process. The parent of the parent is&lt;br /&gt;
the child's grandparent and so on. As with people, each process in the&lt;br /&gt;
system has or had a parent.&lt;br /&gt;
2. Obviously, during boot-up OS/2 creates an initial parentless&lt;br /&gt;
process by &amp;quot;magic,&amp;quot; but this is ancient history by the time any&lt;br /&gt;
application may run, so the anomaly may be safely ignored.&lt;br /&gt;
2 Although we use genealogical terms to describe&lt;br /&gt;
task relationships, a child task, or process, is more like an agent or&lt;br /&gt;
employee of the parent task. Employees are hired to do work for an&lt;br /&gt;
employer. The employer provides a workplace and access to the information&lt;br /&gt;
employees need to do their jobs. The same is generally true for a child&lt;br /&gt;
task. When a child task is created, it inherits (or receives a copy of) a&lt;br /&gt;
great deal of the parent task's environment. For example, it inherits, or&lt;br /&gt;
takes on, the parent's base scheduling priority and its screen group. The&lt;br /&gt;
term inherit is a little inappropriate because the parent task has not&lt;br /&gt;
died. It is alive and well, going about its business.&lt;br /&gt;
     The most important items a child task inherits are its parent's open&lt;br /&gt;
file handles. OS/2 uses a handle mechanism to perform file I/O, as do MS-&lt;br /&gt;
DOS versions 2.0 and later. When a file is opened, OS/2 returns a handle--&lt;br /&gt;
an integer value--to the process. When a program wants to read from or&lt;br /&gt;
write to a file, it gives OS/2 the file handle. Handles are not identical&lt;br /&gt;
among processes. For example, the file referred to by handle 6 of one&lt;br /&gt;
process bears no relationship to the file referred to by another process's&lt;br /&gt;
handle 6, unless one of those processes is a child of the other. When a&lt;br /&gt;
parent process creates a child process, the child process, by default,&lt;br /&gt;
inherits each of the parent's open file handles. For example, a parent&lt;br /&gt;
process has the file \WORK\TEMPFILE open on handle 5; when the child&lt;br /&gt;
process starts up, handle 5 is open and references the \WORK\TEMPFILE file.&lt;br /&gt;
     This undoubtedly seems brain damaged if you are unfamiliar with this&lt;br /&gt;
model. Why is it done in this crazy way? What use does the child process&lt;br /&gt;
have for these open files? What's to keep the child from mucking up the&lt;br /&gt;
parent's files? All this becomes clearer when the other piece of the puzzle&lt;br /&gt;
is in place--the standard file handles.&lt;br /&gt;
&lt;br /&gt;
====4.1.1  Standard File Handles====&lt;br /&gt;
Many OS/2 functions use 16-bit integer values called handles for their&lt;br /&gt;
interfaces. A handle is an object that programmers call a &amp;quot;magic cookie&amp;quot;--&lt;br /&gt;
an arbitrary value that OS/2 provides the application so that the&lt;br /&gt;
application can pass the value back to OS/2 on subsequent calls. Its&lt;br /&gt;
purpose is to simplify the OS/2 interface and speed up the particular&lt;br /&gt;
service. For example, when a program creates a system semaphore, it is&lt;br /&gt;
returned a semaphore handle--a magic cookie--that it uses for subsequent&lt;br /&gt;
request and release operations. Referring to the semaphore via a 16-bit&lt;br /&gt;
value is much faster than passing around a long filename. Furthermore, the&lt;br /&gt;
magic in magic cookie is that the meaning of the 16-bit handle value is&lt;br /&gt;
indecipherable to the application. OS/2 created the value, and it has&lt;br /&gt;
meaning only to OS/2; the application need only retain the value and&lt;br /&gt;
regurgitate it when appropriate. An application can never make any&lt;br /&gt;
assumptions about the values of a magic cookie.&lt;br /&gt;
     File handles are an exceptional form of handle because they are not&lt;br /&gt;
magic cookies. The handle value, in the right circumstances, is meaningful&lt;br /&gt;
to the application and to the system as a whole. Specifically, three handle&lt;br /&gt;
values have special meaning: handle value 0, called STDIN (for standard&lt;br /&gt;
input); handle value 1, called STDOUT (standard output); and handle value&lt;br /&gt;
2, called STDERR (standard error). A simple program--let's call it NUMADD--&lt;br /&gt;
will help to explain the use of these three handles. NUMADD will read two&lt;br /&gt;
lines of ASCII text (each containing a decimal number), convert the numbers&lt;br /&gt;
to binary, add them, and then convert the results to an ASCII string and&lt;br /&gt;
write out the result. Note that we're confining our attention to a simple&lt;br /&gt;
non-screen-oriented program that might be used as a tool, either directly&lt;br /&gt;
by a programmer or by another program (see Figure 4-1 and Listing 4-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      STDIN ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
123 ÄÄÄ¿    ³          ³ STDOUT&lt;br /&gt;
14     ÀÄÄÄ�³  NUMADD  ÃÄÄÄ¿&lt;br /&gt;
            ³          ³   ÀÄÄÄÄ� 137&lt;br /&gt;
            ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-1.  Program NUMADD operation--interactive.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;            /* defines stdin and stdout */&lt;br /&gt;
&lt;br /&gt;
main()&lt;br /&gt;
{&lt;br /&gt;
     int value1, value2, sum;&lt;br /&gt;
&lt;br /&gt;
     fscanf (stdin, &amp;quot;%d&amp;quot;, &amp;amp;value1);&lt;br /&gt;
     fscanf (stdin, &amp;quot;%d&amp;quot;, &amp;amp; value2);&lt;br /&gt;
     sum = value1 + value2;&lt;br /&gt;
     fprintf (stdout, &amp;quot;%d\n&amp;quot;, sum);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Listing 4-1.  Program NUMADD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     By convention, all OS/2 programs read input from STDIN and write&lt;br /&gt;
output to STDOUT. Any error messages are written to STDERR. The program&lt;br /&gt;
itself does not open these handles; it inherits them from the parent&lt;br /&gt;
process. The parent may have opened them itself or inherited them from its&lt;br /&gt;
own parent. As you can see, NUMADD would not contain DosOpen calls;&lt;br /&gt;
instead, it would start immediately issuing fscanf calls on handle 0&lt;br /&gt;
(STDIN), which in turn issues DosRead calls, and, when ready, directly&lt;br /&gt;
issue fprintf calls to handle 1 (STDOUT), which in turn issues DosWrites.&lt;br /&gt;
     Figure 4-2 and Listing 4-2 show a hypothetical application, NUMARITH.&lt;br /&gt;
NUMARITH reads three text lines. The first line contains an operation&lt;br /&gt;
character, such as a plus (+) or a minus (-); the second and third lines&lt;br /&gt;
contain the values to be operated upon. The author of this program doesn't&lt;br /&gt;
want to reinvent the wheel; so when the program NUMARITH encounters a +&lt;br /&gt;
operation, it executes NUMADD to do the work. As shown, the parent process&lt;br /&gt;
NUMARITH has its STDIN connected to the keyboard and its STDOUT connected&lt;br /&gt;
to the screen device drivers.&lt;br /&gt;
3. CMD.EXE inherited these handles from its own parent. This process&lt;br /&gt;
is discussed later.&lt;br /&gt;
3 When NUMADD executes, it reads input from&lt;br /&gt;
the keyboard via STDIN. After the user types the two numbers, NUMADD&lt;br /&gt;
displays the result on the screen via STDOUT. NUMARITH has invoked NUMADD&lt;br /&gt;
to do some work for it, and NUMADD has silently and seamlessly acted as a&lt;br /&gt;
part of NUMARITH. The employee metaphor fits well here. NUMADD acted as an&lt;br /&gt;
employee of NUMARITH, making use of NUMARITH's I/O streams, and as a result&lt;br /&gt;
the contribution of the NUMADD employee to the NUMARITH company is seamless.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
keyboard                                   screen&lt;br /&gt;
    ³     STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT     ³&lt;br /&gt;
    ÀÄÄÄÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
            ÃÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄ´&lt;br /&gt;
            �      ³          ³     �&lt;br /&gt;
            ³      ÀÄÄÄÄÄÂÄÄÄÄÙ     ³&lt;br /&gt;
            ³            ³ DosExec  ³&lt;br /&gt;
   inherits ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿     ³ inherits&lt;br /&gt;
            ³      ³          ³     ³&lt;br /&gt;
            ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄ�ÄÄÙ&lt;br /&gt;
                   ³          ³&lt;br /&gt;
                   ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-2.  Program NUMARITH operation--interactive.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**     Numarith - Perform ASCII Arithmetic&lt;br /&gt;
*&lt;br /&gt;
*       Numarith reads line triplets:&lt;br /&gt;
*&lt;br /&gt;
*               operation&lt;br /&gt;
*               value1&lt;br /&gt;
*               value2&lt;br /&gt;
*&lt;br /&gt;
*       performs the specified operation (+, -, *, /) on&lt;br /&gt;
*       the two values and prints the result on stdout.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
main()&lt;br /&gt;
{&lt;br /&gt;
        char operation;&lt;br /&gt;
&lt;br /&gt;
        fscanf (stdin, &amp;quot;%c&amp;quot;, &amp;amp;operation);&lt;br /&gt;
&lt;br /&gt;
        switch (operation) {&lt;br /&gt;
&lt;br /&gt;
            case '+':  execl (&amp;quot;numadd&amp;quot;,  0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '-':  execl (&amp;quot;numsub&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '*':  execl (&amp;quot;nummul&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '/':  execl (&amp;quot;numdiv&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
            }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Listing 4-2.  Program NUMARITH.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Figure 4-3 shows a similar situation. In this case, however,&lt;br /&gt;
NUMARITH's STDIN and STDOUT handles are open on two files, which we'll call&lt;br /&gt;
DATAIN and DATAOUT. Once again, NUMADD does its work seamlessly. The input&lt;br /&gt;
numbers are read from the command file on STDIN, and the output is properly&lt;br /&gt;
intermingled in the log file on STDOUT. The key here is that this NUMADD is&lt;br /&gt;
exactly the same program that ran in Listing 4-2; NUMADD contains no&lt;br /&gt;
special code to deal with this changed situation. In both examples, NUMADD&lt;br /&gt;
simply reads from STDIN and writes to STDOUT; NUMADD neither knows nor&lt;br /&gt;
cares where those handles point. Exactly the same is true for the parent.&lt;br /&gt;
NUMARITH doesn't know and doesn't care that it's working from files instead&lt;br /&gt;
of from the screen; it simply uses the STDIN and STDOUT handles that it&lt;br /&gt;
inherited from its parent.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
File data in                                     File data out&lt;br /&gt;
     ___                                              ___&lt;br /&gt;
   /     \                                          /     \&lt;br /&gt;
  ³\ ___ /³                                        ³\ ___ /³&lt;br /&gt;
  ³       ³                                        ³       ³&lt;br /&gt;
  ³       ÃÄÄ¿                                  ÚÄ�³       ³&lt;br /&gt;
  ³       ³  ³  STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT  ³  ³       ³&lt;br /&gt;
   \ ___ /   ÀÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÙ   \ ___ /&lt;br /&gt;
                  ÃÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄ´&lt;br /&gt;
                  �      ³          ³     �&lt;br /&gt;
                  ³      ÀÄÄÄÄÄÂÄÄÄÄÙ     ³&lt;br /&gt;
                  ³            ³ DosExec  ³&lt;br /&gt;
         inherits ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿     ³ inherits&lt;br /&gt;
                  ³      ³          ³     ³&lt;br /&gt;
                  ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄ�ÄÄÙ&lt;br /&gt;
                         ³          ³&lt;br /&gt;
                         ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-3.  Program NUMARITH operation--from files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This is the single most important concept in the relationship and&lt;br /&gt;
inheritance structure between processes. The reason a process inherits so&lt;br /&gt;
much from its parent is so that the parent can set up the tool's&lt;br /&gt;
environment--make it read from the parent's STDIN or from a file or from an&lt;br /&gt;
anonymous pipe (see 4.1.2 Anonymous Pipes). This gives the parent the&lt;br /&gt;
flexibility to use a tool program as it wishes, and it frees the tool's&lt;br /&gt;
author from the need to be &amp;quot;all things to all people.&amp;quot;&lt;br /&gt;
     Of equal importance, the inheritance architecture provides&lt;br /&gt;
nesting encapsulation of child processes. NUMARITH's parent process doesn't&lt;br /&gt;
know and doesn't need to know how NUMARITH does its job. NUMARITH can do&lt;br /&gt;
the additions itself, or it can invoke NUMADD as a child, but the&lt;br /&gt;
architecture encapsulates the details of NUMARITH's operation so that&lt;br /&gt;
NUMADD's involvement is hidden from NUMARITH's parent. Likewise, the&lt;br /&gt;
decision of NUMARITH's parent to work from a file or from a device or from&lt;br /&gt;
a pipe is encapsulated (that is, hidden) from NUMARITH and from any child&lt;br /&gt;
processes that NUMARITH may execute to help with its work. Obviously, this&lt;br /&gt;
architecture can be extended arbitrarily: NUMADD can itself execute a child&lt;br /&gt;
process to help NUMADD with its work, and this would silently and invisibly&lt;br /&gt;
work. Neither NUMARITH nor its parent would know or need to know anything&lt;br /&gt;
about how NUMADD was doing its work. Other versions can replace any of&lt;br /&gt;
these applications at any time. The new versions can invoke more or fewer&lt;br /&gt;
child processes or be changed in any other way, and their client (that is,&lt;br /&gt;
parent) processes are unaffected. The architecture of OS/2 is tool-based;&lt;br /&gt;
as long as the function of a tool remains constant (or is supersetted), its&lt;br /&gt;
implementation is irrelevant and can be changed arbitrarily.&lt;br /&gt;
     The STDIN, STDOUT, and STDERR architecture applies to all programs,&lt;br /&gt;
even those that only use VIO, KBD, or the presentation manager and that&lt;br /&gt;
never issue operations on these handles. See Chapter 14, Interactive&lt;br /&gt;
Programs.&lt;br /&gt;
&lt;br /&gt;
====4.1.2  Anonymous Pipes====&lt;br /&gt;
NUMADD and NUMARITH are pretty silly little programs; a moment's&lt;br /&gt;
consideration will show how the inheritance architecture applies to more&lt;br /&gt;
realistic programs. An example is the TREE program that runs when the TREE&lt;br /&gt;
command is given to CMD.EXE. TREE inherits the STDIN and STDOUT handles,&lt;br /&gt;
but it does not use STDIN; it merely writes output to STDOUT. As a result,&lt;br /&gt;
when the user types TREE at a CMD.EXE prompt, the output appears on the&lt;br /&gt;
screen. When TREE appears in a batch file, the output appears in the LOG&lt;br /&gt;
file or on the screen, depending on where STDOUT is pointing.&lt;br /&gt;
     This is all very useful, but what if an application wants to further&lt;br /&gt;
process the output of the child program rather than having the child's&lt;br /&gt;
output intermingled with the application's output? OS/2 does this with&lt;br /&gt;
anonymous pipes. The adjective anonymous distinguishes these pipes from a&lt;br /&gt;
related facility, named pipes, which are not implemented in OS/2 version&lt;br /&gt;
1.0.&lt;br /&gt;
     An anonymous pipe is a data storage buffer that OS/2 maintains. When a&lt;br /&gt;
process opens an anonymous pipe, it receives two file handles--one for&lt;br /&gt;
writing and one for reading. Data can be written to the write handle via&lt;br /&gt;
the DosWrite call and then read back via the read handle and the DosRead&lt;br /&gt;
call. An anonymous pipe is similar to a file in that it is written and read&lt;br /&gt;
via file handle operations, but an anonymous pipe and a file are&lt;br /&gt;
significantly different. Pipe data is stored only in RAM buffers, not on a&lt;br /&gt;
disk, and is accessed only in FIFO (First In First Out) fashion. The&lt;br /&gt;
DosChgFilePtr operation is illegal on pipe handles.&lt;br /&gt;
     An anonymous pipe is of little value to a single process, since it&lt;br /&gt;
acts as a simple FIFO (First In First Out) storage buffer of limited size&lt;br /&gt;
and since the data has to be copied to and from OS/2's pipe buffers when&lt;br /&gt;
DosWrites and DosReads are done. What makes an anonymous pipe valuable is&lt;br /&gt;
that child processes inherit file handles. A parent process can create an&lt;br /&gt;
anonymous pipe and then create a child process, and the child process&lt;br /&gt;
inherits the anonymous pipe handles. The child process can then write to&lt;br /&gt;
the pipe's write handle, and the parent process can read the data via the&lt;br /&gt;
pipe's read handle. Once we add the DosDupHandle function, which allows&lt;br /&gt;
handles to be renumbered, and the standard file handles (STDIN, STDOUT, and&lt;br /&gt;
STDERR), we have the makings of a powerful capability.&lt;br /&gt;
     Let's go back to our NUMARITH and NUMADD programs. Suppose NUMARITH&lt;br /&gt;
wants to use NUMADD's services but that NUMARITH wants to process NUMADD's&lt;br /&gt;
results itself rather than having them appear in NUMARITH's output.&lt;br /&gt;
Furthermore, assume that NUMARITH doesn't want NUMADD to read its arguments&lt;br /&gt;
from NUMARITH's input file; NUMARITH wants to supply NUMADD's arguments&lt;br /&gt;
itself. NUMARITH can do this by following these steps:&lt;br /&gt;
&lt;br /&gt;
# Create two anonymous pipes.&lt;br /&gt;
# Preserve the item pointed to by the current STDIN and STDOUT handles (the item can be a file, a device, or a pipe) by using DosDupHandle to provide a duplicate handle. The handle numbers of the duplicates may be any number as long as it is not the number of STDIN, STDOUT, or STDERR. We know that this is the case because DosDupHandle assigns a handle number that is not in use, and the standard handle numbers are always in use.&lt;br /&gt;
# Close STDIN and STDOUT via DosClose. Whatever object is &amp;quot;on the other end&amp;quot; of the handle is undisturbed because the application still has the object open on another handle.&lt;br /&gt;
# Use DosDupHandle to make the STDIN handle a duplicate of one of the pipe's input handles, and use DosDupHandle to make the STDOUT handle a duplicate of the other pipe's output handle.&lt;br /&gt;
# Create the child process via DosExecPgm.&lt;br /&gt;
# Close the STDIN and STDOUT handles that point to the pipes, and use DosDupHandle and DosClose to effectively rename the objects originally described by STDIN and STDOUT back to those handles.&lt;br /&gt;
&lt;br /&gt;
     The result of this operation is shown in Figure 4-4. NUMADD's STDIN&lt;br /&gt;
and STDOUT handles are pointing to two anonymous pipes, and the parent&lt;br /&gt;
process is holding the other end of those pipes. The parent process used&lt;br /&gt;
DosDupHandle and DosClose to effectively &amp;quot;rename&amp;quot; the STDIN and STDOUT&lt;br /&gt;
handles temporarily so that the child process can inherit the pipe handles&lt;br /&gt;
rather than its parent's STDIN and STDOUT. At this point the parent,&lt;br /&gt;
NUMARITH, can write input values into the pipe connected to NUMADD's STDIN&lt;br /&gt;
and read NUMADD's output from the pipe connected to NUMADD's STDOUT.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT&lt;br /&gt;
 ÄÄÄÄÄÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
          ÀÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄÙ&lt;br /&gt;
          ÚÄÄÄÄÄÄ´          ³�ÄÄÄÄÄÄÄ¿&lt;br /&gt;
          ³      ÀÄÄÄÄÄÂÄÄÄÄÙ        ³&lt;br /&gt;
anonymous ³            ³ DosExecPgm  ³ anonymous&lt;br /&gt;
  pipe    ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿        ³   pipe&lt;br /&gt;
          ³      ³          ³        ³&lt;br /&gt;
          ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                 ³          ³&lt;br /&gt;
                 ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-4.  Invoking NUMADD via an anonymous pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     If you compare Figure 4-4 with Listing 4-2, Figure 4-2, and Figure&lt;br /&gt;
4-3, you see another key feature of this architecture: NUMADD has no&lt;br /&gt;
special code or design to allow it to communicate directly with NUMARITH.&lt;br /&gt;
NUMADD functions correctly whether working from the keyboard and screen,&lt;br /&gt;
from data files, or as a tool for another program. The architecture is&lt;br /&gt;
fully recursive: If NUMADD invokes a child process to help it with its&lt;br /&gt;
work, everything still functions correctly. Whatever mechanism NUMADD uses&lt;br /&gt;
to interact with its child/tool program is invisible to NUMARITH. Likewise,&lt;br /&gt;
if another program uses NUMARITH as a tool, that program is not affected by&lt;br /&gt;
whatever mechanism NUMARITH uses to do its work.&lt;br /&gt;
     This example contains one more important point. Earlier I said that a&lt;br /&gt;
process uses DosRead and DosWrite to do I/O over a pipe, yet our NUMADD&lt;br /&gt;
program uses fscanf and fprintf, two C language library routines. fscanf&lt;br /&gt;
and fprintf themselves call DosRead and DosWrite, and because a pipe handle&lt;br /&gt;
is indistinguishable from a file handle or a device handle for these&lt;br /&gt;
operations, not only does NUMADD work unchanged with pipes, but the library&lt;br /&gt;
subroutines that it calls work as well. This is another example of the&lt;br /&gt;
principle of encapsulation as expressed in OS/2,&lt;br /&gt;
4. And in UNIX, from which this aspect of the architecture was&lt;br /&gt;
adapted.&lt;br /&gt;
4 in which the differences&lt;br /&gt;
among pipes, files, and devices are hidden behind, or encapsulated in, a&lt;br /&gt;
standardized handle interface.&lt;br /&gt;
&lt;br /&gt;
====4.1.3  Details, Details====&lt;br /&gt;
While presenting the &amp;quot;big picture&amp;quot; of the OS/2 tasking and tool&lt;br /&gt;
architecture, I omitted various important details. This section discusses&lt;br /&gt;
them, in no particular order.&lt;br /&gt;
     STDERR (handle value 2) is an output handle on which error messages&lt;br /&gt;
are written. STDERR is necessary because STDOUT is a program's normal&lt;br /&gt;
output stream. For example, suppose a user types:&lt;br /&gt;
&lt;br /&gt;
     DIR  filename &amp;gt;logfile&lt;br /&gt;
&lt;br /&gt;
where the file filename does not exist. If STDERR did not exist as a&lt;br /&gt;
separate entity, no error message would appear, and logfile would&lt;br /&gt;
apparently be created. Later, when the user attempted to examine the&lt;br /&gt;
contents of the file, he or she would see the following message:&lt;br /&gt;
&lt;br /&gt;
     FILE NOT FOUND&lt;br /&gt;
&lt;br /&gt;
For this reason, STDERR generally points to the display screen, and&lt;br /&gt;
applications rarely redirect it.&lt;br /&gt;
     The special meanings of STDIN, STDOUT, and STDERR are not hard coded&lt;br /&gt;
into the OS/2 kernel; they are system conventions. All programs should&lt;br /&gt;
follow these conventions at all times to preserve the flexibility and&lt;br /&gt;
utility of OS/2's tool-based architecture. Even programs that don't do&lt;br /&gt;
handle I/O on the STD handles must still follow the architectural&lt;br /&gt;
conventions (see Chapter 14, Interactive Programs). However, the OS/2&lt;br /&gt;
kernel code takes no special action nor does it contain any special cases&lt;br /&gt;
in support of this convention. Various OS/2 system utilities, such as&lt;br /&gt;
CMD.EXE and the presentation manager, do contain code in support of this&lt;br /&gt;
convention.&lt;br /&gt;
     I mentioned that a child process inherits all its parent's file&lt;br /&gt;
handles unless the parent has explicitly marked a handle &amp;quot;no inherit.&amp;quot; The&lt;br /&gt;
use of STDIN, STDOUT, and STDERR in an inherited environment has been&lt;br /&gt;
discussed, but what of the other handles?&lt;br /&gt;
     Although a child process inherits all of its parent's file handles, it&lt;br /&gt;
is usually interested only in STDIN, STDOUT, and STDERR. What happens to&lt;br /&gt;
the other handles? Generally, nothing. Only handle values 0 (STDIN), 1&lt;br /&gt;
(STDOUT), and 2 (STDERR) have explicit meaning. All other file handles are&lt;br /&gt;
merely magic cookies that OS/2 returns for use in subsequent I/O calls.&lt;br /&gt;
OS/2 doesn't guarantee any particular range or sequence of handle values,&lt;br /&gt;
and applications should never use or rely on explicit handle values other&lt;br /&gt;
than the STD ones.&lt;br /&gt;
     Thus, for example, if a parent process has a file open on handle N and&lt;br /&gt;
the child process inherits that handle, little happens. The child process&lt;br /&gt;
won't get the value N back as a result of a DosOpen because the handle is&lt;br /&gt;
already in use. The child will never issue operations to handle N because&lt;br /&gt;
it didn't open any such handle and knows nothing of its existence. Two side&lt;br /&gt;
effects can result from inheriting &amp;quot;garbage&amp;quot; handles. One is that the&lt;br /&gt;
object to which the handle points cannot be closed until both the parent&lt;br /&gt;
and the child close their handles. Because the child knows nothing of the&lt;br /&gt;
handle, it won't close it. Therefore, a handle close issued by a parent&lt;br /&gt;
won't be effective until the child and all of that child's descendant&lt;br /&gt;
processes (which in turn inherited the handle) have terminated. If another&lt;br /&gt;
application needs the file or device, it is unavailable because a child&lt;br /&gt;
process is unwittingly holding it open.&lt;br /&gt;
     The other side effect is that each garbage handle consumes an entry in&lt;br /&gt;
the child's handle space. Although you can easily increase the default&lt;br /&gt;
maximum of 20 open handles, a child process that intends to open only 10&lt;br /&gt;
files wouldn't request such an increase. If a parent process allows the&lt;br /&gt;
child to inherit 12 open files, the child will run out of available open&lt;br /&gt;
file handles. Writing programs that always raise their file handle limit is&lt;br /&gt;
not good practice because the garbage handles are extra overhead and the&lt;br /&gt;
files-held-open problem remains. Instead, parent programs should minimize&lt;br /&gt;
the number of garbage handles they allow child processes to inherit.&lt;br /&gt;
     Each time a program opens a file, it should do so with the DosOpen&lt;br /&gt;
request with the &amp;quot;don't inherit&amp;quot; bit set if the file is of no specific&lt;br /&gt;
interest to any child programs. If the bit is not set at open time, it can&lt;br /&gt;
be set later via DosSetFHandState. The bit is per-handle, not per-file; so&lt;br /&gt;
if a process has two handles open on the same file, it can allow one but&lt;br /&gt;
not the other to be inherited. Don't omit this step simply because you&lt;br /&gt;
don't plan to run any child processes; unbeknownst to you, dynlink library&lt;br /&gt;
routines may run child programs on your behalf. Likewise, in dynlink&lt;br /&gt;
programs the &amp;quot;no inherit&amp;quot; bit should be set when file opens are issued&lt;br /&gt;
because the client program may create child processes.&lt;br /&gt;
     Finally, do not follow the standard UNIX practice of blindly closing&lt;br /&gt;
file handles 3 through 20 during program initialization. Dynlink subsystems&lt;br /&gt;
are called to initialize themselves for a new client before control is&lt;br /&gt;
passed to the application itself. If subsystems have opened files during&lt;br /&gt;
that initialization, a blanket close operation will close those files and&lt;br /&gt;
cause the dynlink package to fail. All programs use dynlink subsystems,&lt;br /&gt;
whether they realize it or not, because both the OS/2 interface package and&lt;br /&gt;
the presentation manager are such subsystems. Accidentally closing a&lt;br /&gt;
subsystem's file handles can cause bizarre and inexplicable problems. For&lt;br /&gt;
example, when the VIO subsystem is initialized, it opens a handle to the&lt;br /&gt;
screen device. A program that doesn't call VIO may believe that closing&lt;br /&gt;
this handle is safe, but it's not. If a handle write is done to STDOUT when&lt;br /&gt;
STDOUT points to the screen device, OS/2 calls VIO on behalf of the&lt;br /&gt;
application--with potentially disastrous effect.&lt;br /&gt;
     I discussed how a child process, inheriting its parent's STDIN and&lt;br /&gt;
STDOUT, extracts its input from the parent's input stream and intermingles&lt;br /&gt;
its output in the parent's output stream. What keeps the parent process&lt;br /&gt;
from rereading the input consumed by the child, and what keeps the parent&lt;br /&gt;
from overwriting the output data written by the child? The answer is in the&lt;br /&gt;
distinction between duplicated or inherited handles to a file and two&lt;br /&gt;
handles to the same file that are the result of two separate opens.&lt;br /&gt;
     Each time a file is opened, OS/2 allocates a handle to that process&lt;br /&gt;
and makes an entry in the process's handle table. This entry then points to&lt;br /&gt;
the System File Table (SFT) inside OS/2. The SFT contains the seek pointer&lt;br /&gt;
to the file--the spot in the file that is currently being read from or&lt;br /&gt;
written to. When a handle is inherited or duplicated, the new handle points&lt;br /&gt;
to the same SFT entry as the original. Thus, for example, the child's STDIN&lt;br /&gt;
handle shares the same seek pointer as the parent's STDIN handle. When our&lt;br /&gt;
example child program NUMADD read two lines from STDIN, it advanced the&lt;br /&gt;
seek pointer of its own STDIN and that of its parent's STDIN (and perhaps&lt;br /&gt;
that of its grandparent's STDIN and so forth). Likewise, when the child&lt;br /&gt;
writes to STDOUT, the seek pointer advances on STDOUT so that subsequent&lt;br /&gt;
writing by the parent appends to the child's output rather than overwriting&lt;br /&gt;
it.&lt;br /&gt;
     This mechanism has two important ramifications. First, in a situation&lt;br /&gt;
such as our NUMARITH and NUMADD example, the parent process must refrain&lt;br /&gt;
from I/O to the STD handles until the child process has completed so that&lt;br /&gt;
the input or output data doesn't intermingle. Second, the processes must be&lt;br /&gt;
careful in the way they buffer data to and from the STD handles.&lt;br /&gt;
     Most programs that read data from STDIN do so until they encounter an&lt;br /&gt;
EOF (End Of File). These programs can buffer STDIN as they wish. A program&lt;br /&gt;
such as NUMARITH, in which child processes read some but not all of its&lt;br /&gt;
STDIN data, cannot use buffering because the read-ahead data in the buffer&lt;br /&gt;
might be the data that the child process was to read. NUMARITH can't &amp;quot;put&lt;br /&gt;
the data back&amp;quot; by backing up the STDIN seek pointer because STDIN might be&lt;br /&gt;
pointing to a device (such as the keyboard) or to a pipe that cannot be&lt;br /&gt;
seeked. Likewise, because NUMADD was designed to read only two lines&lt;br /&gt;
of input, it also must read STDIN a character at a time to be sure it&lt;br /&gt;
doesn't &amp;quot;overshoot&amp;quot; its two lines.&lt;br /&gt;
     Programs must also be careful about buffering STDOUT. In general, they&lt;br /&gt;
can buffer STDOUT as they wish, but they must be sure to flush out any&lt;br /&gt;
buffered data before they execute any child processes that might write to&lt;br /&gt;
STDOUT.&lt;br /&gt;
     Finally, what if a parent process doesn't want a child process to&lt;br /&gt;
inherit STDIN, STDOUT, or STDERR? The parent process should not mark those&lt;br /&gt;
handles &amp;quot;no inherit&amp;quot; because then those handles will not be open when the&lt;br /&gt;
child process starts. The OS/2 kernel has no built-in recognition of the&lt;br /&gt;
STD file handles; so if the child process does a DosOpen and handle 1 is&lt;br /&gt;
unopened (because the process's parent set &amp;quot;no inherit&amp;quot; on handle 1), OS/2&lt;br /&gt;
might open the new file on handle 1. As a result, output that the child&lt;br /&gt;
process intends for STDOUT appears in the other file that unluckily was&lt;br /&gt;
assigned handle number 1.&lt;br /&gt;
     If for some reason a child process should not inherit a STD handle,&lt;br /&gt;
the parent should use the DosDupHandle/rename technique to cause that&lt;br /&gt;
handle to point to the NULL device. You do this by opening a handle on the&lt;br /&gt;
NULL device and then moving that handle to 0, 1, or 2 with DosDupHandle.&lt;br /&gt;
This technique guarantees that the child's STD handles will all be&lt;br /&gt;
open.&lt;br /&gt;
     The subject of STDIN, STDOUT, and handle inheritance comes up again in&lt;br /&gt;
Chapter 14, Interactive Programs.&lt;br /&gt;
&lt;br /&gt;
===4.2  PIDs and Command Subtrees===&lt;br /&gt;
&lt;br /&gt;
The PID (process identification) is a unique code that OS/2 assigns to each&lt;br /&gt;
process when it is created. The number is a magic cookie. Its value has no&lt;br /&gt;
significance to any process; it's simply a name for a process. The PID may&lt;br /&gt;
be any value except 0. A single PID value is never assigned to two&lt;br /&gt;
processes at the same time. PID values are reused but not &amp;quot;rapidly.&amp;quot; You&lt;br /&gt;
can safely remember a child's PID and then later attempt to affect that&lt;br /&gt;
child by using the PID in an OS/2 call. Even if the child process has died&lt;br /&gt;
unexpectedly, approximately 65,000 processes would have to be created&lt;br /&gt;
before the PID value might be reassigned; even a very active system takes&lt;br /&gt;
at least a day to create, execute, and terminate that many processes.&lt;br /&gt;
     I've discussed at some length the utility of an architecture in which&lt;br /&gt;
child programs can create children and those children can create&lt;br /&gt;
grandchildren and so on. I've also emphasized that the parent need not know&lt;br /&gt;
the architecture of a child process--whether the child process creates&lt;br /&gt;
children and grandchildren of its own. The parent need not know and indeed&lt;br /&gt;
should not know because such information may make the parent dependent on a&lt;br /&gt;
particular implementation of a child or grandchild program, an&lt;br /&gt;
implementation that might change. Given that a parent process starting up a&lt;br /&gt;
child process can't tell if that child creates its own descendants, how can&lt;br /&gt;
the parent process ask the system if the work that the child was to do has&lt;br /&gt;
been completed? The system could tell the parent whether the child process&lt;br /&gt;
is still alive, but this is insufficient. The child process may have farmed&lt;br /&gt;
out all its work to one or more grandchildren and then terminated itself&lt;br /&gt;
before the actual work was started. Furthermore, the parent process may&lt;br /&gt;
want to change the priority of the process(es) that it has created or even&lt;br /&gt;
terminate them because an error was detected or because the user typed&lt;br /&gt;
Ctrl-C.&lt;br /&gt;
     All these needs are met with a concept called the command subtree.&lt;br /&gt;
When a child process is created, its parent is told the child's PID. The&lt;br /&gt;
PID is the name of the single child process, and when taken as a command&lt;br /&gt;
subtree ID, this PID is the name of the entire tree of descendant processes&lt;br /&gt;
of which the child is the root. In other words, when used as a command&lt;br /&gt;
subtree ID, the PID refers not only to the child process but also to any of&lt;br /&gt;
its children and to any children that they may have and so on. A single&lt;br /&gt;
command subtree can conceivably contain dozens of processes (see Figure&lt;br /&gt;
4-5 and Figure 4-6).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                ³  PARENT   ³&lt;br /&gt;
                ÀÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
                ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                ³     A     ³&lt;br /&gt;
                ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
      ÚÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿                   ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
³     B     ³                   ³     C     ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ                   ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     D     ³     ÚÄÄÄÄÄÁÄÄÄÄÄ¿               ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ³     F     ³               ³     G     ³&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÀÄÄÄÄÄÄÄÄÄÄÄÙ               ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
³     E     ³                              ÚÄÄÄÄÄÙ     ÀÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÙ                        ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     H     ³     ³     I     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     J     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-5.  Command subtree. A is the root of (one of) the parent's&lt;br /&gt;
command subtrees. B and C are the root of two of A's subtrees and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                ³  PARENT   ³&lt;br /&gt;
                ÀÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
                ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                ³°°°°°A°°°°°³&lt;br /&gt;
                ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
      ÚÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿                   ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
³     B     ³                   ³     C     ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ                   ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     D     ³     ÚÄÄÄÄÄÁÄÄÄÄÄ¿               ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ³     F     ³               ³     G     ³&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÀÄÄÄÄÄÄÄÄÄÄÄÙ               ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
³°°°°°E°°°°°³                              ÚÄÄÄÄÄÙ     ÀÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÙ                        ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³°°°°°H°°°°°³     ³     I     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     J     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-6.   Command subtree. The shaded processes have died, but the&lt;br /&gt;
subtrees remain. PARENT can still use subtree A to affect all remaining&lt;br /&gt;
subprocesses. Likewise, an operation by C on subtree G will affect&lt;br /&gt;
process J.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Some OS/2 functions, such as DosCWait and DosKillProcess, can take&lt;br /&gt;
either PID or command subtree values, depending on the subfunction&lt;br /&gt;
requested. When the PID form is used, only the named process is affected.&lt;br /&gt;
When the command subtree form is used, the named process and all its&lt;br /&gt;
descendants are affected. This is true even if the child process no longer&lt;br /&gt;
exists or if the family tree of processes contains holes as a result of&lt;br /&gt;
process terminations.&lt;br /&gt;
     No statute of limitations applies to the use of the command subtree&lt;br /&gt;
form. That is, even if child process X died a long time ago, OS/2 still&lt;br /&gt;
allows references to the command subtree X. Consequently, OS/2 places one&lt;br /&gt;
simple restriction on the use of command subtrees so that it isn't forced&lt;br /&gt;
to keep around a complete process history forever: Only the direct parent&lt;br /&gt;
of process X can reference the command subtree X. In other words, X's&lt;br /&gt;
grandparent process can't learn X's PID from its parent and then issue&lt;br /&gt;
command subtree forms of commands; only X's direct parent can. This&lt;br /&gt;
puts an upper limit on the amount and duration of command subtree&lt;br /&gt;
information that OS/2 must retain; when a process terminates, information&lt;br /&gt;
pertaining to its command subtrees can be discarded. The command subtree&lt;br /&gt;
concept is recursive. OS/2 discards information about the terminated&lt;br /&gt;
process's own command subtrees, but if any of its descendant processes&lt;br /&gt;
still exist, the command subtree information pertaining to their child&lt;br /&gt;
processes is retained. And those surviving descendants are still part of&lt;br /&gt;
the command subtree belonging to the terminated process's parent&lt;br /&gt;
process.&lt;br /&gt;
5. Assuming that the parent process itself still exists. In any&lt;br /&gt;
case, all processes are part of a nested set of command subtrees&lt;br /&gt;
belonging to all its surviving ancestor processes.&lt;br /&gt;
5&lt;br /&gt;
&lt;br /&gt;
===4.3  DosExecPgm===&lt;br /&gt;
&lt;br /&gt;
To execute a child process, you use the DosExecPgm call. The form of the&lt;br /&gt;
call is shown in Listing 4-3.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extern unsigned far pascal DOSEXECPGM (&lt;br /&gt;
          char far                 *OBJNAMEBUF,&lt;br /&gt;
          unsigned                 OBJNAMEBUFL,&lt;br /&gt;
          unsigned                 EXECTYPE,&lt;br /&gt;
          char far                 *ARGSTRING,&lt;br /&gt;
          char far                 *ENVSTRING,&lt;br /&gt;
          struct ResultCodes far   *CODEPID,&lt;br /&gt;
          char far                 *PGMNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Listing 4-3.  DosExecPgm call.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The obj namebuf arguments provide an area where OS/2 can return a&lt;br /&gt;
character string if the DosExecPgm function fails. In MS-DOS version 2.0&lt;br /&gt;
and later, the EXEC function was quite simple: It loaded a file into&lt;br /&gt;
memory. Little could go wrong: file not found, file bad format,&lt;br /&gt;
insufficient memory, to name some possibilities. A simple error code&lt;br /&gt;
sufficed to diagnose problems. OS/2's dynamic linking facility is much more&lt;br /&gt;
complicated. The earliest prototype versions of OS/2 were missing these&lt;br /&gt;
obj namebuf arguments, and engineers were quickly frustrated by the error&lt;br /&gt;
code &amp;quot;dynlink library load failed.&amp;quot; &amp;quot;Which library was it? The application&lt;br /&gt;
references seven of them! But all seven of those libraries are alive and&lt;br /&gt;
well. Perhaps it was a library that one of those libraries referenced. But&lt;br /&gt;
which libraries do they reference? Gee, I dunno...&amp;quot; For this reason, the&lt;br /&gt;
object name arguments were added. The buffer is a place where OS/2 returns&lt;br /&gt;
the name of the missing or defective library or other load object, and the&lt;br /&gt;
length argument tells OS/2 the maximum size of the buffer area. Strings&lt;br /&gt;
that will not fit in the area are truncated.&lt;br /&gt;
     The exectype word describes the form of the DosExecPgm. The values are&lt;br /&gt;
as follows.&lt;br /&gt;
&lt;br /&gt;
     0:  Execute the child program synchronously. The thread issuing the&lt;br /&gt;
         DosExecPgm will not execute further until the child process has&lt;br /&gt;
         finished executing. The thread returns from DosExecPgm when the&lt;br /&gt;
         child process itself terminates, not when the command subtree has&lt;br /&gt;
         terminated. This form of DosExecPgm is provided for ease in&lt;br /&gt;
         converting MS-DOS version 3.x programs to OS/2. It is considered&lt;br /&gt;
         obsolete and should generally be avoided. Its use may interfere&lt;br /&gt;
         with proper Ctrl-C and Ctrl-Break handling (see Chapter 14,&lt;br /&gt;
         Interactive Programs).&lt;br /&gt;
&lt;br /&gt;
     1:  Execute the program asynchronously. The child process begins&lt;br /&gt;
         executing as soon as the scheduler allows; the calling thread&lt;br /&gt;
         returns from the DosExecPgm call immediately. You cannot assume&lt;br /&gt;
         that the child process has received CPU time before the parent&lt;br /&gt;
         thread returns from the DosExecPgm call; neither can you assume&lt;br /&gt;
         that the child process has not received such CPU service. This&lt;br /&gt;
         form instructs OS/2 not to bother remembering the child's&lt;br /&gt;
         termination code for a future DosCWait call. It is used when the&lt;br /&gt;
         parent process doesn't care what the result code of the child may&lt;br /&gt;
         be or when or if it completes, and it frees the parent from the &lt;br /&gt;
         necessity of issuing DosCWait calls. Programs executing other&lt;br /&gt;
         programs as tools would rarely use this form. This form might be&lt;br /&gt;
         used by a system utility, for example, whose job is to fire off&lt;br /&gt;
         certain programs once an hour but not to take any action or notice&lt;br /&gt;
         should any of those programs fail. Note that, unlike the detach&lt;br /&gt;
         form described below, the created process is still recorded as a&lt;br /&gt;
         child of the executing parent. The parent can issue a DosCWait&lt;br /&gt;
         call to determine whether the child subtree is still executing,&lt;br /&gt;
         although naturally there is no return code when the child process&lt;br /&gt;
         does terminate.&lt;br /&gt;
&lt;br /&gt;
     2:  This form is similar to form 1 in that it executes the child&lt;br /&gt;
         process asynchronously, but it instructs OS/2 to retain the child&lt;br /&gt;
         process's exit code for future examination by DosCWait. Thus, a&lt;br /&gt;
         program can determine the success or failure of a child process.&lt;br /&gt;
         The parent process should issue an appropriate DosCWait &amp;quot;pretty&lt;br /&gt;
         soon&amp;quot; because OS/2 version 1.0 maintains about 2600 bytes of data&lt;br /&gt;
         structures for a dead process whose parent is expected to DosCWait&lt;br /&gt;
         but hasn't yet done so. To have one of these structures lying&lt;br /&gt;
         around for a few minutes is no great problem, but programs need to&lt;br /&gt;
         issue DosCWaits in a timely fashion to keep from clogging the&lt;br /&gt;
         system with the carcasses of processes that finished hours&lt;br /&gt;
         ago.&lt;br /&gt;
            OS/2 takes care of all the possible timing considerations, so&lt;br /&gt;
         it's OK to issue the DosCWait before or after the child process&lt;br /&gt;
         has completed. Although a parent process can influence the&lt;br /&gt;
         relative assignment of CPU time between the child and parent&lt;br /&gt;
         processes by setting its own and its child's relative priorities,&lt;br /&gt;
         there is no reliable way of determining which process will run&lt;br /&gt;
         when. Write your program in such a way that it doesn't matter if&lt;br /&gt;
         the child completes before or after the DosCWait, or use some form&lt;br /&gt;
         of IPC to synchronize the execution of parent and child processes.&lt;br /&gt;
         See 4.4 DosCWait for more details.&lt;br /&gt;
&lt;br /&gt;
     3:  This form is used by the system debugger, CodeView. The system&lt;br /&gt;
         architecture does not allow one process to latch onto another&lt;br /&gt;
         arbitrary process and start examining and perhaps modifying the&lt;br /&gt;
         target process's execution. Such a facility would result in a&lt;br /&gt;
         massive side effect and is contrary to the tenet of encapsulation&lt;br /&gt;
         in the design religion. Furthermore, such a facility would prevent&lt;br /&gt;
         OS/2 from ever providing an environment secure against malicious&lt;br /&gt;
         programs. OS/2 solves this problem with the DosPtrace function,&lt;br /&gt;
         which peeks, pokes, and controls a child process. This function is&lt;br /&gt;
         allowed to affect only processes that were executed with this&lt;br /&gt;
         special mode value of 3.&lt;br /&gt;
&lt;br /&gt;
     4:  This form executes the child process asynchronously but also&lt;br /&gt;
         detaches it from the process issuing the DosExecPgm call. A&lt;br /&gt;
         detached process does not inherit the parent process's screen&lt;br /&gt;
         group; instead, it executes in a special invalid screen group. Any&lt;br /&gt;
         attempt to do screen, keyboard, or mouse I/O from within this&lt;br /&gt;
         screen group returns an error code. The system does not consider a&lt;br /&gt;
         detached process a child of the parent process; the new process&lt;br /&gt;
         has no connection with the creating process. In other words, it's&lt;br /&gt;
         parentless. This form of DosExecPgm is used to create daemon&lt;br /&gt;
         programs that execute without direct interaction with the user.&lt;br /&gt;
&lt;br /&gt;
     The EnvString argument points to a list of ASCII text strings that&lt;br /&gt;
contain environment values. OS/2 supports a separate environment block for&lt;br /&gt;
each process. A process typically inherits its parent's environment&lt;br /&gt;
strings. In this case, the EnvPointer argument should be NULL, which tells&lt;br /&gt;
OS/2 to supply the child process with a copy of the parent's environment&lt;br /&gt;
strings (see Listing 4-4).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PATH=C:\DOS;C:\EDITORS;C:\TOOLS;C:\XTOOLS;C:\BIN;C:\UBNET&lt;br /&gt;
INCLUDE=\include&lt;br /&gt;
TERM=h19&lt;br /&gt;
INIT=c:\tmp&lt;br /&gt;
HOME=c:\tmp&lt;br /&gt;
USER=c:\tmp&lt;br /&gt;
TEMP=c:\tmp&lt;br /&gt;
TERM=ibmpc&lt;br /&gt;
LIB=c:\lib&lt;br /&gt;
PROMPT=($p)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Listing 4-4.  A typical environment string set.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The environment strings are normally used to customize a particular&lt;br /&gt;
execution environment. For example, suppose a user creates two screen&lt;br /&gt;
groups, each running a copy of CMD.EXE. Each CMD.EXE is a direct child of&lt;br /&gt;
the presentation manager, which manages and creates screen groups, and each&lt;br /&gt;
is also the ancestor of all processes that will run in its particular&lt;br /&gt;
screen group. If the user is running utility programs that use the&lt;br /&gt;
environment string &amp;quot;TEMP=&amp;lt;dirname&amp;gt;&amp;quot; to specify a directory to hold&lt;br /&gt;
temporary files, he or she may want to specify different TEMP= values for&lt;br /&gt;
each copy of CMD.EXE. As a result, the utilities that use TEMP= will access&lt;br /&gt;
the proper directory, depending on which screen group they are run in,&lt;br /&gt;
because they will have inherited the proper TEMP= environment string from&lt;br /&gt;
their CMD.EXE ancestor. See Chapter 10, Environment Strings, for a complete&lt;br /&gt;
discussion.&lt;br /&gt;
     Because of the inheritable nature of environment strings, parent&lt;br /&gt;
processes that edit the environment list should remove or edit only those&lt;br /&gt;
strings with which they are involved; any unrecognized strings should be&lt;br /&gt;
preserved.&lt;br /&gt;
&lt;br /&gt;
===4.4  DosCWait===&lt;br /&gt;
&lt;br /&gt;
When a process executes a child process, it usually wants to know when that&lt;br /&gt;
child process has completed and whether the process succeeded or failed.&lt;br /&gt;
DosCWait, the OS/2 companion function to DosExecPgm, returns such&lt;br /&gt;
information. Before we discuss DosCWait in detail, two observations are in&lt;br /&gt;
order. First, although each DosExecPgm call starts only a single process,&lt;br /&gt;
it's possible--and not uncommon--for that child process to create its own&lt;br /&gt;
children and perhaps they their own and so on. A program should not assume&lt;br /&gt;
that its child process won't create subchildren; instead, programs should&lt;br /&gt;
use the command-subtree forms of DosCWait. One return code from the direct&lt;br /&gt;
child process (that is, the root of the command subtree) is sufficient&lt;br /&gt;
because if that direct child process invokes other processes to do work for&lt;br /&gt;
it the direct child is responsible for monitoring their success via&lt;br /&gt;
DosCWait. In other words, if a child process farms out some of its work to&lt;br /&gt;
a grandchild process and that grandchild process terminates in error, then&lt;br /&gt;
the child process should also terminate with an error return.&lt;br /&gt;
     Second, although we discuss the process's child, in fact processes can&lt;br /&gt;
have multiple child processes and therefore multiple command subtrees at&lt;br /&gt;
any time. The parent process may have interconnected the child processes&lt;br /&gt;
via anonymous pipes, or they may be independent of one another. Issuing&lt;br /&gt;
separate DosCWaits for each process or subtree is unnecessary. The form of&lt;br /&gt;
the DosCWait call is shown in Listing 4-5.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
extern unsigned far pascal DOSCWAIT (&lt;br /&gt;
     unsigned                 ACTIONCODE,&lt;br /&gt;
     unsigned                 WAITOPTION,&lt;br /&gt;
     struct ResultCodes far   *RESULTWORD,&lt;br /&gt;
     unsigned far             *PIDRETURN,&lt;br /&gt;
     unsigned                 PID);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-5.  DosCWait function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Three of the arguments affect the scope of the command: ActionCode,&lt;br /&gt;
WaitOption, and PID. It's easiest to show how these interact by arranging&lt;br /&gt;
their possible values into tables.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Command Subtrees&lt;br /&gt;
&lt;br /&gt;
     These forms of DosCWait operate on the entire command subtree, which&lt;br /&gt;
     may, of course, consist of only one child process. We recommend these&lt;br /&gt;
     forms because they will continue to work correctly if the child&lt;br /&gt;
     process is changed to use more or fewer child processes of its own.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId   Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     1            0            n           Wait until the command subtree&lt;br /&gt;
                                           has completed and then return&lt;br /&gt;
                                           the direct child's termination&lt;br /&gt;
                                           code.&lt;br /&gt;
&lt;br /&gt;
     1            1            n           If the command subtree has&lt;br /&gt;
                                           completed, return the direct&lt;br /&gt;
                                           child's termination code.&lt;br /&gt;
                                           Otherwise, return the&lt;br /&gt;
                                           ERROR_CHILD_NOT_COMPLETE&lt;br /&gt;
                                           error code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Individual Processes&lt;br /&gt;
&lt;br /&gt;
     These forms of DosCWait are used to monitor individual child&lt;br /&gt;
     processes. The processes must be direct children; grandchild or&lt;br /&gt;
     unrelated processes cannot be DosCWaited. Use these forms only when&lt;br /&gt;
     the child process is part of the same application or software package&lt;br /&gt;
     as the parent process; the programmer needs to be certain that she or&lt;br /&gt;
     he can safely ignore the possibility that grandchild processes might&lt;br /&gt;
     still be running after the direct child has terminated.&lt;br /&gt;
1. It is in itself not an error to collect a child process's&lt;br /&gt;
termination code via DosCWait while that child still&lt;br /&gt;
has living descendant processes. However, such a case generally&lt;br /&gt;
means that the child's work, whatever that was, is not yet complete.&lt;br /&gt;
1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId   Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     0            0            0           DosCWait returns as soon as a&lt;br /&gt;
                                           direct child process terminates.&lt;br /&gt;
                                           If a child process had already&lt;br /&gt;
                                           terminated at the time of this&lt;br /&gt;
                                           call, it will return&lt;br /&gt;
                                           immediately.&lt;br /&gt;
&lt;br /&gt;
     0            0            N           DosCWait returns as soon as the&lt;br /&gt;
                                           direct child process N&lt;br /&gt;
                                           terminates. If it had already&lt;br /&gt;
                                           terminated at the time of the&lt;br /&gt;
                                           call, DosCWait returns&lt;br /&gt;
                                           immediately.&lt;br /&gt;
&lt;br /&gt;
     0            1            0           DosCWait checks for a terminated&lt;br /&gt;
                                           direct child process. If one is&lt;br /&gt;
                                           found, its status is returned.&lt;br /&gt;
                                           If none is found, an error code&lt;br /&gt;
                                           is returned.&lt;br /&gt;
&lt;br /&gt;
     0            1            N           DosCWait checks the status of&lt;br /&gt;
                                           the direct child process N. If&lt;br /&gt;
                                           it is terminated, its status is&lt;br /&gt;
                                           returned. If it is still&lt;br /&gt;
                                           running, an error code is&lt;br /&gt;
                                           returned.&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Not Recommended&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId    Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     1            0            0            DosCWait waits until a direct&lt;br /&gt;
                                            child has terminated and then&lt;br /&gt;
                                            waits until all of that child's&lt;br /&gt;
                                            descendants have terminated. It&lt;br /&gt;
                                            then returns the direct child's&lt;br /&gt;
                                            exit code. This form does not&lt;br /&gt;
                                            wait until the first command&lt;br /&gt;
                                            subtree has terminated; it&lt;br /&gt;
                                            selects a command subtree based&lt;br /&gt;
                                            on the first direct child that&lt;br /&gt;
                                            terminates, and then it waits&lt;br /&gt;
                                            as long as necessary for the&lt;br /&gt;
                                            remainder of that command&lt;br /&gt;
                                            subtree, even if other command&lt;br /&gt;
                                            subtrees meanwhile complete.&lt;br /&gt;
&lt;br /&gt;
     1            1            0            This form returns&lt;br /&gt;
                                            ERROR_CHILD_NOT_COMPLETE if any&lt;br /&gt;
                                            process in any of the caller's&lt;br /&gt;
                                            subtrees is still executing. If&lt;br /&gt;
                                            all subtrees have terminated,&lt;br /&gt;
                                            this form returns with a direct&lt;br /&gt;
                                            child's exit code. If no direct&lt;br /&gt;
                                            child processes have unwaited&lt;br /&gt;
                                            exit codes, the code&lt;br /&gt;
                                            ERROR_WAIT_NO_CHILDREN is&lt;br /&gt;
                                            returned.&lt;br /&gt;
&lt;br /&gt;
===4.5  Control of Child Tasks and Command Subtrees===&lt;br /&gt;
&lt;br /&gt;
A parent process has only limited control over its child processes because&lt;br /&gt;
the system is designed to minimize the side effects, or cross talk, between&lt;br /&gt;
processes. Specifically, a parent process can affect its command subtrees&lt;br /&gt;
in two ways: It can change their CPU priority, and it can terminate (kill)&lt;br /&gt;
them. Once again, the command subtree is the recommended form for both&lt;br /&gt;
commands because that form is insensitive to the operational details of the&lt;br /&gt;
child process.&lt;br /&gt;
&lt;br /&gt;
====4.5.1  DosKillProcess====&lt;br /&gt;
A parent process may initiate a child process or command subtree and then&lt;br /&gt;
decide to terminate that activity before the process completes normally.&lt;br /&gt;
Often this comes about because of a direct user command or because the user&lt;br /&gt;
typed Ctrl-Break. See Chapter 14, Interactive Programs, for special&lt;br /&gt;
techniques concerning Ctrl-Break and Ctrl-C.&lt;br /&gt;
     DosKillProcess flags each process in the command subtree (or the&lt;br /&gt;
direct child process if that form is used) for termination. A process&lt;br /&gt;
flagged for termination normally terminates as soon as all its threads&lt;br /&gt;
leave the system (that is, as soon as all its threads return from all&lt;br /&gt;
system calls). The system aborts calls that might block for more than a&lt;br /&gt;
second or two, such as those that read a keyboard character, so that the&lt;br /&gt;
process can terminate quickly. A process can intercept SIGKILL to delay&lt;br /&gt;
termination longer, even indefinitely. Delaying termination inordinately&lt;br /&gt;
via SetSignalHandler/SIGKILL is very bad practice and is considered a bug&lt;br /&gt;
rather than a feature.&lt;br /&gt;
&lt;br /&gt;
====4.5.2  DosSetPrty====&lt;br /&gt;
A child process inherits its parent's process priority when the DosExecPgm&lt;br /&gt;
call is issued. After the DosExecPgm call, the parent can still change the&lt;br /&gt;
process priority of the command subtree or of only the direct child&lt;br /&gt;
process. The command subtree form is recommended; if the child process's&lt;br /&gt;
work deserves priority N, then any child processes that it executes to help&lt;br /&gt;
in that work should also run at priority N.&lt;br /&gt;
&lt;br /&gt;
==5  Threads and Scheduler/Priorities==&lt;br /&gt;
&lt;br /&gt;
===5.1  Threads===&lt;br /&gt;
&lt;br /&gt;
Computers consist of a CPU (central processing unit) and RAM (random access&lt;br /&gt;
memory). A computer program consists of a sequence of instructions that are&lt;br /&gt;
placed, for the most part, one after the other in RAM. The CPU reads each&lt;br /&gt;
instruction in sequence and executes it. The passage of the CPU through the&lt;br /&gt;
instruction sequence is called a thread of execution. All versions of MS-&lt;br /&gt;
DOS executed programs, so they necessarily supported a thread of execution.&lt;br /&gt;
OS/2 is unique, however, in that it supports multiple threads of execution&lt;br /&gt;
within a single process. In other words, a program can execute in two or&lt;br /&gt;
more spots in its code at the same time.&lt;br /&gt;
     Obviously, a multitasking system needs to support multiple threads in&lt;br /&gt;
a systemwide sense. Each process necessarily must have a thread; so if&lt;br /&gt;
there are ten processes in the system, there must be ten threads. Such an&lt;br /&gt;
existence of multiple threads in the system is invisible to the programmer&lt;br /&gt;
because each program executes with only one thread. OS/2 is different from&lt;br /&gt;
this because it allows an individual program to execute with multiple&lt;br /&gt;
threads if it desires.&lt;br /&gt;
     Because threads are elements of processes and because the process is&lt;br /&gt;
the unit of resource ownership, all threads that belong to the same process&lt;br /&gt;
share that process's resources. Thus, if one thread opens a file on file&lt;br /&gt;
handle X, all threads in that process can issue DosReads or DosWrites to&lt;br /&gt;
that handle. If one thread allocates a memory segment, all threads in that&lt;br /&gt;
process can access that memory segment. Threads are analogous to the&lt;br /&gt;
employees of a company. A company may consist of a single employee, or it&lt;br /&gt;
may consist of two or more employees that divide the work among them. Each&lt;br /&gt;
employee has access to the company's resources--its office space and&lt;br /&gt;
equipment. The employees themselves, however, must coordinate their work so&lt;br /&gt;
that they cooperate and don't conflict. As far as the outside world is&lt;br /&gt;
concerned, each employee speaks for the company. Employees can terminate&lt;br /&gt;
and/or more can be hired without affecting how the company is seen from&lt;br /&gt;
outside. The only requirement is that the company have at least one&lt;br /&gt;
employee. When the last employee (thread) dies, the company (process) also&lt;br /&gt;
dies.&lt;br /&gt;
     Although the process is the unit of resource ownership, each thread&lt;br /&gt;
does &amp;quot;own&amp;quot; a small amount of private information. Specifically, each thread&lt;br /&gt;
has its own copy of the CPU's register contents. This is an obvious&lt;br /&gt;
requirement if each thread is to be able to execute different instruction&lt;br /&gt;
sequences. Furthermore, each thread has its own copy of the floating point&lt;br /&gt;
registers. OS/2 creates the process's first thread when the program begins&lt;br /&gt;
execution. Any additional threads are created by means of the&lt;br /&gt;
DosCreateThread call. Any thread can create another thread. All threads in&lt;br /&gt;
a process are considered siblings; there are no parent-child relationships.&lt;br /&gt;
The initial thread, thread 1, has some special characteristics and is&lt;br /&gt;
discussed below.&lt;br /&gt;
&lt;br /&gt;
====5.1.1  Thread Stacks====&lt;br /&gt;
Each thread has its own stack area, pointed to by that thread's SS and SP&lt;br /&gt;
values. Thread 1 is the process's primal thread. OS/2 allocates it stack&lt;br /&gt;
area in response to specifications in the .EXE file. If additional threads&lt;br /&gt;
are created via the DosCreateThread call, the caller specifies a stack area&lt;br /&gt;
for the new thread. Because the memory in which each thread's stack resides&lt;br /&gt;
is owned by the process, any thread can modify this memory; the programmer&lt;br /&gt;
must make sure that this does not happen. The size of the segment in which&lt;br /&gt;
the stack resides is explicitly specified; the size of a thread's stack is&lt;br /&gt;
not. The programmer can place a thread's stack in its own segment or in a&lt;br /&gt;
segment with other data values, including other thread stacks. In any case,&lt;br /&gt;
the programmer must ensure sufficient room for each thread's needs. Each&lt;br /&gt;
thread's stack must have at least 2 KB free in addition to the thread's&lt;br /&gt;
other needs at all times. This extra space is set aside for the needs of&lt;br /&gt;
dynamic link routines, some of which consume considerable stack space. All&lt;br /&gt;
threads must maintain this stack space reserve even if they are not used to&lt;br /&gt;
call dynamic link routines. Because of a bug in many 80286 processors,&lt;br /&gt;
stack segments must be preallocated to their full size. You cannot overrun&lt;br /&gt;
a stack segment and then assume that OS/2 will grow the segment;&lt;br /&gt;
overrunning a stack segment will cause a stack fault, and the process will&lt;br /&gt;
be terminated.&lt;br /&gt;
&lt;br /&gt;
====5.1.2  Thread Uses====&lt;br /&gt;
Threads have a great number of uses. This section describes four of them.&lt;br /&gt;
These examples are intended to be inspirations to the programmer; there are&lt;br /&gt;
many other uses for threads.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.1  Foreground and Background Work&lt;br /&gt;
Threads provide a form of multitasking within a single program; therefore,&lt;br /&gt;
one of their most obvious uses is to provide simultaneous foreground and&lt;br /&gt;
background&lt;br /&gt;
1. Here I mean foreground and background in the sense of directly&lt;br /&gt;
interacting with the user, not as in foreground and background&lt;br /&gt;
screen groups.&lt;br /&gt;
1 processing for a program. For example, a spreadsheet program&lt;br /&gt;
might use one thread to display menus and to read user input. A second&lt;br /&gt;
thread could execute user commands, update the spreadsheet, and so on.&lt;br /&gt;
     This arrangement generally increases the perceived speed of the&lt;br /&gt;
program by allowing the program to prompt for another command before the&lt;br /&gt;
previous command is complete. For example, if the user changes a cell in a&lt;br /&gt;
spreadsheet and then calls for recalculation, the &amp;quot;execute&amp;quot; thread can&lt;br /&gt;
recalculate while the &amp;quot;command&amp;quot; thread allows the user to move the cursor,&lt;br /&gt;
select new menus, and so forth. The spreadsheet should use RAM semaphores&lt;br /&gt;
to protect its structures so that one thread can't change a structure while&lt;br /&gt;
it is being manipulated by another thread. As far as the user can tell, he&lt;br /&gt;
or she is able to overlap commands without restriction. In actuality, the&lt;br /&gt;
previous command is usually complete before the user can finish entering&lt;br /&gt;
the new command. Occasionally, however, the new command is delayed until&lt;br /&gt;
the first has completed execution. This happens, for example, when the user&lt;br /&gt;
of a spreadsheet deletes a row right after saving the spreadsheet to disk.&lt;br /&gt;
Of course, the performance in this worst case situation is no worse than a&lt;br /&gt;
standard single-thread design is for all cases.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.2  Asynchronous Processing&lt;br /&gt;
Another common use of threads is to provide asynchronous elements in a&lt;br /&gt;
program's design. For example, as a protection against power failure, you&lt;br /&gt;
can design an editor so that it writes its RAM buffer to disk once every&lt;br /&gt;
minute. Threads make it unnecessary to scatter time checks throughout the&lt;br /&gt;
program or to sit in polling loops for input so that a time event isn't&lt;br /&gt;
missed while blocked on a read call. You can create a thread whose sole job&lt;br /&gt;
is periodic backup. The thread can call DosSleep to sleep for 60 seconds,&lt;br /&gt;
write the buffer, and then go back to sleep for another 60 seconds.&lt;br /&gt;
     The asynchronous event doesn't have to be time related. For example,&lt;br /&gt;
in a program that communicates over an asynchronous serial port, you can&lt;br /&gt;
dedicate a thread to wait for the modem carrier to come on or to wait for a&lt;br /&gt;
protocol time out. The main thread can continue to interact with the user.&lt;br /&gt;
     Programs that provide services to other programs via IPC can use&lt;br /&gt;
threads to simultaneously respond to multiple requests. For example, one&lt;br /&gt;
thread can watch the incoming work queue while one or more additional&lt;br /&gt;
threads perform the work.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.3  Speed Execution&lt;br /&gt;
You can use threads to speed the execution of single processes by&lt;br /&gt;
overlapping I/O and computation. A single-threaded process can perform&lt;br /&gt;
computations or call OS/2 for disk reads and writes, but not both at the&lt;br /&gt;
same time. A multithreaded process, on the other hand, can compute one&lt;br /&gt;
batch of data while reading the next batch from a device.&lt;br /&gt;
     Eventually, PCs containing multiple 80386 processors will become&lt;br /&gt;
available. An application that uses multiple threads may execute faster by&lt;br /&gt;
using more than one CPU simultaneously.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.4  Organizing Programs&lt;br /&gt;
Finally, you can use threads to organize and simplify the structure of a&lt;br /&gt;
program. For example, in a program for a turnkey security/alarm system, you&lt;br /&gt;
can assign a separate thread for each activity. One thread can watch the&lt;br /&gt;
status of the intrusion switches; a second can send commands to control the&lt;br /&gt;
lights; a third can run the telephone dialer; and a fourth can interface&lt;br /&gt;
with each control panel.&lt;br /&gt;
     This structure simplifies software design. The programmer needn't&lt;br /&gt;
worry that an intrusion switch is triggering unnoticed while the CPU is&lt;br /&gt;
executing the code that waits on the second key of a two-key command.&lt;br /&gt;
Likewise, the programmer doesn't have to worry about talking to two command&lt;br /&gt;
consoles at the same time; because each has its own thread and local&lt;br /&gt;
(stack) variables, multiple consoles can be used simultaneously without&lt;br /&gt;
conflict.&lt;br /&gt;
     Of course, you can write such a program without multiple threads; a&lt;br /&gt;
rat's nest of event flags and polling loops would do the job. Much better&lt;br /&gt;
would be a family of co-routines. But best, and simplest of all, is a&lt;br /&gt;
multithreaded design.&lt;br /&gt;
&lt;br /&gt;
====5.1.3  Interlocking====&lt;br /&gt;
The good news about threads is that they share a process's data, files, and&lt;br /&gt;
resources. The bad news is that they share a process's data, files, and&lt;br /&gt;
resources--and that sometimes these items must be protected against&lt;br /&gt;
simultaneous update by multiple threads. As we discussed earlier, most OS/2&lt;br /&gt;
machines have a single CPU; the &amp;quot;random&amp;quot; preemption of the scheduler,&lt;br /&gt;
switching the CPU among threads, gives the illusion of the simultaneous&lt;br /&gt;
execution of threads. Because the scheduler is deterministic and priority&lt;br /&gt;
based, scheduling a process's threads is certainly not random; but good&lt;br /&gt;
programming practice requires that it be considered so. Each time a program&lt;br /&gt;
runs, external events will perturb the scheduling of the threads. Perhaps&lt;br /&gt;
some other, higher priority task needs the CPU for a while. Perhaps the&lt;br /&gt;
disk arm is in a different position, and a disk read by one thread takes a&lt;br /&gt;
little longer this time than it did the last. You cannot even assume that&lt;br /&gt;
only the highest priority runnable thread is executing because a multiple-&lt;br /&gt;
CPU system may execute the N highest priority threads.&lt;br /&gt;
     The only safe assumption is that all threads are executing&lt;br /&gt;
simultaneously and that--in the absence of explicit interlocking or&lt;br /&gt;
semaphore calls--each thread is always doing the &amp;quot;worst possible thing&amp;quot; in&lt;br /&gt;
terms of simultaneously updating static data or structures. Writing to&lt;br /&gt;
looser standards and then testing the program &amp;quot;to see if it's OK&amp;quot; is&lt;br /&gt;
unacceptable. The very nature of such race conditions, as they are called,&lt;br /&gt;
makes them extremely difficult to find during testing. Murphy's law says&lt;br /&gt;
that such problems are rare during testing and become a plague only after&lt;br /&gt;
the program is released.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.1  Local Variables&lt;br /&gt;
The best way to avoid a collision of threads over static data is to write&lt;br /&gt;
your program to minimize static data. Because each thread has its own&lt;br /&gt;
stack, each thread has its own stack frame in which to store local&lt;br /&gt;
variables. For example, if one thread opens and reads a file and no other&lt;br /&gt;
thread ever manipulates that file, the memory location where that file's&lt;br /&gt;
handle is stored should be in the thread's stack frame, not in static&lt;br /&gt;
memory. Likewise, buffers and work areas that are private to a thread&lt;br /&gt;
should be on that thread's stack frame. Stack variables that are local to&lt;br /&gt;
the current procedure are easily referenced in high-level languages and in&lt;br /&gt;
assembly language. Data items that are referenced by multiple procedures&lt;br /&gt;
can still be located on the stack. Pascal programs can address such items&lt;br /&gt;
directly via the data scope mechanism. C and assembly language programs&lt;br /&gt;
will need to pass pointers to the items into the procedures that use them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.2  RAM Semaphores&lt;br /&gt;
Although using local variables on stack frames greatly reduces problems&lt;br /&gt;
among threads, there will always be at least a few cases in which more than&lt;br /&gt;
one thread needs to access a static data item or a static resource such as&lt;br /&gt;
a file handle. In this situation, write the code that manipulates the&lt;br /&gt;
static item as a critical section (a body of code that manipulates a data&lt;br /&gt;
resource in a nonreentrant way) and then use RAM semaphores to reserve each&lt;br /&gt;
critical section before it is executed. This procedure guarantees that only&lt;br /&gt;
one thread at a time is in a critical section. See 16.2 Data Integrity for&lt;br /&gt;
a more detailed discussion.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.3  DosSuspendThread&lt;br /&gt;
In some situations it may be possible to enumerate all threads that might&lt;br /&gt;
enter a critical section. In these cases, a process's thread can use&lt;br /&gt;
DosSuspendThread to suspend the execution of the other thread(s) that might&lt;br /&gt;
enter the critical section. The DosSuspendThread call can only be used to&lt;br /&gt;
suspend threads that belong to the process making the call; it cannot be&lt;br /&gt;
used to suspend a thread that belongs to another process. Multiple threads&lt;br /&gt;
can be suspended by making multiple DosSuspendThread calls, one per thread.&lt;br /&gt;
If a just-suspended thread is in the middle of a system call, work on that&lt;br /&gt;
system call may or may not proceed. In either case, there will be no&lt;br /&gt;
further execution of application (ring 3) code by a suspended thread.&lt;br /&gt;
     It is usually better to protect critical sections with a RAM semaphore&lt;br /&gt;
than to use DosSuspendThread. Using a semaphore to protect a critical&lt;br /&gt;
section is analogous to using a traffic light to protect an intersection&lt;br /&gt;
(an automotive &amp;quot;critical section&amp;quot; because conflicting uses must be&lt;br /&gt;
prevented). Using DosSuspendThread, on the other hand, is analogous to your&lt;br /&gt;
stopping the other cars each time you go through an intersection; you're&lt;br /&gt;
interfering with the operation of the other cars just in case they might be&lt;br /&gt;
driving through the same intersection as you, presumably an infrequent&lt;br /&gt;
situation. Furthermore, you need a way to ensure that another vehicle isn't&lt;br /&gt;
already in the middle of the intersection when you stop it. Getting back to&lt;br /&gt;
software, you need to ensure that the thread you're suspending isn't&lt;br /&gt;
already executing the critical section at the time that you suspend it. We&lt;br /&gt;
recommend that you avoid DosSuspendThread when possible because of its&lt;br /&gt;
adverse effects on process performance and because of the difficulty in&lt;br /&gt;
guaranteeing that all the necessary threads have been suspended, especially&lt;br /&gt;
when a program undergoes future maintenance and modification.&lt;br /&gt;
     A DosResumeThread call restores the normal operation of a suspended&lt;br /&gt;
thread.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.4  DosEnterCritSec/DosExitCritSec&lt;br /&gt;
DosSuspendThread suspends the execution of a single thread within a&lt;br /&gt;
process. DosEnterCritSec suspends all threads in a process except the one&lt;br /&gt;
making the DosEnterCritSec call. Except for the scope of their operation,&lt;br /&gt;
DosEnterCritSec and DosExitCritSec are similar to DosSuspendThead and&lt;br /&gt;
DosResumeThread, and the same caveats and observations apply.&lt;br /&gt;
     DosExitCritSec will not undo a DosSuspendThread that was already in&lt;br /&gt;
effect. It releases only those threads that were suspended by&lt;br /&gt;
DosEnterCritSec.&lt;br /&gt;
&lt;br /&gt;
====5.1.4  Thread 1====&lt;br /&gt;
Each thread in a process has an associated thread ID. A thread's ID is a&lt;br /&gt;
magic cookie. Its value has no intrinsic meaning to the application; it&lt;br /&gt;
has meaning only as a name for a thread in an operating system call. The&lt;br /&gt;
one exception to this is the process's first thread, whose thread ID is&lt;br /&gt;
always 1.&lt;br /&gt;
     Thread 1 is special: It is the thread that is interrupted when a&lt;br /&gt;
process receives a signal. See Chapter 12, Signals, for further details.&lt;br /&gt;
&lt;br /&gt;
====5.1.5  Thread Death====&lt;br /&gt;
A thread can die in two ways. First, it can terminate itself with the&lt;br /&gt;
DosExit call. Second, when any thread in a process calls DosExit with the&lt;br /&gt;
&amp;quot;exit entire process&amp;quot; argument, all threads belonging to that process are&lt;br /&gt;
terminated &amp;quot;as soon as possible.&amp;quot; If they were executing application code&lt;br /&gt;
at the time DosExit was called, they terminate immediately. If they were in&lt;br /&gt;
the middle of a system call, they terminate &amp;quot;very quickly.&amp;quot; If the system&lt;br /&gt;
call executes quickly enough, its function may complete (although the CPU&lt;br /&gt;
will not return from the system call itself); but if the system call&lt;br /&gt;
involves delays of more than 1 second, it will terminate without&lt;br /&gt;
completing. Whether a thread's last system call completes is usually moot,&lt;br /&gt;
but in a few cases, such as writes to some types of devices, it may be&lt;br /&gt;
noticed that the last write was only partially completed.&lt;br /&gt;
     When a process wants to terminate, it should use the &amp;quot;terminate entire&lt;br /&gt;
process&amp;quot; form of DosExit rather than the &amp;quot;terminate this thread&amp;quot; form.&lt;br /&gt;
Unbeknownst to the calling process, some dynlink packages, including some&lt;br /&gt;
OS/2 system calls, may create threads. These threads are called captive&lt;br /&gt;
threads because only the original calling thread returns from the dynlink&lt;br /&gt;
call; the created thread remains &amp;quot;captive&amp;quot; inside the dynlink package. If a&lt;br /&gt;
program attempts to terminate by causing all its known threads to use the&lt;br /&gt;
DosExit &amp;quot;terminate this thread&amp;quot; form, the termination may not be successful&lt;br /&gt;
because of such captive threads.&lt;br /&gt;
     Of course, if the last remaining thread of a process calls DosExit&lt;br /&gt;
&amp;quot;terminate this thread,&amp;quot; OS/2 terminates the process.&lt;br /&gt;
&lt;br /&gt;
====5.1.6  Performance Characteristics====&lt;br /&gt;
Threads are intended to be fast and cheap. In OS/2 version 1.0, each&lt;br /&gt;
additional thread that is created consumes about 1200 bytes of memory&lt;br /&gt;
inside the OS/2 kernel for its kernel mode stack. This is in addition to&lt;br /&gt;
the 2048 bytes of user mode stack space that we recommend you provide from&lt;br /&gt;
the process's data area. Terminating a thread does not release the kernel&lt;br /&gt;
stack memory, but subsequently creating another thread reuses this memory.&lt;br /&gt;
In other words, the system memory that a process's threads consume is the&lt;br /&gt;
maximum number of threads simultaneously alive times 1200 bytes. This&lt;br /&gt;
figure is exclusive of each thread's stack, which is provided by the&lt;br /&gt;
process from its own memory.&lt;br /&gt;
     The time needed to create a new thread depends on the process's&lt;br /&gt;
previous thread behavior. Creating a thread that will reuse the internal&lt;br /&gt;
memory area created for a previous thread that has terminated takes&lt;br /&gt;
approximately 3 milliseconds.&lt;br /&gt;
2. All timings in this book refer to a 6 mHz IBM AT with one&lt;br /&gt;
wait-state memory. This represents a worst case performance level.&lt;br /&gt;
2 A request to create a new thread that&lt;br /&gt;
extends the process's &amp;quot;thread count high-water mark&amp;quot; requires an internal&lt;br /&gt;
memory allocation operation. This operation may trigger a memory compaction&lt;br /&gt;
or even a segment swapout, so its time cannot be accurately predicted.&lt;br /&gt;
     It takes about 1 millisecond for the system to begin running an&lt;br /&gt;
unblocked thread. In other words, if a lower-priority thread releases a RAM&lt;br /&gt;
semaphore that is being waited on by a higher-priority thread,&lt;br /&gt;
approximately 1 millisecond passes between the lower-priority thread's call&lt;br /&gt;
to release the semaphore and the return of the higher-priority thread from&lt;br /&gt;
its DosSemRequest call.&lt;br /&gt;
     Threads are a key feature of OS/2; they will receive strong support in&lt;br /&gt;
future versions of OS/2 and will play an increasingly important&lt;br /&gt;
architectural role. You can, therefore, expect thread costs and performance&lt;br /&gt;
to be the same or to improve in future releases.&lt;br /&gt;
&lt;br /&gt;
===5.2  Scheduler/Priorities===&lt;br /&gt;
&lt;br /&gt;
A typical running OS/2 system contains a lot of threads. Frequently,&lt;br /&gt;
several threads are ready to execute at any one time. The OS/2 scheduler&lt;br /&gt;
decides which thread to run next and how long to run it before assigning&lt;br /&gt;
the CPU to another thread. OS/2's scheduler is a priority-based&lt;br /&gt;
scheduler; it assigns each thread a priority and then uses that priority to&lt;br /&gt;
decide which thread to run. The OS/2 scheduler is also a preemptive&lt;br /&gt;
scheduler. If a higher-priority thread is ready to execute, OS/2 does not&lt;br /&gt;
wait for the lower-priority thread to finish with the CPU before&lt;br /&gt;
reassigning the CPU; the lower-priority thread is preempted--the CPU is&lt;br /&gt;
summarily yanked away. Naturally, the state of the preempted thread is&lt;br /&gt;
recorded so that its execution can resume later without ill effect.&lt;br /&gt;
     The scheduler's dispatch algorithm is very straightforward: It&lt;br /&gt;
executes the highest-priority runnable thread for as long as the thread&lt;br /&gt;
wants the CPU. When that thread gives up the CPU--perhaps by waiting for an&lt;br /&gt;
I/O operation--that thread is no longer runnable, and the scheduler&lt;br /&gt;
executes the thread with the highest priority that is runnable. If a&lt;br /&gt;
blocked thread becomes runnable and it has a higher priority than the&lt;br /&gt;
thread currently running, the CPU is immediately preempted and assigned to&lt;br /&gt;
the higher-priority thread. In summary, the CPU is always running the&lt;br /&gt;
highest-priority runnable thread.&lt;br /&gt;
     The scheduler's dispatcher is simplicity itself: It's blindly priority&lt;br /&gt;
based. Although the usual focus for OS/2 activities is the process--a&lt;br /&gt;
process lives, dies, opens files, and so on--the scheduler components of&lt;br /&gt;
OS/2 know little about processes. Because the thread is the dispatchable&lt;br /&gt;
entity, the scheduler is primarily thread oriented. If you're not used to&lt;br /&gt;
thinking in terms of threads, you can mentally substitute the word process&lt;br /&gt;
for the word thread in the following discussion. In practice, all of a&lt;br /&gt;
process's threads typically share the same priority, so it's not too&lt;br /&gt;
inaccurate to view the system as being made up of processes that compete&lt;br /&gt;
for CPU resources.&lt;br /&gt;
     In OS/2 threads are classified and run in three categories: general&lt;br /&gt;
priority, time-critical priority, and low priority. These categories are&lt;br /&gt;
further divided into subcategories. Figure 5-1 shows the relationship of&lt;br /&gt;
the three priority categories and their subcategories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   High&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³        ³            /ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³        ³          /  ³   Foreground   ³&lt;br /&gt;
³ Force  ³        /    ³     screen     ³&lt;br /&gt;
³  run   ³      /      ³     group      ³&lt;br /&gt;
³        ³    /        ³                ³&lt;br /&gt;
³        ³  /          ³  interactive   ³&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄ´/            ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
³        ³             ³   Foreground   ³&lt;br /&gt;
³        ³             ³     screen     ³&lt;br /&gt;
³ Normal ³             ³     group      ³&lt;br /&gt;
³        ³             ³                ³&lt;br /&gt;
³        ³             ³ noninteractive ³&lt;br /&gt;
³        ³             ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄ´\            ³                ³&lt;br /&gt;
³        ³  \          ³   Background   ³&lt;br /&gt;
³        ³    \        ³     screen     ³&lt;br /&gt;
³  Idle  ³      \      ³     group      ³&lt;br /&gt;
³  time  ³        \    ³                ³&lt;br /&gt;
³        ³          \  ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³        ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
   Low&lt;br /&gt;
&lt;br /&gt;
Figure 5-1.  Priority categories.&lt;br /&gt;
&lt;br /&gt;
====5.2.1  General Priority Category====&lt;br /&gt;
The majority of threads in the system run in the general priority category&lt;br /&gt;
and belong to one of three subcategories: background, foreground, or&lt;br /&gt;
interactive. To a limited extent, OS/2 dynamically modifies the priorities&lt;br /&gt;
of threads in the general priority category.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.1  Background Subcategory&lt;br /&gt;
The purpose of the OS/2 priority design is to optimize response rather than&lt;br /&gt;
throughput. In other words, the system is not concerned about ensuring that&lt;br /&gt;
all runnable threads get at least some CPU time, and the system is not&lt;br /&gt;
primarily concerned about trying to keep the disks busy when the highest-&lt;br /&gt;
priority thread is compute bound. Instead, OS/2 is concerned about keeping&lt;br /&gt;
less important work from delaying or slowing more important work. This is&lt;br /&gt;
the reason for the background subcategory. The word background has been&lt;br /&gt;
used in many different ways to describe how tasks are performed in many&lt;br /&gt;
operating systems; we use the word to indicate processes that are&lt;br /&gt;
associated with a screen group not currently being displayed.&lt;br /&gt;
     For example, a user is working with a word-processing program but then&lt;br /&gt;
switches from that program to a spreadsheet program. The word-processing&lt;br /&gt;
program becomes background, and the spreadsheet program is promoted from&lt;br /&gt;
background to foreground. When the user selects different screen groups,&lt;br /&gt;
threads change from foreground to background or background to foreground.&lt;br /&gt;
Background threads have the lowest priority in the general priority&lt;br /&gt;
category. Background applications get the CPU (and, through it, the disks)&lt;br /&gt;
only when all foreground threads are idle. As soon as a foreground thread&lt;br /&gt;
is runnable, the CPU is preempted from the background thread. Background&lt;br /&gt;
threads can use leftover machine time, but they can never compete with&lt;br /&gt;
foreground threads.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.2  Foreground and Interactive Subcategories&lt;br /&gt;
All processes associated with the currently active screen group are made&lt;br /&gt;
members of the foreground subcategory. The process that is currently&lt;br /&gt;
interacting with the keyboard is promoted to the interactive subcategory.&lt;br /&gt;
This ensures that the user will get the fastest possible response to a&lt;br /&gt;
command. When the interactive process's threads release the CPU (via&lt;br /&gt;
blocking on some OS/2 call), the noninteractive foreground threads get the&lt;br /&gt;
next crack at it because those threads are usually doing work on behalf of&lt;br /&gt;
the interactive process or work that is in some way related. If no&lt;br /&gt;
foreground thread needs the CPU, background threads may run.&lt;br /&gt;
     Although the scheduler concerns itself with threads rather than&lt;br /&gt;
processes, it's processes that switch between categories--foreground,&lt;br /&gt;
background, and interactive. When a process changes category--for example,&lt;br /&gt;
when a process shows itself to be in the interactive subcategory by doing&lt;br /&gt;
keyboard I/O--the priorities of all its threads are adjusted&lt;br /&gt;
appropriately.&lt;br /&gt;
     Because background threads are the &amp;quot;low men on the totem pole&amp;quot; that is&lt;br /&gt;
composed of quite a few threads, it may seem that they'll never get to run.&lt;br /&gt;
This isn't the case, though, over a long enough period of time. Yes, a&lt;br /&gt;
background thread can be totally starved for CPU time during a 5-second&lt;br /&gt;
interval, but it would be very rare if it received no service during a 1-&lt;br /&gt;
minute interval. Interactive application commands that take more than a few&lt;br /&gt;
seconds of CPU time are rare. Commands involving disk transfer may take&lt;br /&gt;
longer, but the CPU is available for lower-priority threads while the&lt;br /&gt;
interactive process is waiting for disk operations. Finally, a user rarely&lt;br /&gt;
keeps an interactive application fully busy; the normal &amp;quot;type, look, and&lt;br /&gt;
think&amp;quot; cycle has lots of spare time in it for background threads to&lt;br /&gt;
run.&lt;br /&gt;
     But how does this apply to the presentation manager? The presentation&lt;br /&gt;
manager runs many independent interactive tasks within the same screen&lt;br /&gt;
group, so are they all foreground threads? How does OS/2 know which is the&lt;br /&gt;
interactive process? The answer is that the presentation manager advises&lt;br /&gt;
the scheduler. When the presentation manager screen group is displayed, all&lt;br /&gt;
threads within that screen group are placed in the foreground category.&lt;br /&gt;
When the user selects a particular window to receive keyboard or mouse&lt;br /&gt;
events, the presentation manager tells the scheduler that the process using&lt;br /&gt;
that window is now the interactive process. As a result, the system's&lt;br /&gt;
interactive performance is preserved in the presentation manager's screen&lt;br /&gt;
group.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.3  Throughput Balancing&lt;br /&gt;
We mentioned that some operating systems try to optimize system throughput&lt;br /&gt;
by trying to run CPU-bound and I/O-bound applications at the same time. The&lt;br /&gt;
theory is that the I/O-bound application ties up the disk but needs little&lt;br /&gt;
CPU time, so the disk work can be gotten out of the way while the CPU is&lt;br /&gt;
running the CPU-bound task. If the disk thread has the higher priority, the&lt;br /&gt;
tasks run in tandem. Each time the disk operation is completed, the I/O-&lt;br /&gt;
bound thread regains the CPU and issues another disk operation. Leftover&lt;br /&gt;
CPU time goes to the CPU-bound task that, in this case, has a lower&lt;br /&gt;
priority.&lt;br /&gt;
     This won't work, however, if the CPU-bound thread has a higher&lt;br /&gt;
priority than the I/O-bound thread. The CPU-bound thread will tend to hold&lt;br /&gt;
the CPU, and the I/O-bound thread won't get even the small amount of CPU&lt;br /&gt;
time that it needs to issue another I/O request. Traditionally, schedulers&lt;br /&gt;
have been designed to deal with this problem by boosting the priority of&lt;br /&gt;
I/O-bound tasks and lowering the priority of CPU-bound tasks so that,&lt;br /&gt;
eventually, the I/O-bound thread gets enough service to make its I/O&lt;br /&gt;
requests.&lt;br /&gt;
     The OS/2 scheduler incorporates this design to a limited extent. Each&lt;br /&gt;
time a thread issues a system call that blocks, the scheduler looks at the&lt;br /&gt;
period between the time the CPU was assigned to the thread and the time the&lt;br /&gt;
thread blocked itself with a system call.&lt;br /&gt;
3. We use &amp;quot;blocking&amp;quot; rather than &amp;quot;requesting an I/O operation&amp;quot; as&lt;br /&gt;
a test of I/O boundedness because nearly all blocking operations&lt;br /&gt;
wait for I/O. If a thread's data were all in the buffer cache,&lt;br /&gt;
the thread could issue many I/O requests and still be compute&lt;br /&gt;
bound. In other words, when we speak of I/O-bound threads, we&lt;br /&gt;
really mean device bound--not I/O request bound.&lt;br /&gt;
3 If that period of time is short,&lt;br /&gt;
the thread is considered I/O bound, and its priority receives a small&lt;br /&gt;
increment. If a thread is truly I/O bound, it soon receives several such&lt;br /&gt;
increments and, thus, a modest priority promotion. On the other hand, if&lt;br /&gt;
the thread held the CPU for a longer period of time, it is considered CPU&lt;br /&gt;
bound, and its priority receives a small decrement.&lt;br /&gt;
     The I/O boundedness priority adjustment is small. No background&lt;br /&gt;
thread, no matter how I/O bound, can have its priority raised to the point&lt;br /&gt;
where it has a higher priority than any foreground thread, no matter how&lt;br /&gt;
CPU bound. This throughput enhancing optimization applies only to &amp;quot;peer&amp;quot;&lt;br /&gt;
threads--threads with similar priorities. For example, the threads of a&lt;br /&gt;
single process generally have the same base priority, so this adjustment&lt;br /&gt;
helps optimize the throughput of that process.&lt;br /&gt;
&lt;br /&gt;
====5.2.2  Time-Critical Priority Category====&lt;br /&gt;
Foreground threads, particularly interactive foreground threads, receive&lt;br /&gt;
CPU service whenever they want it. Noninteractive foreground threads and,&lt;br /&gt;
particularly, background threads may not receive any CPU time for periods&lt;br /&gt;
of arbitrary length. This approach improves system response, but it's not&lt;br /&gt;
always a good thing. For example, you may be running a network or a&lt;br /&gt;
telecommunications application that drops its connection if it can't&lt;br /&gt;
respond to incoming packets in a timely fashion. Also, you may want to make&lt;br /&gt;
an exception to the principle of &amp;quot;response, not throughput&amp;quot; when it comes&lt;br /&gt;
to printers. Most printers are much slower than their users would like, and&lt;br /&gt;
most printer spooler programs require little in the way of CPU time; so the&lt;br /&gt;
OS/2 print spooler (the program that prints queued output on the printer)&lt;br /&gt;
would like to run at a high priority to keep the printer busy.&lt;br /&gt;
     Time-critical applications are so called because the ability to run in&lt;br /&gt;
a timely fashion is critical to their well-being. Time-critical&lt;br /&gt;
applications may or may not be interactive, and they may be in the&lt;br /&gt;
foreground or in a background screen group, but this should not affect&lt;br /&gt;
their high priority. The OS/2 scheduler contains a time-critical priority&lt;br /&gt;
category to deal with time-critical applications. A thread running in this&lt;br /&gt;
priority category has a higher priority than any non-time-critical thread&lt;br /&gt;
in the system, including interactive threads. Unlike priorities in the&lt;br /&gt;
general category, a time-critical priority is never adjusted; once given a&lt;br /&gt;
time-critical priority, a thread's priority remains fixed until a system&lt;br /&gt;
call changes it.&lt;br /&gt;
     Naturally, time-critical threads should consume only modest amounts of&lt;br /&gt;
CPU time. If an application has a time-critical thread that consumes&lt;br /&gt;
considerable CPU time--say, more than 20 percent--the foreground&lt;br /&gt;
interactive application will be noticeably slowed or even momentarily&lt;br /&gt;
stopped. System usability is severely affected when the interactive&lt;br /&gt;
application can't get service. The screen output stutters and stumbles,&lt;br /&gt;
characters are dropped when commands are typed, and, in general, the&lt;br /&gt;
computer becomes unusable.&lt;br /&gt;
     Not all threads in a process have to be of the same priority. An&lt;br /&gt;
application may need time-critical response for only some of its work; the&lt;br /&gt;
other work can run at a normal priority. For example, in a&lt;br /&gt;
telecommunications program a &amp;quot;receive incoming data&amp;quot; thread might run at a&lt;br /&gt;
time-critical priority but queue messages in memory for processing by a&lt;br /&gt;
normal-priority thread. If the time-critical thread finds that the normal-&lt;br /&gt;
priority thread has fallen behind, it can send a &amp;quot;wait for me&amp;quot; message to&lt;br /&gt;
the sending program.&lt;br /&gt;
     We strongly recommend that processes that use monitors run the monitor&lt;br /&gt;
thread, and only the monitor thread, at a time-critical priority. This&lt;br /&gt;
prevents delayed device response because of delays in processing the&lt;br /&gt;
monitor data stream. See 16.1 Device Monitors for more information.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.3  Low Priority Category&lt;br /&gt;
If you picture the general priority category as a range of priorities, with&lt;br /&gt;
the force run priority category as a higher range, there is a third range,&lt;br /&gt;
called the low priority category, that is lower in priority than the&lt;br /&gt;
general priority category. As a result, threads in this category get CPU&lt;br /&gt;
service only when no other thread in the other categories needs it. This&lt;br /&gt;
category is a mirror image of the time-critical priority category in that&lt;br /&gt;
the system call that sets the thread fixes the priority; OS/2 never changes&lt;br /&gt;
a low priority.&lt;br /&gt;
     I don't expect the low priority category to be particularly popular.&lt;br /&gt;
It's in the system primarily because it falls out for free, as a mirror&lt;br /&gt;
image of the time-critical category. Turnkey systems may want to run some&lt;br /&gt;
housekeeping processes at this priority. Some users enjoy computing PI,&lt;br /&gt;
doing cryptographic analysis, or displaying fractal images; these&lt;br /&gt;
recreations are good candidates for soaking up leftover CPU time. On a more&lt;br /&gt;
practical level, you could run a program that counts seconds of CPU time&lt;br /&gt;
and yields a histogram of CPU utilization during the course of a day.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.4  Setting Process/Thread Priorities&lt;br /&gt;
We've discussed at some length the effect of the various priorities, but we&lt;br /&gt;
haven't discussed how to set these priorities. Because inheritance is an&lt;br /&gt;
important OS/2 concept, how does a parent's priority affect that of the&lt;br /&gt;
child? Finally, although we said that priority is a thread issue rather&lt;br /&gt;
than a process one, we kept bringing up processes anyway. How does all this&lt;br /&gt;
work?&lt;br /&gt;
     Currently, whenever a thread is created, it inherits the priority of&lt;br /&gt;
its creator thread. In the case of DosCreateThread, the thread making the&lt;br /&gt;
call is the creator thread. In the case of thread 1, the thread in the&lt;br /&gt;
parent process that is making the DosExecPgm call is the creator thread.&lt;br /&gt;
When a process makes a DosSetPrty call to change the priority of one of its&lt;br /&gt;
own threads, the new priority always takes effect. When a process uses&lt;br /&gt;
DosSetPrty to change the priority of another process, only the threads in&lt;br /&gt;
that other process which have not had their priorities explicitly set from&lt;br /&gt;
within their own process are changed. This prevents a parent process from&lt;br /&gt;
inadvertently lowering the priority of, say, a time-critical thread by&lt;br /&gt;
changing the base priority of a child process.&lt;br /&gt;
     In a future release, we expect to improve this algorithm so that each&lt;br /&gt;
process has a base priority. A new thread will inherit its creator's base&lt;br /&gt;
priority. A process's thread priorities that are in the general priority&lt;br /&gt;
category will all be relative to the process's base priority so that a&lt;br /&gt;
change in the base priority will raise or lower the priority of all the&lt;br /&gt;
process's general threads while retaining their relative priority&lt;br /&gt;
relationships. Threads in the time-critical and low priority categories&lt;br /&gt;
will continue to be unaffected by their process's base priority.&lt;br /&gt;
&lt;br /&gt;
==6  The User Interface==&lt;br /&gt;
&lt;br /&gt;
OS/2 contains several important subsystems: the file system, the memory&lt;br /&gt;
management subsystem, the multitasking subsystem, and the user interface&lt;br /&gt;
subsystem--the presentation manager. MS-DOS does not define or support a&lt;br /&gt;
user interface subsystem; each application must provide its own. MS-DOS&lt;br /&gt;
utilities use a primitive line-oriented interface, essentially unchanged&lt;br /&gt;
from the interface provided by systems designed to interface with TTYs.&lt;br /&gt;
     OS/2 is intended to be a graphics-oriented operating system, and as&lt;br /&gt;
such it needs to provide a standard graphical user interface (GUI)&lt;br /&gt;
subsystem--for several reasons. First, because such systems are complex to&lt;br /&gt;
create, to expect that each application provide its own is unreasonable.&lt;br /&gt;
Second, a major benefit of a graphical user interface is that applications&lt;br /&gt;
can be intermingled. For example, their output windows can share the&lt;br /&gt;
screen, and the user can transfer data between applications using visual&lt;br /&gt;
metaphors. If each application had its own GUI package, such sharing would&lt;br /&gt;
be impossible. Third, a graphical user interface is supposed to make the&lt;br /&gt;
machine easier to use, but this will be so only if the user can learn one&lt;br /&gt;
interface that will work with all applications.&lt;br /&gt;
&lt;br /&gt;
===6.1  VIO User Interface===&lt;br /&gt;
&lt;br /&gt;
The full graphical user interface subsystem will not ship with OS/2 version&lt;br /&gt;
1.0, so the initial release will contain the character-oriented VIO/KBD/MOU&lt;br /&gt;
subsystem (see Chapter 13, The Presentation Manager and VIO, for more&lt;br /&gt;
details). Although VIO doesn't provide any graphical services, it does&lt;br /&gt;
allow applications to sidestep VIO and construct their own. The VIO&lt;br /&gt;
screen group interface is straightforward. When the machine is booted up,&lt;br /&gt;
the screen displays the screen group list. The user can select an existing&lt;br /&gt;
screen group or create a new one. From within a screen group, the user can&lt;br /&gt;
type a magic key sequence to return the screen to the screen group list.&lt;br /&gt;
Another magic key sequence allows the user to toggle through all existing&lt;br /&gt;
screen groups. One screen group is identified in the screen group list as&lt;br /&gt;
the real mode screen group.&lt;br /&gt;
&lt;br /&gt;
===6.2  The Presentation Manager User Interface===&lt;br /&gt;
&lt;br /&gt;
The OS/2 presentation manager is a powerful and flexible graphical user&lt;br /&gt;
interface. It supports such features as windowing, drop-down and pop-up&lt;br /&gt;
menus, and scroll bars. It works best with a graphical pointing device such&lt;br /&gt;
as a mouse, but it can be controlled exclusively from the keyboard.&lt;br /&gt;
     The presentation manager employs screen windows to allow multiple&lt;br /&gt;
applications to use the screen and keyboard simultaneously. Each&lt;br /&gt;
application uses one or more windows to display its information; the user&lt;br /&gt;
can size and position each window, overlapping some and perhaps shrinking&lt;br /&gt;
others to icons. Mouse and keyboard commands change the input focus between&lt;br /&gt;
windows; this allows the presentation manager to route keystrokes and mouse&lt;br /&gt;
events to the proper application.&lt;br /&gt;
     Because of its windowing capability, the presentation manager doesn't&lt;br /&gt;
need to use the underlying OS/2 screen group mechanism to allow the user to&lt;br /&gt;
switch between running applications. The user starts an application by&lt;br /&gt;
pointing to its name on a menu display; for most applications the&lt;br /&gt;
presentation manager creates a new window and assigns it to the new&lt;br /&gt;
process. Some applications may decline to use the presentation manager's&lt;br /&gt;
graphical user interface and prefer to take direct control of the display.&lt;br /&gt;
When such an application is initiated, the presentation manager creates a&lt;br /&gt;
private screen group for it and switches to that screen group. The user can&lt;br /&gt;
switch away by entering a special key sequence that brings up a menu which&lt;br /&gt;
allows the user to select any running program. If the selected program is&lt;br /&gt;
using the standard presentation manager GUI, the screen is switched to the&lt;br /&gt;
screen group shared by those programs. Otherwise, the screen is switched to&lt;br /&gt;
the private screen group that the specified application is using.&lt;br /&gt;
     To summarize, only the real mode application and applications that&lt;br /&gt;
take direct control of the display hardware need to run in their own screen&lt;br /&gt;
groups. The presentation manager runs all other processes in a single&lt;br /&gt;
screen group and uses its windowing facilities to share the screen among&lt;br /&gt;
them. The user can switch between applications via a special menu; if both&lt;br /&gt;
the previous and the new application are using the standard interface, the&lt;br /&gt;
user can switch the focus directly without going though the menu.&lt;br /&gt;
&lt;br /&gt;
===6.3  Presentation Manager and VIO Compatibility===&lt;br /&gt;
&lt;br /&gt;
In OS/2 version 1.1 and in all subsequent releases, the presentation&lt;br /&gt;
manager will replace and superset the VIO interface. Applications that use&lt;br /&gt;
the character mode VIO interface will continue to work properly as&lt;br /&gt;
windowable presentation manager applications, as will applications that use&lt;br /&gt;
the STDIN and STDOUT file handles for interactive I/O. Applications that&lt;br /&gt;
use the VIO interface to obtain direct access to the graphical display&lt;br /&gt;
hardware will also be supported; as described above, the presentation&lt;br /&gt;
manager will run such applications in their own screen group.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==7  Dynamic Linking==&lt;br /&gt;
&lt;br /&gt;
A central component of OS/2 is dynamic linking. Dynamic links play several&lt;br /&gt;
critical architectural roles. Before we can discuss them at such an&lt;br /&gt;
abstract level, however, we need to understand the nuts and bolts of their&lt;br /&gt;
workings.&lt;br /&gt;
&lt;br /&gt;
===7.1  Static Linking===&lt;br /&gt;
&lt;br /&gt;
A good preliminary to the study of dynamic links (called dynlinks, for&lt;br /&gt;
short) is a review of their relative, static links. Every programmer who&lt;br /&gt;
has gone beyond interpreter-based languages such as BASIC is familiar with&lt;br /&gt;
static links. You code a subroutine or a procedure call to a routine that&lt;br /&gt;
is not present in that compiland (or source file), which we'll call Foo.&lt;br /&gt;
The missing routine is declared external so that the assembler or compiler&lt;br /&gt;
doesn't flag it as an undefined symbol. At linktime, you present the linker&lt;br /&gt;
with the .OBJ file that you created from your compiland, and you also&lt;br /&gt;
provide a .OBJ file&lt;br /&gt;
1. Or a .LIB library file that contains the .OBJ file as a part of it.&lt;br /&gt;
1 that contains the missing routine Foo. The linker&lt;br /&gt;
combines the compilands into a final executable image--the .EXE file--that&lt;br /&gt;
contains the routine Foo as well as the routines that call it. During the&lt;br /&gt;
combination process, the linker adjusts the calls to Foo, which had been&lt;br /&gt;
undefined external references, to point to the place in the .EXE file where&lt;br /&gt;
the linker relocated the Foo routine. This process is diagramed in Figure&lt;br /&gt;
7-1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      .OBJ                            .LIB&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³               ³               ³  Foo  ÄÄÄÄÄÄ  ³&lt;br /&gt;
³   Call Foo    ³               ³       ÄÄÄÄÄÄ  ³&lt;br /&gt;
³               ³               ³       ÄÄÄÄÄÄ  ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ               ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ      .EXE file&lt;br /&gt;
        ³                               ³          ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ÀÄÄÄÄÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÙ          ³               ³&lt;br /&gt;
                  ³     +     ³                    ³   Call �ÄÄÄÄÄÄÅÄ¿&lt;br /&gt;
                ÚÄ�ÄÄÄÄÄÄÄÄÄÄÄ�Ä¿                  ³               ³ ³&lt;br /&gt;
                ³               ³                  ³               ³ ³&lt;br /&gt;
                ³    Linker     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³               ³ ³&lt;br /&gt;
                ³               ³                  ³               ³ ³&lt;br /&gt;
                ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ                  ³               ³ ³&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ �ÄÄÄÄÄÄÅÄÙ&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ        ³&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ        ³&lt;br /&gt;
                                                   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-1.  Static linking.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     In other words, with static linking you can write a program in pieces.&lt;br /&gt;
You can compile one piece at a time by having it refer to the other pieces&lt;br /&gt;
as externals. A program called a linker or a link editor combines these&lt;br /&gt;
pieces into one final .EXE image, fixing up the external references (that&lt;br /&gt;
is, references between one piece and another) that those pieces contain.&lt;br /&gt;
     Writing and compiling your program piecemeal is useful, but the&lt;br /&gt;
primary advantage of static linking is that you can use it to reference a&lt;br /&gt;
standard set of subroutines--a subroutine library--without compiling or&lt;br /&gt;
even possessing the source code for those subroutines. Nearly all high-&lt;br /&gt;
level language packages come with one or more standard runtime libraries&lt;br /&gt;
that contain various useful subroutines that the compiler can call&lt;br /&gt;
implicitly and that the programmer can call explicitly. Source for these&lt;br /&gt;
runtime libraries is rarely provided; the language supplier provides only&lt;br /&gt;
the .OBJ object files, typically in library format.&lt;br /&gt;
     To summarize, in traditional static linking the target code (that is,&lt;br /&gt;
the external subroutine) must be present at linktime and is built into the&lt;br /&gt;
final .EXE module. This makes the .EXE file larger, naturally, but more&lt;br /&gt;
important, the target code can't be changed or upgraded without relinking&lt;br /&gt;
to the main program's .OBJ files. Because the personal computer field is&lt;br /&gt;
built on commercial software whose authors don't release source or .OBJ&lt;br /&gt;
files, this relinking is out of the question for the typical end user.&lt;br /&gt;
Finally, the target code can't be shared among several (different)&lt;br /&gt;
applications that use the same library routines. This is true for two&lt;br /&gt;
reasons. First, the target code was relocated differently by the linker for&lt;br /&gt;
each client; so although the code remains logically the same for each &lt;br /&gt;
application, the address components of the binary instructions are&lt;br /&gt;
different in each .EXE file. Second, the operating system has no way of&lt;br /&gt;
knowing that these applications are using the same library, and it has no&lt;br /&gt;
way of knowing where that library is in each .EXE file. Therefore, it can't&lt;br /&gt;
avoid having duplicate copies of the library in memory.&lt;br /&gt;
&lt;br /&gt;
===7.2  Loadtime Dynamic Linking===&lt;br /&gt;
&lt;br /&gt;
The mechanical process of loadtime dynamic linking is the same as that of&lt;br /&gt;
static linking. The programmer makes an external reference to a subroutine&lt;br /&gt;
and at linktime specifies a library file (or a .OBJ file) that defines the&lt;br /&gt;
reference. The linker produces a .EXE file that OS/2 then loads and&lt;br /&gt;
executes. Behind the scenes, however, things are very much different.&lt;br /&gt;
     Step 1 is the same for both kinds of linking. The external reference&lt;br /&gt;
is compiled or assembled, resulting in a .OBJ file that contains an&lt;br /&gt;
external reference fixup record. The assembler or compiler doesn't know&lt;br /&gt;
about dynamic links; the .OBJ file that an assembler or a compiler produces&lt;br /&gt;
may be used for static links, dynamic links, or, more frequently, a&lt;br /&gt;
combination of both (some externals become dynamic links, others become&lt;br /&gt;
static links).&lt;br /&gt;
     In static linking, the linker finds the actual externally referenced&lt;br /&gt;
subroutine in the library file. In dynamic linking, the linker finds a&lt;br /&gt;
special record that defines a module name string and an entry point name&lt;br /&gt;
string. For example, in our hypothetical routine Foo, the library file&lt;br /&gt;
contains only these two name strings, not the code for Foo itself. (The&lt;br /&gt;
entry point name string doesn't have to be the name by which programs&lt;br /&gt;
called the routine.) The resultant .EXE file doesn't contain the code for&lt;br /&gt;
Foo; it contains a special dynamic link record that specifies these module&lt;br /&gt;
and entry point names for Foo. This is illustrated in Figure 7-2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿     ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     .OBJ      ³ Extern  ³     .LIB      ³     ³     .EXE      ³&lt;br /&gt;
³               ÃÄÄÄÄÄÄÄÄ�³ Foo:          ³     ³               ³&lt;br /&gt;
³   Call Foo    ³         ³ Module dlpack ³     ³      ÚÄÄÄÄ¿   ³&lt;br /&gt;
³               ³         ³ entry Foo     ³     ³ Call ³????ÃÄÄÄÅÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ         ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ     ³      ÀÄÄÄÄÙ   ³  ³&lt;br /&gt;
        ³                         ³         ÚÄÄ�ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³&lt;br /&gt;
        ³                         ³         ³   ³ Reference to  ³�ÄÙ&lt;br /&gt;
        ÀÄÄÄÄÄÄÄÄÄ�  +  �ÄÄÄÄÄÄÄÄÄÙ         ³   ³ dlpack: Foo   ³&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄ¿                 ³   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                ³  Link   ³                 ³&lt;br /&gt;
                ÀÄÄÄÄÂÄÄÄÄÙ                 ³&lt;br /&gt;
                     ³                      ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-2.  Dynamic linking.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When this .EXE file is run, OS/2 loads the code in the .EXE file into&lt;br /&gt;
memory and discovers the dynamic link record(s). For each dynamic link&lt;br /&gt;
module that is named, OS/2 locates the code in the system's dynamic link&lt;br /&gt;
library directory and loads it into memory (unless the module is already in&lt;br /&gt;
use; see below). The system then links the external references in the&lt;br /&gt;
application to the addresses of the called entry points. This process is&lt;br /&gt;
diagramed in Figure 7-3.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿                                    ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     .EXE      ³                                    ³     .DLL      ³&lt;br /&gt;
³               ³                                    ³  dlpack: Foo  ³&lt;br /&gt;
³     Call ÄÄÄÄÄÅÄÄ¿                                 ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³                                 ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³  dlpack: Foo  ³�ÄÙ Disk                            ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ                                    ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ&lt;br /&gt;
        ³                                                    ³&lt;br /&gt;
        ³                                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
        ³                                 ³&lt;br /&gt;
Ä Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä&lt;br /&gt;
        ³                                 ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄ�ÄÄÄÄÄÄÄ¿                 ÚÄÄÄÄÄÄÄ�ÄÄÄÄÄÄÄ¿&lt;br /&gt;
³               ³      RAM        ³               ³&lt;br /&gt;
³               ³      Fixup      ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³               ³     by OS/2     ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³     Call  ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³               ³                 ³               ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-3.  Loadtime dynlink fixups.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To summarize, instead of linking in the target code at linktime, the linker&lt;br /&gt;
places a module name and an entry point name into the .EXE file. When the&lt;br /&gt;
program is loaded (that is, executed), OS/2 locates the target code, loads&lt;br /&gt;
it, and does the necessary linking. Although all we're doing is postponing&lt;br /&gt;
the linkage until loadtime, this technique has several important&lt;br /&gt;
ramifications. First, the target code is not in the .EXE file but in a&lt;br /&gt;
separate dynamic link library (.DLL) file. Thus, the .EXE file is smaller&lt;br /&gt;
because it contains only the name of the target code, not the code itself.&lt;br /&gt;
You can change or upgrade the target code at any time simply by replacing&lt;br /&gt;
this .DLL file. The next time a referencing application is loaded,&lt;br /&gt;
2. With some restrictions. See 7.11.2 Dynlink Life, Death, and Sharing.&lt;br /&gt;
2 it is&lt;br /&gt;
linked to the new version of the target code. Finally, having the target&lt;br /&gt;
code in a .DLL file paves the way for automatic code sharing. OS/2 can&lt;br /&gt;
easily understand that two applications are using the same dynlink code&lt;br /&gt;
because it loaded and linked that code, and it can use this knowledge to&lt;br /&gt;
share the pure segments of that dynlink package rather than loading&lt;br /&gt;
duplicate copies.&lt;br /&gt;
     A final advantage of dynamic linking is that it's totally invisible to&lt;br /&gt;
the user, and it can even be invisible to the programmer. You need to&lt;br /&gt;
understand dynamic linking to create a dynamic link module, but you can use&lt;br /&gt;
one without even knowing that it's not an ordinary static link. The one&lt;br /&gt;
disadvantage of dynamic linking is that programs sometimes take longer to&lt;br /&gt;
load into memory than do those linked with static linking. The good news&lt;br /&gt;
about dynamic linking is that the target code(s) are separate from the main&lt;br /&gt;
.EXE file; this is also the bad news. Because the target code(s) are&lt;br /&gt;
separate from the main .EXE file, a few more disk operations may be&lt;br /&gt;
necessary to load them.&lt;br /&gt;
     The actual performance ramifications depend on the kind of dynlink&lt;br /&gt;
module that is referenced and whether this .EXE file is the first to&lt;br /&gt;
reference the module. This is discussed in more detail in 7.11&lt;br /&gt;
Implementation Details.&lt;br /&gt;
     Although this discussion has concentrated on processes calling dynlink&lt;br /&gt;
routines, dynlink routines can in fact be called by other dynlink routines.&lt;br /&gt;
When OS/2 loads a dynlink routine in response to a process's request, it&lt;br /&gt;
examines that routine to see if it has any dynlink references of its own.&lt;br /&gt;
Any such referenced dynlink routines are also loaded and so on until no&lt;br /&gt;
unsatisfied dynlink references remain.&lt;br /&gt;
&lt;br /&gt;
===7.3  Runtime Dynamic Linking===&lt;br /&gt;
&lt;br /&gt;
The dynamic linking that we have been describing is called load-time&lt;br /&gt;
dynamic linking because it occurs when the .EXE file is loaded. All dynamic&lt;br /&gt;
link names need not appear in the .EXE file at loadtime; a process can link&lt;br /&gt;
itself to a dynlink package at runtime as well. Runtime dynamic linking&lt;br /&gt;
works exactly like loadtime dynamic linking except that the process creates&lt;br /&gt;
the dynlink module and entry point names at runtime and then passes them to&lt;br /&gt;
OS/2 so that OS/2 can locate and load the specified dynlink code.&lt;br /&gt;
     Runtime linking takes place in four steps.&lt;br /&gt;
&lt;br /&gt;
     1.  The process issues a DosLoadModule call to tell OS/2 to locate and&lt;br /&gt;
         load the dynlink code into memory.&lt;br /&gt;
&lt;br /&gt;
     2.  The DosGetProcAddr call is used to obtain the addresses of the&lt;br /&gt;
         routines that the process wants to call.&lt;br /&gt;
&lt;br /&gt;
     3.  The process calls the dynlink library entry points by means of an&lt;br /&gt;
         indirect call through the address returned by DosGetProcAddr.&lt;br /&gt;
&lt;br /&gt;
     4.  When the process has no more use for the dynlink code, it can call&lt;br /&gt;
         DosFreeModule to release the dynlink code. After this call, the&lt;br /&gt;
         process will still have the addresses returned by DosGetProcAddr,&lt;br /&gt;
         but they will be illegal addresses; referencing them will cause a&lt;br /&gt;
         GP fault.&lt;br /&gt;
&lt;br /&gt;
     Runtime dynamic links are useful when a program knows that it will&lt;br /&gt;
want to call some dynlink routines but doesn't know which ones. For&lt;br /&gt;
example, a charting program may support four plotters, and it may want to&lt;br /&gt;
use dynlink plotter driver packages. It doesn't make sense for the&lt;br /&gt;
application to contain loadtime dynamic links to all four plotters because&lt;br /&gt;
only one will be used and the others will take up memory and swap space.&lt;br /&gt;
Instead, the charting program can wait until it learns which plotter is&lt;br /&gt;
installed and then use the runtime dynlink facility to load the appropriate&lt;br /&gt;
package. The application need not even call DosLoadModule when it&lt;br /&gt;
initializes; it can wait until the user issues a plot command before it&lt;br /&gt;
calls DosLoadModule, thereby reducing memory demands on the system.&lt;br /&gt;
     The application need not even be able to enumerate all the modules or&lt;br /&gt;
entry points that may be called. The application can learn the names of the&lt;br /&gt;
dynlink modules from another process or by looking in a configuration file.&lt;br /&gt;
This allows the user of our charting program, for example, to install&lt;br /&gt;
additional plotter drivers that didn't even exist at the time that the&lt;br /&gt;
application was written. Of course, in this example the calling sequences&lt;br /&gt;
of the dynlink plotter driver must be standardized, or the programmer must&lt;br /&gt;
devise a way for the application to figure out the proper way to call these&lt;br /&gt;
newly found routines.&lt;br /&gt;
     Naturally, a process is not limited to one runtime dynlink module;&lt;br /&gt;
multiple calls to DosLoadModule can be used to link to several dynlink&lt;br /&gt;
modules simultaneously. Regardless of the number of modules in use,&lt;br /&gt;
DosFreeModule should be used if the dynlink module will no longer be used&lt;br /&gt;
and the process intends to continue executing. Issuing DosFreeModules is&lt;br /&gt;
unnecessary if the process is about to terminate; OS/2 releases all dynlink&lt;br /&gt;
modules at process termination time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.4  Dynlinks, Processes, and Threads&lt;br /&gt;
&lt;br /&gt;
Simply put, OS/2 views dynlinks as a fancy subroutine package. Dynlinks&lt;br /&gt;
aren't processes, and they don't own any resources. A dynlink executes only&lt;br /&gt;
because a thread belonging to a client process called the dynlink code. The&lt;br /&gt;
dynlink code is executing as the client thread and process because, in the&lt;br /&gt;
eyes of the system, the dynlink is merely a subroutine that process has&lt;br /&gt;
called. Before the client process can call a dynlink package, OS/2 ensures&lt;br /&gt;
that the dynlink's segments are in the address space of the client. No ring&lt;br /&gt;
transition or context switching overhead occurs when a client calls a&lt;br /&gt;
dynlink routine; the far call to a dynlink entry point is just that--an&lt;br /&gt;
ordinary far call to a subroutine in the process's address space.&lt;br /&gt;
     One side effect is that dynlink calls are very fast; little CPU time&lt;br /&gt;
is spent getting to the dynlink package. Another side effect is no&lt;br /&gt;
separation between a client's segments and a dynlink package's segments&lt;br /&gt;
3. Subsystem dynlink packages may be sensitive to this. For&lt;br /&gt;
detailed information, see 7.11.1 Dynlink Data Security.&lt;br /&gt;
3&lt;br /&gt;
because segments belong to processes and only one process is running both&lt;br /&gt;
the client and the dynlink code. The same goes for file handles,&lt;br /&gt;
semaphores, and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5  Data&lt;br /&gt;
&lt;br /&gt;
The careful reader will have noticed something missing in this discussion&lt;br /&gt;
of dynamic linking: We've said nothing about how to handle a dynlink&lt;br /&gt;
routine's data. Subroutines linked with static links have no problem with&lt;br /&gt;
having their own static data; when the linker binds the external code with&lt;br /&gt;
the main code, it sees how much static data the external code needs and&lt;br /&gt;
allocates the necessary space in the proper data segment(s). References&lt;br /&gt;
that the external code makes to its data are then fixed up to point&lt;br /&gt;
to the proper location. Because the linker is combining all the .OBJ&lt;br /&gt;
files into a .EXE file, it can easily divide the static data segment(s)&lt;br /&gt;
among the various compilands.&lt;br /&gt;
     This technique doesn't work for dynamic link routines because their&lt;br /&gt;
code and therefore their data requirements aren't present at linktime. It's&lt;br /&gt;
possible to extend the special dynlink .OBJ file to describe the amount of&lt;br /&gt;
static data that the dynlink package will need, but it won't work.&lt;br /&gt;
4. And even if it did work, it would be a poor design because it&lt;br /&gt;
would restrict our ability to upgrade the dynlink code in the field.&lt;br /&gt;
4&lt;br /&gt;
Because the main code in each application uses different amounts of static&lt;br /&gt;
data, the data area reserved for the dynlink package would end up at a&lt;br /&gt;
different offset in each .EXE file that was built. When these .EXE files&lt;br /&gt;
were executed, the one set of shared dynlink code segments would need to&lt;br /&gt;
reference the data that resides at different addresses for each different&lt;br /&gt;
client. Relocating the static references in all dynlink code modules at&lt;br /&gt;
each occurrence of a context switch is clearly out of the question.&lt;br /&gt;
     An alternative to letting dynamic link routines have their own static&lt;br /&gt;
data is to require that their callers allocate the necessary data areas and&lt;br /&gt;
pass pointers to them upon every call. We easily rejected this scheme: It's&lt;br /&gt;
cumbersome; call statements must be written differently if they're for a&lt;br /&gt;
dynlink routine; and, finally, this hack wouldn't support subsystems, which&lt;br /&gt;
are discussed below.&lt;br /&gt;
     Instead, OS/2 takes advantage of the segmented architecture of the&lt;br /&gt;
80286. Each dynamic link routine can use one or more data segments to hold&lt;br /&gt;
its static data. Each client process has a separate set of these segments.&lt;br /&gt;
Because these segments hold only the dynlink routine's data and none of the&lt;br /&gt;
calling process's data, the offsets of the data items within that segment&lt;br /&gt;
will be the same no matter which client process is calling the dynlink&lt;br /&gt;
code. All we need do to solve our static data addressability problem is&lt;br /&gt;
ensure that the segment selectors of the dynlink routine's static data&lt;br /&gt;
segments are the same for each client process.&lt;br /&gt;
     OS/2 ensures that the dynlink library's segment selectors are the same&lt;br /&gt;
for each client process by means of a technique called the disjoint LDT&lt;br /&gt;
space. I won't attempt a general introduction to the segmented architecture&lt;br /&gt;
of the 80286, but a brief summary is in order. Each process in 80286&lt;br /&gt;
protect mode can have a maximum of 16,383 segments. These segments are&lt;br /&gt;
described in two tables: the LDT (Local Descriptor Table) and the GDT&lt;br /&gt;
(Global Descriptor Table). An application can't read from or write to these&lt;br /&gt;
tables. OS/2 manages them, and the 80286 microprocessor uses their contents&lt;br /&gt;
when a process loads selectors into its segment registers.&lt;br /&gt;
     In practice, the GDT is not used for application segments, which&lt;br /&gt;
leaves the LDT 8192 segments--or, more precisely, 8192 segment selectors,&lt;br /&gt;
which OS/2 can set up to point to memory segments. The 80286 does not&lt;br /&gt;
support efficient position-independent code, so 80286 programs contain&lt;br /&gt;
within them, as part of the instruction stream, the particular segment&lt;br /&gt;
selector needed to access a particular memory location, as well as an&lt;br /&gt;
offset within that segment. This applies to both code and data&lt;br /&gt;
references.&lt;br /&gt;
     When OS/2 loads a program into memory, the .EXE file describes the&lt;br /&gt;
number, type, and size of the program's segments. OS/2 creates these&lt;br /&gt;
segments and allocates a selector for each from the 8192 possible LDT&lt;br /&gt;
selectors. There isn't any conflict with other processes in the system, at&lt;br /&gt;
this point, because each process has its own LDT and its own private set of&lt;br /&gt;
8192 LDT selectors. After OS/2 chooses a selector for each segment, both&lt;br /&gt;
code and data, it uses a table of addresses provided in the .EXE file to&lt;br /&gt;
relocate each segment reference in the program, changing the place holder&lt;br /&gt;
value put there by the linker into the proper segment selector value. OS/2&lt;br /&gt;
never combines or splits segments, so it never has to relocate the offset&lt;br /&gt;
part of addresses, only the segment parts. Address offsets are more common&lt;br /&gt;
than segment references. Because the segment references are relatively few,&lt;br /&gt;
this relocation process is not very time-consuming.&lt;br /&gt;
     If OS/2 discovers that the process that it's loading references a&lt;br /&gt;
dynlink routine--say, our old friend Foo--the situation is more complex.&lt;br /&gt;
For example, suppose that the process isn't the first caller of Foo; Foo is&lt;br /&gt;
already in memory and already relocated to some particular LDT slots in the&lt;br /&gt;
LDT of the earlier client of Foo. OS/2 has to fill in those same slots in&lt;br /&gt;
the new process's LDT with pointers to Foo; it can't assign different LDT&lt;br /&gt;
slots because Foo's code and data have already been relocated to the&lt;br /&gt;
earlier process's slots. If the new process is already using Foo's slot&lt;br /&gt;
numbers for something else, then we are in trouble. This is a problem with&lt;br /&gt;
all of Foo's segments, both data segments and code segments.&lt;br /&gt;
     This is where the disjoint LDT space comes in. OS/2 reserves many of&lt;br /&gt;
each process's LDT slots&lt;br /&gt;
5. In version 1.0, more than half the LDT slots are reserved for&lt;br /&gt;
this disjoint area.&lt;br /&gt;
5 for the disjoint space. The same slot numbers are&lt;br /&gt;
reserved in every process's LDT. When OS/2 allocates an LDT selector for a&lt;br /&gt;
memory segment that may be shared between processes, it allocates an entry&lt;br /&gt;
from the disjoint LDT space. After a selector is allocated, that same slot&lt;br /&gt;
in all other LDTs in the system is reserved. The slot either remains empty&lt;br /&gt;
(that is, invalid) or points to this shared segment; it can have no other&lt;br /&gt;
use. This guarantees that a process that has been running for hours and&lt;br /&gt;
that has created dozens of segments can still call DosLoadModule to get&lt;br /&gt;
access to a dynlink routine; OS/2 will find that the proper slots in this&lt;br /&gt;
process's LDT are ready and waiting. The disjoint LDT space is used for all&lt;br /&gt;
shared memory objects, not just dynlink routines. Shared memory data&lt;br /&gt;
segments are also allocated from the disjoint LDT space. A process's code&lt;br /&gt;
segments are not allocated in the disjoint LDT space, yet they can still be&lt;br /&gt;
shared.&lt;br /&gt;
6. The sharing of pure segments between multiple copies of the&lt;br /&gt;
same program is established when the duplicate copies are loaded.&lt;br /&gt;
OS/2 will use the same selector to do segment mapping as it did&lt;br /&gt;
when it loaded the first copy, so these segments can be shared&lt;br /&gt;
even though their selectors are not in the disjoint space.&lt;br /&gt;
6 Figure 7-4 illustrates the disjoint LDT concept. Bullets in the&lt;br /&gt;
shaded selectors denote reserved but invalid disjoint selectors. These are&lt;br /&gt;
reserved in case that process later requests access to the shared memory&lt;br /&gt;
segments that were assigned those disjoint slots. Only process A is using&lt;br /&gt;
the dynlink package DLX, so its assigned disjoint LDT slots are reserved&lt;br /&gt;
for it in Process B's LDT as well as in the LDT of all other processes in&lt;br /&gt;
the system. Both processes are using the dynlink package DLY.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 Process A                         Process B&lt;br /&gt;
Segment table                     Segment table&lt;br /&gt;
 (LDT) for A                       (LDT) for B&lt;br /&gt;
  ÚÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄ¿ Process    ÚÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³       ÃÄÄÄ´        ³ A's        ³       ³ ÚÄ´        ³ Process&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ segments   ÃÄÄÄÄÄÄÄ´ ³ ³        ³ B's&lt;br /&gt;
  ³°°°°°°°³   ÀÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄ¿ ³°°°°°°°³ ³ ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ÚÄÄÄÄÄÄÄÄÄÄÄÄ´        ³ ÃÄÄÄÄÄÄÄ´ ³            ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³       ³ ³            ³        ³ ³       ÃÄÙ            ³        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³ ÚÄÄÄÄÄÄÄÄ¿ ÀÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄ´    ÚÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ³°°°°°°°³ ³Ú´        ³            ³°°°°°°°³    ³         ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³³³        ³            ÃÄÄÄÄÄÄÄ´    ³&lt;br /&gt;
  ³       ÃÄÙ³ÀÄÄÄÄÄÄÄÄÙ            ³       ÃÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´  ³                      ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°³  ³                      ³°°°°°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´  ³                      ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ÃÄÄÙ                      ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°�°°°³                         ³°°°°°°°ÃÄÄÄ´        ³ Dynlink&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´   ³        ³ DLZ's&lt;br /&gt;
  ³       ³                         ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°�°°°³                         ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´              ³        ³&lt;br /&gt;
  ³       ³                         ³       ³              ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿ Dynlink    ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄ´        ³ DLX's      ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ segments   ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³   ÀÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄ¿ ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´       ÚÄÄÄÄÄÄ´        ³ ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄÄÄÄÄÙ      ³        ³ ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿ ÀÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³ ÚÄ´        ³            ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³ ³        ³            ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÙ ÀÄÄÄÄÄÄÄÄÙ            ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³                         ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°³                         ³°°°°°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³                         ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿            ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄ´        ³ Dynlink    ³°°°°°°°ÃÄÄÄ´        ³ Dynlink&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ DLY's      ÃÄÄÄÄÄÄÄ´   ³        ³ DLY's&lt;br /&gt;
  ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments   ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿ ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³ ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´              ³        ³ ÃÄÄÄÄÄÄÄ´              ³        ³&lt;br /&gt;
  ³       ³              ÀÄÄÄÄÄÄÄÄÙ ³       ³              ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÀÄÄÄÄÄÄÄÙ                         ÀÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-4.  The disjoint LDT space.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5.1  Instance Data&lt;br /&gt;
OS/2 supports two types of data segments for dynlink routines--instance&lt;br /&gt;
and global. Instance data segments hold data specific to each instance of&lt;br /&gt;
the dynlink routine. In other words, a dynlink routine has a separate set&lt;br /&gt;
of instance data segments for each process using it. The dynlink code has&lt;br /&gt;
no difficulty addressing its data; the code can reference the data segment&lt;br /&gt;
selectors as immediate values. The linker and OS/2's loader conspire so&lt;br /&gt;
that the proper selector value is in place when the code executes.&lt;br /&gt;
     The use of instance data segments is nearly invisible both to the&lt;br /&gt;
client process and to the dynlink code. The client process simply calls the&lt;br /&gt;
dynlink routine, totally unaffected by the presence or absence of the&lt;br /&gt;
routine's instance data segment(s). A dynlink routine can even return&lt;br /&gt;
addresses of items in its data segments to the client process. The client&lt;br /&gt;
cannot distinguish between a dynlink routine and a statically linked one.&lt;br /&gt;
Likewise, the code that makes up the dynlink routine doesn't need to do&lt;br /&gt;
anything special to use its instance data segments. The dynlink code was&lt;br /&gt;
assembled or compiled with its static data in one or more segments; the&lt;br /&gt;
code itself references those segments normally. The linker and OS/2 handle&lt;br /&gt;
all details of allocating the disjoint LDT selectors, loading the segments,&lt;br /&gt;
fixing up the references, and so on.&lt;br /&gt;
     A dynlink routine that uses only instance data segments (or no data&lt;br /&gt;
segments at all) can be written as a single client package, as would be a&lt;br /&gt;
statically linked subroutine. Although such a dynlink routine may have&lt;br /&gt;
multiple clients, the presence of multiple clients is invisible to the&lt;br /&gt;
routine itself. Each client has a separate copy of the instance data&lt;br /&gt;
segment(s). When a new client is created, OS/2 loads virgin copies of the&lt;br /&gt;
instance data segments from the .DLL file. The fact that OS/2 is sharing&lt;br /&gt;
the pure code segments of the routine has no effect on the operation of the&lt;br /&gt;
routine itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5.2  Global Data&lt;br /&gt;
The second form of data segment available to a dynlink routine is a global&lt;br /&gt;
data segment. A global data segment, as the name implies, is not duplicated&lt;br /&gt;
for each client process. There is only one copy of each dynlink module's&lt;br /&gt;
global data segment(s); each client process is given shared access to that&lt;br /&gt;
segment. The segment is loaded only once--when the dynlink package is first&lt;br /&gt;
brought into memory to be linked with its first client process. Global data&lt;br /&gt;
segments allow a dynlink routine to be explicitly aware of its multiple&lt;br /&gt;
clients because changes to a global segment made by calls from one client&lt;br /&gt;
process are visible to the dynlink code when called from another client&lt;br /&gt;
process. Global data segments are provided to support subsystems, which are&lt;br /&gt;
discussed later. Figure 7-5 illustrates a dynlink routine with both&lt;br /&gt;
instance and global data segments.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³                                             ³&lt;br /&gt;
³               Code Segment(s)               ³&lt;br /&gt;
³                                             ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³              ³              ÚÁÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³&lt;br /&gt;
³    Global    ³             ÚÁÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³³&lt;br /&gt;
³     data     ³             ³              ³³³&lt;br /&gt;
³  segment(s)  ³             ³   Instance   ³³³&lt;br /&gt;
³              ³             ³     data     ³³³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ             ³  segment(s)  ³ÃÙ&lt;br /&gt;
                             ³              ÃÙ&lt;br /&gt;
                             ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-5.  Dynlink segments.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.6  Dynamic Link Packages As Subroutines&lt;br /&gt;
&lt;br /&gt;
Dynamic link subroutines (or packages) generally fall into two categories--&lt;br /&gt;
subroutines and subsystems. As we discussed earlier, a dynamic link&lt;br /&gt;
subroutine is written and executes in much the same way as a statically&lt;br /&gt;
linked subroutine. The only difference is in the preparation of the dynamic&lt;br /&gt;
link library file, which contains the actual subroutines, and in the&lt;br /&gt;
preparation of the special .OBJ file, to which client programs can link.&lt;br /&gt;
During execution, both the dynlink routines and the client routines can use&lt;br /&gt;
their own static data freely, and they can pass pointers to their data&lt;br /&gt;
areas back and forth to each other. The only difference between static&lt;br /&gt;
linking and dynamic linking, in this model, is that the dynlink routine&lt;br /&gt;
cannot reference any external symbols that the client code defines, nor can&lt;br /&gt;
the client externally reference any dynlink package symbols other than the&lt;br /&gt;
module entry points. Figure 7-6 illustrates a dynamic link routine being&lt;br /&gt;
used as a subroutine. The execution environment is nearly identical to that&lt;br /&gt;
of a traditional statically linked subroutine; the client and the&lt;br /&gt;
subroutine each reference their own static data areas, all of which are&lt;br /&gt;
contained in the process's address space. Note that a dynlink package can&lt;br /&gt;
reference the application's data and the application can reference the&lt;br /&gt;
dynlink package's data, but only if the application or the dynlink package&lt;br /&gt;
passes a pointer to its data to the other.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                         Process address space&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³            far calls                           far calls            ³&lt;br /&gt;
³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ far ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³&lt;br /&gt;
³ ³            ÃÄÄ�³            ³calls³  Dynlink   ÃÄÄ�³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP code  ³   ³  APP code  ÃÄÄÄÄ�³   code     ³   ³   code     ³ ³&lt;br /&gt;
³ ³ segment #1 ³�ÄÄ´ segment #n ³     ³ segment #1 ³�ÄÄ´ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ     ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ ³&lt;br /&gt;
³    ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³           ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³    ³&lt;br /&gt;
³    ³     ³ ³     references³           ³     ³ ³     references³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³           ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³    ³&lt;br /&gt;
³ ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÚÄÄÄ�ÄÄÄÄÄ�ÄÄ¿     ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÚÄÄÄ�ÄÄÄÄÄ�ÄÄ¿ ³&lt;br /&gt;
³ ³            ³   ³            ³     ³  Dynlink   ³   ³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP data  ³   ³  APP data  ³     ³   data     ³   ³   data     ³ ³&lt;br /&gt;
³ ³ segment #1 ³   ³ segment #n ³     ³ segment #1 ³   ³ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-6.  Dynamic link routines as subroutines.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.7  Subsystems&lt;br /&gt;
&lt;br /&gt;
The term dynlink subsystems refers to the design and intended function of a&lt;br /&gt;
particular style of dynlink package and is somewhat artificial. Although&lt;br /&gt;
OS/2 provides special features to help support subsystems, OS/2 does not&lt;br /&gt;
actually classify dynlink modules as subroutines or subsystems; subsystem&lt;br /&gt;
is merely a descriptive term.&lt;br /&gt;
     The term subsystem refers to a dynlink module that provides a set of&lt;br /&gt;
services built around a resource.&lt;br /&gt;
7. In the most general sense of the word. I don't mean a&lt;br /&gt;
&amp;quot;presentation manager resource object.&amp;quot;&lt;br /&gt;
7 For example, OS/2's VIO dynlink entry&lt;br /&gt;
points are considered a dynlink subsystem because they provide a set of&lt;br /&gt;
services to manage the display screen. A subsystem usually has to manage a&lt;br /&gt;
limited resource for an effectively unlimited number of clients; VIO does&lt;br /&gt;
this, managing a single physical display controller and a small number of&lt;br /&gt;
screen groups for an indefinite number of clients.&lt;br /&gt;
     Because subsystems generally manage a limited resource, they have one&lt;br /&gt;
or more global data segments that they use to keep information about the&lt;br /&gt;
state of the resource they're controlling; they also have buffers, flags,&lt;br /&gt;
semaphores, and so on. Per-client work areas are generally kept in instance&lt;br /&gt;
data segments; it's best to reserve the global data segment(s) for global&lt;br /&gt;
information. Figure 7-7 illustrates a dynamic link routine being used as a&lt;br /&gt;
subsystem. A dynlink subsystem differs from a dynlink being used as a&lt;br /&gt;
subroutine only by the addition of a static data segment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                         Process address space&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³            far calls                           far calls            ³&lt;br /&gt;
³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ far ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³&lt;br /&gt;
³ ³            ÃÄÄ�³            ³calls³  Dynlink   ÃÄÄ�³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP code  ³   ³  APP code  ÃÄÄÄÄ�³   code     ³   ³   code     ³ ³&lt;br /&gt;
³ ³ segment #1 ³�ÄÄ´ segment #n ³     ³ segment #1 ³�ÄÄ´ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ     ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ ³&lt;br /&gt;
³    ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³           ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³    ³&lt;br /&gt;
³    ³     ³ ³     references³           ³     ³ ³     references³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³           ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³    ³&lt;br /&gt;
³ ÚÍÍ�ÍÍÍÍÍ�ÍÍÍ»   ÚÍÍÍ�ÍÍÍÍÍ�ÍÍ»     ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÖÄÄÄÁÄÄÄÄÄÁÄÄ¿ ³&lt;br /&gt;
³ ³            º   ³            º     ³  global    ³   º instance   ³ ³&lt;br /&gt;
³ ³  APP data  º   ³  APP data  º     ³   data     ³   º   data     ³ ³&lt;br /&gt;
³ ³ segment #1 º   ³ segment #n º     ³ segment    ³   º segment    ³ ³&lt;br /&gt;
³ ÀÄÄÄÄÄÄÄÄÄÄÄÄ½   ÀÄÄÄÄÄÄÄÄÄÄÄÄ½     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÈÍÍÍÍÍÍÍÍÍÍÍÍÙ ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-7.  Dynamic link routines as subsystems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.7.1  Special Subsystem Support&lt;br /&gt;
Two OS/2 features are particularly valuable to subsystems: global data&lt;br /&gt;
segments (which we've already discussed) and special client initialization&lt;br /&gt;
and termination support. Clearly, if a subsystem is going to manage a&lt;br /&gt;
resource, keeping track of its clients in a global data segment, it needs&lt;br /&gt;
to know when new clients arrive and when old clients terminate. The simple&lt;br /&gt;
dynlink subroutine model doesn't provide this information in a reliable&lt;br /&gt;
fashion. A subsystem undoubtedly has initialize and terminate entry points,&lt;br /&gt;
but client programs may terminate without having called a subsystem's&lt;br /&gt;
terminate entry point. Such a failure may be an error on the part of the&lt;br /&gt;
client, but the system architecture decrees that errors should be&lt;br /&gt;
localized; it's not acceptable for a bug in a client process to be able to&lt;br /&gt;
hang up a subsystem and thus all its clients as well.&lt;br /&gt;
     The two forms of subsystem initialization are global and instance. A&lt;br /&gt;
subsystem can specify either service but not both. If global initialization&lt;br /&gt;
is specified, the initialization entry point is called only once per&lt;br /&gt;
activation of the subsystem. When the subsystem dynlink package is first&lt;br /&gt;
referenced, OS/2 allocates the subsystem's global data segment(s), taking&lt;br /&gt;
their initial values from the .DLL file. OS/2 then calls the subsystem's&lt;br /&gt;
global initialization entry point so that the module can do its one-time&lt;br /&gt;
initialization. The thread that is used to call the initialization entry&lt;br /&gt;
point belongs to that first client process,&lt;br /&gt;
8. The client process doesn't explicitly call a dynlink package's&lt;br /&gt;
initialization entry points. OS/2 uses its godlike powers to&lt;br /&gt;
borrow a thread for the purpose. The mechanism is invisible to the&lt;br /&gt;
client program. It goes without saying, we hope, that it would be&lt;br /&gt;
extremely rude to the client process, not to say damaging, were&lt;br /&gt;
the dynlink package to refuse to return that initialization thread&lt;br /&gt;
or if it were to damage it in some way, such as lowering its&lt;br /&gt;
priority or calling DosExit with it!&lt;br /&gt;
8 so the first client's instance&lt;br /&gt;
data segments are also set up and may be used by the global initialization&lt;br /&gt;
process. This means that although the dynlink subsystem is free to open&lt;br /&gt;
files, read and write their contents, and close them again, it may not open&lt;br /&gt;
a handle to a file, store the handle number in a global data segment, and&lt;br /&gt;
expect to use that handle in the future.&lt;br /&gt;
     Remember, subsystems don't own resources; processes own resources.&lt;br /&gt;
When a dynlink package opens a file, that file is open only for that one&lt;br /&gt;
client process. That handle has meaning only when that particular client is&lt;br /&gt;
calling the subsystem code. If a dynlink package were to store process A's&lt;br /&gt;
handle number in a global data segment and then attempt to do a read from&lt;br /&gt;
that handle when running as process B, at best the read would fail with&lt;br /&gt;
&amp;quot;invalid handle&amp;quot;; at worst some unrelated file of B's would be molested.&lt;br /&gt;
And, of course, when client process A eventually terminates, the handle&lt;br /&gt;
becomes invalid for all clients.&lt;br /&gt;
     The second form of initialization is instance initialization. The&lt;br /&gt;
instance initialization entry point is called in the same way as the global&lt;br /&gt;
initialization entry point except that it is called for every new client&lt;br /&gt;
when that client first attaches to the dynlink package. Any instance data&lt;br /&gt;
segments that exist will already be allocated and will have been given&lt;br /&gt;
their initial values from the .DLL file. The initialization entry point for&lt;br /&gt;
a loadtime dynlink is called before the client's code begins executing. The&lt;br /&gt;
initialization entry point for a runtime dynlink is called when the client&lt;br /&gt;
calls the DosLoadModule function. A dynlink package may not specify both&lt;br /&gt;
global and instance initialization; if it desires both, it should specify&lt;br /&gt;
instance initialization and use a counter in one of its global data&lt;br /&gt;
segments to detect the first instance initialization.&lt;br /&gt;
     Even more important than initialization control is termination&lt;br /&gt;
control. In its global data area, a subsystem may have records, buffers, or&lt;br /&gt;
semaphores on behalf of a client process. It may have queued-up requests&lt;br /&gt;
from that client that it needs to purge when the client terminates. The&lt;br /&gt;
dynlink package need not release instance data segments; because these&lt;br /&gt;
belong to the client process, they are destroyed when the client&lt;br /&gt;
terminates. The global data segments themselves are released if this is the&lt;br /&gt;
dynlink module's last client, so the module may want to take this last&lt;br /&gt;
chance to update a log file, release a system semaphore, and so on.&lt;br /&gt;
     Because a dynlink routine runs as the calling client process, it could&lt;br /&gt;
use DosSetSigHandler to intercept the termination signal. This should never&lt;br /&gt;
be done, however, because the termination signal is not activated for all&lt;br /&gt;
causes of process termination. For example, if the process calls DosExit,&lt;br /&gt;
the termination signal is not sent. Furthermore, there can be only one&lt;br /&gt;
handler per signal type per process. Because client processes don't and&lt;br /&gt;
shouldn't know what goes on inside a dynlink routine, the client process&lt;br /&gt;
and a dynlink routine may conflict in the use of the signal. Such a&lt;br /&gt;
conflict may also occur between two dynlink packages.&lt;br /&gt;
     Using DosExitList service prevents such a collision. DosExitList&lt;br /&gt;
allows a process to specify one or more subroutine addresses that will be&lt;br /&gt;
called when the process terminates. Addresses can be added to and removed&lt;br /&gt;
from the list. DosExitList is ideally suited for termination control. There&lt;br /&gt;
can be many such addresses, and the addresses are called under all&lt;br /&gt;
termination conditions. Both the client process and the subsystem dynlinks&lt;br /&gt;
that it calls can have their own termination routine or routines.&lt;br /&gt;
DosExitList is discussed in more detail in 16.2 Data Integrity.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.8  Dynamic Links As Interfaces to Other Processes&lt;br /&gt;
&lt;br /&gt;
Earlier, I mentioned that dynlink subsystems have difficulty dealing with&lt;br /&gt;
resources--other than global memory--because resource ownership and access&lt;br /&gt;
are on a per-process basis. Life as a dynlink subsystem can be&lt;br /&gt;
schizophrenic. Which files are open, which semaphores are owned and so on&lt;br /&gt;
depends on which client is running your code at the moment. Global memory&lt;br /&gt;
is different; it's the one resource that all clients own jointly. The&lt;br /&gt;
memory remains as long as the client count doesn't go to zero.&lt;br /&gt;
     One way to deal with resource issues is for a dynlink package to act&lt;br /&gt;
as a front end for a server process. During module initialization, the&lt;br /&gt;
dynlink module can check a system semaphore to see whether the server&lt;br /&gt;
process is already running and, if not, start it up. It needs to do this&lt;br /&gt;
with the &amp;quot;detach&amp;quot; form of DosExecPgm so that the server process doesn't&lt;br /&gt;
appear to the system as a child of the subsystem's first client. Such a&lt;br /&gt;
mistake could mean that the client's parent thinks that the command subtree&lt;br /&gt;
it founded by running the client never terminates because the server&lt;br /&gt;
process appears to be part of the command subtree (see Figure 7-8).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
   ³   Grandparent   ³       ³   Grandparent   ³&lt;br /&gt;
   ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
            ³                         ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿      ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³°°°°°°°°°°°³°°°°°°°°°°°³ ³°°°°°°°°°°°³°°°°°°°°°°°³   Ú �³  Daemon  ³&lt;br /&gt;
³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³ ³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³   |  ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³°°³     Process     ³°°³ ³°°³     Process     ³Ä Å Ä Ù   &lt;br /&gt;
³°°ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ°°³ ³°°ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ°°³&lt;br /&gt;
³°°°°°°°°°°°³°°°°°°°°°°°³ ³°°° Command subtree °°°³&lt;br /&gt;
³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°³      Daemon     ³°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°° Command subtree °°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°°°°°°°°°°°°°°°°°°°°°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-8.  Dynlink daemon initiation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When the server process is running, the dynlink subsystem can forward&lt;br /&gt;
some or all requests to it by one of the many IPC facilities. For example,&lt;br /&gt;
a database subsystem might want to use a dedicated server process to hold&lt;br /&gt;
open the database file and do reads and writes to it. It might keep buffers&lt;br /&gt;
and ISAM directories in a shared memory segment to which the dynlink&lt;br /&gt;
subsystem requests access for each of its clients; then requests that can&lt;br /&gt;
be satisfied by data from these buffers won't require the IPC to the server&lt;br /&gt;
process.&lt;br /&gt;
     The only function of some dynlink packages is to act as a procedural&lt;br /&gt;
interface to another process. For example, a spreadsheet program might&lt;br /&gt;
provide an interface through which other applications can retrieve data&lt;br /&gt;
values from a spreadsheet. The best way to do this is for the spreadsheet&lt;br /&gt;
package to contain a dynamic link library that provides clients a&lt;br /&gt;
procedural interface to the spreadsheet process. The library routine itself&lt;br /&gt;
will invoke a noninteractive copy (perhaps a special subset .EXE) of the&lt;br /&gt;
spreadsheet to recover the information, passing it back to the client via&lt;br /&gt;
IPC. Alternatively, the retrieval code that understands the spreadsheet&lt;br /&gt;
data formats could be in the dynlink package itself because that package&lt;br /&gt;
ships with the spreadsheet and will be upgraded when the spreadsheet is. In&lt;br /&gt;
this case, the spreadsheet itself could use the package instead of&lt;br /&gt;
duplicating the functionality in its own .EXE file. In any case, the&lt;br /&gt;
implementation details are hidden from the client process; the client&lt;br /&gt;
process simply makes a procedure call that returns the desired data.&lt;br /&gt;
     Viewed from the highest level, this arrangement is simple: A client&lt;br /&gt;
process uses IPC to get service from a server process via a subroutine&lt;br /&gt;
library. From the programmer's point of view, though, the entire mechanism&lt;br /&gt;
is encapsulated in the dynlink subsystem's interface. A future upgrade to&lt;br /&gt;
the dynlink package may use an improved server process and different forms&lt;br /&gt;
of IPC to talk to it but retain full binary compatibility with the existing&lt;br /&gt;
client base. Figure 7-9 illustrates a dynlink package being used as an&lt;br /&gt;
interface to a daemon process. The figure shows the dynlink package&lt;br /&gt;
interfacing with the daemon process by means of a shared memory segment and&lt;br /&gt;
some other form of IPC, perhaps a named pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                                   client process   |   daemon process&lt;br /&gt;
                                                    |&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿    Far call     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿       |        ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³    APP     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³  Dynlink   ³�ÄÄÄÄÄÄ|ÄÄÄÄÄÄÄÄ´   Daemon   ³&lt;br /&gt;
³    code    ³                 ³    code    ³      IPC       ³    code    ³&lt;br /&gt;
³ segment(s) ³                 ³ segment(s) ÃÄÄÄÄÄÄÄ|ÄÄÄÄÄÄÄ�³ segment(s) ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÂÄÄÄÂÄÄÙ       |        ÀÄÄÂÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
      ³                              ³   ³          |           ³   ³&lt;br /&gt;
      ³                              ³   ÀÄÄÄÄÄÄÄ¿  |   ÚÄÄÄÄÄÄÄÙ   ³&lt;br /&gt;
      ³                              ³           ³  |   ³           ³&lt;br /&gt;
ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿                 ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿ ÚÄÄ�ÄÄ|ÄÄÄ�ÄÄ¿ ÚÄÄÄÄÄÄ�ÄÄÄÄÄ¿&lt;br /&gt;
³    APP     ³                 ³  Dynlink   ³ ³     |      ³ ³   Daemon   ³&lt;br /&gt;
³    data    ³                 ³    data    ³ ³   Shared   ³ ³    data    ³&lt;br /&gt;
³ segment(s) ³                 ³ segment(s) ³ ³   memory   ³ ³ segment(s) ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄ|ÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                                    |&lt;br /&gt;
&lt;br /&gt;
Figure 7-9.  Dynamic link routines as daemon interfaces.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.9  Dynamic Links As Interfaces to the Kernel&lt;br /&gt;
&lt;br /&gt;
We've seen how dynlink libraries can serve as simple subroutine libraries,&lt;br /&gt;
how they can serve as subsystems, and how they can serve as interfaces to&lt;br /&gt;
other processes. OS/2 has one more trick up its sleeve: Dynlink libraries&lt;br /&gt;
can also serve as interfaces to OS/2 itself.&lt;br /&gt;
     Some OS/2 calls are actually implemented as simple library routines.&lt;br /&gt;
For example, DosErrClass is implemented in OS/2 version 1.0 as a simple&lt;br /&gt;
library routine. It takes an error code and locates, in a table, an&lt;br /&gt;
explanatory text string, an error classification, and a recommended action.&lt;br /&gt;
Services such as these were traditionally part of the kernel of operating&lt;br /&gt;
systems, not because they needed to use privileged instructions, but&lt;br /&gt;
because their error tables needed to be changed each time an upgrade to the&lt;br /&gt;
operating system was released. If the service has been provided as a&lt;br /&gt;
statically linked subroutine, older applications running on newer releases&lt;br /&gt;
would receive new error codes that would not be in the library code's&lt;br /&gt;
tables.&lt;br /&gt;
     Although OS/2 implements DosErrClass as a library routine, it's a&lt;br /&gt;
dynlink library routine, and the .DLL file is bundled with the operating&lt;br /&gt;
system itself. Any later release of the system will contain an upgraded&lt;br /&gt;
version of the DosErrClass routine, one that knows about new error codes.&lt;br /&gt;
Consequently, the dynlink facility provides OS/2 with a great deal of&lt;br /&gt;
flexibility in packaging its functionality.&lt;br /&gt;
     Some functions, such as &amp;quot;open file&amp;quot; or &amp;quot;allocate memory,&amp;quot; can't be&lt;br /&gt;
implemented as ordinary subroutines. They need access to key internal data&lt;br /&gt;
structures, and these structures are of course protected so that they can't&lt;br /&gt;
be changed by unprivileged code. To get these services, the processor must&lt;br /&gt;
make a system call, entering the kernel code in a very controlled fashion&lt;br /&gt;
and there running with sufficient privilege to do its work. This privilege&lt;br /&gt;
transition is via a call gate--a feature of the 80286/80386 hardware. A&lt;br /&gt;
program calls a call gate exactly as it performs an ordinary far call;&lt;br /&gt;
special flags in the GDT and LDT tell the processor that this is a call&lt;br /&gt;
gate rather than a regular call.&lt;br /&gt;
     In OS/2, system calls are indistinguishable from ordinary dynlink&lt;br /&gt;
calls. All OS/2 system calls are defined in a dynlink module called&lt;br /&gt;
DosCalls. When OS/2 fixes up dynlink references to this module, it consults&lt;br /&gt;
a special table, built into OS/2, of resident functions. If the function is&lt;br /&gt;
not listed in this table, then an ordinary dynlink is set up. If the&lt;br /&gt;
function is in the table, OS/2 sets up a call gate call in place of the&lt;br /&gt;
ordinary dynlink call. The transparency between library and call gate&lt;br /&gt;
functions explains why passing an invalid address to an OS/2 system call&lt;br /&gt;
causes the calling process to GP fault. Because the OS/2 kernel code&lt;br /&gt;
controls and manages the GP fault mechanism, OS/2 calls that are call gates&lt;br /&gt;
could easily return an error code if an invalid address causes a GP fault.&lt;br /&gt;
If this were done, however, the behavior of OS/2 calls would differ&lt;br /&gt;
depending on their implementation: Dynlink entry points would GP fault for&lt;br /&gt;
invalid addresses;&lt;br /&gt;
9. The LAR and LSL instructions are not sufficient to prevent&lt;br /&gt;
this because another thread in that process may free a segment&lt;br /&gt;
after the LAR but before the reference.&lt;br /&gt;
9 call gate entries would return an error code. OS/2&lt;br /&gt;
prevents this dichotomy and preserves its freedom to, in future releases,&lt;br /&gt;
move function between dynlink and call gate entries by providing a uniform&lt;br /&gt;
reaction to invalid addresses. Because non-call-gate dynlink routines must&lt;br /&gt;
generate GP faults, call gate routines produce them as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.10  The Architectural Role of Dynamic Links&lt;br /&gt;
&lt;br /&gt;
Dynamic links play three major roles in OS/2: They provide the system&lt;br /&gt;
interface; they provide a high-bandwidth device interface; and they support&lt;br /&gt;
open architecture nonkernel service packages.&lt;br /&gt;
     The role of dynamic links as the system interface is clear. They&lt;br /&gt;
provide a uniform, high-efficiency interface to the system kernel as well&lt;br /&gt;
as a variety of nonkernel services. The interface is directly compatible&lt;br /&gt;
with high-level languages, and it takes advantage of special speed-&lt;br /&gt;
enhancing features of the 80286 and 80386 microprocessors.&lt;br /&gt;
10. Specifically, automatic argument passing on calls to the ring&lt;br /&gt;
0 kernel code.&lt;br /&gt;
10 It provides a&lt;br /&gt;
wide and convenient name space, and it allows the distribution of function&lt;br /&gt;
between library code and kernel code. Finally, it provides an essentially&lt;br /&gt;
unlimited expansion capability.&lt;br /&gt;
     But dynamic links do much more than act as system calls. You'll recall&lt;br /&gt;
that in the opening chapters I expressed a need for a device interface that&lt;br /&gt;
was as device independent as device drivers but without their attendant&lt;br /&gt;
overhead. Dynamic links provide this interface because they allow&lt;br /&gt;
applications to make a high-speed call to a subroutine package that can&lt;br /&gt;
directly manipulate the device (see Chapter 18, I/O Privilege Mechanism&lt;br /&gt;
and Debugging/Ptrace). The call itself is fast, and the package can specify&lt;br /&gt;
an arbitrarily wide set of parameters. No privilege or ring transition is&lt;br /&gt;
needed, and the dynlink package can directly access its client's data&lt;br /&gt;
areas. Finally, the dynlink package can use subsystem support features to&lt;br /&gt;
virtualize the device or to referee its use among multiple clients. Device&lt;br /&gt;
independence is provided because a new version of the dynlink interface can&lt;br /&gt;
be installed whenever new hardware is installed. VIO and the presentation&lt;br /&gt;
manager are examples of this kind of dynlink use. Dynlink packages have an&lt;br /&gt;
important drawback when they are being used as device driver replacements:&lt;br /&gt;
They cannot receive hardware interrupts. Some devices, such as video&lt;br /&gt;
displays, do not generate interrupts. Interrupt-driven devices, though,&lt;br /&gt;
require a true device driver. That driver can contain all of the device&lt;br /&gt;
interface function, or the work can be split between a device driver and a&lt;br /&gt;
dynlink package that acts as a front end for that device driver. See&lt;br /&gt;
Chapters 17 and 18 for further discussion of this.&lt;br /&gt;
     Dynlink routines can also act as nonkernel service packages--as an&lt;br /&gt;
open system architecture for software. Most operating systems&lt;br /&gt;
are like the early versions of the Apple Macintosh computer: They are&lt;br /&gt;
closed systems; only their creators can add features to them. Because of&lt;br /&gt;
OS/2's open system architecture, third parties and end users can add system&lt;br /&gt;
services simply by plugging in dynlink modules, just as hardware cards plug&lt;br /&gt;
into an open hardware system. The analogy extends further: Some hardware&lt;br /&gt;
cards become so popular that their interface defines a standard. Examples&lt;br /&gt;
are the Hayes modem and the Hercules Graphics Card. Third-party dynlink&lt;br /&gt;
packages will, over time, establish similar standards. Vendors will offer,&lt;br /&gt;
for example, improved database dynlink routines that are advertised as plug&lt;br /&gt;
compatible with the standard database dynlink interface, but better,&lt;br /&gt;
cheaper, and faster.&lt;br /&gt;
     Dynlinks allow third parties to add interfaces to OS/2; they also&lt;br /&gt;
allow OS/2's developers to add future interfaces. The dynlink interface&lt;br /&gt;
model allows additional functionality to be implemented as subroutines or&lt;br /&gt;
processes or even to be distributed across a network environment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11  Implementation Details&lt;br /&gt;
&lt;br /&gt;
Although dynlink routines often act very much like traditional static&lt;br /&gt;
subroutines, a programmer must be aware of some special considerations&lt;br /&gt;
involved. This section discusses some issues that must be dealt with to&lt;br /&gt;
produce a good dynlink package.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.1  Dynlink Data Security&lt;br /&gt;
We have discussed how a dynlink package runs as a subroutine of the client&lt;br /&gt;
process and that the client process has access to the dynlink package's&lt;br /&gt;
instance and global data segments.&lt;br /&gt;
11. A client process has memory access (addressability) to all of&lt;br /&gt;
the package's global segments but only to those instance data&lt;br /&gt;
segments associated with that process.&lt;br /&gt;
11 This use of the dynlink interface is&lt;br /&gt;
efficient and thus advantageous, but it's also disadvantageous because&lt;br /&gt;
aberrant client processes can damage the dynlink package's global data&lt;br /&gt;
segments.&lt;br /&gt;
     In most circumstances, accidental damage to a dynlink package's data&lt;br /&gt;
segments is rare. Unless the dynlink package returns pointers into its data&lt;br /&gt;
segments to the client process, the client doesn't &amp;quot;know&amp;quot; the dynlink&lt;br /&gt;
package's data segment selectors. The only way such a process could access&lt;br /&gt;
the dynlink's segments would be to accidentally create a random selector&lt;br /&gt;
value that matched one belonging to a dynlink package. Because the&lt;br /&gt;
majority of selector values are illegal, a process would have to be&lt;br /&gt;
very &amp;quot;lucky&amp;quot; to generate a valid dynlink package data selector before it&lt;br /&gt;
generated an unused or code segment selector.&lt;br /&gt;
12.Because if a process generates and writes with a selector that&lt;br /&gt;
is invalid or points to a code segment, the process will be&lt;br /&gt;
terminated immediately with a GP fault.&lt;br /&gt;
12 Naturally, dynlink packages&lt;br /&gt;
shouldn't use global data segments to hold sensitive data because a&lt;br /&gt;
malicious application can figure out the proper selector values.&lt;br /&gt;
     The measures a programmer takes to deal with the security issue depend&lt;br /&gt;
on the nature and sensitivity of the dynlink package. Dynlink packages that&lt;br /&gt;
don't have global data segments are at no risk; an aberrant program can&lt;br /&gt;
damage its instance data segments and thereby fail to run correctly, but&lt;br /&gt;
that's the expected outcome of a program bug. A dynlink package with global&lt;br /&gt;
data segments can minimize the risk by never giving its callers pointers&lt;br /&gt;
into its (the dynlink package's) global data segment. If the amount of&lt;br /&gt;
global data is small and merely detecting damage is sufficient, the global&lt;br /&gt;
data segments could be checksummed.&lt;br /&gt;
     Finally, if accidental damage would be grave, a dynlink package can&lt;br /&gt;
work in conjunction with a special dedicated process, as described above.&lt;br /&gt;
The dedicated process can keep the sensitive data and provide it on a per-&lt;br /&gt;
client basis to the dynlink package in response to an IPC request. Because&lt;br /&gt;
the dedicated process is a separate process, its segments are fully&lt;br /&gt;
protected from the client process as well as from all others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.2  Dynlink Life, Death, and Sharing&lt;br /&gt;
Throughout this discussion, I have referred to sharing pure segments. The&lt;br /&gt;
ability to share pure segments is an optimization that OS/2 makes for all&lt;br /&gt;
memory segments whether they are dynlink segments or an application's .EXE&lt;br /&gt;
file segments. A pure segment is one that is never modified during its&lt;br /&gt;
lifetime. All code segments (except for those created by DosCreateCSAlias)&lt;br /&gt;
are pure; read-only data segments are also pure. When OS/2 notices that&lt;br /&gt;
it's going to load two copies of the same pure segment, it performs a&lt;br /&gt;
behind-the-scenes optimization and gives the second client access to the&lt;br /&gt;
earlier copy of the segment instead of wasting memory with a duplicate&lt;br /&gt;
version.&lt;br /&gt;
     For example, if two copies of a program are run, all code segments are&lt;br /&gt;
pure; at most, only one copy of each code segment will be in memory. OS/2&lt;br /&gt;
flags these segments as &amp;quot;internally shared&amp;quot; and doesn't release them until&lt;br /&gt;
the last user has finished with the segment. This is not the same as&lt;br /&gt;
&amp;quot;shared memory&amp;quot; as it is generally defined in OS/2. Because pure segments&lt;br /&gt;
can only be read, never written, no process can tell that pure segments are&lt;br /&gt;
being shared or be affected by that sharing. Although threads from two or&lt;br /&gt;
more processes may execute the same shared code segment at the same time,&lt;br /&gt;
this is not the same as a multithreaded process. Each copy of a program has&lt;br /&gt;
its own data areas, its own stack, its own file handles, and so on. They&lt;br /&gt;
are totally independent of one another even if OS/2 is quietly sharing&lt;br /&gt;
their pure code segments among them. Unlike multiple threads within a&lt;br /&gt;
single process, threads from different processes cannot affect one another;&lt;br /&gt;
the programmer can safely ignore their possible existence in shared code&lt;br /&gt;
segments.&lt;br /&gt;
     Because the pure segments of a dynlink package are shared, the second&lt;br /&gt;
and subsequent clients of a dynlink package can load much more quickly&lt;br /&gt;
(because these pure segments don't have to be loaded from the .DLL disk&lt;br /&gt;
file). This doesn't mean that OS/2 doesn't have to &amp;quot;hit the disk&amp;quot; at all:&lt;br /&gt;
Many dynlink packages use instance data segments, and OS/2 loads a fresh&lt;br /&gt;
copy of the initial values for these segments from the .DLL file.&lt;br /&gt;
     A dynlink package's second client is its second simultaneous client.&lt;br /&gt;
Under OS/2, only processes have a life of their own. Objects such as&lt;br /&gt;
dynlink packages and shared memory segments exist only as possessions of&lt;br /&gt;
processes. When the last client process of such an object dies or otherwise&lt;br /&gt;
releases the object, OS/2 destroys it and frees up the memory. For example,&lt;br /&gt;
when the first client (since bootup) of a dynlink package references it,&lt;br /&gt;
OS/2 loads the package's code and data segments. Then OS/2 calls the&lt;br /&gt;
package's initialization routine--if the package has one. OS/2 records in&lt;br /&gt;
an internal data structure that this dynlink package has one client. If&lt;br /&gt;
additional clients come along while the first is still using the dynlink&lt;br /&gt;
package, OS/2 increments the package's user count appropriately. Each time&lt;br /&gt;
a client disconnects or dies, the user count is decremented. As long as the&lt;br /&gt;
user count remains nonzero, the package remains in existence, each client&lt;br /&gt;
sharing the original global data segments. When the client count goes to&lt;br /&gt;
zero, OS/2 discards the dynlink package's code and global data segments and&lt;br /&gt;
in effect forgets all about the package. When another client comes along,&lt;br /&gt;
OS/2 reloads the package and reloads its global data segment as if the&lt;br /&gt;
earlier use had never occurred.&lt;br /&gt;
     This mechanism affects a dynlink package only in the management of the&lt;br /&gt;
package's global data segment. The package's code segments are pure, so it&lt;br /&gt;
doesn't matter if they are reloaded from the .DLL file. The instance data&lt;br /&gt;
segments are always reinitialized for each new client, but the data in a&lt;br /&gt;
package's global data segment remains in existence only as long as the&lt;br /&gt;
package has at least one client process. When the last client releases the&lt;br /&gt;
package, the global data segment is discarded. If this is a problem for a&lt;br /&gt;
dynlink package, an associated &amp;quot;dummy&amp;quot; process (which the dynlink package&lt;br /&gt;
could start during its loadtime initialization) can reference the dynlink&lt;br /&gt;
package. As long as this process stays alive, the dynlink package and its&lt;br /&gt;
global data segments stay alive.&lt;br /&gt;
13. If you use this technique, be sure to use the detached form&lt;br /&gt;
of DosExec; see the warning in 7.8 Dynamic Links As&lt;br /&gt;
Interfaces to Other Processes.&lt;br /&gt;
13&lt;br /&gt;
     An alternative is for the dynlink package to keep track of the count&lt;br /&gt;
of its clients and save the contents of its global data segments to a disk&lt;br /&gt;
file when the last client terminates, but this is tricky. Because a process&lt;br /&gt;
may fail to call a dynlink package's &amp;quot;I'm finished&amp;quot; entry point (presumably&lt;br /&gt;
part of the dynlink package's interface) before it terminates, the dynlink&lt;br /&gt;
package must get control to write its segment via DosExitList. If the&lt;br /&gt;
client process is connected to the dynlink package via DosLoadModule (that&lt;br /&gt;
is, via runtime dynamic linking), it cannot disconnect from the package via&lt;br /&gt;
DosFreeModule as long as a DosExitList address points into the dynlink&lt;br /&gt;
package. An attempt to do so returns an error code. Typically, one would&lt;br /&gt;
expect the application to ignore this error code; but because the dynlink&lt;br /&gt;
package is still attached to the client process, it will receive&lt;br /&gt;
DosExitList service when the client eventually terminates. It's important&lt;br /&gt;
that dynlink packages which maintain client state information and therefore&lt;br /&gt;
need DosExitList also offer an &amp;quot;I'm finished&amp;quot; function. When a client calls&lt;br /&gt;
this function, the package should close it out and then remove its&lt;br /&gt;
processing address from DosExitList so that DosFreeModule can take effect&lt;br /&gt;
if the client wishes.&lt;br /&gt;
     Note that OS/2's habit of sharing in-use dynlink libraries has&lt;br /&gt;
implications for the replacement of dynlink packages. Specifically, OS/2&lt;br /&gt;
holds the dynlink .DLL file open for as long as that library has any&lt;br /&gt;
clients. To replace a dynlink library with an upgraded version,&lt;br /&gt;
you must first ensure that all clients of the old package have been&lt;br /&gt;
terminated.&lt;br /&gt;
     While we're on the subject, I'll point out that dynlink segments, like&lt;br /&gt;
.EXE file segments, can be marked (by the linker) as &amp;quot;preload&amp;quot; or &amp;quot;load on&lt;br /&gt;
demand.&amp;quot; When a dynlink module or a .EXE file is loaded, OS/2 immediately&lt;br /&gt;
loads all segments marked &amp;quot;preload&amp;quot; but usually&lt;br /&gt;
14. Segments that are loaded from removable media will be fully&lt;br /&gt;
loaded, regardless of the &amp;quot;load on demand&amp;quot; bit.&lt;br /&gt;
14 does not load any&lt;br /&gt;
segments marked &amp;quot;load on demand.&amp;quot; These segments are loaded only when (and&lt;br /&gt;
if) they are referenced. This mechanism speeds process and library loading&lt;br /&gt;
and reduces swapping by leaving infrequently used segments out of memory&lt;br /&gt;
until they are needed. Once a segment is loaded, its &amp;quot;preload&amp;quot; or &amp;quot;load on&lt;br /&gt;
demand&amp;quot; status has no further bearing; the segment will be swapped or&lt;br /&gt;
discarded without consideration for these bits.&lt;br /&gt;
     Finally, special OS/2 code keeps track of dynamic link &amp;quot;circular&lt;br /&gt;
references.&amp;quot; Because dynlink packages can call other dynlink packages,&lt;br /&gt;
package A can call package B, and package B can call package A. Even if the&lt;br /&gt;
client process C terminates, packages A and B might appear to be in use by&lt;br /&gt;
each other, and they would both stay in memory. OS/2 keeps a graph of&lt;br /&gt;
dynlink clients, both processes and other dynlink packages. When a process&lt;br /&gt;
can no longer reach a dynlink package over this graph--in other words, when&lt;br /&gt;
a package doesn't have a process for a client and when none of its client&lt;br /&gt;
packages have processes for clients and so on--the dynlink package is&lt;br /&gt;
released. Figure 7-10 illustrates a dynamic link circular reference. PA&lt;br /&gt;
and PB are two processes, and LA through LG are dynlink library routines.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿                        ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³°°°°°°°°°°°°³                        ³°°°°°°°°°°°°³&lt;br /&gt;
³°°°°°PA°°°°°³                        ³°°°°°PB°°°°°³&lt;br /&gt;
³°°°°°°°°°°°°³                        ³°°°°°°°°°°°°³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ                        ÀÄÂÄÄÄÄÄÄÄÄÂÄÙ&lt;br /&gt;
      ³                                 ³        ³&lt;br /&gt;
ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿                          ³  ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿&lt;br /&gt;
³     LA     ³�ÄÄÄÄÄÄÄ¿                 ³  ³     LF     ³&lt;br /&gt;
ÀÄÄÂÄÄÄÄÄÄÂÄÄÙ        ³                 ³  ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
   ³   ÚÄÄÁÄÄÄÄÄÄÄÄÄ¿ ³                 ³        ³&lt;br /&gt;
   ³   ³     LB     ÃÄÙ                 ³  ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿&lt;br /&gt;
   ³   ÀÄÂÄÄÄÄÄÄÄÄÄÄÙ                   ³  ³     LG     ³&lt;br /&gt;
   ³     ³  ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ  ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄ�ÄÄÄÄÄ�ÄÄ�¿           ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿          ³&lt;br /&gt;
³     LC     ÃÄÄÄÄÄÄÄÄÄÄ�³     LD     ³�ÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÄ�ÄÄÄÄÄÄÙ           ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
      ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-10.  Dynamic link circular references.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.3  Dynlink Side Effects&lt;br /&gt;
A well-written dynlink library needs to adhere to the OS/2 religious tenet&lt;br /&gt;
of zero side effects. A dynlink library should export to the client process&lt;br /&gt;
only its functional interface and not accidentally export side effects that&lt;br /&gt;
may interfere with the consistent execution of the client.&lt;br /&gt;
     Some possible side effects are obvious: A dynlink routine shouldn't&lt;br /&gt;
close any file handles that it didn't itself open. The same applies to&lt;br /&gt;
other system resources that the client process may be accessing, and it&lt;br /&gt;
applies in the inverse, as well: A dynlink routine that obtains resources&lt;br /&gt;
for itself, in the guise of the client process, should do so in a way that&lt;br /&gt;
doesn't affect the client code. For example, consuming many of the&lt;br /&gt;
available file handles would be a side effect because the client would then&lt;br /&gt;
unexpectedly be short of available file handles. A dynlink package with a&lt;br /&gt;
healthy file handle appetite should be sure to call OS/2 to raise the&lt;br /&gt;
maximum number of file handles so that the client process isn't&lt;br /&gt;
constrained. Finally, the amount of available stack space is a resource&lt;br /&gt;
that a dynlink package must not exhaust. A dynlink routine should try to&lt;br /&gt;
minimize its stack needs, and an upgrade to an existing dynlink package&lt;br /&gt;
must not consume much more stack space than did the earlier version, lest&lt;br /&gt;
the upgrade cause existing clients to fail in the field.&lt;br /&gt;
     Dynlink routines can also cause side effects by issuing some kinds of&lt;br /&gt;
system calls. Because a dynlink routine runs as a subroutine of the client&lt;br /&gt;
process, it must be sure that calls that it makes to OS/2 on behalf of the&lt;br /&gt;
client process don't affect the client application. For example, each&lt;br /&gt;
signal event can have only one handler address; if a dynlink routine&lt;br /&gt;
establishes a signal handler, then that signal handler preempts any handler&lt;br /&gt;
set up by the client application. Likewise, if a dynlink routine changes&lt;br /&gt;
the priority of the thread with which it was called, the dynlink routine&lt;br /&gt;
must be sure to restore that priority before it returns to its caller.&lt;br /&gt;
Several other system functions such as DosError and DosSetVerify also cause&lt;br /&gt;
side effects that can affect the client process.&lt;br /&gt;
     Enumerating all forms of side effects is not possible; it's up to the&lt;br /&gt;
programmer to take the care needed to ensure that a dynlink module is&lt;br /&gt;
properly house-trained. A dynlink module should avoid the side effects&lt;br /&gt;
mentioned as well as similar ones, and, most important, it should behave&lt;br /&gt;
consistently so that if a client application passes its acceptance tests in&lt;br /&gt;
the lab it won't mysteriously fail in the field. This applies doubly to&lt;br /&gt;
upgrades for existing dynlink routines. Upgrades must be written so that if&lt;br /&gt;
a client application works with the earlier release of the dynlink package&lt;br /&gt;
it will work with the new release; obviously the author of the application&lt;br /&gt;
will not have an opportunity to retest existing copies of the application&lt;br /&gt;
against the new release of the dynlink module.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.12  Dynlink Names&lt;br /&gt;
&lt;br /&gt;
Each dynlink entry point has three names associated with it: an external&lt;br /&gt;
name, a module name, and an entry point name. The name the client program&lt;br /&gt;
calls as an external reference is the external name. The programmer works&lt;br /&gt;
with this name, and its syntax and form must be compatible with the&lt;br /&gt;
assembler or compiler being used. The name should be simple and explanatory&lt;br /&gt;
yet unlikely to collide with another external name in the client code or in&lt;br /&gt;
another library. A name such as READ or RESET is a poor choice because of&lt;br /&gt;
the collision possibilities; a name such as XR23P11 is obviously hard to&lt;br /&gt;
work with.&lt;br /&gt;
     The linker replaces the external name with a module name and an entry&lt;br /&gt;
point name, which are embedded in the resultant .EXE file. OS/2 uses the&lt;br /&gt;
module name to locate the dynlink .DLL file; the code for module modname is&lt;br /&gt;
in file MODNAME.DLL. The entry point name specifies the entry point in the&lt;br /&gt;
module; the entry point name need not be the same as the external name. For&lt;br /&gt;
modules with a lot of entry points, the client .EXE file size can be&lt;br /&gt;
minimized and the loading speed maximized by using entry ordinals in place&lt;br /&gt;
of entry point names. See the OS/2 technical reference literature for&lt;br /&gt;
details.&lt;br /&gt;
     Runtime dynamic links are established by using the module name and the&lt;br /&gt;
entry point name; the external name is not used.&lt;br /&gt;
&lt;br /&gt;
==8  File System Name Space==&lt;br /&gt;
&lt;br /&gt;
File system name space is a fancy term for how names of objects are defined&lt;br /&gt;
in OS/2. The words file system are a hint that OS/2 uses one naming scheme&lt;br /&gt;
both for files and for everything else with a name in ASCII format--system&lt;br /&gt;
semaphores, named shared memory, and so forth. First, we'll discuss the&lt;br /&gt;
syntax of names and how to manipulate them; we'll wind up with a discussion&lt;br /&gt;
of how and why we use one naming scheme for all named objects.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.1  Filenames&lt;br /&gt;
&lt;br /&gt;
Before we discuss OS/2 filenames, let's review the format of filenames&lt;br /&gt;
under MS-DOS. In MS-DOS, filenames are required to fit the 8.3 format: a&lt;br /&gt;
name field (which can contain a maximum of 8 characters) and an extension&lt;br /&gt;
field (which can contain a maximum of 3 characters).&lt;br /&gt;
1. As an aside, these sizes date from a tradition established&lt;br /&gt;
many years ago by Digital Equipment Corporation. Digital's very&lt;br /&gt;
early computers used a technique called RAD50 to store 3&lt;br /&gt;
uppercase letters in one 16-bit word, so their file systems&lt;br /&gt;
allowed a 6-character filename and a 3-character extension. CP/M&lt;br /&gt;
later picked up this filename structure. CP/M didn't use RAD50,&lt;br /&gt;
so, in a moment of generosity, it allowed 8-character filenames;&lt;br /&gt;
but the 3-character extension was kept.&lt;br /&gt;
1 The period character&lt;br /&gt;
(.) between the name and the extension is not part of the filename; it's a&lt;br /&gt;
separator character. The filename can consist of uppercase characters only.&lt;br /&gt;
If a user or an application creates a filename that contains lowercase&lt;br /&gt;
characters or a mixture of uppercase and lowercase, MS-DOS converts the&lt;br /&gt;
filename to all uppercase. If an application presents a filename whose name&lt;br /&gt;
or extension field exceeds the allotted length, MS-DOS silently truncates&lt;br /&gt;
the name to the 8.3 format before using it. MS-DOS establishes and enforces&lt;br /&gt;
these rules and maintains the file system structure on the disks. The file&lt;br /&gt;
system that MS-DOS version 3.x supports is called the FAT (File Allocation&lt;br /&gt;
Table) file system. The following are typical MS-DOS and OS/2 filenames:&lt;br /&gt;
&lt;br /&gt;
   \FOOTBALL\SRC\KERNEL\SCHED.ASM&lt;br /&gt;
&lt;br /&gt;
Football is a development project, so this name describes the source for&lt;br /&gt;
the kernel scheduler for the football project.&lt;br /&gt;
&lt;br /&gt;
   \MEMOS\286\MODESWIT.DOC&lt;br /&gt;
&lt;br /&gt;
is a memo discussing 80286 mode switching.&lt;br /&gt;
&lt;br /&gt;
   \\HAGAR\SCRATCH\GORDONL\FOR_MARK&lt;br /&gt;
&lt;br /&gt;
is a file in my scratch directory on the network server HAGAR, placed there&lt;br /&gt;
for use by Mark.&lt;br /&gt;
     The OS/2 architecture views file systems quite differently. As&lt;br /&gt;
microcomputers become more powerful and are used in more and more ways,&lt;br /&gt;
file system characteristics will be needed that might not be met by a&lt;br /&gt;
built-in OS/2 file system. Exotic peripherals, such as WORM&lt;br /&gt;
2. Write Once, Read Many disks. These are generally laser disks of&lt;br /&gt;
very high capacity, but once a track is written, it cannot be erased.&lt;br /&gt;
These disks can appear to be erasable by writing new copies of files&lt;br /&gt;
and directories each time a change is made, abandoning the old ones.&lt;br /&gt;
2 drives,&lt;br /&gt;
definitely require special file systems to meet their special&lt;br /&gt;
characteristics. For this reason, the file system is not built into OS/2&lt;br /&gt;
but is a closely allied component--an installable file system (IFS). An IFS&lt;br /&gt;
is similar to a device driver; it's a body of code that OS/2 loads at boot&lt;br /&gt;
time. The code talks to OS/2 via a standard interface and provides the&lt;br /&gt;
software to manage a file system on a storage device, including the ability&lt;br /&gt;
to create and maintain directories, to allocate disk space, and so&lt;br /&gt;
on.&lt;br /&gt;
     If you are familiar with OS/2 version 1.0, this information may be&lt;br /&gt;
surprising because you have seen no mention of an IFS in the reference&lt;br /&gt;
manuals. That's because the implementation hasn't yet caught up with the&lt;br /&gt;
architecture. We designed OS/2, from the beginning, to support installable&lt;br /&gt;
file systems, one of which would of course be the familiar FAT file system.&lt;br /&gt;
We designed the file system calls, such as DosOpen and DosClose, with this&lt;br /&gt;
in mind. Although scheduling pressures forced us to ship OS/2 version 1.0&lt;br /&gt;
with only the FAT file system--still built in--a future release will&lt;br /&gt;
include the full IFS package. Although at this writing the IFS release of&lt;br /&gt;
OS/2 has not been announced, this information is included here so that you&lt;br /&gt;
can understand the basis for the system name architecture. Also, this&lt;br /&gt;
information will help you write programs that work well under the new&lt;br /&gt;
releases of OS/2 that contain the IFS.&lt;br /&gt;
     Because the IFS will interpret filenames and pathnames and because&lt;br /&gt;
installable file systems can vary considerably, OS/2&lt;br /&gt;
3. Excluding the IFS part.&lt;br /&gt;
3 doesn't contain much&lt;br /&gt;
specific information about the format and meaning of filenames and&lt;br /&gt;
pathnames. In general, the form and meaning of filenames and pathnames are&lt;br /&gt;
private matters between the user and the IFS; both the application and OS/2&lt;br /&gt;
are simply go-betweens. Neither should attempt to parse or understand&lt;br /&gt;
filenames and pathnames. Applications shouldn't parse names because some&lt;br /&gt;
IFSs will support names in formats other than the 8.3 format. Applications&lt;br /&gt;
shouldn't even assume a specific length for a filename or a pathname. All&lt;br /&gt;
OS/2 filename and pathname interfaces, such as DosOpen, DosFindNext, and so&lt;br /&gt;
on, are designed to take name strings of arbitrary length. Applications&lt;br /&gt;
should use name buffers of at least 256 characters to ensure that a long&lt;br /&gt;
name is not truncated.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.2  Network Access&lt;br /&gt;
&lt;br /&gt;
Two hundred and fifty-six characters may seem a bit extreme for the length&lt;br /&gt;
of a filename, and perhaps it is. But OS/2 filenames are often pathnames,&lt;br /&gt;
and pathnames can be quite lengthy. To provide transparent access to files&lt;br /&gt;
on a LAN (local area network), OS/2 makes the network part of the file&lt;br /&gt;
system name space. In other words, a file's pathname can specify a machine&lt;br /&gt;
name as well as a directory path. An application can issue an open to a&lt;br /&gt;
name string such as \WORK\BOOK.DAT or \\VOGON\TEMP\RECALC.ASM. The first&lt;br /&gt;
name specifies the file BOOK.DAT in the directory WORK on the current drive&lt;br /&gt;
of the local machine; the second name specifies the file RECALC.ASM in the&lt;br /&gt;
directory TEMP on the machine VOGON.&lt;br /&gt;
4. Network naming is a bit more complex than this; the name TEMP&lt;br /&gt;
on the machine VOGON actually refers to an offered network&lt;br /&gt;
resource and might appear in any actual disk directory.&lt;br /&gt;
4 Future releases of the Microsoft LAN&lt;br /&gt;
Manager will make further use of the file system name space, so filenames,&lt;br /&gt;
especially program-generated filenames, can easily become very long.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.3  Name Generation and Compatibility&lt;br /&gt;
&lt;br /&gt;
Earlier, I said that applications should pass on filenames entered by the&lt;br /&gt;
user, ignoring their form. This is, of course, a bit unrealistic. Programs&lt;br /&gt;
often need to generate filenames--to hold scratch files, to hold derivative&lt;br /&gt;
filenames (for example, FOO.OBJ derived from FOO.ASM), and so forth. How&lt;br /&gt;
can an application generate or permute such filenames and yet ensure&lt;br /&gt;
compatibility with all installable file systems? The answer is, of course:&lt;br /&gt;
Use the least common denominator approach. In other words, you can safely&lt;br /&gt;
assume that a new IFS must accept the FAT file system's names (the 8.3&lt;br /&gt;
format) because otherwise it would be incompatible with too many programs.&lt;br /&gt;
So if an application sticks to the 8.3 rules when it creates names, it can&lt;br /&gt;
be sure that it is compatible with future file systems. Unlike MS-DOS,&lt;br /&gt;
OS/2&lt;br /&gt;
5. More properly, the FAT installable file system installed in OS/2.&lt;br /&gt;
5 will not truncate name or extension fields that are too long;&lt;br /&gt;
instead, an error will be returned. The case of a filename will continue to&lt;br /&gt;
be insignificant. Some operating systems, such as UNIX, are case sensitive;&lt;br /&gt;
for example, in UNIX the names &amp;quot;foo&amp;quot; and &amp;quot;Foo&amp;quot; refer to different files.&lt;br /&gt;
This works fine for a system used primarily by programmers, who know that a&lt;br /&gt;
lowercase f is ASCII 66 (SUB16) and that an uppercase F is ASCII&lt;br /&gt;
46 (SUB 16). Nonprogrammers, on the other hand, tend to see f and F as the&lt;br /&gt;
same character. Because most OS/2 users are nonprogrammers, OS/2&lt;br /&gt;
installable file systems will continue to be case insensitive.&lt;br /&gt;
     I said that it was safe if program-generated names adhered to the 8.3&lt;br /&gt;
rule. Program-permuted names are likewise safe if they only substitute&lt;br /&gt;
alphanumeric characters for other alphanumeric characters, for example,&lt;br /&gt;
FOO.OBJ for FOO.ASM. Lengthening filenames is also safe (for example,&lt;br /&gt;
changing FOO.C to FOO.OBJ) if your program checks for &amp;quot;invalid name&amp;quot; error&lt;br /&gt;
codes for the new name and has some way to deal with that possibility. In&lt;br /&gt;
any case, write your program so that it isn't confused by enhanced&lt;br /&gt;
pathnames; in the above substitution cases, the algorithm should work from&lt;br /&gt;
the end of the path string and ignore what comes before.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.4  Permissions&lt;br /&gt;
&lt;br /&gt;
Future releases of OS/2 will use the file system name space for more than&lt;br /&gt;
locating a file; it will also contain the permissions for the file. A&lt;br /&gt;
uniform mechanism will associate an access list with every entry in the&lt;br /&gt;
file system name space. This list will prevent unauthorized access--&lt;br /&gt;
accidental or deliberate--to the named file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.5  Other Objects in the File System Name Space&lt;br /&gt;
&lt;br /&gt;
As we've seen, the file system name space is a valuable device in several&lt;br /&gt;
aspects. First, it allows the generation of a variety of names. You can&lt;br /&gt;
group names together (by putting them in the same directory), and you can&lt;br /&gt;
generate entire families of unique names (by creating a new subdirectory).&lt;br /&gt;
Second, the name space can encompass all files and devices on the local&lt;br /&gt;
machine as well as files and devices on remote machines. Finally, file&lt;br /&gt;
system names will eventually support a flexible access and protection&lt;br /&gt;
mechanism.&lt;br /&gt;
     Thus, it comes as no surprise that when the designers of OS/2 needed a&lt;br /&gt;
naming mechanism to deal with nonfile objects, such as shared memory,&lt;br /&gt;
system semaphores, and named pipes, we chose to use the file system name&lt;br /&gt;
space. One small disadvantage to this decision is that a shared memory&lt;br /&gt;
object cannot have a name identical to that of a system semaphore, a named&lt;br /&gt;
pipe, or a disk file. This drawback is trivial, however, compared with the&lt;br /&gt;
benefits of sharing the file system name space. And, of course, you can use&lt;br /&gt;
separate subdirectory names for each type of object, thus preventing name&lt;br /&gt;
collision.&lt;br /&gt;
     Does this mean that system semaphores, shared memory, and pipes have&lt;br /&gt;
actual file system entries on a disk somewhere? Not yet. The FAT file&lt;br /&gt;
system does not support special object names in its directories. Although&lt;br /&gt;
changing it to do so would be easy, the file system would no longer be&lt;br /&gt;
downward compatible with MS-DOS. (MS-DOS 3.x could not read such disks&lt;br /&gt;
written under OS/2.) Because only the FAT file system is available with&lt;br /&gt;
OS/2 version 1.0, that release keeps special RAM-resident pseudo&lt;br /&gt;
directories to hold the special object names. These names must start with&lt;br /&gt;
\SEM\, \SHAREMEM\, \QUEUES\, and \DEV\ to minimize the chance of name&lt;br /&gt;
collision with a real file when they do become special pseudo files in a&lt;br /&gt;
future release of OS/2.&lt;br /&gt;
     Although all file system name space features--networking and (in the&lt;br /&gt;
future) permissions--apply to all file system name space objects from an&lt;br /&gt;
architectural standpoint, not all permutations may be supported.&lt;br /&gt;
Specifically, supporting named shared memory across the network is very&lt;br /&gt;
costly&lt;br /&gt;
6. The entire shared memory segment must be transferred across&lt;br /&gt;
the network each time any byte within it is changed. Some clever&lt;br /&gt;
optimizations can reduce this cost, but none works well enough to&lt;br /&gt;
be feasible.&lt;br /&gt;
6 and won't be implemented.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==9  Memory Management==&lt;br /&gt;
&lt;br /&gt;
A primary function of any multitasking operating system is to allocate&lt;br /&gt;
system resources to each process according to its need. The scheduler&lt;br /&gt;
allocates CPU time among processes (actually, among threads); the memory&lt;br /&gt;
manager allocates both physical memory and virtual memory.&lt;br /&gt;
&lt;br /&gt;
===9.1  Protection Model===&lt;br /&gt;
&lt;br /&gt;
Although MS-DOS provided a simple form of memory management, OS/2 provides&lt;br /&gt;
memory protection. Under MS-DOS 3.x, for example, a program should ask the&lt;br /&gt;
operating system to allocate a memory area before the program uses it.&lt;br /&gt;
Under OS/2, a program must ask the operating system to allocate a memory&lt;br /&gt;
area before the program uses it. As we discussed earlier, the 80286&lt;br /&gt;
microprocessor contains special memory protection hardware. Each memory&lt;br /&gt;
reference that a program makes explicitly or implicitly references a&lt;br /&gt;
segment selector. The segment selector, in turn, references an entry in the&lt;br /&gt;
GDT or the LDT, depending on the form of the selector. Before any program,&lt;br /&gt;
including OS/2 itself, can reference a memory location, that memory&lt;br /&gt;
location must be described in an LDT or a GDT entry, and the selector for&lt;br /&gt;
that entry must be loaded into one of the four segment registers.&lt;br /&gt;
     This hardware design places some restrictions on how programs can use&lt;br /&gt;
addresses.&lt;br /&gt;
&lt;br /&gt;
     þ  A program cannot address memory not set up for it in the LDT or&lt;br /&gt;
        GDT. The only way to address memory is via the LDT and GDT.&lt;br /&gt;
&lt;br /&gt;
     þ  Each segment descriptor in the LDT and GDT contains the physical&lt;br /&gt;
        address and the length of that segment. A program cannot reference&lt;br /&gt;
        an offset into a segment beyond that segment's length.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't put garbage (arbitrary values) into a segment&lt;br /&gt;
        register. Each time a segment register is loaded, the hardware&lt;br /&gt;
        examines the corresponding LDT and GDT to see if the entry is&lt;br /&gt;
        valid. If a program puts an arbitrary value--for example, the lower&lt;br /&gt;
        half of a floating point number--into a segment register, the&lt;br /&gt;
        arbitrary value will probably point to an invalid LDT or GDT entry,&lt;br /&gt;
        causing a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't execute instructions from within a data segment.&lt;br /&gt;
        Attempting to load a data segment selector into the CS register&lt;br /&gt;
        (usually via a far call or a far jump) causes a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't write into a code segment. Attempting to do so&lt;br /&gt;
        causes a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't perform segment arithmetic. Segment arithmetic&lt;br /&gt;
        refers to activities made possible by the addressing mechanism of&lt;br /&gt;
        the 8086 and 8088 microprocessors. Although they are described as&lt;br /&gt;
        having a segment architecture, they are actually linear address&lt;br /&gt;
        space machines that use offset registers--the so-called segment&lt;br /&gt;
        registers. An 8086 can address 1 MB of memory, which requires a 20-&lt;br /&gt;
        bit address. The processor creates this address by multiplying the&lt;br /&gt;
        16-bit segment value by 16 and adding it to the 16-bit offset&lt;br /&gt;
        value. The result is an address between 0 and 1,048,575 (that is, 1&lt;br /&gt;
        MB).&lt;br /&gt;
1. Actually, it's possible to produce addresses beyond 1 MB&lt;br /&gt;
(2^20) by this method if a large enough segment and offset value&lt;br /&gt;
are chosen. The 8086 ignores the carry into the nonexistent 21st&lt;br /&gt;
address bit, effectively wrapping around such large addresses&lt;br /&gt;
into the first 65 KB-16 bytes of physical memory.&lt;br /&gt;
1 The reason these are not true segments is that they don't&lt;br /&gt;
        have any associated length and their names (that is, their&lt;br /&gt;
        selectors) aren't names at all but physical addresses divided by&lt;br /&gt;
        16. These segment values are actually scaled offsets. An address&lt;br /&gt;
        that has a segment value of 100 and an offset value of 100 (shown&lt;br /&gt;
        as 100(SUB10):100(SUB10)), and the address (99(SUB10):116(SUB10))&lt;br /&gt;
        both refer to the same memory location.&lt;br /&gt;
           Many real mode programs take advantage of this situation. Some&lt;br /&gt;
        programs that keep a great many pointers store them as 20-bit&lt;br /&gt;
        values, decomposing those values into the segment:offset form only&lt;br /&gt;
        when they need to de-reference the pointer. To ensure that certain&lt;br /&gt;
        objects have a specific offset value, other programs choose a&lt;br /&gt;
        matching segment value so that the resultant 20-bit address is&lt;br /&gt;
        correct. Neither technique works in OS/2 protect mode. Each segment&lt;br /&gt;
        selector describes its own segment, a segment with a length and an&lt;br /&gt;
        address that are independent of the numeric value of the segment&lt;br /&gt;
        selector. The memory described by segment N has nothing in common&lt;br /&gt;
        with the memory described by segment N+4 or by any other segment&lt;br /&gt;
        unless OS/2 explicitly sets it up that way.&lt;br /&gt;
&lt;br /&gt;
     The segmentation and protection hardware allows OS/2 to impose further&lt;br /&gt;
restrictions on processes.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot edit or examine the contents of the LDT or the&lt;br /&gt;
        GDT. OS/2 simply declines to build an LDT or GDT selector that a&lt;br /&gt;
        process can use to access the contents of those tables. Certain LDT&lt;br /&gt;
        and GDT selectors describe the contents of those tables themselves,&lt;br /&gt;
        but OS/2 sets them up so that they can only be used by ring 0 (that&lt;br /&gt;
        is, privileged) code.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot hook interrupt vectors. MS-DOS version 3.x&lt;br /&gt;
        programs commonly hook interrupt vectors by replacing the address&lt;br /&gt;
        of the interrupt handler with an address from their own code. Thus,&lt;br /&gt;
        these programs can monitor or intercept system calls made via INT&lt;br /&gt;
        21h, BIOS calls also made via interrupts, and hardware interrupts&lt;br /&gt;
        such as the keyboard and the system clock. OS/2 programs cannot do&lt;br /&gt;
        this. OS/2 declines to set up a segment selector that processes can&lt;br /&gt;
        use to address the interrupt vector table.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot call the ROM BIOS code because no selector&lt;br /&gt;
        addresses the ROM BIOS code. Even if such a selector were&lt;br /&gt;
        available, it would be of little use. The ROM BIOS is coded for&lt;br /&gt;
        real mode execution and performs segment arithmetic operations that&lt;br /&gt;
        are no longer legal. If OS/2 provided a ROM BIOS selector, calls to&lt;br /&gt;
        the ROM BIOS would usually generate GP faults.&lt;br /&gt;
&lt;br /&gt;
     þ  Finally, processes cannot run in ring 0, that is, in privileged&lt;br /&gt;
        mode. Both OS/2 and the 80286 hardware are designed to prevent an&lt;br /&gt;
        application program from ever executing in ring 0. Code running in&lt;br /&gt;
        ring 0 can manipulate the LDT and GDT tables as well as other&lt;br /&gt;
        hardware protection features. If OS/2 allowed processes to run in&lt;br /&gt;
        ring 0, the system could never be stable or secure. OS/2 obtains&lt;br /&gt;
        its privileged (literally) state by being the first code loaded at&lt;br /&gt;
        boot time. The boot process takes place in ring 0 and grants ring 0&lt;br /&gt;
        permission to OS/2 by transferring control to OS/2 while remaining&lt;br /&gt;
        in ring 0. OS/2 does not, naturally, extend this favor to the&lt;br /&gt;
        application programs it loads; it ensures that applications can&lt;br /&gt;
        only run in ring 3 user mode.&lt;br /&gt;
2. Applications can also run in ring 2 (see 18.1 I/O Privilege&lt;br /&gt;
Mechanism).&lt;br /&gt;
2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2  Memory Management API&lt;br /&gt;
&lt;br /&gt;
OS/2 provides an extensive memory management API. This book is not a&lt;br /&gt;
reference manual, so I won't cover all the calls. Instead, I'll focus on&lt;br /&gt;
areas that may not be completely self-explanatory.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.1  Shared Memory&lt;br /&gt;
OS/2 supports two kinds of shared memory--named shared memory and giveaway&lt;br /&gt;
shared memory. In both, the memory object shared is a segment. Only an&lt;br /&gt;
entire segment can be shared; sharing part of a segment is not possible.&lt;br /&gt;
Named shared memory is volatile because neither the name of the named&lt;br /&gt;
shared memory nor the memory itself can exist on the FAT file system. When&lt;br /&gt;
the number of processes using a shared memory segment goes to zero, the&lt;br /&gt;
memory is released. Shared memory can't stay around in the absence of&lt;br /&gt;
client processes; it must be reinitialized via DosAllocShrSeg after a&lt;br /&gt;
period of nonuse.&lt;br /&gt;
     Giveaway shared memory allows processes to share access to the same&lt;br /&gt;
segment. Giveaway shared memory segments don't have names because processes&lt;br /&gt;
can't ask to have access to them; a current user of the segment has to give&lt;br /&gt;
access to the segment to a new client process. The term giveaway is a bit&lt;br /&gt;
of a misnomer because the giving process retains access to the memory--the&lt;br /&gt;
access is &amp;quot;given&amp;quot; but not especially &amp;quot;away.&amp;quot; Giveaway shared memory is not&lt;br /&gt;
as convenient as named shared memory. The owner&lt;br /&gt;
3. One of the owners. Anyone with access to a giveaway shared&lt;br /&gt;
segment can give it away itself.&lt;br /&gt;
3 has to know the PID of the&lt;br /&gt;
recipient and  then communicate the recipient's segment selector (returned&lt;br /&gt;
by DosGiveSeg) to that recipient process via some form of IPC.&lt;br /&gt;
     Despite its limitations, giveaway shared memory has important virtues.&lt;br /&gt;
It's a fast and efficient way for one process to transfer data to another;&lt;br /&gt;
and because access is passed &amp;quot;hand to hand,&amp;quot; the wrong process cannot&lt;br /&gt;
accidentally or deliberately gain access to the segment. Most clients of&lt;br /&gt;
giveaway shared memory don't retain access to the segment once they've&lt;br /&gt;
passed it off; they typically call DosFreeSeg on their handle after they've&lt;br /&gt;
called DosGiveSeg. For example, consider the design of a database dynlink&lt;br /&gt;
subsystem that acts as a front end for a database serving process. As part&lt;br /&gt;
of the dynlink initialization process, the package arranged for its client&lt;br /&gt;
process to share a small named shared memory segment with the database&lt;br /&gt;
process. It might be best to use a named pipe or named shared memory--&lt;br /&gt;
created by the database process--to establish initial communication and&lt;br /&gt;
then use this interface only to set up a private piece of giveaway shared&lt;br /&gt;
memory for all further transactions between the client process (via the&lt;br /&gt;
dynlink subsystem) and the database process. Doing it this way, rather than&lt;br /&gt;
having one named shared segment hold service requests from all clients,&lt;br /&gt;
provides greater security. Because each client has its own separate shared&lt;br /&gt;
memory communications area, an amok client can't damage the communications&lt;br /&gt;
of other clients.&lt;br /&gt;
     When a client process asks the database process to read it a record,&lt;br /&gt;
the database process must use a form of IPC to transfer the data to the&lt;br /&gt;
client. Pipes are too slow for the volume of data that our example&lt;br /&gt;
anticipates; shared memory is the best technique. If we were to use named&lt;br /&gt;
shared memory, the database package would have to create a unique shared&lt;br /&gt;
memory name for each record, allocate the memory, and then communicate the&lt;br /&gt;
name to the client (actually, to the dynlink subsystem called by the&lt;br /&gt;
client) so that it can request access. This process has some drawbacks:&lt;br /&gt;
&lt;br /&gt;
     þ  A new unique shared memory name must be created for each request.&lt;br /&gt;
        We could reuse a single shared memory segment, but this would force&lt;br /&gt;
        the client to copy the data out of the segment before it could make&lt;br /&gt;
        another request--too costly a process for an application that must&lt;br /&gt;
        handle a high volume of data.&lt;br /&gt;
&lt;br /&gt;
     þ  Creating named shared memory segments is generally slower than&lt;br /&gt;
        creating giveaway shared memory segments, especially if a large&lt;br /&gt;
        number of named shared memory objects exist, as would be the case&lt;br /&gt;
        in this scenario. The client spends more time when it then requests&lt;br /&gt;
        access to the segment. Creating named shared memory segments is&lt;br /&gt;
        plenty fast enough when it's done once in a while, but in a high-&lt;br /&gt;
        frequency application such as our example, it could become a&lt;br /&gt;
        bottleneck.&lt;br /&gt;
&lt;br /&gt;
     Instead, the database process can create a giveaway shared memory&lt;br /&gt;
segment, load the data into it, and then give it to the client process. The&lt;br /&gt;
database process can easily learn the client's PID; the dynlink interface,&lt;br /&gt;
which runs as the client process, can include it as part of the data&lt;br /&gt;
request. Likewise, the database process can easily return the new client&lt;br /&gt;
selector to the client. This process is fast and efficient and doesn't bog&lt;br /&gt;
down the system by forcing it to deal with a great many name strings.&lt;br /&gt;
     Note that you must specify, at the time of the DosAllocSeg, that the&lt;br /&gt;
segment might be &amp;quot;given away.&amp;quot; Doing so allows OS/2 to allocate the&lt;br /&gt;
selector in the disjoint space, as we discussed earlier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.2  Huge Memory&lt;br /&gt;
The design of the 80286 microprocessor specifies the maximum size of a&lt;br /&gt;
memory segment as 64 KB. For many programs, this number is far too small.&lt;br /&gt;
For example, the internal representation of a large spreadsheet commonly&lt;br /&gt;
takes up 256 KB or more. OS/2 can do nothing to set up a segment that is&lt;br /&gt;
truly larger than 64 KB, but the OS/2 facility called huge segments&lt;br /&gt;
provides a reasonable emulation of segments larger than 64 KB. The trick is&lt;br /&gt;
that a huge segment of, for example, 200 KB is not a single segment but a&lt;br /&gt;
group of four segments, three of which are 64 KB and a fourth of 8 KB. With&lt;br /&gt;
minimal programming burden, OS/2 allows an application to treat the group&lt;br /&gt;
of four segments as a single huge segment.&lt;br /&gt;
     When a process calls DosAllocHuge to allocate a huge segment, OS/2&lt;br /&gt;
allocates several physical segments, the sum of whose size equals the size&lt;br /&gt;
of the virtual huge segment. All component segments are 64 KB, except&lt;br /&gt;
possibly the last one. Unlike an arbitrary collection of segment selectors,&lt;br /&gt;
DosAllocHuge guarantees that the segment selectors it returns are spaced&lt;br /&gt;
uniformly from each other. The selector of the N+1th component segment is&lt;br /&gt;
that of the Nth segment plus i, where i is a power of two. The value of i&lt;br /&gt;
is constant for any given execution of OS/2, but it may vary between&lt;br /&gt;
releases of OS/2 or as a result of internal configuration during bootup. In&lt;br /&gt;
other words, a program must learn the factor i every time it executes; it&lt;br /&gt;
must not hard code the value. There are three ways to learn this value.&lt;br /&gt;
First, a program can call DosGetHugeShift; second, it can read this value&lt;br /&gt;
from the global infoseg; and third, it can reference this value as the&lt;br /&gt;
undefined absolute externals DOSHUGESHIFT (log(SUB2)(i)) or&lt;br /&gt;
DOSHUGEINCR (i). OS/2 will insert the proper value for these&lt;br /&gt;
externals at loadtime. This last method is the most efficient and is&lt;br /&gt;
recommended. Family API programs should call DosGetHugeShift. Figure 9-1&lt;br /&gt;
illustrates the layout of a 200 KB huge memory object. Selectors n + 4i and&lt;br /&gt;
n + 5i are currently invalid but are reserved for future growth of the&lt;br /&gt;
huge object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ÚÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´                              ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³                              ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+0i ³       ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³               ³       ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³      ³               ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³               ³&lt;br /&gt;
n+1i ³       ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄ¿       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³       ³       ³       ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³      ³       ³       ³       ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³       ³       ÀÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+2i ³       ÃÄÄÄÄÄÄÙ       ³               ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
n+3i ³       ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³&lt;br /&gt;
n+4i ³   �   ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³              ³               ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+5i ³   �   ³                              ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´                              ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÀÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 9-1.  Huge memory objects.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Once an application has the first segment selector of the huge segment&lt;br /&gt;
group, called the base segment, and the value log(SUB2)(i), computing the&lt;br /&gt;
address of the Nth byte in the huge segment is easy. Take the high-order&lt;br /&gt;
word of the value of N (that is, N/64 KB), shift it left by log(SUB2)(i)&lt;br /&gt;
(that is, by the DosHugeShift value), and add the base segment selector&lt;br /&gt;
returned by DosAllocHuge. The resultant value is the segment selector for&lt;br /&gt;
the proper component segment; the low-order 16 bits of i are the offset&lt;br /&gt;
into that segment. This computation is reasonably quick to perform since it&lt;br /&gt;
involves only a shift and an addition.&lt;br /&gt;
     Huge segments can be shrunk or grown via DosReallocHuge. If the huge&lt;br /&gt;
segment is to be grown, creating more component physical segments may be&lt;br /&gt;
necessary. Because the address generation rules dictate which selector this&lt;br /&gt;
new segment may have, growing the huge segment may not be possible if that&lt;br /&gt;
selector has already been allocated for another purpose. DosAllocHuge takes&lt;br /&gt;
a maximum growth parameter; it uses this value to reserve sufficient&lt;br /&gt;
selectors to allow the huge segment to grow that big. Applications should&lt;br /&gt;
not provide an unrealistically large number for this argument because doing&lt;br /&gt;
so will waste LDT selectors.&lt;br /&gt;
     The astute reader will notice that the segment arithmetic of the 8086&lt;br /&gt;
environment is not dead; in a sense, it's been resurrected by the huge&lt;br /&gt;
segment mechanism. Applications written for the 8086 frequently use this&lt;br /&gt;
technique to address memory regions greater than 64 KB, using a shift value&lt;br /&gt;
of 12. In other words, if you add 2^12 to an 8086 segment register value,&lt;br /&gt;
the segment register will point to an address 2^12*16, or 64 KB, further in&lt;br /&gt;
physical memory. The offset value between the component segment values was&lt;br /&gt;
always 4096 because of the way the 8086 generated addresses. Although the&lt;br /&gt;
steps involved in computing the segment value are the same in protect mode,&lt;br /&gt;
what's actually happening is considerably different. When you do this&lt;br /&gt;
computation in protect mode, the segment selector value has no inherent&lt;br /&gt;
relationship to the other selectors that make up the huge object. The trick&lt;br /&gt;
only works because OS/2 has arranged for equally spaced-out selectors to&lt;br /&gt;
exist and for each to point to an area of physical memory of the&lt;br /&gt;
appropriate size. Figure 9-2 illustrates the similarities and differences&lt;br /&gt;
between huge model addressing in real and protect modes. The application&lt;br /&gt;
code sequence is identical: A segment selector is computed by adding N*i to&lt;br /&gt;
the base selector. In real mode i is always 4096; in protect mode OS/2&lt;br /&gt;
provides i.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
          REAL MODE        |             PROTECT MODE&lt;br /&gt;
                           |&lt;br /&gt;
           Physical        |&lt;br /&gt;
            Memory         |            Segment&lt;br /&gt;
           ÚÄÄÄÄÄÄ¿        |            Selector         Physical&lt;br /&gt;
           ³      ³        |             Table            Memory&lt;br /&gt;
           ³      ³        |            ÚÄÄÄÄÄÄ¿         ÚÄÄÄÄÄÄ¿&lt;br /&gt;
n+0i ÄÄÄÄÄ�³      ³±       |            ³      ³  ÚÄÄÄÄÄ�³      ³&lt;br /&gt;
           ³      ³±�64 KB | n+0i ÄÄÄÄÄ�³      ÃÄÄÅÄÄÄ¿  ÀÄÄÄÄÄÄÙ&lt;br /&gt;
n+1i ÄÄÄÄÄ�³      ³±       |            ³      ³  ³   ³&lt;br /&gt;
           ³      ³        | n+1i ÄÄÄÄÄ�³      ÃÄÄÅÄ¿ ³  ÚÄÄÄÄÄÄ¿&lt;br /&gt;
n+2i ÄÄÄÄÄ�³      ³        |            ³      ³  ³ ³ ÀÄ�³      ³&lt;br /&gt;
           ³      ³        | n+2i ÄÄÄÄÄ�³      ÃÄÄÙ ³    ÀÄÄÄÄÄÄÙ&lt;br /&gt;
n+3i ÄÄÄÄÄ�³      ³        |            ³      ³    ³    ÚÄÄÄÄÄÄ¿&lt;br /&gt;
           ³      ³        | n+3i ÄÄÄÄÄ�³      ÃÄÄÄÄÅÄÄÄ�³      ³&lt;br /&gt;
           ³      ³        |            ³      ³    ³    ÀÄÄÄÄÄÄÙ&lt;br /&gt;
           ³      ³        |            ³      ³    ³    ÚÄÄÄÄÄÄ¿&lt;br /&gt;
           ³      ³        |            ÀÄÄÄÄÄÄÙ    ÀÄÄÄ�³      ³&lt;br /&gt;
           ³      ³        |        i is set by OS/2     ÀÄÄÄÄÄÄÙ&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ÀÄÄÄÄÄÄÙ        |&lt;br /&gt;
           i = 4096        |&lt;br /&gt;
&lt;br /&gt;
Figure 9-2.  Huge model addressing in real mode and in protect mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Although the similarity between 8086 segment arithmetic and OS/2 huge&lt;br /&gt;
segments is only apparent, it does make it easy to write a program as a&lt;br /&gt;
dual mode application. By using the shift value of 12 in real mode and&lt;br /&gt;
using the OS/2 supplied value in protect mode, the same code functions&lt;br /&gt;
correctly in either mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.3  Executing from Data Segments&lt;br /&gt;
We saw that OS/2 provides the huge segment mechanism to get around the&lt;br /&gt;
segment size restriction imposed by the hardware. OS/2 likewise circumvents&lt;br /&gt;
another hardware restriction--the inability to execute code from data&lt;br /&gt;
segments. Although the demand loading, the discarding, and the swapping of&lt;br /&gt;
code segments make one use of running code from data segments--code&lt;br /&gt;
overlays--obsolete, the capability is still needed. Some high-performance&lt;br /&gt;
programs--the presentation manager, for example--compile &amp;quot;on the fly&amp;quot;&lt;br /&gt;
special code to perform time-critical tasks, such as flipping bits in EGA&lt;br /&gt;
display memory. The optimal sequence may differ depending on several&lt;br /&gt;
factors, so a program may need to compile such code and execute it, gaining&lt;br /&gt;
a significant increase in efficiency over some other approach. OS/2&lt;br /&gt;
supports this need by means of the DosCreateCSAlias call.&lt;br /&gt;
     When DosCreateCSAlias is called with a selector for a data segment, it&lt;br /&gt;
creates a totally different code segment selector (in the eyes of 80286&lt;br /&gt;
hardware) that by some strange coincidence points to exactly the same&lt;br /&gt;
memory locations as does the data segment selector. As a result, code is&lt;br /&gt;
not actually executing from a data segment but from a code segment. Because&lt;br /&gt;
the code segment exactly overlaps that other data segment, the desired&lt;br /&gt;
effect is achieved. The programmer need only be careful to use the data&lt;br /&gt;
selector when writing the segment and to use the code selector when&lt;br /&gt;
executing it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.4  Memory Suballocation&lt;br /&gt;
All memory objects discussed so far have been segments. OS/2 provides a&lt;br /&gt;
facility called memory suballocation that allocates pieces of memory from&lt;br /&gt;
within an application's segment. Pieces of memory can be suballocated from&lt;br /&gt;
within a segment, grown, shrunk, and released. OS/2 uses a classic heap&lt;br /&gt;
algorithm to do this. The DosSubAlloc call uses space made available from&lt;br /&gt;
earlier DosSubFrees when possible, growing the segment as necessary when&lt;br /&gt;
the free heap space is insufficient. We will call the pieces of memory&lt;br /&gt;
returned by DosSubAlloc heap objects.&lt;br /&gt;
     The memory suballocation package works within the domain of a process.&lt;br /&gt;
The suballocation package doesn't allocate the memory from some &amp;quot;system&lt;br /&gt;
pool&amp;quot; outside the process's address space, as does the segment allocator.&lt;br /&gt;
The suballocation package doesn't even allocate segments; it manages only&lt;br /&gt;
segments supplied by and owned by (or at least accessible to) the caller.&lt;br /&gt;
This is a feature because memory protection is on a per-segment basis. If&lt;br /&gt;
the suballocation package were to get its space from some system global&lt;br /&gt;
segment, a process that overwrote its heap object could damage one&lt;br /&gt;
belonging to another process. Figure 9-3 illustrates memory suballocation.&lt;br /&gt;
It shows a segment j being suballocated. H is the suballocation header; the&lt;br /&gt;
shaded areas are free space.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       ÚÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
       ³  Segment  ³       ³  Segment  ³       ³  Segment  ³&lt;br /&gt;
       ³     i     ³       ³     j     ³       ³     k     ³&lt;br /&gt;
       ÀÄÄÄÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                          /            \&lt;br /&gt;
                        /                \&lt;br /&gt;
                      /                    \&lt;br /&gt;
                    /                        \&lt;br /&gt;
                  /                            \&lt;br /&gt;
                /                                \&lt;br /&gt;
              /                                    \&lt;br /&gt;
            /                                        \&lt;br /&gt;
          /                                            \&lt;br /&gt;
        /                                                \&lt;br /&gt;
      /                                                    \&lt;br /&gt;
    /                                                        \&lt;br /&gt;
  /                                                            \&lt;br /&gt;
ÚÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÂÂÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÂÄÄÄÄÄÄÄÂÄÄÄÂÄÄÄÄÂÄÄÄÂÄÄÄÄÄÄÄ¿&lt;br /&gt;
³H ³  ³     ³°°°³³°°°°³           ³ ³°°°°°°°³   ³    ³   ³°°°°°°°³&lt;br /&gt;
³  ³  ³     ³°°°³³°°°°³           ³ ³°°°°°°°³   ³    ³   ³°°°°°°°³&lt;br /&gt;
ÀÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÁÁÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÁÄÄÄÁÄÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 9-3.  Memory suballocation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     We said that the memory suballocator subdivides segments that are&lt;br /&gt;
accessible to the client process. This means that you can use it to&lt;br /&gt;
subdivide space in a shared memory segment. Such a technique can be handy&lt;br /&gt;
when two or more processes are using a shared memory segment for&lt;br /&gt;
intercommunication, but there is risk because an error in one process can&lt;br /&gt;
easily corrupt the heap objects of another.&lt;br /&gt;
     Earlier, in the discussion of dynamic link subsystems, we described&lt;br /&gt;
facilities and techniques for writing a reliable subsystem. The OS/2 memory&lt;br /&gt;
suballocation package is a good example of such a subsystem, so let's look&lt;br /&gt;
at its workings more closely. The first try at the suballocation package&lt;br /&gt;
produced a straightforward heap allocator, much like the one in a C or&lt;br /&gt;
Pascal runtime library. It maintained a free chain of heap objects and&lt;br /&gt;
allocated them at its client's request. If the closest-size free heap&lt;br /&gt;
object was still bigger than the request, it was split into an allocated&lt;br /&gt;
part and a free part. Freed heap objects were coalesced with any adjacent&lt;br /&gt;
free objects. The suballocation package took a segment pointer and some&lt;br /&gt;
other arguments and returned some values--an offset and the changed data in&lt;br /&gt;
the segment itself where the heap headers were stored. If we stretch things&lt;br /&gt;
a little and consider the changed state of the supplied data segment as a&lt;br /&gt;
returned value, then the suballocation package at this stage is much like a&lt;br /&gt;
function: It has no state of its own; it merely returns values computed&lt;br /&gt;
only from the input arguments. This simple suballocation dynlink routine&lt;br /&gt;
uses no global data segments and doesn't even need an instance data&lt;br /&gt;
segment.&lt;br /&gt;
     This simple implementation has an important drawback: More than one&lt;br /&gt;
process can't safely use it to manage a shared memory segment; likewise,&lt;br /&gt;
multiple threads within one process can't use it. The heap free list is a&lt;br /&gt;
critical section; if multiple threads call the suballocator on the same&lt;br /&gt;
segment, the heap free list can become corrupted. This problem necessitated&lt;br /&gt;
upgrading the suballocation package to use a semaphore to protect the&lt;br /&gt;
critical section. If we didn't want to support suballocation of shared&lt;br /&gt;
memory and were only worried about multiple threads within a task, we could&lt;br /&gt;
use RAM semaphores located in the managed segment itself to protect the&lt;br /&gt;
critical section. The semaphore might be left set if the process died&lt;br /&gt;
unexpectedly, but the managed segment isn't shared. It's going to be&lt;br /&gt;
destroyed in any case, so we don't care.&lt;br /&gt;
     But, even in this simple situation of managing only privately owned&lt;br /&gt;
segments, we must concern ourselves with some special situations. One&lt;br /&gt;
problem is signals: What if the suballocator is called with thread 1, and a&lt;br /&gt;
signal (such as SIGINT, meaning that the user pressed Ctrl-C) comes in?&lt;br /&gt;
Thread 1 is interrupted from the suballocation critical section to execute&lt;br /&gt;
the signal handler. Often signal handlers return to the interrupted code,&lt;br /&gt;
and all is well. But what if the signal handler does not return but jumps&lt;br /&gt;
to the application's command loop? Or what if it does return, but before it&lt;br /&gt;
does so calls the memory suballocator? In these two cases, we'd have a&lt;br /&gt;
deadlock on the critical section. We can solve these problems by using the&lt;br /&gt;
DosHoldSignal function. DosHoldSignal does for signals what the CLI&lt;br /&gt;
instruction does for hardware interrupts: It holds them off for a short&lt;br /&gt;
time. Actually, it holds them off forever unless the application releases&lt;br /&gt;
them, but holding signals for more than a second or two is poor practice.&lt;br /&gt;
If you precede the critical section's semaphore claim call with a signal&lt;br /&gt;
hold and follow the critical section's semaphore release call with a signal&lt;br /&gt;
release, you're protected from deadlocks caused by signal handling.&lt;br /&gt;
     Note that unlike the CLI instruction, DosHoldSignal calls nest. OS/2&lt;br /&gt;
counts the number of DosHoldSignal &amp;quot;hold&amp;quot; calls made and holds signals off&lt;br /&gt;
until an equal number of &amp;quot;release&amp;quot; calls are issued. This means that a&lt;br /&gt;
routine can safely execute a hold/release pair without affecting the state&lt;br /&gt;
of its calling code. If the caller had signals held at the time of the&lt;br /&gt;
call, they will remain held. If signals were free at the time of the call,&lt;br /&gt;
the callee's &amp;quot;release&amp;quot; call restores them to that state.&lt;br /&gt;
     Whenever dynlink packages make any call that changes the state of the&lt;br /&gt;
process or the thread, they must be sure to restore that state before they&lt;br /&gt;
return to their caller. Functions that nest, such as DosHoldSignal,&lt;br /&gt;
accomplish this automatically. For other functions, the dynlink package&lt;br /&gt;
should explicitly discover and remember the previous state so that it can&lt;br /&gt;
be restored.&lt;br /&gt;
     Our problems aren't over though. A second problem is brought about by&lt;br /&gt;
the DosExitList facility. If a client process's thread is in the&lt;br /&gt;
suballocation package's critical section and the client terminates&lt;br /&gt;
suddenly--it could be killed externally or have a GP fault--the process&lt;br /&gt;
might not die immediately. If any DosExitList handlers are registered, they&lt;br /&gt;
will be called. They might call the memory suballocator, and once again we&lt;br /&gt;
face deadlock. We could solve this situation with the classic approach of&lt;br /&gt;
making a bug into a feature: Document that the suballocator can't be called&lt;br /&gt;
at exitlist time. This may make sense for some dynlink subsystems, but it's&lt;br /&gt;
too restrictive for an important OS/2 facility. We've got to deal with this&lt;br /&gt;
problem too.&lt;br /&gt;
     The DosHoldSignal trick won't help us here. It would indeed prevent&lt;br /&gt;
external kills, but it would not prevent GP faults and the like. We could&lt;br /&gt;
say, &amp;quot;A program that GP faults is very sick, so all bets are off.&amp;quot; This&lt;br /&gt;
position is valid, except that if the program or one of its dynlink&lt;br /&gt;
subsystems uses DosExitList and the DosExitList handler tries to allocate&lt;br /&gt;
or release a heap object, the process will hang and never terminate&lt;br /&gt;
correctly. This is unacceptable because the user would be forced to reboot&lt;br /&gt;
to get rid of the moribund application. The answer is to use a system&lt;br /&gt;
semaphore rather than a RAM semaphore to protect the memory segment. System&lt;br /&gt;
semaphores are a bit slower than RAM semaphores, but they have some extra&lt;br /&gt;
features. One is that they can be made exclusive; only the thread that owns&lt;br /&gt;
the semaphore can release it. Coupled with this is an &amp;quot;owner death&amp;quot;&lt;br /&gt;
notification facility that allows a process's DosExitList handler an&lt;br /&gt;
opportunity to determine that one of its threads has orphaned a semaphore&lt;br /&gt;
(see 16.2 Data Integrity for details). Our suballocation package can now&lt;br /&gt;
protect itself by using exclusive system semaphores to protect its critical&lt;br /&gt;
section and by registering a DosExitList handler to release that semaphore.&lt;br /&gt;
The exitlist code can discover if a thread in its process has orphaned the&lt;br /&gt;
semaphore and, if so, can release it. Of course, releasing the semaphore&lt;br /&gt;
won't help if the heap headers are in an inconsistent state. You can write&lt;br /&gt;
the suballocation package so that the heap is never in an inconsistent&lt;br /&gt;
state, or you can write it to keep track of the modified state so that the&lt;br /&gt;
exitlist handler can repair the heap structure.&lt;br /&gt;
     In this later case, be sure the DosExitList handler you establish to&lt;br /&gt;
clean up the heap is called first (see DosExitList documentation).&lt;br /&gt;
     Finally, even if we decide that the client application won't be&lt;br /&gt;
allowed to issue suballocation requests during its own exitlist processing,&lt;br /&gt;
we want the memory suballocator to support allocating a shared segment&lt;br /&gt;
among many different processes. Because of this, the actual OS/2&lt;br /&gt;
suballocation package makes use of DosExitList so that the suballocation&lt;br /&gt;
structure and semaphores can be cleaned up should a client thread terminate&lt;br /&gt;
while in the suballocation critical section.&lt;br /&gt;
     The suballocation dynlink package does more than illustrate subsystem&lt;br /&gt;
design; it also illustrates the value of a system architecture that uses&lt;br /&gt;
dynlinks as a standard system interface, regardless of the type of code&lt;br /&gt;
that provides the service. As you have seen, the memory suballocation&lt;br /&gt;
package released with OS/2 version 1.0 doesn't reside in the kernel; it's&lt;br /&gt;
effectively a subroutine package. OS/2 in an 80286 environment will&lt;br /&gt;
undoubtedly preserve this approach in future releases, but a forthcoming&lt;br /&gt;
80386 version of OS/2 may not. The 80386 architecture supports paged&lt;br /&gt;
virtual memory, so memory swapping (actually, paging) can take place on&lt;br /&gt;
part of a segment. This future paging environment may precipitate some&lt;br /&gt;
changes in the memory suballocator. Perhaps we'll want to rearrange the&lt;br /&gt;
heap for better efficiency with paging, or perhaps the OS/2 kernel will&lt;br /&gt;
want to become involved so that it can better anticipate paging demands. In&lt;br /&gt;
any case, any future release of OS/2 has complete flexibility to upgrade&lt;br /&gt;
the memory suballocation package in any externally compatible fashion,&lt;br /&gt;
thanks to the standard interface provided by dynamic links.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.3  Segment Swapping&lt;br /&gt;
&lt;br /&gt;
One of the most important features of the 80286 memory management hardware&lt;br /&gt;
is swapping support. Swapping is a technique by which some code or data&lt;br /&gt;
segments in memory are written to a disk file, thus allowing the memory&lt;br /&gt;
they were using to be reclaimed for another purpose. Later, the swapped-out&lt;br /&gt;
code or data is reloaded into memory. This technique lets you run more&lt;br /&gt;
programs than can simultaneously fit in memory; all you need is enough&lt;br /&gt;
memory to hold the programs that are running at that particular moment.&lt;br /&gt;
Quiescent programs can be swapped to disk to make room for active ones.&lt;br /&gt;
Later, when the swapped programs become active, OS/2 reads them in and&lt;br /&gt;
resumes them. If necessary OS/2 first makes memory available by swapping&lt;br /&gt;
out another quiescent program.&lt;br /&gt;
     Although I used the word program above, swapping is actually done on a&lt;br /&gt;
segment basis. Segments are swapped out individually and completely; the&lt;br /&gt;
OS/2 swapping code doesn't pay attention to relationships between segments&lt;br /&gt;
(they aren't swapped in groups), and the 80286 hardware does not allow only&lt;br /&gt;
part of a segment to be swapped. I simplified the concept a bit in the&lt;br /&gt;
above paragraph. You need not swap out an entire process; you can swap out&lt;br /&gt;
some segments and leave others in memory. OS/2 can and commonly does run a&lt;br /&gt;
process when some of its segments are swapped out. As long as a process&lt;br /&gt;
does not try to use the swapped-out segments, it runs unhindered. If a&lt;br /&gt;
process references a swapped-out segment, the 80286 hardware generates a&lt;br /&gt;
special trap that OS/2 intercepts. The segment fault trap handler swaps in&lt;br /&gt;
the missing segment, first swapping out some other if need be, and then the&lt;br /&gt;
process resumes where it left off. Segment faulting is invisible to a&lt;br /&gt;
process; the process executes normally, except that a segment load&lt;br /&gt;
instruction takes on the order of 30 milliseconds instead of the usual 3&lt;br /&gt;
microseconds.&lt;br /&gt;
     When memory is depleted and a segment must be swapped, OS/2 has to&lt;br /&gt;
choose one to swap out. Making the right choice is important; for example,&lt;br /&gt;
consider a process that alternates references between segment A and segment&lt;br /&gt;
B. If A is swapped out, a poorly designed system might choose B to swap out&lt;br /&gt;
to make room for A. After a few instructions are executed, B has to be&lt;br /&gt;
swapped in. If A is in turn swapped out to make room for B, the system&lt;br /&gt;
would soon spend all its time swapping A and B to and from the disk. This&lt;br /&gt;
is called thrashing, and thrashing can destroy system performance. In other&lt;br /&gt;
words, the effect of swapping is to make some segment loads take 10,000&lt;br /&gt;
times longer than they would if the segment were in memory. Although the&lt;br /&gt;
number 10,000 seems very large, the actual time of about 30 milliseconds is&lt;br /&gt;
not, as long as we don't have to pay those 30 milliseconds very often.&lt;br /&gt;
     A lot hinges on choosing segments to swap out that won't be referenced&lt;br /&gt;
in the near future. OS/2 uses the LRU (Least Recently Used) scheme to&lt;br /&gt;
determine which segment it will swap out. The ideal choice is the segment--&lt;br /&gt;
among those currently in memory--that will be referenced last because this&lt;br /&gt;
postpones the swap-in of that segment as long as possible. Unfortunately,&lt;br /&gt;
it's mathematically provable that no operating system can predict the&lt;br /&gt;
behavior of arbitrary processes. Instead, operating systems try to make an&lt;br /&gt;
educated guess as to which segment in memory is least likely to be&lt;br /&gt;
referenced in the immediate future. The LRU scheme is precisely that--a&lt;br /&gt;
good guess. OS/2 figures that if a segment hasn't been used in a long time&lt;br /&gt;
then it probably won't be used for a long time yet, so it swaps out the&lt;br /&gt;
segment that was last used the longest time ago--in other words, the least&lt;br /&gt;
recently used segment.&lt;br /&gt;
     Of course, it's easy to construct an example where the LRU decision is&lt;br /&gt;
the wrong one or even the worst one. The classic example is a program that&lt;br /&gt;
references, round robin, N segments when there is room in memory for only&lt;br /&gt;
N-1. When you attempt to make room for segment I, the least recently used&lt;br /&gt;
segment will be I+1, which in fact is the segment that will next be used. A&lt;br /&gt;
discussion of reference locality and working set problems, as these are&lt;br /&gt;
called, is beyond the scope of this book. Authors of programs that will&lt;br /&gt;
make repetitious accesses to large bodies of data or code should study the&lt;br /&gt;
available literature on virtual memory systems. Remember, on an 80286, OS/2&lt;br /&gt;
swaps only on a segment basis. A future 80386 release of OS/2 will swap, or&lt;br /&gt;
page, on a 4 KB page basis.&lt;br /&gt;
     The swapping algorithm is strictly LRU among all swap-eligible&lt;br /&gt;
segments in the system. Thread/process priority is not considered; system&lt;br /&gt;
segments that are marked swappable get no special treatment. Some system&lt;br /&gt;
segments are marked nonswappable, however. For example, swapping out the&lt;br /&gt;
OS/2 code that performs swap-ins would be embarrassing. Likewise, the disk&lt;br /&gt;
driver code for the swapping disk must not be swapped out. Some kernel and&lt;br /&gt;
device driver code is called at interrupt time; this is never swapped&lt;br /&gt;
because of the swap-in delay and because of potential interference between&lt;br /&gt;
the swapped-out interrupt handling code and the interrupt handling code of&lt;br /&gt;
the disk driver that will do the swap-in. Finally, some kernel code is&lt;br /&gt;
called in real mode in response to requests from the 3x box. No real mode&lt;br /&gt;
code can be swapped because the processor does not support segment faults&lt;br /&gt;
when running in real mode.&lt;br /&gt;
     The technique of running more programs then there is RAM to hold them&lt;br /&gt;
is called memory overcommit. OS/2 has to keep careful track of the degree&lt;br /&gt;
of overcommit so that it doesn't find itself with too much of a good thing-&lt;br /&gt;
-not enough free RAM, even with swapping, to swap in a swapped-out process.&lt;br /&gt;
Such a situation is doubly painful: Not only can the user not access or&lt;br /&gt;
save the data that he or she has spent the last four hours working on, but&lt;br /&gt;
OS/2 can't even tell the program what's wrong because it can't get the&lt;br /&gt;
program into memory to run it. To prevent this, OS/2 keeps track of its&lt;br /&gt;
commitments and overcommitments in two ways. First, before it starts a&lt;br /&gt;
process, OS/2 ensures that there is enough swap space to run it. Second, it&lt;br /&gt;
ensures that there is always enough available RAM to execute a swapped-out&lt;br /&gt;
process.&lt;br /&gt;
     At first glance, knowing if RAM is sufficient to run a process seems&lt;br /&gt;
simple--either the process fits into memory or it doesn't. Life is a bit&lt;br /&gt;
more complicated than that under OS/2 because the segments of a program or&lt;br /&gt;
a dynlink library may be marked for demand loading. This means that they&lt;br /&gt;
won't come in when the program starts executing but may be called in later.&lt;br /&gt;
Obviously, once a program starts executing, it can make nearly unlimited&lt;br /&gt;
demands for memory. When a program requests a memory allocation, however,&lt;br /&gt;
OS/2 can return an error code if available memory is insufficient. The&lt;br /&gt;
program can then deal with the problem: make do with less, refuse the&lt;br /&gt;
user's command, and so forth.&lt;br /&gt;
     OS/2 isn't concerned about a program's explicit memory requests&lt;br /&gt;
because they can always be refused; the implicit memory requests are the&lt;br /&gt;
problem--faulting in a demand load segment, for example. Not only is there&lt;br /&gt;
no interface to give the program an error code,&lt;br /&gt;
4. A demand load segment is faulted in via a &amp;quot;load segment register&amp;quot;&lt;br /&gt;
instruction. These CPU instructions don't return error codes!&lt;br /&gt;
4 but the program may be&lt;br /&gt;
unable to proceed without the segment. As a result, when a program is first&lt;br /&gt;
loaded (via a DosExecPgm call), OS/2 sums the size of all its impure&lt;br /&gt;
segments even if they are marked for &amp;quot;load on demand.&amp;quot; The same computation&lt;br /&gt;
is done for all the loadtime dynlink libraries it references and for all&lt;br /&gt;
the libraries they reference and so on. This final number, plus the&lt;br /&gt;
internal system per-process overhead, is the maximum implicit memory demand&lt;br /&gt;
of the program. If that much free swap space is available, the program can&lt;br /&gt;
start execution.&lt;br /&gt;
     You have undoubtedly noticed that I said we could run the program if&lt;br /&gt;
there was enough swap space. But a program must be in RAM to execute,&lt;br /&gt;
so why don't we care about the amount of available RAM space? We&lt;br /&gt;
do care. Not about the actual amount of free RAM when we start a program,&lt;br /&gt;
but about the amount of RAM that can be made free--by swapping--if needed.&lt;br /&gt;
If some RAM contains a swappable segment, then we can swap it&lt;br /&gt;
because we set aside enough swap space for the task. Pure segments, by the&lt;br /&gt;
way, are not normally swapped. In lieu of a swap-out, OS/2 simply discards&lt;br /&gt;
them. When it's time to swap them in, OS/2 reloads them from their original&lt;br /&gt;
.EXE or .DLL files.&lt;br /&gt;
5. An exception to this is programs that were executed from removable&lt;br /&gt;
media. OS/2 preloads all pure segments from such .EXE and .DLL files&lt;br /&gt;
and swaps them as necessary. This prevents certain deadlock problems&lt;br /&gt;
involving the hard error daemon and the volume management code.&lt;br /&gt;
5&lt;br /&gt;
     Because not all segments of a process need to be in memory for the&lt;br /&gt;
process to execute, we don't have to ensure enough free RAM for the entire&lt;br /&gt;
process, just enough so that we can simultaneously load six 64 KB segments-&lt;br /&gt;
-the maximum amount of memory needed to run any process. The numbers 6 and&lt;br /&gt;
64 KB are derived from the design of the 80286. To execute even a single&lt;br /&gt;
instruction of a process, all the segments selected by the four segment&lt;br /&gt;
registers must be in memory. The other two necessary segments come from the&lt;br /&gt;
worst case scenario of a program trying to execute a far return instruction&lt;br /&gt;
from a ring 2 segment (see 18.1 I/O Privilege Mechanism). The four&lt;br /&gt;
segments named in the registers must be present for the instruction to&lt;br /&gt;
start, and the two new segments--CS and SS--that the far return instruction&lt;br /&gt;
will reference must be present for the instruction to complete. That makes&lt;br /&gt;
six; the 64 KB comes from the maximum size a segment can reach. As a&lt;br /&gt;
result, as long as OS/2 can free up those six 64 KB memory regions, by&lt;br /&gt;
swapping and discarding if necessary, any swapped-out program can execute.&lt;br /&gt;
     Naturally, if that were the only available memory and it had to be&lt;br /&gt;
shared by all running processes, system response would be very poor.&lt;br /&gt;
Normally, much more RAM space is available. The memory overcommit code is&lt;br /&gt;
concerned only that all processes can run; it won't refuse to start a&lt;br /&gt;
process because it might execute slowly. It could be that the applications&lt;br /&gt;
that a particular user runs and their usage pattern are such that the user&lt;br /&gt;
finds the performance acceptable and thus hasn't bought more memory. Or&lt;br /&gt;
perhaps the slowness is a rare occurrence, and the user is willing to&lt;br /&gt;
accept it just this once. In general, if the system thrashes--&lt;br /&gt;
spends too much time swapping--it's a soft failure: The user knows what's&lt;br /&gt;
wrong, the user knows what to do to make it get better (run fewer programs&lt;br /&gt;
or buy more memory), and the user can meanwhile continue to work.&lt;br /&gt;
     Clearly, because all segments of the applications are swappable and&lt;br /&gt;
because we've ensured that the swap space is sufficient for all of them,&lt;br /&gt;
initiating a new process doesn't consume any of our free or freeable RAM.&lt;br /&gt;
It's the device drivers and their ability to allocate nonswappable segments&lt;br /&gt;
that can drain the RAM pool. For this reason, OS/2 may refuse to load a&lt;br /&gt;
device driver or to honor a device driver's memory allocation request if to&lt;br /&gt;
do so would leave less than six 64 KB areas of RAM available.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.3.1  Swapping Miscellany&lt;br /&gt;
The system swap space consists of a special file, called SWAPPER.DAT,&lt;br /&gt;
created at boot time. The location of the file is described in the&lt;br /&gt;
CONFIG.SYS file. OS/2 may not allocate the entire maximum size of the swap&lt;br /&gt;
file initially; instead, it may allocate a smaller size and grow the swap&lt;br /&gt;
file to its maximum size if needed. The swap file may grow, but in OS/2&lt;br /&gt;
version 1.0 it never shrinks.&lt;br /&gt;
     The available swap space in the system is more than the maximum size&lt;br /&gt;
of the swap file; it also includes extra RAM. Clearly, a system with 8 MB&lt;br /&gt;
of RAM and a 200 KB swap file should be able to run programs that consume&lt;br /&gt;
more than 200 KB. After setting aside the memory consumed by nonswappable&lt;br /&gt;
segments and our six 64 KB reserved areas, the remaining RAM is considered&lt;br /&gt;
part of the swap file for memory overcommit accounting purposes.&lt;br /&gt;
     We mentioned in passing that memory used in real mode can't be&lt;br /&gt;
swapped. This means that the entire 3x box memory area is nonswappable. In&lt;br /&gt;
fact, the casual attitude of MS-DOS applications toward memory allocation&lt;br /&gt;
forces OS/2 to keep a strict boundary between real mode and protect mode&lt;br /&gt;
memory. Memory below the RMSIZE value specified in CONFIG.SYS belongs&lt;br /&gt;
exclusively to the real mode program, minus that consumed by the device&lt;br /&gt;
drivers and the parts of the OS/2 kernel that run in real mode.&lt;br /&gt;
     Early in the development of OS/2, attempts were made to put protect&lt;br /&gt;
mode segments into any unused real mode memory, but we abandoned this&lt;br /&gt;
approach. First, because the risk was great that the real mode program&lt;br /&gt;
might overwrite part of the segment. Although this is technically a bug on&lt;br /&gt;
the part of the real mode application, such bugs generally do not affect&lt;br /&gt;
program execution in an MS-DOS environment because that memory is unused at&lt;br /&gt;
the time. Thus, such bugs undoubtedly exist unnoticed in today's MS-DOS&lt;br /&gt;
applications, waiting to wreak havoc in the OS/2 environment.&lt;br /&gt;
     A second reason concerns existing real mode applications having been&lt;br /&gt;
written for a single-tasking environment. Such an application commonly asks&lt;br /&gt;
for 1 MB of memory, a request that must be refused. The refusal, however,&lt;br /&gt;
also specifies the amount of memory available at the time of the call. Real&lt;br /&gt;
mode applications then turn around and ask for that amount, but they don't&lt;br /&gt;
check to see if an &amp;quot;insufficient memory&amp;quot; error code was returned from the&lt;br /&gt;
second call. After all, how could such a code be returned? The operating&lt;br /&gt;
system has just said that the memory was available. This coding sequence&lt;br /&gt;
can cause disaster in a multitasking environment where the memory might&lt;br /&gt;
have been allocated elsewhere between the first and second call from the&lt;br /&gt;
application. This is another reason OS/2 sets aside a fixed region of&lt;br /&gt;
memory for the 3x box and never uses it for other purposes, even if it&lt;br /&gt;
appears to be idle.&lt;br /&gt;
     We mentioned that OS/2's primary concern is that programs be able to&lt;br /&gt;
execute at all; whether they execute well is the user's problem. This&lt;br /&gt;
approach is acceptable because OS/2 is a single-user system. Multiuser&lt;br /&gt;
systems need to deal with thrashing situations because the users that&lt;br /&gt;
suffer from thrashing may not be the ones who created it and may be&lt;br /&gt;
powerless to alleviate it. In a single-user environment, however, the user&lt;br /&gt;
is responsible for the load that caused the thrashing, the user is the one&lt;br /&gt;
who is suffering from it, and the user is the one who can fix the situation&lt;br /&gt;
by buying more RAM or terminating a few applications. Nevertheless,&lt;br /&gt;
applications with considerable memory needs should be written so as to&lt;br /&gt;
minimize their impact on the system swapper.&lt;br /&gt;
     Fundamentally, all swapping optimization techniques boil down to one&lt;br /&gt;
issue: locality of reference. This means keeping the memory locations that&lt;br /&gt;
are referenced near one another in time and in space. If your program&lt;br /&gt;
supports five functions, put the code of each function in a separate&lt;br /&gt;
segment, with another segment holding common code. The user can then work&lt;br /&gt;
with one function, and the other segments can be swapped. If each function&lt;br /&gt;
had some code in each of five segments, all segments would have to be in&lt;br /&gt;
memory at all times.&lt;br /&gt;
     A large body of literature deals with these issues because of the&lt;br /&gt;
prevalence of virtual memory systems in the mainframe environment. Most of&lt;br /&gt;
this work was done when RAM was very expensive. To precisely determine&lt;br /&gt;
which segments or pages should be resident and which should be swapped was&lt;br /&gt;
worth a great deal of effort. Memory was costly, and swapping devices were&lt;br /&gt;
fast, so algorithms were designed to &amp;quot;crank the screws down tight&amp;quot; and free&lt;br /&gt;
up as much memory as possible. After all, if they misjudged and swapped&lt;br /&gt;
something that was needed soon, it could be brought back in quickly. The&lt;br /&gt;
OS/2 environment is inverted: RAM is comparatively cheap, and the swapping&lt;br /&gt;
disk, being the regular system hard disk, is comparatively slow.&lt;br /&gt;
Consequently, OS/2's swapping strategy is to identify segments that are&lt;br /&gt;
clearly idle and swap them (because cheap RAM doesn't mean free RAM) but&lt;br /&gt;
not to judge things so closely that segments are frequently swapped when&lt;br /&gt;
they should not be.&lt;br /&gt;
     A key concept derived from this classic virtual memory work is that of&lt;br /&gt;
the working set. A thread's working set is the set of segments it will&lt;br /&gt;
reference &amp;quot;soon&amp;quot;--in the next several seconds or few minutes. Programmers&lt;br /&gt;
should analyze their code to determine its working sets; obviously the set&lt;br /&gt;
of segments in the working set will vary with the work the application is&lt;br /&gt;
doing. Code and data should be arranged between segments so that the size&lt;br /&gt;
of each common working set consists of a minimum amount of memory. For&lt;br /&gt;
example, if a program contains extensive code and data to deal with&lt;br /&gt;
uncommon error situations, these items should reside in separate segments&lt;br /&gt;
so that they aren't resident except when needed. You don't want to burden&lt;br /&gt;
the system with too many segments; two functions that are frequently used&lt;br /&gt;
together should occupy the same segment, but large unrelated bodies of code&lt;br /&gt;
and data should have their own segments or be grouped with other items that&lt;br /&gt;
are in their working set. Consider segment size when packing items into&lt;br /&gt;
segments. Too many small segments increase system overhead; large segments&lt;br /&gt;
decrease the efficiency of the swap mechanism. Splitting a segment in two&lt;br /&gt;
doesn't make sense if all code in the segment belongs to the same working&lt;br /&gt;
set, but it does make sense to split large bodies of unrelated code and&lt;br /&gt;
data.&lt;br /&gt;
     As we said before, an exhaustive discussion of these issues is beyond&lt;br /&gt;
the scope of this book. Programmers writing memory-intensive applications&lt;br /&gt;
should study the literature and their programs to optimize their&lt;br /&gt;
performance in an OS/2 environment. Minimizing an application's memory&lt;br /&gt;
requirements is more than being a &amp;quot;good citizen&amp;quot;; the smaller a program's&lt;br /&gt;
working set, the better it will run when the system load picks up.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.4  Status and Information&lt;br /&gt;
&lt;br /&gt;
OS/2 takes advantage of the 80286 LDT and GDT architecture in providing two&lt;br /&gt;
special segments, called infosegs, that contain system information. OS/2&lt;br /&gt;
updates these segments when changes take place, so their information is&lt;br /&gt;
always current. One infoseg is global, and the other is local. The global&lt;br /&gt;
infoseg contains information about the system as a whole; the local infoseg&lt;br /&gt;
contains process specific data. Naturally, the global infoseg is read only&lt;br /&gt;
and is shared among all processes. Local infosegs are also read only, but&lt;br /&gt;
each process has its own.&lt;br /&gt;
     The global infoseg contains time and date information. The &amp;quot;seconds&lt;br /&gt;
elapsed since 1970&amp;quot; field is particularly useful for time-stamping events&lt;br /&gt;
because calculating the interval between two times is easy. Simply subtract&lt;br /&gt;
and then divide by the number of seconds in the unit of time in which&lt;br /&gt;
you're interested. It's important that you remember that the date/time&lt;br /&gt;
fields are 32-bit fields but the 80286 reads data 16 bits at a time. Thus,&lt;br /&gt;
if an application reads the two time-stamp words at the same time as they&lt;br /&gt;
are being updated, it may read a bad value--not a value off by 1, but a&lt;br /&gt;
value that is off by 63335. The easiest way to deal with this is to read&lt;br /&gt;
the value and then compare the just read value with the infoseg contents.&lt;br /&gt;
If they are the same, your read value is correct. If they differ, continue&lt;br /&gt;
reading and comparing until the read and infoseg  values agree. The RAS&lt;br /&gt;
6. Reliability, Availability, and Serviceability. A buzzword that refers&lt;br /&gt;
to components intended to aid field diagnosis of system malfunctions.&lt;br /&gt;
6&lt;br /&gt;
information is used for field system diagnosis and is not of general&lt;br /&gt;
interest to programmers.&lt;br /&gt;
     The local infoseg segment contains process and thread information. The&lt;br /&gt;
information is accurate for the currently executing thread. The subscreen&lt;br /&gt;
group value is used by the presentation manager subsystem and is not of&lt;br /&gt;
value to applications. For more information on global and local infosegs,&lt;br /&gt;
see the OS/2 reference manual.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==10  Environment Strings==&lt;br /&gt;
&lt;br /&gt;
A major requirement of OS/2 is the ability to support logical device and&lt;br /&gt;
directory names. For example, a program needs to write a temporary scratch&lt;br /&gt;
file to the user's fastest disk. Which disk is that? Is it drive C, the&lt;br /&gt;
hard disk? Some machines don't have a hard disk. Is it drive B, the floppy&lt;br /&gt;
drive? Some machines don't have a drive B. And even if a hard disk on drive&lt;br /&gt;
C exists, maybe drive D also exists and has more free space. Or perhaps&lt;br /&gt;
drive E is preferred because it's a RAM disk. Perhaps it's not, though,&lt;br /&gt;
because the user wants the scratch file preserved when the machine is&lt;br /&gt;
powered down. This program needs the ability to specify a logical&lt;br /&gt;
directory--the scratch file directory--rather than a physical drive and&lt;br /&gt;
directory such as A:\ or C:\TEMP. The user could then specify the physical&lt;br /&gt;
location (drive and directory) that corresponds to the logical&lt;br /&gt;
directory.&lt;br /&gt;
     Another example is a spell-checker program that stores two&lt;br /&gt;
dictionaries on a disk. Presumably, the dictionary files were copied to a&lt;br /&gt;
hard disk when the program was installed, but on which drive and directory?&lt;br /&gt;
The checker's author could certainly hard code a directory such as&lt;br /&gt;
C:\SPELLCHK\DICT1. But what if the user doesn't have a C drive, or what if&lt;br /&gt;
drive C is full and the user wants to use drive D instead? How can this&lt;br /&gt;
program offer the user the flexibility of putting the dictionary files&lt;br /&gt;
where they best fit and yet still find them when it needs them?&lt;br /&gt;
     The answer to these problems is a logical device and directory name&lt;br /&gt;
facility. Such a facility should have three characteristics:&lt;br /&gt;
&lt;br /&gt;
     þ  It should allow the user to map the logical directories onto the&lt;br /&gt;
        actual (physical) devices and directories at will. It should be&lt;br /&gt;
        possible to change these mappings without changing the programs&lt;br /&gt;
        that use them.&lt;br /&gt;
&lt;br /&gt;
     þ  The set of possible logical devices and directories should be very&lt;br /&gt;
        large and arbitrarily expandable. Some new program, such as our&lt;br /&gt;
        spelling checker, will always need a new logical directory.&lt;br /&gt;
&lt;br /&gt;
     þ  The name set should be large and collision free. Many programs will&lt;br /&gt;
        want to use logical directory names. If all names must come from a&lt;br /&gt;
        small set of possibilities, such as X1, X2, X3, and so on, two&lt;br /&gt;
        applications, written independently, may each choose the same name&lt;br /&gt;
        for conflicting uses.&lt;br /&gt;
&lt;br /&gt;
     The original version of MS-DOS did not provide for logical devices and&lt;br /&gt;
directories. In those days a maximum PC configuration consisted of two&lt;br /&gt;
floppy disks. Operating the machine entailed playing a lot of &amp;quot;disk jockey&amp;quot;&lt;br /&gt;
as the user moved system, program, and data disks in and out of the drives.&lt;br /&gt;
The user was the only one who could judge which drive should contain which&lt;br /&gt;
floppy and its associated data, and data files moved from drive to drive&lt;br /&gt;
dynamically. A logical device mechanism would have been of little use.&lt;br /&gt;
Logical directories were not needed because MS-DOS version 1.0 didn't&lt;br /&gt;
support directories. MS-DOS versions 2.x and 3.x propagated the &amp;quot;physical&lt;br /&gt;
names only&amp;quot; architecture because of memory limitations and because of the&lt;br /&gt;
catch-22 of new operating system features: Applications won't take&lt;br /&gt;
advantage of the new feature because many machines are running older&lt;br /&gt;
versions of MS-DOS without that new feature.&lt;br /&gt;
     None of these reasons holds true for OS/2. All OS/2 protect mode&lt;br /&gt;
applications will be rewritten. OS/2 has access to plenty of memory.&lt;br /&gt;
Finally, OS/2 needs a logical drive/directory mechanism: All OS/2 machines&lt;br /&gt;
have hard disks or similar facilities, and all OS/2 machines will run a&lt;br /&gt;
variety of sophisticated applications that need access to private files and&lt;br /&gt;
work areas. As a result, the environment string mechanism in MS-DOS has&lt;br /&gt;
been expanded to serve as the logical name in OS/2.&lt;br /&gt;
     Because of the memory allocation techniques employed by MS-DOS&lt;br /&gt;
programs and because of the lack of segment motion and swapping in real&lt;br /&gt;
mode, the MS-DOS environment list was very limited in size. The size of the&lt;br /&gt;
environment segment was easily exceeded. OS/2 allows environment segments&lt;br /&gt;
to be grown arbitrarily, at any time, subject only to the hardware's 64 KB&lt;br /&gt;
length limitation. In keeping with the OS/2 architecture, each process has&lt;br /&gt;
its own environment segment. By default, the child inherits a copy of the&lt;br /&gt;
parent's segment, but the parent can substitute other environment values at&lt;br /&gt;
DosExecPgm time.&lt;br /&gt;
     Using the environment string facility to provide logical names is&lt;br /&gt;
straightforward. If a convention for the logical name that you need doesn't&lt;br /&gt;
already exist, you must choose a meaningful name. Your installation&lt;br /&gt;
instructions or software should document how to use the environment string;&lt;br /&gt;
the application should display an error message or use an appropriate&lt;br /&gt;
default if the logical names do not appear in the environment string.&lt;br /&gt;
Because each process has its own environment segment that it inherited from&lt;br /&gt;
its parent, batch files, startup scripts, and initiator programs that load&lt;br /&gt;
applications can conveniently set up the necessary strings. This also&lt;br /&gt;
allows several applications or multiple copies of the same application to&lt;br /&gt;
define the same logical name differently.&lt;br /&gt;
     The existing conventions are:&lt;br /&gt;
&lt;br /&gt;
     PATH=&lt;br /&gt;
     PATH defines a list of directories that CMD.EXE searches when it has&lt;br /&gt;
     been instructed to execute a program. The directories are searched&lt;br /&gt;
     from left to right and are separated by semicolons. For example,&lt;br /&gt;
&lt;br /&gt;
        PATH=C:\BIN;D:\TOOLS;.&lt;br /&gt;
&lt;br /&gt;
     means search C:\BIN first, D:\TOOLS second, and the current working&lt;br /&gt;
     directory third.&lt;br /&gt;
&lt;br /&gt;
     DPATH=&lt;br /&gt;
     DPATH defines a list of directories that programs may search to locate&lt;br /&gt;
     a data file. The directories are searched from left to right and are&lt;br /&gt;
     separated by semicolons. For example:&lt;br /&gt;
&lt;br /&gt;
        DPATH=C:\DBM;D:\TEMP;.&lt;br /&gt;
&lt;br /&gt;
Applications use DPATH as a convenience to the user: A user can work from&lt;br /&gt;
one directory and reference data files in another directory, named in the&lt;br /&gt;
DPATH string, without specifying the full path names of the data files.&lt;br /&gt;
Obviously, applications and users must use this technique with care.&lt;br /&gt;
Searching too widely for a filename is extremely dangerous; the wrong file&lt;br /&gt;
may be found because filenames themselves are often duplicated in different&lt;br /&gt;
directories. To use the DPATH string, an application must first use&lt;br /&gt;
DosScanEnv to locate the DPATH string, and then it must use DosSearchPath&lt;br /&gt;
to locate the data file.&lt;br /&gt;
&lt;br /&gt;
     INCLUDE=&lt;br /&gt;
     The INCLUDE name defines the drive and directory where compiler and&lt;br /&gt;
     assembler standard include files are located.&lt;br /&gt;
&lt;br /&gt;
     INIT=&lt;br /&gt;
     The INIT name defines the drive and directory that contains&lt;br /&gt;
     initialization and configuration information for the application. For&lt;br /&gt;
     example, some applications define files that contain the user's&lt;br /&gt;
     preferred defaults. These files might be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
     LIB=&lt;br /&gt;
     The LIB name defines the drive and directory where the standard&lt;br /&gt;
     language library modules are kept.&lt;br /&gt;
&lt;br /&gt;
     PROMPT=&lt;br /&gt;
     The PROMPT name defines the CMD.EXE prompt string. Special character&lt;br /&gt;
     sequences are defined so that the CMD.EXE prompt can contain the&lt;br /&gt;
     working directory, the date and time, and so on. See CMD.EXE&lt;br /&gt;
     documentation for details.&lt;br /&gt;
&lt;br /&gt;
     TEMP=&lt;br /&gt;
     The TEMP name defines the drive and directory for temporary files.&lt;br /&gt;
     This directory is on a device that is relatively fast and has&lt;br /&gt;
     sufficient room for scratch files. The TEMP directory should be&lt;br /&gt;
     considered volatile; its contents can be lost during a reboot&lt;br /&gt;
     operation.&lt;br /&gt;
&lt;br /&gt;
     The environment segment is a very flexible tool that you can use to&lt;br /&gt;
customize the environment of an application or a group of applications. For&lt;br /&gt;
example, you can use environment strings to specify default options for&lt;br /&gt;
applications. Users can use the same systemwide default or change that&lt;br /&gt;
value for a particular screen group or activation of the application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==11  Interprocess Communication==&lt;br /&gt;
&lt;br /&gt;
Interprocess Communication (IPC) is central to OS/2. As we discussed&lt;br /&gt;
earlier, effective IPC is needed to support both the tool-based&lt;br /&gt;
architecture and the dynlink interface for interprocess services. Because&lt;br /&gt;
IPC is so important, OS/2 provides several forms to fulfill a variety of&lt;br /&gt;
needs.&lt;br /&gt;
&lt;br /&gt;
===11.1  Shared Memory===&lt;br /&gt;
&lt;br /&gt;
Shared memory has already been discussed in some detail. To summarize, the&lt;br /&gt;
two forms are named shared memory (access is requested by the client by&lt;br /&gt;
name) and giveaway shared memory (a current owner gives access to another&lt;br /&gt;
process). Shared memory is the most efficient form of IPC because no data&lt;br /&gt;
copying or calls to the operating system kernel are involved once the&lt;br /&gt;
shared memory has been set up. Shared memory does require more effort on&lt;br /&gt;
the part of the client processes; a protocol must be established,&lt;br /&gt;
semaphores and flags are usually needed, and exposure to amok programs and&lt;br /&gt;
premature termination must be considered. Applications that expect to deal&lt;br /&gt;
with a low volume of data may want to consider using named pipes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2  Semaphores&lt;br /&gt;
&lt;br /&gt;
A semaphore is a flag or a signal. In its basic form a semaphore has only&lt;br /&gt;
two states--on and off or stop and go. A railroad semaphore, for example,&lt;br /&gt;
is either red or green--stop or go. In computer software, a semaphore is a&lt;br /&gt;
flag or a signal used by one thread of execution to flag or signal&lt;br /&gt;
another. Often it's for purposes of mutual exclusion: &amp;quot;I'm in here, stay&lt;br /&gt;
out.&amp;quot; But sometimes it can be used to indicate other events: &amp;quot;Your data is&lt;br /&gt;
ready.&amp;quot;&lt;br /&gt;
     OS/2 supports two kinds of semaphores, each of which can be used in&lt;br /&gt;
two different ways. The two kinds of semaphores--RAM semaphores and system&lt;br /&gt;
semaphores--have a lot in common, and the same system API is used to&lt;br /&gt;
manipulate both. A RAM semaphore, as its name implies, uses a 4-byte data&lt;br /&gt;
structure kept in a RAM location that must be accessible to all threads&lt;br /&gt;
that use it. The system API that manipulates RAM semaphores is located in a&lt;br /&gt;
dynlink subsystem. This code claims semaphores with an atomic test-and-set&lt;br /&gt;
operation,&lt;br /&gt;
1. An atomic operation is one that is indivisible and therefore&lt;br /&gt;
cannot be interrupted in the middle.&lt;br /&gt;
1 so it need not enter the kernel (ring 0) to protect itself&lt;br /&gt;
against preemption. As a result, the most common tasks--claiming a free&lt;br /&gt;
semaphore and freeing a semaphore that has no waiters--are very fast, on&lt;br /&gt;
the order of 100 microseconds on a 6-MHz 1-wait-state IBM PC/AT.&lt;br /&gt;
2. This is the standard environment when quoting speeds; it's&lt;br /&gt;
both the common case and the worst case. Machines that don't run&lt;br /&gt;
at this speed run faster.&lt;br /&gt;
2 If the&lt;br /&gt;
semaphore is already claimed and the caller must block or if another thread&lt;br /&gt;
is waiting on the semaphore, the semaphore dynlink package must enter&lt;br /&gt;
kernel mode.&lt;br /&gt;
     System semaphores, on the other hand, use a data structure that is&lt;br /&gt;
kept in system memory outside the address space of any process. Therefore,&lt;br /&gt;
system semaphore operations are slower than RAM semaphore operations, on&lt;br /&gt;
the order of 350 microseconds for an uncontested semaphore claim. Some&lt;br /&gt;
important advantages offset this operating speed however. System semaphores&lt;br /&gt;
support mechanisms that prevent deadlock by crashing programs, and system&lt;br /&gt;
semaphores support exclusivity and counting features. As a general rule,&lt;br /&gt;
you should use RAM semaphores when the requirement is wholly contained&lt;br /&gt;
within one process. When multiple processes may be involved, use system&lt;br /&gt;
semaphores.&lt;br /&gt;
     The first step, regardless of the type or use of the semaphore, is to&lt;br /&gt;
create it. An application creates RAM semaphores simply by allocating a 4-&lt;br /&gt;
byte area of memory initialized to zero. The far address of this area is&lt;br /&gt;
the RAM semaphore handle. The DosCreateSem call creates system semaphores.&lt;br /&gt;
(The DosCreateSem call takes an exclusivity argument, which we'll discuss&lt;br /&gt;
later.) Although semaphores control thread execution, semaphore handles&lt;br /&gt;
are owned by the process. Once a semaphore is created and its handle&lt;br /&gt;
obtained, all threads in that process can use that handle. Other&lt;br /&gt;
processes must open the semaphore via DosOpenSem. There is no explicit&lt;br /&gt;
open for a RAM semaphore. To be useful for IPC, the RAM semaphore must&lt;br /&gt;
be in a shared memory segment so that another process can access it;&lt;br /&gt;
the other process simply learns the far address of the RAM semaphore. A RAM&lt;br /&gt;
semaphore is initialized by zeroing out its 4-byte memory area.&lt;br /&gt;
     Except for opening and closing, RAM and system semaphores use exactly&lt;br /&gt;
the same OS/2 semaphore calls. Each semaphore call takes a semaphore handle&lt;br /&gt;
as an argument. A RAM semaphore's handle is its address; a system&lt;br /&gt;
semaphore's handle was returned by the create or open call. The OS/2&lt;br /&gt;
semaphore routines can distinguish between RAM and system semaphores by&lt;br /&gt;
examining the handle they are passed. Because system semaphores and their&lt;br /&gt;
names are kept in an internal OS/2 data area, they are a finite resource;&lt;br /&gt;
the number of RAM semaphores is limited only by the amount of available RAM&lt;br /&gt;
to hold them.&lt;br /&gt;
     The most common use of semaphores is to protect critical sections. To&lt;br /&gt;
reiterate, a critical section is a body of code that manipulates a data&lt;br /&gt;
resource in a nonreentrant way. In other words, a critical section will&lt;br /&gt;
screw up if two threads call it at the same time on the same data resource.&lt;br /&gt;
A critical section can cover more than one section of code; if one&lt;br /&gt;
subroutine adds entries to a table and another subroutine removes entries,&lt;br /&gt;
both subroutines are in the table's critical section. A critical section is&lt;br /&gt;
much like an airplane washroom, and the semaphore is like the sign that&lt;br /&gt;
says &amp;quot;Occupied.&amp;quot; The first user sets the semaphore and starts manipulating&lt;br /&gt;
the resource; meanwhile others arrive, see that the semaphore is set, and&lt;br /&gt;
block (that is, wait) outside. When the critical section becomes available&lt;br /&gt;
and the semaphore is cleared, only one of the waiting threads gets to claim&lt;br /&gt;
it; the others keep on waiting.&lt;br /&gt;
     Using semaphores to protect critical sections is straightforward. At&lt;br /&gt;
the top of a section of code that will manipulate the critical resource,&lt;br /&gt;
insert a call to DosSemRequest. When this call returns, the semaphore is&lt;br /&gt;
claimed, and the code can proceed. When the code is finished and the&lt;br /&gt;
critical section is &amp;quot;clean,&amp;quot; call DosSemClear. DosSemClear releases the&lt;br /&gt;
semaphore and reactivates any thread waiting on it.&lt;br /&gt;
     System semaphores are different from RAM semaphores in this&lt;br /&gt;
application in one critical respect. If a system semaphore is created for&lt;br /&gt;
exclusive use, it can be used as a counting semaphore. Exclusive use means&lt;br /&gt;
that only the thread that set the semaphore can clear it;&lt;br /&gt;
3. This is a departure from the principle of resource ownership&lt;br /&gt;
by process, not by thread. The thread, not the process, owns the&lt;br /&gt;
privilege to clear a set &amp;quot;exclusive use&amp;quot; semaphore.&lt;br /&gt;
3 this is expected&lt;br /&gt;
when protecting critical sections. A counting semaphore can be set many&lt;br /&gt;
times but must be released an equal number of times before it becomes free.&lt;br /&gt;
For example, an application contains function A and function B, each of&lt;br /&gt;
which manipulates the same critical section. Each claims the semaphore at&lt;br /&gt;
its beginning and releases it at its end. However, under some&lt;br /&gt;
circumstances, function A may need to call function B. Function A can't&lt;br /&gt;
release the semaphore before it calls B because it's still in the critical&lt;br /&gt;
section and the data is in an inconsistent state. But when B issues&lt;br /&gt;
DosSemRequest on the semaphore, it blocks because the semaphore was already&lt;br /&gt;
set by A.&lt;br /&gt;
     A counting semaphore solves this problem. When function B makes the&lt;br /&gt;
second, redundant DosSemRequest call, OS/2 recognizes it as the same thread&lt;br /&gt;
that already owns the semaphore, and instead of blocking the thread, it&lt;br /&gt;
increments a counter to show that the semaphore has been claimed twice.&lt;br /&gt;
Later, when function B releases the semaphore, OS/2 decrements the counter.&lt;br /&gt;
Because the counter is not at zero, the semaphore is not really clear and&lt;br /&gt;
thus not released. The semaphore is truly released only after function B&lt;br /&gt;
returns to function A, and A, finishing its work, releases the semaphore a&lt;br /&gt;
second time.&lt;br /&gt;
     A second major use of semaphores is signaling (unrelated to the signal&lt;br /&gt;
facility of OS/2). Signaling is using semaphores to notify threads that&lt;br /&gt;
certain events or activities have taken place. For example, consider a&lt;br /&gt;
multithreaded application that uses one thread to communicate over a serial&lt;br /&gt;
port and another thread to compute with the results of that communication.&lt;br /&gt;
The computing thread tells the communication thread to send a message and&lt;br /&gt;
get a reply, and then it goes about its own business. Later, the computing&lt;br /&gt;
thread wants to block until the reply is received but only if the reply&lt;br /&gt;
hasn't already been received--it may have already arrived, in which case&lt;br /&gt;
the computing thread doesn't want to block.&lt;br /&gt;
     You can handle this by using a semaphore as a flag. The computing&lt;br /&gt;
thread sets the semaphore via DosSemSet before it gives the order to the&lt;br /&gt;
communications thread. When the computing thread is ready to wait for the&lt;br /&gt;
reply, it does a DosSemWait on the semaphore it set earlier. When the&lt;br /&gt;
communications thread receives the reply, it clears the semaphore. When the&lt;br /&gt;
computing thread calls DosSemWait, it will continue without&lt;br /&gt;
delay if the semaphore is already clear. Otherwise, the computing thread&lt;br /&gt;
blocks until the semaphore is cleared. In this example, we aren't&lt;br /&gt;
protecting a critical section; we're using the semaphore transition&lt;br /&gt;
from set to clear to flag an event between multiple threads. Our needs are&lt;br /&gt;
the opposite of a critical section semaphore: We don't want the semaphore&lt;br /&gt;
to be exclusively owned; if it were, the communications thread couldn't&lt;br /&gt;
release it. We also don't want the semaphore to be counting. If it counts,&lt;br /&gt;
the computing thread won't block when it does the DosSemWait; OS/2 would&lt;br /&gt;
recognize that it did the DosSemSet earlier and would increment the&lt;br /&gt;
semaphore counter.&lt;br /&gt;
     OS/2 itself uses semaphore signaling in this fashion when asynchronous&lt;br /&gt;
communication is needed. For example, asynchronous I/O uses semaphores in&lt;br /&gt;
the signaling mode to indicate that an I/O operation has completed. The&lt;br /&gt;
system timer services use semaphores in the signaling mode to indicate that&lt;br /&gt;
the specified time has elapsed. OS/2 supports a special form of semaphore&lt;br /&gt;
waiting, called DosMuxSemWait, which allows a thread to wait on more than&lt;br /&gt;
one semaphore at one time. As soon as any specified semaphore becomes&lt;br /&gt;
clear, DosMuxSemWait returns. DosMuxSemWait, like DosSemWait, only waits&lt;br /&gt;
for a semaphore to become clear; it doesn't set or claim the semaphore as&lt;br /&gt;
does DosSemRequest. DosMuxSemWait allows a thread to wait on a variety of&lt;br /&gt;
events and to wake up whenever one of those events occurs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2.1  Semaphore Recovery&lt;br /&gt;
We discussed earlier some difficulties that can arise if a semaphore is&lt;br /&gt;
left set &amp;quot;orphaned&amp;quot; when its owner terminates unexpectedly. We'll review&lt;br /&gt;
the topic because it's critical that applications handle the situation&lt;br /&gt;
correctly and because that correctness generally has to be demonstrable by&lt;br /&gt;
inspection. It's very difficult to demonstrate and fix timing-related bugs&lt;br /&gt;
by just testing a program.&lt;br /&gt;
     Semaphores can become orphaned in at least four ways:&lt;br /&gt;
&lt;br /&gt;
     1.  An incoming signal can divert the CPU, and the signal handler can&lt;br /&gt;
         fail to return to the point of interruption.&lt;br /&gt;
&lt;br /&gt;
     2.  A process can kill another process without warning.&lt;br /&gt;
&lt;br /&gt;
     3.  A process can incur a GP fault, which is fatal.&lt;br /&gt;
&lt;br /&gt;
     4.  A process can malfunction because of a coding error and fail to&lt;br /&gt;
         release a semaphore.&lt;br /&gt;
&lt;br /&gt;
     The action to take in such events depends on how semaphores are being&lt;br /&gt;
used. In some situations, no action is needed. Our example of the computing&lt;br /&gt;
and communications threads is such a situation. If the process dies, the&lt;br /&gt;
semaphore and all its users die. Special treatment is necessary only if the&lt;br /&gt;
application uses DosExitList to run code that needs to use the semaphore.&lt;br /&gt;
This should rarely be necessary because semaphores are used within a&lt;br /&gt;
process to coordinate multiple threads and only one thread remains&lt;br /&gt;
when the exitlist is activated. Likewise, a process can receive signals&lt;br /&gt;
only if it has asked for them, so an application that does not use signals&lt;br /&gt;
need not worry about their interrupting its critical sections. An&lt;br /&gt;
application that does use signals can use DosHoldSignal, always return&lt;br /&gt;
from a signal handler, or prevent thread 1 (the signal-handling thread)&lt;br /&gt;
from entering critical sections.&lt;br /&gt;
     In other situations, the semaphore can protect a recoverable resource.&lt;br /&gt;
For example, you can use a system semaphore to protect access to a printer&lt;br /&gt;
that for some reason is being dealt with directly by applications rather&lt;br /&gt;
than by the system spooler. If the owner of the &amp;quot;I'm using the printer&amp;quot;&lt;br /&gt;
system semaphore dies unexpectedly, the next thread that tries to claim the&lt;br /&gt;
semaphore will be able to do so but will receive a special error code that&lt;br /&gt;
says, &amp;quot;The owner of this semaphore died while holding it.&amp;quot; In such a case,&lt;br /&gt;
the application can simply write a form feed or two to the printer and&lt;br /&gt;
continue. Other possible actions are to clean up the protected resource or&lt;br /&gt;
to execute a process that will do so. Finally, an application can display a&lt;br /&gt;
message to the user saying, &amp;quot;Gee, this database is corrupt! You better do&lt;br /&gt;
something,&amp;quot; and then terminate. In this case, the application should&lt;br /&gt;
deliberately terminate while holding the semaphore so that any other&lt;br /&gt;
threads waiting on it will receive the &amp;quot;owner died&amp;quot; message. Once the&lt;br /&gt;
&amp;quot;owner died&amp;quot; code is received, that state is cleared; so if the recipient&lt;br /&gt;
of the code releases the semaphore without fixing the inconsistencies in&lt;br /&gt;
the critical section, problems will result.&lt;br /&gt;
     Additional matters must be considered if a process intends to clean up&lt;br /&gt;
its own semaphores by means of a DosExitList handler. First, exclusive&lt;br /&gt;
(that is, counting) semaphores must be used. Although an exitlist routine&lt;br /&gt;
can tell that a RAM or nonexclusive system semaphore is reserved, it cannot&lt;br /&gt;
tell whether it is the process that reserved it. You may be tempted simply&lt;br /&gt;
to keep a flag byte that is set each time the semaphore is claimed and&lt;br /&gt;
cleared each time the semaphore is released, but that solution contains a&lt;br /&gt;
potentially deadly window of failure. If the thread sets the &amp;quot;I own it&amp;quot;&lt;br /&gt;
flag before it calls DosSemRequest, the thread could terminate between&lt;br /&gt;
setting the flag and receiving the semaphore. In that case, the exitlist&lt;br /&gt;
routine would believe, wrongly, that it owns the semaphore and would&lt;br /&gt;
therefore release it--a very unpleasant surprise for the true owner of the&lt;br /&gt;
semaphore. Conversely, if the thread claims the semaphore and then sets the&lt;br /&gt;
flag, a window exists in which the semaphore is claimed but the flag does&lt;br /&gt;
not say so. This is also disastrous.&lt;br /&gt;
     Using exclusive system semaphores solves these problems. As I&lt;br /&gt;
mentioned earlier, when the thread that has set a system semaphore dies&lt;br /&gt;
with the semaphore set, the semaphore is placed into a special &amp;quot;owner died&amp;quot;&lt;br /&gt;
state so that the next thread to attempt to claim the semaphore is informed&lt;br /&gt;
of its orphan status. There is an extra twist to this for exclusive-use&lt;br /&gt;
system semaphores. Should the process die due to an external cause or due&lt;br /&gt;
to a DosExit call and that process has a DosExitList handler, all orphaned&lt;br /&gt;
system semaphores are placed in a special &amp;quot;owner died&amp;quot; state so that only&lt;br /&gt;
that process's remaining thread--the one executing the DosExitList&lt;br /&gt;
handlers--can claim the semaphore. When it does so, it still receives the&lt;br /&gt;
special &amp;quot;owner died&amp;quot; code. The exitlist handler can use DosSemWait with a&lt;br /&gt;
timeout value of 0 to see if the semaphore is set. If the &amp;quot;owner died&amp;quot; code&lt;br /&gt;
is returned, then the DosExitList handler cleans up the resource and then&lt;br /&gt;
issues DosSemClear to clear the semaphore. If a thread terminates by&lt;br /&gt;
explicitly calling DosExit with the &amp;quot;terminate this thread&amp;quot; subcode, any&lt;br /&gt;
exclusive-use system semaphores that it has set will not enter this special&lt;br /&gt;
&amp;quot;owner died&amp;quot; state but will instead assume the general &amp;quot;owner died&amp;quot; state&lt;br /&gt;
that allows any thread in the system to claim the semaphore and receive the&lt;br /&gt;
&amp;quot;owner died&amp;quot; code. Likewise, any semaphores in the special &amp;quot;owner died&amp;quot;&lt;br /&gt;
state that are not cleared by the DosExitList handlers become normal &amp;quot;owner&lt;br /&gt;
died&amp;quot; semaphores when the process completely terminates.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2.2  Semaphore Scheduling&lt;br /&gt;
Although multiple threads can wait for a semaphore, only one thread gets&lt;br /&gt;
the semaphore when it becomes available. OS/2 schedules semaphore grants&lt;br /&gt;
based on CPU priority: The highest-priority waiting thread claims the&lt;br /&gt;
semaphore. If several waiting threads are at the highest priority, OS/2&lt;br /&gt;
distributes the grants among them evenly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.3  Named Pipes&lt;br /&gt;
&lt;br /&gt;
We've already discussed anonymous pipes--stream oriented IPC mechanisms&lt;br /&gt;
that work via the DosRead and DosWrite calls. Two processes can communicate&lt;br /&gt;
via anonymous pipes only if one is a descendant of the other and if the&lt;br /&gt;
descendant has inherited the parent's handle to the pipe. Anonymous pipes&lt;br /&gt;
are used almost exclusively to transfer input and output data to and from a&lt;br /&gt;
child process or to and from a subtree of child processes.&lt;br /&gt;
     OS/2 supports another form of pipes called named pipes. Named pipes&lt;br /&gt;
are not available in OS/2 version 1.0; they will be available in a later&lt;br /&gt;
release. I discuss them here because of their importance in the system&lt;br /&gt;
architecture. Also, because of the extensible nature of OS/2, it's possible&lt;br /&gt;
that named pipe functionality will be added to the system by including the&lt;br /&gt;
function in some other Microsoft system software package that, when it runs&lt;br /&gt;
under OS/2, installs the capability. In such a case, application programs&lt;br /&gt;
will be unable to distinguish the &amp;quot;add-on&amp;quot; named pipe facility from the&lt;br /&gt;
&amp;quot;built-in&amp;quot; version that will eventually be included in OS/2.&lt;br /&gt;
     Named pipes are much like anonymous pipes in that they're a serial&lt;br /&gt;
communications channel between two processes and they use the DosRead and&lt;br /&gt;
DosWrite interface. They are different, however, in several important&lt;br /&gt;
ways.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes have names in the file system name space. Users of a&lt;br /&gt;
        named pipe need not be related; they need only know the name of a&lt;br /&gt;
        pipe to access it.&lt;br /&gt;
&lt;br /&gt;
     þ  Because named pipes use the file system name space and because that&lt;br /&gt;
        name space can describe machines on a network, named pipes work&lt;br /&gt;
        both locally (within a single machine) and remotely (across a&lt;br /&gt;
        network).&lt;br /&gt;
&lt;br /&gt;
     þ  An anonymous pipe is a byte-stream mechanism. The system considers&lt;br /&gt;
        the data sent through an anonymous pipe as an undifferentiated&lt;br /&gt;
        stream of bytes. The writer can write a 100-byte block of data, and&lt;br /&gt;
        the reader can read the data with two 30-byte reads and one 40-byte&lt;br /&gt;
        read. If the byte stream contains individual messages, the&lt;br /&gt;
        recipient must determine where they start and stop. Named pipes can&lt;br /&gt;
        be used in this byte-stream mode, but named pipes also support&lt;br /&gt;
        support message mode, in which processes read and write streams&lt;br /&gt;
        of messages. When the named pipe is in message mode, OS/2&lt;br /&gt;
        (figuratively!) separates the messages from each other with pieces&lt;br /&gt;
        of waxed paper so that the reader can ask for &amp;quot;the next message&amp;quot;&lt;br /&gt;
        rather than for &amp;quot;the next 100 bytes.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes are full duplex, whereas anonymous pipes are actually a&lt;br /&gt;
        pair of pipes, each half duplex. When an anonymous pipe is created,&lt;br /&gt;
        two handles are returned--a read handle and a write handle. An open&lt;br /&gt;
        of a named pipe returns a single handle, which may (depending on&lt;br /&gt;
        the mode of the DosOpen) be both read and written. Although a full&lt;br /&gt;
        duplex named pipe is accessed via a single handle, the data moving&lt;br /&gt;
        in each direction is kept totally separate. A named pipe should be&lt;br /&gt;
        viewed as two separate pipes between the reader and the writer--one&lt;br /&gt;
        holds data going in, the other holds data coming back. For example,&lt;br /&gt;
        if a thread writes to a named pipe handle and then reads from that&lt;br /&gt;
        handle, the thread will not read back the data it just wrote. The&lt;br /&gt;
        data the thread just wrote in is in the outgoing side; the read&lt;br /&gt;
        reads from the incoming side.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes are frequently used to communicate with processes that&lt;br /&gt;
        provide a service to one or more clients, usually simultaneously.&lt;br /&gt;
        The named pipe API contains special functions to facilitate such&lt;br /&gt;
        use: pipe reusability, multiple pipes with identical names, and so&lt;br /&gt;
        on. These are discussed below.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes support transaction I/O calls that provide an efficient&lt;br /&gt;
        way to implement local and remote procedure call dialogs between&lt;br /&gt;
        processes.&lt;br /&gt;
&lt;br /&gt;
     þ  Programs running on MS-DOS version 3.x workstations can access&lt;br /&gt;
        named pipes on an OS/2 server to conduct dialogs with server&lt;br /&gt;
        applications because, to a client, a named pipe looks exactly like&lt;br /&gt;
        a file.&lt;br /&gt;
&lt;br /&gt;
     You'll recall that the creator of an anonymous pipe uses a special&lt;br /&gt;
interface (DosMakePipe) to create the pipe but that the client process&lt;br /&gt;
can use the DosRead and DosWrite functions, remaining ignorant of the&lt;br /&gt;
nature of the handle. The same holds true for named pipes when they&lt;br /&gt;
are used in stream mode. The creator of a named pipe uses a special API&lt;br /&gt;
to set it up, but its clients can use the pipe while remaining ignorant&lt;br /&gt;
of its nature as long as that use is serial.&lt;br /&gt;
4. Random access, using DosSeek, is not supported for&lt;br /&gt;
pipes and will cause an error code to be returned.&lt;br /&gt;
4 Named pipes are created by&lt;br /&gt;
the DosMakeNmPipe call. Once the pipe is created, one of the serving&lt;br /&gt;
process's threads must wait via the DosConnectNmPipe call for the client&lt;br /&gt;
to open the pipe. The client cannot successfully open the pipe until a&lt;br /&gt;
DosConnectNmPipe has been issued to it by the server process.&lt;br /&gt;
     Although the serving process understands that it's using a named pipe&lt;br /&gt;
and can therefore call a special named pipe API, the client process need&lt;br /&gt;
not be aware that it's using a named pipe because the normal DosOpen call&lt;br /&gt;
is used to open the pipe. Because named pipes appear in the file system&lt;br /&gt;
name space, the client can, for example, open a file called \PIPE\STATUS,&lt;br /&gt;
unaware that it's a named pipe being managed by another process. The&lt;br /&gt;
DosMakeNmPipe call returns a handle to the serving end of the pipe; the&lt;br /&gt;
DosOpen call returns a handle to the client end. As soon as the client&lt;br /&gt;
opens a pipe, the DosConnectNmPipe call returns to the serving process.&lt;br /&gt;
     Communication over a named pipe is similar to that over an anonymous&lt;br /&gt;
pipe: The client and server each issue reads and writes to the handle, as&lt;br /&gt;
appropriate for the mode of the open. When a process at one end of the pipe&lt;br /&gt;
closes it, the process at the other end gets an error code in response to&lt;br /&gt;
write operations and an EOF indication in response to read operations.&lt;br /&gt;
     The scenario just described is simple enough, but that's the problem:&lt;br /&gt;
It's too simple. In real life, a serving process probably stays around so&lt;br /&gt;
that it can serve the next client. This is the purpose behind the&lt;br /&gt;
DosConnectNmPipe call. After the first client closes its end of the named&lt;br /&gt;
pipe and the server end sees the EOF on the pipe, the server end issues a&lt;br /&gt;
DosDisconnectNmPipe call to acknowledge that the client has closed the pipe&lt;br /&gt;
(either explicitly or via termination). It can then issue another&lt;br /&gt;
DosConnectNmPipe call to reenable that pipe for reopening by another client&lt;br /&gt;
or by the same client. In other words, the connect and disconnect&lt;br /&gt;
operations allow a server to let clients, one by one, connect to it via a&lt;br /&gt;
single named pipe. The DosDisconnectNmPipe call can be used to forcibly&lt;br /&gt;
disconnect a client. This action is appropriate if a client makes an&lt;br /&gt;
invalid request or otherwise shows signs of ill health.&lt;br /&gt;
     We can serve multiple clients, one at a time, but what about serving&lt;br /&gt;
them in parallel? As we've described it so far, our serving process handles&lt;br /&gt;
only one client. A client's DosOpen call fails if the named pipe already&lt;br /&gt;
has a client user or if the server process hasn't issued the&lt;br /&gt;
DosConnectNmPipe call. This is where the instancing parameter, supplied to&lt;br /&gt;
DosMakeNmPipe, comes in.&lt;br /&gt;
     When a named pipe is first opened,&lt;br /&gt;
5. Like other non-file-system resident named objects, a named pipe&lt;br /&gt;
remains known to the system only as long as a process has it open.&lt;br /&gt;
When all handles to a named pipe are closed, OS/2 forgets all&lt;br /&gt;
information concerning the named pipe. The next DosMakeNmPipe&lt;br /&gt;
call recreates the named pipe from ground zero.&lt;br /&gt;
5 the instance count parameter is&lt;br /&gt;
specified in the pipe flag's word. If this count is greater than 1, the&lt;br /&gt;
pipe can be opened by a server process more than once. Additional opens are&lt;br /&gt;
done via DosMakeNmPipe, which returns another handle to access the new&lt;br /&gt;
instance of the pipe. Obviously the pipe isn't being &amp;quot;made&amp;quot; for the second&lt;br /&gt;
and subsequent calls to DosMakeNmPipe, but the DosOpen call can't be used&lt;br /&gt;
instead because it opens the client end of the named pipe, not the server&lt;br /&gt;
end. The instance count argument is ignored for the second and subsequent&lt;br /&gt;
DosMakeNmPipe calls. Extra instances of a named pipe can be created by the&lt;br /&gt;
same process that created the first instance, or they can be created by&lt;br /&gt;
other processes. Figure 11-1 illustrates multiple instances of a named&lt;br /&gt;
pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿              \\pipe\pipename      &lt;br /&gt;
³  Client  ÃÄÄÄÄÄ¿               �                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     A    ³     ³     °°°°°°°°°°°°°°°°°°°°°°     ³           ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ     ³               ÚÄÄÄÄÄÄÄÄÄÄ¿     ³           ³&lt;br /&gt;
                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´          ³     ³           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿                  ÚÄÄÁÄÄÄÄÄÄÄ¿  ³     ³           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´          ³  ³     ³           ³&lt;br /&gt;
³     B    ³               ÚÄÄÁÄÄÄÄÄÄÄ¿  ³  ³     ³  Server   ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ     ÚÄÄÄÄÄÄÄÄÄ´          ³  ³  ÃÄÄÄÄÄ´  Process  ³&lt;br /&gt;
                 ³      ÚÄÄÁÄÄÄÄÄÄÄ¿  ³  ÃÄÄÙ     ³           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿     ³  ÚÄÄÄ´          ³  ³  ÃÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÙ  ³   ³          ³  ÃÄÄÙ        ³           ³&lt;br /&gt;
³     C    ³        ³   ³          ³  ÃÄÄÄÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ        ³   ³          ÃÄÄÙ           ³           ³&lt;br /&gt;
                    ³   ³          ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿        ³   ÀÄÄÄÄÄÄÄÄÄÄÙ              ³           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÄÄÄÙ                             ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³     D    ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 11-1.  Multiple instances of a named pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When a client process does a DosOpen on a named pipe that has multiple&lt;br /&gt;
instances, OS/2 connects it to any server instance of the pipe that has&lt;br /&gt;
issued a DosConnectNmPipe call. If no instances are available and enabled,&lt;br /&gt;
the client receives an error code. OS/2 makes no guarantees about&lt;br /&gt;
distributing the incoming work evenly across all server instances; it&lt;br /&gt;
assumes that all server threads that issued a DosConnectNmPipe call are&lt;br /&gt;
equal.&lt;br /&gt;
     The multiple instance capability allows a single server process or&lt;br /&gt;
perhaps multiple server processes to handle many clients simultaneously.&lt;br /&gt;
One process using four threads can serve four clients as rapidly as four&lt;br /&gt;
processes, each with one thread, can do the job. As long as threads don't&lt;br /&gt;
interfere with one another by blocking on critical sections, a multiprocess&lt;br /&gt;
server has no inherent efficiency advantage over a multithread server.&lt;br /&gt;
     The OS/2 named pipe package includes some composite operations for&lt;br /&gt;
client processes: DosTransactNmPipe and DosCallNmPipe. DosTransactNmPipe is&lt;br /&gt;
much like a DosWrite followed by a DosRead: It sends a message to the&lt;br /&gt;
server end of the named pipe and then reads a reply. DosCallNmPipe does&lt;br /&gt;
the same on an unopened named pipe: It has the combined effect of a&lt;br /&gt;
DosOpen, a DosTransactNmPipe, and a DosClose. These calls are of little&lt;br /&gt;
value if the client and server processes are on the same machine; the&lt;br /&gt;
client could easily build such subroutines itself by appropriately&lt;br /&gt;
combining DosOpen, DosClose, DosRead, and DosWrite. These calls are in the&lt;br /&gt;
named pipe package because they provide significant performance savings in&lt;br /&gt;
a networked environment. If the server process is on a different machine&lt;br /&gt;
from the client process, OS/2 and the network transport can use a&lt;br /&gt;
datagramlike mechanism to implement these calls in a network-efficient&lt;br /&gt;
fashion. Because named pipes work invisibly across the network, any client&lt;br /&gt;
process that performs these types of operations should use these composite&lt;br /&gt;
calls, even if the author of the program didn't anticipate the program&lt;br /&gt;
being used in a networked environment. Using the composite calls will&lt;br /&gt;
 ensure the performance gains if a user decides to use a server process&lt;br /&gt;
located across the network. Readers familiar with network architecture will&lt;br /&gt;
recognize the DosCallNmPipe function as a form of remote procedure call. In&lt;br /&gt;
effect, it allows a process to make a procedure call to another process,&lt;br /&gt;
even a process on another machine.&lt;br /&gt;
     The OS/2 named pipe facility contains a great many features, as befits&lt;br /&gt;
its importance in realizing the OS/2 tool-based architecture. This book is&lt;br /&gt;
not intended to provide an exhaustive coverage of features, but a few other&lt;br /&gt;
miscellaneous items merit mention.&lt;br /&gt;
     Our above discussion concentrated on stream-based communications,&lt;br /&gt;
which can be convenient because they allow a client process to use a named&lt;br /&gt;
pipe while ignorant of its nature. For example, you can write a spooler&lt;br /&gt;
package for a device not supported by the system spoolers--say, for a&lt;br /&gt;
plotter device. Input to the spooler can be via a named pipe, perhaps&lt;br /&gt;
\PIPE\PLOTOUT. An application could then be told to write its plotter&lt;br /&gt;
output to a file named \PIPE\PLOTOUT or even \\PLOTMACH\PIPE\PLOTOUT&lt;br /&gt;
(across a network). The application will then use the spooler at the&lt;br /&gt;
other end of the named pipe.&lt;br /&gt;
     Sometimes, though, the client process does understand that it's&lt;br /&gt;
talking to a named pipe, and the information exchanged is a series of&lt;br /&gt;
messages rather than a long stream of plotter data. In this case, the named&lt;br /&gt;
pipe can be configured as a message stream in which each message is&lt;br /&gt;
indivisible and atomic at the interface. In other words, when a process&lt;br /&gt;
reads from a named pipe, it gets only one message per read, and it gets the&lt;br /&gt;
entire message. Messages can queue up in the pipe, but OS/2 remembers the&lt;br /&gt;
message boundaries so that it can split them apart as they are read.&lt;br /&gt;
Message streams can be used effectively in a networking environment because&lt;br /&gt;
the network transport can better judge how to assemble packets.&lt;br /&gt;
     Although our examples have shown the client and server processes&lt;br /&gt;
issuing calls and blocking until they are done, named pipes can be&lt;br /&gt;
configured to operate in a nonblocking fashion. This allows a server or a&lt;br /&gt;
client to test a pipe to see if it's ready for a particular operation,&lt;br /&gt;
thereby guaranteeing that the process won't be held up for some period&lt;br /&gt;
waiting for a request to complete. Processes can also use DosPeekNmPipe, a&lt;br /&gt;
related facility that returns a peek at any data (without consuming the&lt;br /&gt;
data) currently waiting to be read in the pipe interface. Servers can use&lt;br /&gt;
this to scan a client's request to see if they're interested in handling it&lt;br /&gt;
at that time.&lt;br /&gt;
     Finally, we mentioned that a process that attempts a DosOpen to a&lt;br /&gt;
named pipe without any available instances is returned an error code.&lt;br /&gt;
Typically, a client in this situation wants to wait for service to become&lt;br /&gt;
available, and it doesn't want to sit in a polling loop periodically &lt;br /&gt;
testing for server availability. The DosWaitNmPipe call is provided for&lt;br /&gt;
this situation; it allows a client to block until an instance of the named&lt;br /&gt;
pipe becomes available. When DosWaitNmPipe returns, the client must still&lt;br /&gt;
do a DosOpen. The DosOpen can fail, however, if another process has taken&lt;br /&gt;
the pipe instance in the time between the &amp;quot;wait&amp;quot; and the &amp;quot;open&amp;quot; calls. But&lt;br /&gt;
because multiple waiters for a named pipe are serviced in priority order,&lt;br /&gt;
such a &amp;quot;race&amp;quot; condition is uncommon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.4  Queues&lt;br /&gt;
&lt;br /&gt;
Queues are another form of IPC. In many ways they are similar to named&lt;br /&gt;
pipes, but they are also significantly different. Like named pipes, they&lt;br /&gt;
use the file system name space, and they pass messages rather than byte&lt;br /&gt;
streams. Unlike named pipes, queues allow multiple writes to a single queue&lt;br /&gt;
because the messages bring with them information about their sending&lt;br /&gt;
process that enables the queue reader to distinguish between messages from&lt;br /&gt;
different senders. Named pipes are strictly FIFO, whereas queue messages&lt;br /&gt;
can be read in a variety of orders. Finally, queues use shared memory as a&lt;br /&gt;
transfer mechanism; so although they're faster than named pipes for higher&lt;br /&gt;
volume data transfers on a single machine, they don't work across the&lt;br /&gt;
network.&lt;br /&gt;
     The interface to the queue package is similar but not identical to&lt;br /&gt;
that of the named pipe interface. Like named pipes, each queue has a single&lt;br /&gt;
owner that creates it. Clients open and close the queue while the owner,&lt;br /&gt;
typically, lives on. Unlike named pipes, the client process must use a&lt;br /&gt;
special queue API (DosReadQueue, DosWriteQueue, and so on) and thus must be&lt;br /&gt;
written especially to use the queue package. Although each queue has a&lt;br /&gt;
single owner, each queue can have multiple clients; so the queue mechanism&lt;br /&gt;
doesn't need a facility to have multiple queues of the same name, nor does&lt;br /&gt;
it need a DosWaitNmPipe equivalent.&lt;br /&gt;
     Queue messages are somewhat different from named pipe messages. In&lt;br /&gt;
addition to carrying the body of the message, each queue message carries&lt;br /&gt;
two additional pieces of information. One is the PID of the sender; OS/2&lt;br /&gt;
provides this information, and the sender cannot affect it. The other is a&lt;br /&gt;
word value that the sender supplied and that OS/2 and the queue package do&lt;br /&gt;
not interpret. Queue servers and clients can use this information as they&lt;br /&gt;
wish to facilitate communication.&lt;br /&gt;
     The queue package also contains a peek facility, similar to that of&lt;br /&gt;
named pipes but with an interesting twist. If a process peeks the named&lt;br /&gt;
pipe and then later reads from it, it can be sure that the message it reads&lt;br /&gt;
is the same one that it peeked because named pipes are always FIFO. Queues,&lt;br /&gt;
however, allow records to be read in different orders of priority. If a&lt;br /&gt;
queue is being read in priority order, a process might well peek a message,&lt;br /&gt;
but by the time the process issues the queue read, some other message of&lt;br /&gt;
higher priority may have arrived and thus be at the front of the list. To&lt;br /&gt;
get around this problem, when the queue package peeks a message, it returns&lt;br /&gt;
a magic cookie to the caller along with the message. The caller can supply&lt;br /&gt;
this cookie to a subsequent DosReadQueue call to ensure that the peeked&lt;br /&gt;
message is the one read, overriding the normal message-ranking process.&lt;br /&gt;
This magic cookie can also be supplied to the DosPeekQueue call to peek the&lt;br /&gt;
second and subsequent records in the queue.&lt;br /&gt;
     Finally, one extremely important difference between queues and named&lt;br /&gt;
pipes is that named pipes transfer (that is, copy) the data from the client&lt;br /&gt;
to the server process. Queues transfer only the address of the data; the&lt;br /&gt;
queue package does not touch the data itself. Thus, the data body of the&lt;br /&gt;
queue message must be addressable to both the client and the serving&lt;br /&gt;
process. This is straightforward if both the client and serving threads&lt;br /&gt;
belong to the same process. If the client and serving threads are from&lt;br /&gt;
different processes, however, the data body of the queue message must be in&lt;br /&gt;
a shared memory segment that is addressable to both the client and the&lt;br /&gt;
server.&lt;br /&gt;
     A related issue is buffer reusability. An application can reuse a&lt;br /&gt;
memory area immediately after its thread returns from the named pipe call&lt;br /&gt;
that wrote the data from that area; but when using a queue, the sender must&lt;br /&gt;
not overwrite the message area until it's sure the reading process is&lt;br /&gt;
finished with the message.&lt;br /&gt;
     One way to kill both these birds--the shared memory and the memory&lt;br /&gt;
reuse problems--with one stone is to use the memory suballocation package.&lt;br /&gt;
Both the client and the queue server need to have shared access to a memory&lt;br /&gt;
segment that is then managed by the memory suballocation package. The&lt;br /&gt;
client allocates a memory object to hold the queue message and write it to&lt;br /&gt;
the queue. The queue server can address that queue message because it's in&lt;br /&gt;
the shared memory segment. When the queue manager is finished with the&lt;br /&gt;
message, it calls the memory suballocator to release the memory object. The&lt;br /&gt;
client need not worry about when the server is finished with the message&lt;br /&gt;
because the client allocates a new message buffer for each new message,&lt;br /&gt;
relying on the server to return the messages fast enough so that the memory&lt;br /&gt;
suballocator doesn't run out of available space.&lt;br /&gt;
     A similar technique on a segment level is to use giveaway shared&lt;br /&gt;
memory. The client allocates a giveaway segment for each message content,&lt;br /&gt;
creates the message, gives away a shared addressability to the segment to&lt;br /&gt;
the server process, and then writes the message (actually, the message's&lt;br /&gt;
address) to the queue. Note that the sender uses the recipient's selector&lt;br /&gt;
as the data address in this case, not its own selector. When the thread&lt;br /&gt;
returns from that DosWriteQueue call, the client releases its access to the&lt;br /&gt;
segment via DosFreeSeg. When the server process is finished with the&lt;br /&gt;
message, it also releases the memory segment. Because the queue server is&lt;br /&gt;
the last process with access to that segment, the segment is then returned&lt;br /&gt;
to the free pool.&lt;br /&gt;
     Software designers need to consider carefully the tradeoffs between&lt;br /&gt;
queues, named pipes, and other forms of IPC. Queues are potentially very&lt;br /&gt;
fast because only addresses are copied, not the data itself; but the work&lt;br /&gt;
involved in managing and reusing the shared memory may consume the time&lt;br /&gt;
savings if the messages are small. In general, small messages that are&lt;br /&gt;
always read FIFO should go by named pipes, as should applications that&lt;br /&gt;
communicate with clients and servers across a network. Very large or high&lt;br /&gt;
data rate messages may be better suited to queues.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.5  Dynamic Data Exchange (DDE)&lt;br /&gt;
&lt;br /&gt;
DDE is a form of IPC available to processes that use the presentation&lt;br /&gt;
manager API. The presentation manager's interface is message oriented; that&lt;br /&gt;
is, the primary means of communication between a process and the&lt;br /&gt;
presentation manager is the passing of messages. The presentation manager&lt;br /&gt;
message interface allows applications to define private messages that have&lt;br /&gt;
a unique meaning throughout the PC. DDE is, strictly speaking, a protocol&lt;br /&gt;
that defines new messages for communication between applications that use&lt;br /&gt;
it.&lt;br /&gt;
     DDE messages can be directed at a particular recipient or broadcast to&lt;br /&gt;
all presentation manager applications on a particular PC. Typically, a&lt;br /&gt;
client process broadcasts a message that says, &amp;quot;Does anyone out there have&lt;br /&gt;
this information?&amp;quot; or &amp;quot;Does anyone out there provide this service?&amp;quot; If no&lt;br /&gt;
response is received, the answer is taken to be no. If a response is&lt;br /&gt;
received, it contains an identifying code&lt;br /&gt;
6.A window handle.&lt;br /&gt;
6 that allows the two processes to&lt;br /&gt;
communicate privately.&lt;br /&gt;
     DDE's broadcast mechanism and message orientation gives it a lot of&lt;br /&gt;
flexibility in a multiprocessing environment. For example, a specialized&lt;br /&gt;
application might be scanning stock quotes that are arriving via a special&lt;br /&gt;
link. A spreadsheet program could use DDE to tell this scanner application&lt;br /&gt;
to notify it whenever the quotes change for certain stocks that are&lt;br /&gt;
mentioned in its spreadsheet. Another application, perhaps called Market&lt;br /&gt;
Alert, might ask the scanner to notify it of trades in a different set of&lt;br /&gt;
stocks so that the alert program can flash a banner if those stocks trade&lt;br /&gt;
outside a prescribed range. DDEs can be used only by presentation manager&lt;br /&gt;
applications to communicate with the same.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.6  Signaling&lt;br /&gt;
&lt;br /&gt;
Signals are asynchronous notification mechanisms that operate in a fashion&lt;br /&gt;
analogous to hardware interrupts. Like hardware interrupts, when a signal&lt;br /&gt;
arrives at a process, that process's thread 1 stops after the instruction&lt;br /&gt;
it is executing and begins executing at a specified handler address. The&lt;br /&gt;
many special considerations to take into account when using signals are&lt;br /&gt;
discussed in Chapter 12. This section discusses their use as a form of&lt;br /&gt;
IPC.&lt;br /&gt;
     Processes typically receive signals in response to external events&lt;br /&gt;
that must be serviced immediately. Examples of such events are the user&lt;br /&gt;
pressing Ctrl-C or a process being killed. Three signals (flag A, flag B,&lt;br /&gt;
and flag C), however, are caused by another process&lt;br /&gt;
7. This is the typical case; but like all other system calls that affect&lt;br /&gt;
processes, a thread can make such a call to affect its own process.&lt;br /&gt;
7 issuing an explicit&lt;br /&gt;
DosFlagProcess API. DosFlagProcess is a unique form of IPC because it's&lt;br /&gt;
asynchronous. The recipient doesn't have to block or poll waiting for the&lt;br /&gt;
event; it finds out about it (by discovering itself to be executing the&lt;br /&gt;
signal handler) as soon as the scheduler gives it CPU time.&lt;br /&gt;
     DosFlagProcess, however, has some unique drawbacks. First, a signal&lt;br /&gt;
carries little information with it: only the number of the signal and a&lt;br /&gt;
single argument word. Second, signals can interrupt and interfere with some&lt;br /&gt;
system calls. Third, OS/2 views signals more as events than as messages; so&lt;br /&gt;
if signals are sent faster than the recipient can process them, OS/2&lt;br /&gt;
discards some of the overrun. These disadvantages (discussed in Chapter 12)&lt;br /&gt;
restrict signals to a rather specialized role as an IPC mechanism.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.7  Combining IPC Forms&lt;br /&gt;
&lt;br /&gt;
We've discussed each form of IPC, listing its strengths and weaknesses. If&lt;br /&gt;
you use forms in conjunction, however, you benefit from their combined&lt;br /&gt;
strengths. For example, a process can use named pipes or DDE to establish&lt;br /&gt;
contact with another process and then agree with it to send a high volume&lt;br /&gt;
of data via shared memory. An application that provides an IPC interface&lt;br /&gt;
should also provide a dynlink package to hide the details of the IPC. This&lt;br /&gt;
gives designers the flexibility to improve the IPC component of their&lt;br /&gt;
package in future releases while still maintaining interface compatibility&lt;br /&gt;
with their clients.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==12  Signals==&lt;br /&gt;
&lt;br /&gt;
The OS/2 signal mechanism is similar, but not identical, to the UNIX signal&lt;br /&gt;
mechanism. A signal is much like a hardware interrupt except that it is&lt;br /&gt;
initiated and implemented in software. Just as a hardware interrupt causes&lt;br /&gt;
the CS, IP, and Flags registers to be saved on the stack and execution to&lt;br /&gt;
begin at a handler address, a signal causes the application's CS, IP, and&lt;br /&gt;
Flags registers to be saved on the stack and execution to begin at a&lt;br /&gt;
signal-handler address. An IRET instruction returns control to the&lt;br /&gt;
interrupted address in both cases. Signals are different from hardware&lt;br /&gt;
interrupts in that they are a software construct and don't involve&lt;br /&gt;
privilege transitions, stack switches, or ring 0 code.&lt;br /&gt;
     OS/2 supports six signals--three common signals (Ctrl-C, Ctrl-Break,&lt;br /&gt;
and program termination) and three general-purpose signals. The Ctrl-C and&lt;br /&gt;
Ctrl-Break signals occur in response to keyboard activity; the program&lt;br /&gt;
termination signal occurs when a process is killed via the DosKill call.&lt;br /&gt;
1. The process termination signal handler is not called under&lt;br /&gt;
all conditions of process termination, only in response to&lt;br /&gt;
DosKill. Normal exits, GP faults, and so on do not&lt;br /&gt;
activate the process termination signal handler.&lt;br /&gt;
1&lt;br /&gt;
The three general-purpose signals are generated by an explicit call from a&lt;br /&gt;
thread, typically a thread from another process. A signal handler is in the&lt;br /&gt;
form of a far subroutine, that is, a subroutine that returns with a far&lt;br /&gt;
return instruction. When a signal arrives, the process's thread 1 is&lt;br /&gt;
interrupted from its current location and made to call the signal-handler&lt;br /&gt;
procedure with an argument provided by the signal generator. The signal-&lt;br /&gt;
handler code can return,&lt;br /&gt;
2. OS/2 interposes a code thunk, so the signal handler need not&lt;br /&gt;
concern itself with executing an IRET instruction, which language&lt;br /&gt;
compilers usually won't generate. When the signal handler is&lt;br /&gt;
entered, its return address points to a piece of OS/2 code that&lt;br /&gt;
contains the IRET instruction.&lt;br /&gt;
2 in which case the CPU returns from where it was&lt;br /&gt;
interrupted, or the signal handler can clean its stack and jump into the&lt;br /&gt;
process's code at some other spot, as in the C language's longjmp facility.&lt;br /&gt;
     The analogy between signals and hardware interrupts holds still&lt;br /&gt;
further. As it does in a hardware interrupt, the system blocks further&lt;br /&gt;
interrupts from the same source so that the signal handler won't be&lt;br /&gt;
arbitrarily reentered. The signal handler must issue a special form of&lt;br /&gt;
DosSetSigHandler to dismiss the signal and allow further signals to occur.&lt;br /&gt;
Typically this is done at the end of the signal-handling routine unless the&lt;br /&gt;
signal handler is reentrant. Also, like hardware interrupts, the equivalent&lt;br /&gt;
of the CLI instruction--the DosHoldSignal call--is used to protect critical&lt;br /&gt;
sections from being interrupted via a signal.&lt;br /&gt;
     Unlike hardware interrupts, signals have no interrupt priority. As&lt;br /&gt;
each enabled signal occurs, the signal handler is entered, even if another&lt;br /&gt;
signal handler must be interrupted. New signal events that come in while&lt;br /&gt;
that signal is still being processed from an earlier event--before the&lt;br /&gt;
signal has been dismissed by the handler--are held until the previous&lt;br /&gt;
signal event has been dismissed. Like hardware interrupts, this is a&lt;br /&gt;
pending-signal flag, not a counter. If three signals of the same kind are&lt;br /&gt;
held off, only one signal event occurs when that signal becomes&lt;br /&gt;
reenabled.&lt;br /&gt;
     A signal event occurs in the context of a process whose thread of&lt;br /&gt;
execution is interrupted for the signal handler; a signal doesn't cause the&lt;br /&gt;
CPU to stop executing another process in order to execute the first&lt;br /&gt;
process's signal handler. When OS/2 &amp;quot;posts&amp;quot; a signal to a process, it&lt;br /&gt;
simply makes a mark that says, &amp;quot;The next time we run this guy, store his&lt;br /&gt;
CS, IP, and Flags values on the stack and start executing here instead.&amp;quot;&lt;br /&gt;
The system uses its regular priority rules to assign the CPU to threads;&lt;br /&gt;
when the scheduler next runs the signaled thread, the dispatcher code that&lt;br /&gt;
sends the CPU into the application's code reads the &amp;quot;posted signal&amp;quot; mark&lt;br /&gt;
and does the required work.&lt;br /&gt;
     Because a signal &amp;quot;pseudo interrupt&amp;quot; is merely a trick of the&lt;br /&gt;
dispatcher, signal handlers don't run in ring 0 as do hardware interrupt&lt;br /&gt;
handlers; they run in ring 3 as do all application threads. In general, as&lt;br /&gt;
far as OS/2 is concerned, the process isn't in any sort of special state&lt;br /&gt;
when it's executing a signal handler, and no special rules govern what a&lt;br /&gt;
thread can and cannot do in a signal handler.&lt;br /&gt;
     Receiving a signal when thread 1 is executing an application or&lt;br /&gt;
dynlink code is straightforward: The system saves CS, IP, and Flags, and&lt;br /&gt;
the signal handler saves the rest. The full register complement can be&lt;br /&gt;
restored after the signal has been processed, and thread 1's normal&lt;br /&gt;
execution resumes without incident. If thread 1 is executing a system call&lt;br /&gt;
that takes the CPU inside the kernel, the situation is more complex. OS/2&lt;br /&gt;
can't emulate an interrupt from the system ring 0 code to the application's&lt;br /&gt;
ring 3 code, nor can OS/2 take the chance that the signal handler never&lt;br /&gt;
returns from the signal&lt;br /&gt;
3.It's acceptable for a signal handler to clean up thread 1's&lt;br /&gt;
stack, dismiss the signal, jump to another part of the&lt;br /&gt;
application, and never return from the signal. For example, an&lt;br /&gt;
application can jump into its &amp;quot;prompt and command loop&amp;quot; in&lt;br /&gt;
response to the press of Ctrl-C.&lt;br /&gt;
3 and therefore leaves OS/2's internals in an&lt;br /&gt;
intermediate state. Instead, when a signal is posted and thread 1 is&lt;br /&gt;
executing ring 0 OS/2 code, the system either completes its operations&lt;br /&gt;
before recognizing the signal or aborts the operation and then recognizes&lt;br /&gt;
the signal. If the operation is expected to take place &amp;quot;quickly,&amp;quot; the&lt;br /&gt;
system completes the operation, and the signal is recognized at the point&lt;br /&gt;
where the CPU resumes executing the application's ring 3 code.&lt;br /&gt;
     All non-I/O operations are deemed to complete &amp;quot;quickly,&amp;quot; with the&lt;br /&gt;
exception of the explicit blocking operations such as DosSleep, DosSemWait,&lt;br /&gt;
and so on. I/O operations depend on the specific device. Disk I/O completes&lt;br /&gt;
quickly, but keyboard and serial I/O generally do not. Clearly, if we wait&lt;br /&gt;
for the user to finish typing a line before we recognize a signal, we might&lt;br /&gt;
never recognize it--especially if the signal is Ctrl-C! In the case of&lt;br /&gt;
&amp;quot;slow devices,&amp;quot; OS/2 or the device driver terminates the operation and&lt;br /&gt;
returns to the application with an error code. The signal is recognized&lt;br /&gt;
when the CPU is about to resume executing the ring 3 application code that&lt;br /&gt;
follows the system call that was interrupted.&lt;br /&gt;
     Although the application is given an error code to explain that the&lt;br /&gt;
system call was interrupted, the application may be unable to reissue the&lt;br /&gt;
system call to complete the work. In the case of device I/O, the&lt;br /&gt;
application typically can't tell how much, if any, of the requested output&lt;br /&gt;
or input took place before the signal interrupted the operation. If an&lt;br /&gt;
output operation is not reissued, some data at the end of the write may be&lt;br /&gt;
missing. If an output operation is restarted, then some data at the&lt;br /&gt;
beginning of the write may be written twice. In the case of DosSleep, the&lt;br /&gt;
application cannot tell how much of the requested sleep has elapsed. These&lt;br /&gt;
issues are not usually a problem; it's typically keyboard input that is&lt;br /&gt;
interrupted. In the case of the common signals (Ctrl-C, Ctrl-Break, and&lt;br /&gt;
process killed) the application typically flushes partial keyboard input&lt;br /&gt;
anyway. Applications that use other &amp;quot;slow&amp;quot; devices or the IPC flag signals&lt;br /&gt;
need to deal with this, however.&lt;br /&gt;
     Although a process can have multiple threads, only thread 1 is used to&lt;br /&gt;
execute the signal handler.&lt;br /&gt;
4. For this reason, a process should not terminate thread 1 and&lt;br /&gt;
continue executing with others; then it cannot receive signals.&lt;br /&gt;
4 This leads to an obvious solution to the&lt;br /&gt;
interrupted system call problem: Applications that will be inconvenienced&lt;br /&gt;
by interrupted system calls due to signals should dedicate thread 1 to work&lt;br /&gt;
that doesn't make interruptible system calls and use other thread(s) for&lt;br /&gt;
that work. In the worst case, thread 1 can be totally dedicated to waiting&lt;br /&gt;
for signals: It can block on a RAM semaphore that is never released, or it&lt;br /&gt;
can execute a DosSleep loop.&lt;br /&gt;
     A couple of practical details about signals are worth noting. First,&lt;br /&gt;
the user of a high-level language such as C need not worry about saving the&lt;br /&gt;
registers inside the signal-handler routine. The language runtimes&lt;br /&gt;
typically provide code to handle all these details; as far as the&lt;br /&gt;
application program is concerned, the signal handler is asynchronously far&lt;br /&gt;
called, and it can return from the signal by the return() statement. Also,&lt;br /&gt;
no application can receive a signal without first requesting it, so you&lt;br /&gt;
need not worry about setting up signal handlers if your application doesn't&lt;br /&gt;
explicitly ask to use them. A process can have only one signal-handling&lt;br /&gt;
address for each signal, so general-purpose dynlink routines (ones that&lt;br /&gt;
might be called by applications that aren't bundled with the dynlink&lt;br /&gt;
package) should never set a signal handler; doing so might override a&lt;br /&gt;
handler established by the client program code.&lt;br /&gt;
     Signals interact with critical sections in much the same way as&lt;br /&gt;
interrupts do. If a signal arrives while thread 1 is executing a critical&lt;br /&gt;
section that is protected by a semaphore and if that signal handler never&lt;br /&gt;
returns to the interrupted location, the critical section's semaphore will&lt;br /&gt;
be left jammed on. Even if the signal handler eventually returns, deadlock&lt;br /&gt;
occurs if it attempts to enter the critical section during processing of&lt;br /&gt;
the signal (perhaps it called a dynlink package, unaware that the package&lt;br /&gt;
contained a critical section). Dynlink packages must deal with this problem&lt;br /&gt;
by means of the DosHoldSignal call, which is analogous to the CLI/STI&lt;br /&gt;
instructions for hardware interrupts: The DosHoldSignal holds off arriving&lt;br /&gt;
signals until they are released. Held-off signals should be released within&lt;br /&gt;
a second or two so that the user won't be pounding Ctrl-C and thinking that&lt;br /&gt;
the application has crashed. Applications can use DosHoldSignal, or they&lt;br /&gt;
can simply ensure that thread 1 never enters critical sections, perhaps by&lt;br /&gt;
reserving it for signal handling, as discussed above.&lt;br /&gt;
     Ctrl-C and Ctrl-Break are special, device-specific operations. Setting&lt;br /&gt;
a signal handler for these signals is a form of I/O to the keyboard device;&lt;br /&gt;
applications must never do this until they have verified that they have&lt;br /&gt;
been assigned the keyboard device. See Chapter 14, Interactive Programs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==13  The Presentation Manager and VIO==&lt;br /&gt;
&lt;br /&gt;
In the early chapters of this book, I emphasized the importance of a high-&lt;br /&gt;
powered, high-bandwidth graphical user interface. It's a lot of work for an&lt;br /&gt;
application to manage graphical rendition, windowing, menus, and so on, and&lt;br /&gt;
it's hard for the user to learn a completely different interface for each&lt;br /&gt;
application. Therefore, OS/2 contains a subsystem called the presentation&lt;br /&gt;
manager (PM) that provides these services and more. The presentation&lt;br /&gt;
manager is implemented as a dynlink subsystem and daemon process&lt;br /&gt;
combination, and it provides:&lt;br /&gt;
&lt;br /&gt;
* High-performance graphical windowing.&lt;br /&gt;
* A powerful user interface model, including drop-down menus, scroll bars, icons, and mouse and keyboard interfaces. Most of these facilities are optional to the application; it can choose the standard services or &amp;quot;roll its own.&amp;quot;&lt;br /&gt;
* Device independence. The presentation manager contains a sophisticated multilevel device interface so that as much work as possible is pushed down to &amp;quot;smart&amp;quot; graphics cards to optimize performance.&lt;br /&gt;
&lt;br /&gt;
     Interfacing an application with the presentation manager involves a&lt;br /&gt;
degree of effort that not all programmers may want to put forth. The&lt;br /&gt;
interface to the application may be so simple that the presentation&lt;br /&gt;
manager's features are of little value, or the programmer may want to port&lt;br /&gt;
an MS-DOS application to OS/2 with the minimum degree of change. For these&lt;br /&gt;
reasons, OS/2 provides a second interface package called VIO,&lt;br /&gt;
1. VIO is a convenience term that encompasses three dynlink&lt;br /&gt;
subsystems: KBD (keyboard), VIO (Video I/O; the display adapter),&lt;br /&gt;
and MOU (mouse).&lt;br /&gt;
1 which is&lt;br /&gt;
primarily character oriented and looks much like the MS-DOS ROM BIOS video&lt;br /&gt;
interface. The initial release of OS/2 contains only VIO, implemented as a&lt;br /&gt;
separate package. The next release will contain the presentation manager,&lt;br /&gt;
and VIO will then become an alternate interface to the presentation&lt;br /&gt;
manager.&lt;br /&gt;
     Fundamentally, the presentation manager and VIO are the equivalent of&lt;br /&gt;
device drivers. They are implemented as dynlink packages because they are&lt;br /&gt;
device dependent and need to be replaced if different devices are used.&lt;br /&gt;
Dynlinks are used instead of true device drivers because they can provide&lt;br /&gt;
high throughput for the screen device: A simple call is made directly to&lt;br /&gt;
the code that paints the pixels on the screen. Also, the dynlink interface&lt;br /&gt;
allows the presentation manager to be implemented partially as a dynlink&lt;br /&gt;
subsystem and partially as a daemon process accessed by that subsystem.&lt;br /&gt;
     These packages are complex; explaining them in detail is beyond the&lt;br /&gt;
scope of this book. Instead, I will discuss from a general perspective the&lt;br /&gt;
special issues for users of this package.&lt;br /&gt;
     VIO is essentially character oriented. It supports graphics-based&lt;br /&gt;
applications, but only to the extent of allowing them to manipulate the&lt;br /&gt;
display controller directly so that they can &amp;quot;go around&amp;quot; VIO and provide&lt;br /&gt;
special interfaces related to screen switching of graphics applications&lt;br /&gt;
(see below). The base VIO package plays a role similar to that of the ROM&lt;br /&gt;
BIOS INT 10/INT 16 interface used in MS-DOS. It contains some useful&lt;br /&gt;
enhancements but in general is a superset of the ROM BIOS functions, so INT&lt;br /&gt;
10-based real mode applications can be quickly adjusted to use VIO instead.&lt;br /&gt;
VIO is replaceable, in whole or in part, to allow applications being run&lt;br /&gt;
with VIO to be managed later by the presentation manager package.&lt;br /&gt;
     The presentation manager is entirely different from VIO. It offers an&lt;br /&gt;
extremely rich and powerful set of functions that support windowing, and it&lt;br /&gt;
offers a full, device-independent graphics facility. Its message-oriented&lt;br /&gt;
architecture is well suited to interactive applications. Once the&lt;br /&gt;
presentation manager programming model is learned and the key elements of&lt;br /&gt;
its complex interface are understood, a programmer can take advantage of a&lt;br /&gt;
very sexy user interface with comparatively little effort. The presentation&lt;br /&gt;
manager also replaces the existing VIO/KBD/MOU calls in order to support&lt;br /&gt;
older programs that use these interfaces.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.1  Choosing Between PM and VIO&lt;br /&gt;
&lt;br /&gt;
The roles of VIO and the presentation manager sometimes cause confusion:&lt;br /&gt;
Which should you use for your application? The default interface for a new&lt;br /&gt;
application should be the presentation manager. It's not in OS/2 version&lt;br /&gt;
1.0 because of scheduling restrictions; owners of version 1.0 will receive&lt;br /&gt;
the presentation manager as soon as it is available, and all future&lt;br /&gt;
releases will be bundled with the presentation manager. The presentation&lt;br /&gt;
manager will be present on essentially all personal computer OS/2&lt;br /&gt;
installations, so you are not restricting the potential market for an&lt;br /&gt;
application if you write it for the presentation manager.&lt;br /&gt;
     The presentation manager interface allows an application to utilize a&lt;br /&gt;
powerful, graphical user interface. In general form it's standardized for&lt;br /&gt;
ease of use, but it can be customized in a specific implementation so that&lt;br /&gt;
an application can provide important value-added features. On the other&lt;br /&gt;
hand, if you are porting an application from the real mode environment, you&lt;br /&gt;
will find it easier to use the VIO interface. Naturally, such programs run&lt;br /&gt;
well under the presentation manager, but they forgo the ability to use&lt;br /&gt;
graphics and to interact with the presentation manager. The user can still&lt;br /&gt;
&amp;quot;window&amp;quot; the VIO application's screen image, but without the application's&lt;br /&gt;
knowledge or cooperation. To summarize, you have three choices when writing&lt;br /&gt;
an application:&lt;br /&gt;
&lt;br /&gt;
     1.  Only use the VIO interface in character mode. This works well in a&lt;br /&gt;
         presentation manager environment and is a good choice for ported&lt;br /&gt;
         real mode applications. The VIO interface is also supported by the&lt;br /&gt;
         Family API mechanism. This mode is compatible with the Family API&lt;br /&gt;
         facility.&lt;br /&gt;
&lt;br /&gt;
     2.  Use the special VIO interface facilities to sidestep VIO and&lt;br /&gt;
         directly manipulate the display screen in either character or &lt;br /&gt;
         graphics mode. This also works in a presentation manager&lt;br /&gt;
         environment, but the application will not be able to run in a &lt;br /&gt;
         window. This approach can be compatible with the Family API if it &lt;br /&gt;
         is carefully implemented.&lt;br /&gt;
&lt;br /&gt;
     3.  Use the presentation manager interface--the most sophisticated&lt;br /&gt;
         interface for the least effort. The presentation manager interface&lt;br /&gt;
         provides a way to &amp;quot;operate&amp;quot; applications that will become a widely&lt;br /&gt;
         known user standard because of the capabilities of the interface,&lt;br /&gt;
         because of the support it receives from key software vendors, and&lt;br /&gt;
         because it's bundled with OS/2. The user is obviously at an&lt;br /&gt;
         advantage if he or she does not have to spend time learning a new&lt;br /&gt;
         interface and operational metaphors to use your application.&lt;br /&gt;
         Finally, Microsoft is a strong believer in the power of a&lt;br /&gt;
         graphical user interface; future releases of OS/2 will contain&lt;br /&gt;
         &amp;quot;more-faster-better&amp;quot; presentation manager features. Many of these&lt;br /&gt;
         improvements will apply to existing presentation manager&lt;br /&gt;
         applications; others will expand the interface API. The standard&lt;br /&gt;
         of performance for application interfaces, as well as for&lt;br /&gt;
         application performance, continues to evolve. The rudimentary&lt;br /&gt;
         interfaces and function of the first-generation PC software are no&lt;br /&gt;
         longer considered competitive. Although OS/2 can do nothing to&lt;br /&gt;
         alleviate the developer's burden of keeping an application's&lt;br /&gt;
         function competitive, the presentation manager is a great help in&lt;br /&gt;
         keeping the application's interface state of the art.&lt;br /&gt;
&lt;br /&gt;
     Clearly, using the presentation manager interface is the best&lt;br /&gt;
strategy for new or extensively reworked applications. The presentation&lt;br /&gt;
manager API will be expanded and improved; the INT 10-like VIO functions&lt;br /&gt;
and the VIO direct screen access capabilities will be supported for the&lt;br /&gt;
foreseeable future, but they're an evolutionary dead end. Given that, you&lt;br /&gt;
may want to use the VIO mechanism or the Family API facilities to quickly&lt;br /&gt;
port an application from a real mode version and then use the presentation&lt;br /&gt;
manager in a product upgrade release.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.2  Background I/O&lt;br /&gt;
&lt;br /&gt;
A process is in the background when it is no longer interacting directly&lt;br /&gt;
with the user. In a presentation manager environment, this means that none&lt;br /&gt;
of the process's windows are the keyboard focus. The windows themselves may&lt;br /&gt;
still be visible, or they may be obscured or iconic. In a VIO environment,&lt;br /&gt;
a process is in the background when the user has selected another screen&lt;br /&gt;
group. In this case, the application's screen display is not visible.&lt;br /&gt;
     A presentation manager application can easily continue to update its&lt;br /&gt;
window displays when it is in the background; the application can continue&lt;br /&gt;
to call the presentation manager to change its window contents in any way&lt;br /&gt;
it wishes. A presentation manager application can arrange to be informed&lt;br /&gt;
when it enters and leaves the background (actually, receives and loses the&lt;br /&gt;
keyboard focus), or it can simply carry on with its work, oblivious to the&lt;br /&gt;
issue. Background I/O can continue regardless of whether I/O form is&lt;br /&gt;
character or graphics based.&lt;br /&gt;
     VIO applications can continue to do I/O in background mode as well.&lt;br /&gt;
The VIO package maintains a logical video buffer for each screen group;&lt;br /&gt;
when VIO calls are made to update the display of a screen group that is in&lt;br /&gt;
the background, VIO makes the requested changes to the logical video&lt;br /&gt;
buffer. When the screen group is restored to the foreground, the updated&lt;br /&gt;
contents of the logical video buffer are copied to the display's physical&lt;br /&gt;
video buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.3  Graphics Under VIO&lt;br /&gt;
&lt;br /&gt;
VIO is a character-oriented package and provides character mode&lt;br /&gt;
applications with a variety of services. As we have just seen, when a&lt;br /&gt;
screen switch takes place, VIO automatically handles saving the old screen&lt;br /&gt;
image and restoring the new. VIO does provide a mechanism to allow an&lt;br /&gt;
application to sidestep VIO and directly manipulate the physical video&lt;br /&gt;
buffer, where it is then free to use any graphical capability of the&lt;br /&gt;
hardware. There are two major disadvantages to sidestepping VIO for&lt;br /&gt;
graphics rather than using the presentation manager services:&lt;br /&gt;
&lt;br /&gt;
     1.  The application is device dependent because it must manipulate the&lt;br /&gt;
         video display hardware directly.&lt;br /&gt;
&lt;br /&gt;
     2.  VIO can no longer save or restore the state of the physical video&lt;br /&gt;
         buffer during screen switch operations. The application must use a&lt;br /&gt;
         special VIO interface to provide these functions itself.&lt;br /&gt;
&lt;br /&gt;
     The following discussion applies only to applications that want to&lt;br /&gt;
sidestep the presentation manager and VIO interfaces and interact directly&lt;br /&gt;
with the display hardware.&lt;br /&gt;
     Gaining access to the video hardware is easy; the VIO call VioGetBuf&lt;br /&gt;
provides a selector to the video buffer and also gives the application's&lt;br /&gt;
ring 2 code segments, if any, permission to program the video controller's&lt;br /&gt;
registers. The complication arises from the screen-switching capabilities&lt;br /&gt;
of OS/2. When the user switches the application into a background screen&lt;br /&gt;
group, the contents of the video memory belong to someone else; the&lt;br /&gt;
application's video memory is stored somewhere in RAM. It is disastrous&lt;br /&gt;
when an application doesn't pay attention to this process and accidentally&lt;br /&gt;
updates the video RAM or the video controller while they are assigned to&lt;br /&gt;
another screen group.&lt;br /&gt;
     Two important issues are connected with screen switching: (1) How does&lt;br /&gt;
an application find out that it's in background mode? (2) Who saves and&lt;br /&gt;
restores its screen image and where? The VioScrLock call handles screen&lt;br /&gt;
access. Before every access to the display memory or the display&lt;br /&gt;
controller, an application must first issue the VioScrLock call. While the&lt;br /&gt;
call is in effect, OS/2 cannot perform any screen switches. Naturally, the&lt;br /&gt;
application must do its work and quickly release the screen switch lockout.&lt;br /&gt;
Failure to release the lock in a timely fashion has the effect of hanging&lt;br /&gt;
the system, not only for a user's explicit screen switch commands, but also&lt;br /&gt;
for other facilities that use the screen switch mechanism, such as the hard&lt;br /&gt;
error handler. Hard errors can't be presented to the user while the screen&lt;br /&gt;
lock is in effect. If OS/2 needs to switch screens, and an aberrant&lt;br /&gt;
application has the screen lock set, OS/2 will cancel the lock and perform&lt;br /&gt;
the screen switch after a period (currently 30 seconds). This is still a&lt;br /&gt;
disaster scenario, although a mollified one, because the application that&lt;br /&gt;
was summarily &amp;quot;delocked&amp;quot; will probably end up with a trashed screen image.&lt;br /&gt;
The screen lock and unlock calls execute relatively rapidly, so they can be&lt;br /&gt;
called frequently to protect only the actual write-to-screen operation,&lt;br /&gt;
leaving the screen unlocked during computation. Basically, an application&lt;br /&gt;
should use VioScrLock to protect a block of I/O that can be written, in its&lt;br /&gt;
entirety, without significant recomputation. Examples of such blocks are a&lt;br /&gt;
screen scroll, a screen erase, and a write to a cell in a spreadsheet&lt;br /&gt;
program.&lt;br /&gt;
     VioScrLock must be used to protect code sequences that program the&lt;br /&gt;
display hardware as well as code sequences that write to video memory. Some&lt;br /&gt;
peripheral programming sequences are noninterruptible. For example, a two-&lt;br /&gt;
step programming sequence in which the first I/O write selects a&lt;br /&gt;
multiplexed register and the second write modifies that register is&lt;br /&gt;
uninterruptible because the first write placed the peripheral device into a&lt;br /&gt;
special state. Such sequences must be protected within one lock/unlock&lt;br /&gt;
pair.&lt;br /&gt;
     Sometimes when an application calls VioScrLock, it receives a special&lt;br /&gt;
error code that says, &amp;quot;The screen is unavailable.&amp;quot; This means that the&lt;br /&gt;
screen has been switched into the background and that the application may&lt;br /&gt;
not--and must not--manipulate the display hardware. Typically, the program&lt;br /&gt;
issues a blocking form of VioScrLock that suspends the thread until the&lt;br /&gt;
screen is again in the foreground and the video display buffers contain&lt;br /&gt;
that process's image.&lt;br /&gt;
     An application that directly manipulates the video hardware must do&lt;br /&gt;
more than simply lay low when it is in the background. It must also save&lt;br /&gt;
and restore the entire video state--the contents of the display buffer and&lt;br /&gt;
the modes, palates, cursors, and so on of the display controller. VIO does&lt;br /&gt;
not provide this service to direct-screen manipulation processes for two&lt;br /&gt;
reasons. First, the process is very likely using the display in a graphics&lt;br /&gt;
mode. Some display cards contain a vast amount of video memory, and VIO&lt;br /&gt;
would be forced to save it all just in case the application was using it&lt;br /&gt;
all. Second, many popular display controllers such as the EGA and&lt;br /&gt;
compatibles contain many write-only control registers. This means that VIO&lt;br /&gt;
cannot read the controller state back from the card in order to save it for&lt;br /&gt;
a later restoration. The only entity that understands the state of the&lt;br /&gt;
card, and therefore the only entity that can restore that state, is the&lt;br /&gt;
code that programmed it--the application itself.&lt;br /&gt;
     But how does the system notify the process when it's time to save or&lt;br /&gt;
restore? Processes can call the system in many ways, but the system can't&lt;br /&gt;
call processes. OS/2 deals with this situation by inverting the usual&lt;br /&gt;
meaning of call and return. When a process first decides to refresh its own&lt;br /&gt;
screen, it creates an extra thread and uses that thread to call the&lt;br /&gt;
VioSavRedrawWait function. The thread doesn't return from this call right&lt;br /&gt;
away; instead, VIO holds the thread &amp;quot;captive&amp;quot; until it's time for a screen&lt;br /&gt;
switch. To notify the process that it must now save its screen image, VIO&lt;br /&gt;
allows the captive thread to return from the VioSavRedrawWait call. The&lt;br /&gt;
process then saves the display state and screen contents, typically using&lt;br /&gt;
the returned thread. When the save operation is complete, VioSavRedrawWait&lt;br /&gt;
is called again. This notifies VIO that the save is complete and that the&lt;br /&gt;
screen can now be switched; it also resets the cycle so that the process&lt;br /&gt;
can again be notified when it's time to restore its saved screen image. In&lt;br /&gt;
effect, this mechanism makes the return from VioSavRedrawWait analogous to&lt;br /&gt;
a system-to-process call, and it makes the later call to VioSavRedrawWait&lt;br /&gt;
analogous to a return from process to system.&lt;br /&gt;
     The design of OS/2 generally avoids features in which the system calls&lt;br /&gt;
a process to help a system activity such as screen switching. This is&lt;br /&gt;
because a tenet of the OS/2 design religion is that an aberrant process&lt;br /&gt;
should not be able to crash the system. Clearly, we're vulnerable to that&lt;br /&gt;
in this case. VIO postpones the screen switch until the process saves its&lt;br /&gt;
screen image, but what if the process somehow hangs up and doesn't complete&lt;br /&gt;
the save? The screen is in an indeterminate state, and no process can use&lt;br /&gt;
the screen and keyboard. As far as the user is concerned, the system has&lt;br /&gt;
crashed. True, other processes in the system are alive and well, but if the&lt;br /&gt;
user can't get to them, even to save his or her work, their continued&lt;br /&gt;
health is of little comfort.&lt;br /&gt;
     The designers of OS/2 were stuck here, between a rock and a hard&lt;br /&gt;
place: Applications had to be able to save their screen image if they were&lt;br /&gt;
to have direct video access, but such a facility violated the &amp;quot;no crashing&amp;quot;&lt;br /&gt;
tenet of the design religion. Because the video access had to be supported&lt;br /&gt;
and the system had to be crash resistant, we found a two-part workaround.&lt;br /&gt;
     The first part concerns the most common cause of a process hanging up&lt;br /&gt;
in its screen-save operation: hard errors. When a hard error occurs, the&lt;br /&gt;
hard error daemon uses the screen switch mechanism to take control of the&lt;br /&gt;
screen and the keyboard. The hard error daemon saves the existing screen&lt;br /&gt;
image and keeps the application that was in the foreground at the time of&lt;br /&gt;
the hard error from fighting with the daemon over control of the screen and&lt;br /&gt;
the keyboard. However, if the hard error daemon uses the screen-switching&lt;br /&gt;
mechanism and if the screen-switching mechanism allows the foreground&lt;br /&gt;
process to save its own screen image, that process might, while saving its&lt;br /&gt;
screen image, try to use the device that has the hard error and thus&lt;br /&gt;
deadlock the system. The device in error won't service more requests until&lt;br /&gt;
the hard error is cleared, but the hard error can't be cleared until the&lt;br /&gt;
daemon takes control. The daemon can't take control until the foreground&lt;br /&gt;
process is through saving, and the foreground process can't complete saving&lt;br /&gt;
until the device services its request. Note that this deadlock doesn't&lt;br /&gt;
require an explicit I/O operation on the part of the foreground process;&lt;br /&gt;
simply allocating memory or referencing a segment might cause swapping or&lt;br /&gt;
loading activity on the device that is experiencing the hard error.&lt;br /&gt;
     A two-part approach is used to solve this problem. First, deadlocks&lt;br /&gt;
involving the hard error daemon are managed by having the hard error screen&lt;br /&gt;
switch do a partial screen save. When I said earlier that VIO would not&lt;br /&gt;
save the video memory of direct access screen groups, I was lying a bit.&lt;br /&gt;
When the system is doing a hard error screen switch, VIO will save the&lt;br /&gt;
first part (typically 4 KB) of the video memory--enough to display a page&lt;br /&gt;
of text. We don't have to worry about how much video RAM the application&lt;br /&gt;
was using because the video display will be switched to character mode and&lt;br /&gt;
the hard error daemon will overwrite only a small part of video memory.&lt;br /&gt;
Naturally, this means that the hard error daemon must always restore the&lt;br /&gt;
original screen group; it can't switch to a third screen group because the&lt;br /&gt;
first one's video memory wasn't fully saved.&lt;br /&gt;
     VIO and the hard error daemon keep enough free RAM around to save this&lt;br /&gt;
piece of the video memory so that a hard error screen switch can always&lt;br /&gt;
take place without the need for memory swapping. When the hard error daemon&lt;br /&gt;
is finished with the screen, the overwritten video memory is restored from&lt;br /&gt;
the buffer. As we discussed above, however, VIO can't restore the state of&lt;br /&gt;
the video controller itself; only the application can do that. The&lt;br /&gt;
VioModeWait function is used to notify the application that it must restore&lt;br /&gt;
the screen state.&lt;br /&gt;
     In summary, any application that directly accesses the video hardware&lt;br /&gt;
must provide captive threads to VioSavRedrawWait and to VioModeWait.&lt;br /&gt;
VioSavRedrawWait will return when the application is to save or to restore&lt;br /&gt;
the video memory. VioModeWait will return when the application is to&lt;br /&gt;
restore the state of the video controller from the application's own record&lt;br /&gt;
of the controller's state.&lt;br /&gt;
     The second part of the &amp;quot;application hangs while saving screen and&lt;br /&gt;
hangs system&amp;quot; solution is unfortunately ad hoc: If the application does not&lt;br /&gt;
complete its screen save operation within approximately 30 seconds, the&lt;br /&gt;
system considers it hung and switches the screen anyway. The hung process&lt;br /&gt;
is suspended while it's in background so that it won't suddenly &amp;quot;come&lt;br /&gt;
alive&amp;quot; and manipulate the screen. When the process is again in the&lt;br /&gt;
foreground, the system unsuspends it and hopes that it will straighten&lt;br /&gt;
itself out. In such a case, the application's screen image may be trashed.&lt;br /&gt;
At best, the user can enter a &amp;quot;repaint screen&amp;quot; command to the application&lt;br /&gt;
and all will be well; at worst, the application is hung up, but the system&lt;br /&gt;
itself is alive and well. Building a system that can detect and correct&lt;br /&gt;
errors on the part of an application is impossible; the best that we can&lt;br /&gt;
hope to do is to keep an aberrant application from damaging the rest of the&lt;br /&gt;
system.&lt;br /&gt;
     I hope that this long and involved discussion of rules, regulations,&lt;br /&gt;
doom, and disaster has not frightened you into contemplating programs that&lt;br /&gt;
communicate by Morse code. You need be concerned with these issues only if&lt;br /&gt;
you write applications that manipulate the display device directly,&lt;br /&gt;
circumventing either VIO or the presentation manager interfaces. These&lt;br /&gt;
concerns are not an issue when you are writing ordinary text applications&lt;br /&gt;
that use VIO or the presentation manager or graphics applications that use&lt;br /&gt;
the presentation manager. VIO and the presentation manager handle screen&lt;br /&gt;
saving and support background display writing. Finally, I'll point out, as&lt;br /&gt;
a curiosity, that even processes that use handle operations only to write&lt;br /&gt;
to STDOUT use VIO or the presentation manager. When STDOUT points to the&lt;br /&gt;
screen device, the operating system routes STDOUT writes to the&lt;br /&gt;
VIO/presentation manager packages. This is, of course, invisible to the&lt;br /&gt;
application; it need not concern itself with foreground/background, EGA&lt;br /&gt;
screen modes, hard error screen restorations, and the like.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==14  Interactive Programs==&lt;br /&gt;
&lt;br /&gt;
A great many applications interact with the user via the screen and the&lt;br /&gt;
keyboard. Because the primary function of most desktop computers is to run&lt;br /&gt;
interactive applications, OS/2 contains a variety of services that make&lt;br /&gt;
interaction powerful and efficient.&lt;br /&gt;
     As we've seen in earlier chapters, interactive programs can use the&lt;br /&gt;
presentation manager to manage their interface, or they can do it&lt;br /&gt;
themselves, using VIO or direct video access for their I/O. The&lt;br /&gt;
presentation manager provides a great deal of function and automatically&lt;br /&gt;
solves a great many problems. For example, a presentation manager&lt;br /&gt;
application doesn't have to concern itself with the sharing of the single&lt;br /&gt;
keyboard among all processes in its screen group. The presentation manager&lt;br /&gt;
takes care of that by handling the keyboard and by simply sending keyboard&lt;br /&gt;
events to each process, as appropriate.&lt;br /&gt;
     If you're writing an application that uses the presentation manager,&lt;br /&gt;
then you can skip this chapter. If you're writing an application that does&lt;br /&gt;
not use the presentation manager but that may be used in an interactive&lt;br /&gt;
fashion, it's very important that you understand the issues discussed in&lt;br /&gt;
this chapter. They apply to all programs that use VIO or the STDIN/STDOUT&lt;br /&gt;
handles to do interactive I/O, even if such programs are being run via the&lt;br /&gt;
presentation manager.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
14.1  I/O Architecture&lt;br /&gt;
&lt;br /&gt;
Simply put, the system I/O architecture says that all programs read their&lt;br /&gt;
main input from the STDIN handle and write their main output to the STDOUT&lt;br /&gt;
handle. This applies to all non-presentation manager applications, but&lt;br /&gt;
especially to interactive applications. The reason is that OS/2 and&lt;br /&gt;
program-execution utilities such as CMD.EXE (shell programs) cooperate to&lt;br /&gt;
use the STDIN/STDOUT mechanism to control access to the screen and&lt;br /&gt;
keyboard. For example, if two processes read from the keyboard at the same&lt;br /&gt;
time, some keys go to one process, and the rest go to the other in an&lt;br /&gt;
unpredictable fashion. Likewise, it is a bad idea for more than one process&lt;br /&gt;
to write to the screen at the same time.&lt;br /&gt;
1. Within the same screen group and/or window, of course.&lt;br /&gt;
Applications that use different virtual screens can each write to&lt;br /&gt;
their own screen without regard for other virtual screens.&lt;br /&gt;
1 Clearly, you don't want too many&lt;br /&gt;
processes doing keyboard/screen I/O within a single screen group, but you&lt;br /&gt;
also don't want too few. It would be embarrassing if a user terminated one&lt;br /&gt;
interactive application in a screen group, such as a program run from&lt;br /&gt;
CMD.EXE, and CMD.EXE failed to resume use of the keyboard/screen to print a&lt;br /&gt;
prompt.&lt;br /&gt;
     So how will we handle this? We might be running a great many processes&lt;br /&gt;
in a screen group. For example, you could use CMD.EXE to execute a&lt;br /&gt;
spreadsheet program, which was told to execute a subshell--another copy of&lt;br /&gt;
CMD.EXE. The user could then execute a program to interpret a special batch&lt;br /&gt;
script, which in turn executes an editor. And this editor was told to run a&lt;br /&gt;
copy of a C compiler to scan the source being edited for errors. Oh, yes,&lt;br /&gt;
and we forgot to mention that the top level CMD.EXE was told to run a copy&lt;br /&gt;
of the assembler in parallel with all these other operations (similar to&lt;br /&gt;
the UNIX &amp;quot;&amp;amp;&amp;quot; operation).&lt;br /&gt;
     Many processes are running in this screen group; some of them are&lt;br /&gt;
interactive, and some are not, and at any time only one is using the&lt;br /&gt;
keyboard and the screen. Although it would be handy to declare that the&lt;br /&gt;
most recently executed process will use the keyboard and the screen, you&lt;br /&gt;
can't: The most recently executed program was the C compiler, and it's not&lt;br /&gt;
even interactive. OS/2 cannot decide which process should be using the&lt;br /&gt;
screen and keyboard because OS/2 lacks any knowledge of the function of&lt;br /&gt;
each process. OS/2 knows only their child-parent relationships, and the&lt;br /&gt;
situation can be far too complex for that information to be sufficient.&lt;br /&gt;
     Because OS/2 can't determine which process should be using the screen&lt;br /&gt;
and the keyboard, it doesn't try. The processes themselves make the&lt;br /&gt;
determination. The rule is simple: The process that is currently using the&lt;br /&gt;
screen and the keyboard can grant access to a child process, or it can keep&lt;br /&gt;
access for itself. If a process grants access to a child process, then it&lt;br /&gt;
must keep off the screen and the keyboard until that child terminates. Once&lt;br /&gt;
the child process is granted use of the screen and the keyboard, the child&lt;br /&gt;
process is free to do as it wishes, perhaps granting access to its own&lt;br /&gt;
children. Until that child process terminates, the parent must avoid device&lt;br /&gt;
conflict by staying quiet.&lt;br /&gt;
     Let's look at how this works in real life. For example, CMD.EXE, the&lt;br /&gt;
first process in the screen group, starts up with STDIN open on the&lt;br /&gt;
keyboard and STDOUT open on the screen. (The system did this by magic.)&lt;br /&gt;
When this copy of CMD.EXE is told to execute the spreadsheet program,&lt;br /&gt;
CMD.EXE doesn't know if the spreadsheet program is interactive or not, so&lt;br /&gt;
it lets the child process--the spreadsheet program--inherit its STDIN and&lt;br /&gt;
STDOUT handles, which point to the keyboard and to the screen. Because&lt;br /&gt;
CMD.EXE granted access to the screen and the keyboard to the child, CMD.EXE&lt;br /&gt;
can't use STDIN or STDOUT until that child process terminates. Typically,&lt;br /&gt;
at this point CMD.EXE would DosCWait on its child process.&lt;br /&gt;
     Now the spreadsheet program comes alive. It writes to STDOUT, which is&lt;br /&gt;
the screen, and it reads from STDIN, which is the keyboard. When the&lt;br /&gt;
spreadsheet program is instructed to run CMD.EXE, it does so, presuming, as&lt;br /&gt;
did its parent, that CMD.EXE is interactive and therefore letting CMD.EXE&lt;br /&gt;
inherit its STDIN and STDOUT handles. Now the spreadsheet must avoid any&lt;br /&gt;
STDIN/STDOUT I/O until its child--CMD.EXE--terminates. As long as these&lt;br /&gt;
processes continue to run interactive children, things are going to work&lt;br /&gt;
out OK. When the children start to die and execution starts popping back up&lt;br /&gt;
the tree, applications restart, using the screen and the keyboard in the&lt;br /&gt;
proper order.&lt;br /&gt;
     But what about the detached assembly that CMD.EXE started before it&lt;br /&gt;
ran the spreadsheet? In this case, the user has explicitly told CMD.EXE&lt;br /&gt;
that it wants the application run &amp;quot;detached&amp;quot; from the keyboard. If the user&lt;br /&gt;
specified a STDIN for the assembler--perhaps a file--then CMD.EXE sets that&lt;br /&gt;
up for the child's STDIN. If the user didn't specify an alternate STDIN,&lt;br /&gt;
CMD.EXE opens STDIN on the  NULL device so that an application that reads&lt;br /&gt;
it will receive EOF. In this way, CMD.EXE (which knew that the application&lt;br /&gt;
wasn't to use the keyboard because the user gave explicit instructions) did&lt;br /&gt;
not let the child process inherit STDIN, so CMD.EXE continues to use it,&lt;br /&gt;
printing a new prompt and reading a new command. Figure 14-1 shows a&lt;br /&gt;
typical process tree. The shaded processes have inherited a STDIN, which&lt;br /&gt;
points to the keyboard, and a STDOUT, which points to the screen. All such&lt;br /&gt;
processes must lie on a single path if the rules are followed because each&lt;br /&gt;
process has the option of allowing a maximum of one child to inherit its&lt;br /&gt;
STDIN and STDOUT handles unchanged.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ÀÄÄÂÄÄÄÄÄÄÄÄÂÄÄÙ                 ÚÄÄÄÄ¿    Processes&lt;br /&gt;
             ÚÄÄÄÄÄÄÙ        ÀÄÄÄÄÄÄ¿             ³°°°°³  = using the&lt;br /&gt;
             ³                      ³             ÀÄÄÄÄÙ    keyboard &lt;br /&gt;
         ÚÄÄÄÁÄÄ¿                ÚÄÄÁÄÄÄ¿&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ÀÄÂÄÄÄÄÙ                ÀÄÂÄÄÂÄÙ&lt;br /&gt;
        ÚÄÄÙ                    ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
    ÚÄÄÄÁÄÄ¿                 ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
    ³      ³                 ³°°°°³    ³    ³&lt;br /&gt;
    ³      ³                 ÀÂÄÄÂÙ    ÀÄÄÄÂÙ&lt;br /&gt;
    ÀÄÂÄÄÂÄÙ               ÚÄÄÙ  ÀÄÄ¿      ÀÄÄ¿&lt;br /&gt;
   ÚÄÄÙ  ÀÄÄ¿           ÚÄÄÁÄ¿    ÚÄÁÄÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
ÚÄÄÁÄ¿    ÚÄÁÄÄ¿        ³°°°°³    ³    ³    ³    ³&lt;br /&gt;
³    ³    ³    ³        ÀÂÄÄÂÙ    ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÙ    ÀÄÄÄÄÙ      ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
                   ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
                   ³    ³    ³°°°°³&lt;br /&gt;
                   ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 14-1.  Processes using the keyboard.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     You are undoubtedly becoming a bit concerned at this point: &amp;quot;Does this&lt;br /&gt;
mean I'm forced to use the limited, serial STDIN/STDOUT interface for my&lt;br /&gt;
high-resolution graphics output?&amp;quot; I'm glad you asked. What we've been&lt;br /&gt;
discussing is the architectural model that must be followed because it's&lt;br /&gt;
used systemwide to avoid screen and keyboard conflicts. However,&lt;br /&gt;
applications can and should use special services to optimize their&lt;br /&gt;
interactive I/O as long as they do so according to the architectural model.&lt;br /&gt;
Specifically, OS/2 provides the KBD, VIO, and MOU dynlink packages. These&lt;br /&gt;
high-performance programs interface directly with the hardware, avoiding&lt;br /&gt;
the STDIN/STDOUT limited interfaces. The key is &amp;quot;directly with the&lt;br /&gt;
hardware&amp;quot;: A process is welcome to use hardware-specific interfaces&lt;br /&gt;
to optimize performance, but only after it has ensured that the&lt;br /&gt;
architectural model grants it access to that device.&lt;br /&gt;
     In practice, this is straightforward. Any interactive program that&lt;br /&gt;
wants to use KBD first ensures (via DosQHandType) that its STDIN handle is&lt;br /&gt;
open on the keyboard. If STDIN is not open on the keyboard, the keyboard&lt;br /&gt;
belongs to another program, and the interactive program must not burst in&lt;br /&gt;
on the rightful owner by using KBD. All dynlink device interfaces are&lt;br /&gt;
trusting souls and won't check your bona fides before they do their stuff,&lt;br /&gt;
so the application must look before it leaps. The same applies to STDOUT,&lt;br /&gt;
to the keyboard device, and to the VIO package. All applications must&lt;br /&gt;
verify that STDIN and STDOUT point to the keyboard and the screen before&lt;br /&gt;
they use any device-direct interface, which includes VIO, KBD, MOU, and&lt;br /&gt;
direct device access.&lt;br /&gt;
     What's an interactive program to do if it finds that STDIN or STDOUT&lt;br /&gt;
doesn't point to the keyboard and screen devices? I don't know, but the&lt;br /&gt;
author of the application does. Some applications might not be truly&lt;br /&gt;
interactive and therefore would work fine. For example, Microsoft Macro&lt;br /&gt;
Assembler (MASM) can prompt the user for the names of source, object, and&lt;br /&gt;
listing files. Although MASM is technically interacting with the user, MASM&lt;br /&gt;
is not an interactive application because it doesn't depend on the ability&lt;br /&gt;
to interact to do its work. If STDIN points to a file, MASM is perfectly&lt;br /&gt;
happy reading the filenames from that file. MASM doesn't need to see if&lt;br /&gt;
STDIN points to the keyboard because MASM doesn't need to use the KBD&lt;br /&gt;
package. Instead, MASM reads its names from STDIN and takes what it&lt;br /&gt;
gets.&lt;br /&gt;
     Other programs may not require an interactive interface, but when they&lt;br /&gt;
are interacting, they may want to use KBD or VIO to improve performance.&lt;br /&gt;
Such applications should test STDIN and STDOUT to see if they point to&lt;br /&gt;
the appropriate devices. If they do, applications can circumvent the&lt;br /&gt;
STDIN/STDOUT limitations and use KBD and VIO. If they don't, the&lt;br /&gt;
applications are stuck with STDIN and STDOUT. Finally, many interactive&lt;br /&gt;
applications make no sense at all in a noninteractive environment. These&lt;br /&gt;
applications need to check STDIN and STDOUT, and, if they don't point to&lt;br /&gt;
the devices, the applications should write an error message to STDERR and&lt;br /&gt;
terminate. Admittedly, the user is in error if he or she attempts to run an&lt;br /&gt;
interactive application, such as a WYSIWYG editor, detached, but printing&lt;br /&gt;
an error message is far better than trashing the display screen and&lt;br /&gt;
fighting with CMD.EXE over the keyboard. The screen group would then be&lt;br /&gt;
totally unusable, and the user might not even be able to terminate the&lt;br /&gt;
editor if he or she can't get the terminate command through the keyboard&lt;br /&gt;
contention.&lt;br /&gt;
     It's technically possible, although highly unusual, for an application&lt;br /&gt;
to inherit access to the keyboard yet not have access to the screen. More&lt;br /&gt;
commonly, an application has access to the screen but not to the keyboard.&lt;br /&gt;
Although most users would find it confusing, power users can detach&lt;br /&gt;
programs such as compilers so that any output summary or error messages&lt;br /&gt;
they produce appear on the screen. Although the user may end up with&lt;br /&gt;
intermingled output, he or she may like the instant notification. Each&lt;br /&gt;
application that wants to use VIO, KBD, or the environment manager needs to&lt;br /&gt;
check STDIN and STDOUT individually for access to the appropriate device.&lt;br /&gt;
     Earlier in this section, we talked about how applications work when&lt;br /&gt;
they create children that inherit the screen and the keyboard, and it&lt;br /&gt;
probably sounded complicated. In practice, it can be simple. For example,&lt;br /&gt;
the technique used to DosExecPgm a child that will inherit the keyboard can&lt;br /&gt;
be used when the parent itself doesn't have the keyboard and thus can't&lt;br /&gt;
bequeath it. Therefore, the parent doesn't need to check its STDIN status&lt;br /&gt;
during the DosExecPgm. To summarize, here are the rules:&lt;br /&gt;
&lt;br /&gt;
     Executing Programs&lt;br /&gt;
&lt;br /&gt;
     þ  If the child process is to inherit the STDIN handle, the parent&lt;br /&gt;
        process must not access that handle any further until the child&lt;br /&gt;
        process terminates.&lt;br /&gt;
&lt;br /&gt;
     þ  If the child process is not to inherit the STDIN handle (so that&lt;br /&gt;
        your program can continue to interact), then the child process&lt;br /&gt;
        STDIN must be opened on a file or on the NULL device. Don't rely on&lt;br /&gt;
        the child not to use the handle; the child might DosExecPgm a&lt;br /&gt;
        grandchild that is not so well mannered.&lt;br /&gt;
&lt;br /&gt;
     þ  A process can let only one child at a time inherit its STDIN. If a&lt;br /&gt;
        process is going to run multiple child processes in parallel, only&lt;br /&gt;
        one can inherit STDIN; the others must use alternative STDIN&lt;br /&gt;
        sources.&lt;br /&gt;
&lt;br /&gt;
     þ  All these rules apply to STDINs open on pipes and files as well as&lt;br /&gt;
        to KBD, so your application needn't check the source of STDIN.&lt;br /&gt;
&lt;br /&gt;
     All Processes&lt;br /&gt;
&lt;br /&gt;
     þ  Verify that the STDIN handle points to the keyboard before using&lt;br /&gt;
        KBD, SIGBRK, or SIGCTLC (see below). You must not use these direct&lt;br /&gt;
        device facilities if STDIN is not open on the keyboard.&lt;br /&gt;
&lt;br /&gt;
     þ  Verify that the STDOUT handle points to the screen before using&lt;br /&gt;
        VIO. You must not use direct device facilities if STDOUT is not&lt;br /&gt;
        open on the screen.&lt;br /&gt;
&lt;br /&gt;
     þ  If the process executes any child that inherits its STDIN, it must&lt;br /&gt;
        not terminate itself until that child process terminates. This is&lt;br /&gt;
        because the parent will assume that the termination of the direct&lt;br /&gt;
        child means that the STDIN handle is now available.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
14.2  Ctrl-C and Ctrl-Break Handling&lt;br /&gt;
&lt;br /&gt;
Just when you think that it's safe to go back into the operating system,&lt;br /&gt;
one more device and process tree issue needs to be discussed: the handling&lt;br /&gt;
of Ctrl-C and Ctrl-Break. (Once again, this discussion applies only to&lt;br /&gt;
programs that don't explicitly use the presentation manager facility. Those&lt;br /&gt;
applications that do use the presentation manager have all these issues&lt;br /&gt;
handled for them automatically.) These two events are tied to the keyboard&lt;br /&gt;
hardware, so their routing has a great deal in common with the above&lt;br /&gt;
discussion. The fundamental problem is simple: When the user presses Ctrl-C&lt;br /&gt;
or Ctrl-Break, what's the operating system to do? Clearly, a process or&lt;br /&gt;
processes or perhaps an entire subtree of processes must be killed or&lt;br /&gt;
signaled. But do we kill or signal? And which one(s)?&lt;br /&gt;
     OS/2 defines a convention that allows the processes themselves to&lt;br /&gt;
decide. Consider a type of application--a &amp;quot;command application&amp;quot;--that runs&lt;br /&gt;
in command mode. In command mode, a command application reads a command,&lt;br /&gt;
executes the command, and then typically returns to command mode.&lt;br /&gt;
Furthermore, when the user presses Ctrl-Break, the command application&lt;br /&gt;
doesn't want to terminate but to stop what it's doing and return to command&lt;br /&gt;
mode. This is the style of most interactive applications but not that of&lt;br /&gt;
most noninteractive applications. For example, if the user types MASM to&lt;br /&gt;
CMD.EXE, the CMD.EXE program runs MASM as a child process. CMD.EXE is a&lt;br /&gt;
command application, but MASM is not. The distinction between &amp;quot;command&lt;br /&gt;
application&amp;quot; and &amp;quot;noncommand application&amp;quot; is not made by OS/2 but is merely&lt;br /&gt;
descriptive terminology that is useful in this discussion.&lt;br /&gt;
     The system convention is that Ctrl-C and Ctrl-Break mean &amp;quot;Stop what&lt;br /&gt;
you're doing.&amp;quot; OS/2 generates signals in response to Ctrl-C and Ctrl-Break;&lt;br /&gt;
it never directly kills a process. OS/2 can easily decide which process to&lt;br /&gt;
signal when Ctrl-C or Ctrl-Break is pressed: It signals the lowest command&lt;br /&gt;
process in the process tree in that screen group. At first glance, this may&lt;br /&gt;
not seem easy. How can OS/2 distinguish command processes, and how can it&lt;br /&gt;
determine the &amp;quot;lowest&amp;quot;? The total process tree in a screen group may be&lt;br /&gt;
very complex; some processes in it may have died, creating multiple now-&lt;br /&gt;
independent &amp;quot;treelets.&amp;quot;&lt;br /&gt;
     The process tree may be complex, but the tree of processes using the&lt;br /&gt;
keyboard is simpler because a process can't let multiple children&lt;br /&gt;
simultaneously inherit its STDIN. A process can only inherit the&lt;br /&gt;
keyboard,&lt;br /&gt;
2. Actually, a process should only inherit the&lt;br /&gt;
keyboard. The keyboard device can be opened explicitly, but doing&lt;br /&gt;
so when a process's inherited STDIN doesn't point to the keyboard&lt;br /&gt;
device would be a serious error.&lt;br /&gt;
2 not open it explicitly; so a single path down the tree must&lt;br /&gt;
intersect (or contain) all command processes. This single path can't be&lt;br /&gt;
fragmented because of missing processes due to child death because a&lt;br /&gt;
process that has let a child inherit its STDIN must not terminate until the&lt;br /&gt;
child does. So, all OS/2 needs is to find any command process in the&lt;br /&gt;
command subtree and then look at its descendants for another command&lt;br /&gt;
process and so on. The bottommost process receives the signal.&lt;br /&gt;
3. Actually, OS/2 uses a more efficient algorithm than this; I'm&lt;br /&gt;
merely illustrating that finding the lowest command process is&lt;br /&gt;
not difficult.&lt;br /&gt;
3&lt;br /&gt;
     Figure 14-2 illustrates a possible process tree. The shaded processes&lt;br /&gt;
have inherited handles to the keyboard and screen; those marked with C are&lt;br /&gt;
command processes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ³°°°°°°c°°°°°°°³&lt;br /&gt;
                 ÀÄÄÂÄÄÄÄÄÄÄÄÂÄÄÙ                ÚÄÄÄÄ¿    Processes&lt;br /&gt;
             ÚÄÄÄÄÄÄÙ        ÀÄÄÄÄÄÄ¿            ³°°°°³  = using the&lt;br /&gt;
             ³                      ³            ÀÄÄÄÄÙ    keyboard &lt;br /&gt;
         ÚÄÄÄÁÄÄ¿                ÚÄÄÁÄÄÄ¿&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ³      ³                ³°°c°°°³&lt;br /&gt;
         ÀÄÂÄÄÄÄÙ                ÀÄÂÄÄÂÄÙ&lt;br /&gt;
        ÚÄÄÙ                    ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
    ÚÄÄÄÁÄÄ¿                 ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
    ³      ³                 ³°c°°³    ³    ³&lt;br /&gt;
    ³      ³                 ÀÂÄÄÂÙ    ÀÄÄÄÂÙ&lt;br /&gt;
    ÀÄÂÄÄÂÄÙ               ÚÄÄÙ  ÀÄÄ¿      ÀÄÄ¿&lt;br /&gt;
   ÚÄÄÙ  ÀÄÄ¿           ÚÄÄÁÄ¿    ÚÄÁÄÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
ÚÄÄÁÄ¿    ÚÄÁÄÄ¿        ³°c°°³    ³    ³    ³    ³&lt;br /&gt;
³    ³    ³    ³        ÀÂÄÄÂÙ    ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÙ    ÀÄÄÄÄÙ      ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
                   ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
                   ³    ³    ³°°°°³&lt;br /&gt;
                   ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 14-2.  Ctrl-C routing in a process tree.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This now begs the final question: How can OS/2 tell if an application&lt;br /&gt;
is a command process or not? It can tell because all command&lt;br /&gt;
processes/command applications do something that other processes never do.&lt;br /&gt;
By definition, a command process doesn't want to be summarily killed when&lt;br /&gt;
the user presses Ctrl-C or Ctrl-Break, so all command processes establish&lt;br /&gt;
signal handlers for Ctrl-C and Ctrl-Break. Because all command processes&lt;br /&gt;
intercept Ctrl-C and Ctrl-Break, all we need now is to establish the&lt;br /&gt;
convention that only command processes intercept Ctrl-C and Ctrl-Break.&lt;br /&gt;
This hearkens back to our earlier discussion of checking STDIN before&lt;br /&gt;
directly using the keyboard device. Telling the keyboard device that you&lt;br /&gt;
want the Ctrl-C or Ctrl-Break signals routed to your process is a form of&lt;br /&gt;
I/O with the keyboard device, and it must only be done if your program has&lt;br /&gt;
verified that STDIN points to the keyboard device. Furthermore,&lt;br /&gt;
intercepting Ctrl-C or Ctrl-Break just so that your program can clean up&lt;br /&gt;
during unexpected termination is unnecessary and insufficient. The SIGTERM&lt;br /&gt;
signal or, better, the exitlist mechanism provides this capability and&lt;br /&gt;
covers causes of death other than the keyboard. So all processes that&lt;br /&gt;
intercept Ctrl-C and Ctrl-Break have access to the keyboard, and they want&lt;br /&gt;
to do something other than die when the user presses Ctrl-C or Ctrl-Break.&lt;br /&gt;
They fit the command process definition.&lt;br /&gt;
     Now that we've exhaustively shown how OS/2 finds which process to send&lt;br /&gt;
a Ctrl-C signal, what should the process do when it gets the signal? Obey&lt;br /&gt;
the system convention and stop what it's doing as quickly as is wise. If&lt;br /&gt;
the application isn't working on a command, the application typically&lt;br /&gt;
flushes the keyboard type-ahead buffer and reprompts. If the application is&lt;br /&gt;
working on a command that is implemented in code within the application,&lt;br /&gt;
the application jumps from the signal handler to its command loop or, more&lt;br /&gt;
commonly, sets a flag to terminate the current command prematurely.&lt;br /&gt;
4. Occasionally, terminating a command halfway through could&lt;br /&gt;
leave the user's work trashed. In such a case, finishing the&lt;br /&gt;
command is prudent.&lt;br /&gt;
4&lt;br /&gt;
     Finally, if the application is running a child process, it typically&lt;br /&gt;
stops what it's doing by issuing a DosKill on that child command subtree.&lt;br /&gt;
This, then, is how Ctrl-C can kill a program such as MASM. Ctrl-C is sent&lt;br /&gt;
to MASM's closest ancestor that is a command process,&lt;br /&gt;
5. Often, CMD.EXE is MASM's direct ancestor, but other programs, such&lt;br /&gt;
as a build utility, could have come between CMD.EXE and MASM.&lt;br /&gt;
5 which in turn issues&lt;br /&gt;
a DosKill on MASM's subtree. MASM does any exitlist cleanup that it&lt;br /&gt;
wishes (probably deleting scratch files) and then terminates. When the&lt;br /&gt;
command process that ran MASM, typically CMD.EXE, sees that MASM has&lt;br /&gt;
terminated, it prints ^C on the screen, followed by a new prompt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==15  The File System==&lt;br /&gt;
&lt;br /&gt;
The file system in OS/2 version 1.0 is little changed from that of MS-DOS,&lt;br /&gt;
partially in an effort to preserve compatibility with MS-DOS programs and&lt;br /&gt;
partially due to limitations imposed by the project's schedule. When the&lt;br /&gt;
schedule for an &amp;quot;all singing, all dancing&amp;quot; OS/2 was shown to be too long,&lt;br /&gt;
planned file system improvements were moved to a future release. To explain&lt;br /&gt;
the rationale for postponing something so useful, I'll digress a little.&lt;br /&gt;
     The microcomputer industry developed around the dual concepts of mass&lt;br /&gt;
market software and standards. Because software is mass marketed, you can&lt;br /&gt;
buy some very sophisticated and useful programs for a modest sum of money--&lt;br /&gt;
at least modest in comparison to the development cost, which is often&lt;br /&gt;
measured in millions of dollars. Mass marketing encourages standards&lt;br /&gt;
because users don't want to buy machines, peripherals, and systems that&lt;br /&gt;
don't run these programs. Likewise, the acceptance of the standards&lt;br /&gt;
encourages the development of mass market software because standards make&lt;br /&gt;
it possible for a single binary program to execute correctly on a great&lt;br /&gt;
many machines and thus provide a market big enough to repay the development&lt;br /&gt;
costs of a major application.&lt;br /&gt;
     This synergy, or positive feedback, between standards and mass market&lt;br /&gt;
software affected the process of developing operating systems. At first&lt;br /&gt;
glance, adding new features to an operating system seems straightforward.&lt;br /&gt;
The developers create new features in a new release, and then applications&lt;br /&gt;
are written to use those new features. However, with mass market software,&lt;br /&gt;
it doesn't work that way. Microsoft could indeed release a new version of&lt;br /&gt;
OS/2 with new features (and, in fact, we certainly will do so), but&lt;br /&gt;
initially few new applications will use said new features. This is&lt;br /&gt;
because of the initial limited market penetration of the new release. For&lt;br /&gt;
example, let's assume that at a certain time after the availability of a&lt;br /&gt;
new release, 10 percent of OS/2 users have upgraded. An ISV (Independent&lt;br /&gt;
Software Vendor) is planning its next new product--one it hopes will be a&lt;br /&gt;
bestseller. The ISV must decide whether to use the new feature and&lt;br /&gt;
automatically lock itself out of 90 percent of the potential market or to&lt;br /&gt;
use the common subset of features contained in the earlier OS/2 release and&lt;br /&gt;
be able to run on all machines, including the 10 percent running the OS/2&lt;br /&gt;
upgrade. In general, ISVs won't use a nonvital feature until the great&lt;br /&gt;
majority of existing systems support that feature.&lt;br /&gt;
     The key to introducing new features in an operating system isn't that&lt;br /&gt;
they be available and useful; it's that the release which contains those&lt;br /&gt;
new features sees widespread use as quickly as possible. If this doesn't&lt;br /&gt;
happen, then the new feature pretty much dies stillborn. This is why each&lt;br /&gt;
MS-DOS release that contained major new functionality coincided with a&lt;br /&gt;
release that was required to use new hardware. MS-DOS version 2.0 was&lt;br /&gt;
required for the IBM XT product line; MS-DOS version 3.0 was required for&lt;br /&gt;
the IBM AT product line. And OS/2 is no exception: It wasn't required for a&lt;br /&gt;
new &amp;quot;box,&amp;quot; but it was required to bring out the protect mode machine lying&lt;br /&gt;
fallow inside 80286-based machines. If a new release of a system doesn't&lt;br /&gt;
provide a new feature that makes people want it or need it badly, then&lt;br /&gt;
market penetration will be slow. People will pay the cost and endure the&lt;br /&gt;
hassle of upgrading only if their applications require it, and those&lt;br /&gt;
applications dare require it only if most people have already upgraded.&lt;br /&gt;
     Because of this, the initial release of OS/2 is &amp;quot;magical&amp;quot; in the eyes&lt;br /&gt;
of its developers. It provides a window of opportunity in which to&lt;br /&gt;
introduce new features into the PC operating system standard, a window that&lt;br /&gt;
won't be open quite as wide again for a long time. And this postponed major&lt;br /&gt;
file system enhancements: The file system can be enhanced in a later&lt;br /&gt;
release and benefit existing applications without any change on their part,&lt;br /&gt;
whereas many other OS/2 features needed to be in the first release or they&lt;br /&gt;
might never be available.&lt;br /&gt;
&lt;br /&gt;
===15.1  The OS/2 File System===&lt;br /&gt;
&lt;br /&gt;
Although OS/2 version 1.0 contains little in the way of file system&lt;br /&gt;
improvements, it does contain two that are significant. The first is&lt;br /&gt;
asynchronous I/O. Asynchronous I/O consists of two functions, DosReadAsync&lt;br /&gt;
and DosWriteAsync. These functions are identical to DosRead and DosWrite&lt;br /&gt;
except that they return to the caller immediately, usually before the I/O&lt;br /&gt;
operation has completed. Each takes the handle of a semaphore that is&lt;br /&gt;
cleared when the I/O operation completes. The threads of the calling&lt;br /&gt;
process can use this semaphore to poll for operation complete, or they can&lt;br /&gt;
wait for the operation to complete. The DosMuxSemWait call is particularly&lt;br /&gt;
useful in this regard because it allows a process to wait for several&lt;br /&gt;
semaphore events, which can be asynchronous I/O events, IPC events, and&lt;br /&gt;
timer events, intermingled as the programmer wishes.&lt;br /&gt;
     The second file system feature is extended partitioning; it supports&lt;br /&gt;
dividing large physical disks into multiple sections, several of which may&lt;br /&gt;
contain FAT file systems. In effect, it causes OS/2 to treat a large hard&lt;br /&gt;
disk as two or more smaller ones, each of which meets the file system's&lt;br /&gt;
size limits. It's widely believed that MS-DOS is limited to disks less than&lt;br /&gt;
32 MB in size. This isn't strictly true. The limitation is that a disk can&lt;br /&gt;
have no more than 65,535 sectors; the standard sector size is 512 bytes,&lt;br /&gt;
which gives the 32 MB value. Furthermore, each disk is limited to 32,768&lt;br /&gt;
clusters. A sector is the unit of disk storage; disks can read and write&lt;br /&gt;
only integral sectors. A sector's size is established when the disk is&lt;br /&gt;
formatted. A cluster is the unit of disk space allocation for files and&lt;br /&gt;
directories. It may be as small as one sector, or it may be four sectors,&lt;br /&gt;
eight sectors, or some other size. Because the MS-DOS file system supports&lt;br /&gt;
a maximum of 65 KB sectors but only 32 KB clusters, a 32 MB disk must be&lt;br /&gt;
allocated in two-sector (or bigger) clusters. It's possible to write a&lt;br /&gt;
device driver that uses a sector size that is a multiple of 512 bytes,&lt;br /&gt;
which gets around the 65 KB sector restriction and allows the use of a disk&lt;br /&gt;
greater than 32 MB. This trick works for MS-DOS and for OS/2, but it's not&lt;br /&gt;
optimal because it doesn't do anything to increase the maximum number of&lt;br /&gt;
allocation clusters from the existing 32 KB value,&lt;br /&gt;
1. The FAT file system can deal with a maximum of 32 KB allocation&lt;br /&gt;
units, or clusters. No matter what the size of the disk, all files&lt;br /&gt;
must consume disk space in increments of no smaller than 1/32Kth of&lt;br /&gt;
the total disk size. This means that a 60 MB disk, using 1024 byte&lt;br /&gt;
sectors, allocates space in 2048-byte increments.&lt;br /&gt;
1 which means that&lt;br /&gt;
because many disk files are small a lot of space is wasted due to internal&lt;br /&gt;
fragmentation.&lt;br /&gt;
     The OS/2 version 1.0 extended partitioning feature provides an interim&lt;br /&gt;
solution that is not quite as convenient as large sectors but that reduces&lt;br /&gt;
the wastage from internal fragmentation: It allows more than one disk&lt;br /&gt;
partition to contain a FAT file system. Multipartitioned disks are possible&lt;br /&gt;
under MS-DOS, but only one partition can be an MS-DOS (that is, FAT) file&lt;br /&gt;
system. This restriction has been relaxed in OS/2 so that, for example, a&lt;br /&gt;
60 MB disk can be partitioned into two separate logical disks (for example,&lt;br /&gt;
C and D), each 30 MB.&lt;br /&gt;
&lt;br /&gt;
===15.2  Media Volume Management===&lt;br /&gt;
&lt;br /&gt;
The multitasking capability of OS/2 necessitated major file system&lt;br /&gt;
enhancements in the area of volume management. A disk volume is the name&lt;br /&gt;
given to the file system and files on a particular disk medium. A disk&lt;br /&gt;
drive that contains a fixed medium always contains the same volume, but a&lt;br /&gt;
disk drive from which the media (such as floppy disks) can be removed will&lt;br /&gt;
contain whatever disk--whatever volume--the user has in it at the time.&lt;br /&gt;
That volumes can change becomes a problem in a multitasking environment.&lt;br /&gt;
For example, suppose a user is using a word processor to edit a file on a&lt;br /&gt;
floppy disk in drive A. The editor has opened the file and is keeping it&lt;br /&gt;
open for the duration of the edit. Without closing the file or terminating&lt;br /&gt;
the editor, the user can switch to a screen group in which a spreadsheet&lt;br /&gt;
program is running. The user might then need to insert a different disk&lt;br /&gt;
into drive A--one that contains data needed by the spreadsheet. If the user&lt;br /&gt;
then switches back to the word processor without remembering to change the&lt;br /&gt;
floppy disk, disaster will strike. Pressing the Page Down key will cause&lt;br /&gt;
the editor to try to read another sector from its already open disk file.&lt;br /&gt;
The operating system knows--because of FAT information stored in RAM&lt;br /&gt;
buffers_ that the next sector in the text file is sector N, and it will&lt;br /&gt;
issue a read to sector N on the wrong medium--the spreadsheet floppy disk--&lt;br /&gt;
and return that to the word processor program as the next sector of the&lt;br /&gt;
text file. And, at that, the user is getting off lightly; he or she might&lt;br /&gt;
just as easily have given the word processor a command that caused it to&lt;br /&gt;
write a sector to the disk, which would do double damage. A file on the&lt;br /&gt;
spreadsheet floppy would be destroyed by the &amp;quot;random&amp;quot; write, and the text&lt;br /&gt;
file would be corrupted as well because it's missing a sector write that it&lt;br /&gt;
should have received.&lt;br /&gt;
     We can't solve this problem by admonishing the user to be careful;&lt;br /&gt;
many programs read from and write to disk without direct user intervention.&lt;br /&gt;
For example, the word processor might save work in progress to disk every&lt;br /&gt;
two minutes. If this time interval elapses while the user is still working&lt;br /&gt;
with the spreadsheet program on the spreadsheet floppy disk, our&lt;br /&gt;
hypothetical &amp;quot;flawless&amp;quot; user is still S.O.L.&lt;br /&gt;
2. Severely out of luck.&lt;br /&gt;
2&lt;br /&gt;
     OS/2 resolves these problems by recognizing that when an application&lt;br /&gt;
does I/O to an open file the I/O is not really aimed at drive A; it's aimed&lt;br /&gt;
at a particular floppy disk volume--the one containing the open file. Each&lt;br /&gt;
disk volume, removable or not, has a volume name stored in its root&lt;br /&gt;
directory and a unique 32-bit volume identifier stored in its boot sector.&lt;br /&gt;
Figure 15-1 illustrates the two volume names--one for computer use and one&lt;br /&gt;
for human use. Each file handle is associated with a particular 32-bit&lt;br /&gt;
volume ID. When an I/O request is made for a file handle, OS/2 checks to&lt;br /&gt;
see if the proper volume is in the drive by comparing the 32-bit value of&lt;br /&gt;
the request with that of the medium currently spinning. If they match, the&lt;br /&gt;
operation completes. If the mounted volume is different from the requested&lt;br /&gt;
volume, OS/2 uses the hard error daemon mechanism to prompt the user to&lt;br /&gt;
insert the correct volume in the drive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                            VOLUME LABELS&lt;br /&gt;
Sector                                                          Sector&lt;br /&gt;
  0                                                               N&lt;br /&gt;
  ÚÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³     ³             ³      ³                                    ³&lt;br /&gt;
  ³     ³             ³      ³                                    ³&lt;br /&gt;
  ÀÄÄ�ÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄ�ÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ÀÄ 32-bit volume ID  ÀÄÄÄÄÄÄÄ volume name in&lt;br /&gt;
        in boot sector             home directory&lt;br /&gt;
&lt;br /&gt;
Figure 15-1.  Volume ID and volume name location.&lt;br /&gt;
&lt;br /&gt;
     Three problems must be overcome to make this scheme practical. First,&lt;br /&gt;
checking the volume ID of the medium must be fast. You can't afford to read&lt;br /&gt;
the boot sector each time you do an I/O operation if doing so halves the&lt;br /&gt;
speed of disk I/O. Second, you need assurance that volume IDs are unique.&lt;br /&gt;
Third, you need a plan to deal with a volume that doesn't have a volume ID.&lt;br /&gt;
     Keeping down the cost of volume verification is easy if you know when&lt;br /&gt;
a volume is changed; obviously, the ID is read from the boot sector only&lt;br /&gt;
when a medium has been changed. But how can OS/2 tell that a media change&lt;br /&gt;
has occurred? It can't; that's a device driver issue.&lt;br /&gt;
     For starters, if the device contains a nonremovable medium, rechecking&lt;br /&gt;
its volume ID is never necessary. The device driver understands this, and&lt;br /&gt;
when it is asked the status of the medium, it responds, &amp;quot;Unchanged.&amp;quot; Some&lt;br /&gt;
removable media drives have a flag bit that warns the driver that the door&lt;br /&gt;
has been opened. In this case, when asked, the device driver tells OS/2&lt;br /&gt;
that the medium is &amp;quot;uncertain.&amp;quot; The driver doesn't know for sure that it&lt;br /&gt;
was really changed, but it may have been; so OS/2 rechecks the volume ID.&lt;br /&gt;
Rechecking the volume ID is more difficult when a removable media device&lt;br /&gt;
has no such indicator.&lt;br /&gt;
     In this case, the author of the device driver uses device-specific&lt;br /&gt;
knowledge to decide on a minimum possible time to effect a media change. If&lt;br /&gt;
the device is ready, yet less than the minimum possible time has elapsed&lt;br /&gt;
since the last operation, the driver knows that the same medium must be in&lt;br /&gt;
the drive. If more than the minimum possible time has elapsed, the driver&lt;br /&gt;
returns &amp;quot;medium uncertain,&amp;quot; and OS/2 rechecks the volume label. This time&lt;br /&gt;
interval is typically 2 seconds for floppy disk drives, so effectively an&lt;br /&gt;
extra disk read is done after every idle period; for any given episode of&lt;br /&gt;
disk I/O, however, no extra reads are needed.&lt;br /&gt;
     Ensuring that a volume ID is unique is another problem. Simply&lt;br /&gt;
lecturing the user on the wisdom of unique IDs is inadequate; the user will&lt;br /&gt;
still label three disks &amp;quot;temp&amp;quot; or number them all as &amp;quot;10.&amp;quot; And even the&lt;br /&gt;
hypothetical perfect user might borrow from a neighbor a disk whose name is&lt;br /&gt;
the same as one the user already owns. OS/2 deals with this problem by&lt;br /&gt;
using a 32-bit randomized value for disk volume IDs. When a disk is&lt;br /&gt;
formatted, the user enters a supposedly unique name. This name is&lt;br /&gt;
checksummed, and the result, combined with the number of seconds between&lt;br /&gt;
the present and 1980, is used to seed a random number generator. This&lt;br /&gt;
generator returns a 32-bit volume ID. Although accidentally duplicating a&lt;br /&gt;
volume ID is obviously possible, the four billion possible codes make it&lt;br /&gt;
quite unlikely.&lt;br /&gt;
     The name the user enters is used only to prompt the user to insert the&lt;br /&gt;
volume when necessary, so it need not be truly unique for the volume&lt;br /&gt;
management system to work. If the user names several disks WORK, OS/2 still&lt;br /&gt;
sees them as independent volumes because their volume IDs are different. If&lt;br /&gt;
the user inserts the wrong WORK disk in response to a prompt, OS/2&lt;br /&gt;
recognizes it as the wrong disk and reissues the &amp;quot;Insert disk WORK&amp;quot; prompt.&lt;br /&gt;
After trying each WORK volume in turn, the user will probably decide to&lt;br /&gt;
relabel the disks!&lt;br /&gt;
     The thorniest problem arises from unlabeled disks--disks formatted&lt;br /&gt;
with MS-DOS. Forcing the user to label these disks is unacceptable, as is&lt;br /&gt;
having OS/2 automatically label them with volume IDs: The disk may be read-&lt;br /&gt;
only, perhaps permanently so. Even if the disk is not read-only, the&lt;br /&gt;
problem of low density and high density raises its ugly head. Low-density&lt;br /&gt;
disks can be read in a high-density drive, but writes made to a low-density&lt;br /&gt;
disk from a high-density drive can only be read on high-density drives. If&lt;br /&gt;
a low-density disk is placed in a high-density drive and then labeled by&lt;br /&gt;
OS/2, its boot sector is no longer readable when the disk is placed in a&lt;br /&gt;
low-density drive.&lt;br /&gt;
     For volumes without a proper volume ID, OS/2 attempts to create a&lt;br /&gt;
unique substitute volume ID by checksumming parts of the volume's root&lt;br /&gt;
directory and its FAT table. OS/2 uses the existing volume name if one&lt;br /&gt;
exists; if there is no volume name, OS/2 attempts to describe the disk.&lt;br /&gt;
None of these techniques is foolproof, and they require extra disk&lt;br /&gt;
operations every time the medium is identified. Therefore, software&lt;br /&gt;
distributors and users should make every effort to label disks that OS/2&lt;br /&gt;
systems are to use. OS/2 labels are backward compatible with MS-DOS version&lt;br /&gt;
3.x labels.&lt;br /&gt;
     The OS/2 DISKCOPY command makes a byte-by-byte verbatim copy of a&lt;br /&gt;
floppy disk, except that the duplicate disk has a different volume ID value&lt;br /&gt;
in the boot sector (the volume label name is not changed). OS/2 users can't&lt;br /&gt;
tell this, however, because the DISKCOMP utility lies, and if two disks are&lt;br /&gt;
identical in every byte except for the volume ID, it reports that the disks&lt;br /&gt;
are identical. However, if the user uses DISKCOPY to duplicate the disk&lt;br /&gt;
under OS/2 and then compares the two with DISKCOMP under MS-DOS 3.x, a&lt;br /&gt;
difference is reported.&lt;br /&gt;
     Our discussion so far has centered on file reads and writes to an open&lt;br /&gt;
handle. Reads and writes are volume-oriented operations because they're&lt;br /&gt;
aimed at the volume on which the file resides. DosOpens, on the other hand,&lt;br /&gt;
are drive oriented because they search the default or specified drive for&lt;br /&gt;
the file in question (or create it) regardless of the volume in the drive.&lt;br /&gt;
All handle operations are volume oriented, and all name-based calls are&lt;br /&gt;
drive oriented. Currently, you cannot specify that a given file is to be&lt;br /&gt;
opened on or created on a specific volume. To ensure that a scratch or&lt;br /&gt;
output file is created on a certain volume, arrange to have a file open on&lt;br /&gt;
that volume and issue a write to that file immediately before doing the&lt;br /&gt;
file open. The write operation followed by a DosBufReset will ensure that&lt;br /&gt;
the particular medium is in the drive at that time.&lt;br /&gt;
&lt;br /&gt;
===15.3  I/O Efficiency===&lt;br /&gt;
&lt;br /&gt;
OS/2 provides full blocking and deblocking services for all disk I/O&lt;br /&gt;
requests. A program can read or write any number of bytes, and OS/2 will&lt;br /&gt;
read the proper sectors into internal buffers so that only the specified&lt;br /&gt;
bytes are affected. Naturally, every DosRead or DosWrite call takes time to&lt;br /&gt;
execute, so if your program makes few I/O calls, each for large amounts of&lt;br /&gt;
data, it will execute faster.&lt;br /&gt;
     I/O performance can be further improved by making sector aligned&lt;br /&gt;
calls, that is, by requesting a transfer of an integral multiple of 512&lt;br /&gt;
bytes to or from a file seek position that is itself a multiple of 512.&lt;br /&gt;
OS/2 reads and writes entire disk sectors directly from and to the device&lt;br /&gt;
hardware without an intermediate copy step through system buffers. Because&lt;br /&gt;
the file system keeps logically adjacent sectors physically adjacent on the&lt;br /&gt;
disk, disk seek times and rotational latency are such that one can read or&lt;br /&gt;
write four sectors of data (2048 bytes) in essentially the same time needed&lt;br /&gt;
to read or write one sector (512 bytes).&lt;br /&gt;
     Even if the length or the file position of the request isn't a&lt;br /&gt;
multiple of 512, OS/2 performs the initial fraction of the request via its&lt;br /&gt;
buffers, directly transfers any whole sectors out of the middle of the&lt;br /&gt;
request, and uses the buffers for the fractional remainder. Even if your&lt;br /&gt;
requests aren't sector aligned, making them as large as feasible is&lt;br /&gt;
beneficial.&lt;br /&gt;
     To summarize, I/O is most efficient when requests are large and sector&lt;br /&gt;
aligned. Even misaligned requests can be almost optimally serviced if they&lt;br /&gt;
are large. Programs that cannot naturally make aligned requests and that&lt;br /&gt;
are not I/O intensive should take advantage of the blocking and deblocking&lt;br /&gt;
services that OS/2 provides. Likewise, programs that need to make large,&lt;br /&gt;
unaligned requests should use OS/2's blocking management. Programs that&lt;br /&gt;
need to make frequent, small, nonaligned requests will perform best if they&lt;br /&gt;
read blocks of sectors into internal buffers and deblock the data&lt;br /&gt;
themselves, avoiding the overhead of frequent DosRead or DosWrite calls.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==16  Device Monitors, Data Integrity, and Timer Services==&lt;br /&gt;
&lt;br /&gt;
In discussing the design goals of OS/2, I mentioned continuing to support&lt;br /&gt;
the kinds of functionality found in MS-DOS, even when that functionality&lt;br /&gt;
was obtained by going around the operating system. A good example of such&lt;br /&gt;
functionality is device data manipulation, a technique that usually&lt;br /&gt;
involves hooking interrupt vectors and that is used by many application&lt;br /&gt;
programs. For example, pop-up programs such as SideKick have become very&lt;br /&gt;
popular. These programs get into memory via the terminate and stay resident&lt;br /&gt;
mechanism and then edit the keyboard interrupt vector to point to their&lt;br /&gt;
code. These programs examine each keystroke to see if it is their special&lt;br /&gt;
activate key. If not, they transfer control to the original interrupt&lt;br /&gt;
handler. If the keystroke is their special activate key, they retain&lt;br /&gt;
control of the CPU and display, or &amp;quot;pop up,&amp;quot; a message or a menu on the&lt;br /&gt;
screen. Other programs hook the keyboard vector to provide spell checking&lt;br /&gt;
or keyboard macro expansion. Some programs also hook the BIOS entry vector&lt;br /&gt;
that commands the printer, either to substitute alternate printer driver&lt;br /&gt;
code or to manipulate the data sent to the printer. Programs that turn a&lt;br /&gt;
spreadsheet's output sideways are an example of this.&lt;br /&gt;
     In general, these programs edit, or hook, the interrupt vectors that&lt;br /&gt;
receive device interrupts and communicate with device driver routines in&lt;br /&gt;
the ROM BIOS. The functions provided by such programs and their evident&lt;br /&gt;
popularity among users demonstrate a need for programs to be able to&lt;br /&gt;
monitor and/or modify device data streams. The OS/2 mechanism that does&lt;br /&gt;
this is called a device monitor.&lt;br /&gt;
&lt;br /&gt;
===16.1  Device Monitors===&lt;br /&gt;
&lt;br /&gt;
The design of device monitors had to meet the general requirements and&lt;br /&gt;
religion of OS/2. Specifically, the MS-DOS technique of letting&lt;br /&gt;
applications receive interrupts by editing the interrupt vectors could not&lt;br /&gt;
be allowed because doing so would destroy the system's ability to provide a&lt;br /&gt;
stable environment. Furthermore, unlike MS-DOS, OS/2 doesn't use the ROM&lt;br /&gt;
BIOS as a form of device driver, so hooking the BIOS communication vectors&lt;br /&gt;
would not provide access to the device data stream. In addition, allowing&lt;br /&gt;
an application to arbitrarily interfere with a device driver's operation is&lt;br /&gt;
contrary to OS/2 design principles; the device driver is the architectural&lt;br /&gt;
embodiment of knowledge about the device, and it must be involved in and&lt;br /&gt;
&amp;quot;aware&amp;quot; of any external manipulation of the data stream. The result is an&lt;br /&gt;
OS/2 device monitor mechanism that allows processes, running in their&lt;br /&gt;
normal ring 3 state, to monitor and edit device data streams with the prior&lt;br /&gt;
permission and knowledge of the appropriate device driver.&lt;br /&gt;
     Specifically, a process registers itself as a device monitor by&lt;br /&gt;
calling the appropriate device driver via a DosMonReg call.&lt;br /&gt;
1. Which is a dynlink package that eventually calls the device&lt;br /&gt;
driver via a DosDevIOCtl call.&lt;br /&gt;
1 The process&lt;br /&gt;
also provides two data buffers, one for incoming monitor data and another&lt;br /&gt;
for outgoing monitor data. Processes can easily call OS/2, but OS/2 has no&lt;br /&gt;
way to call processes.&lt;br /&gt;
2. Signals are a partial exception to this, but signals have&lt;br /&gt;
limitations, as discussed earlier.&lt;br /&gt;
2 OS/2 gets around this by inverting the normal&lt;br /&gt;
sense of a call and return sequence. When OS/2 needs to &amp;quot;call&amp;quot; a process,&lt;br /&gt;
it requires that process to call OS/2 beforehand with one of its threads.&lt;br /&gt;
OS/2 holds this thread captive until the callback event takes place. OS/2&lt;br /&gt;
then accomplishes a call to the process by releasing the thread so that it&lt;br /&gt;
returns from the holding system call and resumes execution within the&lt;br /&gt;
process. When the process is ready to &amp;quot;return&amp;quot; to OS/2, it recalls the&lt;br /&gt;
holding entry point (see Figure 16-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      Process calls OS/2     |        OS/2 &amp;quot;calls&amp;quot; Process&lt;br /&gt;
                             | &lt;br /&gt;
Process  call                |  Process  call           FCN    call&lt;br /&gt;
    ÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄ    |      ÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
          �       �          |            �       �             �&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
  Ä Ä Ä Ä Å Ä Ä Ä Å Ä Ä Ä    |    Ä Ä Ä Ä Å Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Å Ä Ä&lt;br /&gt;
 OS/2     ³       ³          |   OS/2     ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          �       �          |            �       �             �&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³  FCN  ³          |            ³       ³             ³&lt;br /&gt;
          ÀÄÄÄÄÄÄÄÙ          |            ÀÄÄ&amp;lt; &amp;lt;ÄÄÙ             ÀÄÄÄ&amp;lt;&lt;br /&gt;
                Return       |                &amp;gt; &amp;gt; Return             &amp;gt;&lt;br /&gt;
&lt;br /&gt;
Figure 16-1.  &amp;quot;Calling&amp;quot; a process from OS/2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 uses this technique for monitors as well. A monitoring process is&lt;br /&gt;
required to call the OS/2 entry point directly after registering itself as&lt;br /&gt;
a device monitor. OS/2 notifies the monitor process of the presence of data&lt;br /&gt;
in the incoming buffer by allowing this thread to return to the process.&lt;br /&gt;
Figure 16-2 illustrates a device with two monitoring processes, X and Y.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿              ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³         Monitor          ³              ³         Monitor          ³&lt;br /&gt;
³        Process X         ³              ³        Process Y         ³&lt;br /&gt;
³                          ³              ³                          ³&lt;br /&gt;
³ DosMonRead   DosMonWrite ³              ³ DosMonRead   DosMonWrite ³&lt;br /&gt;
ÀÄÄÄÄÄ�ÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ              ÀÄÄÄÄÄ�ÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ&lt;br /&gt;
      ÀÄ¿          ÀÄÄÄÄÄÄÄÄÄÄÄ¿      ÚÄÄÄÄÄÄÄÄÄÙ            ³&lt;br /&gt;
        ³                      ³      ³                      ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄ¿&lt;br /&gt;
³       ³                      ³      ³                      ³       ³&lt;br /&gt;
³   ÚÄÄÄÁÄÄÄÄ¿                Ú�ÄÄÄÄÄÄÁ¿                ÚÄÄÄÄ�ÄÄÄ¿   ³&lt;br /&gt;
³   ³ Buffer ³                ³ Buffer ³                ³ Buffer ³   ³&lt;br /&gt;
³   ³   1    ³                ³   2    ³                ³   3    ³   ³&lt;br /&gt;
³   ÀÄÄÄ�ÄÄÄÄÙ                ÀÄÄÄÄÄÄÄÄÙ                ÀÄÄÄÄÂÄÄÄÙ   ³&lt;br /&gt;
³  ÚÄÄÄÄÙ                        OS/2                        ÀÄÄÄÄ¿  ³&lt;br /&gt;
ÀÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÙ&lt;br /&gt;
   ³ Data in                                             Data out ³&lt;br /&gt;
   ³                                                              ³&lt;br /&gt;
ÚÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄ¿&lt;br /&gt;
³  ³                        Device driver                         �  ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 16-2.  Device monitors.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     But we need to discuss a few additional details. First, because OS/2&lt;br /&gt;
strives to make processes see a consistent environment regardless of the&lt;br /&gt;
presence of other processes, each device can have as many monitors as the&lt;br /&gt;
device driver allows. OS/2 connects multiple device monitors into a chain&lt;br /&gt;
so that the device data stream is passed through the first monitor in the&lt;br /&gt;
chain, then through the second monitor, and so on. When a process registers&lt;br /&gt;
itself as a monitor, it specifies whether it wants to be first in the chain&lt;br /&gt;
or last in the chain; some applications are sensitive to this. The first&lt;br /&gt;
monitor to register itself as first is truly first; the next monitor to ask&lt;br /&gt;
for first actually becomes second, and so forth. The same algorithm applies&lt;br /&gt;
to monitors that want to be last: The first to so request becomes the last,&lt;br /&gt;
the second to request last becomes next to last, and so forth.&lt;br /&gt;
     The actual format of the monitor data stream is device specific; the&lt;br /&gt;
device driver decrees the format. Some device drivers have special rules&lt;br /&gt;
and requirements. For example, the keyboard device driver allows monitoring&lt;br /&gt;
processes to insert the &amp;quot;screen switch&amp;quot; key sequence into the data stream,&lt;br /&gt;
whereupon it is recognized as if the user had typed it at the physical&lt;br /&gt;
keyboard. But the device driver will not pass such sequences that really&lt;br /&gt;
were typed through the monitor chain; they are directly obeyed instead.&lt;br /&gt;
     This approach prevents an amok keyboard monitor from effectively&lt;br /&gt;
crashing the system by intercepting and consuming all attempts by the user&lt;br /&gt;
to switch screen groups. The screen device driver does not allow device&lt;br /&gt;
monitors, not because the performance impact would be too big (as it, in&lt;br /&gt;
fact, would be) but because the VIO and presentation manager dynlink&lt;br /&gt;
packages totally circumvent the screen device driver so that it never sees&lt;br /&gt;
any screen data being written.&lt;br /&gt;
     The DosMonRead call holds the device's thread until incoming data is&lt;br /&gt;
available. The DosMonWrite call returns the CPU to the process as soon as&lt;br /&gt;
it is able. The same thread that calls DosMonRead need not be the one to&lt;br /&gt;
call DosMonWrite (see below).&lt;br /&gt;
     Because monitors are an important component of OS/2, an application&lt;br /&gt;
must be very careful to use them properly; therefore, some caveats are in&lt;br /&gt;
order. First, monitors are inserted into the device data chain, with&lt;br /&gt;
obvious effects on the data throughput rate of the device. Each time the&lt;br /&gt;
user presses a key, for example, a packet must pass through every monitor&lt;br /&gt;
in the keyboard chain before the application can read the key and obey or&lt;br /&gt;
echo it. Clearly, any sluggishness on the part of a monitor or the presence&lt;br /&gt;
of too many monitors in a chain will adversely affect system response. The&lt;br /&gt;
thread involved in reading, processing, and writing monitor data should be&lt;br /&gt;
set at a high priority. We recommend the lowest of the force run priority&lt;br /&gt;
categories. Furthermore, the monitor component of a monitoring application&lt;br /&gt;
must contain no critical sections or other events that could slow or&lt;br /&gt;
suspend its operation. In addition, if a monitor data stream will be&lt;br /&gt;
extensively processed, a normal-priority thread must be used to handle that&lt;br /&gt;
processing so that the high-priority thread can continue to transfer&lt;br /&gt;
monitor data in and out without impediment. For example, an auxiliary&lt;br /&gt;
thread and buffer must be used if a keyboard monitor is to write all&lt;br /&gt;
keystrokes to a disk buffer.&lt;br /&gt;
     Finally, if a monitor process terminates abnormally although OS/2&lt;br /&gt;
properly unlinks it from the monitor chain, the data in the process's&lt;br /&gt;
monitor buffers is lost. Clearly, losing an unspecified amount of data&lt;br /&gt;
without warning from the keyboard data stream or perhaps from printer&lt;br /&gt;
output will upset the user no little amount. Monitoring processes must be&lt;br /&gt;
written carefully so that they minimize this risk.&lt;br /&gt;
     The device monitor feature threatens OS/2's fundamental architectural&lt;br /&gt;
principles more than any other. Thus, its presence in the system testifies&lt;br /&gt;
to its importance. Specifically, device monitors violate the design&lt;br /&gt;
principle of minimizing interference between processes, a.k.a.&lt;br /&gt;
encapsulation. Clearly, a process that is monitoring a device's data stream&lt;br /&gt;
can affect the output of or input to a great many processes other than&lt;br /&gt;
itself. This is sometimes called a feature, not a bug. For example, the&lt;br /&gt;
printer spooler uses monitors to intercept output aimed at the printer,&lt;br /&gt;
storing it on disk, and to feed data from those disk files to the actual&lt;br /&gt;
printer device. Clearly, spooling printer output interferes with another&lt;br /&gt;
process, but the interference is valuable. Designers of monitoring&lt;br /&gt;
applications must ensure that their applications damage neither the&lt;br /&gt;
system's performance nor its stability.&lt;br /&gt;
&lt;br /&gt;
===16.2  Data Integrity===&lt;br /&gt;
&lt;br /&gt;
I've discussed data integrity in a multitasking environment several times.&lt;br /&gt;
This section does not review that material in detail but brings together&lt;br /&gt;
all the elements and introduces a few related system facilities.&lt;br /&gt;
     The first problem in a multitasking system is that multiple processes,&lt;br /&gt;
or multiple threads within a process, may try to simultaneously manipulate&lt;br /&gt;
the same resource--a file, a device, a data structure in memory, or even a&lt;br /&gt;
single byte of memory. When the manipulation of a resource must be&lt;br /&gt;
serialized to work correctly, that manipulation is called a critical&lt;br /&gt;
section. This term refers to the act of manipulating the resource, but not&lt;br /&gt;
particularly to the code that does so. Clearly, if any of four subroutines&lt;br /&gt;
can manipulate a particular resource, entering any of the four is entering&lt;br /&gt;
the critical section.&lt;br /&gt;
     The problem is more pervasive than a programmer unfamiliar with the&lt;br /&gt;
issue might assume. For example, even the simple act of testing a word to&lt;br /&gt;
see if it holds the value 4 and incrementing it if it doesn't is a critical&lt;br /&gt;
section. If only one thread in one process can access this word, then the&lt;br /&gt;
critical section is serialized. But if more than one thread can access the&lt;br /&gt;
word, then more than one thread could be in the critical section at the&lt;br /&gt;
same time, with disastrous results. Specifically, consider the assembly&lt;br /&gt;
language sequence shown in Listing 16-1. It looks simple enough: Test to&lt;br /&gt;
see if COUNT holds 4; if it doesn't, increment it; if it does, jump to the&lt;br /&gt;
label COMPLETE. Listing 16-2 shows what might go wrong in a multithreaded&lt;br /&gt;
environment: Thread A checks the value to see if it's 4, but it's 3. Right&lt;br /&gt;
after the compare instruction, a context switch takes place, and thread B&lt;br /&gt;
is executed. Thread B also performs the compare, sees the value as 3, and&lt;br /&gt;
increments it. Later, thread A resumes execution, after the compare&lt;br /&gt;
instruction, at a location where it believes the COUNT value to be 3; so it&lt;br /&gt;
also increments the value of COUNT. The value is now 5 and will continue to&lt;br /&gt;
be incremented way past the value of 4 that was supposed to be its upper&lt;br /&gt;
limit. The label COMPLETE may never be reached.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
COUNT        DW      0               ; Event counter&lt;br /&gt;
&lt;br /&gt;
        .&lt;br /&gt;
        .&lt;br /&gt;
        CMP     COUNT,4        ; is this the 4th?&lt;br /&gt;
        JE      COMPLETE       ; yes, we're done&lt;br /&gt;
        INC     COUNT          ; count event&lt;br /&gt;
        .&lt;br /&gt;
        .&lt;br /&gt;
&lt;br /&gt;
Listing 16-1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        Thread A                                     Thread B&lt;br /&gt;
&lt;br /&gt;
             .&lt;br /&gt;
             .&lt;br /&gt;
             CMP     COUNT,4  [count is now 3]&lt;br /&gt;
             -------------------context switch---&amp;gt;&lt;br /&gt;
                                    CMP     COUNT,4 [count is 3]&lt;br /&gt;
                                    JE      COMPLETE&lt;br /&gt;
                                    INC     COUNT   [count is 4]&lt;br /&gt;
                                              .&lt;br /&gt;
                                              .&lt;br /&gt;
             &amp;lt;-----------context switch--------&lt;br /&gt;
             JE      COMPLETE [jmp not taken]&lt;br /&gt;
             INC     COUNT    [count is now 5]&lt;br /&gt;
&lt;br /&gt;
Listing 16-2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     I apologize for again lecturing on this topic, but such problems are&lt;br /&gt;
very nonobvious, rarely turn up in testing, are nearly impossible to find&lt;br /&gt;
in the field, and the very possibility of their existence is new with OS/2.&lt;br /&gt;
Thus, &amp;quot;too much is not enough,&amp;quot; caveat-wise. Now that we've reviewed the&lt;br /&gt;
problems, let's look at the solutions.&lt;br /&gt;
     A programmer inexperienced with a multitasking environment might&lt;br /&gt;
protest that this scenario is unlikely, and indeed it is. Maybe the chances&lt;br /&gt;
are only 1 in 1 million that it would happen. But because a microprocessor&lt;br /&gt;
executes 1 million instructions a second, it might not be all that long&lt;br /&gt;
before the 1-in-1-million unlucky chance comes true. Furthermore, an&lt;br /&gt;
incorrect program normally has multiple unprotected critical sections, many&lt;br /&gt;
of which are larger than the 2-instruction window in our simple example.&lt;br /&gt;
     The program must identify and protect all critical sections; a program&lt;br /&gt;
that fails to do so will randomly fail. You can't take solace in there&lt;br /&gt;
being only one CPU and assuming that OS/2 probably won't context switch in&lt;br /&gt;
the critical section. OS/2 can context switch at any time, and because&lt;br /&gt;
context switching can be triggered by unpredictable external events, such&lt;br /&gt;
as serial port I/O and rotational latency on a disk, no amount of testing&lt;br /&gt;
can prove that an unprotected critical section is safe. In reality, a test&lt;br /&gt;
environment is often relatively simple; context switching tends to occur at&lt;br /&gt;
consistent intervals, which means that such problems tend not to turn up&lt;br /&gt;
during program test. Instead, they turn up in the real world, and give your&lt;br /&gt;
program a reputation for instability.&lt;br /&gt;
     Naturally, testing has its place, but the only sure way to deal with&lt;br /&gt;
critical sections is to examine your code carefully while assuming that all&lt;br /&gt;
threads in the system are executing simultaneously.&lt;br /&gt;
3. This is more than a Gedankenexperiment. Multiple&lt;br /&gt;
processor machines will be built, and when they are, OS/2 will execute&lt;br /&gt;
multiple threads, even within one process, truly simultaneously.&lt;br /&gt;
3 Furthermore, when&lt;br /&gt;
examining a code sequence, always assume that the CPU will reschedule in&lt;br /&gt;
the worst way. If there is any possible window, reality will find it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.1  Semaphores&lt;br /&gt;
The traditional solution for protecting critical sections is the semaphore.&lt;br /&gt;
The two OS/2 semaphores--RAM and system--each have advantages, and the&lt;br /&gt;
operation of each is guaranteed to be completely immune to critical section&lt;br /&gt;
problems. In the jargon, their operation is guaranteed atomic. Whenever a&lt;br /&gt;
thread is going to manipulate a critical resource, it first claims the&lt;br /&gt;
semaphore that protects the resource. Only after it controls the semaphore&lt;br /&gt;
does it look at the resource because the resource's values may have changed&lt;br /&gt;
between the time the semaphore was requested and the time it was granted.&lt;br /&gt;
After the thread completes its manipulation of the resource, it releases&lt;br /&gt;
the semaphore.&lt;br /&gt;
     The semaphore mechanism protects well against all cooperating&lt;br /&gt;
4. Obviously, if some thread refuses to claim the semaphore,&lt;br /&gt;
nothing can be done.&lt;br /&gt;
4&lt;br /&gt;
threads, whether they belong to the same process or to different processes.&lt;br /&gt;
Another OS/2 mechanism, called DosEnterCritSec, can be used to protect a&lt;br /&gt;
critical section that is accessed only by threads belonging to a single&lt;br /&gt;
process. When a thread issues the DosEnterCritSec call, OS/2 suspends&lt;br /&gt;
execution of all other threads in that process until a subsequent&lt;br /&gt;
DosEnterCritSec call is issued. Naturally, only threads executing in&lt;br /&gt;
application mode are suspended; threads executing inside the OS/2 kernel&lt;br /&gt;
are not suspended until they attempt to return to application mode.&lt;br /&gt;
5. I leave as an exercise to the reader to explain why the&lt;br /&gt;
DosEnterCritSec call is not safe unless all other threads&lt;br /&gt;
in the process make use of it for that critical section as well.&lt;br /&gt;
5 The&lt;br /&gt;
use of DosEnterCritSec is dangerous because the process's other threads may&lt;br /&gt;
be suspended while they are holding a critical section. If the thread that&lt;br /&gt;
issued the DosEnterCritSec then also tries to enter that critical section,&lt;br /&gt;
the process will deadlock. If a dynlink package is involved, it may have&lt;br /&gt;
created extra threads unbeknownst to the client process so that the client&lt;br /&gt;
may not even be aware that such a critical section exists and might be in&lt;br /&gt;
use. For this reason, DosEnterCritSec is safe only when used to protect&lt;br /&gt;
short sections of code that can't block or deadlock and that don't call any&lt;br /&gt;
dynlink modules.&lt;br /&gt;
     Still another OS/2 critical section facility is file sharing and&lt;br /&gt;
record locking, which can be used to protect critical sections when they&lt;br /&gt;
consist of files or parts of files. For example, a database program&lt;br /&gt;
certainly considers its master database file a critical section, and it&lt;br /&gt;
doesn't want anyone messing with it while the database application has it&lt;br /&gt;
open. It can open the file with the file-sharing mode set to &amp;quot;allow no&lt;br /&gt;
(other) readers, allow no writers.&amp;quot; As long as the database application&lt;br /&gt;
keeps the file open, OS/2 prevents any other process from opening (or&lt;br /&gt;
deleting!) that file.&lt;br /&gt;
     The record-locking mechanism can be used to provide a smaller&lt;br /&gt;
granularity of protection. A process can lock a range of bytes within a&lt;br /&gt;
file, and while that lock is in effect, OS/2 prevents any other process&lt;br /&gt;
from reading or writing those bytes. These two specialized forms of&lt;br /&gt;
critical section protection are unique in that they protect a process&lt;br /&gt;
against all other processes, even &amp;quot;uncooperating&amp;quot; ones that don't protect&lt;br /&gt;
their own access to the critical section. Unfortunately, the file-sharing&lt;br /&gt;
and record-locking mechanisms don't contain any provision for blocking&lt;br /&gt;
until the conflict is released. Applications that want to wait for the&lt;br /&gt;
conflict to clear must use a polling loop. Use DosSleep to block for at&lt;br /&gt;
least a half second between each poll.&lt;br /&gt;
     Unfortunately, although semaphores protect critical sections well,&lt;br /&gt;
sometimes they bring problems of their own. Specifically, what happens if&lt;br /&gt;
an asynchronous event, such as program termination or a signal, pulls the&lt;br /&gt;
CPU away from inside a critical section and the CPU never returns to&lt;br /&gt;
release the semaphore? The answers range from &amp;quot;moot&amp;quot; to &amp;quot;disaster,&amp;quot;&lt;br /&gt;
depending on the circumstances. The possibilities are so manifold that&lt;br /&gt;
I'll group some of them.&lt;br /&gt;
     What can you do if the CPU is pulled away inside a critical&lt;br /&gt;
section?&lt;br /&gt;
&lt;br /&gt;
     þ  Ignore it. This is fine if the critical section is wholly accessed&lt;br /&gt;
        by a single process and that process doesn't use signals to modify&lt;br /&gt;
        the normal path of execution and if neither the process nor its&lt;br /&gt;
        dynlink routines attempt to enter the critical section during&lt;br /&gt;
        DosExitList processing.&lt;br /&gt;
&lt;br /&gt;
     þ  Clear the semaphore. This is an option if you know that the&lt;br /&gt;
        resource protected by the semaphore has no state, such as a&lt;br /&gt;
        semaphore that protects the right to be writing to the screen. The&lt;br /&gt;
        trick is to ensure that the interrupted thread set the semaphore&lt;br /&gt;
        and that you don't accidentally clear the semaphore when you don't&lt;br /&gt;
        set it. For example, if the semaphore is wholly used within a&lt;br /&gt;
        single process but that process's DosExitList handlers may use it,&lt;br /&gt;
        they can force the semaphore clear when they are entered.&lt;br /&gt;
&lt;br /&gt;
     þ  Detect the situation and repair the critical section. This&lt;br /&gt;
        detection can be made for RAM semaphores only during process&lt;br /&gt;
        termination and only if the semaphore is solely used by that&lt;br /&gt;
        process. In such a case, you know that a thread in the process set&lt;br /&gt;
        the semaphore, and you know that the thread is no longer executing&lt;br /&gt;
        the critical section because all threads are terminated. You can&lt;br /&gt;
        test the semaphore by using a nonblocking DosSemSet; if it's set,&lt;br /&gt;
        &amp;quot;recover&amp;quot; the resource.&lt;br /&gt;
&lt;br /&gt;
        System semaphores are generally better suited for this. When the&lt;br /&gt;
        owning thread of a system semaphore dies, the semaphore is given a&lt;br /&gt;
        special mark. The next attempt to set the semaphore returns with a&lt;br /&gt;
        code that tells the new owner that the previous owner died within&lt;br /&gt;
        the critical section. The new owner has the option of cleaning up&lt;br /&gt;
        the resource.&lt;br /&gt;
&lt;br /&gt;
     Another possibility is to try to prevent the CPU from being yanked out&lt;br /&gt;
of a critical section. Signals can be momentarily delayed with the&lt;br /&gt;
DosHoldSignal mechanism. Process termination that results from an external&lt;br /&gt;
kill can be postponed by setting up a signal handler for the KILL signal&lt;br /&gt;
and then using DosHoldSignal. This last technique doesn't protect you&lt;br /&gt;
against termination due to GP fault and the like however.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.2  DosBufReset&lt;br /&gt;
One remaining data integrity issue--disk data synchronization--is not&lt;br /&gt;
related to critical sections. Often, when a DosWrite call is made, OS/2&lt;br /&gt;
holds the data in a buffer rather than writing it immediately to disk.&lt;br /&gt;
Naturally, any subsequent calls made to read this data are satisfied&lt;br /&gt;
correctly, so an application cannot see that the data has not yet been&lt;br /&gt;
written unless the reading application uses direct physical access to the &lt;br /&gt;
volume (that is, raw media reads). This case explains why CHKDSK may&lt;br /&gt;
erroneously report errors that run on a volume that has open files.&lt;br /&gt;
     OS/2 eventually writes the data to the disk, so this buffering is of&lt;br /&gt;
concern only when the system crashes with unwritten buffered data.&lt;br /&gt;
Naturally, such crashes are expected to be rare, but some applications may&lt;br /&gt;
find the possibility so threatening that they want to take protective&lt;br /&gt;
steps. The two OS/2 functions for this purpose are flushing and&lt;br /&gt;
writethroughs. The flush operation--DosBufReset--writes all dirty buffers--&lt;br /&gt;
those with changed but unwritten data in them--to the disk. When the call&lt;br /&gt;
returns, the data is on the disk. Use this call sparingly; although its&lt;br /&gt;
specification promises only that it will flush buffers associated with the&lt;br /&gt;
specified file handle(s), for most file systems it writes all dirty buffers&lt;br /&gt;
in the system to disk. Moreover, if file handles are open to a server&lt;br /&gt;
machine on the network, most or all of that server's buffers get flushed,&lt;br /&gt;
even those that were used by other client machines on the network. Because&lt;br /&gt;
of these costs, applications should use this operation judiciously.&lt;br /&gt;
     Note that it's not true that a flush operation simply causes a write&lt;br /&gt;
to be done sooner rather than later. A flush operation may also cause extra&lt;br /&gt;
disk writes. For example, consider an application that is writing data 10&lt;br /&gt;
bytes at a time. In this case, OS/2 buffers the data until it has a full&lt;br /&gt;
sector's worth. A series of buffer flush operations arriving at this time&lt;br /&gt;
would cause the assembly buffer to be written to the disk many extra and&lt;br /&gt;
unnecessary times.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.3  Writethroughs&lt;br /&gt;
Buffer flushes are expensive, and unless they are used frequently, they&lt;br /&gt;
don't guarantee a particular write ordering. Some applications, such as&lt;br /&gt;
database managers, may want to guarantee that data be written to the disk&lt;br /&gt;
in exactly the same order in which it was given to OS/2 via DosWrite. For&lt;br /&gt;
example, an application may want to guarantee that the data is in place in&lt;br /&gt;
a database before the allocation chain is written and that the chain be&lt;br /&gt;
written before the database directory is updated. Such an ordering may make&lt;br /&gt;
it easy for the package to recover the database in case of a crash.&lt;br /&gt;
     The OS/2 mechanism for doing this is called writethrough--a status bit&lt;br /&gt;
that can be set for individual file handles. If a writethrough is in effect&lt;br /&gt;
for a handle to which the write is issued, OS/2 guarantees that the data&lt;br /&gt;
will be written to the disk before the DosWrite operation returns.&lt;br /&gt;
Obviously, applications using writethrough should write their data in large&lt;br /&gt;
chunks; writing many small chunks of data to a file marked for writethrough&lt;br /&gt;
is very inefficient.&lt;br /&gt;
&lt;br /&gt;
     Three caveats are associated with writethroughs:&lt;br /&gt;
&lt;br /&gt;
     þ  If writethrough is set on a file after it is open, all subsequent&lt;br /&gt;
        writes are written through, but data from previous writes may still&lt;br /&gt;
        be in dirty buffers.&lt;br /&gt;
&lt;br /&gt;
     þ  If a writethrough file is being shared by multiple processes or is&lt;br /&gt;
        open on multiple handles, all instances of that file should be&lt;br /&gt;
        marked writethrough. Data written to a handle not marked&lt;br /&gt;
        writethrough may go into the buffers.&lt;br /&gt;
&lt;br /&gt;
     þ  The operation of data writethroughs has some nonintuitive surprises&lt;br /&gt;
        when used with the current FAT file system. Specifically, although&lt;br /&gt;
        this feature works as advertised to place the file's data sectors&lt;br /&gt;
        on the disk, it does not update the directory entry that specifies&lt;br /&gt;
        the size of the file. Thus, if you extend a file by 10 sectors and&lt;br /&gt;
        the system crashes before you close the file, the data in those 10&lt;br /&gt;
        sectors is lost. If you had writethrough set, then those 10 sectors&lt;br /&gt;
        of data were indeed written to the disk; but because the directory&lt;br /&gt;
        entry wasn't updated, CHKDSK will return those sectors to the free&lt;br /&gt;
        list.&lt;br /&gt;
&lt;br /&gt;
        The writethrough operation protects the file's data but not the&lt;br /&gt;
        directory or allocation information. This is not a concern as long&lt;br /&gt;
        as you write over a portion of the file that has been already&lt;br /&gt;
        extended, but any writes that extend the file are not protected.&lt;br /&gt;
        The good news is that the data will be on the disk, as guaranteed,&lt;br /&gt;
        but the bad news is that the directory entry won't be updated; if&lt;br /&gt;
        the system crashes, file extensions cannot be recovered. The&lt;br /&gt;
        recommended solution is to use DosNewSize to extend the file as&lt;br /&gt;
        needed, followed by DosBufReset to update the directory information&lt;br /&gt;
        on the disk, and then to writethrough the data as needed.&lt;br /&gt;
        Overextending the file size is better than doing too many&lt;br /&gt;
        NewSize/BufReset combinations; if you overextend, you can always&lt;br /&gt;
        shrink the file before closing it with a final DosNewSize.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.3  Timer Services&lt;br /&gt;
&lt;br /&gt;
Frequently, applications want to keep track of the passage of real time. A&lt;br /&gt;
game program may want events to occur asynchronously with the user's input;&lt;br /&gt;
a telecommunications program may want to track how long a response takes&lt;br /&gt;
and perhaps declare a link timed-out after some interval. Other programs&lt;br /&gt;
may need to pace the display of a demonstration or assume a default action&lt;br /&gt;
if the user doesn't respond in a reasonable amount of time. OS/2 provides&lt;br /&gt;
several facilities to track the passage of real time; applications should&lt;br /&gt;
use these facilities and shun polling and timing loops because the timing&lt;br /&gt;
of such loops depends on the system's workload and the CPU's speed and&lt;br /&gt;
because they totally lock out from execution any thread of a lower&lt;br /&gt;
priority.&lt;br /&gt;
     Time intervals in OS/2 are discussed in terms of milliseconds to&lt;br /&gt;
isolate the concept of a time interval from the physical mechanism&lt;br /&gt;
(periodic clock interrupts) that measures time intervals. Although you can&lt;br /&gt;
specify a time interval down to the millisecond, the system does not&lt;br /&gt;
guarantee any such accuracy.&lt;br /&gt;
     On most hardware, OS/2 version 1.0 uses a periodic system clock&lt;br /&gt;
interrupt of 32 Hz (32 times a second). This means that OS/2 measures time&lt;br /&gt;
intervals with a quantum size of 31.25 milliseconds. As a result, any&lt;br /&gt;
timeout value is subject to quantization error of this order. For example,&lt;br /&gt;
if a process asks to sleep for 25 milliseconds, OS/2 knows that the request&lt;br /&gt;
was made at some time after the most recent clock tick, but it cannot tell&lt;br /&gt;
how long after, other than that less than 31.25 milliseconds had elapsed&lt;br /&gt;
between the previous clock tick and the sleep request. After the sleep&lt;br /&gt;
request is made, another clock tick occurs. Once again, OS/2 can't tell how&lt;br /&gt;
much time has elapsed since the sleep request and the new clock tick, other&lt;br /&gt;
than that it was less than 31.25 milliseconds. Lacking this knowledge, OS/2&lt;br /&gt;
uses a simple algorithm: At each clock tick, OS/2 decrements each timeout&lt;br /&gt;
value in the system by the clock tick interval (generally 31.25&lt;br /&gt;
milliseconds). Thus, our 25-millisecond sleep request may come back in 1&lt;br /&gt;
millisecond or less or in 31.25 milliseconds. A request to block for 33&lt;br /&gt;
milliseconds could come back in 32 milliseconds or in 62.5 milliseconds.&lt;br /&gt;
     Clearly, the OS/2 timer functions are intended for human-scale timing,&lt;br /&gt;
in which the 1/32-second quantization error is not noticeable, and not for&lt;br /&gt;
high-precision timing of fast events. Regardless of the resolution of the&lt;br /&gt;
timer, the system's preemptive scheduler prevents the implementation of&lt;br /&gt;
high-accuracy short-interval timing. Even if a timer system call were to&lt;br /&gt;
time out after a precise interval, the calling thread might not resume&lt;br /&gt;
execution immediately because a higher-priority thread might be executing&lt;br /&gt;
elsewhere.&lt;br /&gt;
     One form of OS/2 timer services is built into some system calls. For&lt;br /&gt;
example, all semaphore blocking calls support an argument that allows the&lt;br /&gt;
caller to specify a timeout value. When the specified time has elapsed, the&lt;br /&gt;
call returns with a &amp;quot;call timed out&amp;quot; error code. Some threads use this&lt;br /&gt;
facility to guard against being indefinitely locked out; if the semaphore&lt;br /&gt;
call times out, the thread can give up, display an error message, or try&lt;br /&gt;
another tactic. Other threads may use the facility expecting to be timed&lt;br /&gt;
out: They use the timeout facility to perform periodic tasks and use the&lt;br /&gt;
semaphore just as an emergency flag. Another thread in the system can&lt;br /&gt;
provide an emergency wakeup for the timer thread simply by clearing the&lt;br /&gt;
semaphore.&lt;br /&gt;
     Blocking on a semaphore merely to delay for a specific interval is&lt;br /&gt;
unnecessary; the DosSleep call allows a thread to block unconditionally for&lt;br /&gt;
an arbitrary length of time, subject, of course, to the timer's&lt;br /&gt;
quantization error.&lt;br /&gt;
6. And subject to the fact that if thread 1 is doing the DosSleeping&lt;br /&gt;
the sleep will be interrupted if a signal is taken.&lt;br /&gt;
6 DosSleep measures time intervals in a synchronous&lt;br /&gt;
fashion: The thread is held inside the operating system until the time&lt;br /&gt;
interval has elapsed. OS/2 provides an asynchronous timer service that&lt;br /&gt;
allows timing to take place in parallel with a thread's normal execution.&lt;br /&gt;
Specifically, the DosTimerAsync call is made with a timeout interval, such&lt;br /&gt;
as DosSleep, and also with the handle of a system semaphore.&lt;br /&gt;
7. Unlike most semaphore applications, the timer functions work&lt;br /&gt;
only with system semaphores. RAM semaphores may not be used&lt;br /&gt;
because of the difficulty in posting a RAM semaphore at interrupt&lt;br /&gt;
time; the RAM that contains the semaphore may be swapped out.&lt;br /&gt;
7 The&lt;br /&gt;
DosTimerAsync call returns immediately; later, when the time interval has&lt;br /&gt;
elapsed, the system semaphore is cleared. The process can poll the&lt;br /&gt;
semaphore to see if the time is up, and/or it can block on the semaphore to&lt;br /&gt;
wait for the time to elapse. Of course, if a process contains multiple&lt;br /&gt;
threads, some can poll and others can block.&lt;br /&gt;
     The DosTimerStart call is identical to the DosTimerAsync call except&lt;br /&gt;
that the semaphore is repeatedly cleared at the specified interval until a&lt;br /&gt;
corresponding DosTimerStop call is made. DosTimerStart clears the&lt;br /&gt;
semaphore; the process must set it again after it's been cleared.&lt;br /&gt;
     None of the above-mentioned facilities is completely accurate for&lt;br /&gt;
tracking the time of day or the amount of elapsed time. As we mentioned, if&lt;br /&gt;
a higher-priority thread is consuming enough CPU time, unpredictable delays&lt;br /&gt;
occur. Even DosTimerStart is susceptible to losing ticks because if the CPU&lt;br /&gt;
is unavailable for a long enough period the process won't be able to reset&lt;br /&gt;
the semaphore soon enough to prevent missing its next clearing.&lt;br /&gt;
Applications that want a precise measurement of elapsed time should use the&lt;br /&gt;
time values stored in the global infoseg. We also recommend that&lt;br /&gt;
applications with a critical need to manage timeouts, even if they are&lt;br /&gt;
executing in the lower-priority background, dedicate a thread to managing&lt;br /&gt;
the time-critical work and elevate that thread to a higher priority. This&lt;br /&gt;
will ensure that time-critical events aren't missed because a high-priority&lt;br /&gt;
foreground thread is going through a period of intensive CPU usage. Of&lt;br /&gt;
course, such an application must be designed so that the high-priority&lt;br /&gt;
timer event thread does not itself consume significant CPU time; it should&lt;br /&gt;
simply log the timer events and rely on its fellow normal-priority threads&lt;br /&gt;
to handle the major work involved.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==17  Device Drivers and Hard Errors==&lt;br /&gt;
&lt;br /&gt;
The multitasking nature of OS/2 makes OS/2 device drivers considerably more&lt;br /&gt;
complex than MS-DOS device drivers. Furthermore, whenever you have devices,&lt;br /&gt;
you must deal with device failures--the infamous hard errors. The handling&lt;br /&gt;
of hard errors in a multitasking environment is likewise considerably more&lt;br /&gt;
complex than it was under MS-DOS.&lt;br /&gt;
&lt;br /&gt;
===17.1  Device Drivers===&lt;br /&gt;
&lt;br /&gt;
This section gives an overview of device drivers, paying special attention&lt;br /&gt;
to their key architectural elements. Writing a device driver is a complex&lt;br /&gt;
task that must be undertaken with considerable care; a great many caveats&lt;br /&gt;
and &amp;quot;gotchas&amp;quot; lie in wait for the unsuspecting programmer. Many of these&lt;br /&gt;
&amp;quot;gotchas&amp;quot; are of that most favorite breed: ones that never show up in&lt;br /&gt;
testing, only in the field. This section is by no means an exhaustive&lt;br /&gt;
discussion of device drivers, nor is it a how-to guide. Study the OS/2&lt;br /&gt;
device driver reference documentation carefully before setting out to write&lt;br /&gt;
your own.&lt;br /&gt;
     In Chapter 2 I briefly discussed device independence and the role&lt;br /&gt;
that device drivers play in bringing it about. I said that a device driver&lt;br /&gt;
is a package of code that transforms I/O requests made in standard, device-&lt;br /&gt;
independent fashion into the operations necessary to make a specific piece&lt;br /&gt;
of hardware fulfill that request. A device driver takes data and status&lt;br /&gt;
information from the hardware, in the hardware-specific format, and&lt;br /&gt;
massages that information into the form that the operating system expects&lt;br /&gt;
to receive.&lt;br /&gt;
     The device driver architecture has two key elements. First, each&lt;br /&gt;
hardware device has its own device driver to hide the specific details of&lt;br /&gt;
the device from the operating system. Second, device drivers are not hard-&lt;br /&gt;
wired into the operating system when it is manufactured; they are&lt;br /&gt;
dynamically installed at boot time. This second point is the interesting&lt;br /&gt;
one. If all device drivers were hard-wired into OS/2, the technique of&lt;br /&gt;
encapsulating device-dependent code into specific packages would be good&lt;br /&gt;
engineering practice but of little interest to the user. OS/2 would run&lt;br /&gt;
only on a system configured with a certain magic set of peripheral devices.&lt;br /&gt;
But because device drivers are dynamically installable at boot time, OS/2&lt;br /&gt;
can work with a variety of devices, even ones that didn't exist when OS/2&lt;br /&gt;
was written, as long as a proper device driver for that device is installed&lt;br /&gt;
at boot time. Note that device drivers can be installed only at boot time;&lt;br /&gt;
they cannot be installed after the system has completed booting up. This is&lt;br /&gt;
because in a future secure environment the ability to dynamically install a&lt;br /&gt;
device driver would give any application the ability to violate system&lt;br /&gt;
security.&lt;br /&gt;
     Saying that device drivers merely translate between the operating&lt;br /&gt;
system and the device is a bit of oversimplification; in reality, they are&lt;br /&gt;
responsible for encapsulating, or owning, nearly all device-specific&lt;br /&gt;
knowledge about the device. Device drivers service the interrupts that&lt;br /&gt;
their devices generate, and they work at task time (that is, at&lt;br /&gt;
noninterrupt time). If a device monitor is necessary for a device, the&lt;br /&gt;
device driver writer decides that and provides the necessary support. If an&lt;br /&gt;
application needs direct access to a device's I/O ports or to its special&lt;br /&gt;
mapped memory,&lt;br /&gt;
1. For example, the display memory of a CGA or an EGA card.&lt;br /&gt;
1 the driver offers those services to processes. The device&lt;br /&gt;
driver also knows whether multiple processes should simultaneously use the&lt;br /&gt;
device and either allows or disallows this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.1  Device Drivers and OS/2 Communication&lt;br /&gt;
Because device drivers need to call and be called by OS/2 efficiently and&lt;br /&gt;
because device drivers must handle hardware interrupts efficiently, they&lt;br /&gt;
must run at ring 0. This means that device drivers must be trusted and must&lt;br /&gt;
be trustworthy. A flaky device driver--or worse, a malicious one--can do&lt;br /&gt;
unlimited and nearly untraceable damage to any application or data file in&lt;br /&gt;
the system.&lt;br /&gt;
     OS/2 can easily call a device driver. Because OS/2 loaded the device&lt;br /&gt;
driver into memory, it knows the address of its entry point and can call it&lt;br /&gt;
directly. For the device driver to call OS/2 is trickier because the driver&lt;br /&gt;
doesn't know the memory locations that OS/2 occupies nor does it have any&lt;br /&gt;
control over the memory descriptor tables (LDT and GDT). When the device&lt;br /&gt;
driver is initialized, OS/2 supplies the device driver with the address of&lt;br /&gt;
the OS/2 DevHlp entry point. Device drivers call this address to access a&lt;br /&gt;
variety of OS/2 services, called DevHlp services. The OS/2 DevHlp address&lt;br /&gt;
references a GDT selector so that the DevHlp address is valid at all times--&lt;br /&gt;
in protected mode, in real mode,&lt;br /&gt;
2. A GDT selector cannot literally be valid in real mode because the&lt;br /&gt;
GDT is not in use. OS/2 uses a technique called tiling so that the&lt;br /&gt;
selector, when used as a segment address in real mode, addresses&lt;br /&gt;
the same physical memory as does the protect mode segment.&lt;br /&gt;
2 at interrupt time, and during device&lt;br /&gt;
driver initialization. Some DevHlp functions are only valid in certain&lt;br /&gt;
modes, but the DevHlp facility is always available.&lt;br /&gt;
     Why don't device drivers simply use dynamic links to access OS/2&lt;br /&gt;
services, the way that applications do? The OS/2 kernel dynlink interface&lt;br /&gt;
is designed for processes running in user mode, at ring 3, to call the ring&lt;br /&gt;
0 kernel. In other words, it's designed for outsiders to call in, but&lt;br /&gt;
device drivers are already inside. They run at ring 0, in kernel mode, and&lt;br /&gt;
at interrupt time. One, of course, could kludge things so that device&lt;br /&gt;
drivers make dynlink calls, and then special code at those OS/2 entry&lt;br /&gt;
points would recognize a device driver request and do all the special&lt;br /&gt;
handling. But every system call from a normal application would be slowed&lt;br /&gt;
by this extra code, and every service call from a device driver would&lt;br /&gt;
likewise be slowed. As a result, device drivers have their own private,&lt;br /&gt;
high-efficiency &amp;quot;backdoor&amp;quot; entry into OS/2. Figure 17-1 illustrates&lt;br /&gt;
the call linkages between OS/2 and a device driver. OS/2 calls only one&lt;br /&gt;
entry point in the device driver, providing a function code that the device&lt;br /&gt;
driver uses to address a dispatch table. OS/2 learns the address of&lt;br /&gt;
thisentry point when it loads the device driver. The device driver in turn&lt;br /&gt;
calls only one OS/2 address, the DevHlp entry point. It also supplies a&lt;br /&gt;
function code that is used to address a dispatch table. The device driver&lt;br /&gt;
is told this address when it receives its initialize call from OS/2. Not&lt;br /&gt;
shown is the device driver's interrupt entry point.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
           OS/2                                   Device driver&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Far call (FCN)   ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³                        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄ¿    ³  Function              ³&lt;br /&gt;
³               DevHlp   ³             ³    ³   Table                ³&lt;br /&gt;
³         Function Table ³             ³    ³  ÚÄÄÄÄÄÄ¿  ÚÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄ¿  ÚÄÄÄÄÄÄ¿  ³             ÀÄÄÄÄÅÄ�³      ÃÄÄÙ           ³&lt;br /&gt;
³           ÀÄÄ´      ³�ÄÅÄÄÄÄÄÄÄÄÄÄ¿       ³  ³      ÃÄÄÄÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄÄÄÄ´      ³  ³ DevHlp   ³       ³  ³      ÃÄÄ¿           ³&lt;br /&gt;
³           ÚÄÄ´      ³  ³ address  ³       ³  ÀÄÄÄÄÄÄÙ  ÀÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄÙ  ÀÄÄÄÄÄÄÙ  ³          ³       ³                        ³&lt;br /&gt;
³                        ³          ÀÄÄÄÄÄÄÄ´                        ³&lt;br /&gt;
³                        ³       Far call   ³                        ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ     (DevHlp FCN) ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 17-1.  Device driver call linkages.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.2  Device Driver Programming Model&lt;br /&gt;
The programming model for device drivers under MS-DOS is simple. Device&lt;br /&gt;
drivers are called to perform a function, and they return when that&lt;br /&gt;
function is complete or they encounter an unrecoverable error. If the&lt;br /&gt;
device is interrupt driven, the CPU hangs in a loop inside the device&lt;br /&gt;
driver while waiting for the driver's interrupt handler to be entered; when&lt;br /&gt;
the operation is complete, the interrupt handler sets a private flag to&lt;br /&gt;
break the task-time CPU out of its wait loop.&lt;br /&gt;
     The OS/2 device driver model is considerably more complicated because&lt;br /&gt;
OS/2 is a multitasking system. Even if the thread that calls the device&lt;br /&gt;
driver with a request has nothing better to do than wait for the operation&lt;br /&gt;
to complete, other threads in the system could make good use of the time.&lt;br /&gt;
Another effect of the OS/2 multitasking architecture is that two or more&lt;br /&gt;
threads can simultaneously call a device driver. To explore this last issue&lt;br /&gt;
fully, we'll digress for a moment and discuss the OS/2 internal execution&lt;br /&gt;
model.&lt;br /&gt;
     By design, OS/2 acts more like a subroutine library than like a&lt;br /&gt;
process. The only dispatchable entities in the system are threads, and all&lt;br /&gt;
threads belong to processes. When a process's thread calls OS/2, that&lt;br /&gt;
thread executes OS/2's code.&lt;br /&gt;
3. The few exceptions to this don't affect the issues discussed here.&lt;br /&gt;
3 It's like walking up to the counter at a&lt;br /&gt;
fast-food restaurant and placing your order. You then slip on an apron, run&lt;br /&gt;
around behind the counter, and prepare your own order. When the food is&lt;br /&gt;
ready, you take off the apron, run back around to the front of the counter,&lt;br /&gt;
and pick up the food. The counter represents the boundary between ring 0&lt;br /&gt;
(kernel mode) and ring 3 (application mode), and the apron represents the&lt;br /&gt;
privileged state necessary to work behind the counter.&lt;br /&gt;
     Naturally, OS/2 is reentrant; at any one time many threads are&lt;br /&gt;
executing inside OS/2, but each is doing work for only one process--the&lt;br /&gt;
process to whom that thread belongs. Behind the counter are several folks&lt;br /&gt;
wearing aprons, but each is working only on his or her own order. This&lt;br /&gt;
approach simplifies the internals of OS/2: Each instance of a section of&lt;br /&gt;
code is doing only one thing for one client. If a section of code must wait&lt;br /&gt;
for something, it simply blocks (analogous to a semaphore wait) as long as&lt;br /&gt;
it has to and resumes when it can. Threads within the kernel that are&lt;br /&gt;
competing for a single resource do so by internal semaphores, and they are&lt;br /&gt;
given access to these semaphores on a priority basis, just as they are when&lt;br /&gt;
executing in application mode.&lt;br /&gt;
     OS/2 makes little distinction between a thread running inside the&lt;br /&gt;
kernel and one running outside, in the application's code itself: The&lt;br /&gt;
process's LDT remains valid, and the thread, while inside the kernel, can&lt;br /&gt;
access any memory location that was accessible to the process in&lt;br /&gt;
application mode, in addition to being able to access restricted ring 0&lt;br /&gt;
memory. The only distinction the scheduler makes between threads inside and&lt;br /&gt;
those outside the kernel is that the scheduler never preempts a thread&lt;br /&gt;
running inside the kernel. This greatly relaxes the rigor with which kernel&lt;br /&gt;
code needs to protect its critical sections: When the CPU is executing&lt;br /&gt;
kernel code, the scheduler performs a context switch only when the CPU&lt;br /&gt;
voluntarily blocks itself. As long as kernel code doesn't block itself,&lt;br /&gt;
wait on a semaphore, or call a subroutine that waits on a semaphore, it&lt;br /&gt;
needn't worry about any other thread entering its critical section.&lt;br /&gt;
4. Although hardware interrupts still occur; any critical section&lt;br /&gt;
modified at interrupt time is still vulnerable.&lt;br /&gt;
4&lt;br /&gt;
     When OS/2 calls a device driver at task time, it does so with the&lt;br /&gt;
thread that was executing OS/2--the thread that belongs to the client&lt;br /&gt;
process and that made the original service call. Thus, the task-time part&lt;br /&gt;
of a device driver is running, at ring 0, in the client's context. The&lt;br /&gt;
client's LDT is active, all the client's addresses are active, and the&lt;br /&gt;
device driver is immune from being preempted by other task-time threads&lt;br /&gt;
(but not by interrupt service) until it blocks via a DevHlp&lt;br /&gt;
function or returns to OS/2.&lt;br /&gt;
     OS/2 device drivers are divided into two general categories: those for&lt;br /&gt;
character mode devices and those for block mode devices. This terminology&lt;br /&gt;
is traditional, but don't take it too literally because character mode&lt;br /&gt;
operations can be done to block mode devices. The actual distinction is&lt;br /&gt;
that character mode device drivers do I/O synchronously; that is, they do&lt;br /&gt;
operations in first in, first out order. Block mode device drivers can be&lt;br /&gt;
asynchronous; they can perform I/O requests in an order different from the&lt;br /&gt;
one in which they received them. A traditional serial character device,&lt;br /&gt;
such as a printer, must not change the order of its requests; doing so&lt;br /&gt;
scrambles the output. A block device, such as a disk, can reverse the order&lt;br /&gt;
of two sector reads without problems.&lt;br /&gt;
     Figure 17-2 shows an algorithm for character mode device drivers.&lt;br /&gt;
5. See the device driver reference manual for more details.&lt;br /&gt;
5&lt;br /&gt;
OS/2 calls the device driver with a request, as shown at the top of the&lt;br /&gt;
figure. If the device driver is busy with another request, the new&lt;br /&gt;
requesting thread should block on a RAM semaphore until the device is&lt;br /&gt;
available. When the device is free, the task-time thread does the requested&lt;br /&gt;
operation. Sometimes the work can be done at task time (such as an IOCTL&lt;br /&gt;
call asking about the number of characters in an input buffer), but more&lt;br /&gt;
frequently the task-time thread initiates the operation, and the work is&lt;br /&gt;
completed at interrupt time. If the task-time thread needs to wait for&lt;br /&gt;
interrupt service, it should block on a RAM semaphore&lt;br /&gt;
6. Located in the device driver data area.&lt;br /&gt;
6 that the interrupt-&lt;br /&gt;
time code will clear. When the last associated device interrupt takes place&lt;br /&gt;
and the operation is complete, the interrupt code releases the RAM&lt;br /&gt;
semaphore. The task-time thread awakens and returns to OS/2 with the status&lt;br /&gt;
bits properly set in the request block.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       OS/2 code            Device driver           Device interrupt&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Issue request to&lt;br /&gt;
device driver. ÄÄÄÄÄÄ� Block until device is&lt;br /&gt;
                       available.&lt;br /&gt;
&lt;br /&gt;
                       Peform request. Block&lt;br /&gt;
                       until interrupts&lt;br /&gt;
                       complete if necessary.  ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ Device interrupt (if any)&lt;br /&gt;
                                               ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ Perform next step in I/O&lt;br /&gt;
                                               ³ operation.&lt;br /&gt;
                                               ³&lt;br /&gt;
                                               ³ When done, use DevHlp&lt;br /&gt;
                                               ³ ProcRun to unblock task&lt;br /&gt;
                                               ³ time thread.&lt;br /&gt;
                                               ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ End of interrupt&lt;br /&gt;
                       Complete request and    ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                       return to OS/2&lt;br /&gt;
Request now complete.&lt;br /&gt;
Continue.&lt;br /&gt;
&lt;br /&gt;
Figure 17-2.  Simplified character mode device driver model.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Figure 17-3 shows an algorithm for block devices. The general outline&lt;br /&gt;
is the same as that for character devices but more complicated because of&lt;br /&gt;
the asynchronous nature of random-access devices. Because requests can be&lt;br /&gt;
processed in any order, most block device drivers maintain an internal work&lt;br /&gt;
queue to which they add each new request. They usually use a special DevHlp&lt;br /&gt;
function to sort the work queue in sector number order so that disk head&lt;br /&gt;
motion is minimized.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       OS/2 code             Device driver           Device interrupt&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Issue request to&lt;br /&gt;
device driver. ÄÄÄÄÄÄÄ� Add request to device&lt;br /&gt;
                        request list. Fire up&lt;br /&gt;
                        device if not active.&lt;br /&gt;
&lt;br /&gt;
                        Return to OS/2 with&lt;br /&gt;
                        DONE clear. ÄÄ¿&lt;br /&gt;
           ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
           �                                    ³ Device interrupt&lt;br /&gt;
OS/2 thread(s) block on                         ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
incomplete request when                         ³ Perform next step in&lt;br /&gt;
they can proceed no                             ³ I/O operation.&lt;br /&gt;
further without the                             ³&lt;br /&gt;
data.                                           ³ If request is done&lt;br /&gt;
                                                ³    Pull request from&lt;br /&gt;
                                                ³    list&lt;br /&gt;
Any threads blocked ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
on this request are                             ³    Use DevHlp DevDone&lt;br /&gt;
awakened.                                       ³    to tell OS/2 that&lt;br /&gt;
                                                ³    the I/O is done.&lt;br /&gt;
                                                ³&lt;br /&gt;
                                                ³    If further work on&lt;br /&gt;
                                                ³    queue, start work&lt;br /&gt;
                                                ³    on next item.&lt;br /&gt;
                                                ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                                ³ End of interrupt&lt;br /&gt;
Request now complete.                           ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Continue.&lt;br /&gt;
&lt;br /&gt;
Figure 17-3.  Block mode device driver model.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The easiest way to understand this figure is to think of a block mode&lt;br /&gt;
device driver as being made up of N threads: one interrupt-time thread does&lt;br /&gt;
the actual work, and all the others are task-time threads that queue the&lt;br /&gt;
work. As each request comes into the driver via a task-time thread, that&lt;br /&gt;
thread simply puts the request on the queue and returns to OS/2. Later, the&lt;br /&gt;
device driver's interrupt service routine calls the DevHlp DevDone function&lt;br /&gt;
to tell OS/2 that the operation is complete. Returning to OS/2 with the&lt;br /&gt;
operation incomplete is permissible because the request block status bits&lt;br /&gt;
show that the operation is incomplete.&lt;br /&gt;
     Sometimes, OS/2 needs to wait for the operation (such as a read from a&lt;br /&gt;
directory); so when the device driver returns with the operation&lt;br /&gt;
incomplete, OS/2 simply waits for it to finish. In other circumstances,&lt;br /&gt;
such as flushing the cache buffers, OS/2 may not wait around for the&lt;br /&gt;
operation to complete. It may go on about its business or even issue a new&lt;br /&gt;
request to the driver, using, of course, a new request block because the&lt;br /&gt;
old one is still in use. This design gives the system a great deal of&lt;br /&gt;
parallelism and thereby improves throughput.&lt;br /&gt;
     I said that a block mode device driver consists of several task-time&lt;br /&gt;
threads and one interrupt-time thread. The term interrupt-time thread is a&lt;br /&gt;
bit misleading, however, because it's not a true thread managed by the&lt;br /&gt;
scheduler but a pseudo thread created by the hardware interrupt mechanism.&lt;br /&gt;
For example, a disk device driver has four requests queued up, and the READ&lt;br /&gt;
operation for the first request is in progress. When it completes, the&lt;br /&gt;
driver's interrupt service routine is entered by the hardware interrupt&lt;br /&gt;
generated by the disk controller. That interrupt-time thread, executing the&lt;br /&gt;
driver's interrupt service routine, checks the status, verifies that all is&lt;br /&gt;
OK, and calls various DevHlp routines to post the request as complete and&lt;br /&gt;
to remove it from the queue. It then notes that requests remain on the&lt;br /&gt;
queue and starts work on the next one, which involves a seek operation. The&lt;br /&gt;
driver's interrupt-time code issues the seek command to the hardware and&lt;br /&gt;
then returns from the interrupt. When the disk stops seeking, another&lt;br /&gt;
interrupt is generated; the interrupt-time code notes the successful seek,&lt;br /&gt;
issues the read or write operation to the controller, and exits.&lt;br /&gt;
     As you can see, the repeated activation of the device driver's&lt;br /&gt;
interrupt service routine is much like a thread, but with two major&lt;br /&gt;
differences. First, every time an interrupt service routine is entered, it&lt;br /&gt;
has a fresh stack. A task-time thread has register contents and a stack&lt;br /&gt;
that are preserved by the system; neither is preserved for an interrupt&lt;br /&gt;
service routine between interrupts. A task-time thread keeps track of what&lt;br /&gt;
it was doing by its CS:IP address, its register contents, and its stack&lt;br /&gt;
contents. An interrupt service routine must keep track of its work by means&lt;br /&gt;
of static values stored in the device driver's data segment. Typically,&lt;br /&gt;
interrupt service routines implement a state machine and maintain the&lt;br /&gt;
current state in the driver's data segment. Second, a true thread remains&lt;br /&gt;
in existence until explicitly terminated; an interrupt service thread is an&lt;br /&gt;
illusion of a thread that is maintained by repeated interrupts. If any one&lt;br /&gt;
execution of the interrupt service routine fails to give the hardware a&lt;br /&gt;
command that will generate another interrupt, the interrupt pseudo thread&lt;br /&gt;
will no longer exist after the interrupt service routine returns.&lt;br /&gt;
     The block mode driver algorithm description left out a detail. If the&lt;br /&gt;
disk is idle when a new request comes in, the request is put on the queue,&lt;br /&gt;
but there is no interrupt-time pseudo thread to service the request. Thus,&lt;br /&gt;
both the task-time and interrupt-time parts of a device driver must be able&lt;br /&gt;
to initiate an operation. The recommended approach is to use a software&lt;br /&gt;
state machine to control the hardware and to ensure that the state machine,&lt;br /&gt;
at least the start operation part of it, is callable at both task and&lt;br /&gt;
interrupt time. The algorithm above is then modified so that after the&lt;br /&gt;
task-time part of a block device driver puts its request on the driver's&lt;br /&gt;
internal queue it verifies that the device (or state machine or interrupt&lt;br /&gt;
pseudo thread) is active. If the device has been idle, the task-time thread&lt;br /&gt;
in the device driver initiates the operation by calling the initial state&lt;br /&gt;
of the state machine; it then returns to OS/2. This primes the pump;&lt;br /&gt;
the interrupt pseudo thread now continues to run until the request queue is&lt;br /&gt;
empty.&lt;br /&gt;
     Figure 17-4 shows an overview of the OS/2 device driver architecture.&lt;br /&gt;
Each device driver consists of a task-time part, an interrupt-time part (if&lt;br /&gt;
the device generates interrupts), and the start-operation code that is&lt;br /&gt;
executed in either mode. The driver's data area typically contains state&lt;br /&gt;
information, flags, and semaphores to handle communication between the&lt;br /&gt;
task-time part and the interrupt. Figure 17-4 also shows that the task-time&lt;br /&gt;
part of a device driver can have multiple instances. It can be called by&lt;br /&gt;
several threads at the same time, just as a shared dynlink library routine&lt;br /&gt;
might be. Unlike a dynlink library, a device driver has no instance data&lt;br /&gt;
segment; the device driver's data segment is a global data segment,&lt;br /&gt;
accessible to all execution instances of the device driver's task-time&lt;br /&gt;
component. Just as a dynlink package uses semaphores to protect critical&lt;br /&gt;
data areas in its global data segment, a device driver uses semaphores to&lt;br /&gt;
protect the critical data values in its data segment. Unlike dynlink&lt;br /&gt;
routines, the device driver has an additional special thread--the interrupt&lt;br /&gt;
service thread. A device driver can't protect critical sections that are&lt;br /&gt;
accessed at interrupt time by using semaphores because an interrupt service&lt;br /&gt;
thread cannot block. It must complete the interrupt service and exit--&lt;br /&gt;
quickly, at that. When you write device drivers, you must minimize the&lt;br /&gt;
critical sections that are entered by the interrupt service thread and&lt;br /&gt;
protect them via the CLI/STI instruction sequence.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
         ³              &amp;quot;D&amp;quot;³&lt;br /&gt;
      ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ³                ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
      ³              &amp;quot;C&amp;quot;³  ³                ³   Device driver   ³&lt;br /&gt;
   ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ÃÄÄÙ                ³ interrupt service ³&lt;br /&gt;
   ³              &amp;quot;B&amp;quot;³  ³                   ÀÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ÃÄÄÙ                     ³           ³&lt;br /&gt;
³  Instance &amp;quot;A&amp;quot;   ³  ³                        ³           ³&lt;br /&gt;
³  device driver  ÃÄÄÙ                ÚÄÄÄÄÄÄÄ�ÄÄÄ¿       ³&lt;br /&gt;
³  task-time code ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³ Start I/O ³       ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ                   ³   code    ³       ³&lt;br /&gt;
         ³                            ÀÄÄÄÄÄÂÄÄÄÄÄÙ       ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                         ÚÄÄÄÄÄÄÄÄ�ÄÄÄÄÄÄ¿      ³&lt;br /&gt;
         ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³ Device driver ³�ÄÄÄÄÄÙ&lt;br /&gt;
                                   ³     data      ³&lt;br /&gt;
                                   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 17-4.  Device driver code structure.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.3  Device Management&lt;br /&gt;
Device drivers do more than talk to the device; they also manage it for the&lt;br /&gt;
system. Device drivers are called each time a process opens or closes a&lt;br /&gt;
device; device drivers determine whether a device can be used by more than&lt;br /&gt;
one process simultaneously. Likewise, device drivers receive device monitor&lt;br /&gt;
requests from applications via the IOCTL interface and, when appropriate,&lt;br /&gt;
call OS/2 via the DevHlp interface to perform the bulk of the monitor work.&lt;br /&gt;
Finally, device drivers can grant processes access to the device's I/O&lt;br /&gt;
ports, to the device's mapped memory, and/or to special control areas in&lt;br /&gt;
the device driver's data area itself. Once again, processes ask for these&lt;br /&gt;
features via IOCTL; the device driver grants the requests via a DevHlp&lt;br /&gt;
dialog with OS/2. Some device drivers are degenerate; they don't actually&lt;br /&gt;
transfer data but exist solely to manage these other tasks. The screen&lt;br /&gt;
device driver is an example. Screen data is always written directly to the&lt;br /&gt;
display buffer by VIO, the application, or the presentation manager. The&lt;br /&gt;
screen device driver exists to grant direct access, manage screen groups,&lt;br /&gt;
and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.4  Dual Mode&lt;br /&gt;
The last key architectural feature of device drivers is that they are&lt;br /&gt;
written in dual mode: The driver code, both task time and interrupt time,&lt;br /&gt;
must be able to execute in protected mode and real mode. The process of&lt;br /&gt;
mode switching between protected mode and real mode is quite slow--about&lt;br /&gt;
800 microseconds. If we decreed that all device drivers run only in&lt;br /&gt;
protected mode and that service interrupts run only in protected mode, a&lt;br /&gt;
disk request from a real mode program might require six or more mode&lt;br /&gt;
switches--one for the request, and five for the interrupts--for a penalty&lt;br /&gt;
of almost 5 milliseconds. Consequently, device drivers must run in whatever&lt;br /&gt;
mode the CPU is in when the request comes along or the interrupt arrives.&lt;br /&gt;
     At first glance, this seems easy enough: As long as the device driver&lt;br /&gt;
refrains from computing its own segment selectors, it can execute in either&lt;br /&gt;
mode. The catch is that OS/2 may switch between modes at every call and/or&lt;br /&gt;
interrupt, and the addresses of code and data items are different in each&lt;br /&gt;
mode. A device driver might be called in protected mode with an address in&lt;br /&gt;
the client process's address space. When the &amp;quot;data ready&amp;quot; interrupt&lt;br /&gt;
arrives, however, the CPU may be running in real mode, and that client's&lt;br /&gt;
address is no longer valid--for two reasons. One, the segment selector part&lt;br /&gt;
of a memory address has a different meaning in real mode than it does in&lt;br /&gt;
protected mode; and, two, the client's selector was in the LDT, and the LDT&lt;br /&gt;
is invalid at interrupt time.&lt;br /&gt;
7. See the device driver reference manual for more details.&lt;br /&gt;
7 OS/2 helps device drivers deal with&lt;br /&gt;
addressing in a dual mode environment in three ways:&lt;br /&gt;
&lt;br /&gt;
     1.  Some addresses are the same in both modes and in either protected&lt;br /&gt;
         mode or real mode. The DevHlp entry point, the global infoseg&lt;br /&gt;
         address, the request packet address, and any addresses returned&lt;br /&gt;
         via the DevHlp GetDosVar function are valid at all times and in&lt;br /&gt;
         both modes.&lt;br /&gt;
&lt;br /&gt;
     2.  Although the segment selector value for the device driver's code&lt;br /&gt;
         and data segments is different in each mode, OS/2 loads the proper&lt;br /&gt;
         values into CS and DS before it calls the device driver's task-&lt;br /&gt;
         time or interrupt-time entry points. As long as a device driver is&lt;br /&gt;
         careful not to &amp;quot;remember&amp;quot; and reuse these values, it won't notice&lt;br /&gt;
         that they (possibly) change at every call.&lt;br /&gt;
&lt;br /&gt;
     3.  OS/2 provides a variety of DevHlp functions that allow a device&lt;br /&gt;
         driver to convert a selector:offset pair into a physical address&lt;br /&gt;
         and then later convert this physical address back into a&lt;br /&gt;
         selector:offset pair that is valid at that particular time. This&lt;br /&gt;
         allows device drivers to convert addresses that are outside their&lt;br /&gt;
         own segments into physical addresses and then, upon each task-time&lt;br /&gt;
         or interrupt-time call to the driver, convert that physical&lt;br /&gt;
         address back into one that is usable in the current mode. This&lt;br /&gt;
         avoids the problem of recording a selector:offset pair in protect&lt;br /&gt;
         mode and then trying to use it as a segment:offset pair in real&lt;br /&gt;
         mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2  Hard Errors&lt;br /&gt;
&lt;br /&gt;
Sometimes the system encounters an error that it can neither ignore nor&lt;br /&gt;
correct but which the user can correct. A classic example is the user&lt;br /&gt;
leaving ajar the door to the floppy drive; the system can do nothing to&lt;br /&gt;
access that floppy disk until someone closes the door. Such an error is&lt;br /&gt;
called a hard error. The term originated to describe an error that won't go&lt;br /&gt;
away when the operation is retried, but it also aptly describes the effort&lt;br /&gt;
involved in the design of OS/2 to deal with such errors.&lt;br /&gt;
     The manner in which MS-DOS handles hard errors is straightforward. In&lt;br /&gt;
our drive door example, MS-DOS discovers the problem when it is deep inside&lt;br /&gt;
the bowels of the system, communicating with the disk driver. The driver&lt;br /&gt;
reports the problem, and MS-DOS displays some text on the screen--the&lt;br /&gt;
infamous &amp;quot;Abort, Retry, Ignore?&amp;quot; message. Typically, the user fixes the&lt;br /&gt;
problem and replies; MS-DOS then takes the action specified by the user,&lt;br /&gt;
finishes its work, and returns to the application. Often, applications&lt;br /&gt;
didn't want the system to handle hard errors automatically. Perhaps they&lt;br /&gt;
were concerned about data integrity and wanted to be aware of a disk&lt;br /&gt;
writing problem, or they wanted to prevent the user from specifying&lt;br /&gt;
&amp;quot;Ignore,&amp;quot;&lt;br /&gt;
8. This response is classic. Sophisticated users understand the likely&lt;br /&gt;
consequences of such a reply, but most users would interpret &amp;quot;Ignore&amp;quot;&lt;br /&gt;
as &amp;quot;Make the problem go away&amp;quot;--an apparently ideal solution!&lt;br /&gt;
8 or they didn't want MS-DOS to write over their screen display&lt;br /&gt;
without their knowing. To handle these situations, MS-DOS lets applications&lt;br /&gt;
store the address of a hard error handler in the INT 24 vector; if a&lt;br /&gt;
handler is present, MS-DOS calls it instead of its own handler.&lt;br /&gt;
     The system is in an unusual state while processing an MS-DOS hard&lt;br /&gt;
error. The application originally calls MS-DOS via the INT 21 vector. MS-&lt;br /&gt;
DOS then calls several levels deep within itself, whereupon an internal MS-&lt;br /&gt;
DOS routine calls the hard error handler back in the application. Because&lt;br /&gt;
MS-DOS is not generally reentrant, the application cannot recall MS-DOS via&lt;br /&gt;
INT 21 at this point; doing so would mean that it has called MS-DOS twice&lt;br /&gt;
at the same time. The application probably needs to do screen and keyboard&lt;br /&gt;
I/O when handling the hard error, so MS-DOS was made partially reentrant.&lt;br /&gt;
The original call involves disk I/O, so MS-DOS can be reentered via a&lt;br /&gt;
screen/keyboard I/O call without problem.&lt;br /&gt;
     Several problems prevented us from adopting a similar scheme for OS/2.&lt;br /&gt;
First, unlike the single-tasking MS-DOS, OS/2 cannot suspend operations&lt;br /&gt;
while the operating system calls an application--a call that might not&lt;br /&gt;
return for a long time. Second, major technical and security problems are&lt;br /&gt;
involved with calling from ring 0 (the privileged kernel mode) to ring 3&lt;br /&gt;
(the application mode). Also, in the MS-DOS environment, deciding which&lt;br /&gt;
process was responsible for the operation that triggered the hard error is&lt;br /&gt;
easy: Only one application is running. OS/2 may have a hard time&lt;br /&gt;
determining which process to alert because more than one process may have&lt;br /&gt;
caused a disk FAT sector or a disk directory to be edited. The improved&lt;br /&gt;
buffering techniques employed by OS/2 may cause a hard error to occur at a&lt;br /&gt;
time when no process is doing any I/O. Finally, even if we solve all these&lt;br /&gt;
problems, the application that triggers the hard error may be running in a&lt;br /&gt;
background screen group and be unable to display a message or use the&lt;br /&gt;
keyboard. Even if the application is in the foreground screen group, it&lt;br /&gt;
can't use the screen and keyboard if it's not the process currently&lt;br /&gt;
controlling them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2.1  The Hard Error Daemon&lt;br /&gt;
This last problem yields a clue to the solution. OS/2 supports multiple&lt;br /&gt;
screen groups, and its screen group mechanism manages multiple simultaneous&lt;br /&gt;
use of the screen and the keyboard, keeping the current users--one in each&lt;br /&gt;
screen group--isolated from one another. Clearly, we need to use screen&lt;br /&gt;
groups to allow a hard error dialog to be completed with the user without&lt;br /&gt;
interfering with the current foreground application. Doing so solves the&lt;br /&gt;
problem of writing on another application's screen image and therefore&lt;br /&gt;
removes most of the need for notifying an application that a hard error has&lt;br /&gt;
occurred.&lt;br /&gt;
     Specifically, OS/2 always has running a process called the hard error&lt;br /&gt;
daemon. When a hard error occurs, OS/2 doesn't attempt to figure out which&lt;br /&gt;
process caused it; instead, it notifies the hard error daemon. The hard&lt;br /&gt;
error daemon performs a special form of screen group switch to the reserved&lt;br /&gt;
hard error screen group and then displays its message and reads its input.&lt;br /&gt;
Because the hard error daemon is the only process in this screen group,&lt;br /&gt;
screen and keyboard usage do not conflict. The previous foreground process&lt;br /&gt;
is now temporarily in the background; the screen group mechanism keeps it&lt;br /&gt;
at bay.&lt;br /&gt;
     Meanwhile, the process thread that encountered the hard error in the&lt;br /&gt;
kernel is blocked there, waiting for the hard error daemon to get a&lt;br /&gt;
response from the user. The thread that handles the hard error is never the&lt;br /&gt;
thread that caused the hard error, and the kernel is already fully&lt;br /&gt;
reentrant for different threads; so the hard error daemon thread is free to&lt;br /&gt;
call OS/2 at will. When the user corrects the problem and responds to the&lt;br /&gt;
hard error daemon, the hard error daemon sends the response back to the&lt;br /&gt;
kernel, which allows the thread that encountered the error to take the&lt;br /&gt;
specified action. That thread either retries the operation or produces an&lt;br /&gt;
error code; the hard error daemon returns the system to the original&lt;br /&gt;
screen group. The screen group code then does its usual trick of&lt;br /&gt;
restoring the screen image to its previous state. Figure 17-5 illustrates&lt;br /&gt;
the hard error handling sequence. A process thread encounters a hard error&lt;br /&gt;
while in the OS/2 kernel. The thread blocks at that point while the hard&lt;br /&gt;
error daemon's previously captured thread is released. The hard error&lt;br /&gt;
daemon performs a special modified screen switch at (1), displays its&lt;br /&gt;
message, gets the user's response, restores the application screen group at&lt;br /&gt;
(2), and reenters the OS/2 kernel. The response code is then passed to the&lt;br /&gt;
blocked application thread, which then resumes execution.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
             DosCall&lt;br /&gt;
   Process  ÄÄÄÄ¿                                        ÚÄÄÄÄÄÄÄÄ&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³  OS/2 kernel  ³                                        ³  Return   ³&lt;br /&gt;
³               ³ Hard                                   ³  to       ³&lt;br /&gt;
³               ³ error                                  ³  process  ³&lt;br /&gt;
³               ÀÄÄÄÄÄÄ´                      ÃÄÄÄ&amp;gt;Ä&amp;gt;ÄÄÄÄÙ           ³&lt;br /&gt;
³                                                &amp;lt; &amp;lt;                 ³&lt;br /&gt;
³                      ÃÄ¿                  ÚÄ´                      ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                         �                  �&lt;br /&gt;
                 returns ³                  ³ calls&lt;br /&gt;
                         ³                  ³&lt;br /&gt;
   Hard error daemon     ³ Display message  ³&lt;br /&gt;
                         ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                      (1)    Get response    (2)&lt;br /&gt;
 &lt;br /&gt;
                                    TimeÄÄÄÄÄÄ�&lt;br /&gt;
&lt;br /&gt;
Figure 17-5.  Hard error handling.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Although the most common cause of hard errors is a disk problem for&lt;br /&gt;
example, an open drive door or a medium error--other events that require&lt;br /&gt;
user intervention or user notification use the hard error mechanism. For&lt;br /&gt;
example, the volume management package (see 15.2 Media Volume Management)&lt;br /&gt;
uses the hard error mechanism to display its &amp;quot;Insert volume &amp;lt;name&amp;gt;&amp;quot;&lt;br /&gt;
messages. As I mentioned earlier, MS-DOS applications running in the&lt;br /&gt;
compatibility box can encounter problems, such as locked files, that they&lt;br /&gt;
can't understand. Rather than have these applications fail mysteriously,&lt;br /&gt;
OS/2 uses the hard error daemon mechanism to inform the user of the cause&lt;br /&gt;
of the real mode application's difficulties. Although the application&lt;br /&gt;
running in the compatibility box sees an operating system that acts like&lt;br /&gt;
MS-DOS, the operating system is actually OS/2. Because of this, hard errors&lt;br /&gt;
encountered by a real mode process are handled by an amalgam of the MS-DOS&lt;br /&gt;
INT 24 mechanism and the OS/2 hard error daemon. See Chapter 19, The 3X&lt;br /&gt;
Box.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2.2  Application Hard Error Handling&lt;br /&gt;
In some cases an application doesn't want the system to handle its hard&lt;br /&gt;
errors. For example, an application designed for unattended or remote&lt;br /&gt;
operation, such as a network server, may want to pass notification of hard&lt;br /&gt;
errors to a remote correspondent rather than hanging up forever with a&lt;br /&gt;
message on a screen that might not be read for hours. Another example is a&lt;br /&gt;
database program concerned about the integrity of its master file; it may&lt;br /&gt;
want to know about hard errors so that it can take some special action or&lt;br /&gt;
perhaps use an alternative master file on another device. OS/2 allows a&lt;br /&gt;
process to disable automatic hard error handling on a per file basis. Our&lt;br /&gt;
network example will want to disable hard error pop-ups for anything the&lt;br /&gt;
process does; our database example may want to disable hard error pop-ups&lt;br /&gt;
only for its master file, keeping their convenience for any other files&lt;br /&gt;
that it might access. When a hard error occurs on behalf of a process or a&lt;br /&gt;
handle that has hard error pop-ups disabled, OS/2 assumes that a FAIL&lt;br /&gt;
response was entered to a hypothetical hard error pop-up and returns to the&lt;br /&gt;
application with a special error code. The application must analyze the&lt;br /&gt;
code and take the necessary actions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==18  I/O Privilege Mechanism and Debugging/Ptrace==&lt;br /&gt;
&lt;br /&gt;
The earlier chapters of this book focused on the &amp;quot;captains and kings&amp;quot; of&lt;br /&gt;
the operating system world, the major architectural features. But like any&lt;br /&gt;
real world operating system, OS/2 contains a variety of miscellaneous&lt;br /&gt;
facilities that have to be there to get the work done. Although these&lt;br /&gt;
facilities may not be major elements in some architectural grand scheme,&lt;br /&gt;
they still have to obey the principles of the design religion. Two of them&lt;br /&gt;
are the I/O privilege mechanism and the debugging facility.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
18.1  I/O Privilege Mechanism&lt;br /&gt;
&lt;br /&gt;
Earlier I discussed the need for a mechanism that allows applications high-&lt;br /&gt;
speed direct access to devices. But the mechanism must control access in&lt;br /&gt;
such a way that the system's stability isn't jeopardized and in such a way&lt;br /&gt;
that applications don't fight over device control. OS/2 meets this&lt;br /&gt;
requirement with its I/O privilege mechanism. This facility allows a&lt;br /&gt;
process to ask a device driver for direct access to the device's I/O&lt;br /&gt;
ports and any dedicated or mapped memory locations it has. The I/O&lt;br /&gt;
privilege mechanism can be used directly by an application, which&lt;br /&gt;
necessarily makes it device dependent, or indirectly by a dynlink package.&lt;br /&gt;
The dynlink package can act as a kind of device driver; a new version can&lt;br /&gt;
be shipped with new hardware to maintain application compatibility. This&lt;br /&gt;
pseudo device driver is normally much faster than a true device driver&lt;br /&gt;
because of the customized procedural interface; not entering ring 0 and the&lt;br /&gt;
OS/2 kernel code saves much time.&lt;br /&gt;
     Unfortunately, this isn't a free lunch. Dynlink pseudo device drivers&lt;br /&gt;
can do everything that true device drivers can except handle interrupts.&lt;br /&gt;
Because hardware interrupts must be handled at ring 0, the handler must be&lt;br /&gt;
part of a true device driver. Frequently, a compromise is in order: Both a&lt;br /&gt;
dynlink package and a true device driver are provided. The true device&lt;br /&gt;
driver handles the interrupts, and the dynlink package does the rest of the&lt;br /&gt;
work. The two typically communicate via shared memory and/or private&lt;br /&gt;
IOCTLs. An example of such a compromise is the system KBD dynlink package.&lt;br /&gt;
The system VIO package doesn't need a device driver to handle interrupts&lt;br /&gt;
because the display device doesn't generate any.&lt;br /&gt;
     The two components in the OS/2 I/O access model are access to the&lt;br /&gt;
device's memory and access to its I/O ports. Granting and controlling&lt;br /&gt;
access to a device's mapped memory is easy because the 80286 protect mode&lt;br /&gt;
supports powerful memory management facilities. First, a process asks the&lt;br /&gt;
device driver for access to the device's memory, for example, to the memory&lt;br /&gt;
buffer of a CGA board. Typically, a dynlink package, rather than an&lt;br /&gt;
application, does this via the DosDevIOCtl call. If the device driver&lt;br /&gt;
approves the request, it asks OS/2 via the DevHlp interface to set up an&lt;br /&gt;
LDT memory descriptor to the proper physical memory locations. OS/2 returns&lt;br /&gt;
the resultant selector to the device driver, which returns it to the&lt;br /&gt;
calling process. This technique isn't limited to memory-mapped device&lt;br /&gt;
memory; device drivers can use it to allow their companion dynlink packages&lt;br /&gt;
direct access to a piece of the device driver's data segment. In this way,&lt;br /&gt;
a combination dynlink/device driver device interface can optimize&lt;br /&gt;
communication between the dynlink package and the device driver.&lt;br /&gt;
     Providing I/O port access to a process is more difficult because it is&lt;br /&gt;
supported more modestly by the 80286 processor. The 80286 uses its ring&lt;br /&gt;
protection mechanism to control I/O access; the system can grant code&lt;br /&gt;
running at a certain ring privilege access to all I/O ports, but it can't&lt;br /&gt;
grant access to only some I/O ports. It's too dangerous to grant an&lt;br /&gt;
application access to all I/O ports simply because it uses VIO and VIO&lt;br /&gt;
needs direct port access for the display adapter. This solution would mean&lt;br /&gt;
that OS/2's I/O space is effectively unprotected because almost all&lt;br /&gt;
programs use VIO or the presentation manager directly or indirectly.&lt;br /&gt;
     Instead, OS/2 was designed to allow, upon request from the device&lt;br /&gt;
driver, any code segments marked&lt;br /&gt;
1. This is done via a special command to the linker.&lt;br /&gt;
1 to execute at ring 2 to have I/O access.&lt;br /&gt;
The bad news is that access to all I/O ports must be granted&lt;br /&gt;
indiscriminately, but the good news is that the system is vulnerable to&lt;br /&gt;
program bugs only when those ring 2 segments are being executed. The&lt;br /&gt;
capabilities of ring 2 code, as it's called, are restricted: Ring 2 code&lt;br /&gt;
cannot issue dynlink calls to the system. This is partly a result of ring&lt;br /&gt;
architecture (supporting ring 2 system calls would require significant&lt;br /&gt;
additional overhead) and partly to discourage lazy programmers from&lt;br /&gt;
flagging their entire process as ring 2 to avoid sequestering their I/O&lt;br /&gt;
routines.&lt;br /&gt;
     As I said, in OS/2 version 1.0 the ring mechanism can restrict I/O&lt;br /&gt;
access only to a limited degree. Any malicious program and some buggy&lt;br /&gt;
programs can still damage system stability by manipulating the system's&lt;br /&gt;
peripherals. Furthermore, a real mode application can issue any I/O&lt;br /&gt;
instruction at any time. A future release of OS/2 that runs only on the&lt;br /&gt;
80386 processor will solve these problems. The 80386 hardware is&lt;br /&gt;
specifically designed to allow processes access to some I/O ports but not&lt;br /&gt;
to others through a bit map the system maintains. This map, which of course&lt;br /&gt;
the application cannot directly change, tells the 80386 which port&lt;br /&gt;
addresses may be accessed and which must be refused. This map applies&lt;br /&gt;
equally to protect mode and real mode applications.&lt;br /&gt;
2. Actually, to &amp;quot;virtual real mode&amp;quot; applications. This is&lt;br /&gt;
functionally the same as real mode on earlier processors.&lt;br /&gt;
2 OS/2 will use the&lt;br /&gt;
port addresses supplied by the device driver to allow access only to the&lt;br /&gt;
I/O ports associated with the device(s) to which the process has been&lt;br /&gt;
granted access. This release will not support application code segments&lt;br /&gt;
running at ring 2; any segments so marked will be loaded and run at ring 3.&lt;br /&gt;
The change will be invisible to all applications that use only the&lt;br /&gt;
proper I/O ports. Applications that request access to one device and then&lt;br /&gt;
use their I/O permissions to program another device will fail.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
18.2  Debugging/Ptrace&lt;br /&gt;
&lt;br /&gt;
Because OS/2 goes to a great deal of effort to keep one application from&lt;br /&gt;
interfering with another, special facilities were built to allow debugging&lt;br /&gt;
programs to manipulate and examine a debuggee (the process being debugged).&lt;br /&gt;
Because a debugger is available for OS/2 and writing your own is laborious,&lt;br /&gt;
we expect few programmers to write debuggers. This discussion is included,&lt;br /&gt;
nevertheless, because it further illuminates the OS/2 architectural&lt;br /&gt;
approach.&lt;br /&gt;
     The first concern of a debugger is that it be able to read and write&lt;br /&gt;
the debuggee's code and data segments as well as intercept traps, signals,&lt;br /&gt;
breakpoints, and the like. All these capabilities are strictly in the&lt;br /&gt;
domain of OS/2, so OS/2 must &amp;quot;export&amp;quot; them to the debugger program. A&lt;br /&gt;
second concern is system security: Obviously, the debug interface provides&lt;br /&gt;
a golden opportunity for &amp;quot;cracker&amp;quot; programs to manipulate any other&lt;br /&gt;
program, thereby circumventing passwords, encryption, or any other&lt;br /&gt;
protection scheme. OS/2 prevents this by requiring that the debuggee&lt;br /&gt;
process be flagged as a debug target when it is initially executed; a&lt;br /&gt;
debugger can't latch onto an already-running process. Furthermore, when&lt;br /&gt;
secure versions of OS/2 are available, processes executed under control of&lt;br /&gt;
a debugger will be shorn of any permissions they might have that are in&lt;br /&gt;
excess of those owned by the debugger.&lt;br /&gt;
     Before we examine the debugging interface, we should digress for a&lt;br /&gt;
moment and discuss the OS/2 approach to forcing actions upon threads and&lt;br /&gt;
processes. Earlier I described the process of kernel execution. I mentioned&lt;br /&gt;
that when a process thread makes a kernel request that thread itself enters&lt;br /&gt;
kernel mode and services its own request. This arrangement simplified the&lt;br /&gt;
design of the kernel because a function is coded to perform one action for&lt;br /&gt;
one client in a serial, synchronous fashion. Furthermore, nothing is ever&lt;br /&gt;
forced on a thread that is in kernel mode; any action taken on a thread in&lt;br /&gt;
kernel mode is taken by that thread itself. For example, if a process is to&lt;br /&gt;
be killed and one of its threads is in kernel mode, OS/2 doesn't terminate&lt;br /&gt;
that thread; it sets a flag that says, &amp;quot;Please kill yourself at your&lt;br /&gt;
earliest convenience.&amp;quot; Consequently, OS/2 doesn't need special code to&lt;br /&gt;
enumerate and release any internal flags or resources that a killed kernel&lt;br /&gt;
mode thread might leave orphaned, and in general no thread need&lt;br /&gt;
&amp;quot;understand&amp;quot; the state of any other. The thread to be killed cleans itself&lt;br /&gt;
up, releasing resources, flags, and whatever before it obligingly commits&lt;br /&gt;
suicide.&lt;br /&gt;
     But when is the thread's &amp;quot;earliest convenience&amp;quot;? Thread termination is&lt;br /&gt;
a forced event, and all threads check for any pending forced events&lt;br /&gt;
immediately before they leave kernel mode and reenter application mode.&lt;br /&gt;
This transition takes place frequently: not only when a system call returns&lt;br /&gt;
to the calling application, but also each time a context switch takes&lt;br /&gt;
place.&lt;br /&gt;
     Although it may appear that forced events might languish unprocessed,&lt;br /&gt;
they are serviced rapidly. For example, when a process issues a DosKill&lt;br /&gt;
function on its child process, each thread in the child process is marked&lt;br /&gt;
&amp;quot;kill yourself.&amp;quot; Because the parent process had the CPU, obviously, when it&lt;br /&gt;
issued the DosKill, each of the child's threads is in kernel mode, either&lt;br /&gt;
because the thread is working on a system call or because it was&lt;br /&gt;
artificially placed in kernel mode when the scheduler preempted it. Before&lt;br /&gt;
any of those now-marked threads can execute even a single instruction of&lt;br /&gt;
the child application's code, they must go through OS/2's dispatch routine.&lt;br /&gt;
The &amp;quot;kill yourself&amp;quot; flag is noted, and the thread terminates itself instead&lt;br /&gt;
of returning to application mode. As you can see, the final effect of this&lt;br /&gt;
approach is far from slow: The DosKill takes effect immediately--not one&lt;br /&gt;
more instruction of the child process is executed.&lt;br /&gt;
3. Excepting any SIGKILL handlers and&lt;br /&gt;
DosExitList handlers, of course.&lt;br /&gt;
3 The only significant&lt;br /&gt;
delay in recognizing a forced event occurs when a system call takes a long&lt;br /&gt;
time to process. OS/2 is not very CPU bound, so any call that takes a &amp;quot;long&lt;br /&gt;
time&amp;quot; (1 second or more) must be blocked for most of that time.&lt;br /&gt;
     When a kernel thread issues a block call for an event that might take&lt;br /&gt;
a long time--such as waiting for a keystroke or waiting for a semaphore to&lt;br /&gt;
clear--it uses a special form of block called an interruptible block. When&lt;br /&gt;
OS/2 posts a force flag against a thread, it checks to see if that thread&lt;br /&gt;
is blocking interruptibly. If it is, that thread is released from its block&lt;br /&gt;
with a special code that says, &amp;quot;You were awakened not because the event has&lt;br /&gt;
come to pass but because a forced event was posted.&amp;quot; That thread must then&lt;br /&gt;
finish the system call quickly (generally by declaring an error) so that&lt;br /&gt;
the thread can go through the dispatch routine and recognize the force&lt;br /&gt;
flag. I described this mechanism in Chapter 12 when I talked about another&lt;br /&gt;
kind of forced event--the OS/2 signal mechanism. An incoming signal is a&lt;br /&gt;
forced event for a process's thread 1; it therefore receives the same&lt;br /&gt;
timely response and has the same effect of aborting a slow system call.&lt;br /&gt;
     I've gone through this long discussion of forced events and how&lt;br /&gt;
they're processed because the internal debugging facility is based on one&lt;br /&gt;
giant special forced event. When a process is placed in debug state, a&lt;br /&gt;
trace force flag is permanently set for the initial thread of that process&lt;br /&gt;
and for any other threads it creates. When any of those threads are in&lt;br /&gt;
kernel mode--and they enter kernel mode whenever anything of interest takes&lt;br /&gt;
place--they execute the debuggee half of the OS/2 trace code. The debugger&lt;br /&gt;
half is executed by a debugger thread that issues special DosPtrace calls;&lt;br /&gt;
the two halves of the package communicate through a shared memory area&lt;br /&gt;
built into OS/2.&lt;br /&gt;
     When the debuggee encounters a special event (for example, a Ctrl-C&lt;br /&gt;
signal or a GP fault), the trace force event takes precedence over any&lt;br /&gt;
other, and the debuggee's thread executes the debuggee half of the&lt;br /&gt;
DosPtrace code. This code writes a record describing the event into a&lt;br /&gt;
communications buffer, wakes up the debugger thread, which is typically&lt;br /&gt;
blocked in the debugger's part of the DosPtrace code, and blocks, awaiting&lt;br /&gt;
a reply. The debugger's thread wakes up and returns to the debugger with&lt;br /&gt;
the event information. When the debugger recalls DosPtrace with a command,&lt;br /&gt;
the command is written into the communications area, and the debuggee is&lt;br /&gt;
awakened to read and obey. The command might be &amp;quot;Resume normal execution,&amp;quot;&lt;br /&gt;
&amp;quot;Process the event as you normally would,&amp;quot; or &amp;quot;Give me the contents of&lt;br /&gt;
these locations in your address space,&amp;quot; whereupon the debuggee thread&lt;br /&gt;
replies and remains in the DosPtrace handler.&lt;br /&gt;
     This approach is simple to implement, does the job well, and takes&lt;br /&gt;
advantage of existing OS/2 features. For example, no special code is needed&lt;br /&gt;
to allow the debugger access to the debuggee's address space because the&lt;br /&gt;
debuggee itself, unwittingly in the DosPtrace code, reads and writes its&lt;br /&gt;
own address space. Credit goes to the UNIX ptrace facility, upon which this&lt;br /&gt;
facility was closely modeled.&lt;br /&gt;
     Finally, here are a few incidental facts that the readers of this&lt;br /&gt;
book, being likely users of debugging facilities, should know. OS/2&lt;br /&gt;
maintains a linkage between the debugger process and the debuggee process.&lt;br /&gt;
When the debugger process terminates, the debuggee process also terminates&lt;br /&gt;
if it has not already done so. The debuggee program need not be a direct&lt;br /&gt;
child of the debugger; when the debugger process makes its initial&lt;br /&gt;
DosPtrace call, OS/2 connects it to the last process that was executed with&lt;br /&gt;
the special tracing option. If a process is executed with the tracing&lt;br /&gt;
option but no debugger process subsequently issues a DosPtrace function,&lt;br /&gt;
the jilted debuggee process is terminated in about two minutes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==19  The 3x Box==&lt;br /&gt;
&lt;br /&gt;
It's of critical importance that OS/2 do a good job of running existing MS-&lt;br /&gt;
DOS applications, but as we've discussed, this is a difficult task. To&lt;br /&gt;
offer the official MS-DOS interfaces under OS/2 and therefore claim upward&lt;br /&gt;
compatibility would be easy; unfortunately, few popular applications would&lt;br /&gt;
run successfully in such an environment. Most sophisticated applications&lt;br /&gt;
take direct control of the machine environment and use MS-DOS for tasks the&lt;br /&gt;
application doesn't want to bother with, such as file I/O, keyboard&lt;br /&gt;
buffering, and so forth. If we're to run existing applications&lt;br /&gt;
successfully, we must provide a close facsimile to a real mode PC running&lt;br /&gt;
MS-DOS in all respects, not just the INT 21 program interface.&lt;br /&gt;
     OS/2 provides such a highly compatible environment, called the real&lt;br /&gt;
mode screen group, the compatibility box, or simply the 3x box. The 3x box&lt;br /&gt;
is an environment that emulates an 8086-based PC running MS-DOS version&lt;br /&gt;
3.3.&lt;br /&gt;
1. For OS/2 version 1.0, the 3x box is compatible with MS-DOS&lt;br /&gt;
version 3.3.&lt;br /&gt;
1 MS-DOS programs execute in real mode, and because emulating real&lt;br /&gt;
mode from within protected mode is prohibitively slow, OS/2 physically&lt;br /&gt;
switches into real mode to execute MS-DOS applications. Because MS-DOS&lt;br /&gt;
programs are well aware of the MS-DOS memory layout, this layout is&lt;br /&gt;
replicated for the OS/2 3x box. The first N bytes (typically 640 KB) are&lt;br /&gt;
reserved for the exclusive use of the low-memory parts of OS/2 and the 3x&lt;br /&gt;
box; protected mode applications never use any of this memory. Thus,&lt;br /&gt;
programs that are careless about memory allocation or that make single-&lt;br /&gt;
tasking assumptions about the availability of memory can run in a&lt;br /&gt;
multitasking environment. Figure 19-1 illustrates the OS/2 memory layout.&lt;br /&gt;
The low bytes of memory are reserved for the device drivers and portions of&lt;br /&gt;
OS/2 that must run in real mode. The remainder of the space, up to the&lt;br /&gt;
RMSIZE value, is dedicated to the 3x box. Memory from 640 KB to 1 MB is&lt;br /&gt;
reserved for ROMs and video display buffers. Memory above 1 MB holds the&lt;br /&gt;
remainder of OS/2 and all protect mode applications. Nonswappable, fixed&lt;br /&gt;
segments are kept at one end of this memory to reduce fragmentation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      N ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ³    Protect     ³&lt;br /&gt;
        ³      mode      ³&lt;br /&gt;
        ³  applications  ³&lt;br /&gt;
        ³   (movable)    ³&lt;br /&gt;
        &amp;lt;                &amp;lt;&lt;br /&gt;
         &amp;gt;                &amp;gt;&lt;br /&gt;
        &amp;lt;                &amp;lt;&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³     Fixed      ³&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³      OS/2      ³&lt;br /&gt;
   1 MB ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
 640 KB ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ³      Real      ³&lt;br /&gt;
        ³      mode      ³&lt;br /&gt;
        ³  application   ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³   Additional   ³&lt;br /&gt;
        ³ device drivers ³&lt;br /&gt;
  ~100k ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ³    Low OS/2    ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
   90:0 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³    Bios ROM    ³&lt;br /&gt;
      0 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 19-1.  System memory layout.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 uses the screen group mechanism to provide a user interface to&lt;br /&gt;
the 3x box. One screen group is designated the real mode screen group;&lt;br /&gt;
automatically, OS/2 executes COMMAND.COM in that screen group when it is&lt;br /&gt;
first selected. The user accesses the real mode environment by selecting&lt;br /&gt;
that screen group and returns to the protected mode environment by&lt;br /&gt;
selecting another screen group. OS/2 version 1.0 supports a single real&lt;br /&gt;
mode screen group because the real mode compatibility is provided by&lt;br /&gt;
actually running the application in real mode. Thus, only one 640 KB area&lt;br /&gt;
is reserved for all real mode applications, and adjudicating between the&lt;br /&gt;
conflicting hardware manipulations of multiple real mode applications&lt;br /&gt;
without any assistance from the 80286 microprocessor hardware would be&lt;br /&gt;
prohibitively difficult. The 80386 microprocessor, however, provides a&lt;br /&gt;
special hardware facility called virtual 8086 mode that will allow a future&lt;br /&gt;
release of OS/2 to support multiple real mode screen groups, but only on an&lt;br /&gt;
80386-based machine.&lt;br /&gt;
     The operating system that services the 3x application's INT 21&lt;br /&gt;
requests is not an exact copy of MS-DOS; it's actually a low-memory&lt;br /&gt;
extension of OS/2 itself. Because OS/2 is derived from MS-DOS, OS/2&lt;br /&gt;
executes MS-DOS functions in a manner identical to that of the real MS-DOS.&lt;br /&gt;
OS/2 supports the non-MS-DOS functions mentioned above by staying out of&lt;br /&gt;
the way as much as possible and letting the 3x application &amp;quot;party hearty&amp;quot;&lt;br /&gt;
with the hardware. For example, hooking most interrupt vectors is&lt;br /&gt;
supported, as is hooking INT 21 and the ROM BIOS INT vectors. The ROM BIOS&lt;br /&gt;
calls themselves are fully supported. Frequently, staying out of the way is&lt;br /&gt;
not as easy as it may sound. For example, OS/2 must intercept and monitor&lt;br /&gt;
real mode calls made to the disk driver part of the ROM BIOS so that it can&lt;br /&gt;
prevent conflict with ongoing, asynchronous protect-mode disk I/O. OS/2 may&lt;br /&gt;
find it necessary to momentarily block a real mode application's BIOS call&lt;br /&gt;
until the protect mode device driver can release the hardware. Once the&lt;br /&gt;
real mode application is in the BIOS, the same interlock mechanism prevents&lt;br /&gt;
the protect mode device driver from entering the disk I/O critical&lt;br /&gt;
section.&lt;br /&gt;
     Hard errors encountered by the real mode application are handled by a&lt;br /&gt;
hybrid of the OS/2 hard error daemon and the 3x box INT 24 mechanism in a&lt;br /&gt;
three-step process, as follows:&lt;br /&gt;
&lt;br /&gt;
     1:  Hard error codes caused by events unique to the OS/2 environment--&lt;br /&gt;
         such as a volume manager media change request--activate the hard&lt;br /&gt;
         error daemon so that the user can get an accurate explanation&lt;br /&gt;
         of the problem. The user's response to the hard error is saved&lt;br /&gt;
         but is not yet acted upon. Hard error codes, which are also&lt;br /&gt;
         present in MS-DOS version 3.3, skip this step and start at&lt;br /&gt;
         step 2.&lt;br /&gt;
&lt;br /&gt;
     2:  If the real mode application has installed its own hard error&lt;br /&gt;
         handler via the INT 24 vector, it is called. If step 1 was&lt;br /&gt;
         skipped, the code should be known to the application, and it is&lt;br /&gt;
         presented unchanged. If step 1 was taken, the error code is&lt;br /&gt;
         transformed to ERROR_I24_GEN_FAILURE for this step. The response&lt;br /&gt;
         returned by the program, if valid for this class of hard error, is&lt;br /&gt;
         acted upon. This means that hard errors new to OS/2 can actually&lt;br /&gt;
         generate two pop-ups--one from the hard error daemon with an&lt;br /&gt;
         accurate message and one from the application itself with a&lt;br /&gt;
         General Failure message. This allows the user to understand the&lt;br /&gt;
         true cause of the hard error and yet notifies the application that&lt;br /&gt;
         a hard error has occurred. In such a case, the action specified by&lt;br /&gt;
         the application when it returned from its own hard error handler&lt;br /&gt;
         is the one taken, not the action specified by the user to the&lt;br /&gt;
         initial hard error daemon pop-up.&lt;br /&gt;
&lt;br /&gt;
3:       If the real mode application has not registered its hard error&lt;br /&gt;
         handler via the INT 24 mechanism, OS/2 provides a default handler&lt;br /&gt;
         that uses the hard error daemon. If step 1 was taken and the hard&lt;br /&gt;
         error daemon has already run, it is not run again; OS/2 takes the&lt;br /&gt;
         action specified in response to the hard error pop-up that was&lt;br /&gt;
         displayed. If step 1 was not taken because the hard error code is&lt;br /&gt;
         MS-DOS 3.x compatible and if step 2 was not taken because the&lt;br /&gt;
         application did not provide its own handler, then OS/2 activates&lt;br /&gt;
         the hard error daemon in step 3 to present the message and receive&lt;br /&gt;
         a reply.&lt;br /&gt;
&lt;br /&gt;
     The 3x box supports only MS-DOS functionality; no new OS/2 features&lt;br /&gt;
are available to 3x box applications--no new API, no multiple threads, no&lt;br /&gt;
IPC, no semaphores, and so on.&lt;br /&gt;
2. There are two exceptions. The OPEN function was&lt;br /&gt;
extended, and an INT 2F multiplex function was added to notify&lt;br /&gt;
real mode applications of screen switches.&lt;br /&gt;
2 This decision was made for two reasons.&lt;br /&gt;
First, although any real mode application can damage the system's&lt;br /&gt;
stability, allowing real mode applications to access some protect mode&lt;br /&gt;
features may aggravate the problem. For example, terminate and stay&lt;br /&gt;
resident programs may manipulate the CPU in such a way as to make it&lt;br /&gt;
impossible for a real mode application to protect a critical section with&lt;br /&gt;
semaphores and yet guarantee that it won't leave the semaphore orphaned.&lt;br /&gt;
Second, because OS/2 has only one real mode box and it labors under a 640&lt;br /&gt;
KB memory ceiling, it doesn't make sense to develop new real mode&lt;br /&gt;
applications that use new OS/2 functions and thus require OS/2.&lt;br /&gt;
     The 3x box emulation extends to interrupts. OS/2 continues to context&lt;br /&gt;
switch the CPU when the 3x box is active; that is, the 3x box application&lt;br /&gt;
is the foreground application. Because the foreground process receives a&lt;br /&gt;
favorable priority, its CPU is preempted only when a time-critical protect&lt;br /&gt;
mode application needs to run or when the real mode application blocks. If&lt;br /&gt;
the CPU is running a protect mode application when a device interrupt comes&lt;br /&gt;
in, OS/2 switches to real mode so that a real mode application that is&lt;br /&gt;
hooking the interrupt vectors can receive the interrupt in real mode. When&lt;br /&gt;
the interrupt is complete, OS/2 switches back to protected mode and resumes&lt;br /&gt;
the protected application.&lt;br /&gt;
     Although protected mode applications can continue to run when the 3x&lt;br /&gt;
box is in the foreground, the reverse is not true. When the 3x box screen&lt;br /&gt;
group is in the background, all 3x box execution is suspended, including&lt;br /&gt;
interrupts. Unlike protected mode applications, real mode applications&lt;br /&gt;
cannot be trusted to refrain from manipulating the screen hardware when&lt;br /&gt;
they are in a background screen group. Normally, a real mode application&lt;br /&gt;
doesn't notice its suspension when it's in background mode; the only thing&lt;br /&gt;
it might notice is that the system time-of-day has apparently &amp;quot;jumped&lt;br /&gt;
forward.&amp;quot; Because mode switching is a slow process and leaves interrupts&lt;br /&gt;
disabled for almost 1 millisecond, mode switching can cause interrupt&lt;br /&gt;
overruns on fast devices such as serial ports. The best way to deal with&lt;br /&gt;
this is to switch the real mode application into a background screen group;&lt;br /&gt;
with no more real mode programs to execute, OS/2 does no further mode&lt;br /&gt;
switching.&lt;br /&gt;
     Some OS/2 utility programs such as FIND are packaged as Family API&lt;br /&gt;
applications. A single binary can run in both protected mode and real mode,&lt;br /&gt;
and the user is saved the inconvenience of switching from real mode to&lt;br /&gt;
protected mode to do simple utility functions. This works well for simple&lt;br /&gt;
utility programs without full screen or graphical interfaces and for&lt;br /&gt;
programs that have modest memory demands and that in other ways have little&lt;br /&gt;
need of OS/2's extended capabilities. Obviously, if an application can make&lt;br /&gt;
good use of OS/2's protect mode features, it should be written to be&lt;br /&gt;
protect mode only so that it can take advantage of those features.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==20  Family API==&lt;br /&gt;
&lt;br /&gt;
When a new release of a PC operating system is announced, application&lt;br /&gt;
writers face a decision: Should they write a new application to use some of&lt;br /&gt;
the new features or should they use only the features in earlier releases?&lt;br /&gt;
If they go for the sexy new features, their product might do more, be&lt;br /&gt;
easier to write, or be more efficient; but when the program hits the&lt;br /&gt;
market, only 10 percent of existing PCs may be running the new release. Not&lt;br /&gt;
all of the existing machines have the proper processor to be able to run&lt;br /&gt;
the new system, and, of those, many of their users haven't seen the need to&lt;br /&gt;
go to the expense and endure the hassle of upgrading their operating&lt;br /&gt;
system. If it's viable to write the new application so that it requires&lt;br /&gt;
only the old operating system (and therefore runs in compatibility mode&lt;br /&gt;
under the new operating system), then it's tempting to do so. Even though&lt;br /&gt;
the product is not as good as it might be, it can sell to 100 percent of&lt;br /&gt;
the installed base of machines--10 times as many as it would if it required&lt;br /&gt;
the new operating system.&lt;br /&gt;
     And here you have the classic &amp;quot;catch-22&amp;quot; of software standards: If&lt;br /&gt;
users don't see a need, they won't use the new system. If they don't use&lt;br /&gt;
the new system, applications will not be written explicitly for it; so the&lt;br /&gt;
users never see a need. Without some way to prime the pump, it will be a&lt;br /&gt;
long time before a comprehensive set of applications are available that use&lt;br /&gt;
the new system's features.&lt;br /&gt;
     OS/2 tackles this problem in several ways. The software bundled with&lt;br /&gt;
OS/2 runs in protected mode, and OS/2 attempts to include as much&lt;br /&gt;
additional user function as possible to increase its value to a user who&lt;br /&gt;
initially owns no protected mode applications. The most important user&lt;br /&gt;
acceptance feature of OS/2, however, is called Family API. Family API is a&lt;br /&gt;
special subset of the OS/2 protected mode API. Using special tools included&lt;br /&gt;
in the OS/2 developer's kit, you can build applications that use only the&lt;br /&gt;
Family API. The resultant .EXE file(s) run unchanged in OS/2 protect mode&lt;br /&gt;
or on an 8086 running MS-DOS 2.x or 3.x.&lt;br /&gt;
1. Of course, they also run in the MS-DOS 3.x compatible screen group&lt;br /&gt;
under OS/2; but except for convenience utilities, it's generally a&lt;br /&gt;
waste to dedicate the one real mode screen group to running an appl-&lt;br /&gt;
cation that could run in any of the many protect mode screen groups.&lt;br /&gt;
1 Thus, developers don't have to&lt;br /&gt;
choose between writing applications that are OS/2 protect mode and writing&lt;br /&gt;
applications that are MS-DOS compatible; they can use the Family API&lt;br /&gt;
mechanism and do both. Your applications will run as protected mode&lt;br /&gt;
applications under OS/2 and as MS-DOS applications under a true MS-DOS&lt;br /&gt;
system.&lt;br /&gt;
     Clearly, the Family API is a noteworthy feature. It offers some OS/2&lt;br /&gt;
functions, together with the dynamic link system interface, to programs&lt;br /&gt;
that run under MS-DOS without a copy of OS/2 anywhere in sight. It does&lt;br /&gt;
this by providing an OS/2 compatibility library that accepts the OS/2&lt;br /&gt;
system interface calls and implements them itself, calling the underlying&lt;br /&gt;
MS-DOS system via INT 21 as necessary. This information should give you a&lt;br /&gt;
big head start in figuring out which OS/2 functions are included in the&lt;br /&gt;
Family API: Clearly all functions that have similar INT 21 functions--such&lt;br /&gt;
as DosOpen, DosRead, and DosAllocSeg--are supported. Also present are&lt;br /&gt;
functions, such as DosSubAlloc, that can be supported directly by the&lt;br /&gt;
special Family API library. Features that are extremely difficult to&lt;br /&gt;
support in a true MS-DOS environment, such as multiple threads and&lt;br /&gt;
asynchronous I/O, are not present in the Family API.&lt;br /&gt;
     Where does this library come from? And how does it get loaded by MS-&lt;br /&gt;
DOS to satisfy the OS/2 executable's dynlink requests? It's all done with&lt;br /&gt;
mirrors, as the expression goes, and the &amp;quot;mirrors&amp;quot; must be built into the&lt;br /&gt;
application's .EXE file because that file is all that's present when a&lt;br /&gt;
Family API application is executed under MS-DOS. Figure 20-1 shows the&lt;br /&gt;
layout of a Family API .EXE file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         Family API&lt;br /&gt;
            .EXE&lt;br /&gt;
     ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³&lt;br /&gt;
     ³°°°MS-DOS 3.x°°°°³&lt;br /&gt;
     ³°°°.EXE header°ÄÄÅÄÄ¿&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³  ³&lt;br /&gt;
     ³°°°Family API°°°°³  ³ Shaded area is&lt;br /&gt;
     ³°°°°°loader°°°°°°³  ³ read by MS-DOS as&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³ a real mode&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³  ³ application&lt;br /&gt;
     ³°°°Family API°°°°³  ³&lt;br /&gt;
     ³°°°°°library°°°°°³  ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´�ÄÙ&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³      OS/2       ³&lt;br /&gt;
     ³   .EXE header   ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³      OS/2       ³&lt;br /&gt;
     ³   application   ³&lt;br /&gt;
     ³    segments     ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³     Dynlink     ³&lt;br /&gt;
     ³      names      ³&lt;br /&gt;
     ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 20-1.  Family API executable (.EXE) format.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 needed to define a new .EXE file because the existing MS-DOS .EXE&lt;br /&gt;
file format contained too little information for the OS/2 protect mode&lt;br /&gt;
segmented environment. Because, as we've discussed, the 8086 memory&lt;br /&gt;
architecture is--despite the terminology normally used--a linear memory&lt;br /&gt;
architecture, the MS-DOS .EXE format described only a single hunk of memory&lt;br /&gt;
that was to be loaded contiguously. OS/2 needs each segment described&lt;br /&gt;
separately, with information on its status: read only, code or data, demand&lt;br /&gt;
load or preload, and so on. Naturally, OS/2 also needs a .EXE format with&lt;br /&gt;
special records to describe loadtime dynamic links. This new .EXE format&lt;br /&gt;
was defined so that its initial bytes look exactly like those of the old&lt;br /&gt;
MS-DOS .EXE file header. A special flag bit is set in this fake .EXE&lt;br /&gt;
 header that is ignored by all releases of MS-DOS but that OS/2 recognizes&lt;br /&gt;
to mean &amp;quot;It's not true. I'm really an OS/2 .EXE file. Seek to this location&lt;br /&gt;
to find the true, new-style .EXE header.&amp;quot;&lt;br /&gt;
     When an MS-DOS system is told to load this .EXE file, it sees and&lt;br /&gt;
believes the old .EXE file header. This header does not describe the&lt;br /&gt;
application itself but a body of special code built into the .EXE file&lt;br /&gt;
before the actual application's code: the Family API loader and library. In&lt;br /&gt;
other words, to MS-DOS this .EXE file looks like a valid, executable&lt;br /&gt;
program, and that program is the Family API loader and library. The Family&lt;br /&gt;
API loader and library are loaded into memory, and execution begins. MS-DOS&lt;br /&gt;
doesn't load in the body of the application itself because it wasn't&lt;br /&gt;
described as part of the load image in the special MS-DOS .EXE file header.&lt;br /&gt;
As soon as it starts to execute, the Family API loader begins reading in&lt;br /&gt;
the application's segments, performs a loader's general relocation chores,&lt;br /&gt;
and fixes up dynlink references to the proper entry points in the Family&lt;br /&gt;
API library package. When the application is loaded, the Family API loader&lt;br /&gt;
block moves the application to its final execution address, which overlays&lt;br /&gt;
most of the Family API loader to reclaim that space, and execution&lt;br /&gt;
begins.&lt;br /&gt;
     All OS/2 .EXE files have this fake MS-DOS .EXE format header. In non-&lt;br /&gt;
Family API executables, the Family API loader and library are missing, and&lt;br /&gt;
by default the header describes an impossibly big MS-DOS executable. Should&lt;br /&gt;
the application be accidentally run under a non-OS/2 system or in the OS/2&lt;br /&gt;
compatibility screen group, MS-DOS will refuse to load the program.&lt;br /&gt;
Optionally, the programmer can link in a small stub program that goes where&lt;br /&gt;
the Family API loader would and that prints a more meaningful error&lt;br /&gt;
message. As we said earlier, the old-style .EXE headers on the front of the&lt;br /&gt;
file contain a flag bit to alert OS/2 to the presence of a new-style .EXE&lt;br /&gt;
header further into the file. Because this header doesn't describe the&lt;br /&gt;
Family API loader and library parts of the file, OS/2 ignores their&lt;br /&gt;
presence when it loads a Family API application in protected mode; the&lt;br /&gt;
application's dynlink references are fixed up to the normal dynlink&lt;br /&gt;
libraries, and the Family API versions of those libraries are ignored.&lt;br /&gt;
     There Ain't No Such Thing As A Free Lunch, and the same unfortunately&lt;br /&gt;
applies to the Family API mechanism. First, although the Family API allows&lt;br /&gt;
dynlink calls to be used in an MS-DOS environment, this is not true&lt;br /&gt;
dynlinking; it's quasi dynlinking. Obviously, runtime dynlinking is not&lt;br /&gt;
supported, but even loadtime dynlinking is special because the dynlink&lt;br /&gt;
target library is bound into the .EXE file. One of the advantages of&lt;br /&gt;
dynlinks is that the target code is not part of the .EXE file and can&lt;br /&gt;
therefore be changed and upgraded without changing the .EXE file. This is&lt;br /&gt;
not true of the dynlink emulation library used by the Family API because&lt;br /&gt;
it is built into the .EXE file. Fortunately, this disadvantage isn't&lt;br /&gt;
normally a problem. Dynlink libraries are updated either to improve&lt;br /&gt;
their implementation or to add new features. The Family API library can't&lt;br /&gt;
be improved very much because its environment--MS-DOS--is limited and&lt;br /&gt;
unchanging. If new Family API features were added, loading that new library&lt;br /&gt;
with preexisting Family API .EXE files would make no sense; those programs&lt;br /&gt;
wouldn't be calling the new features.&lt;br /&gt;
     A more significant drawback is the size and speed hit that the Family&lt;br /&gt;
API introduces. Clearly, the size of a Family API .EXE file is extended by&lt;br /&gt;
the size of the Family API loader and the support library. The tools used&lt;br /&gt;
to build Family API executables include only those library routines used by&lt;br /&gt;
the program, but even so the library and the loader add up to a nontrivial&lt;br /&gt;
amount of memory--typically 10 KB to 14 KB in the .EXE file and perhaps&lt;br /&gt;
9KB (the loader is not included) in RAM. Finally, loading a Family API&lt;br /&gt;
application under MS-DOS is slower than loading a true MS-DOS .EXE file.&lt;br /&gt;
Comparing loadtime against the loadtime of MS-DOS is tough for any&lt;br /&gt;
operating system because loading faster than MS-DOS is difficult. The .EXE&lt;br /&gt;
file consists of a single lump of contiguous data that can be read into&lt;br /&gt;
memory in a single disk read operation. A relocation table must also be&lt;br /&gt;
read, but it's typically very small. It's hard for any system to be faster&lt;br /&gt;
than this. Clearly, loading a Family API application is slower because the&lt;br /&gt;
loader and library must be loaded, and then they must, a segment at a time,&lt;br /&gt;
bring in the body of the application.&lt;br /&gt;
     Although the Family API makes dual environment applications possible,&lt;br /&gt;
it can't totally hide from an application the difference between the MS-DOS&lt;br /&gt;
3.x and the OS/2 execution environment. For example, the Family API&lt;br /&gt;
supports only the DosFindFirst function for a single search handle at a&lt;br /&gt;
time. An application that wants to perform multiple directory searches&lt;br /&gt;
simultaneously should use DosGetMachineMode to determine its environment&lt;br /&gt;
and then use the unrestricted DosFindFirst function if running in protect&lt;br /&gt;
mode or use the INT 21 functions if running in real mode. Likewise, an&lt;br /&gt;
application that wants to manipulate printer data needs to contain version-&lt;br /&gt;
specific code to hook INT 17 or to use device monitors, depending on the&lt;br /&gt;
environment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Part III  The Future&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==21  The Future==&lt;br /&gt;
&lt;br /&gt;
This chapter is difficult to write because describing the second version of&lt;br /&gt;
OS/2 becomes uninteresting when that version is released. Furthermore,&lt;br /&gt;
preannouncing products is bad practice; the trade-offs between schedule and&lt;br /&gt;
customer demand can accelerate the inclusion of some features and postpone&lt;br /&gt;
others, often late in the development cycle. If we talk explicitly about&lt;br /&gt;
future features, developers may plan their work around the availability of&lt;br /&gt;
those features and be left high and dry if said features are postponed. As&lt;br /&gt;
a result, this chapter is necessarily vague about both the functional&lt;br /&gt;
details and the release schedule, discussing future goals for features&lt;br /&gt;
rather than the features themselves. Design your application, not so that&lt;br /&gt;
it depends on the features described here, but so that it is compatible&lt;br /&gt;
with them.&lt;br /&gt;
     OS/2 version 1.0 is the first standard MS-DOS-compatible operating&lt;br /&gt;
system that unlocks the memory-addressing potential of the 80286--a &amp;quot;train&amp;quot;&lt;br /&gt;
that will &amp;quot;pull&amp;quot; a great many APIs into the standard. On the other hand,&lt;br /&gt;
foreseeable future releases cannot expect such penetration, so the&lt;br /&gt;
designers of OS/2 version 1.0 focused primarily on including a full set of&lt;br /&gt;
APIs. Major performance improvements were postponed for future releases&lt;br /&gt;
simply because such improvements can be easily added later, whereas new&lt;br /&gt;
APIs cannot. Most of the planned work is to take further advantage of&lt;br /&gt;
existing interfaces, not to create new ones.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.1  File System&lt;br /&gt;
&lt;br /&gt;
Clearly, the heart of the office automation environment is data--lots of&lt;br /&gt;
data--searching for it, reading it, and, less frequently, writing it. A&lt;br /&gt;
machine's raw number-crunching capacity is relatively uninteresting in this&lt;br /&gt;
milieu; the important issue is how fast the machine can get the data and&lt;br /&gt;
manipulate it. Certainly, raw CPU power is advantageous; it allows the use&lt;br /&gt;
of a relatively compute bound graphical user interface, for example. But&lt;br /&gt;
I/O performance is becoming the limiting factor, especially in a&lt;br /&gt;
multitasking environment. Where does this data come from? If it's from the&lt;br /&gt;
keyboard, no problem; human typing speeds are glacially slow to a computer.&lt;br /&gt;
If the data is from a non-mass-storage device, OS/2's direct device access&lt;br /&gt;
facilities should provide sufficient throughput. That leaves the file&lt;br /&gt;
system for local disks and the network for remote data. The file system is&lt;br /&gt;
a natural for a future release upgrade. Its interface is generic so that&lt;br /&gt;
applications written for the first release will work compatibly with new&lt;br /&gt;
file systems in subsequent releases.&lt;br /&gt;
     Talking about pending file system improvements is relatively easy&lt;br /&gt;
because the weaknesses in the current FAT file system are obvious.&lt;br /&gt;
&lt;br /&gt;
     þ  Large Disk Support&lt;br /&gt;
        Clearly, a new file system will support arbitrarily large&lt;br /&gt;
        disks without introducing prohibitive allocation fragmentation.&lt;br /&gt;
        Allocation fragmentation refers to the minimum amount of disk space&lt;br /&gt;
        that a file system can allocate to a small file--the allocation&lt;br /&gt;
        unit. If the allocation unit is size N, the average file on the&lt;br /&gt;
        disk is expected to waste N/2 bytes of disk space because each file&lt;br /&gt;
        has a last allocation unit and on the average that unit will be&lt;br /&gt;
        only half filled. Actually, if the allocation unit is large, say&lt;br /&gt;
        more than 2 KB, the average fragmentation loss is greater than this&lt;br /&gt;
        estimate because a disproportionate number of files are small.&lt;br /&gt;
           The existing MS-DOS FAT file system can handle large disks,&lt;br /&gt;
        but at the cost of using very large allocation units. Depending on&lt;br /&gt;
        the number and the size of the files, a 100 MB disk might be as&lt;br /&gt;
        much as 50 percent wasted by this fragmentation. The new Microsoft&lt;br /&gt;
        file system will support a very small allocation unit--probably 512&lt;br /&gt;
        bytes--to reduce this fragmentation, and this small allocation unit&lt;br /&gt;
        size will not adversely affect the performance of the file system.&lt;br /&gt;
&lt;br /&gt;
     þ  File Protection&lt;br /&gt;
        A new file system also must support file access protection as part&lt;br /&gt;
        of the move toward a fully secure environment. File protection is&lt;br /&gt;
        typically a feature of multiuser operating systems; the MS-DOS FAT&lt;br /&gt;
        file system was designed for a single-user environment and contains&lt;br /&gt;
        no protection facilities. So why do we need them now? One reason is&lt;br /&gt;
        that a networked PC is physically a single-user machine, but&lt;br /&gt;
        logically it's a multiuser machine because multiple users can&lt;br /&gt;
        access the same files over the network. Also, as we shall see, it&lt;br /&gt;
        is sometimes useful to be able to protect your own files from&lt;br /&gt;
        access by yourself.&lt;br /&gt;
           Today, most network installations consist of server machines&lt;br /&gt;
        and client machines, with client machines able to access only files&lt;br /&gt;
        on server machines. MSNET and PCNET servers have a rudimentary form&lt;br /&gt;
        of file protection, but it needs improvement (see below). In the&lt;br /&gt;
        future, as machines become bigger and as products improve, files on&lt;br /&gt;
        client machines will also be available across the network. Clearly,&lt;br /&gt;
        a strong protection mechanism is needed to eliminate risks to a&lt;br /&gt;
        client machine's files. Finally, a file protection mechanism can be&lt;br /&gt;
        useful even on a single-user machine that is not accessible from a&lt;br /&gt;
        network. Today a variety of &amp;quot;Trojan&amp;quot; programs claim to be one thing&lt;br /&gt;
        but actually are another. In a nonnetworked environment, these&lt;br /&gt;
        programs are generally examples of mindless vandalism; typically,&lt;br /&gt;
        they purge the contents of the victim's hard disk. In a future&lt;br /&gt;
        office environment, they might edit payroll files or send sensitive&lt;br /&gt;
        data to someone waiting across the network. If you, as a user, can&lt;br /&gt;
        put sensitive files under password protection, they are safe even&lt;br /&gt;
        from yourself when you unwittingly run a Trojan program. That&lt;br /&gt;
        program doesn't know the password, and you certainly will decline&lt;br /&gt;
        to supply it. Self-protection also prevents someone from sitting&lt;br /&gt;
        down at your PC while you are at lunch or on vacation and wreaking&lt;br /&gt;
        havoc with your files.&lt;br /&gt;
           Protection mechanisms take two general forms:&lt;br /&gt;
        capability tokens and access lists. capability token gives access&lt;br /&gt;
        to an object if the requestor can supply the proper token,&lt;br /&gt;
        which itself can take a variety of forms. A per-file or per-&lt;br /&gt;
        directory password, such as is available on existing MSNET&lt;br /&gt;
        and PCNET products, is a kind of  capability token: If you&lt;br /&gt;
        can present the password, you can access the file. Note that&lt;br /&gt;
        the password is associated with the item, not the user. The&lt;br /&gt;
        front door key to your house is a good example of a&lt;br /&gt;
        capability token, and it shows the features and limitations&lt;br /&gt;
        of the approach very well. Access to your house depends on&lt;br /&gt;
        owning the capability token--the key--and not on who you&lt;br /&gt;
        are. If you don't have your key, you can't get in, even if&lt;br /&gt;
        it's your own house. Anybody that does have the key can get&lt;br /&gt;
        in, no matter who they are. A key can sometimes be handy:&lt;br /&gt;
        You can loan it to someone for a day and then get it back.&lt;br /&gt;
        You can give it to the plumber's office, for example, and&lt;br /&gt;
        the office can give it to the plumber, who can in turn give&lt;br /&gt;
        it to an assistant. Capability tokens are flexible because&lt;br /&gt;
        you can pass them around without notifying the owner of the&lt;br /&gt;
        protected object.&lt;br /&gt;
           This benefit is also the major drawback of capability token&lt;br /&gt;
        systems: The capabilities can be passed around willy-nilly&lt;br /&gt;
        and, like a key, can be duplicated. Once you give your key&lt;br /&gt;
        out, you never know if you've gotten &amp;quot;them&amp;quot; back again. You&lt;br /&gt;
        can't enumerate who has access to your house, and if they&lt;br /&gt;
        refuse to return a key or if they've duplicated it, you&lt;br /&gt;
        can't withdraw access to your house. The only way to regain&lt;br /&gt;
        control over your house is to change the lock, which means&lt;br /&gt;
        that you have to reissue keys to everybody who should get&lt;br /&gt;
        access. In the world of houses and keys, this isn't much of&lt;br /&gt;
        a problem because keys aren't given out that much and it's&lt;br /&gt;
        easy to contact the few people who should have them.&lt;br /&gt;
        Changing the capability &amp;quot;lock&amp;quot; on a computer file is much&lt;br /&gt;
        more difficult, however, because it may mean updating a&lt;br /&gt;
        great many programs that are allowed access, and they all&lt;br /&gt;
        have to be updated simultaneously so that none is&lt;br /&gt;
        accidentally locked out. And, of course, the distribution of&lt;br /&gt;
        the new capability token must be carried out securely; you&lt;br /&gt;
        must ensure that no &amp;quot;bad guy&amp;quot; gets a chance to see and copy&lt;br /&gt;
        the token.&lt;br /&gt;
           And, finally, because a separate capability token, or&lt;br /&gt;
        password, needs to be kept for each file or directory, you can't&lt;br /&gt;
        possibly memorize them all. Instead, they get built into programs,&lt;br /&gt;
        stored in files, entered into batch scripts, and so on. All these&lt;br /&gt;
        passwords--the ones that are difficult to change because of the&lt;br /&gt;
        hassle of updating everybody--are being kept around in &amp;quot;plain text&amp;quot;&lt;br /&gt;
        in standardized locations, an invitation for pilferage. And just as&lt;br /&gt;
        the lock on your door won't tell you how many keys exist, a&lt;br /&gt;
        capability token system won't be able to warn you that someone has&lt;br /&gt;
        stolen a copy of the capability token.&lt;br /&gt;
           An alternative approach is the access list mechanism. It is&lt;br /&gt;
        equivalent to the guard at the movie studio gate who has a&lt;br /&gt;
        list of people on his clipboard. Each protected object is&lt;br /&gt;
        associated with a list of who is allowed what kind of access. It's&lt;br /&gt;
        easy to see who has access--simply look at the list. It's easy to&lt;br /&gt;
        give or take away access--simply edit the list. Maintaining the&lt;br /&gt;
        list is easy because no change is made unless someone is to be&lt;br /&gt;
        added or removed, and the list can contain group names, such as&lt;br /&gt;
        &amp;quot;anyone from the production department&amp;quot; or &amp;quot;all vice presidents.&amp;quot;&lt;br /&gt;
           The fly in this particular ointment--and the reason that&lt;br /&gt;
        MSNET didn't use this approach--is in authenticating the&lt;br /&gt;
        identification of the person who wants access. In our movie studio,&lt;br /&gt;
        a picture badge is probably sufficient.&lt;br /&gt;
1. Note that the photo on the badge, together with a hard-to duplicate&lt;br /&gt;
design, keeps the badge from being just another capability token.&lt;br /&gt;
1 With the computer, we use&lt;br /&gt;
        a personal password. This password doesn't show that you have&lt;br /&gt;
        access to a particular file; it shows that you are who you claim to&lt;br /&gt;
        be. Also, because you have only one password, you can memorize it;&lt;br /&gt;
        it needn't be written on any list. Finally, you can change the&lt;br /&gt;
        password frequently because only one person--the one changing it,&lt;br /&gt;
        you--needs to be notified. Once the computer system knows that&lt;br /&gt;
        you're truly Hiram G. Hornswoggle, it grants or refuses access&lt;br /&gt;
        based on whether you're on an access list or belong to a group that&lt;br /&gt;
        is on an access list. MS-DOS can't use this approach because it's&lt;br /&gt;
        an unprotected system; whatever flag it sets in memory to say that&lt;br /&gt;
        you have properly authenticated yourself can be set by a cheater&lt;br /&gt;
        program. OS/2 is a protect mode operating system and is secure from&lt;br /&gt;
        such manipulation provided that no untrusted real mode applications&lt;br /&gt;
        are executed.&lt;br /&gt;
2. A future OS/2 release will take advantage of the 80386&lt;br /&gt;
processor's virtual real mode facility to make it safe to run&lt;br /&gt;
untrusted real mode programs on an 80386.&lt;br /&gt;
2 A networking environment provides an extra&lt;br /&gt;
        challenge because you can write a program--perhaps running&lt;br /&gt;
        on an MS-DOS machine to avoid protection mechanisms--that &amp;quot;sniffs&amp;quot;&lt;br /&gt;
        the network, examining every communication. A client machine can't&lt;br /&gt;
        send a plain-text password over the network to authenticate its&lt;br /&gt;
        user because a sniffer could see it. And it certainly can't send a&lt;br /&gt;
        message saying, &amp;quot;I'm satisfied that this is really Hiram.&amp;quot; The&lt;br /&gt;
        client machine may be running bogus software that will lie and say&lt;br /&gt;
        that when it isn't true. In other words, a network authentication&lt;br /&gt;
        protocol must assume that &amp;quot;bad guys&amp;quot; can read all net transmissions&lt;br /&gt;
        and can generate any transmission they wish.&lt;br /&gt;
           As should be clear by now, a future OS/2 file system will&lt;br /&gt;
        support per-object permission lists. OS/2 will be enhanced&lt;br /&gt;
        to support users' identifying themselves by means of personal&lt;br /&gt;
        passwords. Future network software will support a secure network&lt;br /&gt;
        authentication protocol.&lt;br /&gt;
&lt;br /&gt;
     A new file system will do more than support access lists; it will also&lt;br /&gt;
support filenames longer than the FAT 8.3 convention, and it will support&lt;br /&gt;
extended file attributes. The FAT file system supports a very limited set&lt;br /&gt;
of attributes, each of which are binary flags--system, hidden, read-only,&lt;br /&gt;
and so on. Extended attributes allow an arbitrary set of attributes,&lt;br /&gt;
represented as text strings, to be associated with each file. Individual&lt;br /&gt;
applications will be able to define specific attributes, set them on files,&lt;br /&gt;
and later query their values. Extended attributes can be used, for example,&lt;br /&gt;
to name the application that created the file. This would allow a user to&lt;br /&gt;
click the mouse over the filename on a directory display and have the&lt;br /&gt;
presentation manager bring up the proper application on that file.&lt;br /&gt;
     Finally, although this file system wish list looks pretty good, how do&lt;br /&gt;
we know that we've covered all the bases? And will our new file system work&lt;br /&gt;
well with CD-ROM&lt;br /&gt;
3. Special versions of compact discs that contain digital data instead&lt;br /&gt;
of digitized music. When accessed via a modified CD player, they&lt;br /&gt;
provide approximately 600 MB of read-only storage.&lt;br /&gt;
3 disks and WORM drives? The answers are &amp;quot;We don't&amp;quot; and&lt;br /&gt;
&amp;quot;It doesn't,&amp;quot; so a future OS/2 release will support installable file&lt;br /&gt;
systems. An installable file system is similar to an installable device&lt;br /&gt;
driver. When the system is initialized, not only device drivers but new&lt;br /&gt;
file system management packages can be installed into OS/2. This will allow&lt;br /&gt;
specialized file systems to handle specialized devices such as CD-ROMs and&lt;br /&gt;
WORM, as well as providing an easy interface to media written on foreign&lt;br /&gt;
file systems that are on non-MS-DOS or non-OS/2 systems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2  The 80386&lt;br /&gt;
&lt;br /&gt;
Throughout this book, the name 80386 keeps cropping up, almost as a kind of&lt;br /&gt;
magical incantation. To a system designer, it is a magical device. It&lt;br /&gt;
provides the protection facilities of the 80286, but it also provides three&lt;br /&gt;
other key features.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.1  Large Segments&lt;br /&gt;
The 80386 has a segmented architecture very much like that of the 80286,&lt;br /&gt;
but 80286 segments are limited to 64 KB. On the 80386, segments can be as&lt;br /&gt;
large as 4 million KB; segments can be so large that an entire program can&lt;br /&gt;
run in 2 segments (one code and one data) and essentially ignore the&lt;br /&gt;
segmentation facilities of the processor. This is called flat model.&lt;br /&gt;
Writing programs that deal with large structures is easier using flat&lt;br /&gt;
model, and because compilers have a hard time generating optimal segmented&lt;br /&gt;
code, converting 8086/80286 large model programs to 80386 flat model can&lt;br /&gt;
produce dramatic increases in execution speed.&lt;br /&gt;
     Although a future release of OS/2 will certainly support large&lt;br /&gt;
segments and applications that use flat model internally, OS/2 will not&lt;br /&gt;
necessarily provide a flat model API. The system API for 32-bit&lt;br /&gt;
applications may continue to use segmented (that is, 48-bit) addresses.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.2  Multiple Real Mode Boxes&lt;br /&gt;
The 80386 provides a mode of execution called virtual real mode. Processes&lt;br /&gt;
that run in this mode execute instructions exactly as they would in real&lt;br /&gt;
mode, but they are not truly in real mode; they are in a special 8086-&lt;br /&gt;
compatible protected mode. The additional memory management and protection&lt;br /&gt;
facilities that this mode provides allow a future version of OS/2 to&lt;br /&gt;
support more than one real mode box at the same time; multiple real mode&lt;br /&gt;
applications will be able to execute simultaneously and to continue&lt;br /&gt;
executing while in background mode. The virtual real mode eliminates the&lt;br /&gt;
need for mode switching; thus, the user can execute real mode applications&lt;br /&gt;
while running communications applications that have a high interrupt&lt;br /&gt;
rate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.3  Full Protection Capability&lt;br /&gt;
The virtual real mode capability, coupled with the 80386's ability to&lt;br /&gt;
allow/disallow I/O access on a port-by-port basis, provides the hardware&lt;br /&gt;
foundation for a future OS/2 that is fully secure. In a fully secure OS/2,&lt;br /&gt;
the modules loaded in during bootup--the operating system itself, device&lt;br /&gt;
drivers, installable file systems, and so on--must be trusted, but no other&lt;br /&gt;
program can accidentally or deliberately damage others, read protected&lt;br /&gt;
files, or otherwise access or damage restricted data. The only damage an&lt;br /&gt;
aberrant or malicious program will be able to do is to slow down the&lt;br /&gt;
machine by hogging the resources, such as consuming most of the RAM or CPU&lt;br /&gt;
time. This is relatively harmless; the user can simply kill the offending&lt;br /&gt;
program and not run it anymore.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.4  Other Features&lt;br /&gt;
The 80386 contains other significant features besides speed, such as paged&lt;br /&gt;
virtual memory, that don't appear in an API or in a specific user benefit.&lt;br /&gt;
For this reason, we won't discuss them here other than to state the&lt;br /&gt;
obvious: An 80386 machine is generally considerably faster than an 80286-&lt;br /&gt;
based one.&lt;br /&gt;
     So what do these 80386 features mean for the 80286? What role will it&lt;br /&gt;
play in the near and far future? Should a developer write for the 80286 or&lt;br /&gt;
the 80386? First, OS/2 for the 80386&lt;br /&gt;
4. A product still under development at the time of this writing.&lt;br /&gt;
All releases of OS/2 will run on the 80386, but the initial OS/2&lt;br /&gt;
release treats the 80386 as a &amp;quot;fast 80286.&amp;quot; The only 80386&lt;br /&gt;
feature it uses is the faster mode-switching capability.&lt;br /&gt;
4 is the same operating system,&lt;br /&gt;
essentially, as OS/2 for the 80286. The only new API in 80386 OS/2 will be&lt;br /&gt;
the 32-bit wide one for 32-bit mode 80386-only binaries. The other&lt;br /&gt;
features--such as virtual memory, I/O permission mapping, and multiple real&lt;br /&gt;
mode boxes--are of value to the user but don't present any new APIs and&lt;br /&gt;
therefore are compatible with all applications. Certainly, taking advantage&lt;br /&gt;
of the 80386's new instruction order codes and 2^32-byte-length segments&lt;br /&gt;
will require a new API; in fact, a program must be specially written and&lt;br /&gt;
compiled for that environment. Only applications that can't function at all&lt;br /&gt;
using the smaller 80286-compatible segments need to become 80386 dependent;&lt;br /&gt;
80286 protect mode programs will run without change and without any&lt;br /&gt;
disadvantage on the 80386, taking advantage of its improved speed.&lt;br /&gt;
     To summarize, there is only one operating system, OS/2. OS/2 supports&lt;br /&gt;
16-bit protected mode applications that run on all machines, and OS/2 will&lt;br /&gt;
support 32-bit protected mode applications that will run only on 80386&lt;br /&gt;
machines. A developer should consider writing an application for the&lt;br /&gt;
32-bit model&lt;br /&gt;
5. When it is announced and documented.&lt;br /&gt;
5 only if the application performs so poorly in the 16-bit&lt;br /&gt;
model that a 16-bit version is worthless. Otherwise, one should develop&lt;br /&gt;
applications for the 16-bit model; such applications will run well on all&lt;br /&gt;
existing OS/2-compatible machines and on all OS/2 releases. Later, when the&lt;br /&gt;
80386 and OS/2-386 have sufficient market penetration, you may want to&lt;br /&gt;
release higher-performance upgrades to products that require the 80386.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.3  The Next Ten Years&lt;br /&gt;
&lt;br /&gt;
Microsoft believes that OS/2 will be a major influence in the personal&lt;br /&gt;
computer industry for roughly the next ten years. The standardization of&lt;br /&gt;
computing environments that mass market software brings about gives such&lt;br /&gt;
standards abnormal longevity, while the incredible rate of hardware&lt;br /&gt;
improvements brings on great pressure to change. As a result, we expect&lt;br /&gt;
OS/2 to live long and prosper, where long is a relative term in an industry&lt;br /&gt;
in which nothing can survive more than a decade. What might OS/2's&lt;br /&gt;
successor system look like? If we could answer that today, a successor&lt;br /&gt;
system would be unnecessary. Clearly, the increases in CPU performance will&lt;br /&gt;
continue. Personal computers will undoubtedly follow in the footsteps of&lt;br /&gt;
their supercomputer brethren and become used for more than calculation, but&lt;br /&gt;
also for simulation, modeling, and expert systems, not only in the&lt;br /&gt;
workplace but also in the home. The future will become clearer, over time,&lt;br /&gt;
as this most wonderful of tools continues to change its users.&lt;br /&gt;
     The development of OS/2 is, to date, the largest project that&lt;br /&gt;
Microsoft has ever taken on. From an initially very small group of&lt;br /&gt;
Microsoft engineers, to a still small joint Microsoft-IBM design team, to&lt;br /&gt;
finally a great many developers, builders, testers, and documenters from&lt;br /&gt;
both Microsoft and IBM, the project became known affectionately as the&lt;br /&gt;
&amp;quot;Black Hole.&amp;quot;&lt;br /&gt;
     As I write this, OS/2 is just weeks away from retail sale. It's been a&lt;br /&gt;
great pleasure for me and for the people who worked with me to see our&lt;br /&gt;
black hole begin to give back to our customers the fruits of the labors&lt;br /&gt;
that were poured into it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Glossary&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
anonymous pipe&lt;br /&gt;
a data storage buffer that OS/2 maintains in RAM; used for interprocess&lt;br /&gt;
communications.&lt;br /&gt;
&lt;br /&gt;
Applications Program Interface (API)&lt;br /&gt;
the set of calls a program uses to obtain services from the operating&lt;br /&gt;
system. The term API denotes a service interface, whatever its form.&lt;br /&gt;
&lt;br /&gt;
background category&lt;br /&gt;
a classification of processes that consists of those associated with a&lt;br /&gt;
screen group not currently being displayed.&lt;br /&gt;
&lt;br /&gt;
call gate&lt;br /&gt;
a special LDT or GDT entry that describes a subroutine entry point rather&lt;br /&gt;
than a memory segment. A far call to a call gate selector will cause a&lt;br /&gt;
transfer to the entry point specified in the call gate. This is a feature&lt;br /&gt;
of the 80286/80386 hardware and is normally used to provide a transition&lt;br /&gt;
from a lower privilege state to a higher one.&lt;br /&gt;
&lt;br /&gt;
captive thread&lt;br /&gt;
a thread that has been created by a dynlink package and that stays within&lt;br /&gt;
the dynlink code, never transferring back to the client process's code;&lt;br /&gt;
also a thread that is used to call a service entry point and that will&lt;br /&gt;
never return or that will return only if some specific event occurs.&lt;br /&gt;
&lt;br /&gt;
child process&lt;br /&gt;
a process created by another process (its parent process).&lt;br /&gt;
&lt;br /&gt;
closed system&lt;br /&gt;
hardware or software design that cannot be enhanced in the field by third-&lt;br /&gt;
party suppliers.&lt;br /&gt;
&lt;br /&gt;
command subtree&lt;br /&gt;
a process and all its descendants.&lt;br /&gt;
&lt;br /&gt;
context switch&lt;br /&gt;
the act of switching the CPU from the execution of one thread to another,&lt;br /&gt;
which may belong to the same process or to a different one.&lt;br /&gt;
&lt;br /&gt;
cooked mode&lt;br /&gt;
a mode established by programs for keyboard input. In cooked mode, OS/2&lt;br /&gt;
handles the line-editing characters such as the back space.&lt;br /&gt;
&lt;br /&gt;
critical section&lt;br /&gt;
a body of code that manipulates a data resource in a non-reentrant way.&lt;br /&gt;
&lt;br /&gt;
daemon program&lt;br /&gt;
a process that performs a utility function without interaction with the&lt;br /&gt;
user. For example, the swapper process is a daemon program.&lt;br /&gt;
&lt;br /&gt;
debuggee&lt;br /&gt;
the program being debugged.&lt;br /&gt;
&lt;br /&gt;
debugger&lt;br /&gt;
a program that helps the programmer locate the source of problems found&lt;br /&gt;
during runtime testing of a program.&lt;br /&gt;
&lt;br /&gt;
device driver&lt;br /&gt;
a program that transforms I/O requests made in a standard, device-&lt;br /&gt;
independent fashion into the operations necessary to make a specific piece&lt;br /&gt;
of hardware fulfill that request.&lt;br /&gt;
&lt;br /&gt;
device monitor&lt;br /&gt;
a mechanism that allows processes to track and/or modify device data&lt;br /&gt;
streams.&lt;br /&gt;
&lt;br /&gt;
disjoint LDT space&lt;br /&gt;
the LDT selectors reserved for memory objects that are shared or that may&lt;br /&gt;
be shared among processes.&lt;br /&gt;
&lt;br /&gt;
dynamic link&lt;br /&gt;
a method of postponing the resolution of external references until loadtime&lt;br /&gt;
or runtime. A dynamic link allows the called subroutines to be packaged,&lt;br /&gt;
dist ributed, and maintained independently of their callers. OS/2 extends&lt;br /&gt;
the dynamic link (or dynlink) mechanism to serve as the primary method by&lt;br /&gt;
which all system and nonsystem services are obtained.&lt;br /&gt;
&lt;br /&gt;
dynlink&lt;br /&gt;
see dynamic link.&lt;br /&gt;
&lt;br /&gt;
dynlink library&lt;br /&gt;
a file, in a special format, that contains the binary code for a group of&lt;br /&gt;
dynamically linked subroutines.&lt;br /&gt;
&lt;br /&gt;
dynlink routine&lt;br /&gt;
see dynamic link.&lt;br /&gt;
&lt;br /&gt;
dynlink subsystem&lt;br /&gt;
a dynlink module that provides a set of services built around a resource.&lt;br /&gt;
&lt;br /&gt;
encapsulation&lt;br /&gt;
the principle of hiding the internal implementation of a program, function,&lt;br /&gt;
or service so that its clients can tell what it does but not how it does&lt;br /&gt;
it.&lt;br /&gt;
&lt;br /&gt;
environment strings&lt;br /&gt;
a series of user-definable and program-definable strings that are&lt;br /&gt;
associated with each process. The initial values of environment strings are&lt;br /&gt;
established by a process's parent.&lt;br /&gt;
&lt;br /&gt;
exitlist&lt;br /&gt;
a list of subroutines that OS/2 calls when a process has terminated. The&lt;br /&gt;
exitlist is executed after process termination but before the process is&lt;br /&gt;
actually destroyed.&lt;br /&gt;
&lt;br /&gt;
Family Applications Program Interface (Family API)&lt;br /&gt;
a standard execution environment under MS-DOS versions 2.x and 3.x and&lt;br /&gt;
OS/2. The programmer can use the Family API to create an application that&lt;br /&gt;
uses a subset of OS/2 functions (but a superset of MS-DOS 3.x functions)&lt;br /&gt;
and that runs in a binary-compatible fashion under MS-DOS versions 2.x and&lt;br /&gt;
3.x and OS/2.&lt;br /&gt;
&lt;br /&gt;
file handle&lt;br /&gt;
a binary value that represents an open file; used in all file I/O calls.&lt;br /&gt;
&lt;br /&gt;
file locking&lt;br /&gt;
an OS/2 facility that allows one program to temporarily prevent other&lt;br /&gt;
programs from reading and/or writing a particular file.&lt;br /&gt;
&lt;br /&gt;
file system name space&lt;br /&gt;
names that have the format of filenames. All such names will eventually&lt;br /&gt;
represent disk &amp;quot;files&amp;quot;--data or special. Initially, some of these names are&lt;br /&gt;
kept in internal OS/2 RAM tables and are not present on any disk volume.&lt;br /&gt;
&lt;br /&gt;
forced event&lt;br /&gt;
an event or action that is forced upon a thread or a process from an&lt;br /&gt;
external source; for example, a Ctrl-C or a DosKill command.&lt;br /&gt;
&lt;br /&gt;
foreground category&lt;br /&gt;
a classification of processes that consists of those associated with the&lt;br /&gt;
currently active screen group.&lt;br /&gt;
&lt;br /&gt;
GDT&lt;br /&gt;
see global descriptor table.&lt;br /&gt;
&lt;br /&gt;
general priority category&lt;br /&gt;
the OS/2 classification of threads that consists of three subcategories:&lt;br /&gt;
background, foreground, and interactive.&lt;br /&gt;
&lt;br /&gt;
general protection (GP) fault&lt;br /&gt;
an error that occurs when a program accesses invalid memory locations or&lt;br /&gt;
accesses valid locations in an invalid way (such as writing into read-only&lt;br /&gt;
memory areas).&lt;br /&gt;
&lt;br /&gt;
giveaway shared memory&lt;br /&gt;
a shared memory mechanism in which a process that already has access to the&lt;br /&gt;
segment can grant access to another process. Processes cannot obtain access&lt;br /&gt;
for themselves; access must be granted by another process that already has&lt;br /&gt;
access.&lt;br /&gt;
&lt;br /&gt;
global data segment&lt;br /&gt;
a data segment that is shared among all instances of a dynlink routine; in&lt;br /&gt;
other words, a single segment that is accessible to all processes that call&lt;br /&gt;
a particular dynlink routine.&lt;br /&gt;
&lt;br /&gt;
global descriptor table (GDT)&lt;br /&gt;
an element of the 80286/80386 memory management hardware. The GDT holds the&lt;br /&gt;
descriptions of as many as 4095 global segments. A global segment is&lt;br /&gt;
accessible to all processes.&lt;br /&gt;
&lt;br /&gt;
global subsystem initialization&lt;br /&gt;
a facility that allows a dynlink routine to specify that its initialize&lt;br /&gt;
entry point should be called when the dynlink package is loaded on behalf&lt;br /&gt;
of its first client.&lt;br /&gt;
&lt;br /&gt;
grandparent process&lt;br /&gt;
the parent process of a process that created a process.&lt;br /&gt;
&lt;br /&gt;
handle&lt;br /&gt;
an arbitrary integer value that OS/2 returns to a process so that the&lt;br /&gt;
process can return it to OS/2 on subsequent calls; known to programmers as&lt;br /&gt;
a magic cookie.&lt;br /&gt;
&lt;br /&gt;
hard error&lt;br /&gt;
an error that the system detects but which it cannot correct without user&lt;br /&gt;
intervention.&lt;br /&gt;
&lt;br /&gt;
hard error daemon&lt;br /&gt;
a daemon process that services hard errors. The hard error daemon may be an&lt;br /&gt;
independent process, or it may be a thread that belongs to the session&lt;br /&gt;
manager or to the presentation manager.&lt;br /&gt;
&lt;br /&gt;
huge segments&lt;br /&gt;
a software technique that allows the creation and use of pseudo segments&lt;br /&gt;
larger than 65 KB.&lt;br /&gt;
&lt;br /&gt;
installable file system (IFS)&lt;br /&gt;
a body of code that OS/2 loads at boot time and that provides the software&lt;br /&gt;
to manage a file system on a storage device, including the ability to&lt;br /&gt;
create and maintain directories, allocate disk space, and so on.&lt;br /&gt;
&lt;br /&gt;
instance data segment&lt;br /&gt;
a memory segment that holds data specific to each instance of the dynlink&lt;br /&gt;
routine.&lt;br /&gt;
&lt;br /&gt;
instance subsystem initialization&lt;br /&gt;
a service that dynlink routines can request. A dynlink routine's initialize&lt;br /&gt;
entry point is called each time a new client is linked to the routine.&lt;br /&gt;
&lt;br /&gt;
interactive category&lt;br /&gt;
a classification of processes that consists of the process currently&lt;br /&gt;
interacting with the keyboard.&lt;br /&gt;
&lt;br /&gt;
interactive program&lt;br /&gt;
a program whose function is to obey commands from a user, such as an editor&lt;br /&gt;
or a spreadsheet program. Programs such as compilers may literally interact&lt;br /&gt;
by asking for filenames and compilation options, but they are considered&lt;br /&gt;
noninteractive because their function is to compile a source program, not&lt;br /&gt;
to provide answers to user-entered commands.&lt;br /&gt;
&lt;br /&gt;
interprocess communications (IPC)&lt;br /&gt;
the ability of processes and threads to transfer data and messages among&lt;br /&gt;
themselves; used to offer services to and receive services from other&lt;br /&gt;
programs.&lt;br /&gt;
&lt;br /&gt;
interruptible block&lt;br /&gt;
a special form of a blocking operation used inside the OS/2  kernel so that&lt;br /&gt;
events such as process kill and Ctrl-C can interrupt a thread that is&lt;br /&gt;
waiting, inside OS/2, for an event.&lt;br /&gt;
&lt;br /&gt;
I/O privilege mechanism&lt;br /&gt;
a facility that allows a process to ask a device driver for direct access&lt;br /&gt;
to the device's I/O ports and any dedicated or mapped memory locations it&lt;br /&gt;
has. The I/O privilege mechanism can be used directly by an application or&lt;br /&gt;
indirectly by a dynlink package.&lt;br /&gt;
&lt;br /&gt;
IPC&lt;br /&gt;
see interprocess communications.&lt;br /&gt;
&lt;br /&gt;
KBD&lt;br /&gt;
an abbreviated name for the dynlink package that manages the keyboard&lt;br /&gt;
device. All its entry points start with Kbd.&lt;br /&gt;
&lt;br /&gt;
kernel&lt;br /&gt;
the central part of OS/2. It resides permanently in fixed memory locations&lt;br /&gt;
and executes in the privileged ring 0 state.&lt;br /&gt;
&lt;br /&gt;
LDT&lt;br /&gt;
see local descriptor table.&lt;br /&gt;
&lt;br /&gt;
loadtime dynamic linking&lt;br /&gt;
the act of connecting a client process to dynamic link libraries when the&lt;br /&gt;
process is first loaded into memory.&lt;br /&gt;
&lt;br /&gt;
local descriptor table (LDT)&lt;br /&gt;
an element of the 80286/80386 memory management hardware. The LDT holds the&lt;br /&gt;
descriptions of as many as 4095 local segments. Each process has its own&lt;br /&gt;
LDT and cannot access the LDTs of other processes.&lt;br /&gt;
&lt;br /&gt;
logical device&lt;br /&gt;
a symbolic name for a device that the user can cause to be mapped to any&lt;br /&gt;
physical (actual) device.&lt;br /&gt;
&lt;br /&gt;
logical directory&lt;br /&gt;
a symbolic name for a directory that the user can cause to be mapped to any&lt;br /&gt;
actual drive and directory.&lt;br /&gt;
&lt;br /&gt;
low priority category&lt;br /&gt;
a classification of processes that consists of processes that get CPU time&lt;br /&gt;
only when no other thread in the other categories needs it; this category&lt;br /&gt;
is lower in priority than the general priority category.&lt;br /&gt;
&lt;br /&gt;
magic cookie&lt;br /&gt;
see handle.&lt;br /&gt;
&lt;br /&gt;
memory manager&lt;br /&gt;
the section of OS/2 that allocates both physical memory and virtual memory.&lt;br /&gt;
&lt;br /&gt;
memory overcommit&lt;br /&gt;
allocating more memory to the running program than physically exists.&lt;br /&gt;
&lt;br /&gt;
memory suballocation&lt;br /&gt;
the OS/2 facility that allocates pieces of memory from within an&lt;br /&gt;
application's segment.&lt;br /&gt;
&lt;br /&gt;
MOU&lt;br /&gt;
an abbreviated name for the dynlink package that manages the mouse device.&lt;br /&gt;
All its entry points start with Mou.&lt;br /&gt;
&lt;br /&gt;
multitasking operating system&lt;br /&gt;
an operating system in which two or more programs/threads can execute&lt;br /&gt;
simultaneously.&lt;br /&gt;
&lt;br /&gt;
named pipe&lt;br /&gt;
a data storage buffer that OS/2 maintains in RAM; used for interprocess&lt;br /&gt;
communication.&lt;br /&gt;
&lt;br /&gt;
named shared memory&lt;br /&gt;
a memory segment that can be accessed simultaneously by more than one&lt;br /&gt;
process. Its name allows processes to request access to it.&lt;br /&gt;
&lt;br /&gt;
open system&lt;br /&gt;
hardware or software design that allows third-party additions and upgrades&lt;br /&gt;
in the field.&lt;br /&gt;
&lt;br /&gt;
object name buffer&lt;br /&gt;
the area in which OS/2 returns a character string if the DosExecPgm&lt;br /&gt;
function fails.&lt;br /&gt;
&lt;br /&gt;
parallel multitasking&lt;br /&gt;
the process whereby programs execute simultaneously.&lt;br /&gt;
&lt;br /&gt;
parent process&lt;br /&gt;
a process that creates another process, which is called the child process.&lt;br /&gt;
&lt;br /&gt;
physical memory&lt;br /&gt;
the RAM (Random Access Memory) physically present inside the machine.&lt;br /&gt;
&lt;br /&gt;
PID (Process Identification Number)&lt;br /&gt;
a unique code that OS/2 assigns to a process when the process is created.&lt;br /&gt;
The PID may be any value except 0.&lt;br /&gt;
&lt;br /&gt;
pipe&lt;br /&gt;
see anonymous pipe; named pipe.&lt;br /&gt;
&lt;br /&gt;
presentation manager&lt;br /&gt;
the graphical user interface for OS/2.&lt;br /&gt;
&lt;br /&gt;
priority&lt;br /&gt;
(also known as CPU priority) the numeric value assigned to each runnable&lt;br /&gt;
thread in the system. Threads with a higher priority are assigned the CPU&lt;br /&gt;
in preference to those with a lower priority.&lt;br /&gt;
&lt;br /&gt;
privilege mode&lt;br /&gt;
a special execution mode (also known as ring 0) supported by the&lt;br /&gt;
80286/80386 hardware. Code executing in this mode can execute restricted&lt;br /&gt;
instructions that are used to manipulate key system structures and tables.&lt;br /&gt;
Only the OS/2 kernel and device drivers run in this mode.&lt;br /&gt;
&lt;br /&gt;
process&lt;br /&gt;
the executing instance of a binary file. In OS/2, the terms task and&lt;br /&gt;
process are used interchangeably. A process is the unit of ownership, and&lt;br /&gt;
processes own resources such as memory, open files, dynlink libraries, and&lt;br /&gt;
semaphores.&lt;br /&gt;
&lt;br /&gt;
protect mode&lt;br /&gt;
the operating mode of the 80286 microprocessor that allows the operating&lt;br /&gt;
system to use features that protect one application from another; also&lt;br /&gt;
called protected mode.&lt;br /&gt;
&lt;br /&gt;
queue&lt;br /&gt;
an orderly list of elements waiting for processing.&lt;br /&gt;
&lt;br /&gt;
RAM semaphore&lt;br /&gt;
a kind of semaphore that is based in memory accessible to a thread; fast,&lt;br /&gt;
but with limited functionality. See system semaphore.&lt;br /&gt;
&lt;br /&gt;
raw mode&lt;br /&gt;
a mode established by programs for keyboard input. In raw mode OS/2 passes&lt;br /&gt;
to the caller each character typed immediately as it is typed. The caller&lt;br /&gt;
is responsible for handling line-editing characters such as the back space.&lt;br /&gt;
&lt;br /&gt;
real mode&lt;br /&gt;
the operating mode of the 80286 microprocessor that runs programs designed&lt;br /&gt;
for the 8086/8088 microprocessor.&lt;br /&gt;
&lt;br /&gt;
record locking&lt;br /&gt;
the mechanism that allows a process to lock a range of bytes within a file.&lt;br /&gt;
While the lock is in effect, no other process can read or write those&lt;br /&gt;
bytes.&lt;br /&gt;
&lt;br /&gt;
ring 3&lt;br /&gt;
the privilege level that is used to run applications. Code executing at&lt;br /&gt;
this level cannot modify critical system structures.&lt;br /&gt;
&lt;br /&gt;
runtime dynamic linking&lt;br /&gt;
the act of establishing a dynamic link after a process has begun execution.&lt;br /&gt;
This is done by providing OS/2 with the module and entry point names; OS/2&lt;br /&gt;
returns the address of the routine.&lt;br /&gt;
&lt;br /&gt;
scheduler&lt;br /&gt;
the part of OS/2 that decides which thread to run and how long to run it&lt;br /&gt;
before assigning the CPU to another thread; also, the part of OS/2 that&lt;br /&gt;
determines the priority value for each thread.&lt;br /&gt;
&lt;br /&gt;
screen group&lt;br /&gt;
a group of one or more processes that share (generally in a serial fashion)&lt;br /&gt;
a single logical screen and keyboard.&lt;br /&gt;
&lt;br /&gt;
semaphore&lt;br /&gt;
a software flag or signal used to coordinate the activities of two or more&lt;br /&gt;
threads; commonly used to protect a critical section.&lt;br /&gt;
&lt;br /&gt;
serial multitasking&lt;br /&gt;
the process whereby multiple programs execute, but only one at a time.&lt;br /&gt;
&lt;br /&gt;
session manager&lt;br /&gt;
a system utility that manages screen group switching. The session manager&lt;br /&gt;
is used only in the absence of the presentation manager; the presentation&lt;br /&gt;
manager replaces the session manager.&lt;br /&gt;
&lt;br /&gt;
shared memory&lt;br /&gt;
a memory segment that can be accessed simultaneously by more than one&lt;br /&gt;
process.&lt;br /&gt;
&lt;br /&gt;
signaling&lt;br /&gt;
using semaphores to notify threads that certain events or activities have&lt;br /&gt;
taken place.&lt;br /&gt;
&lt;br /&gt;
signals&lt;br /&gt;
notification mechanisms implemented in software that operate in a fashion&lt;br /&gt;
analogous to hardware interrupts.&lt;br /&gt;
&lt;br /&gt;
software tools approach&lt;br /&gt;
a design philosophy in which each program and application in a package is&lt;br /&gt;
dedicated to performing a specific task and doing that task very well. See&lt;br /&gt;
also encapsulation.&lt;br /&gt;
&lt;br /&gt;
stack frame&lt;br /&gt;
a portion of a thread's stack that contains a procedure's local variables&lt;br /&gt;
and parameters.&lt;br /&gt;
&lt;br /&gt;
static linking&lt;br /&gt;
the combining of multiple compilands into a single executable file, thereby&lt;br /&gt;
resolving undefined external references.&lt;br /&gt;
&lt;br /&gt;
single-tasking&lt;br /&gt;
a computer environment in which only one program runs at a time.&lt;br /&gt;
&lt;br /&gt;
swapping&lt;br /&gt;
the technique by which some code or data in memory is written to a disk&lt;br /&gt;
file, thus allowing the memory it was using to be reused for another&lt;br /&gt;
purpose.&lt;br /&gt;
&lt;br /&gt;
system semaphore&lt;br /&gt;
a semaphore that is implemented in OS/2's internal memory area; somewhat&lt;br /&gt;
slower than RAM semaphores, but providing more features.&lt;br /&gt;
&lt;br /&gt;
System File Table (SFT)&lt;br /&gt;
an internal OS/2 table that contains an entry for every file currently&lt;br /&gt;
open.&lt;br /&gt;
&lt;br /&gt;
task&lt;br /&gt;
see process.&lt;br /&gt;
&lt;br /&gt;
thread&lt;br /&gt;
the OS/2 mechanism that allows more than one path of execution through the&lt;br /&gt;
same instance of an application program.&lt;br /&gt;
&lt;br /&gt;
thread ID&lt;br /&gt;
the handle of a particular thread within a process.&lt;br /&gt;
&lt;br /&gt;
thread of execution&lt;br /&gt;
the passage of the CPU through the instruction sequence.&lt;br /&gt;
&lt;br /&gt;
time-critical priority&lt;br /&gt;
a classification of processes that may be interactive or noninteractive, in&lt;br /&gt;
the foreground or background screen group, which have a higher priority&lt;br /&gt;
than any non-time-critical thread in the system.&lt;br /&gt;
&lt;br /&gt;
time slice&lt;br /&gt;
the amount of execution time that the scheduler will give a thread before&lt;br /&gt;
reassigning the CPU to another thread of equal priority.&lt;br /&gt;
&lt;br /&gt;
VIO&lt;br /&gt;
an abbreviated name of the dynlink package that manages the display device.&lt;br /&gt;
All its entry points start with Vio.&lt;br /&gt;
&lt;br /&gt;
virtual memory&lt;br /&gt;
the memory space allocated to and used by a process. At the time it is&lt;br /&gt;
being referenced, the virtual memory must be present in physical memory,&lt;br /&gt;
but otherwise it may be swapped to a disk file.&lt;br /&gt;
&lt;br /&gt;
virtualization&lt;br /&gt;
the general technique of hiding a complicated actual situation behind a&lt;br /&gt;
simple, standard interface.&lt;br /&gt;
&lt;br /&gt;
writethrough&lt;br /&gt;
an option available when a file write operation is performed which&lt;br /&gt;
specifies that the normal caching mechanism is to be sidestepped and the&lt;br /&gt;
data is to be written through to the disk surface immediately.&lt;br /&gt;
&lt;br /&gt;
3x box&lt;br /&gt;
the OS/2 environment that emulates an 8086-based PC running MS-DOS versions&lt;br /&gt;
2.x or 3.x.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Index&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Symbols&lt;br /&gt;
----&lt;br /&gt;
3x box&lt;br /&gt;
80286 processor&lt;br /&gt;
   bugs in&lt;br /&gt;
   I/O access control in&lt;br /&gt;
   segmented architecture of&lt;br /&gt;
   size of segments&lt;br /&gt;
80386 processor&lt;br /&gt;
   I/O access control in&lt;br /&gt;
   key features of&lt;br /&gt;
8080 processor&lt;br /&gt;
8086/8088 processor&lt;br /&gt;
   memory limitations of&lt;br /&gt;
   real mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A&lt;br /&gt;
----&lt;br /&gt;
Abort, Retry, Ignore message (MS-DOS)&lt;br /&gt;
access lists&lt;br /&gt;
addresses&lt;br /&gt;
   invalid&lt;br /&gt;
   subroutine&lt;br /&gt;
addressing, huge model&lt;br /&gt;
address offsets&lt;br /&gt;
address space, linear&lt;br /&gt;
allocation. See also memory&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
anonymous pipes&lt;br /&gt;
API&lt;br /&gt;
   80386 processor&lt;br /&gt;
   Family&lt;br /&gt;
   memory management&lt;br /&gt;
Apple Macintosh&lt;br /&gt;
application environment. See environment&lt;br /&gt;
application mode&lt;br /&gt;
applications&lt;br /&gt;
   ``combo''&lt;br /&gt;
   command&lt;br /&gt;
   communicating between&lt;br /&gt;
   compatibility with MS-DOS&lt;br /&gt;
   designing for both OS/2 and MS-DOS&lt;br /&gt;
   device monitors and&lt;br /&gt;
   dual mode&lt;br /&gt;
   I/O-bound&lt;br /&gt;
   protecting&lt;br /&gt;
   real mode&lt;br /&gt;
   running MS-DOS&lt;br /&gt;
   time-critical&lt;br /&gt;
Applications Program Interface. See API&lt;br /&gt;
   Family&lt;br /&gt;
architecture&lt;br /&gt;
   80386 processor&lt;br /&gt;
   design concepts of OS/2&lt;br /&gt;
   device driver&lt;br /&gt;
   I/O&lt;br /&gt;
   segmented&lt;br /&gt;
arguments&lt;br /&gt;
   DosCWait&lt;br /&gt;
   DosExecPgm&lt;br /&gt;
ASCII text strings&lt;br /&gt;
ASCIIZ strings&lt;br /&gt;
asynchronous I/O&lt;br /&gt;
asynchronous processing&lt;br /&gt;
atomic operation&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
B&lt;br /&gt;
----&lt;br /&gt;
background&lt;br /&gt;
   category&lt;br /&gt;
   I/O&lt;br /&gt;
   processing&lt;br /&gt;
   threads and applications&lt;br /&gt;
base segment&lt;br /&gt;
BAT files, MS-DOS&lt;br /&gt;
BIOS entry vector, hooking the&lt;br /&gt;
blocking services&lt;br /&gt;
block mode&lt;br /&gt;
   device drivers&lt;br /&gt;
   driver algorithm&lt;br /&gt;
blocks, interruptible&lt;br /&gt;
boot process&lt;br /&gt;
boot time, installing device drivers at&lt;br /&gt;
breakthroughs, technological&lt;br /&gt;
buffer reusability&lt;br /&gt;
buffers&lt;br /&gt;
   flushing&lt;br /&gt;
   monitor&lt;br /&gt;
bugs&lt;br /&gt;
   80286 processor&lt;br /&gt;
   program&lt;br /&gt;
byte-stream mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
C&lt;br /&gt;
----&lt;br /&gt;
call gate&lt;br /&gt;
call and return sequence, OS/2&lt;br /&gt;
call statements, writing for dynamic link routines&lt;br /&gt;
call timed out error&lt;br /&gt;
capability tokens&lt;br /&gt;
captive threads&lt;br /&gt;
categories, priority&lt;br /&gt;
Central Processing Unit. See CPU&lt;br /&gt;
character mode&lt;br /&gt;
   device driver model for&lt;br /&gt;
child processes&lt;br /&gt;
   controlling&lt;br /&gt;
CHKDSK&lt;br /&gt;
circular references&lt;br /&gt;
CLI instruction&lt;br /&gt;
client processes&lt;br /&gt;
closed system&lt;br /&gt;
clusters&lt;br /&gt;
CMD.EXE&lt;br /&gt;
   I/O architecture and&lt;br /&gt;
   logical device and directory names&lt;br /&gt;
code, swapping&lt;br /&gt;
CodeView&lt;br /&gt;
command application&lt;br /&gt;
COMMAND.COM&lt;br /&gt;
command mode&lt;br /&gt;
command processes&lt;br /&gt;
command subtrees&lt;br /&gt;
   controlling&lt;br /&gt;
command threads&lt;br /&gt;
communication, interprocess&lt;br /&gt;
compatibility&lt;br /&gt;
   downward&lt;br /&gt;
   functional&lt;br /&gt;
   levels of&lt;br /&gt;
   MS-DOS&lt;br /&gt;
   name generation and&lt;br /&gt;
   VIO and presentation manager&lt;br /&gt;
compatibility box&lt;br /&gt;
compatibility issues, OS/2&lt;br /&gt;
compatibility mode&lt;br /&gt;
computers&lt;br /&gt;
   mental work and&lt;br /&gt;
   multiple CPU&lt;br /&gt;
   networked (see also networks)&lt;br /&gt;
   Von Neumann&lt;br /&gt;
CONFIG.SYS file&lt;br /&gt;
consistency, design&lt;br /&gt;
context switching&lt;br /&gt;
conventions, system&lt;br /&gt;
cooked mode&lt;br /&gt;
CP/M, compatibility with&lt;br /&gt;
CP/M-80&lt;br /&gt;
CPU See also 8086/8088 processor, 80286 processor, 80386 processor&lt;br /&gt;
   priority&lt;br /&gt;
crashes, system&lt;br /&gt;
critical sections&lt;br /&gt;
   protecting&lt;br /&gt;
   signals and&lt;br /&gt;
CS register&lt;br /&gt;
Ctrl-Break and Ctrl-C&lt;br /&gt;
customized environment&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
D&lt;br /&gt;
----&lt;br /&gt;
daemon, hard error&lt;br /&gt;
daemon interfaces, dynamic links as&lt;br /&gt;
daemon program&lt;br /&gt;
data&lt;br /&gt;
   global&lt;br /&gt;
   handling in dynamic linking&lt;br /&gt;
   instance&lt;br /&gt;
   instructions and&lt;br /&gt;
   swapped-out&lt;br /&gt;
data integrity&lt;br /&gt;
data segments, executing from&lt;br /&gt;
data streams, monitoring&lt;br /&gt;
debuggee&lt;br /&gt;
debugger&lt;br /&gt;
   system&lt;br /&gt;
debugging&lt;br /&gt;
demand loading&lt;br /&gt;
demand load segment&lt;br /&gt;
densities, disk&lt;br /&gt;
design, concepts of OS/2&lt;br /&gt;
design goals&lt;br /&gt;
DevHlp&lt;br /&gt;
device data streams&lt;br /&gt;
device drivers&lt;br /&gt;
   architecture of&lt;br /&gt;
   block mode&lt;br /&gt;
   character mode&lt;br /&gt;
   code structure&lt;br /&gt;
   definition of&lt;br /&gt;
   dynamic link pseudo&lt;br /&gt;
   OS/2 communication and&lt;br /&gt;
   programming model for&lt;br /&gt;
device independence&lt;br /&gt;
   definition of&lt;br /&gt;
device management&lt;br /&gt;
device monitors&lt;br /&gt;
device names&lt;br /&gt;
devices&lt;br /&gt;
   direct access of&lt;br /&gt;
   logical&lt;br /&gt;
device-specific code, encapsulating&lt;br /&gt;
Digital Research&lt;br /&gt;
direct device access&lt;br /&gt;
directories&lt;br /&gt;
   ISAM&lt;br /&gt;
   logical&lt;br /&gt;
   working&lt;br /&gt;
directory names&lt;br /&gt;
directory tree hierarchy&lt;br /&gt;
disjoint LDT space&lt;br /&gt;
disk data synchronization&lt;br /&gt;
disk I/O requests&lt;br /&gt;
disk seek times&lt;br /&gt;
disk space, allocation of&lt;br /&gt;
DISKCOMP&lt;br /&gt;
DISKCOPY&lt;br /&gt;
disks&lt;br /&gt;
   laser&lt;br /&gt;
   unlabeled&lt;br /&gt;
dispatcher&lt;br /&gt;
display device, manipulating the&lt;br /&gt;
display memory&lt;br /&gt;
.DLL files&lt;br /&gt;
DosAllocHuge&lt;br /&gt;
DosAllocSeg&lt;br /&gt;
DosAllocShrSeg&lt;br /&gt;
DosBufReset&lt;br /&gt;
DosCallNmPipe&lt;br /&gt;
DosCalls&lt;br /&gt;
DosClose&lt;br /&gt;
DosConnectNmPipe&lt;br /&gt;
DosCreateCSAlias&lt;br /&gt;
DosCreateSem&lt;br /&gt;
DosCreateThread&lt;br /&gt;
DosCWait&lt;br /&gt;
DosDevIOCtl&lt;br /&gt;
DosDupHandle&lt;br /&gt;
DosEnterCritSec&lt;br /&gt;
DosErrClass&lt;br /&gt;
DosError&lt;br /&gt;
DosExecPgm&lt;br /&gt;
DosExit&lt;br /&gt;
DosExitCritSec&lt;br /&gt;
DosExitList&lt;br /&gt;
DosFindFirst&lt;br /&gt;
DosFindNext&lt;br /&gt;
DosFlagProcess&lt;br /&gt;
DosFreeModule&lt;br /&gt;
DosFreeSeg&lt;br /&gt;
DosGetHugeShift&lt;br /&gt;
DosGetMachineMode&lt;br /&gt;
DosGetProcAddr&lt;br /&gt;
DosGiveSeg&lt;br /&gt;
DosHoldSignal&lt;br /&gt;
DosKill&lt;br /&gt;
DosKillProcess&lt;br /&gt;
DosLoadModule&lt;br /&gt;
DosMakeNmPipe&lt;br /&gt;
DosMakePipe&lt;br /&gt;
DosMonRead&lt;br /&gt;
DosMonReq&lt;br /&gt;
DosMonWrite&lt;br /&gt;
DosMuxSemWait&lt;br /&gt;
DosNewSize&lt;br /&gt;
DosOpen&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosPeekNmPipe&lt;br /&gt;
DosPtrace&lt;br /&gt;
DosRead&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosReadQueue&lt;br /&gt;
DosReallocHuge&lt;br /&gt;
DosResumeThread&lt;br /&gt;
DosScanEnv&lt;br /&gt;
DosSearchPath&lt;br /&gt;
DosSemClear&lt;br /&gt;
DosSemRequest&lt;br /&gt;
DosSemSet&lt;br /&gt;
DosSemWait&lt;br /&gt;
DosSetFHandState&lt;br /&gt;
DosSetPrty&lt;br /&gt;
DosSetSigHandler&lt;br /&gt;
DosSetVerify&lt;br /&gt;
DosSleep&lt;br /&gt;
DosSubAlloc&lt;br /&gt;
DosSubFrees&lt;br /&gt;
DosSuspendThread&lt;br /&gt;
DosTimerAsync&lt;br /&gt;
DosTimerStart&lt;br /&gt;
DosTimerStop&lt;br /&gt;
DosTransactNmPipe&lt;br /&gt;
DosWrite&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosWriteQueue&lt;br /&gt;
downward compatibility&lt;br /&gt;
DPATH&lt;br /&gt;
drive-oriented operations&lt;br /&gt;
drives, high- and low-density&lt;br /&gt;
dual mode&lt;br /&gt;
Dynamic Data Exchange (DDE)&lt;br /&gt;
dynamic linking&lt;br /&gt;
   Family API use of&lt;br /&gt;
   loadtime&lt;br /&gt;
   runtime&lt;br /&gt;
dynamic link libraries&lt;br /&gt;
dynamic link routines, calling&lt;br /&gt;
dynamic links&lt;br /&gt;
   architectural role of&lt;br /&gt;
   circular references in&lt;br /&gt;
   details on implementing&lt;br /&gt;
   device drivers and&lt;br /&gt;
   interfaces to other processes&lt;br /&gt;
   naming&lt;br /&gt;
   side effects of&lt;br /&gt;
   using for pseudo device drivers&lt;br /&gt;
dynlink See also dynamic links&lt;br /&gt;
   routines&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
E&lt;br /&gt;
----&lt;br /&gt;
encapsulation&lt;br /&gt;
   device driver&lt;br /&gt;
entry ordinals&lt;br /&gt;
entry point name&lt;br /&gt;
environment&lt;br /&gt;
   customized&lt;br /&gt;
   dual mode&lt;br /&gt;
   protected&lt;br /&gt;
   single-tasking&lt;br /&gt;
   single-tasking versus multitasking&lt;br /&gt;
   stable&lt;br /&gt;
   stand-alone&lt;br /&gt;
   virtualizing the&lt;br /&gt;
environment block&lt;br /&gt;
environment strings&lt;br /&gt;
EnvPointer&lt;br /&gt;
EnvString&lt;br /&gt;
error codes, compatibility mode&lt;br /&gt;
errors&lt;br /&gt;
   general protection fault&lt;br /&gt;
   hard&lt;br /&gt;
   localization of&lt;br /&gt;
   program&lt;br /&gt;
   timed out&lt;br /&gt;
.EXE files&lt;br /&gt;
   executing&lt;br /&gt;
   Family API&lt;br /&gt;
EXEC function (MS-DOS)&lt;br /&gt;
execute threads&lt;br /&gt;
executing programs&lt;br /&gt;
execution speed&lt;br /&gt;
exitlist&lt;br /&gt;
expandability&lt;br /&gt;
extended file attributes&lt;br /&gt;
extended partitioning&lt;br /&gt;
extension, filename&lt;br /&gt;
external name, dynamic link&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
F&lt;br /&gt;
----&lt;br /&gt;
Family API&lt;br /&gt;
   drawbacks of&lt;br /&gt;
Family Applications Program Interface (Family API)&lt;br /&gt;
far return instruction&lt;br /&gt;
fault, memory not present&lt;br /&gt;
fault errors, general protection&lt;br /&gt;
faults, GP&lt;br /&gt;
features&lt;br /&gt;
   additional&lt;br /&gt;
   introducing new operating system&lt;br /&gt;
file&lt;br /&gt;
   allocation, improving&lt;br /&gt;
   attributes, extended&lt;br /&gt;
   handles&lt;br /&gt;
   I/O&lt;br /&gt;
   limits, floppy disk&lt;br /&gt;
   locking&lt;br /&gt;
   protection&lt;br /&gt;
   seek position&lt;br /&gt;
   sharing&lt;br /&gt;
   system&lt;br /&gt;
File Allocation Table&lt;br /&gt;
filenames, OS/2 and MS-DOS&lt;br /&gt;
files&lt;br /&gt;
   .DLL&lt;br /&gt;
   .EXE&lt;br /&gt;
   linking&lt;br /&gt;
   naming&lt;br /&gt;
   .OBJ&lt;br /&gt;
file system&lt;br /&gt;
   future of&lt;br /&gt;
   hierarchical&lt;br /&gt;
   installable&lt;br /&gt;
file system name space&lt;br /&gt;
   named pipes and&lt;br /&gt;
file utilization&lt;br /&gt;
FIND utility program&lt;br /&gt;
flags register&lt;br /&gt;
flat model, 80386 processor&lt;br /&gt;
flexibility, maximum&lt;br /&gt;
flush operation, buffer&lt;br /&gt;
forced events&lt;br /&gt;
foreground&lt;br /&gt;
   category&lt;br /&gt;
   processing&lt;br /&gt;
   threads and applications&lt;br /&gt;
fprintf&lt;br /&gt;
fragmentation&lt;br /&gt;
   disk&lt;br /&gt;
   internal&lt;br /&gt;
functions&lt;br /&gt;
   addition of&lt;br /&gt;
   resident&lt;br /&gt;
future versions of OS/2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
G&lt;br /&gt;
----&lt;br /&gt;
garbage collection&lt;br /&gt;
garbage handles&lt;br /&gt;
general failure error&lt;br /&gt;
general priority category&lt;br /&gt;
general protection fault (GP fault)&lt;br /&gt;
   errors&lt;br /&gt;
GetDosVar&lt;br /&gt;
giveaway shared memory&lt;br /&gt;
global data&lt;br /&gt;
global data segments&lt;br /&gt;
Global Descriptor Table (GDT)&lt;br /&gt;
global subsystem initialization&lt;br /&gt;
glossary&lt;br /&gt;
goals&lt;br /&gt;
   design&lt;br /&gt;
   OS/2&lt;br /&gt;
GP faults&lt;br /&gt;
grandparent processes&lt;br /&gt;
graphical user interface&lt;br /&gt;
   standard&lt;br /&gt;
graphics, VIO and&lt;br /&gt;
graphics devices&lt;br /&gt;
graphics drivers, device-independent&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
H&lt;br /&gt;
----&lt;br /&gt;
handles&lt;br /&gt;
   closing&lt;br /&gt;
   closing with dynamic links&lt;br /&gt;
   duplicated and inherited&lt;br /&gt;
   garbage&lt;br /&gt;
   semaphore&lt;br /&gt;
hard error daemon&lt;br /&gt;
hard errors&lt;br /&gt;
   handling in real mode&lt;br /&gt;
hardware, nonstandard&lt;br /&gt;
hardware devices. See devices&lt;br /&gt;
hardware interrupts&lt;br /&gt;
   device drivers and&lt;br /&gt;
hardware-specific interfaces&lt;br /&gt;
Hayes modems&lt;br /&gt;
heap algorithm&lt;br /&gt;
heap objects&lt;br /&gt;
Hercules Graphics Card&lt;br /&gt;
hierarchical file system&lt;br /&gt;
hooking the keyboard vector&lt;br /&gt;
huge memory&lt;br /&gt;
huge model addressing&lt;br /&gt;
huge segments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I&lt;br /&gt;
----&lt;br /&gt;
IBM PC/AT&lt;br /&gt;
INCLUDE&lt;br /&gt;
Independent Software Vendors (ISVs)&lt;br /&gt;
industrial revolution, second&lt;br /&gt;
infosegs&lt;br /&gt;
inheritance&lt;br /&gt;
INIT&lt;br /&gt;
initialization&lt;br /&gt;
   device driver&lt;br /&gt;
   global subsystem&lt;br /&gt;
   instance subsystem&lt;br /&gt;
initialization entry points, dynamic link&lt;br /&gt;
input/output. See I/O&lt;br /&gt;
installable file system (IFS)&lt;br /&gt;
instance data&lt;br /&gt;
   segment&lt;br /&gt;
instance subsystem initialization&lt;br /&gt;
instructions&lt;br /&gt;
   sequence of&lt;br /&gt;
insufficient memory&lt;br /&gt;
INT 21&lt;br /&gt;
INT 2F multiplex function&lt;br /&gt;
integrated applications&lt;br /&gt;
integrity, data&lt;br /&gt;
Intel. See also 8086/8088 processor, 80286 processor, 80386 processor&lt;br /&gt;
   80286 processor&lt;br /&gt;
   80386 processor&lt;br /&gt;
   8080 processor&lt;br /&gt;
   8086/8088 processor&lt;br /&gt;
interactive&lt;br /&gt;
   category&lt;br /&gt;
   processes&lt;br /&gt;
   programs&lt;br /&gt;
interface, user&lt;br /&gt;
interlocking&lt;br /&gt;
internal fragmentation&lt;br /&gt;
interprocess communication (IPC)&lt;br /&gt;
interrupt handling code&lt;br /&gt;
interruptible block&lt;br /&gt;
interrupts&lt;br /&gt;
   3x box emulation&lt;br /&gt;
   hardware&lt;br /&gt;
   signals and&lt;br /&gt;
interrupt service routine, device driver&lt;br /&gt;
interrupt-time thread&lt;br /&gt;
interrupt vectors&lt;br /&gt;
   hooking&lt;br /&gt;
I/O&lt;br /&gt;
   architecture&lt;br /&gt;
   asynchronous&lt;br /&gt;
   background&lt;br /&gt;
   disk requests&lt;br /&gt;
   efficiency&lt;br /&gt;
   file&lt;br /&gt;
   port access&lt;br /&gt;
   privilege mechanism&lt;br /&gt;
   requesting an operation&lt;br /&gt;
   signals and&lt;br /&gt;
   video&lt;br /&gt;
I/O-bound applications&lt;br /&gt;
IOCTL call&lt;br /&gt;
IP register&lt;br /&gt;
IPC See also interprocess communication&lt;br /&gt;
   combining forms of&lt;br /&gt;
   using with dynamic links&lt;br /&gt;
IRET instruction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
K&lt;br /&gt;
----&lt;br /&gt;
KBD&lt;br /&gt;
kernel&lt;br /&gt;
   interfacing with dynamic links&lt;br /&gt;
   mode&lt;br /&gt;
keyboard, processes using the&lt;br /&gt;
keyboard mode, cooked and raw&lt;br /&gt;
keyboard vector, hooking the&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&lt;br /&gt;
----&lt;br /&gt;
label names, volume&lt;br /&gt;
large disk support&lt;br /&gt;
LAR instruction&lt;br /&gt;
latency, rotational&lt;br /&gt;
Least Recently Used (LRU) scheme&lt;br /&gt;
levels, compatibility&lt;br /&gt;
LIB&lt;br /&gt;
libraries&lt;br /&gt;
   dynamic link&lt;br /&gt;
   sharing dynamic link&lt;br /&gt;
   subroutine and runtime&lt;br /&gt;
linear address space&lt;br /&gt;
linking&lt;br /&gt;
   dynamic&lt;br /&gt;
   static&lt;br /&gt;
loadtime dynamic linking&lt;br /&gt;
local area networks. See networks&lt;br /&gt;
Local Descriptor Table (LDT)&lt;br /&gt;
locality of reference&lt;br /&gt;
localization of errors&lt;br /&gt;
local variables&lt;br /&gt;
locking, file. See file and record locking&lt;br /&gt;
logical&lt;br /&gt;
   device and directory name facility&lt;br /&gt;
   devices&lt;br /&gt;
   directories&lt;br /&gt;
   disks, partitioning&lt;br /&gt;
low priority category&lt;br /&gt;
LSL instruction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
M&lt;br /&gt;
----&lt;br /&gt;
magic cookie&lt;br /&gt;
mass marketing, software and&lt;br /&gt;
media volume management&lt;br /&gt;
memory&lt;br /&gt;
   display&lt;br /&gt;
   huge&lt;br /&gt;
   insufficient&lt;br /&gt;
   layout of system&lt;br /&gt;
   limitations of in 8088 processor&lt;br /&gt;
   named shared&lt;br /&gt;
   physical&lt;br /&gt;
   protection&lt;br /&gt;
   shared&lt;br /&gt;
   shared giveaway&lt;br /&gt;
   swapping&lt;br /&gt;
   swapping segments in&lt;br /&gt;
   swapped-out&lt;br /&gt;
   utilization&lt;br /&gt;
   video&lt;br /&gt;
   virtual&lt;br /&gt;
memory management&lt;br /&gt;
   API&lt;br /&gt;
   hardware&lt;br /&gt;
memory manager&lt;br /&gt;
   definition of&lt;br /&gt;
memory not present fault&lt;br /&gt;
memory objects, tracking&lt;br /&gt;
memory overcommit&lt;br /&gt;
memory segments, allocating&lt;br /&gt;
memory suballocation&lt;br /&gt;
memory unit&lt;br /&gt;
mental work, mechanizing&lt;br /&gt;
message mode&lt;br /&gt;
Microsoft, vision of&lt;br /&gt;
Microsoft Macro Assembler (MASM)&lt;br /&gt;
model, architectural&lt;br /&gt;
modes&lt;br /&gt;
   incompatible&lt;br /&gt;
   keyboard&lt;br /&gt;
modular tools&lt;br /&gt;
module name&lt;br /&gt;
monitors, device&lt;br /&gt;
MOU&lt;br /&gt;
MS-DOS&lt;br /&gt;
   compatibility with&lt;br /&gt;
   environment list&lt;br /&gt;
   expandability of&lt;br /&gt;
   filenames in&lt;br /&gt;
   history of&lt;br /&gt;
   memory allocation in&lt;br /&gt;
   running applications in OS/2&lt;br /&gt;
   version 1.0&lt;br /&gt;
   version 2.0&lt;br /&gt;
   version 3.0&lt;br /&gt;
   version 4.0&lt;br /&gt;
   version 5.0&lt;br /&gt;
   versions of&lt;br /&gt;
multitasking&lt;br /&gt;
   data integrity and&lt;br /&gt;
   definition of&lt;br /&gt;
   device drivers and&lt;br /&gt;
   full&lt;br /&gt;
   MS-DOS version&lt;br /&gt;
   parallel&lt;br /&gt;
   serial&lt;br /&gt;
multiuser systems, thrashing and&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
N&lt;br /&gt;
----&lt;br /&gt;
named pipes&lt;br /&gt;
   multiple instances of&lt;br /&gt;
named shared memory&lt;br /&gt;
name generation, compatibility and&lt;br /&gt;
names, dynamic link&lt;br /&gt;
name set&lt;br /&gt;
network piggybacking&lt;br /&gt;
networks&lt;br /&gt;
   access to&lt;br /&gt;
   compatibility on&lt;br /&gt;
   file protection on&lt;br /&gt;
   importance of security on&lt;br /&gt;
network virtual circuit&lt;br /&gt;
NUMADD program&lt;br /&gt;
NUMARITH application&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
O&lt;br /&gt;
----&lt;br /&gt;
.OBJ files&lt;br /&gt;
obj namebuf&lt;br /&gt;
object name buffer&lt;br /&gt;
objects&lt;br /&gt;
   defining&lt;br /&gt;
   file system name space and&lt;br /&gt;
office automation&lt;br /&gt;
   objective of&lt;br /&gt;
   operating system for&lt;br /&gt;
offset registers&lt;br /&gt;
OPEN function&lt;br /&gt;
open system&lt;br /&gt;
operating systems&lt;br /&gt;
   multitasking&lt;br /&gt;
   other&lt;br /&gt;
ordinals, entry&lt;br /&gt;
orphaned semaphores&lt;br /&gt;
overlays, code&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
P&lt;br /&gt;
----&lt;br /&gt;
packet, request&lt;br /&gt;
paged virtual memory&lt;br /&gt;
paperless office&lt;br /&gt;
parallel multitasking&lt;br /&gt;
parent processes&lt;br /&gt;
partitioning, extended&lt;br /&gt;
passwords, file&lt;br /&gt;
Paterson, Tim&lt;br /&gt;
PATH&lt;br /&gt;
pathnames&lt;br /&gt;
peripherals, high-bandwidth&lt;br /&gt;
permissions&lt;br /&gt;
physical memory&lt;br /&gt;
PID&lt;br /&gt;
piggybacking&lt;br /&gt;
pipes&lt;br /&gt;
   anonymous&lt;br /&gt;
   named&lt;br /&gt;
pointer, seek&lt;br /&gt;
preemptive scheduler&lt;br /&gt;
presentation manager&lt;br /&gt;
   choosing between VIO and&lt;br /&gt;
   DDE and&lt;br /&gt;
priority&lt;br /&gt;
   categories of&lt;br /&gt;
   time-critical&lt;br /&gt;
priority scheduler&lt;br /&gt;
   semaphore&lt;br /&gt;
privilege mechanism, I/O&lt;br /&gt;
privilege mode&lt;br /&gt;
privilege transition&lt;br /&gt;
process termination signal handler&lt;br /&gt;
processes&lt;br /&gt;
   calling from OS/2&lt;br /&gt;
   child&lt;br /&gt;
   client&lt;br /&gt;
   command&lt;br /&gt;
   daemon&lt;br /&gt;
   dynamic linking and&lt;br /&gt;
   foreground and background&lt;br /&gt;
   grandparent&lt;br /&gt;
   interactive&lt;br /&gt;
   interfacing with dynamic links&lt;br /&gt;
   parent&lt;br /&gt;
   problems with multiple&lt;br /&gt;
   threads and&lt;br /&gt;
processing&lt;br /&gt;
   asynchronous&lt;br /&gt;
   foreground and background&lt;br /&gt;
processing unit&lt;br /&gt;
process tree&lt;br /&gt;
programming model, device drivers&lt;br /&gt;
programs&lt;br /&gt;
   debugger&lt;br /&gt;
   executing&lt;br /&gt;
   interactive&lt;br /&gt;
   running on OS/2&lt;br /&gt;
program termination signal&lt;br /&gt;
PROMPT&lt;br /&gt;
protected environment&lt;br /&gt;
protection&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
   model&lt;br /&gt;
   side-effects&lt;br /&gt;
protect mode&lt;br /&gt;
   huge model addressing in&lt;br /&gt;
   real mode versus&lt;br /&gt;
protocol, DDE&lt;br /&gt;
pseudo interrupts&lt;br /&gt;
Ptrace&lt;br /&gt;
pure segment&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Q, R&lt;br /&gt;
----&lt;br /&gt;
queues&lt;br /&gt;
RAM See also memory&lt;br /&gt;
   available&lt;br /&gt;
   semaphores&lt;br /&gt;
Random Access Memory. See RAM&lt;br /&gt;
random selector values&lt;br /&gt;
RAS information&lt;br /&gt;
raw mode&lt;br /&gt;
real mode&lt;br /&gt;
   80386 processor&lt;br /&gt;
   8086 processor&lt;br /&gt;
   applications&lt;br /&gt;
   compatibility box&lt;br /&gt;
   device drivers in&lt;br /&gt;
   hard errors in&lt;br /&gt;
   huge model addressing in&lt;br /&gt;
   protect mode versus&lt;br /&gt;
   screen group&lt;br /&gt;
   swapping in&lt;br /&gt;
real time, tracking passage of&lt;br /&gt;
record locking&lt;br /&gt;
redirector code (MS-DOS)&lt;br /&gt;
reference locality&lt;br /&gt;
registers&lt;br /&gt;
   offset&lt;br /&gt;
   segment&lt;br /&gt;
   signals and&lt;br /&gt;
Reliability, Availability, and Serviceability (RAS)&lt;br /&gt;
religion, OS/2&lt;br /&gt;
remote procedure call&lt;br /&gt;
request packet&lt;br /&gt;
resident functions&lt;br /&gt;
resources, manipulating&lt;br /&gt;
return()&lt;br /&gt;
ring 3&lt;br /&gt;
ring transition&lt;br /&gt;
ROM BIOS&lt;br /&gt;
root directory&lt;br /&gt;
rotational latency&lt;br /&gt;
runtime dynamic linking&lt;br /&gt;
runtime library&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
S&lt;br /&gt;
----&lt;br /&gt;
scheduler&lt;br /&gt;
   preemptive&lt;br /&gt;
   priority&lt;br /&gt;
   semaphore&lt;br /&gt;
SCP-DOS&lt;br /&gt;
screen device, handle for&lt;br /&gt;
screen groups&lt;br /&gt;
   multiple&lt;br /&gt;
   using in 3x box&lt;br /&gt;
screen images, manipulating&lt;br /&gt;
screen-save operation&lt;br /&gt;
screen switch&lt;br /&gt;
screen updates, requirements for&lt;br /&gt;
Seattle Computer Products&lt;br /&gt;
sector aligned calls&lt;br /&gt;
sectors&lt;br /&gt;
   blocks of&lt;br /&gt;
security, dynamic link&lt;br /&gt;
seek pointer&lt;br /&gt;
segment arithmetic&lt;br /&gt;
segment fault&lt;br /&gt;
segments&lt;br /&gt;
   80286 architecture&lt;br /&gt;
   80386 architecture&lt;br /&gt;
   data&lt;br /&gt;
   dynamic link&lt;br /&gt;
   executing from data&lt;br /&gt;
   global data&lt;br /&gt;
   huge&lt;br /&gt;
   internally shared&lt;br /&gt;
   nonswappable&lt;br /&gt;
   pure&lt;br /&gt;
   sharing&lt;br /&gt;
   stack&lt;br /&gt;
   status and information&lt;br /&gt;
segment selectors&lt;br /&gt;
segment swapping&lt;br /&gt;
semaphore handles&lt;br /&gt;
semaphores&lt;br /&gt;
   data integrity and&lt;br /&gt;
   file system name space and&lt;br /&gt;
   orphaned&lt;br /&gt;
   RAM&lt;br /&gt;
   recovering&lt;br /&gt;
   scheduling&lt;br /&gt;
   system&lt;br /&gt;
serial multitasking&lt;br /&gt;
session manager&lt;br /&gt;
SetSignalHandler&lt;br /&gt;
shared memory&lt;br /&gt;
   giveaway&lt;br /&gt;
   named&lt;br /&gt;
sharing segments&lt;br /&gt;
side effects&lt;br /&gt;
   controlling&lt;br /&gt;
   dynamic link&lt;br /&gt;
   protection&lt;br /&gt;
signal handler address&lt;br /&gt;
signal handlers&lt;br /&gt;
signaling&lt;br /&gt;
signals&lt;br /&gt;
   holding&lt;br /&gt;
SIGTERM signal&lt;br /&gt;
single-tasking&lt;br /&gt;
single-tasking environment, containing errors in&lt;br /&gt;
software design, OS/2 concepts of&lt;br /&gt;
software tools approach&lt;br /&gt;
speed execution&lt;br /&gt;
stable environment&lt;br /&gt;
stack frames&lt;br /&gt;
stacks, thread&lt;br /&gt;
stack segments&lt;br /&gt;
standard error. See STDERR&lt;br /&gt;
standard file handles&lt;br /&gt;
standard input. See STDIN&lt;br /&gt;
standard output. See STDOUT&lt;br /&gt;
standards, software&lt;br /&gt;
static data segments, adding&lt;br /&gt;
static links&lt;br /&gt;
status and information, segment&lt;br /&gt;
STDERR&lt;br /&gt;
STDIN&lt;br /&gt;
   compatibility with presentation manager&lt;br /&gt;
STDIN/STDOUT mechanism, I/O&lt;br /&gt;
STDOUT&lt;br /&gt;
   compatibility with presentation manager&lt;br /&gt;
strings, environment&lt;br /&gt;
suballocation, memory&lt;br /&gt;
subroutine library&lt;br /&gt;
subroutines, dynamic link packages as&lt;br /&gt;
subsystems&lt;br /&gt;
   dynamic link&lt;br /&gt;
   special support&lt;br /&gt;
subtask model&lt;br /&gt;
subtrees, command&lt;br /&gt;
   controlling&lt;br /&gt;
swapped-out memory&lt;br /&gt;
SWAPPER.DAT&lt;br /&gt;
swapping&lt;br /&gt;
   segment&lt;br /&gt;
system&lt;br /&gt;
   conventions&lt;br /&gt;
   crashes&lt;br /&gt;
   diagnosis&lt;br /&gt;
   File Table (SFT)&lt;br /&gt;
   memory layout&lt;br /&gt;
   security, debuggers and&lt;br /&gt;
   semaphores&lt;br /&gt;
system, hanging the&lt;br /&gt;
systems&lt;br /&gt;
   closed and open&lt;br /&gt;
   file&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
T&lt;br /&gt;
----&lt;br /&gt;
task See also processes&lt;br /&gt;
task-time thread&lt;br /&gt;
technological breakthroughs&lt;br /&gt;
TEMP&lt;br /&gt;
terminate and stay resident mechanism&lt;br /&gt;
thrashing&lt;br /&gt;
thread 1&lt;br /&gt;
thread death&lt;br /&gt;
thread of execution&lt;br /&gt;
thread ID&lt;br /&gt;
threads&lt;br /&gt;
   background&lt;br /&gt;
   captive&lt;br /&gt;
   collisions of&lt;br /&gt;
   dynamic linking and&lt;br /&gt;
   foreground and background processing with&lt;br /&gt;
   foreground and interactive&lt;br /&gt;
   from different processes&lt;br /&gt;
   I/O-bound&lt;br /&gt;
   interrupt-time&lt;br /&gt;
   low priority&lt;br /&gt;
   multiple&lt;br /&gt;
   organizing programs using&lt;br /&gt;
   performance characteristics of&lt;br /&gt;
   priority categories of&lt;br /&gt;
   problems with multiple&lt;br /&gt;
   switching the CPU among&lt;br /&gt;
   task-time&lt;br /&gt;
   working sets and&lt;br /&gt;
thread stacks&lt;br /&gt;
throughput balancing&lt;br /&gt;
time-critical priority category&lt;br /&gt;
time intervals&lt;br /&gt;
timer services&lt;br /&gt;
time slice&lt;br /&gt;
timing, thread&lt;br /&gt;
tools, software&lt;br /&gt;
TOPS-10&lt;br /&gt;
TREE&lt;br /&gt;
tree, process&lt;br /&gt;
tree-structured file system&lt;br /&gt;
Trojan programs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
U&lt;br /&gt;
----&lt;br /&gt;
UNIX&lt;br /&gt;
   naming conventions in&lt;br /&gt;
   ptrace facility&lt;br /&gt;
upper- and lowercase&lt;br /&gt;
upward compatibility&lt;br /&gt;
user interface&lt;br /&gt;
   graphical&lt;br /&gt;
   presentation manager&lt;br /&gt;
   VIO&lt;br /&gt;
utilization&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
V&lt;br /&gt;
----&lt;br /&gt;
variables, local&lt;br /&gt;
versions, future OS/2&lt;br /&gt;
video hardware, accessing&lt;br /&gt;
VIO,&lt;br /&gt;
   choosing between presentation manager and&lt;br /&gt;
   dynamic link entry points&lt;br /&gt;
   graphics under&lt;br /&gt;
   subsystem&lt;br /&gt;
   user interface&lt;br /&gt;
VioGetBuf&lt;br /&gt;
VioModeWait&lt;br /&gt;
VioSavRedrawWait&lt;br /&gt;
VioScrLock&lt;br /&gt;
virtual circuit, network&lt;br /&gt;
virtual display device&lt;br /&gt;
virtualization&lt;br /&gt;
   device&lt;br /&gt;
virtualizing the environment&lt;br /&gt;
virtual memory&lt;br /&gt;
   concepts of&lt;br /&gt;
   paged&lt;br /&gt;
virtual real mode (80386)&lt;br /&gt;
volume, disk&lt;br /&gt;
volume ID&lt;br /&gt;
volume-oriented operations&lt;br /&gt;
Von Neumann, John&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
W&lt;br /&gt;
----&lt;br /&gt;
windowing interface&lt;br /&gt;
working directories&lt;br /&gt;
working set&lt;br /&gt;
WORM drives&lt;br /&gt;
writethrough&lt;br /&gt;
WYSIWYG&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
X&lt;br /&gt;
----&lt;br /&gt;
XENIX&lt;br /&gt;
&lt;br /&gt;
Z&lt;br /&gt;
----&lt;br /&gt;
zero-terminated strings&lt;br /&gt;
&lt;br /&gt;
[[Category: OS/2]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=Inside_OS/2&amp;diff=36399</id>
		<title>Inside OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=Inside_OS/2&amp;diff=36399"/>
				<updated>2025-06-13T12:35:23Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the text from the book.&lt;br /&gt;
&lt;br /&gt;
INSIDE OS/2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
INSIDE OS/2 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Gordon Letwin &lt;br /&gt;
Chief Architect, Systems Software, Microsoft(R)&lt;br /&gt;
&lt;br /&gt;
Foreword by Bill Gates &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
PUBLISHED BY&lt;br /&gt;
Microsoft Press&lt;br /&gt;
A Division of Microsoft Corporation&lt;br /&gt;
16011 NE 36th Way, Box 97017, Redmond, Washington 98073-9717&lt;br /&gt;
&lt;br /&gt;
Copyright (C) 1988 by Microsoft Press&lt;br /&gt;
All rights reserved. No part of the contents of this book may be&lt;br /&gt;
reproduced or transmitted in any form or by any means without the written&lt;br /&gt;
permission of the publisher.&lt;br /&gt;
&lt;br /&gt;
Library of Congress Cataloging in Publication Data&lt;br /&gt;
Letwin, Gordon.&lt;br /&gt;
Inside OS/2.&lt;br /&gt;
&lt;br /&gt;
Includes index.&lt;br /&gt;
1. MS OS/2 (Computer operating system) I. Title.&lt;br /&gt;
II. Title: Inside OS/Two.&lt;br /&gt;
QA76.76.063L48    1988    005.4'46    87-31579&lt;br /&gt;
ISBN 1-55615-117-9&lt;br /&gt;
&lt;br /&gt;
Printed and bound in the United States of America&lt;br /&gt;
&lt;br /&gt;
1 2 3 4 5 6 7 8 9 MLML 8 9 0 9 8&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade in the United States by Harper &amp;amp; Row.&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade in Canada by General Publishing Company, Ltd.&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade outside the United States and Canada by&lt;br /&gt;
Penguin Books Ltd.&lt;br /&gt;
&lt;br /&gt;
Penguin Books Ltd., Harmondsworth, Middlesex, England&lt;br /&gt;
Penguin Books Australia Ltd., Ringwood, Victoria, Australia&lt;br /&gt;
Penguin Books N.Z. Ltd., 182-190 Wairau Road, Auckland 10, New Zealand&lt;br /&gt;
&lt;br /&gt;
British Cataloging in publication Data available&lt;br /&gt;
&lt;br /&gt;
Editor: Patricia Pratt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                              Dedication&lt;br /&gt;
&lt;br /&gt;
                               To R.P.W.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Contents&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Foreword by Bill Gates&lt;br /&gt;
&lt;br /&gt;
Introduction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part I: The Project&lt;br /&gt;
&lt;br /&gt;
Chapter 1.  History of the Project&lt;br /&gt;
             1.1  MS-DOS version 1.0&lt;br /&gt;
             1.2  MS-DOS version 2.0&lt;br /&gt;
             1.3  MS-DOS version 3.0&lt;br /&gt;
             1.4  MS-DOS version 4.0&lt;br /&gt;
&lt;br /&gt;
Chapter 2.  Goals and Compatibility Issues&lt;br /&gt;
             2.1  Goals&lt;br /&gt;
                   2.1.1  Graphical User Interface&lt;br /&gt;
                   2.1.2  Multitasking&lt;br /&gt;
                   2.1.3  Memory Management&lt;br /&gt;
                   2.1.4  Protection&lt;br /&gt;
                   2.1.5  Encapsulation&lt;br /&gt;
                   2.1.6  Interprocess Communication (IPC)&lt;br /&gt;
                   2.1.7  Direct Device Access&lt;br /&gt;
             2.2  Compatibility Issues&lt;br /&gt;
                   2.2.1  Real Mode vs Protect Mode&lt;br /&gt;
                   2.2.2  Running Applications in Real (Compatibility) Mode&lt;br /&gt;
                           2.2.2.1  Memory Utilization&lt;br /&gt;
                           2.2.2.2  File Locking&lt;br /&gt;
                           2.2.2.3  Network Piggybacking&lt;br /&gt;
                   2.2.3  Popular Function Compatibility&lt;br /&gt;
                   2.2.4  Downward Compatibility&lt;br /&gt;
                           2.2.4.1  Family API&lt;br /&gt;
                           2.2.4.2  Network Server-Client Compatibility&lt;br /&gt;
&lt;br /&gt;
Chapter 3.  The OS/2 Religion&lt;br /&gt;
             3.1  Maximum Flexibility&lt;br /&gt;
             3.2  Stable Environment&lt;br /&gt;
                   3.2.1  Memory Protection&lt;br /&gt;
                   3.2.2  Side-Effects Protection&lt;br /&gt;
             3.3  Localization of Errors&lt;br /&gt;
             3.4  Software Tools Approach&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part II: The Architecture&lt;br /&gt;
&lt;br /&gt;
Chapter 4.  Multitasking&lt;br /&gt;
             4.1  Subtask Model&lt;br /&gt;
                   4.1.1  Standard File Handles&lt;br /&gt;
                   4.1.2  Anonymous Pipes&lt;br /&gt;
                   4.1.3  Details, Details&lt;br /&gt;
             4.2  PIDs and Command Subtrees&lt;br /&gt;
             4.3  DosExecPgm&lt;br /&gt;
             4.4  DosCWait&lt;br /&gt;
             4.5  Control of Child Tasks and Command Subtrees&lt;br /&gt;
                   4.5.1  DosKillProcess&lt;br /&gt;
                   4.5.2  DosSetPriority&lt;br /&gt;
&lt;br /&gt;
Chapter 5.  Threads and Scheduler/Priorities&lt;br /&gt;
             5.1  Threads&lt;br /&gt;
                   5.1.1  Thread Stacks&lt;br /&gt;
                   5.1.2  Thread Uses&lt;br /&gt;
                           5.1.2.1  Foreground and Background Work&lt;br /&gt;
                           5.1.2.2  Asynchronous Processing&lt;br /&gt;
                           5.1.2.3  Speed Execution&lt;br /&gt;
                           5.1.2.4  Organizing Programs&lt;br /&gt;
                   5.1.3  Interlocking&lt;br /&gt;
                           5.1.3.1  Local Variables&lt;br /&gt;
                           5.1.3.2  RAM Semaphores&lt;br /&gt;
                           5.1.3.3  DosSuspendThread&lt;br /&gt;
                           5.1.3.4  DosEnterCritSec/DosExitCritSec&lt;br /&gt;
                   5.1.4  Thread 1&lt;br /&gt;
                   5.1.5  Thread Death&lt;br /&gt;
                   5.1.6  Performance Characteristics&lt;br /&gt;
             5.2  Scheduler/Priorities&lt;br /&gt;
                   5.2.1  General Priority Category&lt;br /&gt;
                           5.2.1.1  Background Subcategory&lt;br /&gt;
                           5.2.1.2  Foreground and Interactive&lt;br /&gt;
                                     Subcategories&lt;br /&gt;
                           5.2.1.3  Throughput Balancing&lt;br /&gt;
                   5.2.2  Time-Critical Priority Category&lt;br /&gt;
                   5.2.3  Force Background Priority Category&lt;br /&gt;
                   5.2.4  Setting Process/Thread Priorities&lt;br /&gt;
&lt;br /&gt;
Chapter 6.  The User Interface&lt;br /&gt;
             6.1  VIO User Interface&lt;br /&gt;
             6.2  The Presentation Manager User Interface&lt;br /&gt;
             6.3  Presentation Manager and VIO Compatibility&lt;br /&gt;
&lt;br /&gt;
Chapter 7.  Dynamic Linking&lt;br /&gt;
             7.1  Static Linking&lt;br /&gt;
             7.2  Loadtime Dynamic Linking&lt;br /&gt;
             7.3  Runtime Dynamic Linking&lt;br /&gt;
             7.4  Dynlinks, Processes, and Threads&lt;br /&gt;
             7.5  Data&lt;br /&gt;
                   7.5.1  Instance Data&lt;br /&gt;
                   7.5.2  Global Data&lt;br /&gt;
             7.6  Dynamic Link Packages As Subroutines&lt;br /&gt;
             7.7  Subsystems&lt;br /&gt;
                   7.7.1  Special Subsystem Support&lt;br /&gt;
             7.8  Dynamic Links As Interfaces to Other Processes&lt;br /&gt;
             7.9  Dynamic Links As Interfaces to the Kernel&lt;br /&gt;
             7.10 The Architectural Role of Dynamic Links&lt;br /&gt;
             7.11 Implementation Details&lt;br /&gt;
                   7.11.1  Dynlink Data Security&lt;br /&gt;
                   7.11.2  Dynlink Life, Death, and Sharing&lt;br /&gt;
                   7.11.3  Dynlink Side Effects&lt;br /&gt;
             7.12 Dynlink Names&lt;br /&gt;
&lt;br /&gt;
Chapter 8.  File System Name Space&lt;br /&gt;
             8.1  Filenames&lt;br /&gt;
             8.2  Network Access&lt;br /&gt;
             8.3  Name Generation and Compatibility&lt;br /&gt;
             8.4  Permissions&lt;br /&gt;
             8.5  Other Objects in the File System Name Space&lt;br /&gt;
&lt;br /&gt;
Chapter 9.  Memory Management&lt;br /&gt;
             9.1  Protection Model&lt;br /&gt;
             9.2  Memory Management API&lt;br /&gt;
                   9.2.1  Shared Memory&lt;br /&gt;
                   9.2.2  Huge Memory&lt;br /&gt;
                   9.2.3  Executing from Data Segments&lt;br /&gt;
                   9.2.4  Memory Suballocation&lt;br /&gt;
             9.3  Segment Swapping&lt;br /&gt;
                   9.3.1  Swapping Miscellany&lt;br /&gt;
             9.4  Status and Information&lt;br /&gt;
&lt;br /&gt;
Chapter 10. Environment Strings&lt;br /&gt;
&lt;br /&gt;
Chapter 11. Interprocess Communication (IPC)&lt;br /&gt;
             11.1  Shared Memory&lt;br /&gt;
             11.2  Semaphores&lt;br /&gt;
                    11.2.1  Semaphore Recovery&lt;br /&gt;
                    11.2.2  Semaphore Scheduling&lt;br /&gt;
             11.3  Named Pipes&lt;br /&gt;
             11.4  Queues&lt;br /&gt;
             11.5  Dynamic Data Exchange (DDE)&lt;br /&gt;
             11.6  Signals&lt;br /&gt;
             11.7  Combining IPC Forms&lt;br /&gt;
&lt;br /&gt;
Chapter 12. Signals&lt;br /&gt;
&lt;br /&gt;
Chapter 13. The Presentation Manager and VIO&lt;br /&gt;
             13.1  Choosing Between PM and VIO&lt;br /&gt;
             13.2  Background I/O&lt;br /&gt;
             13.3  Graphics Under VIO&lt;br /&gt;
&lt;br /&gt;
Chapter 14. Interactive Programs&lt;br /&gt;
             14.1  I/O Architecture&lt;br /&gt;
             14.2  Ctrl-C and Ctrl-Break Handling&lt;br /&gt;
&lt;br /&gt;
Chapter 15. The File System&lt;br /&gt;
             15.1  The OS/2 File System&lt;br /&gt;
             15.2  Media Volume Management&lt;br /&gt;
             15.3  I/O Efficiency&lt;br /&gt;
&lt;br /&gt;
Chapter 16. Device Monitors, Data Integrity, and Timer Services&lt;br /&gt;
             16.1  Device Monitors&lt;br /&gt;
             16.2  Data Integrity&lt;br /&gt;
                    16.2.1  Semaphores&lt;br /&gt;
                    16.2.2  DosBufReset&lt;br /&gt;
                    16.2.3  Writethroughs&lt;br /&gt;
             16.3  Timer Services&lt;br /&gt;
&lt;br /&gt;
Chapter 17. Device Drivers and Hard Errors&lt;br /&gt;
             17.1  Device Drivers&lt;br /&gt;
                    17.1.1  Device Drivers and OS/2 Communication&lt;br /&gt;
                    17.1.2  Device Driver Programming Model&lt;br /&gt;
                    17.1.3  Device Management&lt;br /&gt;
                    17.1.4  Dual Mode&lt;br /&gt;
             17.2  Hard Errors&lt;br /&gt;
                    17.2.1  The Hard Error Daemon&lt;br /&gt;
                    17.2.2  Application Hard Error Handling&lt;br /&gt;
&lt;br /&gt;
Chapter 18. I/O Privilege Mechanism and Debugging/Ptrace&lt;br /&gt;
             18.1  I/O Privilege Mechanism&lt;br /&gt;
             18.2  Debugging/Ptrace&lt;br /&gt;
&lt;br /&gt;
Chapter 19. The 3x Box&lt;br /&gt;
&lt;br /&gt;
Chapter 20. Family API&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part III: The Future&lt;br /&gt;
&lt;br /&gt;
Chapter 21. The Future&lt;br /&gt;
             21.1  File System&lt;br /&gt;
             21.2  The 80386&lt;br /&gt;
                    21.2.1  Large Segments&lt;br /&gt;
                    21.2.2  Multiple Real Mode Boxes&lt;br /&gt;
                    21.2.3  Full Protection Capability&lt;br /&gt;
                    21.2.4  Other Features&lt;br /&gt;
             21.3  The Next Ten Years&lt;br /&gt;
&lt;br /&gt;
Glossary&lt;br /&gt;
&lt;br /&gt;
Index&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Acknowledgments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Although a book can have a single author, a work such as OS/2 necessarily&lt;br /&gt;
owes its existence to the efforts of a great many people. The architecture&lt;br /&gt;
described herein was hammered out by a joint Microsoft/ IBM design team:&lt;br /&gt;
Ann, Anthony, Carolyn, Ed, Gordon, Jerry, Mark, Mike, Ray, and Ross. This&lt;br /&gt;
team accomplished a great deal of work in a short period of time.&lt;br /&gt;
     The bulk of the credit, and my thanks, go to the engineers who&lt;br /&gt;
designed and implemented the code and made it work. The size of the teams&lt;br /&gt;
involved throughout the project prevents me from listing all the names&lt;br /&gt;
here. It's hard for someone who has not been involved in a software project&lt;br /&gt;
of this scope to imagine the problems, pressure, chaos, and &amp;quot;reality&lt;br /&gt;
shifts&amp;quot; that arise in a never-ending stream. These people deserve great&lt;br /&gt;
credit for their skill and determination in making OS/2 come to pass.&lt;br /&gt;
     Thanks go to the OS/2 development staffers who found time, in the heat&lt;br /&gt;
of the furnace, to review and critique this book: Ian Birrell, Ross Cook,&lt;br /&gt;
Rick Dewitt, Dave Gilman, Vic Heller, Mike McLaughlin, Jeff Parsons, Ray&lt;br /&gt;
Pedrizetti, Robert Reichel, Rajen Shah, Anthony Short, Ben Slivka, Pete&lt;br /&gt;
Stewart, Indira Subramanian, Bryan Willman, and Mark Zbikowski.&lt;br /&gt;
     I'd like to give special thanks to Mark Zbikowski and Aaron Reynolds,&lt;br /&gt;
the &amp;quot;gurus of DOS.&amp;quot; Without their successes there would never have been an&lt;br /&gt;
opportunity for a product such as OS/2.&lt;br /&gt;
     And finally I'd like to thank Bill Gates for creating and captaining&lt;br /&gt;
one hell of a company, thereby making all this possible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Foreword&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 is destined to be a very important piece of software. During the&lt;br /&gt;
next 10 years, millions of programmers and users will utilize this system.&lt;br /&gt;
From time to time they will come across a feature or a limitation and&lt;br /&gt;
wonder why it's there. The best way for them to understand the overall&lt;br /&gt;
philosophy of the system will be to read this book. Gordon Letwin is&lt;br /&gt;
Microsoft's architect for OS/2. In his very clear and sometimes humorous&lt;br /&gt;
way, Gordon has laid out in this book why he included what he did and why&lt;br /&gt;
he didn't include other things.&lt;br /&gt;
     The very first generation of microcomputers were 8-bit machines, such&lt;br /&gt;
as the Commodore Pet, the TRS-80, the Apple II, and the CPM 80 based&lt;br /&gt;
machines. Built into almost all of them was Microsoft's BASIC Interpreter.&lt;br /&gt;
I met Gordon Letwin when I went to visit Heath's personal computer group&lt;br /&gt;
(now part of Zenith). Gordon had written his own BASIC as well as an&lt;br /&gt;
operating system for the Heath system, and he wasn't too happy that his&lt;br /&gt;
management was considering buying someone else's. In a group of about 15&lt;br /&gt;
people, he bluntly pointed out the limitations of my BASIC versus his.&lt;br /&gt;
After Heath licensed my BASIC, I convinced Gordon that Microsoft was the&lt;br /&gt;
place to be if you wanted your great software to be popular, and so he&lt;br /&gt;
became one of Microsoft's first 10 programmers. His first project was to&lt;br /&gt;
single-handedly write a compiler for Microsoft BASIC. He put a sign on his&lt;br /&gt;
door that read&lt;br /&gt;
&lt;br /&gt;
        Do not disturb, feed, poke, tease...the animal&lt;br /&gt;
&lt;br /&gt;
and in 5 months wrote a superb compiler that is still the basis for all our&lt;br /&gt;
BASIC compilers. Unlike the code that a lot of superstar programmers write,&lt;br /&gt;
Gordon's source code is a model of readability and includes precise&lt;br /&gt;
explanations of algorithms and why they were chosen.&lt;br /&gt;
     When the Intel 80286 came along, with its protected mode completely&lt;br /&gt;
separate from its compatible real mode, we had no idea how we were going to&lt;br /&gt;
get at its new capabilities. In fact, we had given up until Gordon came up&lt;br /&gt;
with the patented idea described in this book that has been referred to as&lt;br /&gt;
&amp;quot;turning the car off and on at 60 MPH.&amp;quot; When we first explained the idea to&lt;br /&gt;
Intel and many of its customers, they were sure it wouldn't work. Even&lt;br /&gt;
Gordon wasn't positive it would work until he wrote some test programs that&lt;br /&gt;
proved it did.&lt;br /&gt;
     Gordon's role as an operating systems architect is to overview our&lt;br /&gt;
designs and approaches and make sure they are as simple and as elegant as&lt;br /&gt;
possible. Part of this job includes reviewing people's code. Most&lt;br /&gt;
programmers enjoy having Gordon look over their code and point out how it&lt;br /&gt;
could be improved and simplified. A lot of programs end up about half as&lt;br /&gt;
big after Gordon has explained a better way to write them. Gordon doesn't&lt;br /&gt;
mince words, however, so in at least one case a particularly sensitive&lt;br /&gt;
programmer burst into tears after reading his commentary. Gordon isn't&lt;br /&gt;
content to just look over other people's code. When a particular project&lt;br /&gt;
looks very difficult, he dives in. Currently, Gordon has decided to&lt;br /&gt;
personally write most of our new file system, which will be dramatically&lt;br /&gt;
faster than our present one. On a recent &amp;quot;vacation&amp;quot; he wrote more than 50&lt;br /&gt;
pages of source code.&lt;br /&gt;
     This is Gordon's debut as a book author, and like any good designer he&lt;br /&gt;
has already imagined what bad reviews might say. I think this book is both&lt;br /&gt;
fun and important. I hope you enjoy it as much as I have.&lt;br /&gt;
&lt;br /&gt;
Bill Gates&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Introduction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technological breakthroughs develop in patterns that are distinct from&lt;br /&gt;
patterns of incremental advancements. An incremental advancement--an&lt;br /&gt;
improvement to an existing item--is straightforward and unsurprising. An&lt;br /&gt;
improvement is created; people see the improvement, know what it will do&lt;br /&gt;
for them, and start using it.&lt;br /&gt;
     A major advance without closely related antecedents--a technological&lt;br /&gt;
breakthrough--follows a different pattern. The field of communication is a&lt;br /&gt;
good example. Early in this century, a large infrastructure existed to&lt;br /&gt;
facilitate interpersonal communication. Mail was delivered twice a day, and&lt;br /&gt;
a variety of efficient services relayed messages. A businessman dictated a&lt;br /&gt;
message to his secretary, who gave it to a messenger service. The service&lt;br /&gt;
carried the message to its nearby destination, where a secretary delivered&lt;br /&gt;
it to the recipient.&lt;br /&gt;
     Into this environment came a technological breakthrough--the&lt;br /&gt;
telephone. The invention of the telephone was a breakthrough, not an&lt;br /&gt;
incremental advance, because it provided an entirely new way to communicate.&lt;br /&gt;
It wasn't an improvement over an existing method. That it was&lt;br /&gt;
a breakthrough development impeded its acceptance. Most business people&lt;br /&gt;
considered it a newfangled toy, of little practical use. &amp;quot;What good does it&lt;br /&gt;
do me? By the time I dictate the message, and my secretary writes it down&lt;br /&gt;
and gives it to the mailroom, and they phone the addressee's mailroom, and&lt;br /&gt;
the message is copied--perhaps incorrectly--and delivered to the&lt;br /&gt;
addressee's secretary, it would have been as fast to have it delivered by&lt;br /&gt;
messenger! All my correspondents are close by, and, besides, with&lt;br /&gt;
messengers I don't have to pay someone to sit by the telephone all day in&lt;br /&gt;
case a message comes in.&amp;quot;&lt;br /&gt;
     This is a classic example of the earliest stages of breakthrough&lt;br /&gt;
technology--potential users evaluate it by trying to fit it into present&lt;br /&gt;
work patterns. Our example businessman has not yet realized that he needn't&lt;br /&gt;
write the message down anymore and that it needn't be copied down at the&lt;br /&gt;
destination. He also doesn't realize that the reason his recipients are&lt;br /&gt;
close by is that they have to be for decent messenger delivery. The&lt;br /&gt;
telephone relaxed this requirement, allowing more efficient locations near&lt;br /&gt;
factories and raw materials or where office space was cheaper. But it&lt;br /&gt;
was necessary for the telephone to be accepted before these advantages &lt;br /&gt;
could be realized.&lt;br /&gt;
     Another impedance to the acceptance of a breakthrough technology is&lt;br /&gt;
that the necessary new infrastructure is not in place. A telephone did&lt;br /&gt;
little good if your intended correspondent didn't have one. The nature of&lt;br /&gt;
telephones required a standard; until that standard was set, your&lt;br /&gt;
correspondent might own a phone, but it could be connected to a network&lt;br /&gt;
unreachable by you. Furthermore, because the technology was in its infancy,&lt;br /&gt;
the facilities were crude.&lt;br /&gt;
     These obstacles were not insurmountable. The communications&lt;br /&gt;
requirements of some people were so critical that they were willing to&lt;br /&gt;
invent new procedures and to put up with the problems of the early stages.&lt;br /&gt;
Some people, because of their daring or ambition, used the new system to&lt;br /&gt;
augment their existing system. And finally, because the new technology was&lt;br /&gt;
so powerful, some used it to enhance the existing technology. For example,&lt;br /&gt;
a messenger service might establish several offices with telephone linkage&lt;br /&gt;
between them and use the telephones to speed delivery of short messages by&lt;br /&gt;
phoning them to the office nearest the destination, where they were copied&lt;br /&gt;
down and delivered normally. Using the telephone in this fashion was&lt;br /&gt;
wasteful, but where demand for the old service was high enough, any&lt;br /&gt;
improvement, however &amp;quot;wasteful,&amp;quot; was welcome.&lt;br /&gt;
     After it has a foot in the door, a breakthrough technology is&lt;br /&gt;
unstoppable. After a time, standards are established, the bugs are worked&lt;br /&gt;
out, and, most important, the tool changes its users. Once the telephone&lt;br /&gt;
became available, business and personal practices developed in new&lt;br /&gt;
patterns, patterns that were not considered before because they were not&lt;br /&gt;
possible. Messenger services used to be fast enough, but only because,&lt;br /&gt;
before the telephone, the messenger service was the fastest technology&lt;br /&gt;
available. The telephone changed the life-style of its users.&lt;br /&gt;
     This change in the structure of human activity explains why an&lt;br /&gt;
intelligent person could say, &amp;quot;Telephones are silly gadgets,&amp;quot; and a few&lt;br /&gt;
years later say, &amp;quot;Telephones are indispensable.&amp;quot; This change in the tool&lt;br /&gt;
user--caused by the tool itself--also makes predicting the ultimate effect&lt;br /&gt;
of the new technology difficult. Extrapolating from existing trends is&lt;br /&gt;
wildly inaccurate because the new tool destroys many practices and creates&lt;br /&gt;
wholly unforeseen ones. It's great fun to read early, seemingly silly&lt;br /&gt;
predictions of life in the future and to laugh at the predictors, but the&lt;br /&gt;
predictors were frequently intelligent and educated. Their only mistake was&lt;br /&gt;
in treating the new development as an incremental advance rather than as a&lt;br /&gt;
breakthrough technology. They saw how the new development would improve&lt;br /&gt;
their current practices, but they couldn't see how it would replace those&lt;br /&gt;
practices.&lt;br /&gt;
     Digital computers are an obvious breakthrough technology, and they've&lt;br /&gt;
shared the classic three-stage pattern: &amp;quot;exotic toys,&amp;quot; &amp;quot;limited use,&amp;quot; and&lt;br /&gt;
&amp;quot;indispensable.&amp;quot; Mainframe computers have gone the full route, in the&lt;br /&gt;
milieu of business and scientific computing. IBM's initial estimate of the&lt;br /&gt;
computer market was a few dozen machines. But, as the technology and the&lt;br /&gt;
support infrastructure grew, and as people's ways of working adapted to&lt;br /&gt;
computers, the use of computers grew--from the census bureau, to life&lt;br /&gt;
insurance companies, to payroll systems, and finally to wholly new&lt;br /&gt;
functions such as MIS (Management Information Sciences) systems and airline&lt;br /&gt;
reservation networks.&lt;br /&gt;
     Microcomputers are in the process of a similar development. The&lt;br /&gt;
&amp;quot;exotic toy&amp;quot; stage has already given way to the &amp;quot;limited use&amp;quot; stage. We're&lt;br /&gt;
just starting to develop standards and infrastructure and are only a few&lt;br /&gt;
years from the &amp;quot;indispensable&amp;quot; stage. In anticipation of this stage,&lt;br /&gt;
Microsoft undertook the design and the development of OS/2.&lt;br /&gt;
     Although studying the mainframe computer revolution helps in trying to&lt;br /&gt;
predict the path of the microcomputer revolution, microcomputers are more&lt;br /&gt;
than just &amp;quot;cheap mainframes.&amp;quot; The microcomputer revolution will follow the&lt;br /&gt;
tradition of breakthroughs, creating new needs and new uses that cannot be&lt;br /&gt;
anticipated solely by studying what happened with mainframe systems.&lt;br /&gt;
     This book was written because of the breakthrough nature of the&lt;br /&gt;
microcomputer and the impact of the coming second industrial revolution.&lt;br /&gt;
The designers of OS/2 tried to anticipate, to the greatest extent possible,&lt;br /&gt;
the demands that would be placed on the system when the tool--the personal&lt;br /&gt;
computer--and the tool user reached their new equilibrium. A knowledge of&lt;br /&gt;
MS-DOS and a thorough reading of the OS/2 reference manuals will not, in&lt;br /&gt;
themselves, clarify the key issues of the programming environment that OS/2&lt;br /&gt;
was written to support. This is true not only because of the complexity of&lt;br /&gt;
the product but because many design elements were chosen to provide&lt;br /&gt;
services that from a prebreakthrough perspective--don't seem needed and&lt;br /&gt;
solve problems that haven't yet arisen.&lt;br /&gt;
     Other books provide reference information and detailed how-to&lt;br /&gt;
instructions for writing OS/2 programs. This book describes the underlying&lt;br /&gt;
architectural models that make up OS/2 and discusses how those models are&lt;br /&gt;
expected to meet the foreseen and unforeseen requirements of the oncoming&lt;br /&gt;
office automation revolution. It focuses on the general issues, problems,&lt;br /&gt;
and solutions that all OS/2 programs encounter regardless of the&lt;br /&gt;
programming and interface models that a programmer may employ.&lt;br /&gt;
     As is often the case in a technical discussion, everything in OS/2 is&lt;br /&gt;
interconnected in some fashion to everything else. A discussion on the&lt;br /&gt;
shinbone naturally leads to a discussion of the thighbone and so on. The&lt;br /&gt;
author and the editor of this book have tried hard to group the material&lt;br /&gt;
into a logical progression without redundancy, but the very nature of the&lt;br /&gt;
material makes complete success at this impossible. It's often desirable,&lt;br /&gt;
in fact, to repeat material, perhaps from a different viewpoint or with a&lt;br /&gt;
different emphasis. For these reasons, the index references every mention&lt;br /&gt;
of an item or a topic, however peripheral. Having too many references&lt;br /&gt;
(including a few worthless ones) is far better than having too few&lt;br /&gt;
references. When you're looking for information about a particular subject,&lt;br /&gt;
I recommend that you first consult the contents page to locate the major&lt;br /&gt;
discussion and then peruse the index to pick up references that may appear&lt;br /&gt;
in unexpected places.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part I  The Project&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==1  History of the Project==&lt;br /&gt;
&lt;br /&gt;
Microsoft was founded to realize a vision of a microcomputer on every&lt;br /&gt;
desktop--a vision of the second industrial revolution. The first industrial&lt;br /&gt;
revolution mechanized physical work. Before the eighteenth century, nearly&lt;br /&gt;
all objects were created and constructed by human hands, one at a time.&lt;br /&gt;
With few exceptions, such as animal-powered plowing and cartage, all power&lt;br /&gt;
was human muscle power. The second industrial revolution will mechanize&lt;br /&gt;
routine mental work. Today, on the verge of the revolution, people are&lt;br /&gt;
still doing &amp;quot;thought work,&amp;quot; one piece at a time.&lt;br /&gt;
     Certain tasks--those massive in scope and capable of being rigidly&lt;br /&gt;
described, such as payroll calculations--have been automated, but the&lt;br /&gt;
majority of &amp;quot;thought work&amp;quot; is still done by people, not by computers. We&lt;br /&gt;
have the computer equivalent of the plow horse, but we don't have the&lt;br /&gt;
computer equivalent of the electric drill or the washing machine.&lt;br /&gt;
     Of course, computers cannot replace original thought and creativity&lt;br /&gt;
(at least, not in the near future) any more than machines have replaced&lt;br /&gt;
design and creativity in the physical realm. But the bulk of the work in a&lt;br /&gt;
white-collar office involves routine manipulation of information. The&lt;br /&gt;
second industrial revolution will relieve us of the &amp;quot;grunt work&amp;quot;--routine&lt;br /&gt;
data manipulation, analysis, and decisions--freeing us to deal only with&lt;br /&gt;
those situations that require human judgment.&lt;br /&gt;
     Most people do not recognize the inevitability of the second&lt;br /&gt;
industrial revolution. They can't see how a computer could do 75 percent of&lt;br /&gt;
their work because their work was structured in the absence of computers.&lt;br /&gt;
But, true to the pattern for technological breakthroughs, the tremendous&lt;br /&gt;
utility of the microcomputer will transform its users and the way they do&lt;br /&gt;
their work.&lt;br /&gt;
     For example, a great deal of work is hard to computerize because the&lt;br /&gt;
input information arrives on paper and it would take too long to type it&lt;br /&gt;
all in. Ten years ago, computer proponents envisioned the &amp;quot;paperless&lt;br /&gt;
office&amp;quot; as a solution for this problem: All material would be generated by&lt;br /&gt;
computer and then transferred electronically or via disk to other&lt;br /&gt;
computers. Offices are certainly becoming more paperless, and the arrival&lt;br /&gt;
of powerful networking systems will accelerate this, but paper continues to&lt;br /&gt;
be a very useful medium. As a result, in recent years growth has occurred&lt;br /&gt;
in another direction--incorporating paper as a computer input and output&lt;br /&gt;
device. Powerful laser printers, desktop publishing systems, and optical&lt;br /&gt;
scanners and optical character recognition will make it more practical to&lt;br /&gt;
input from and output to paper.&lt;br /&gt;
     Although the founders of Microsoft fully appreciate the impact of the&lt;br /&gt;
second industrial revolution, nobody can predict in detail how the&lt;br /&gt;
revolution will unfold. Instead, Microsoft bases its day-to-day decisions&lt;br /&gt;
on dual sets of goals: short-term goals, which are well known, and a long-&lt;br /&gt;
term goal--our vision of the automated office. Each decision has to meet&lt;br /&gt;
our short-term goals, and it must be consonant with our long-term vision, a&lt;br /&gt;
vision that becomes more precise as the revolution progresses.&lt;br /&gt;
     When 16-bit microprocessors were first announced, Microsoft knew that&lt;br /&gt;
the &amp;quot;iron&amp;quot; was now sufficiently powerful to begin to realize this vision.&lt;br /&gt;
But a powerful computer environment requires both strong iron and a&lt;br /&gt;
sophisticated operating system. The iron was becoming available, but the&lt;br /&gt;
operating system that had been standard for 8-bit microprocessors was&lt;br /&gt;
inadequate. This is when and why Microsoft entered the operating system&lt;br /&gt;
business: We knew that we needed a powerful operating system to realize our&lt;br /&gt;
vision and that the only way to guarantee its existence and suitability was&lt;br /&gt;
to write it ourselves.&lt;br /&gt;
&lt;br /&gt;
===1.1  MS-DOS version 1.0===&lt;br /&gt;
&lt;br /&gt;
MS-DOS got its start when IBM asked Microsoft to develop a disk operating&lt;br /&gt;
system for a new product that IBM was developing, the IBM Personal Computer&lt;br /&gt;
(PC). Microsoft's only operating system product at that time was XENIX, a&lt;br /&gt;
licensed version of AT&amp;amp;T's UNIX  operating system. XENIX/UNIX requires a&lt;br /&gt;
processor with memory management and protection facilities. Because the&lt;br /&gt;
8086/8088 processors had neither and because XENIX/UNIX memory&lt;br /&gt;
requirements--modest by minicomputer standards of the day--were nonetheless&lt;br /&gt;
large by microcomputer standards, a different operating system had to be&lt;br /&gt;
developed.&lt;br /&gt;
     CP/M-80, developed by Digital Research, Incorporated (DRI), had been&lt;br /&gt;
the standard 8-bit operating system, and the majority of existing&lt;br /&gt;
microcomputer software had been written to run on CP/M-80. For this reason,&lt;br /&gt;
Microsoft decided to make MS-DOS version 1.0 as compatible as possible with&lt;br /&gt;
CP/M-80. The 8088 processor would not run the existing CP/M-80 programs,&lt;br /&gt;
which were written for the 8080 processor, but because 8080 programs could&lt;br /&gt;
be easily and semiautomatically converted to run on the 8088, Microsoft&lt;br /&gt;
felt that minimizing adaptation hassles by minimizing operating system&lt;br /&gt;
incompatibility would hasten the acceptance of MS-DOS on the IBM PC.&lt;br /&gt;
     A major software product requires a great deal of development time,&lt;br /&gt;
and IBM was in a hurry to introduce its PC. Microsoft, therefore, looked&lt;br /&gt;
around for a software product to buy that could be built onto to create MS-&lt;br /&gt;
DOS version 1.0. Such a product was found at Seattle Computer Products. Tim&lt;br /&gt;
Paterson, an engineer there, had produced a CP/M-80 &amp;quot;clone,&amp;quot; called SCP-&lt;br /&gt;
DOS, that ran on the 8088 processor. Microsoft purchased full rights to&lt;br /&gt;
this product and to its source code and used the product as a starting&lt;br /&gt;
point in the development of MS-DOS version 1.0.&lt;br /&gt;
     MS-DOS version 1.0 was released in August 1981. Available only for the&lt;br /&gt;
IBM PC, it consisted of 4000 lines of assembly-language source code and ran&lt;br /&gt;
in 8 KB of memory. MS-DOS version 1.1 was released in 1982 and worked with&lt;br /&gt;
double-sided 320 KB floppy disks.&lt;br /&gt;
     Microsoft's goal was that MS-DOS version 1.0 be highly CP/M&lt;br /&gt;
compatible, and it was. Ironically, it was considerably more compatible&lt;br /&gt;
than DRI's own 8088 product, CP/M-86. As we shall see later, this CP/M&lt;br /&gt;
compatibility, necessary at the time, eventually came to cause Microsoft&lt;br /&gt;
engineers a great deal of difficulty.&lt;br /&gt;
&lt;br /&gt;
===1.2  MS-DOS version 2.0===&lt;br /&gt;
&lt;br /&gt;
In early 1982, IBM disclosed to Microsoft that it was developing a hard&lt;br /&gt;
disk-based personal computer, the IBM XT. Microsoft began work on MS-DOS&lt;br /&gt;
version 2.0 to provide support for the new disk hardware. Changes were&lt;br /&gt;
necessary because MS-DOS, in keeping with its CP/M-80 compatible heritage,&lt;br /&gt;
had been designed for a floppy disk environment. A disk could contain only&lt;br /&gt;
one directory, and that directory could contain a maximum of 64 files. This&lt;br /&gt;
decision was reasonable when first made because floppy disks held only&lt;br /&gt;
about 180 KB of data.&lt;br /&gt;
     For the hard disk, however, the 64-file limit was much too small, and&lt;br /&gt;
using a single directory to manage perhaps hundreds of files was&lt;br /&gt;
clumsy. Therefore, the MS-DOS version 2.0 developers--Mark Zbikowski, Aaron&lt;br /&gt;
Reynolds, Chris Peters, and Nancy Panners--added a hierarchical file&lt;br /&gt;
system. In a hierarchical file system a directory can contain other&lt;br /&gt;
directories and files. In turn, those directories can con- tain a mixture&lt;br /&gt;
of files and directories and so on. A hierarchically designed system starts&lt;br /&gt;
with the main, or &amp;quot;root,&amp;quot; directory, which itself can contain (as seen in&lt;br /&gt;
Figure 1-1) a tree-structured collection of files and directories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                directory WORK&lt;br /&gt;
                           ADMIN&lt;br /&gt;
               Ú────────── BUDGET&lt;br /&gt;
               ³           CALENDAR&lt;br /&gt;
               ³           LUNCH.DOC&lt;br /&gt;
               ³           PAYROLL ─────────¿&lt;br /&gt;
               ³           PHONE.LST        ³&lt;br /&gt;
               ³           SCHED.DOC        ³&lt;br /&gt;
               ³                            ³&lt;br /&gt;
           directory BUDGET              directory PAYROLL&lt;br /&gt;
                      MONTH                         ADDRESSES&lt;br /&gt;
                      QUARTER                       MONTHLY&lt;br /&gt;
                      YEAR                          NAMES&lt;br /&gt;
                      1986 ────────¿                RETIRED ─────¿&lt;br /&gt;
            Ú──────── 1985         ³                VACATION     ³&lt;br /&gt;
            ³                      ³                WEEKLY       ³&lt;br /&gt;
            ³                      ³                             ³&lt;br /&gt;
     directory 1985         directory 1986             directory RETIRED&lt;br /&gt;
            ³                      ³                             ³&lt;br /&gt;
&lt;br /&gt;
Figure 1-1.  A directory tree hierarchy. Within the WORK directory&lt;br /&gt;
are five files (ADMIN, CALENDAR, LUNCH.DOC, PHONE.LST, SCHED.DOC) and two&lt;br /&gt;
subdirectories (BUDGET, PAYROLL). Each subdirectory has its own&lt;br /&gt;
subdirectories.&lt;br /&gt;
&lt;br /&gt;
===1.3  MS-DOS version 3.0====&lt;br /&gt;
&lt;br /&gt;
MS-DOS version 3.0 was introduced in August 1984, when IBM announced the&lt;br /&gt;
IBM PC/AT. The AT contains an 80286 processor, but, when running DOS, it&lt;br /&gt;
uses the 8086 emulation mode built into the chip and runs as a &amp;quot;fast 8086.&amp;quot;&lt;br /&gt;
The chip's extended addressing range and its protected mode architecture&lt;br /&gt;
sit unused.&lt;br /&gt;
1. Products such as Microsoft XENIX/UNIX run on the PC/AT and&lt;br /&gt;
compatibles, using the processor's protected mode. This is possible&lt;br /&gt;
because XENIX/UNIX and similar systems had no preexisting real mode&lt;br /&gt;
applications that needed to be supported.&lt;br /&gt;
1&lt;br /&gt;
     MS-DOS version 3.1 was released in November 1984 and contained&lt;br /&gt;
networking support. In January 1986, MS-DOS version 3.2--a minor revision--&lt;br /&gt;
was released. This version supported 3-1/2-inch floppy disks and contained&lt;br /&gt;
the formatting function for a device in the device driver. In 1987, MS-DOS&lt;br /&gt;
version 3.3 followed; the primary enhancement of this release was support&lt;br /&gt;
for the IBM PS/2 and compatible hardware.&lt;br /&gt;
&lt;br /&gt;
===1.4  MS-DOS version 4.0====&lt;br /&gt;
&lt;br /&gt;
Microsoft started work on a multitasking version of MS-DOS in January 1983.&lt;br /&gt;
At the time, it was internally called MS-DOS version 3.0. When a new&lt;br /&gt;
version of the single-tasking MS-DOS was shipped under the name MS-DOS&lt;br /&gt;
version 3.0, the multitasking version was renamed, internally, to MS-DOS&lt;br /&gt;
version 4.0. A version of this product--a multitasking, real-mode only MS-&lt;br /&gt;
DOS--was shipped as MS-DOS version 4.0. Because MS-DOS version 4.0 runs&lt;br /&gt;
only in real mode, it can run on 8088 and 8086 machines as well as on 80286&lt;br /&gt;
machines. The limitations of the real mode environment make MS-DOS version&lt;br /&gt;
4.0 a specialized product. Although MS-DOS version 4.0 supports full&lt;br /&gt;
preemptive multitasking, system memory is limited to the 640 KB available&lt;br /&gt;
in real mode, with no swapping.&lt;br /&gt;
2. It is not feasible to support general purpose swapping without&lt;br /&gt;
memory management hardware that is unavailable in 8086 real mode.&lt;br /&gt;
2 This means that all processes have to fit&lt;br /&gt;
into the single 640 KB memory area. Only one MS-DOS version 3.x compatible&lt;br /&gt;
real mode application can be run; the other processes must be special MS-&lt;br /&gt;
DOS version 4.0 processes that understand their environment and cooperate&lt;br /&gt;
with the operating system to coexist peacefully with the single MS-DOS&lt;br /&gt;
version 3.x real mode application.&lt;br /&gt;
     Because of these restrictions, MS-DOS version 4.0 was not intended for&lt;br /&gt;
general release, but as a platform for specific OEMs to support extended PC&lt;br /&gt;
architectures. For example, a powerful telephone management system could be&lt;br /&gt;
built into a PC by using special MS-DOS version 4.0 background processes to&lt;br /&gt;
control the telephone equipment. The resulting machine could then be&lt;br /&gt;
marketed as a &amp;quot;compatible MS-DOS 3 PC with a built-in superphone.&amp;quot;&lt;br /&gt;
     Although MS-DOS version 4.0 was released as a special OEM product, the&lt;br /&gt;
project--now called MS-DOS version 5.0--continued. The goal was to take&lt;br /&gt;
advantage of the protected mode of the 80286 to provide full general&lt;br /&gt;
purpose multitasking without the limitations--as seen in MS-DOS version&lt;br /&gt;
4.0--of a real-mode only environment. Soon, Microsoft and IBM signed a&lt;br /&gt;
Joint Development Agreement that provided for the design and development of&lt;br /&gt;
MS-DOS version 5.0 (now called CP/DOS). The agreement is complex, but it&lt;br /&gt;
basically provides for joint development and then subsequent joint&lt;br /&gt;
ownership, with both companies holding full rights to the resulting&lt;br /&gt;
product.&lt;br /&gt;
     As the project neared completion, the marketing staffs looked at&lt;br /&gt;
CP/DOS, nee DOS 5, nee DOS 4, nee DOS 3, and decided that it needed...you&lt;br /&gt;
guessed it...a name change. As a result, the remainder of this book will&lt;br /&gt;
discuss the design and function of an operating system called OS/2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==2  Goals and Compatibility Issues===&lt;br /&gt;
&lt;br /&gt;
OS/2 is similar to traditional multitasking operating systems in many ways:&lt;br /&gt;
It provides multitasking, scheduling, disk management, memory management,&lt;br /&gt;
and so on. But it is also different in many ways, because a personal&lt;br /&gt;
computer is very different from a multiuser minicomputer. The designers of&lt;br /&gt;
OS/2 worked from two lists: a set of goals and a set of compatibility&lt;br /&gt;
issues. This chapter describes those goals and compatibility issues and&lt;br /&gt;
provides the context for a later discussion of the design itself.&lt;br /&gt;
&lt;br /&gt;
===2.1  Goals===&lt;br /&gt;
&lt;br /&gt;
The primary goal of OS/2 is to be the ideal office automation operating&lt;br /&gt;
system. The designers worked toward this goal by defining the following&lt;br /&gt;
intermediate and, seemingly, contradictory goals:&lt;br /&gt;
&lt;br /&gt;
* To provide device-independent graphics drivers without introducing any significant overhead.&lt;br /&gt;
* To allow applications direct access to high-bandwidth peripherals but maintain the ability to virtualize or apportion the usage of those peripherals.&lt;br /&gt;
* To provide multitasking without reducing the performance and response available from a single-tasking system.&lt;br /&gt;
* To provide a fully customized environment for each program and its descendants yet also provide a standard environment that is unaffected by other programs in the system.&lt;br /&gt;
* To provide a protected environment to ensure system stability yet one that will not constrain applications from the capabilities they have under nonprotected systems.&lt;br /&gt;
&lt;br /&gt;
====2.1.1  Graphical User Interface====&lt;br /&gt;
By far the fastest and easiest way people receive information is through&lt;br /&gt;
the eye. We are inherently visual creatures. Our eyes receive information&lt;br /&gt;
rapidly; they can &amp;quot;seek&amp;quot; to the desired information and &amp;quot;zoom&amp;quot; their&lt;br /&gt;
attention in and out with small, rapid movements of the eye muscles. A&lt;br /&gt;
large part of the human brain is dedicated to processing visual&lt;br /&gt;
information. People abstract data and meaning from visual material--from&lt;br /&gt;
text to graphics to motion pictures--hundreds of times faster than from any&lt;br /&gt;
other material.&lt;br /&gt;
     As a result, if an office automation system is to provide quantities&lt;br /&gt;
of information quickly and in a form in which it can be easily absorbed, a&lt;br /&gt;
powerful graphics capability is essential. Such capabilities were rare in&lt;br /&gt;
earlier minicomputer operating systems because of the huge memory and&lt;br /&gt;
compute power costs of high-resolution displays. Today's microcomputers&lt;br /&gt;
have the memory to contain the display information, they have the CPU power&lt;br /&gt;
to create and manipulate that information, and they have no better use for&lt;br /&gt;
those capabilities than to support powerful, easy-to-use graphical&lt;br /&gt;
applications.&lt;br /&gt;
     Graphics can take many forms--pictures, tables, drawings, charts--&lt;br /&gt;
perhaps incorporating color and even animation. All are powerful adjuncts&lt;br /&gt;
to the presentation of alphanumeric text. Graphical applications don't&lt;br /&gt;
necessarily employ charts and pictures. A WYSIWYG (What You See Is What You&lt;br /&gt;
Get) typesetting program may display only text, but if that text is drawn&lt;br /&gt;
in graphics mode, the screen can show any font, in any type size, with&lt;br /&gt;
proportional spacing, kerning, and so on.&lt;br /&gt;
     The screen graphics components of OS/2 need to be device independent;&lt;br /&gt;
that is, an application must display the proper graphical &amp;quot;picture&amp;quot; without&lt;br /&gt;
relying on the specific characteristics of any particular graphical display&lt;br /&gt;
interface board. Each year the state of the art in displays gets better; it&lt;br /&gt;
would be extremely shortsighted to tie applications to a particular display&lt;br /&gt;
board, for no matter how good it is, within a couple of years it will be&lt;br /&gt;
obsolete.&lt;br /&gt;
     The idea is to encapsulate device-specific code by requiring that each&lt;br /&gt;
device come with a software package called a device driver. The application&lt;br /&gt;
program issues commands for a generic device, and the device driver then&lt;br /&gt;
translates those commands to fit the characteristics of the actual device.&lt;br /&gt;
The result is that the manufacturer of a new graphics display board needs&lt;br /&gt;
to write an appropriate device driver and supply it with the board. The&lt;br /&gt;
application program doesn't need to know anything about the device, and the&lt;br /&gt;
device driver doesn't need to know anything about the application, other&lt;br /&gt;
than the specification of the common interface they share. This common&lt;br /&gt;
interface describes a virtual display device; the general technique of&lt;br /&gt;
hiding a complicated actual situation behind a simple, standard interface&lt;br /&gt;
is called &amp;quot;virtualization.&amp;quot;&lt;br /&gt;
     Figure 2-1 shows the traditional operating system device driver&lt;br /&gt;
architecture. Applications don't directly call device drivers because&lt;br /&gt;
device drivers need to execute in the processor's privilege mode to&lt;br /&gt;
manipulate their device; the calling application must run in normal mode.&lt;br /&gt;
In the language of the 80286/80386 family of processors, privilege mode is&lt;br /&gt;
called ring 0, and normal mode is called ring 3. The operating system&lt;br /&gt;
usually acts as a middleman: It receives the request, validates it, deals&lt;br /&gt;
with issues that arise when there is only one device but multiple&lt;br /&gt;
applications are using it, and then passes the request to the device&lt;br /&gt;
driver. The device driver's response or return of data takes the reverse&lt;br /&gt;
path, winding its way through the operating system and back to the&lt;br /&gt;
application program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         Application                      Kernel             Device driver&lt;br /&gt;
          (ring 3)                       (ring 0)               (ring 0)&lt;br /&gt;
──────────────────────────────────────────────────────────────────────────&lt;br /&gt;
                                   Request&lt;br /&gt;
                             ³     packet:                  ³ Ú──────────¿&lt;br /&gt;
                                Ú──────────¿    Device        ³  Device  ³&lt;br /&gt;
                             ³  ³          ³    descriptor ÄÅÄ´  driver  ³&lt;br /&gt;
Call deviceio (arg 1...argn)    ³ function ³    #1            À──────────Ù&lt;br /&gt;
                             ³  ³   arg1   ³          ú     ³&lt;br /&gt;
        ───────────────────────�³    ù     ÃÄ¿        ú&lt;br /&gt;
                  Ring       ³  ³   argn   ³ ³        ú     ³&lt;br /&gt;
                  transition    ³          ³ ³        ú       Ú──────────¿&lt;br /&gt;
                             ³  À──────────Ù À� Device      ³ ³  Device  ³&lt;br /&gt;
                                                descriptor ÄÄÄ´  driver  ³&lt;br /&gt;
                             ³                  #N          ³ À──────────Ù&lt;br /&gt;
&lt;br /&gt;
Figure 2-1.  Traditional device driver architecture. When an application&lt;br /&gt;
wants to do device I/0, it calls the operating system, which builds a&lt;br /&gt;
device request packet, determines the target device, and delivers the&lt;br /&gt;
packet. The device driver's response follows the opposite route through the&lt;br /&gt;
kernel back to the application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This approach solves the device virtualization problem, but at a cost&lt;br /&gt;
in performance. The interface between the application and the device driver&lt;br /&gt;
is narrow; that is, the form messages can take is usually restricted.&lt;br /&gt;
Commonly, the application program is expected to build a request block that&lt;br /&gt;
contains all the information and data that the device driver needs to&lt;br /&gt;
service the request; the actual call to the operating system is simply&lt;br /&gt;
&amp;quot;pass this request block to the device driver.&amp;quot; Setting up this block takes&lt;br /&gt;
time, and breaking it down in the device driver again takes time. More time&lt;br /&gt;
is spent on the reply; the device driver builds, the operating system&lt;br /&gt;
copies, and the application breaks down. Further time is spent calling down&lt;br /&gt;
through the internal layers of the operating system, examining and copying&lt;br /&gt;
the request block, routing to the proper device driver, and so forth.&lt;br /&gt;
Finally, the transition between rings (privilege and normal mode) is also&lt;br /&gt;
time-consuming, and two such transitions occur--to privilege mode and back&lt;br /&gt;
again.&lt;br /&gt;
     Such a cost in performance was acceptable in nongraphics-based systems&lt;br /&gt;
because, typically, completely updating a screen required only 1920 (or&lt;br /&gt;
fewer) bytes of data. Today's graphics devices can require 256,000 bytes or&lt;br /&gt;
more per screen update, and future devices will be even more demanding.&lt;br /&gt;
Furthermore, applications may expect to update these high-resolution&lt;br /&gt;
screens several times a second.&lt;br /&gt;
1. It's not so much the amount of data that slows the traditional&lt;br /&gt;
device driver model, but the number of requests and replies. Disk&lt;br /&gt;
devices work well through the traditional model because disk&lt;br /&gt;
requests tend to be large (perhaps 40,000 bytes). Display devices&lt;br /&gt;
tend to be written piecemeal--a character, a word, or a line at a&lt;br /&gt;
time. It is the high rate of these individual calls that slows the&lt;br /&gt;
device driver model, not the number of bytes written to the screen.&lt;br /&gt;
1&lt;br /&gt;
     OS/2 needed powerful, device-independent graphical display support&lt;br /&gt;
that had a wide, efficient user interface--one that did not involve ring&lt;br /&gt;
transitions, the operating system, or other unnecessary overhead. As we'll&lt;br /&gt;
see later, OS/2 meets this requirement by means of a mechanism called&lt;br /&gt;
dynamic linking.&lt;br /&gt;
&lt;br /&gt;
====2.1.2  Multitasking====&lt;br /&gt;
To be really useful, a personal computer must be able to do more than one&lt;br /&gt;
chore at a time--an ability called multitasking. We humans multitask all&lt;br /&gt;
the time. For example, you may be involved in three projects at work, be&lt;br /&gt;
halfway through a novel, and be taking Spanish lessons. You pick up each&lt;br /&gt;
task in turn, work on it for a while, and then put it down and work on&lt;br /&gt;
something else. This is called serial multitasking. Humans can also do some&lt;br /&gt;
tasks simultaneously, such as driving a car and talking. This is called&lt;br /&gt;
parallel multitasking.&lt;br /&gt;
     In a serial multitasking computer environment, a user can switch&lt;br /&gt;
activities at will, working for a while at each. For example, a user can&lt;br /&gt;
leave a word-processing program without terminating it, consult a&lt;br /&gt;
spreadsheet, and then return to the waiting word-processing program. Or, if&lt;br /&gt;
someone telephones and requests an appointment, the user can switch from a&lt;br /&gt;
spreadsheet to a scheduling program, consult the calendar, and then return&lt;br /&gt;
to the spreadsheet.&lt;br /&gt;
     The obvious value of multitasking makes it another key requirement for&lt;br /&gt;
OS/2: Many programs or applications can run at the same time. But&lt;br /&gt;
multitasking is useful for more than just switching between applications:&lt;br /&gt;
Parallel multitasking allows an application to do work by itself--perhaps&lt;br /&gt;
print a large file or recalculate a large spreadsheet--while the user&lt;br /&gt;
works with another application. Because OS/2 supports full multitasking,&lt;br /&gt;
it can execute programs in addition to the application(s) the user is&lt;br /&gt;
running, providing advanced services such as network mail without&lt;br /&gt;
interrupting or interfering with the user's work.&lt;br /&gt;
2. Present-day machines contain only one CPU, so at any instant&lt;br /&gt;
only one program can be executing. At this microscopic level,&lt;br /&gt;
OS/2 is a serial multitasking system. It is not considered serial&lt;br /&gt;
multitasking, however, because it performs preemptive scheduling.&lt;br /&gt;
At any time, OS/2 can remove the CPU from the currently running&lt;br /&gt;
program and assign it to another program. Because these&lt;br /&gt;
rescheduling events may occur many times a second at totally&lt;br /&gt;
unpredictable places within the running programs, it is accurate&lt;br /&gt;
to view the system as if each program truly runs simultaneously&lt;br /&gt;
with other programs.&lt;br /&gt;
&lt;br /&gt;
====2.1.3  Memory Management====&lt;br /&gt;
Multitasking is fairly easy to achieve. All that's necessary is a source of&lt;br /&gt;
periodic hardware interrupts, such as a clock circuit, to enable the&lt;br /&gt;
operating system to effect a &amp;quot;context switch,&amp;quot; or to reschedule. To be&lt;br /&gt;
useful, however, a multitasking system needs an effective memory management&lt;br /&gt;
system. For example, a user wants to run two applications on a system. Each&lt;br /&gt;
starts at as low a memory location as possible to maximize the amount of&lt;br /&gt;
memory it can use. Unfortunately, if the system supports multitasking and&lt;br /&gt;
the user tries to run both applications simultaneously, each attempts to&lt;br /&gt;
use the same memory cells, and the applications destroy each other.&lt;br /&gt;
     A memory management system solves this problem by using special&lt;br /&gt;
hardware facilities built into 80286/80386 processors (for example, IBM&lt;br /&gt;
PC/AT machines and compatibles and 80386-based machines).&lt;br /&gt;
3. Earlier 8086/8088 processors used in PCs, PC/XTs, and similar&lt;br /&gt;
machines lack this hardware. This is why earlier versions of MS-DOS&lt;br /&gt;
didn't support multitasking and why OS/2 won't run on such machines.&lt;br /&gt;
3 The memory&lt;br /&gt;
management system uses the hardware to virtualize the memory of the machine&lt;br /&gt;
so that each program appears to have all memory to itself.&lt;br /&gt;
     Memory management is more than keeping programs out of each other's&lt;br /&gt;
way. The system must track the owner or user(s) of each piece of memory so&lt;br /&gt;
that the memory space can be reclaimed when it is no longer needed, even if&lt;br /&gt;
the owner of the memory neglects to explicitly release it. Some operating&lt;br /&gt;
systems avoid this work by assuming that no application will ever fail to&lt;br /&gt;
return its memory when done or by examining the contents of memory and&lt;br /&gt;
ascertaining from those contents whether the memory is still being used.&lt;br /&gt;
(This is called &amp;quot;garbage collection.&amp;quot;) Neither alternative was acceptable&lt;br /&gt;
for OS/2. Because OS/2 will run a variety of programs written by many&lt;br /&gt;
vendors, identifying free memory by inspection is impossible, and assuming&lt;br /&gt;
perfection from the applications themselves is unwise. Tracking the&lt;br /&gt;
ownership and usage of memory objects can be complex, as we shall see in&lt;br /&gt;
our discussion on dynamic link libraries.&lt;br /&gt;
     Finally, the memory management system must manage memory overcommit.&lt;br /&gt;
The multitasking capability of OS/2 allows many applications to be run&lt;br /&gt;
simultaneously; thus, RAM must hold all these programs and their data.&lt;br /&gt;
Although RAM becomes cheaper every year, buying enough to hold all of one's&lt;br /&gt;
applications at one time is still prohibitive. Furthermore, although RAM&lt;br /&gt;
prices continue to drop, the memory requirements of applications will&lt;br /&gt;
continue to rise. Consequently, OS/2 must contain an effective mechanism to&lt;br /&gt;
allocate more memory to the running programs than in fact physically&lt;br /&gt;
exists. This is called memory overcommit.&lt;br /&gt;
     OS/2 accomplishes this magic with the classic technique of swapping.&lt;br /&gt;
OS/2 periodically examines each segment of memory to see if it has been&lt;br /&gt;
used recently. When a request is made for RAM and none is available, the&lt;br /&gt;
least recently used segment of memory (the piece that has been unused for&lt;br /&gt;
the longest time) is written to a disk file, and the RAM it occupied is&lt;br /&gt;
made available. Later, if a program attempts to use the swapped-out memory,&lt;br /&gt;
a &amp;quot;memory not present&amp;quot; fault occurs. OS/2 intercepts the fault and reloads&lt;br /&gt;
the memory information from the disk into memory, swapping out some other&lt;br /&gt;
piece of memory, if necessary, to make room. This whole process is&lt;br /&gt;
invisible to the application that uses the swapped memory area; the only&lt;br /&gt;
impact is a small delay while the needed memory is read back from the&lt;br /&gt;
disk.&lt;br /&gt;
     The fundamental concepts of memory overcommit and swapping are simple,&lt;br /&gt;
but a good implementation is not. OS/2 must choose the right piece of&lt;br /&gt;
memory to swap out, and it must swap it out efficiently. Not only must care&lt;br /&gt;
be taken that the swap file doesn't grow too big and consume all the free&lt;br /&gt;
disk space but also that deadlocks don't occur. For example, if all the&lt;br /&gt;
disk swap space is filled, it may be impossible to swap into RAM a piece of&lt;br /&gt;
memory because no free RAM is available, and OS/2 can't free up RAM because&lt;br /&gt;
no swap space exists to write it out to. Naturally, the greater the load on&lt;br /&gt;
the system, the slower the system will be, but the speed degradation must&lt;br /&gt;
be gradual and acceptable, and the system must never deadlock.&lt;br /&gt;
     The issues involved in memory management and the memory management&lt;br /&gt;
facilities that OS/2 provides are considerably more complex than this&lt;br /&gt;
overview. We'll return to the subject of memory management in detail in&lt;br /&gt;
Chapter 9.&lt;br /&gt;
&lt;br /&gt;
====2.1.4  Protection====&lt;br /&gt;
I mentioned earlier that OS/2 cannot trust applications to behave&lt;br /&gt;
correctly. I was talking about memory management, but this concern&lt;br /&gt;
generalizes into the next key requirement: OS/2 must protect applications &lt;br /&gt;
from the proper or improper actions of other applications that may be&lt;br /&gt;
running on the system.&lt;br /&gt;
     Because OS/2 will run applications and programs from a variety of&lt;br /&gt;
vendors, every user's machine will execute a different set of applications,&lt;br /&gt;
running in different ways on different data. No software vendor can fully&lt;br /&gt;
test a product in all possible environments. This makes it critical that an&lt;br /&gt;
error on the part of one program does not crash the system or some other&lt;br /&gt;
program or, worse, corrupt data and not bring down the system. Even if no&lt;br /&gt;
data is damaged, system crashes are unacceptable. Few users have the&lt;br /&gt;
background or equipment even to diagnose which application caused the&lt;br /&gt;
problem.&lt;br /&gt;
     Furthermore, malice, as well as accident, is a concern. Microsoft's&lt;br /&gt;
vision of the automated office cannot be realized without a system that is&lt;br /&gt;
secure from deliberate attack. No corporation will be willing to base its&lt;br /&gt;
operations on a computer network when any person in that company--with the&lt;br /&gt;
help of some &amp;quot;cracker&amp;quot; programs bought from the back of a computer&lt;br /&gt;
magazine--can see and change personnel or payroll files, billing notices,&lt;br /&gt;
or strategic planning memos.&lt;br /&gt;
     Today, personal computers are being used as a kind of super-&lt;br /&gt;
sophisticated desk calculator. As such, data is secured by traditional&lt;br /&gt;
means--physical locks on office doors, computers, or file cabinets that&lt;br /&gt;
store disks. Users don't see a need for a protected environment because&lt;br /&gt;
their machine is physically protected. This lack of interest in protection&lt;br /&gt;
is another example of the development of a breakthrough technology.&lt;br /&gt;
Protection is not needed because the machine is secure and operates on data&lt;br /&gt;
brought to it by traditional office channels. In the future, however,&lt;br /&gt;
networked personal computers will become universal and will act both as the&lt;br /&gt;
processors and as the source (via the network) of the data. Thus, in this&lt;br /&gt;
role, protection is a key requirement and is indeed a prerequisite for&lt;br /&gt;
personal computers to assume that central role.&lt;br /&gt;
&lt;br /&gt;
====2.1.5  Encapsulation====&lt;br /&gt;
When a program runs in a single-tasking system such as MS-DOS version 3.x,&lt;br /&gt;
its environment is always constant--consisting of the machine and MS-DOS.&lt;br /&gt;
The program can expect to get the same treatment from the system and to&lt;br /&gt;
provide exactly the same interaction with the user each time it runs. In a&lt;br /&gt;
multitasking environment, however, many programs can be running. Each&lt;br /&gt;
program can be using files and devices in different ways; each program can&lt;br /&gt;
be using the mouse, each program can have the screen display in a different&lt;br /&gt;
mode, and so on. OS/2 must encapsulate, or isolate, each program so that it&lt;br /&gt;
&amp;quot;sees&amp;quot; a uniform environment each time it runs, even though the computer&lt;br /&gt;
environment itself may be different each time.&lt;br /&gt;
&lt;br /&gt;
====2.1.6  Interprocess Communication (IPC)====&lt;br /&gt;
In a single-tasking environment such as MS-DOS version 3.x, each program&lt;br /&gt;
stands alone. If it needs a particular service not provided by the&lt;br /&gt;
operating system, it must provide that service itself. For example, every&lt;br /&gt;
application that needs a sort facility must contain its own.&lt;br /&gt;
     Likewise, if a spreadsheet needs to access values from a database, it&lt;br /&gt;
must contain the code to do so. This extra code complicates the spreadsheet&lt;br /&gt;
program, and it ties the program to a particular database product or&lt;br /&gt;
format. A user might be unable to switch to a better product because the&lt;br /&gt;
spreadsheet is unable to understand the new database's file formats.&lt;br /&gt;
     A direct result of such a stand-alone environment is the creation of&lt;br /&gt;
very large and complex &amp;quot;combo&amp;quot; packages such as Lotus Symphony. Because&lt;br /&gt;
every function that the user may want must be contained within one program,&lt;br /&gt;
vendors supply packages that attempt to contain everything.&lt;br /&gt;
     In practice, such chimeric programs tend to be large and cumbersome,&lt;br /&gt;
and their individual functional components (spreadsheets, word processors,&lt;br /&gt;
and databases, for example) are generally more difficult to use and less&lt;br /&gt;
sophisticated than individual applications that specialize in a single&lt;br /&gt;
function.&lt;br /&gt;
     The stand-alone environment forces the creation of larger and more&lt;br /&gt;
complex programs, each of which typically understands only its own file&lt;br /&gt;
formats and works poorly, if at all, with data produced by other programs.&lt;br /&gt;
This vision of personal computer software growing monstrous until&lt;br /&gt;
collapsing from its own weight brings about another OS/2 requirement:&lt;br /&gt;
Applications must be able to communicate, easily and efficiently, with&lt;br /&gt;
other applications.&lt;br /&gt;
     More specifically, an application must be able to find (or name) the&lt;br /&gt;
application that provides the information or service that the client needs,&lt;br /&gt;
and it must be able to establish efficient communication with the provider&lt;br /&gt;
program without requiring that either application have specific knowledge&lt;br /&gt;
of the internal workings of the other. Thus, a spreadsheet program must be&lt;br /&gt;
able to communicate with a database program and access the values it needs.&lt;br /&gt;
The spreadsheet program is therefore not tied to any particular database&lt;br /&gt;
program but can work with any database system that recognizes OS/2 IPC&lt;br /&gt;
requests.&lt;br /&gt;
     Applications running under OS/2 not only retain their full power as&lt;br /&gt;
individual applications but also benefit from cross-application&lt;br /&gt;
communication. Furthermore, the total system can be enhanced by upgrading&lt;br /&gt;
an application that provides services to others. When a new, faster, or&lt;br /&gt;
more fully featured database package is installed, not only is the user's&lt;br /&gt;
database application improved but the database functions of the spreadsheet&lt;br /&gt;
program are improved as well.&lt;br /&gt;
     The OS/2 philosophy is that no program should reinvent the wheel.&lt;br /&gt;
Programs should be written to offer their services to other programs and to&lt;br /&gt;
take advantage of the offered services of other programs. The result is a&lt;br /&gt;
maximally effective and efficient system.&lt;br /&gt;
&lt;br /&gt;
====2.1.7  Direct Device Access====&lt;br /&gt;
Earlier, we discussed the need for a high-performance graphical interface&lt;br /&gt;
and the limitations of the traditional device driver architecture. OS/2&lt;br /&gt;
contains a built-in solution for the screen graphical interface, but what&lt;br /&gt;
about other, specialized devices that may require a higher bandwidth&lt;br /&gt;
interface than device drivers provide? The one sure prediction about the&lt;br /&gt;
future of a technological breakthrough is that you can't fully predict it.&lt;br /&gt;
For this reason, the final key requirement for OS/2 is that it contain an&lt;br /&gt;
&amp;quot;escape hatch&amp;quot; in anticipation of devices that have performance needs too&lt;br /&gt;
great for a device driver model.&lt;br /&gt;
     OS/2 provides this expandability by allowing applications direct&lt;br /&gt;
access to hardware devices--both the I/O ports and any device memory. This&lt;br /&gt;
must be done, of course, in such a way that only devices which are intended&lt;br /&gt;
to be used in this fashion can be so accessed. Applications are prevented&lt;br /&gt;
from using this access technique on devices that are being managed by the&lt;br /&gt;
operating system or by a device driver. This facility gives applications&lt;br /&gt;
the ability to take advantage of special nonstandard hardware such as OCRs&lt;br /&gt;
(Optical Character Readers), digitizer tablets, Fax equipment, special&lt;br /&gt;
purpose graphics cards, and the like.&lt;br /&gt;
&lt;br /&gt;
===2.2  Compatibility Issues===&lt;br /&gt;
&lt;br /&gt;
But OS/2 has to do more than meet the goals we've discussed: It must be&lt;br /&gt;
compatible with 8086/8088 and 80286 architecture, and it must be compatible&lt;br /&gt;
with MS-DOS. By far the easiest solution would have been to create a new&lt;br /&gt;
multitasking operating system that would not be compatible with MS-DOS, but&lt;br /&gt;
such a system is unacceptable. Potential users may be excited about the new&lt;br /&gt;
system, but they won't buy it until applications are available. Application&lt;br /&gt;
writers may likewise be excited, but they won't adapt their products for it&lt;br /&gt;
until the system has sold enough copies to gain significant market share.&lt;br /&gt;
This &amp;quot;catch 22&amp;quot; means that the only people who will buy the new operating&lt;br /&gt;
system are the developers' mothers, and they probably get it at a discount&lt;br /&gt;
anyway.&lt;br /&gt;
&lt;br /&gt;
====2.2.1  Real Mode vs Protect Mode====&lt;br /&gt;
The first real mode compatibility issue relates to the design of the 80286&lt;br /&gt;
microprocessor--the &amp;quot;brain&amp;quot; of an MS-DOS computer. This chip has two&lt;br /&gt;
incompatible modes--real (compatibility) mode and protect mode. Real mode&lt;br /&gt;
is designed to run programs in exactly the same manner as they run on the&lt;br /&gt;
8086/8088 processor. In other words, when the 80286 is in real mode, it&lt;br /&gt;
&amp;quot;looks&amp;quot; to the operating system and programs exactly like a fast&lt;br /&gt;
8088.&lt;br /&gt;
     But the designers of the 80286 wanted it to be more than a fast 8088.&lt;br /&gt;
They wanted to add such features as memory management, memory protection,&lt;br /&gt;
and the ring protection mechanism, which allows the operating system to&lt;br /&gt;
protect one application from another. They weren't able to do this while&lt;br /&gt;
remaining fully compatible with the earlier 8088 chip, so they added a&lt;br /&gt;
second mode to the 80286--protect mode. When the processor is running in&lt;br /&gt;
protect mode, it provides these important new features, but it will not run&lt;br /&gt;
most programs written for the 8086/8088.&lt;br /&gt;
     In effect, an 80286 is two separate microprocessors in one package. It&lt;br /&gt;
can act like a very fast 8088--compatible, but with no new capabilities--or&lt;br /&gt;
it can act like an 80286--incompatible, but providing new features.&lt;br /&gt;
Unfortunately, the designers of the chip didn't appreciate the importance&lt;br /&gt;
of compatibility in the MS-DOS marketplace, and they designed the 80286 so&lt;br /&gt;
that it can run in either mode but can't switch back and forth at will.&lt;br /&gt;
4. The 80286 initializes itself in real mode. There is a command&lt;br /&gt;
to switch from real mode to protect mode, but there is no command&lt;br /&gt;
to switch back.&lt;br /&gt;
&lt;br /&gt;
In other words, an 80286 was designed to run only old 8086/8088 programs,&lt;br /&gt;
or it can run only new 80286 style programs, but never both at the same&lt;br /&gt;
time.&lt;br /&gt;
     In summary, OS/2 was required to do something that the 80286 was not&lt;br /&gt;
designed for--execute both 8086/8088 style (real mode) and 80286 style&lt;br /&gt;
(protect) mode programs at the same time. The existence of this book should&lt;br /&gt;
lead you to believe that this problem was solved, and indeed it was.&lt;br /&gt;
&lt;br /&gt;
====2.2.2  Running Applications in Real (Compatibility) Mode====&lt;br /&gt;
Solving the real mode vs protect mode problem, however, presented other&lt;br /&gt;
problems. In general, the problems came about because the real mode&lt;br /&gt;
programs were written for MS-DOS versions 2.x or 3.x, both of which are&lt;br /&gt;
single-tasking environments.&lt;br /&gt;
     Although MS-DOS is normally spoken of as an operating system, it could&lt;br /&gt;
just as accurately be called a &amp;quot;system executive.&amp;quot; Because it runs in an&lt;br /&gt;
unprotected environment, applications are free to edit interrupt vectors,&lt;br /&gt;
manipulate peripherals, and in general take over from MS-DOS wherever they&lt;br /&gt;
wish. This flexibility is one reason for the success of MS-DOS; if MS-DOS&lt;br /&gt;
doesn't offer the service your program needs, you can always help yourself.&lt;br /&gt;
Developers were free to explore new possibilities, often with great&lt;br /&gt;
success. Most applications view MS-DOS as a program loader and as a set of&lt;br /&gt;
file system subroutines, interfacing directly with the hardware for all&lt;br /&gt;
their other needs, such as intercepting interrupt vectors, editing disk&lt;br /&gt;
controller parameter tables, and so on.&lt;br /&gt;
     It may seem that if a popular application &amp;quot;pokes&amp;quot; the operating system&lt;br /&gt;
and otherwise engages in unsavory practices that the authors or users of&lt;br /&gt;
the application will suffer because a future release, such as OS/2, may not&lt;br /&gt;
run the application correctly. To the contrary, the market dynamics state&lt;br /&gt;
that the application has now set a standard, and it's the operating system&lt;br /&gt;
developers who suffer because they must support that standard. Usually,&lt;br /&gt;
that &amp;quot;standard&amp;quot; operating system interface is not even known; a great deal&lt;br /&gt;
of experimentation is necessary to discover exactly which undocumented side&lt;br /&gt;
effects, system internals, and timing relationships the application is&lt;br /&gt;
dependent on.&lt;br /&gt;
     Offering an MS-DOS-compatible Applications Program Interface (API)&lt;br /&gt;
provides what we call level 1 compatibility. Allowing applications to&lt;br /&gt;
continue to manipulate system hardware provides level 2 compatibility.&lt;br /&gt;
Level 3 issues deal with providing an execution environment that supports&lt;br /&gt;
the hidden assumptions that programs written for a single-tasking&lt;br /&gt;
environment may make. Three are discussed below by way of illustration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.1  Memory Utilization&lt;br /&gt;
The existing real mode applications that OS/2 must support were written for&lt;br /&gt;
an environment in which no other programs are running. As a result,&lt;br /&gt;
programs typically consume all available memory in the system in the belief&lt;br /&gt;
that, since no other program is around to use any leftover memory, they&lt;br /&gt;
might as well use it all. If a program doesn't ask for all available memory&lt;br /&gt;
at first, it may ask for the remainder at some later time. Such a&lt;br /&gt;
subsequent request could never be refused under MS-DOS versions 2.x and&lt;br /&gt;
3.x, and applications were written to depend on this. Therefore, such a&lt;br /&gt;
request must be satisfied under OS/2 to maintain full compatibility.&lt;br /&gt;
     Even the manner of a memory request depends on single-tasking&lt;br /&gt;
assumptions. Programs typically ask for all memory in two steps. First,&lt;br /&gt;
they ask for the maximum amount of memory that an 8088 can provide--1 MB.&lt;br /&gt;
The application's programmer knew that the request would be refused because&lt;br /&gt;
1 MB is greater than the 640 KB maximum supported by MS-DOS; but when MS-&lt;br /&gt;
DOS refuses the request, it tells the application exactly how much memory&lt;br /&gt;
is available. Programs then ask for that amount of memory. The programmer&lt;br /&gt;
knew that MS-DOS would not refuse the second memory request for&lt;br /&gt;
insufficient memory because when MS-DOS responded to the first request it&lt;br /&gt;
told the application exactly how much memory was available. Consequently,&lt;br /&gt;
programmers rarely included a check for an &amp;quot;insufficient memory&amp;quot; error from&lt;br /&gt;
the second call.&lt;br /&gt;
     This shortcut introduces problems in the OS/2 multitasking&lt;br /&gt;
environment. When OS/2 responded to the first too-large request, it would&lt;br /&gt;
return the amount of memory available at that exact moment. Other programs&lt;br /&gt;
are simultaneously executing; by the time our real mode program makes its&lt;br /&gt;
second request, some more memory may have been given out, and the second&lt;br /&gt;
request may also be too large. It won't do any good for OS/2 to respond&lt;br /&gt;
with an error code, however, because the real mode application does not&lt;br /&gt;
check for one (it was written in the belief that it is impossible to get&lt;br /&gt;
such a code on the second call). The upshot is that even if OS/2 refused&lt;br /&gt;
the second call the real mode application would assume that it had been&lt;br /&gt;
given the memory, would use it, and in the process would destroy the other&lt;br /&gt;
program(s) that were the true owners of that memory.&lt;br /&gt;
     Obviously, OS/2 must resolve this and similar issues to support the&lt;br /&gt;
existing base of real mode applications.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.2  File Locking&lt;br /&gt;
Because multitasking systems run more than one program at the same time,&lt;br /&gt;
two programs may try to write or to modify the same file at the same time.&lt;br /&gt;
Or one may try to read a file while another is changing that file's&lt;br /&gt;
contents. Multitasking systems usually solve this problem by means of a&lt;br /&gt;
file-locking mechanism, which allows one program to temporarily prevent&lt;br /&gt;
other programs from reading and/or writing a particular file.&lt;br /&gt;
     An application may find that a file it is accessing has been locked by&lt;br /&gt;
some other application in the system. In such a situation, OS/2 normally&lt;br /&gt;
returns a &amp;quot;file locked&amp;quot; error code, and the application typically gives up&lt;br /&gt;
or waits and retries the operation later. OS/2 cannot return a &amp;quot;file&lt;br /&gt;
locked&amp;quot; error to an old-style real mode application, though, because when&lt;br /&gt;
the application was written (for MS-DOS versions 2.x or 3.x) no such error&lt;br /&gt;
code existed because no such error was possible. Few real mode applications&lt;br /&gt;
even bother to check their read and write operations for error codes, and&lt;br /&gt;
those that do wouldn't &amp;quot;understand&amp;quot; the error code and wouldn't handle it&lt;br /&gt;
correctly.&lt;br /&gt;
     OS/2 cannot compromise the integrity of the file-locking mechanism by&lt;br /&gt;
allowing the real mode application to ignore locks, but it cannot report&lt;br /&gt;
that the file is locked to the application either. OS/2 must determine the&lt;br /&gt;
proper course of action and then take that action on behalf of the real&lt;br /&gt;
mode application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.3  Network Piggybacking&lt;br /&gt;
Running under MS-DOS version 3.1, an application can use an existing&lt;br /&gt;
network virtual circuit to communicate with an application running on the&lt;br /&gt;
server machine to which the virtual circuit is connected. This is called&lt;br /&gt;
&amp;quot;piggybacking&amp;quot; the virtual circuit because the applications on each end are&lt;br /&gt;
borrowing a circuit that the network redirector established for other&lt;br /&gt;
purposes. The two sets of programs can use a single circuit for two&lt;br /&gt;
different purposes without confusion under MS-DOS version 3.1 because of&lt;br /&gt;
its single-tasking nature. The redirector only uses the circuit when the&lt;br /&gt;
application calls MS-DOS to perform a network function. Because the CPU is&lt;br /&gt;
inside MS-DOS, it can't be executing the application software that sends&lt;br /&gt;
private messages, which leaves the circuit free for use by the&lt;br /&gt;
redirector.&lt;br /&gt;
     Conversely, if the application is sending its own private messages--&lt;br /&gt;
piggybacking--then it can't be executing MS-DOS, and therefore the&lt;br /&gt;
redirector code (which is built into MS-DOS) can't be using the virtual&lt;br /&gt;
circuit.&lt;br /&gt;
     This is no longer the case in OS/2. OS/2 is a multitasking system, and&lt;br /&gt;
one application can use the redirector at the same time that the real mode&lt;br /&gt;
application is piggybacking the circuit. OS/2 must somehow interlock access&lt;br /&gt;
to network virtual circuits so that multiple users of a network virtual&lt;br /&gt;
circuit do not conflict.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.3  Popular Function Compatibility&lt;br /&gt;
We've discussed some issues of binary compatibility, providing applications&lt;br /&gt;
the internal software interfaces they had in MS-DOS. This is because it is&lt;br /&gt;
vitally important that existing applications run correctly, unchanged,&lt;br /&gt;
under the new operating system.&lt;br /&gt;
5. An extremely high degree of compatibility is required for&lt;br /&gt;
virtually any application to run because a typical application&lt;br /&gt;
uses a great many documented and undocumented interfaces and&lt;br /&gt;
features of the earlier system. If any one of those interfaces&lt;br /&gt;
is not supplied, the application will not run correctly.&lt;br /&gt;
Consequently, we cannot provide 90 percent compatibility and&lt;br /&gt;
expect to run 90 percent of existing applications; 99.9 percent&lt;br /&gt;
compatibility is required for such a degree of success.&lt;br /&gt;
5 OS/2 also needs to provide functional&lt;br /&gt;
compatibility; it has to allow the creation of protect mode applications&lt;br /&gt;
that provide the functions that users grew to know and love in real mode&lt;br /&gt;
applications.&lt;br /&gt;
     This can be difficult because many popular applications (for example,&lt;br /&gt;
&amp;quot;terminate and stay resident loadable helper&amp;quot; routines such as SideKick)&lt;br /&gt;
were written for a single-tasking, unprotected environment without regard&lt;br /&gt;
to the ease with which their function could be provided in a protected&lt;br /&gt;
environment. For example, a popular application may implement some of its&lt;br /&gt;
features by patching (that is, editing) MS-DOS itself. This cannot be&lt;br /&gt;
allowed in OS/2 (the reason is discussed in Chapter 4), so OS/2 must&lt;br /&gt;
provide alternative mechanisms for protect mode applications to provide&lt;br /&gt;
services that users have grown to expect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4  Downward Compatibility&lt;br /&gt;
So far, our discussion on compatibility has focused exclusively on upward&lt;br /&gt;
compatibility--old programs must run in the new system but not vice versa.&lt;br /&gt;
Downward compatibility--running new programs under MS-DOS--is also&lt;br /&gt;
important. Developers are reluctant to write OS/2-only applications until&lt;br /&gt;
OS/2 has achieved major penetration of the market, yet this very&lt;br /&gt;
unavailability of software slows such penetration. If it's possible to&lt;br /&gt;
write applications that take advantage of OS/2's protect mode yet also run&lt;br /&gt;
unchanged under MS-DOS version 3.x, ISVs (Independent Software Vendors) can&lt;br /&gt;
write their products for OS/2 without locking themselves out of the&lt;br /&gt;
existing MS-DOS market.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4.1  Family API&lt;br /&gt;
To provide downward compatibility for applications, OS/2 designers&lt;br /&gt;
integrated a Family&lt;br /&gt;
6.Family refers to the MS-DOS/OS/2 family of&lt;br /&gt;
operating systems.&lt;br /&gt;
6 Applications Program Interface (Family API) into the&lt;br /&gt;
OS/2 project. The Family API provides a standard execution environment&lt;br /&gt;
under MS-DOS version 3.x and OS/2. Using the Family API, a programmer can&lt;br /&gt;
create an application that uses a subset of OS/2 functions (but a superset&lt;br /&gt;
of MS-DOS version 3.x functions) and that runs in a binary compatible&lt;br /&gt;
fashion under MS-DOS version 3.x and OS/2. In effect, some OS/2 functions&lt;br /&gt;
can be retrofitted into an MS-DOS version 3.x environment by means of the&lt;br /&gt;
Family API.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4.2  Network Server-Client Compatibility&lt;br /&gt;
Another important form of upward and downward compatibility is the network&lt;br /&gt;
system. You can expect any OS/2 system to be on a network, communicating&lt;br /&gt;
not only with MS-DOS 3.x systems but, one day, with a new version of OS/2&lt;br /&gt;
as well. The network interface must be simultaneously upwardly and&lt;br /&gt;
downwardly compatible with all past and future versions of networking MS-&lt;br /&gt;
DOS.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==3  The OS/2 Religion==&lt;br /&gt;
&lt;br /&gt;
Religion, in the context of software design, is a body of beliefs about&lt;br /&gt;
design rights and design wrongs. A particular design is praised or&lt;br /&gt;
criticized on the basis of fact--it is small or large, fast or slow--and&lt;br /&gt;
also on the basis of religion--it is good or bad, depending on how well it&lt;br /&gt;
obeys the religious precepts. Purpose and consistency underlie the design&lt;br /&gt;
religion as a whole; its influence is felt in every individual judgment.&lt;br /&gt;
     The purpose of software design religion is to specify precepts that&lt;br /&gt;
designers can follow when selecting an approach from among the many&lt;br /&gt;
possibilities before them. A project of the size and scope of OS/2 needed a&lt;br /&gt;
carefully thought out religion because OS/2 will dramatically affect this&lt;br /&gt;
and future generations of operating systems. It needed a strong religion&lt;br /&gt;
for another reason: to ensure consistency among wide-ranging features&lt;br /&gt;
implemented by a large team of programmers. Such consistency is very&lt;br /&gt;
important; if one programmer optimizes design to do A well, at the expense&lt;br /&gt;
of doing B less well, and another programmer--in the absence of religious&lt;br /&gt;
guidance--does the opposite, the end result is a product that does neither&lt;br /&gt;
A nor B well.&lt;br /&gt;
     This chapter discusses the major architectural dogmas of the OS/2&lt;br /&gt;
religion: maximum flexibility, a stable environment, localization of&lt;br /&gt;
errors, and the software tools approach.&lt;br /&gt;
&lt;br /&gt;
===3.1  Maximum Flexibility===&lt;br /&gt;
&lt;br /&gt;
The introduction to this book discusses the process of technological&lt;br /&gt;
breakthroughs. I have pointed out that one of the easiest predictions about&lt;br /&gt;
breakthroughs is that fully predicting their course is impossible. For&lt;br /&gt;
example, the 8088 microprocessor is designed to address 1 MB of memory, but&lt;br /&gt;
the IBM PC and compatible machines are designed so that addressable memory&lt;br /&gt;
is limited to 640 KB. When this decision was made, 640 KB was ten times the&lt;br /&gt;
memory that the then state-of-the-art 8080 machines could use; the initial&lt;br /&gt;
PCs were going to ship with 16 KB in them, and it seemed to all concerned&lt;br /&gt;
that 640 KB was overly generous. Yet it took only a few years before 640 KB&lt;br /&gt;
became the typical memory complement of a machine, and within another year&lt;br /&gt;
that amount of memory was viewed as pitifully small.&lt;br /&gt;
     OS/2's design religion addresses the uncertain future by decreeing&lt;br /&gt;
that--to the extent compatible with other elements in the design religion--&lt;br /&gt;
OS/2 shall be as flexible as possible. The tenet of flexibility is that&lt;br /&gt;
each component of OS/2 should be designed as if massive changes will occur&lt;br /&gt;
in that area in a future release. In other words, the current component&lt;br /&gt;
should be designed in a way that does not restrict new features and in a&lt;br /&gt;
way that can be easily supported by a new version of OS/2, one that might&lt;br /&gt;
differ dramatically in internal design.&lt;br /&gt;
     Several general principles result from a design goal of flexibility.&lt;br /&gt;
All are intended to facilitate change, which is inevitable in the general&lt;br /&gt;
yet unpredictable in the specific:&lt;br /&gt;
&lt;br /&gt;
     1.  All OS/2 features should be sufficiently elemental (simple) that&lt;br /&gt;
         they can be easily supported in any future system, including&lt;br /&gt;
         systems fundamentally different in design from OS/2. Either the&lt;br /&gt;
         features themselves are this simple, or the features are built&lt;br /&gt;
         using base features that are this simple. The adjective simple&lt;br /&gt;
         doesn't particularly refer to externals--a small number of&lt;br /&gt;
         functions and options--but to internals. The internal operating&lt;br /&gt;
         system infrastructure necessary to provide a function should be&lt;br /&gt;
         either very simple or so fundamental to the nature of operating&lt;br /&gt;
         systems that it is inevitable in future releases.&lt;br /&gt;
            By way of analogy, as time travelers we may be able to guess&lt;br /&gt;
         very little about the twenty-first century, but we do know that &lt;br /&gt;
         people will still need to eat. The cuisine of the twenty-first&lt;br /&gt;
         century may be unguessable, but certainly future kitchens will&lt;br /&gt;
         contain facilities to cut and heat food. If we bring food that&lt;br /&gt;
         needs only those two operations, we'll find that even if there's&lt;br /&gt;
         nothing to our liking on the twenty-first-century standard menu&lt;br /&gt;
         the kitchen can still meet our needs.&lt;br /&gt;
            We've seen how important upward compatibility is for computer&lt;br /&gt;
         operating systems, so we can rest assured that the future MS-DOS&lt;br /&gt;
         &amp;quot;kitchen&amp;quot; will be happy to make the necessary effort to support&lt;br /&gt;
         old programs. All we have to do today is to ensure that such&lt;br /&gt;
         support is possible. Producing a compatible line of operating&lt;br /&gt;
         system releases means more than looking backward; it also means&lt;br /&gt;
         looking forward.&lt;br /&gt;
&lt;br /&gt;
     2.  All system interfaces need to support expansion in a future&lt;br /&gt;
         release. For example, if a call queries the status of a disk file,&lt;br /&gt;
         then in addition to passing the operating system a pointer to a&lt;br /&gt;
         structure to fill in with the information, the application must&lt;br /&gt;
         also pass in the length of that structure. Although the current&lt;br /&gt;
         release of the operating system returns N bytes of information, a&lt;br /&gt;
         future release may support new kinds of disk files and may return&lt;br /&gt;
         M bytes of information. Because the application tells the&lt;br /&gt;
         operating system, via the buffer length parameter, which version&lt;br /&gt;
         of the information structure that the application understands (the&lt;br /&gt;
         old short version or the new longer version), the operating system&lt;br /&gt;
         can support both old programs and new programs simultaneously.&lt;br /&gt;
            In general, all system interfaces should be designed to support&lt;br /&gt;
         the current feature set without restraining the addition of&lt;br /&gt;
         features to the interfaces in future releases. Extra room should&lt;br /&gt;
         be left in count and flag arguments for future expansion, and all&lt;br /&gt;
         passed and returned structures need either to be self-sizing or to&lt;br /&gt;
         include a size argument.&lt;br /&gt;
            One more interface deserves special mention--the file system&lt;br /&gt;
         interface. Expanding the capabilities of the file system, such as&lt;br /&gt;
         allowing filenames longer than eight characters, is difficult&lt;br /&gt;
         because many old applications don't know how to process filenames&lt;br /&gt;
         that are longer than eight characters or they regard the longer&lt;br /&gt;
         names as illegal and reject them. OS/2 solves this and similar&lt;br /&gt;
         problems by specifying that all filenames supplied to or returned&lt;br /&gt;
         from the operating system be zero-terminated strings (ASCIIZ&lt;br /&gt;
         strings) of arbitrary length.&lt;br /&gt;
            Programmers are specifically cautioned against parsing or&lt;br /&gt;
         otherwise &amp;quot;understanding&amp;quot; filenames. Programs should consider file&lt;br /&gt;
         system pathnames as &amp;quot;magic cookies&amp;quot; to be passed to and from the&lt;br /&gt;
         operating system, but not to be parsed by the program. The details&lt;br /&gt;
         of this interface and other expandable interfaces are discussed in&lt;br /&gt;
         later chapters.&lt;br /&gt;
&lt;br /&gt;
     3.  OS/2 needs to support the addition of functions at any time. The&lt;br /&gt;
         implementation details of these functions need to be hidden from&lt;br /&gt;
         the client applications so that those details can be changed at&lt;br /&gt;
         any time. Indeed, OS/2 should disguise even the source of a&lt;br /&gt;
         feature. Some APIs are serviced by kernel code, others are&lt;br /&gt;
         serviced by subroutine libraries, and still others may be serviced&lt;br /&gt;
         by other processes running in the system. Because a client&lt;br /&gt;
         application can't tell the difference, the system designers are&lt;br /&gt;
         free to change the implementation of an API as necessary. For&lt;br /&gt;
         example, an OS/2 kernel API might be considerably changed in a&lt;br /&gt;
         future release. The old API can continue to be supported by the&lt;br /&gt;
         creation of a subroutine library routine. This routine would take&lt;br /&gt;
         the old form of the API, convert it to the new form, call the OS/2&lt;br /&gt;
         kernel, and then backconvert the result. Such a technique allows&lt;br /&gt;
         future versions of OS/2 to support new features while continuing&lt;br /&gt;
         to provide the old features to existing programs. These techniques&lt;br /&gt;
         are discussed in detail in Chapter 7, Dynamic Linking.&lt;br /&gt;
&lt;br /&gt;
     4.  Finally, to provide maximum flexibility, the operating system&lt;br /&gt;
         should be extensible and expandable in a piecemeal fashion out in&lt;br /&gt;
         the field. In other words, a user should be able to add functions&lt;br /&gt;
         to the system--for example, a database engine--or to upgrade or&lt;br /&gt;
         replace system components--such as a new graphics display driver--&lt;br /&gt;
         without a new release from Microsoft. A microcomputer design that&lt;br /&gt;
         allows third-party hardware additions and upgrades in the field is&lt;br /&gt;
         called an open system. The IBM PC line is a classic example of an&lt;br /&gt;
         open system. A design that contains no provisions for such&lt;br /&gt;
         enhancements is called a closed system. The earliest version of&lt;br /&gt;
         the Apple Macintosh is an example. At first glance, MS-DOS appears&lt;br /&gt;
         to be a closed software system because it contains no provisions&lt;br /&gt;
         for expansion. In practice, its unprotected environment makes MS-&lt;br /&gt;
         DOS the king of the open software systems because every&lt;br /&gt;
         application is free to patch the system and access the hardware as&lt;br /&gt;
         it sees fit. Keeping a software system open is as important as&lt;br /&gt;
         keeping a hardware system open. Because OS/2 is a protected&lt;br /&gt;
         operating system, explicit features, such as dynamic linking, are&lt;br /&gt;
         provided to allow system expansion by Microsoft, other software&lt;br /&gt;
         vendors, and users themselves. The topic of open systems is&lt;br /&gt;
         discussed more fully in Chapter 7.&lt;br /&gt;
&lt;br /&gt;
===3.2  A Stable Environment===&lt;br /&gt;
&lt;br /&gt;
An office automation operating system has to provide its users--the&lt;br /&gt;
application programs and the human operator--with a stable environment.&lt;br /&gt;
Every application should work the same way each time it's run; and each&lt;br /&gt;
time an application is given the same data, it should produce the same&lt;br /&gt;
result. The normal operation of one application should not affect any other&lt;br /&gt;
application. Even a program error (bug) should not affect other programs in&lt;br /&gt;
the system. Finally, if a program has bugs, the operating system should&lt;br /&gt;
detect those bugs whenever possible and report them to the user. These&lt;br /&gt;
certainly are obvious goals, but the nature of present-day computers makes&lt;br /&gt;
them surprisingly difficult to achieve.&lt;br /&gt;
&lt;br /&gt;
====3.2.1  Memory Protection====&lt;br /&gt;
Modern computers are based on the Von Neumann design--named after John Von&lt;br /&gt;
Neumann, the pioneering Hungarian-born American mathematician and computer&lt;br /&gt;
scientist. A Von Neumann computer consists of only two parts: a memory unit&lt;br /&gt;
and a processing unit. The memory unit contains both the data to be&lt;br /&gt;
operated on and the instructions (or program) that command the processing&lt;br /&gt;
unit. The processing unit reads instructions from memory; these&lt;br /&gt;
instructions may tell it to issue further reads to memory to retrieve data,&lt;br /&gt;
to operate on data retrieved earlier, or to store data back into&lt;br /&gt;
memory.&lt;br /&gt;
     A Von Neumann computer does not distinguish between instructions and&lt;br /&gt;
data; both are stored in binary code in the computer's memory. Individual&lt;br /&gt;
programs are responsible for keeping track of which memory locations hold&lt;br /&gt;
instructions and which hold data, and each program uses the memory in a&lt;br /&gt;
different way. Because the computer does not distinguish between&lt;br /&gt;
instructions and data, a program may operate on its own instructions&lt;br /&gt;
exactly as it operates on data. A program can read, modify, and write&lt;br /&gt;
computer instructions at will.&lt;br /&gt;
1. This is a simplification. OS/2 and the 80286 CPU contain&lt;br /&gt;
features that do distinguish somewhat between instructions and&lt;br /&gt;
data and that limit the ability of programs to modify their own&lt;br /&gt;
instructions. See 9.1 Protection Model for more information.&lt;br /&gt;
1&lt;br /&gt;
     This is exactly what OS/2 does when it is commanded to run a program:&lt;br /&gt;
It reads the program into memory by treating it as data, and then it causes&lt;br /&gt;
the data in those locations to be executed. It is even possible for a&lt;br /&gt;
program to dynamically &amp;quot;reprogram&amp;quot; itself by manipulating its own&lt;br /&gt;
instructions.&lt;br /&gt;
     Computer programs are extremely complex, and errors in their logic can&lt;br /&gt;
cause the program to unintentionally modify data or instructions in memory.&lt;br /&gt;
For example, a carelessly written program might contain a command buffer 80&lt;br /&gt;
bytes in size because it expects no commands longer than 80 bytes. If a&lt;br /&gt;
user types a longer command, perhaps in error, and the program does not&lt;br /&gt;
contain a special check for this circumstance, the program will overwrite&lt;br /&gt;
the memory beyond the 80-byte command buffer, destroying the data or&lt;br /&gt;
instructions placed there.&lt;br /&gt;
2. This is a simplified example. Rarely would a present-day,&lt;br /&gt;
well-tested application contain such a naive error, but errors of&lt;br /&gt;
this type--albeit in a much more complex form--exist in nearly&lt;br /&gt;
all software.&lt;br /&gt;
2&lt;br /&gt;
     In a single-tasking environment such as MS-DOS, only one application&lt;br /&gt;
runs at a time. An error such as our example could damage memory belonging&lt;br /&gt;
to MS-DOS, the application, or memory that is not in use. In practice (due&lt;br /&gt;
to memory layout conventions) MS-DOS is rarely damaged. An aberrant program&lt;br /&gt;
typically damages itself or modifies memory not in use. In any case, the&lt;br /&gt;
error goes undetected, the program produces an incorrect result, or the&lt;br /&gt;
system crashes. In the last two cases, the user loses work, but it is clear&lt;br /&gt;
which application is in error--the one executing at the time of the crash.&lt;br /&gt;
(For completeness, I'll point out that it is possible for an aberrant&lt;br /&gt;
application to damage MS-DOS subtly enough so that the application itself&lt;br /&gt;
completes correctly, but the next time an application runs, it fails. This&lt;br /&gt;
is rare, and the new application generally fails immediately upon startup;&lt;br /&gt;
so after a few such episodes with different applications, the user&lt;br /&gt;
generally identifies the true culprit.)&lt;br /&gt;
     As we have seen, errors in programs are relatively well contained in a&lt;br /&gt;
single-tasking system. MS-DOS cannot, unfortunately, correct the error, nor&lt;br /&gt;
can it very often detect the error (these tasks can be shown to be&lt;br /&gt;
mathematically impossible, in the general case). But at least the errors&lt;br /&gt;
are contained within the aberrant application; and should errors in data or&lt;br /&gt;
logic become apparent, the user can identify the erring application. When&lt;br /&gt;
we execute a second program in memory alongside the first, the situation&lt;br /&gt;
becomes more complicated.&lt;br /&gt;
     The first difficulty arises because the commonest error for a program&lt;br /&gt;
to make is to use memory that MS-DOS has not allocated to it. In a single-&lt;br /&gt;
tasking environment these memory locations are typically unused, but in a&lt;br /&gt;
multitasking environment the damaged location(s) probably belong to some&lt;br /&gt;
other program. That program will then either give incorrect results, damage&lt;br /&gt;
still other memory locations, crash, or some combination of these. In&lt;br /&gt;
summary, a memory addressing error is more dangerous because there is more&lt;br /&gt;
in memory to damage and that damage will have a more severe effect.&lt;br /&gt;
     The second difficulty arises, not from explicit programming errors,&lt;br /&gt;
but from conflicts in the normal operation of two or more co-resident&lt;br /&gt;
programs that are in some fashion incompatible. A simple example is called&lt;br /&gt;
&amp;quot;hooking the keyboard vector&amp;quot; (see Figure 3-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
BEFORE&lt;br /&gt;
                      Vector table                    Keyboard device&lt;br /&gt;
                     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿                   driver routine&lt;br /&gt;
  Device interrupt   ³            ³        ÚÄÄÄÄÄÄÄÄ� x x x x: ÄÄÄÄÄÄ&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÃÄÄÄÄÄÄÄÄÄÄÄÄ´        ³                   ÄÄÄÄÄÄ&lt;br /&gt;
             ÀÄÄÄÄÄ� ³  x x x x   ÃÄÄÄÄÄÄÄÄÙ                   ÄÄÄÄÄÄ&lt;br /&gt;
                     ÃÄÄÄÄÄÄÄÄÄÄÄÄ´                            ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³                            ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³&lt;br /&gt;
                     ³            ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
AFTER                                 Application      Keyboard device&lt;br /&gt;
                      Vector table    edits table      driver&lt;br /&gt;
                     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿    ³            Ú� x x x x: ÄÄÄÄÄÄ&lt;br /&gt;
  Device interrupt   ³            ³    ³            ³           ÄÄÄÄÄÄ&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÃÄÄÄÄÄÄÄÄÄÄÄÄ´ �ÄÄÙ            ÀÄÄÄÄÄÄÄÄÄ¿ ÄÄÄÄÄÄ&lt;br /&gt;
             ÀÄÄÄÄÄ� ³  y y y y   ÃÄ¿   Application           ³ ÄÄÄÄÄÄ&lt;br /&gt;
                     ÃÄÄÄÄÄÄÄÄÄÄÄÄ´ ³   routine               ³ ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³ ÀÄ� y y y y: ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ³            ³              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ³            ³              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                                                 ÄÄÄÄÄÄ       ³&lt;br /&gt;
                                                 jmp x x x x ÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 3-1.  Hooking the keyboard vector.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     In this case, an application modifies certain MS-DOS memory locations&lt;br /&gt;
so that when a key is pressed the application code, instead of the MS-DOS&lt;br /&gt;
code, is notified by the hardware. Applications do this because it allows&lt;br /&gt;
them to examine certain keyboard events, such as pressing the shift key&lt;br /&gt;
without pressing any other key, that MS-DOS does not pass on to&lt;br /&gt;
applications which ask MS-DOS to read the keyboard for them. It works fine&lt;br /&gt;
for one application to &amp;quot;hook&amp;quot; the keyboard vector; although hooking the&lt;br /&gt;
keyboard vector modifies system memory locations that don't belong to the&lt;br /&gt;
application, the application generally gets away with it successfully. In a&lt;br /&gt;
multitasking environment, however, a second application may want to do the&lt;br /&gt;
same trick, and the system probably won't function correctly. The result is&lt;br /&gt;
that a stable environment requires memory protection. An application must&lt;br /&gt;
not be allowed to modify, accidentally or deliberately, memory that isn't&lt;br /&gt;
assigned to that application.&lt;br /&gt;
&lt;br /&gt;
====3.2.2  Side-Effects Protection====&lt;br /&gt;
A stable environment requires more than memory protection; it also requires&lt;br /&gt;
that the system be designed so that the execution of one application&lt;br /&gt;
doesn't cause side effects for any other application. Side effects can be&lt;br /&gt;
catastrophic or they can be unremarkable, but in all cases they violate the&lt;br /&gt;
tenet of a stable environment.&lt;br /&gt;
     For example, consider the practice of hooking the keyboard interrupt&lt;br /&gt;
vector. If one application uses this technique to intercept keystrokes, it&lt;br /&gt;
will intercept all keystrokes, even those intended for some other&lt;br /&gt;
application. The side effects in this case are catastrophic--the hooking&lt;br /&gt;
application sees keystrokes that aren't intended for it, and the other&lt;br /&gt;
applications don't get any keystrokes at all.&lt;br /&gt;
     Side effects can plague programs even when they are using official&lt;br /&gt;
system features if those features are not carefully designed. For example,&lt;br /&gt;
a mainframe operating system called TOPS-10 contains a program that&lt;br /&gt;
supports command files similar to MS-DOS .BAT files, and it also contains a&lt;br /&gt;
program that provides delayed offline execution of commands. Unfortunately,&lt;br /&gt;
both programs use the same TOPS-10 facility to do their work. If you&lt;br /&gt;
include a .BAT file in a delayed command list, the two programs will&lt;br /&gt;
conflict, and the .BAT file will not execute.&lt;br /&gt;
     OS/2 deals with side effects by virtualizing to the greatest extent&lt;br /&gt;
possible each application's operating environment. This means that OS/2&lt;br /&gt;
tries to make each application &amp;quot;see&amp;quot; a standard environment that is&lt;br /&gt;
unaffected by changes in another application's environment. The effect is&lt;br /&gt;
like that of a building of identical apartments. When each tenant moves in,&lt;br /&gt;
he or she gets a standard environment, a duplicate of all the apartments.&lt;br /&gt;
Each tenant can customize his or her environment, but doing so doesn't&lt;br /&gt;
affect the other tenants or their environments.&lt;br /&gt;
     Following are some examples of application environment issues that&lt;br /&gt;
OS/2 virtualizes.&lt;br /&gt;
&lt;br /&gt;
     þ  Working Directories. Each application has a working (or current)&lt;br /&gt;
        directory for each disk drive. Under MS-DOS version 3.x, if a child&lt;br /&gt;
        process changes the working directory for drive C and then exits,&lt;br /&gt;
        the working directory for drive C remains changed when the parent&lt;br /&gt;
        process regains control. OS/2 eliminates this side effect by&lt;br /&gt;
        maintaining a separate list of working directories for each process&lt;br /&gt;
        in the system. Thus, when an application changes its working&lt;br /&gt;
        directories, the working directories of other applications in the&lt;br /&gt;
        system remain unchanged.&lt;br /&gt;
&lt;br /&gt;
     þ  Memory Utilization. The simple act of memory consumption produces&lt;br /&gt;
        side effects. If one process consumes all available RAM, none is&lt;br /&gt;
        left for the others. The OS/2 memory management system uses memory&lt;br /&gt;
        overcommit (swapping) so that the memory needs of each application&lt;br /&gt;
        can be met.&lt;br /&gt;
&lt;br /&gt;
     þ  Priority. OS/2 uses a priority-based scheduler to assign the CPU to&lt;br /&gt;
        the processes that need it. Applications can adjust their priority&lt;br /&gt;
        and that of their child processes as they see fit. However, the&lt;br /&gt;
        very priority of a task causes side effects. Consider a process&lt;br /&gt;
        that tells OS/2 that it must run at a higher priority than any&lt;br /&gt;
        other task in the system. If a second process makes the same&lt;br /&gt;
        request, a conflict occurs: Both processes cannot be the highest&lt;br /&gt;
        priority in the system. In general, the priority that a process&lt;br /&gt;
        wants for itself depends on the priorities of the other processes&lt;br /&gt;
        in the system. The OS/2 scheduler contains a sophisticated&lt;br /&gt;
        absolute/relative mechanism to deal with these conflicts.&lt;br /&gt;
&lt;br /&gt;
     þ  File Utilization. As discussed earlier, one application may modify&lt;br /&gt;
        the files that another application is using, causing an unintended&lt;br /&gt;
        side effect. The OS/2 file-locking mechanism prevents unintended&lt;br /&gt;
        modifications, and the OS/2 record-locking mechanism coordinates&lt;br /&gt;
        intentional parallel updates to a single file.&lt;br /&gt;
&lt;br /&gt;
     þ  Environment Strings. OS/2 retains the MS-DOS concept of environment&lt;br /&gt;
        strings: Each process has its own set. A child process inherits a&lt;br /&gt;
        copy of the parent's environment strings, but changing the strings&lt;br /&gt;
        in this copy will not affect the original strings in the parent's&lt;br /&gt;
        environment.&lt;br /&gt;
&lt;br /&gt;
     þ  Keyboard Mode. OS/2 applications can place the keyboard in one of&lt;br /&gt;
        two modes--cooked or raw. These modes tell OS/2 whether the&lt;br /&gt;
        application wants to handle, for example, the backspace character&lt;br /&gt;
        (raw mode) or whether it wants OS/2 to handle the backspace&lt;br /&gt;
        character for it (cooked mode). The effect of these calls on&lt;br /&gt;
        subsequent keyboard read operations would cause side effects for&lt;br /&gt;
        other applications reading from the keyboard, so OS/2 maintains a&lt;br /&gt;
        record of the cooked/raw status of each application and silently&lt;br /&gt;
        switches the mode of the keyboard when an application issues a&lt;br /&gt;
        keyboard read request.&lt;br /&gt;
&lt;br /&gt;
===3.3  Localization of Errors===&lt;br /&gt;
&lt;br /&gt;
A key element in creating a stable environment is localizing errors. Humans&lt;br /&gt;
always make errors, and human creations such as computer programs always&lt;br /&gt;
contain errors. Before the development of computers, routine human errors&lt;br /&gt;
were usually limited in scope. Unfortunately, as the saying goes, a&lt;br /&gt;
computer can make a mistake in 60 seconds that it would take a whole office&lt;br /&gt;
force a year to make. Although OS/2 can do little to prevent such errors,&lt;br /&gt;
it needs to do its best to localize the errors.&lt;br /&gt;
     Localizing errors consists of two activities: minimizing as much as&lt;br /&gt;
possible the impact of the error on other applications in the system, and&lt;br /&gt;
maximizing the opportunity for the user to understand which of the many&lt;br /&gt;
programs running in the computer caused the error. These two activities are&lt;br /&gt;
interrelated in that the more successful the operating system is in&lt;br /&gt;
restricting the damage to the domain of a single program, the easier it is&lt;br /&gt;
for the user to know which program is at fault.&lt;br /&gt;
     The most important aspect of error localization has already been&lt;br /&gt;
discussed at length--memory management and protection. Other error&lt;br /&gt;
localization principles include the following:&lt;br /&gt;
&lt;br /&gt;
     þ  No program can crash or hang the system. A fundamental element of&lt;br /&gt;
        the OS/2 design religion is that no application program can,&lt;br /&gt;
        accidentally or even deliberately, crash or hang the system. If a&lt;br /&gt;
        failing application could crash the system, obviously the system&lt;br /&gt;
        did not localize the error! Furthermore, the user would be unable&lt;br /&gt;
        to identify the responsible application because the entire system&lt;br /&gt;
        would be dead.&lt;br /&gt;
&lt;br /&gt;
     þ  No program can make inoperable any screen group other than its own.&lt;br /&gt;
        As we'll see in later chapters of this book, sometimes design&lt;br /&gt;
        goals, design religions, or both conflict. For example, the precept&lt;br /&gt;
        of no side effects conflicts with the requirement of supporting&lt;br /&gt;
        keyboard macro expander applications. The sole purpose of such an&lt;br /&gt;
        application is to cause a side effect--specifically to translate&lt;br /&gt;
        certain keystroke sequences into other sequences. OS/2 resolves&lt;br /&gt;
        this conflict by allowing applications to examine and modify the&lt;br /&gt;
        flow of data to and from devices (see Chapter 16) but in a&lt;br /&gt;
        controlled fashion. Thus, an aberrant keyboard macro application&lt;br /&gt;
        that starts to &amp;quot;eat&amp;quot; all keys, passing none through to the&lt;br /&gt;
        application, can make its current screen group unusable, but it&lt;br /&gt;
        can't affect the user's ability to change screen groups.&lt;br /&gt;
           Note that keyboard monitors can intercept and consume any&lt;br /&gt;
        character or character sequence except for the keystrokes that OS/2&lt;br /&gt;
        uses to switch screen groups (Ctrl-Esc and Alt-Esc). This is to&lt;br /&gt;
        prevent aberrant keyboard monitor applications from accidentally&lt;br /&gt;
        locking the user into his or her screen group by consuming and&lt;br /&gt;
        discarding the keyboard sequences that are used to switch from&lt;br /&gt;
        screen groups.&lt;br /&gt;
&lt;br /&gt;
     þ  Applications cannot intercept general protection (GP) fault errors.&lt;br /&gt;
        A GP fault occurs when a program accesses invalid memory locations&lt;br /&gt;
        or accesses valid locations in an invalid way (such as writing into&lt;br /&gt;
        read-only memory areas). OS/2 always terminates the operation and&lt;br /&gt;
        displays a message for the user. A GP fault is evidence that the&lt;br /&gt;
        program's logic is incorrect, and therefore it cannot be expected&lt;br /&gt;
        to fix itself or trusted to notify the user of its ill health.&lt;br /&gt;
           The OS/2 design does allow almost any other error on the part of&lt;br /&gt;
        an application to be detected and handled by that application. For&lt;br /&gt;
        example, &amp;quot;Illegal filename&amp;quot; is an error caused by user input, not&lt;br /&gt;
        by the application. The application can deal with this error as it&lt;br /&gt;
        sees fit, perhaps correcting and retrying the operation. An error&lt;br /&gt;
        such as &amp;quot;Floppy disk drive not ready&amp;quot; is normally handled by OS/2&lt;br /&gt;
        but can be handled by the application. This is useful for&lt;br /&gt;
        applications that are designed to operate unattended; they need to&lt;br /&gt;
        handle errors themselves rather than waiting for action to be taken&lt;br /&gt;
        by a nonexistent user.&lt;br /&gt;
&lt;br /&gt;
===3.4  Software Tools Approach===&lt;br /&gt;
&lt;br /&gt;
In Chapter 2 we discussed IPC and the desirability of having separate&lt;br /&gt;
functions contained in separate programs. We discussed the flexibility of&lt;br /&gt;
such an approach over the &amp;quot;one man band&amp;quot; approach of an all-in-one&lt;br /&gt;
application. We also touched on the value of being able to upgrade the&lt;br /&gt;
functionality of the system incrementally by replacing individual programs.&lt;br /&gt;
All these issues are software tools issues.&lt;br /&gt;
     Software tools refers to a design philosophy which says that&lt;br /&gt;
individual programs and applications should be like tools: Each should do&lt;br /&gt;
one job and do it very well. A person who wants to turn screws and also&lt;br /&gt;
drive nails should get a screwdriver and a hammer rather than a single tool&lt;br /&gt;
that does neither job as well.&lt;br /&gt;
     The tools approach is used routinely in nonsoftware environments and&lt;br /&gt;
is taken for granted. For example, inside a standard PC the hardware and&lt;br /&gt;
electronics are isolated into functional components that communicate via&lt;br /&gt;
interfaces. The power supply is in a box by itself; its interface is the&lt;br /&gt;
line cord and some power connectors. The disk drives are separate from the&lt;br /&gt;
rest of the electronics; they interface via more connectors. Each component&lt;br /&gt;
is the equivalent of a software application: It does one job and does it&lt;br /&gt;
well. When the disk drive needs power, it doesn't build in a power supply;&lt;br /&gt;
it uses the standard interface to the power supply module--the power&lt;br /&gt;
&amp;quot;specialist&amp;quot; in the system.&lt;br /&gt;
     Occasionally, the software tools approach is criticized for being&lt;br /&gt;
inefficient. People may argue that space is wasted and time is lost by&lt;br /&gt;
packaging key functions separately; if they are combined, the argument&lt;br /&gt;
goes, nothing is wasted. This argument is correct in that some RAM and CPU&lt;br /&gt;
time is spent on interface issues, but it ignores the gains involved in&lt;br /&gt;
&amp;quot;sending out the work&amp;quot; to a specialist rather than doing it oneself. One&lt;br /&gt;
could argue, for example, that if I built an all-in-one PC system I'd save&lt;br /&gt;
money because I wouldn't have to buy connectors to plug everything&lt;br /&gt;
together. I might also save a little by not having to buy buffer chips to&lt;br /&gt;
drive signals over those connectors. But in doing so, I'd lose the&lt;br /&gt;
advantage of being able to buy my power supply from a very high-volume and&lt;br /&gt;
high-efficiency supplier--someone who can make a better, cheaper supply,&lt;br /&gt;
even with the cost of connectors, than my computer company can.&lt;br /&gt;
     Finally, the user gains from the modular approach. If you need more&lt;br /&gt;
disk capability, you can buy one and plug it in. You are not limited to one&lt;br /&gt;
disk maker but rather can choose the one that's right for your needs--&lt;br /&gt;
expensive and powerful or cheap and modest. You can buy third-party&lt;br /&gt;
hardware, such as plug-in cards, that the manufacturer of your computer&lt;br /&gt;
doesn't make. All in all, the modest cost of a few connectors and driver&lt;br /&gt;
chips is paid back manyfold, both in direct system costs (due to the&lt;br /&gt;
efficiency of specialization) and in the additional capability and&lt;br /&gt;
flexibility of the machine.&lt;br /&gt;
     As I said earlier, the software tools approach is the software&lt;br /&gt;
equivalent of an open system. It's an important part of the OS/2 religion:&lt;br /&gt;
Although the system doesn't require a modular tools approach from&lt;br /&gt;
applications programs, it should do everything in its power to facilitate&lt;br /&gt;
such systems, and it should itself be constructed in that fashion.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Part II  The Architecture&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==4  Multitasking==&lt;br /&gt;
&lt;br /&gt;
I have discussed the goals and compatibility issues that OS/2 is intended&lt;br /&gt;
to meet, and I have described the design religion that was established for&lt;br /&gt;
OS/2. The following chapters discuss individual design elements in some&lt;br /&gt;
detail, emphasizing not only how the elements work and are used but the&lt;br /&gt;
role they play in the system as a whole.&lt;br /&gt;
     In a multitasking operating system, two or more programs can execute&lt;br /&gt;
at the same time. Some benefits of such a feature are obvious: You (the&lt;br /&gt;
user) can switch between several application programs without saving work&lt;br /&gt;
and exiting one program to start another. When the telephone rings, for&lt;br /&gt;
example, you can switch from the word processor application you are using&lt;br /&gt;
to write a memo and go to the application that is managing your appointment&lt;br /&gt;
calendar or to the spreadsheet application that contains the figures that&lt;br /&gt;
are necessary to answer your caller's query.&lt;br /&gt;
     This type of multitasking is similar to what people do when they're&lt;br /&gt;
not working with a computer. You may leave a report half read on your desk&lt;br /&gt;
to address a more pressing need, such as answering the phone. Later,&lt;br /&gt;
perhaps after other tasks intervene, you return to the report. You don't&lt;br /&gt;
terminate a project and return your reference materials to the bookshelf,&lt;br /&gt;
the files, and the library to answer the telephone; you merely switch your&lt;br /&gt;
attention for a while and later pick up where you left off.&lt;br /&gt;
     This kind of multitasking is called serial multitasking because&lt;br /&gt;
actions are performed one at a time. Although you probably haven't thought&lt;br /&gt;
of it this way, you've spent much of your life serially multitasking. Every&lt;br /&gt;
day when you leave for work, you suspend your home life and resume your&lt;br /&gt;
work life. That evening, you reverse the process. You serially multitask a&lt;br /&gt;
hobby--each time picking it up where you left off last time and then&lt;br /&gt;
leaving off again. Reading the comics in a daily newspaper is a prodigious&lt;br /&gt;
feat of human serial multitasking--you switch from one to another of&lt;br /&gt;
perhaps 20 strips, remembering for each what has gone on before and then&lt;br /&gt;
waiting until tomorrow for the next installment. Although serial&lt;br /&gt;
multitasking is very useful, it is not nearly as useful as full&lt;br /&gt;
multitasking--the kind of multitasking built into OS/2.&lt;br /&gt;
     Full multitasking on the computer involves doing more than one thing--&lt;br /&gt;
running more than one application--at the same time. Humans do a little of&lt;br /&gt;
this, but not too much. People commonly talk while they drive cars, eat&lt;br /&gt;
while watching television, and walk while chewing gum. None of these&lt;br /&gt;
activities requires one's full concentration though. Humans generally can't&lt;br /&gt;
fully multitask activities that require a significant amount of&lt;br /&gt;
concentration because they have only one brain.&lt;br /&gt;
     For that matter, a personal computer has only one &amp;quot;brain&amp;quot;--one CPU.&lt;br /&gt;
1. Although multiple-CPU computers are well known, personal computers&lt;br /&gt;
with multiple CPUs are uncommon. In any case, this discussion applies,&lt;br /&gt;
with the obvious extensions, to multiple-CPU systems.&lt;br /&gt;
1&lt;br /&gt;
But OS/2 can switch this CPU from one activity to another very rapidly--&lt;br /&gt;
dozens or even hundreds of times a second. All executing programs seem to&lt;br /&gt;
be running at the same time, at least on the human scale of time. For&lt;br /&gt;
example, if five programs are running and each in turn gets 0.01 second of&lt;br /&gt;
CPU time (that is, 10 milliseconds), in 1 second each program receives 20&lt;br /&gt;
time slices. To most observers, human or other computer software, all five&lt;br /&gt;
programs appear to be running simultaneously but each at one-fifth its&lt;br /&gt;
maximum speed. We'll return to the topic of time slicing later; for now,&lt;br /&gt;
it's easiest--and, as we shall see, best--to pretend that all executing&lt;br /&gt;
programs run simultaneously.&lt;br /&gt;
     The full multitasking capabilities of OS/2 allow the personal computer&lt;br /&gt;
to act as more than a mere engine to run applications; the personal&lt;br /&gt;
computer can now be a system of services. The user can interact with a&lt;br /&gt;
spreadsheet program, for example, while a mail application is receiving&lt;br /&gt;
network messages that the user can read later. At the same time, other&lt;br /&gt;
programs may be downloading data from a mainframe computer or spooling&lt;br /&gt;
output to a printer or a plotter. The user may have explicitly initiated&lt;br /&gt;
some of these activities; a program may have initiated others. Regardless,&lt;br /&gt;
they all execute simultaneously, and they all do their work without&lt;br /&gt;
requiring the user's attention or intervention.&lt;br /&gt;
     Full multitasking is useful to programs themselves. Earlier, we&lt;br /&gt;
discussed the advantages of a tools approach--writing programs so that&lt;br /&gt;
they can offer their services to other programs. The numerous advantages&lt;br /&gt;
of this technique are possible only because of full&lt;br /&gt;
multitasking. For&lt;br /&gt;
example, if a program is to be able to invoke another program to sort a &lt;br /&gt;
data file, the sort program must execute at the same time as its client &lt;br /&gt;
program. It wouldn't be very useful if the client program had to &lt;br /&gt;
terminate in order  for the sort program to run.&lt;br /&gt;
     Finally, full multitasking is useful within a program itself. A thread&lt;br /&gt;
is an OS/2 mechanism that allows more than one path of execution through a&lt;br /&gt;
particular application. (Threads are discussed in detail later; for now it&lt;br /&gt;
will suffice to imagine that several CPUs can be made to execute the same&lt;br /&gt;
program simultaneously.) This allows individual applications to perform&lt;br /&gt;
more than one task at a time. For example, if the user tells a spreadsheet&lt;br /&gt;
program to recalculate a large budget analysis, the program can use one&lt;br /&gt;
thread to do the calculating and another to prompt for, read, and obey the&lt;br /&gt;
user's next command. In effect, multiple operations overlap during&lt;br /&gt;
execution and thereby increase the program's responsiveness to the user.&lt;br /&gt;
     OS/2 uses a time-sliced, priority-based preemptive scheduler to&lt;br /&gt;
provide full multitasking. In other words, the OS/2 scheduler preempts--&lt;br /&gt;
takes away--the CPU from one application at any time the scheduler desires&lt;br /&gt;
and assigns the CPU another application. Programs don't surrender the CPU&lt;br /&gt;
when they feel like it; OS/2 preempts it. Each program in the system (more&lt;br /&gt;
precisely, each thread in the system) has its own priority. When a thread&lt;br /&gt;
of a higher priority than the one currently running wants to run, the&lt;br /&gt;
scheduler preempts the running thread in favor of the higher priority one.&lt;br /&gt;
If two or more runnable threads have the same highest priority, OS/2 runs&lt;br /&gt;
each in turn for a fraction of a second--a time slice.&lt;br /&gt;
     The OS/2 scheduler does not periodically look around to see if the&lt;br /&gt;
highest priority thread is running. Such an approach wastes CPU time and&lt;br /&gt;
slows response time because a higher priority thread must wait to run until&lt;br /&gt;
the next scheduler scan. Instead, other parts of the system call the&lt;br /&gt;
scheduler when they think that a thread other than the one running should&lt;br /&gt;
be executed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.1  Subtask Model&lt;br /&gt;
&lt;br /&gt;
The terms task and process are used interchangeably to describe the direct&lt;br /&gt;
result of executing a binary (.EXE) file. A process is the unit of&lt;br /&gt;
ownership under OS/2, and processes own resources such as memory, open&lt;br /&gt;
files, connections to dynlink libraries, and semaphores. Casual users would&lt;br /&gt;
call a process a &amp;quot;program&amp;quot;; and, in fact, under MS-DOS all programs and&lt;br /&gt;
applications consist of a single process. OS/2 uses the terms task or&lt;br /&gt;
process because a single application program under OS/2 may consist of more&lt;br /&gt;
than one process. This section describes how this is done.&lt;br /&gt;
     First, some more terminology. When a process creates, or execs,&lt;br /&gt;
another process, the creator process is called the parent process, and the&lt;br /&gt;
created process is called the child process. The parent of the parent is&lt;br /&gt;
the child's grandparent and so on. As with people, each process in the&lt;br /&gt;
system has or had a parent.&lt;br /&gt;
2. Obviously, during boot-up OS/2 creates an initial parentless&lt;br /&gt;
process by &amp;quot;magic,&amp;quot; but this is ancient history by the time any&lt;br /&gt;
application may run, so the anomaly may be safely ignored.&lt;br /&gt;
2 Although we use genealogical terms to describe&lt;br /&gt;
task relationships, a child task, or process, is more like an agent or&lt;br /&gt;
employee of the parent task. Employees are hired to do work for an&lt;br /&gt;
employer. The employer provides a workplace and access to the information&lt;br /&gt;
employees need to do their jobs. The same is generally true for a child&lt;br /&gt;
task. When a child task is created, it inherits (or receives a copy of) a&lt;br /&gt;
great deal of the parent task's environment. For example, it inherits, or&lt;br /&gt;
takes on, the parent's base scheduling priority and its screen group. The&lt;br /&gt;
term inherit is a little inappropriate because the parent task has not&lt;br /&gt;
died. It is alive and well, going about its business.&lt;br /&gt;
     The most important items a child task inherits are its parent's open&lt;br /&gt;
file handles. OS/2 uses a handle mechanism to perform file I/O, as do MS-&lt;br /&gt;
DOS versions 2.0 and later. When a file is opened, OS/2 returns a handle--&lt;br /&gt;
an integer value--to the process. When a program wants to read from or&lt;br /&gt;
write to a file, it gives OS/2 the file handle. Handles are not identical&lt;br /&gt;
among processes. For example, the file referred to by handle 6 of one&lt;br /&gt;
process bears no relationship to the file referred to by another process's&lt;br /&gt;
handle 6, unless one of those processes is a child of the other. When a&lt;br /&gt;
parent process creates a child process, the child process, by default,&lt;br /&gt;
inherits each of the parent's open file handles. For example, a parent&lt;br /&gt;
process has the file \WORK\TEMPFILE open on handle 5; when the child&lt;br /&gt;
process starts up, handle 5 is open and references the \WORK\TEMPFILE file.&lt;br /&gt;
     This undoubtedly seems brain damaged if you are unfamiliar with this&lt;br /&gt;
model. Why is it done in this crazy way? What use does the child process&lt;br /&gt;
have for these open files? What's to keep the child from mucking up the&lt;br /&gt;
parent's files? All this becomes clearer when the other piece of the puzzle&lt;br /&gt;
is in place--the standard file handles.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.1.1  Standard File Handles&lt;br /&gt;
Many OS/2 functions use 16-bit integer values called handles for their&lt;br /&gt;
interfaces. A handle is an object that programmers call a &amp;quot;magic cookie&amp;quot;--&lt;br /&gt;
an arbitrary value that OS/2 provides the application so that the&lt;br /&gt;
application can pass the value back to OS/2 on subsequent calls. Its&lt;br /&gt;
purpose is to simplify the OS/2 interface and speed up the particular&lt;br /&gt;
service. For example, when a program creates a system semaphore, it is&lt;br /&gt;
returned a semaphore handle--a magic cookie--that it uses for subsequent&lt;br /&gt;
request and release operations. Referring to the semaphore via a 16-bit&lt;br /&gt;
value is much faster than passing around a long filename. Furthermore, the&lt;br /&gt;
magic in magic cookie is that the meaning of the 16-bit handle value is&lt;br /&gt;
indecipherable to the application. OS/2 created the value, and it has&lt;br /&gt;
meaning only to OS/2; the application need only retain the value and&lt;br /&gt;
regurgitate it when appropriate. An application can never make any&lt;br /&gt;
assumptions about the values of a magic cookie.&lt;br /&gt;
     File handles are an exceptional form of handle because they are not&lt;br /&gt;
magic cookies. The handle value, in the right circumstances, is meaningful&lt;br /&gt;
to the application and to the system as a whole. Specifically, three handle&lt;br /&gt;
values have special meaning: handle value 0, called STDIN (for standard&lt;br /&gt;
input); handle value 1, called STDOUT (standard output); and handle value&lt;br /&gt;
2, called STDERR (standard error). A simple program--let's call it NUMADD--&lt;br /&gt;
will help to explain the use of these three handles. NUMADD will read two&lt;br /&gt;
lines of ASCII text (each containing a decimal number), convert the numbers&lt;br /&gt;
to binary, add them, and then convert the results to an ASCII string and&lt;br /&gt;
write out the result. Note that we're confining our attention to a simple&lt;br /&gt;
non-screen-oriented program that might be used as a tool, either directly&lt;br /&gt;
by a programmer or by another program (see Figure 4-1 and Listing 4-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      STDIN ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
123 ÄÄÄ¿    ³          ³ STDOUT&lt;br /&gt;
14     ÀÄÄÄ�³  NUMADD  ÃÄÄÄ¿&lt;br /&gt;
            ³          ³   ÀÄÄÄÄ� 137&lt;br /&gt;
            ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-1.  Program NUMADD operation--interactive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;            /* defines stdin and stdout */&lt;br /&gt;
&lt;br /&gt;
main()&lt;br /&gt;
{&lt;br /&gt;
     int value1, value2, sum;&lt;br /&gt;
&lt;br /&gt;
     fscanf (stdin, &amp;quot;%d&amp;quot;, &amp;amp;value1);&lt;br /&gt;
     fscanf (stdin, &amp;quot;%d&amp;quot;, &amp;amp; value2);&lt;br /&gt;
     sum = value1 + value2;&lt;br /&gt;
     fprintf (stdout, &amp;quot;%d\n&amp;quot;, sum);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-1.  Program NUMADD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     By convention, all OS/2 programs read input from STDIN and write&lt;br /&gt;
output to STDOUT. Any error messages are written to STDERR. The program&lt;br /&gt;
itself does not open these handles; it inherits them from the parent&lt;br /&gt;
process. The parent may have opened them itself or inherited them from its&lt;br /&gt;
own parent. As you can see, NUMADD would not contain DosOpen calls;&lt;br /&gt;
instead, it would start immediately issuing fscanf calls on handle 0&lt;br /&gt;
(STDIN), which in turn issues DosRead calls, and, when ready, directly&lt;br /&gt;
issue fprintf calls to handle 1 (STDOUT), which in turn issues DosWrites.&lt;br /&gt;
     Figure 4-2 and Listing 4-2 show a hypothetical application, NUMARITH.&lt;br /&gt;
NUMARITH reads three text lines. The first line contains an operation&lt;br /&gt;
character, such as a plus (+) or a minus (-); the second and third lines&lt;br /&gt;
contain the values to be operated upon. The author of this program doesn't&lt;br /&gt;
want to reinvent the wheel; so when the program NUMARITH encounters a +&lt;br /&gt;
operation, it executes NUMADD to do the work. As shown, the parent process&lt;br /&gt;
NUMARITH has its STDIN connected to the keyboard and its STDOUT connected&lt;br /&gt;
to the screen device drivers.&lt;br /&gt;
3. CMD.EXE inherited these handles from its own parent. This process&lt;br /&gt;
is discussed later.&lt;br /&gt;
3 When NUMADD executes, it reads input from&lt;br /&gt;
the keyboard via STDIN. After the user types the two numbers, NUMADD&lt;br /&gt;
displays the result on the screen via STDOUT. NUMARITH has invoked NUMADD&lt;br /&gt;
to do some work for it, and NUMADD has silently and seamlessly acted as a&lt;br /&gt;
part of NUMARITH. The employee metaphor fits well here. NUMADD acted as an&lt;br /&gt;
employee of NUMARITH, making use of NUMARITH's I/O streams, and as a result&lt;br /&gt;
the contribution of the NUMADD employee to the NUMARITH company is seamless.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
keyboard                                   screen&lt;br /&gt;
    ³     STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT     ³&lt;br /&gt;
    ÀÄÄÄÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
            ÃÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄ´&lt;br /&gt;
            �      ³          ³     �&lt;br /&gt;
            ³      ÀÄÄÄÄÄÂÄÄÄÄÙ     ³&lt;br /&gt;
            ³            ³ DosExec  ³&lt;br /&gt;
   inherits ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿     ³ inherits&lt;br /&gt;
            ³      ³          ³     ³&lt;br /&gt;
            ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄ�ÄÄÙ&lt;br /&gt;
                   ³          ³&lt;br /&gt;
                   ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-2.  Program NUMARITH operation--interactive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**     Numarith - Perform ASCII Arithmetic&lt;br /&gt;
*&lt;br /&gt;
*       Numarith reads line triplets:&lt;br /&gt;
*&lt;br /&gt;
*               operation&lt;br /&gt;
*               value1&lt;br /&gt;
*               value2&lt;br /&gt;
*&lt;br /&gt;
*       performs the specified operation (+, -, *, /) on&lt;br /&gt;
*       the two values and prints the result on stdout.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
main()&lt;br /&gt;
{&lt;br /&gt;
        char operation;&lt;br /&gt;
&lt;br /&gt;
        fscanf (stdin, &amp;quot;%c&amp;quot;, &amp;amp;operation);&lt;br /&gt;
&lt;br /&gt;
        switch (operation) {&lt;br /&gt;
&lt;br /&gt;
            case '+':  execl (&amp;quot;numadd&amp;quot;,  0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '-':  execl (&amp;quot;numsub&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '*':  execl (&amp;quot;nummul&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '/':  execl (&amp;quot;numdiv&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
            }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-2.  Program NUMARITH.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Figure 4-3 shows a similar situation. In this case, however,&lt;br /&gt;
NUMARITH's STDIN and STDOUT handles are open on two files, which we'll call&lt;br /&gt;
DATAIN and DATAOUT. Once again, NUMADD does its work seamlessly. The input&lt;br /&gt;
numbers are read from the command file on STDIN, and the output is properly&lt;br /&gt;
intermingled in the log file on STDOUT. The key here is that this NUMADD is&lt;br /&gt;
exactly the same program that ran in Listing 4-2; NUMADD contains no&lt;br /&gt;
special code to deal with this changed situation. In both examples, NUMADD&lt;br /&gt;
simply reads from STDIN and writes to STDOUT; NUMADD neither knows nor&lt;br /&gt;
cares where those handles point. Exactly the same is true for the parent.&lt;br /&gt;
NUMARITH doesn't know and doesn't care that it's working from files instead&lt;br /&gt;
of from the screen; it simply uses the STDIN and STDOUT handles that it&lt;br /&gt;
inherited from its parent.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
File data in                                     File data out&lt;br /&gt;
     ___                                              ___&lt;br /&gt;
   /     \                                          /     \&lt;br /&gt;
  ³\ ___ /³                                        ³\ ___ /³&lt;br /&gt;
  ³       ³                                        ³       ³&lt;br /&gt;
  ³       ÃÄÄ¿                                  ÚÄ�³       ³&lt;br /&gt;
  ³       ³  ³  STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT  ³  ³       ³&lt;br /&gt;
   \ ___ /   ÀÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÙ   \ ___ /&lt;br /&gt;
                  ÃÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄ´&lt;br /&gt;
                  �      ³          ³     �&lt;br /&gt;
                  ³      ÀÄÄÄÄÄÂÄÄÄÄÙ     ³&lt;br /&gt;
                  ³            ³ DosExec  ³&lt;br /&gt;
         inherits ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿     ³ inherits&lt;br /&gt;
                  ³      ³          ³     ³&lt;br /&gt;
                  ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄ�ÄÄÙ&lt;br /&gt;
                         ³          ³&lt;br /&gt;
                         ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-3.  Program NUMARITH operation--from files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This is the single most important concept in the relationship and&lt;br /&gt;
inheritance structure between processes. The reason a process inherits so&lt;br /&gt;
much from its parent is so that the parent can set up the tool's&lt;br /&gt;
environment--make it read from the parent's STDIN or from a file or from an&lt;br /&gt;
anonymous pipe (see 4.1.2 Anonymous Pipes). This gives the parent the&lt;br /&gt;
flexibility to use a tool program as it wishes, and it frees the tool's&lt;br /&gt;
author from the need to be &amp;quot;all things to all people.&amp;quot;&lt;br /&gt;
     Of equal importance, the inheritance architecture provides&lt;br /&gt;
nesting encapsulation of child processes. NUMARITH's parent process doesn't&lt;br /&gt;
know and doesn't need to know how NUMARITH does its job. NUMARITH can do&lt;br /&gt;
the additions itself, or it can invoke NUMADD as a child, but the&lt;br /&gt;
architecture encapsulates the details of NUMARITH's operation so that&lt;br /&gt;
NUMADD's involvement is hidden from NUMARITH's parent. Likewise, the&lt;br /&gt;
decision of NUMARITH's parent to work from a file or from a device or from&lt;br /&gt;
a pipe is encapsulated (that is, hidden) from NUMARITH and from any child&lt;br /&gt;
processes that NUMARITH may execute to help with its work. Obviously, this&lt;br /&gt;
architecture can be extended arbitrarily: NUMADD can itself execute a child&lt;br /&gt;
process to help NUMADD with its work, and this would silently and invisibly&lt;br /&gt;
work. Neither NUMARITH nor its parent would know or need to know anything&lt;br /&gt;
about how NUMADD was doing its work. Other versions can replace any of&lt;br /&gt;
these applications at any time. The new versions can invoke more or fewer&lt;br /&gt;
child processes or be changed in any other way, and their client (that is,&lt;br /&gt;
parent) processes are unaffected. The architecture of OS/2 is tool-based;&lt;br /&gt;
as long as the function of a tool remains constant (or is supersetted), its&lt;br /&gt;
implementation is irrelevant and can be changed arbitrarily.&lt;br /&gt;
     The STDIN, STDOUT, and STDERR architecture applies to all programs,&lt;br /&gt;
even those that only use VIO, KBD, or the presentation manager and that&lt;br /&gt;
never issue operations on these handles. See Chapter 14, Interactive&lt;br /&gt;
Programs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.1.2  Anonymous Pipes&lt;br /&gt;
NUMADD and NUMARITH are pretty silly little programs; a moment's&lt;br /&gt;
consideration will show how the inheritance architecture applies to more&lt;br /&gt;
realistic programs. An example is the TREE program that runs when the TREE&lt;br /&gt;
command is given to CMD.EXE. TREE inherits the STDIN and STDOUT handles,&lt;br /&gt;
but it does not use STDIN; it merely writes output to STDOUT. As a result,&lt;br /&gt;
when the user types TREE at a CMD.EXE prompt, the output appears on the&lt;br /&gt;
screen. When TREE appears in a batch file, the output appears in the LOG&lt;br /&gt;
file or on the screen, depending on where STDOUT is pointing.&lt;br /&gt;
     This is all very useful, but what if an application wants to further&lt;br /&gt;
process the output of the child program rather than having the child's&lt;br /&gt;
output intermingled with the application's output? OS/2 does this with&lt;br /&gt;
anonymous pipes. The adjective anonymous distinguishes these pipes from a&lt;br /&gt;
related facility, named pipes, which are not implemented in OS/2 version&lt;br /&gt;
1.0.&lt;br /&gt;
     An anonymous pipe is a data storage buffer that OS/2 maintains. When a&lt;br /&gt;
process opens an anonymous pipe, it receives two file handles--one for&lt;br /&gt;
writing and one for reading. Data can be written to the write handle via&lt;br /&gt;
the DosWrite call and then read back via the read handle and the DosRead&lt;br /&gt;
call. An anonymous pipe is similar to a file in that it is written and read&lt;br /&gt;
via file handle operations, but an anonymous pipe and a file are&lt;br /&gt;
significantly different. Pipe data is stored only in RAM buffers, not on a&lt;br /&gt;
disk, and is accessed only in FIFO (First In First Out) fashion. The&lt;br /&gt;
DosChgFilePtr operation is illegal on pipe handles.&lt;br /&gt;
     An anonymous pipe is of little value to a single process, since it&lt;br /&gt;
acts as a simple FIFO (First In First Out) storage buffer of limited size&lt;br /&gt;
and since the data has to be copied to and from OS/2's pipe buffers when&lt;br /&gt;
DosWrites and DosReads are done. What makes an anonymous pipe valuable is&lt;br /&gt;
that child processes inherit file handles. A parent process can create an&lt;br /&gt;
anonymous pipe and then create a child process, and the child process&lt;br /&gt;
inherits the anonymous pipe handles. The child process can then write to&lt;br /&gt;
the pipe's write handle, and the parent process can read the data via the&lt;br /&gt;
pipe's read handle. Once we add the DosDupHandle function, which allows&lt;br /&gt;
handles to be renumbered, and the standard file handles (STDIN, STDOUT, and&lt;br /&gt;
STDERR), we have the makings of a powerful capability.&lt;br /&gt;
     Let's go back to our NUMARITH and NUMADD programs. Suppose NUMARITH&lt;br /&gt;
wants to use NUMADD's services but that NUMARITH wants to process NUMADD's&lt;br /&gt;
results itself rather than having them appear in NUMARITH's output.&lt;br /&gt;
Furthermore, assume that NUMARITH doesn't want NUMADD to read its arguments&lt;br /&gt;
from NUMARITH's input file; NUMARITH wants to supply NUMADD's arguments&lt;br /&gt;
itself. NUMARITH can do this by following these steps:&lt;br /&gt;
&lt;br /&gt;
     1.  Create two anonymous pipes.&lt;br /&gt;
&lt;br /&gt;
     2.  Preserve the item pointed to by the current STDIN and STDOUT&lt;br /&gt;
         handles (the item can be a file, a device, or a pipe) by using&lt;br /&gt;
         DosDupHandle to provide a duplicate handle. The handle numbers of&lt;br /&gt;
         the duplicates may be any number as long as it is not the number&lt;br /&gt;
         of STDIN, STDOUT, or STDERR. We know that this is the case because&lt;br /&gt;
         DosDupHandle assigns a handle number that is not in use, and the&lt;br /&gt;
         standard handle numbers are always in use.&lt;br /&gt;
&lt;br /&gt;
     3.  Close STDIN and STDOUT via DosClose. Whatever object is &amp;quot;on the&lt;br /&gt;
         other end&amp;quot; of the handle is undisturbed because the application&lt;br /&gt;
         still has the object open on another handle.&lt;br /&gt;
&lt;br /&gt;
     4.  Use DosDupHandle to make the STDIN handle a duplicate of one of&lt;br /&gt;
         the pipe's input handles, and use DosDupHandle to make the STDOUT&lt;br /&gt;
         handle a duplicate of the other pipe's output handle.&lt;br /&gt;
&lt;br /&gt;
     5.  Create the child process via DosExecPgm.&lt;br /&gt;
&lt;br /&gt;
     6.  Close the STDIN and STDOUT handles that point to the pipes, and&lt;br /&gt;
         use DosDupHandle and DosClose to effectively rename the objects&lt;br /&gt;
         originally described by STDIN and STDOUT back to those handles.&lt;br /&gt;
&lt;br /&gt;
     The result of this operation is shown in Figure 4-4. NUMADD's STDIN&lt;br /&gt;
and STDOUT handles are pointing to two anonymous pipes, and the parent&lt;br /&gt;
process is holding the other end of those pipes. The parent process used&lt;br /&gt;
DosDupHandle and DosClose to effectively &amp;quot;rename&amp;quot; the STDIN and STDOUT&lt;br /&gt;
handles temporarily so that the child process can inherit the pipe handles&lt;br /&gt;
rather than its parent's STDIN and STDOUT. At this point the parent,&lt;br /&gt;
NUMARITH, can write input values into the pipe connected to NUMADD's STDIN&lt;br /&gt;
and read NUMADD's output from the pipe connected to NUMADD's STDOUT.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT&lt;br /&gt;
 ÄÄÄÄÄÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
          ÀÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄÙ&lt;br /&gt;
          ÚÄÄÄÄÄÄ´          ³�ÄÄÄÄÄÄÄ¿&lt;br /&gt;
          ³      ÀÄÄÄÄÄÂÄÄÄÄÙ        ³&lt;br /&gt;
anonymous ³            ³ DosExecPgm  ³ anonymous&lt;br /&gt;
  pipe    ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿        ³   pipe&lt;br /&gt;
          ³      ³          ³        ³&lt;br /&gt;
          ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                 ³          ³&lt;br /&gt;
                 ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-4.  Invoking NUMADD via an anonymous pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     If you compare Figure 4-4 with Listing 4-2, Figure 4-2, and Figure&lt;br /&gt;
4-3, you see another key feature of this architecture: NUMADD has no&lt;br /&gt;
special code or design to allow it to communicate directly with NUMARITH.&lt;br /&gt;
NUMADD functions correctly whether working from the keyboard and screen,&lt;br /&gt;
from data files, or as a tool for another program. The architecture is&lt;br /&gt;
fully recursive: If NUMADD invokes a child process to help it with its&lt;br /&gt;
work, everything still functions correctly. Whatever mechanism NUMADD uses&lt;br /&gt;
to interact with its child/tool program is invisible to NUMARITH. Likewise,&lt;br /&gt;
if another program uses NUMARITH as a tool, that program is not affected by&lt;br /&gt;
whatever mechanism NUMARITH uses to do its work.&lt;br /&gt;
     This example contains one more important point. Earlier I said that a&lt;br /&gt;
process uses DosRead and DosWrite to do I/O over a pipe, yet our NUMADD&lt;br /&gt;
program uses fscanf and fprintf, two C language library routines. fscanf&lt;br /&gt;
and fprintf themselves call DosRead and DosWrite, and because a pipe handle&lt;br /&gt;
is indistinguishable from a file handle or a device handle for these&lt;br /&gt;
operations, not only does NUMADD work unchanged with pipes, but the library&lt;br /&gt;
subroutines that it calls work as well. This is another example of the&lt;br /&gt;
principle of encapsulation as expressed in OS/2,&lt;br /&gt;
4. And in UNIX, from which this aspect of the architecture was&lt;br /&gt;
adapted.&lt;br /&gt;
4 in which the differences&lt;br /&gt;
among pipes, files, and devices are hidden behind, or encapsulated in, a&lt;br /&gt;
standardized handle interface.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.1.3  Details, Details&lt;br /&gt;
While presenting the &amp;quot;big picture&amp;quot; of the OS/2 tasking and tool&lt;br /&gt;
architecture, I omitted various important details. This section discusses&lt;br /&gt;
them, in no particular order.&lt;br /&gt;
     STDERR (handle value 2) is an output handle on which error messages&lt;br /&gt;
are written. STDERR is necessary because STDOUT is a program's normal&lt;br /&gt;
output stream. For example, suppose a user types:&lt;br /&gt;
&lt;br /&gt;
     DIR  filename &amp;gt;logfile&lt;br /&gt;
&lt;br /&gt;
where the file filename does not exist. If STDERR did not exist as a&lt;br /&gt;
separate entity, no error message would appear, and logfile would&lt;br /&gt;
apparently be created. Later, when the user attempted to examine the&lt;br /&gt;
contents of the file, he or she would see the following message:&lt;br /&gt;
&lt;br /&gt;
     FILE NOT FOUND&lt;br /&gt;
&lt;br /&gt;
For this reason, STDERR generally points to the display screen, and&lt;br /&gt;
applications rarely redirect it.&lt;br /&gt;
     The special meanings of STDIN, STDOUT, and STDERR are not hard coded&lt;br /&gt;
into the OS/2 kernel; they are system conventions. All programs should&lt;br /&gt;
follow these conventions at all times to preserve the flexibility and&lt;br /&gt;
utility of OS/2's tool-based architecture. Even programs that don't do&lt;br /&gt;
handle I/O on the STD handles must still follow the architectural&lt;br /&gt;
conventions (see Chapter 14, Interactive Programs). However, the OS/2&lt;br /&gt;
kernel code takes no special action nor does it contain any special cases&lt;br /&gt;
in support of this convention. Various OS/2 system utilities, such as&lt;br /&gt;
CMD.EXE and the presentation manager, do contain code in support of this&lt;br /&gt;
convention.&lt;br /&gt;
     I mentioned that a child process inherits all its parent's file&lt;br /&gt;
handles unless the parent has explicitly marked a handle &amp;quot;no inherit.&amp;quot; The&lt;br /&gt;
use of STDIN, STDOUT, and STDERR in an inherited environment has been&lt;br /&gt;
discussed, but what of the other handles?&lt;br /&gt;
     Although a child process inherits all of its parent's file handles, it&lt;br /&gt;
is usually interested only in STDIN, STDOUT, and STDERR. What happens to&lt;br /&gt;
the other handles? Generally, nothing. Only handle values 0 (STDIN), 1&lt;br /&gt;
(STDOUT), and 2 (STDERR) have explicit meaning. All other file handles are&lt;br /&gt;
merely magic cookies that OS/2 returns for use in subsequent I/O calls.&lt;br /&gt;
OS/2 doesn't guarantee any particular range or sequence of handle values,&lt;br /&gt;
and applications should never use or rely on explicit handle values other&lt;br /&gt;
than the STD ones.&lt;br /&gt;
     Thus, for example, if a parent process has a file open on handle N and&lt;br /&gt;
the child process inherits that handle, little happens. The child process&lt;br /&gt;
won't get the value N back as a result of a DosOpen because the handle is&lt;br /&gt;
already in use. The child will never issue operations to handle N because&lt;br /&gt;
it didn't open any such handle and knows nothing of its existence. Two side&lt;br /&gt;
effects can result from inheriting &amp;quot;garbage&amp;quot; handles. One is that the&lt;br /&gt;
object to which the handle points cannot be closed until both the parent&lt;br /&gt;
and the child close their handles. Because the child knows nothing of the&lt;br /&gt;
handle, it won't close it. Therefore, a handle close issued by a parent&lt;br /&gt;
won't be effective until the child and all of that child's descendant&lt;br /&gt;
processes (which in turn inherited the handle) have terminated. If another&lt;br /&gt;
application needs the file or device, it is unavailable because a child&lt;br /&gt;
process is unwittingly holding it open.&lt;br /&gt;
     The other side effect is that each garbage handle consumes an entry in&lt;br /&gt;
the child's handle space. Although you can easily increase the default&lt;br /&gt;
maximum of 20 open handles, a child process that intends to open only 10&lt;br /&gt;
files wouldn't request such an increase. If a parent process allows the&lt;br /&gt;
child to inherit 12 open files, the child will run out of available open&lt;br /&gt;
file handles. Writing programs that always raise their file handle limit is&lt;br /&gt;
not good practice because the garbage handles are extra overhead and the&lt;br /&gt;
files-held-open problem remains. Instead, parent programs should minimize&lt;br /&gt;
the number of garbage handles they allow child processes to inherit.&lt;br /&gt;
     Each time a program opens a file, it should do so with the DosOpen&lt;br /&gt;
request with the &amp;quot;don't inherit&amp;quot; bit set if the file is of no specific&lt;br /&gt;
interest to any child programs. If the bit is not set at open time, it can&lt;br /&gt;
be set later via DosSetFHandState. The bit is per-handle, not per-file; so&lt;br /&gt;
if a process has two handles open on the same file, it can allow one but&lt;br /&gt;
not the other to be inherited. Don't omit this step simply because you&lt;br /&gt;
don't plan to run any child processes; unbeknownst to you, dynlink library&lt;br /&gt;
routines may run child programs on your behalf. Likewise, in dynlink&lt;br /&gt;
programs the &amp;quot;no inherit&amp;quot; bit should be set when file opens are issued&lt;br /&gt;
because the client program may create child processes.&lt;br /&gt;
     Finally, do not follow the standard UNIX practice of blindly closing&lt;br /&gt;
file handles 3 through 20 during program initialization. Dynlink subsystems&lt;br /&gt;
are called to initialize themselves for a new client before control is&lt;br /&gt;
passed to the application itself. If subsystems have opened files during&lt;br /&gt;
that initialization, a blanket close operation will close those files and&lt;br /&gt;
cause the dynlink package to fail. All programs use dynlink subsystems,&lt;br /&gt;
whether they realize it or not, because both the OS/2 interface package and&lt;br /&gt;
the presentation manager are such subsystems. Accidentally closing a&lt;br /&gt;
subsystem's file handles can cause bizarre and inexplicable problems. For&lt;br /&gt;
example, when the VIO subsystem is initialized, it opens a handle to the&lt;br /&gt;
screen device. A program that doesn't call VIO may believe that closing&lt;br /&gt;
this handle is safe, but it's not. If a handle write is done to STDOUT when&lt;br /&gt;
STDOUT points to the screen device, OS/2 calls VIO on behalf of the&lt;br /&gt;
application--with potentially disastrous effect.&lt;br /&gt;
     I discussed how a child process, inheriting its parent's STDIN and&lt;br /&gt;
STDOUT, extracts its input from the parent's input stream and intermingles&lt;br /&gt;
its output in the parent's output stream. What keeps the parent process&lt;br /&gt;
from rereading the input consumed by the child, and what keeps the parent&lt;br /&gt;
from overwriting the output data written by the child? The answer is in the&lt;br /&gt;
distinction between duplicated or inherited handles to a file and two&lt;br /&gt;
handles to the same file that are the result of two separate opens.&lt;br /&gt;
     Each time a file is opened, OS/2 allocates a handle to that process&lt;br /&gt;
and makes an entry in the process's handle table. This entry then points to&lt;br /&gt;
the System File Table (SFT) inside OS/2. The SFT contains the seek pointer&lt;br /&gt;
to the file--the spot in the file that is currently being read from or&lt;br /&gt;
written to. When a handle is inherited or duplicated, the new handle points&lt;br /&gt;
to the same SFT entry as the original. Thus, for example, the child's STDIN&lt;br /&gt;
handle shares the same seek pointer as the parent's STDIN handle. When our&lt;br /&gt;
example child program NUMADD read two lines from STDIN, it advanced the&lt;br /&gt;
seek pointer of its own STDIN and that of its parent's STDIN (and perhaps&lt;br /&gt;
that of its grandparent's STDIN and so forth). Likewise, when the child&lt;br /&gt;
writes to STDOUT, the seek pointer advances on STDOUT so that subsequent&lt;br /&gt;
writing by the parent appends to the child's output rather than overwriting&lt;br /&gt;
it.&lt;br /&gt;
     This mechanism has two important ramifications. First, in a situation&lt;br /&gt;
such as our NUMARITH and NUMADD example, the parent process must refrain&lt;br /&gt;
from I/O to the STD handles until the child process has completed so that&lt;br /&gt;
the input or output data doesn't intermingle. Second, the processes must be&lt;br /&gt;
careful in the way they buffer data to and from the STD handles.&lt;br /&gt;
     Most programs that read data from STDIN do so until they encounter an&lt;br /&gt;
EOF (End Of File). These programs can buffer STDIN as they wish. A program&lt;br /&gt;
such as NUMARITH, in which child processes read some but not all of its&lt;br /&gt;
STDIN data, cannot use buffering because the read-ahead data in the buffer&lt;br /&gt;
might be the data that the child process was to read. NUMARITH can't &amp;quot;put&lt;br /&gt;
the data back&amp;quot; by backing up the STDIN seek pointer because STDIN might be&lt;br /&gt;
pointing to a device (such as the keyboard) or to a pipe that cannot be&lt;br /&gt;
seeked. Likewise, because NUMADD was designed to read only two lines&lt;br /&gt;
of input, it also must read STDIN a character at a time to be sure it&lt;br /&gt;
doesn't &amp;quot;overshoot&amp;quot; its two lines.&lt;br /&gt;
     Programs must also be careful about buffering STDOUT. In general, they&lt;br /&gt;
can buffer STDOUT as they wish, but they must be sure to flush out any&lt;br /&gt;
buffered data before they execute any child processes that might write to&lt;br /&gt;
STDOUT.&lt;br /&gt;
     Finally, what if a parent process doesn't want a child process to&lt;br /&gt;
inherit STDIN, STDOUT, or STDERR? The parent process should not mark those&lt;br /&gt;
handles &amp;quot;no inherit&amp;quot; because then those handles will not be open when the&lt;br /&gt;
child process starts. The OS/2 kernel has no built-in recognition of the&lt;br /&gt;
STD file handles; so if the child process does a DosOpen and handle 1 is&lt;br /&gt;
unopened (because the process's parent set &amp;quot;no inherit&amp;quot; on handle 1), OS/2&lt;br /&gt;
might open the new file on handle 1. As a result, output that the child&lt;br /&gt;
process intends for STDOUT appears in the other file that unluckily was&lt;br /&gt;
assigned handle number 1.&lt;br /&gt;
     If for some reason a child process should not inherit a STD handle,&lt;br /&gt;
the parent should use the DosDupHandle/rename technique to cause that&lt;br /&gt;
handle to point to the NULL device. You do this by opening a handle on the&lt;br /&gt;
NULL device and then moving that handle to 0, 1, or 2 with DosDupHandle.&lt;br /&gt;
This technique guarantees that the child's STD handles will all be&lt;br /&gt;
open.&lt;br /&gt;
     The subject of STDIN, STDOUT, and handle inheritance comes up again in&lt;br /&gt;
Chapter 14, Interactive Programs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.2  PIDs and Command Subtrees&lt;br /&gt;
&lt;br /&gt;
The PID (process identification) is a unique code that OS/2 assigns to each&lt;br /&gt;
process when it is created. The number is a magic cookie. Its value has no&lt;br /&gt;
significance to any process; it's simply a name for a process. The PID may&lt;br /&gt;
be any value except 0. A single PID value is never assigned to two&lt;br /&gt;
processes at the same time. PID values are reused but not &amp;quot;rapidly.&amp;quot; You&lt;br /&gt;
can safely remember a child's PID and then later attempt to affect that&lt;br /&gt;
child by using the PID in an OS/2 call. Even if the child process has died&lt;br /&gt;
unexpectedly, approximately 65,000 processes would have to be created&lt;br /&gt;
before the PID value might be reassigned; even a very active system takes&lt;br /&gt;
at least a day to create, execute, and terminate that many processes.&lt;br /&gt;
     I've discussed at some length the utility of an architecture in which&lt;br /&gt;
child programs can create children and those children can create&lt;br /&gt;
grandchildren and so on. I've also emphasized that the parent need not know&lt;br /&gt;
the architecture of a child process--whether the child process creates&lt;br /&gt;
children and grandchildren of its own. The parent need not know and indeed&lt;br /&gt;
should not know because such information may make the parent dependent on a&lt;br /&gt;
particular implementation of a child or grandchild program, an&lt;br /&gt;
implementation that might change. Given that a parent process starting up a&lt;br /&gt;
child process can't tell if that child creates its own descendants, how can&lt;br /&gt;
the parent process ask the system if the work that the child was to do has&lt;br /&gt;
been completed? The system could tell the parent whether the child process&lt;br /&gt;
is still alive, but this is insufficient. The child process may have farmed&lt;br /&gt;
out all its work to one or more grandchildren and then terminated itself&lt;br /&gt;
before the actual work was started. Furthermore, the parent process may&lt;br /&gt;
want to change the priority of the process(es) that it has created or even&lt;br /&gt;
terminate them because an error was detected or because the user typed&lt;br /&gt;
Ctrl-C.&lt;br /&gt;
     All these needs are met with a concept called the command subtree.&lt;br /&gt;
When a child process is created, its parent is told the child's PID. The&lt;br /&gt;
PID is the name of the single child process, and when taken as a command&lt;br /&gt;
subtree ID, this PID is the name of the entire tree of descendant processes&lt;br /&gt;
of which the child is the root. In other words, when used as a command&lt;br /&gt;
subtree ID, the PID refers not only to the child process but also to any of&lt;br /&gt;
its children and to any children that they may have and so on. A single&lt;br /&gt;
command subtree can conceivably contain dozens of processes (see Figure&lt;br /&gt;
4-5 and Figure 4-6).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                ³  PARENT   ³&lt;br /&gt;
                ÀÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
                ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                ³     A     ³&lt;br /&gt;
                ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
      ÚÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿                   ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
³     B     ³                   ³     C     ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ                   ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     D     ³     ÚÄÄÄÄÄÁÄÄÄÄÄ¿               ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ³     F     ³               ³     G     ³&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÀÄÄÄÄÄÄÄÄÄÄÄÙ               ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
³     E     ³                              ÚÄÄÄÄÄÙ     ÀÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÙ                        ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     H     ³     ³     I     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     J     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-5.  Command subtree. A is the root of (one of) the parent's&lt;br /&gt;
command subtrees. B and C are the root of two of A's subtrees and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                ³  PARENT   ³&lt;br /&gt;
                ÀÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
                ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                ³°°°°°A°°°°°³&lt;br /&gt;
                ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
      ÚÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿                   ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
³     B     ³                   ³     C     ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ                   ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     D     ³     ÚÄÄÄÄÄÁÄÄÄÄÄ¿               ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ³     F     ³               ³     G     ³&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÀÄÄÄÄÄÄÄÄÄÄÄÙ               ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
³°°°°°E°°°°°³                              ÚÄÄÄÄÄÙ     ÀÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÙ                        ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³°°°°°H°°°°°³     ³     I     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     J     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-6.   Command subtree. The shaded processes have died, but the&lt;br /&gt;
subtrees remain. PARENT can still use subtree A to affect all remaining&lt;br /&gt;
subprocesses. Likewise, an operation by C on subtree G will affect&lt;br /&gt;
process J.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Some OS/2 functions, such as DosCWait and DosKillProcess, can take&lt;br /&gt;
either PID or command subtree values, depending on the subfunction&lt;br /&gt;
requested. When the PID form is used, only the named process is affected.&lt;br /&gt;
When the command subtree form is used, the named process and all its&lt;br /&gt;
descendants are affected. This is true even if the child process no longer&lt;br /&gt;
exists or if the family tree of processes contains holes as a result of&lt;br /&gt;
process terminations.&lt;br /&gt;
     No statute of limitations applies to the use of the command subtree&lt;br /&gt;
form. That is, even if child process X died a long time ago, OS/2 still&lt;br /&gt;
allows references to the command subtree X. Consequently, OS/2 places one&lt;br /&gt;
simple restriction on the use of command subtrees so that it isn't forced&lt;br /&gt;
to keep around a complete process history forever: Only the direct parent&lt;br /&gt;
of process X can reference the command subtree X. In other words, X's&lt;br /&gt;
grandparent process can't learn X's PID from its parent and then issue&lt;br /&gt;
command subtree forms of commands; only X's direct parent can. This&lt;br /&gt;
puts an upper limit on the amount and duration of command subtree&lt;br /&gt;
information that OS/2 must retain; when a process terminates, information&lt;br /&gt;
pertaining to its command subtrees can be discarded. The command subtree&lt;br /&gt;
concept is recursive. OS/2 discards information about the terminated&lt;br /&gt;
process's own command subtrees, but if any of its descendant processes&lt;br /&gt;
still exist, the command subtree information pertaining to their child&lt;br /&gt;
processes is retained. And those surviving descendants are still part of&lt;br /&gt;
the command subtree belonging to the terminated process's parent&lt;br /&gt;
process.&lt;br /&gt;
5. Assuming that the parent process itself still exists. In any&lt;br /&gt;
case, all processes are part of a nested set of command subtrees&lt;br /&gt;
belonging to all its surviving ancestor processes.&lt;br /&gt;
5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.3  DosExecPgm&lt;br /&gt;
&lt;br /&gt;
To execute a child process, you use the DosExecPgm call. The form of the&lt;br /&gt;
call is shown in Listing 4-3.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
extern unsigned far pascal DOSEXECPGM (&lt;br /&gt;
          char far                 *OBJNAMEBUF,&lt;br /&gt;
          unsigned                 OBJNAMEBUFL,&lt;br /&gt;
          unsigned                 EXECTYPE,&lt;br /&gt;
          char far                 *ARGSTRING,&lt;br /&gt;
          char far                 *ENVSTRING,&lt;br /&gt;
          struct ResultCodes far   *CODEPID,&lt;br /&gt;
          char far                 *PGMNAME);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-3.  DosExecPgm call.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The obj namebuf arguments provide an area where OS/2 can return a&lt;br /&gt;
character string if the DosExecPgm function fails. In MS-DOS version 2.0&lt;br /&gt;
and later, the EXEC function was quite simple: It loaded a file into&lt;br /&gt;
memory. Little could go wrong: file not found, file bad format,&lt;br /&gt;
insufficient memory, to name some possibilities. A simple error code&lt;br /&gt;
sufficed to diagnose problems. OS/2's dynamic linking facility is much more&lt;br /&gt;
complicated. The earliest prototype versions of OS/2 were missing these&lt;br /&gt;
obj namebuf arguments, and engineers were quickly frustrated by the error&lt;br /&gt;
code &amp;quot;dynlink library load failed.&amp;quot; &amp;quot;Which library was it? The application&lt;br /&gt;
references seven of them! But all seven of those libraries are alive and&lt;br /&gt;
well. Perhaps it was a library that one of those libraries referenced. But&lt;br /&gt;
which libraries do they reference? Gee, I dunno...&amp;quot; For this reason, the&lt;br /&gt;
object name arguments were added. The buffer is a place where OS/2 returns&lt;br /&gt;
the name of the missing or defective library or other load object, and the&lt;br /&gt;
length argument tells OS/2 the maximum size of the buffer area. Strings&lt;br /&gt;
that will not fit in the area are truncated.&lt;br /&gt;
     The exectype word describes the form of the DosExecPgm. The values are&lt;br /&gt;
as follows.&lt;br /&gt;
&lt;br /&gt;
     0:  Execute the child program synchronously. The thread issuing the&lt;br /&gt;
         DosExecPgm will not execute further until the child process has&lt;br /&gt;
         finished executing. The thread returns from DosExecPgm when the&lt;br /&gt;
         child process itself terminates, not when the command subtree has&lt;br /&gt;
         terminated. This form of DosExecPgm is provided for ease in&lt;br /&gt;
         converting MS-DOS version 3.x programs to OS/2. It is considered&lt;br /&gt;
         obsolete and should generally be avoided. Its use may interfere&lt;br /&gt;
         with proper Ctrl-C and Ctrl-Break handling (see Chapter 14,&lt;br /&gt;
         Interactive Programs).&lt;br /&gt;
&lt;br /&gt;
     1:  Execute the program asynchronously. The child process begins&lt;br /&gt;
         executing as soon as the scheduler allows; the calling thread&lt;br /&gt;
         returns from the DosExecPgm call immediately. You cannot assume&lt;br /&gt;
         that the child process has received CPU time before the parent&lt;br /&gt;
         thread returns from the DosExecPgm call; neither can you assume&lt;br /&gt;
         that the child process has not received such CPU service. This&lt;br /&gt;
         form instructs OS/2 not to bother remembering the child's&lt;br /&gt;
         termination code for a future DosCWait call. It is used when the&lt;br /&gt;
         parent process doesn't care what the result code of the child may&lt;br /&gt;
         be or when or if it completes, and it frees the parent from the &lt;br /&gt;
         necessity of issuing DosCWait calls. Programs executing other&lt;br /&gt;
         programs as tools would rarely use this form. This form might be&lt;br /&gt;
         used by a system utility, for example, whose job is to fire off&lt;br /&gt;
         certain programs once an hour but not to take any action or notice&lt;br /&gt;
         should any of those programs fail. Note that, unlike the detach&lt;br /&gt;
         form described below, the created process is still recorded as a&lt;br /&gt;
         child of the executing parent. The parent can issue a DosCWait&lt;br /&gt;
         call to determine whether the child subtree is still executing,&lt;br /&gt;
         although naturally there is no return code when the child process&lt;br /&gt;
         does terminate.&lt;br /&gt;
&lt;br /&gt;
     2:  This form is similar to form 1 in that it executes the child&lt;br /&gt;
         process asynchronously, but it instructs OS/2 to retain the child&lt;br /&gt;
         process's exit code for future examination by DosCWait. Thus, a&lt;br /&gt;
         program can determine the success or failure of a child process.&lt;br /&gt;
         The parent process should issue an appropriate DosCWait &amp;quot;pretty&lt;br /&gt;
         soon&amp;quot; because OS/2 version 1.0 maintains about 2600 bytes of data&lt;br /&gt;
         structures for a dead process whose parent is expected to DosCWait&lt;br /&gt;
         but hasn't yet done so. To have one of these structures lying&lt;br /&gt;
         around for a few minutes is no great problem, but programs need to&lt;br /&gt;
         issue DosCWaits in a timely fashion to keep from clogging the&lt;br /&gt;
         system with the carcasses of processes that finished hours&lt;br /&gt;
         ago.&lt;br /&gt;
            OS/2 takes care of all the possible timing considerations, so&lt;br /&gt;
         it's OK to issue the DosCWait before or after the child process&lt;br /&gt;
         has completed. Although a parent process can influence the&lt;br /&gt;
         relative assignment of CPU time between the child and parent&lt;br /&gt;
         processes by setting its own and its child's relative priorities,&lt;br /&gt;
         there is no reliable way of determining which process will run&lt;br /&gt;
         when. Write your program in such a way that it doesn't matter if&lt;br /&gt;
         the child completes before or after the DosCWait, or use some form&lt;br /&gt;
         of IPC to synchronize the execution of parent and child processes.&lt;br /&gt;
         See 4.4 DosCWait for more details.&lt;br /&gt;
&lt;br /&gt;
     3:  This form is used by the system debugger, CodeView. The system&lt;br /&gt;
         architecture does not allow one process to latch onto another&lt;br /&gt;
         arbitrary process and start examining and perhaps modifying the&lt;br /&gt;
         target process's execution. Such a facility would result in a&lt;br /&gt;
         massive side effect and is contrary to the tenet of encapsulation&lt;br /&gt;
         in the design religion. Furthermore, such a facility would prevent&lt;br /&gt;
         OS/2 from ever providing an environment secure against malicious&lt;br /&gt;
         programs. OS/2 solves this problem with the DosPtrace function,&lt;br /&gt;
         which peeks, pokes, and controls a child process. This function is&lt;br /&gt;
         allowed to affect only processes that were executed with this&lt;br /&gt;
         special mode value of 3.&lt;br /&gt;
&lt;br /&gt;
     4:  This form executes the child process asynchronously but also&lt;br /&gt;
         detaches it from the process issuing the DosExecPgm call. A&lt;br /&gt;
         detached process does not inherit the parent process's screen&lt;br /&gt;
         group; instead, it executes in a special invalid screen group. Any&lt;br /&gt;
         attempt to do screen, keyboard, or mouse I/O from within this&lt;br /&gt;
         screen group returns an error code. The system does not consider a&lt;br /&gt;
         detached process a child of the parent process; the new process&lt;br /&gt;
         has no connection with the creating process. In other words, it's&lt;br /&gt;
         parentless. This form of DosExecPgm is used to create daemon&lt;br /&gt;
         programs that execute without direct interaction with the user.&lt;br /&gt;
&lt;br /&gt;
     The EnvString argument points to a list of ASCII text strings that&lt;br /&gt;
contain environment values. OS/2 supports a separate environment block for&lt;br /&gt;
each process. A process typically inherits its parent's environment&lt;br /&gt;
strings. In this case, the EnvPointer argument should be NULL, which tells&lt;br /&gt;
OS/2 to supply the child process with a copy of the parent's environment&lt;br /&gt;
strings (see Listing 4-4).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
PATH=C:\DOS;C:\EDITORS;C:\TOOLS;C:\XTOOLS;C:\BIN;C:\UBNET&lt;br /&gt;
INCLUDE=\include&lt;br /&gt;
TERM=h19&lt;br /&gt;
INIT=c:\tmp&lt;br /&gt;
HOME=c:\tmp&lt;br /&gt;
USER=c:\tmp&lt;br /&gt;
TEMP=c:\tmp&lt;br /&gt;
TERM=ibmpc&lt;br /&gt;
LIB=c:\lib&lt;br /&gt;
PROMPT=($p)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-4.  A typical environment string set.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The environment strings are normally used to customize a particular&lt;br /&gt;
execution environment. For example, suppose a user creates two screen&lt;br /&gt;
groups, each running a copy of CMD.EXE. Each CMD.EXE is a direct child of&lt;br /&gt;
the presentation manager, which manages and creates screen groups, and each&lt;br /&gt;
is also the ancestor of all processes that will run in its particular&lt;br /&gt;
screen group. If the user is running utility programs that use the&lt;br /&gt;
environment string &amp;quot;TEMP=&amp;lt;dirname&amp;gt;&amp;quot; to specify a directory to hold&lt;br /&gt;
temporary files, he or she may want to specify different TEMP= values for&lt;br /&gt;
each copy of CMD.EXE. As a result, the utilities that use TEMP= will access&lt;br /&gt;
the proper directory, depending on which screen group they are run in,&lt;br /&gt;
because they will have inherited the proper TEMP= environment string from&lt;br /&gt;
their CMD.EXE ancestor. See Chapter 10, Environment Strings, for a complete&lt;br /&gt;
discussion.&lt;br /&gt;
     Because of the inheritable nature of environment strings, parent&lt;br /&gt;
processes that edit the environment list should remove or edit only those&lt;br /&gt;
strings with which they are involved; any unrecognized strings should be&lt;br /&gt;
preserved.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.4  DosCWait&lt;br /&gt;
&lt;br /&gt;
When a process executes a child process, it usually wants to know when that&lt;br /&gt;
child process has completed and whether the process succeeded or failed.&lt;br /&gt;
DosCWait, the OS/2 companion function to DosExecPgm, returns such&lt;br /&gt;
information. Before we discuss DosCWait in detail, two observations are in&lt;br /&gt;
order. First, although each DosExecPgm call starts only a single process,&lt;br /&gt;
it's possible--and not uncommon--for that child process to create its own&lt;br /&gt;
children and perhaps they their own and so on. A program should not assume&lt;br /&gt;
that its child process won't create subchildren; instead, programs should&lt;br /&gt;
use the command-subtree forms of DosCWait. One return code from the direct&lt;br /&gt;
child process (that is, the root of the command subtree) is sufficient&lt;br /&gt;
because if that direct child process invokes other processes to do work for&lt;br /&gt;
it the direct child is responsible for monitoring their success via&lt;br /&gt;
DosCWait. In other words, if a child process farms out some of its work to&lt;br /&gt;
a grandchild process and that grandchild process terminates in error, then&lt;br /&gt;
the child process should also terminate with an error return.&lt;br /&gt;
     Second, although we discuss the process's child, in fact processes can&lt;br /&gt;
have multiple child processes and therefore multiple command subtrees at&lt;br /&gt;
any time. The parent process may have interconnected the child processes&lt;br /&gt;
via anonymous pipes, or they may be independent of one another. Issuing&lt;br /&gt;
separate DosCWaits for each process or subtree is unnecessary. The form of&lt;br /&gt;
the DosCWait call is shown in Listing 4-5.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
extern unsigned far pascal DOSCWAIT (&lt;br /&gt;
     unsigned                 ACTIONCODE,&lt;br /&gt;
     unsigned                 WAITOPTION,&lt;br /&gt;
     struct ResultCodes far   *RESULTWORD,&lt;br /&gt;
     unsigned far             *PIDRETURN,&lt;br /&gt;
     unsigned                 PID);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-5.  DosCWait function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Three of the arguments affect the scope of the command: ActionCode,&lt;br /&gt;
WaitOption, and PID. It's easiest to show how these interact by arranging&lt;br /&gt;
their possible values into tables.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Command Subtrees&lt;br /&gt;
&lt;br /&gt;
     These forms of DosCWait operate on the entire command subtree, which&lt;br /&gt;
     may, of course, consist of only one child process. We recommend these&lt;br /&gt;
     forms because they will continue to work correctly if the child&lt;br /&gt;
     process is changed to use more or fewer child processes of its own.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId   Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     1            0            n           Wait until the command subtree&lt;br /&gt;
                                           has completed and then return&lt;br /&gt;
                                           the direct child's termination&lt;br /&gt;
                                           code.&lt;br /&gt;
&lt;br /&gt;
     1            1            n           If the command subtree has&lt;br /&gt;
                                           completed, return the direct&lt;br /&gt;
                                           child's termination code.&lt;br /&gt;
                                           Otherwise, return the&lt;br /&gt;
                                           ERROR_CHILD_NOT_COMPLETE&lt;br /&gt;
                                           error code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Individual Processes&lt;br /&gt;
&lt;br /&gt;
     These forms of DosCWait are used to monitor individual child&lt;br /&gt;
     processes. The processes must be direct children; grandchild or&lt;br /&gt;
     unrelated processes cannot be DosCWaited. Use these forms only when&lt;br /&gt;
     the child process is part of the same application or software package&lt;br /&gt;
     as the parent process; the programmer needs to be certain that she or&lt;br /&gt;
     he can safely ignore the possibility that grandchild processes might&lt;br /&gt;
     still be running after the direct child has terminated.&lt;br /&gt;
1. It is in itself not an error to collect a child process's&lt;br /&gt;
termination code via DosCWait while that child still&lt;br /&gt;
has living descendant processes. However, such a case generally&lt;br /&gt;
means that the child's work, whatever that was, is not yet complete.&lt;br /&gt;
1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId   Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     0            0            0           DosCWait returns as soon as a&lt;br /&gt;
                                           direct child process terminates.&lt;br /&gt;
                                           If a child process had already&lt;br /&gt;
                                           terminated at the time of this&lt;br /&gt;
                                           call, it will return&lt;br /&gt;
                                           immediately.&lt;br /&gt;
&lt;br /&gt;
     0            0            N           DosCWait returns as soon as the&lt;br /&gt;
                                           direct child process N&lt;br /&gt;
                                           terminates. If it had already&lt;br /&gt;
                                           terminated at the time of the&lt;br /&gt;
                                           call, DosCWait returns&lt;br /&gt;
                                           immediately.&lt;br /&gt;
&lt;br /&gt;
     0            1            0           DosCWait checks for a terminated&lt;br /&gt;
                                           direct child process. If one is&lt;br /&gt;
                                           found, its status is returned.&lt;br /&gt;
                                           If none is found, an error code&lt;br /&gt;
                                           is returned.&lt;br /&gt;
&lt;br /&gt;
     0            1            N           DosCWait checks the status of&lt;br /&gt;
                                           the direct child process N. If&lt;br /&gt;
                                           it is terminated, its status is&lt;br /&gt;
                                           returned. If it is still&lt;br /&gt;
                                           running, an error code is&lt;br /&gt;
                                           returned.&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Not Recommended&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId    Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     1            0            0            DosCWait waits until a direct&lt;br /&gt;
                                            child has terminated and then&lt;br /&gt;
                                            waits until all of that child's&lt;br /&gt;
                                            descendants have terminated. It&lt;br /&gt;
                                            then returns the direct child's&lt;br /&gt;
                                            exit code. This form does not&lt;br /&gt;
                                            wait until the first command&lt;br /&gt;
                                            subtree has terminated; it&lt;br /&gt;
                                            selects a command subtree based&lt;br /&gt;
                                            on the first direct child that&lt;br /&gt;
                                            terminates, and then it waits&lt;br /&gt;
                                            as long as necessary for the&lt;br /&gt;
                                            remainder of that command&lt;br /&gt;
                                            subtree, even if other command&lt;br /&gt;
                                            subtrees meanwhile complete.&lt;br /&gt;
&lt;br /&gt;
     1            1            0            This form returns&lt;br /&gt;
                                            ERROR_CHILD_NOT_COMPLETE if any&lt;br /&gt;
                                            process in any of the caller's&lt;br /&gt;
                                            subtrees is still executing. If&lt;br /&gt;
                                            all subtrees have terminated,&lt;br /&gt;
                                            this form returns with a direct&lt;br /&gt;
                                            child's exit code. If no direct&lt;br /&gt;
                                            child processes have unwaited&lt;br /&gt;
                                            exit codes, the code&lt;br /&gt;
                                            ERROR_WAIT_NO_CHILDREN is&lt;br /&gt;
                                            returned.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.5  Control of Child Tasks and Command Subtrees&lt;br /&gt;
&lt;br /&gt;
A parent process has only limited control over its child processes because&lt;br /&gt;
the system is designed to minimize the side effects, or cross talk, between&lt;br /&gt;
processes. Specifically, a parent process can affect its command subtrees&lt;br /&gt;
in two ways: It can change their CPU priority, and it can terminate (kill)&lt;br /&gt;
them. Once again, the command subtree is the recommended form for both&lt;br /&gt;
commands because that form is insensitive to the operational details of the&lt;br /&gt;
child process.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.5.1  DosKillProcess&lt;br /&gt;
A parent process may initiate a child process or command subtree and then&lt;br /&gt;
decide to terminate that activity before the process completes normally.&lt;br /&gt;
Often this comes about because of a direct user command or because the user&lt;br /&gt;
typed Ctrl-Break. See Chapter 14, Interactive Programs, for special&lt;br /&gt;
techniques concerning Ctrl-Break and Ctrl-C.&lt;br /&gt;
     DosKillProcess flags each process in the command subtree (or the&lt;br /&gt;
direct child process if that form is used) for termination. A process&lt;br /&gt;
flagged for termination normally terminates as soon as all its threads&lt;br /&gt;
leave the system (that is, as soon as all its threads return from all&lt;br /&gt;
system calls). The system aborts calls that might block for more than a&lt;br /&gt;
second or two, such as those that read a keyboard character, so that the&lt;br /&gt;
process can terminate quickly. A process can intercept SIGKILL to delay&lt;br /&gt;
termination longer, even indefinitely. Delaying termination inordinately&lt;br /&gt;
via SetSignalHandler/SIGKILL is very bad practice and is considered a bug&lt;br /&gt;
rather than a feature.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.5.2  DosSetPrty&lt;br /&gt;
A child process inherits its parent's process priority when the DosExecPgm&lt;br /&gt;
call is issued. After the DosExecPgm call, the parent can still change the&lt;br /&gt;
process priority of the command subtree or of only the direct child&lt;br /&gt;
process. The command subtree form is recommended; if the child process's&lt;br /&gt;
work deserves priority N, then any child processes that it executes to help&lt;br /&gt;
in that work should also run at priority N.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==5  Threads and Scheduler/Priorities==&lt;br /&gt;
&lt;br /&gt;
===5.1  Threads===&lt;br /&gt;
&lt;br /&gt;
Computers consist of a CPU (central processing unit) and RAM (random access&lt;br /&gt;
memory). A computer program consists of a sequence of instructions that are&lt;br /&gt;
placed, for the most part, one after the other in RAM. The CPU reads each&lt;br /&gt;
instruction in sequence and executes it. The passage of the CPU through the&lt;br /&gt;
instruction sequence is called a thread of execution. All versions of MS-&lt;br /&gt;
DOS executed programs, so they necessarily supported a thread of execution.&lt;br /&gt;
OS/2 is unique, however, in that it supports multiple threads of execution&lt;br /&gt;
within a single process. In other words, a program can execute in two or&lt;br /&gt;
more spots in its code at the same time.&lt;br /&gt;
     Obviously, a multitasking system needs to support multiple threads in&lt;br /&gt;
a systemwide sense. Each process necessarily must have a thread; so if&lt;br /&gt;
there are ten processes in the system, there must be ten threads. Such an&lt;br /&gt;
existence of multiple threads in the system is invisible to the programmer&lt;br /&gt;
because each program executes with only one thread. OS/2 is different from&lt;br /&gt;
this because it allows an individual program to execute with multiple&lt;br /&gt;
threads if it desires.&lt;br /&gt;
     Because threads are elements of processes and because the process is&lt;br /&gt;
the unit of resource ownership, all threads that belong to the same process&lt;br /&gt;
share that process's resources. Thus, if one thread opens a file on file&lt;br /&gt;
handle X, all threads in that process can issue DosReads or DosWrites to&lt;br /&gt;
that handle. If one thread allocates a memory segment, all threads in that&lt;br /&gt;
process can access that memory segment. Threads are analogous to the&lt;br /&gt;
employees of a company. A company may consist of a single employee, or it&lt;br /&gt;
may consist of two or more employees that divide the work among them. Each&lt;br /&gt;
employee has access to the company's resources--its office space and&lt;br /&gt;
equipment. The employees themselves, however, must coordinate their work so&lt;br /&gt;
that they cooperate and don't conflict. As far as the outside world is&lt;br /&gt;
concerned, each employee speaks for the company. Employees can terminate&lt;br /&gt;
and/or more can be hired without affecting how the company is seen from&lt;br /&gt;
outside. The only requirement is that the company have at least one&lt;br /&gt;
employee. When the last employee (thread) dies, the company (process) also&lt;br /&gt;
dies.&lt;br /&gt;
     Although the process is the unit of resource ownership, each thread&lt;br /&gt;
does &amp;quot;own&amp;quot; a small amount of private information. Specifically, each thread&lt;br /&gt;
has its own copy of the CPU's register contents. This is an obvious&lt;br /&gt;
requirement if each thread is to be able to execute different instruction&lt;br /&gt;
sequences. Furthermore, each thread has its own copy of the floating point&lt;br /&gt;
registers. OS/2 creates the process's first thread when the program begins&lt;br /&gt;
execution. Any additional threads are created by means of the&lt;br /&gt;
DosCreateThread call. Any thread can create another thread. All threads in&lt;br /&gt;
a process are considered siblings; there are no parent-child relationships.&lt;br /&gt;
The initial thread, thread 1, has some special characteristics and is&lt;br /&gt;
discussed below.&lt;br /&gt;
&lt;br /&gt;
====5.1.1  Thread Stacks====&lt;br /&gt;
Each thread has its own stack area, pointed to by that thread's SS and SP&lt;br /&gt;
values. Thread 1 is the process's primal thread. OS/2 allocates it stack&lt;br /&gt;
area in response to specifications in the .EXE file. If additional threads&lt;br /&gt;
are created via the DosCreateThread call, the caller specifies a stack area&lt;br /&gt;
for the new thread. Because the memory in which each thread's stack resides&lt;br /&gt;
is owned by the process, any thread can modify this memory; the programmer&lt;br /&gt;
must make sure that this does not happen. The size of the segment in which&lt;br /&gt;
the stack resides is explicitly specified; the size of a thread's stack is&lt;br /&gt;
not. The programmer can place a thread's stack in its own segment or in a&lt;br /&gt;
segment with other data values, including other thread stacks. In any case,&lt;br /&gt;
the programmer must ensure sufficient room for each thread's needs. Each&lt;br /&gt;
thread's stack must have at least 2 KB free in addition to the thread's&lt;br /&gt;
other needs at all times. This extra space is set aside for the needs of&lt;br /&gt;
dynamic link routines, some of which consume considerable stack space. All&lt;br /&gt;
threads must maintain this stack space reserve even if they are not used to&lt;br /&gt;
call dynamic link routines. Because of a bug in many 80286 processors,&lt;br /&gt;
stack segments must be preallocated to their full size. You cannot overrun&lt;br /&gt;
a stack segment and then assume that OS/2 will grow the segment;&lt;br /&gt;
overrunning a stack segment will cause a stack fault, and the process will&lt;br /&gt;
be terminated.&lt;br /&gt;
&lt;br /&gt;
====5.1.2  Thread Uses====&lt;br /&gt;
Threads have a great number of uses. This section describes four of them.&lt;br /&gt;
These examples are intended to be inspirations to the programmer; there are&lt;br /&gt;
many other uses for threads.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.1  Foreground and Background Work&lt;br /&gt;
Threads provide a form of multitasking within a single program; therefore,&lt;br /&gt;
one of their most obvious uses is to provide simultaneous foreground and&lt;br /&gt;
background&lt;br /&gt;
1. Here I mean foreground and background in the sense of directly&lt;br /&gt;
interacting with the user, not as in foreground and background&lt;br /&gt;
screen groups.&lt;br /&gt;
1 processing for a program. For example, a spreadsheet program&lt;br /&gt;
might use one thread to display menus and to read user input. A second&lt;br /&gt;
thread could execute user commands, update the spreadsheet, and so on.&lt;br /&gt;
     This arrangement generally increases the perceived speed of the&lt;br /&gt;
program by allowing the program to prompt for another command before the&lt;br /&gt;
previous command is complete. For example, if the user changes a cell in a&lt;br /&gt;
spreadsheet and then calls for recalculation, the &amp;quot;execute&amp;quot; thread can&lt;br /&gt;
recalculate while the &amp;quot;command&amp;quot; thread allows the user to move the cursor,&lt;br /&gt;
select new menus, and so forth. The spreadsheet should use RAM semaphores&lt;br /&gt;
to protect its structures so that one thread can't change a structure while&lt;br /&gt;
it is being manipulated by another thread. As far as the user can tell, he&lt;br /&gt;
or she is able to overlap commands without restriction. In actuality, the&lt;br /&gt;
previous command is usually complete before the user can finish entering&lt;br /&gt;
the new command. Occasionally, however, the new command is delayed until&lt;br /&gt;
the first has completed execution. This happens, for example, when the user&lt;br /&gt;
of a spreadsheet deletes a row right after saving the spreadsheet to disk.&lt;br /&gt;
Of course, the performance in this worst case situation is no worse than a&lt;br /&gt;
standard single-thread design is for all cases.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.2  Asynchronous Processing&lt;br /&gt;
Another common use of threads is to provide asynchronous elements in a&lt;br /&gt;
program's design. For example, as a protection against power failure, you&lt;br /&gt;
can design an editor so that it writes its RAM buffer to disk once every&lt;br /&gt;
minute. Threads make it unnecessary to scatter time checks throughout the&lt;br /&gt;
program or to sit in polling loops for input so that a time event isn't&lt;br /&gt;
missed while blocked on a read call. You can create a thread whose sole job&lt;br /&gt;
is periodic backup. The thread can call DosSleep to sleep for 60 seconds,&lt;br /&gt;
write the buffer, and then go back to sleep for another 60 seconds.&lt;br /&gt;
     The asynchronous event doesn't have to be time related. For example,&lt;br /&gt;
in a program that communicates over an asynchronous serial port, you can&lt;br /&gt;
dedicate a thread to wait for the modem carrier to come on or to wait for a&lt;br /&gt;
protocol time out. The main thread can continue to interact with the user.&lt;br /&gt;
     Programs that provide services to other programs via IPC can use&lt;br /&gt;
threads to simultaneously respond to multiple requests. For example, one&lt;br /&gt;
thread can watch the incoming work queue while one or more additional&lt;br /&gt;
threads perform the work.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.3  Speed Execution&lt;br /&gt;
You can use threads to speed the execution of single processes by&lt;br /&gt;
overlapping I/O and computation. A single-threaded process can perform&lt;br /&gt;
computations or call OS/2 for disk reads and writes, but not both at the&lt;br /&gt;
same time. A multithreaded process, on the other hand, can compute one&lt;br /&gt;
batch of data while reading the next batch from a device.&lt;br /&gt;
     Eventually, PCs containing multiple 80386 processors will become&lt;br /&gt;
available. An application that uses multiple threads may execute faster by&lt;br /&gt;
using more than one CPU simultaneously.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.4  Organizing Programs&lt;br /&gt;
Finally, you can use threads to organize and simplify the structure of a&lt;br /&gt;
program. For example, in a program for a turnkey security/alarm system, you&lt;br /&gt;
can assign a separate thread for each activity. One thread can watch the&lt;br /&gt;
status of the intrusion switches; a second can send commands to control the&lt;br /&gt;
lights; a third can run the telephone dialer; and a fourth can interface&lt;br /&gt;
with each control panel.&lt;br /&gt;
     This structure simplifies software design. The programmer needn't&lt;br /&gt;
worry that an intrusion switch is triggering unnoticed while the CPU is&lt;br /&gt;
executing the code that waits on the second key of a two-key command.&lt;br /&gt;
Likewise, the programmer doesn't have to worry about talking to two command&lt;br /&gt;
consoles at the same time; because each has its own thread and local&lt;br /&gt;
(stack) variables, multiple consoles can be used simultaneously without&lt;br /&gt;
conflict.&lt;br /&gt;
     Of course, you can write such a program without multiple threads; a&lt;br /&gt;
rat's nest of event flags and polling loops would do the job. Much better&lt;br /&gt;
would be a family of co-routines. But best, and simplest of all, is a&lt;br /&gt;
multithreaded design.&lt;br /&gt;
&lt;br /&gt;
====5.1.3  Interlocking====&lt;br /&gt;
The good news about threads is that they share a process's data, files, and&lt;br /&gt;
resources. The bad news is that they share a process's data, files, and&lt;br /&gt;
resources--and that sometimes these items must be protected against&lt;br /&gt;
simultaneous update by multiple threads. As we discussed earlier, most OS/2&lt;br /&gt;
machines have a single CPU; the &amp;quot;random&amp;quot; preemption of the scheduler,&lt;br /&gt;
switching the CPU among threads, gives the illusion of the simultaneous&lt;br /&gt;
execution of threads. Because the scheduler is deterministic and priority&lt;br /&gt;
based, scheduling a process's threads is certainly not random; but good&lt;br /&gt;
programming practice requires that it be considered so. Each time a program&lt;br /&gt;
runs, external events will perturb the scheduling of the threads. Perhaps&lt;br /&gt;
some other, higher priority task needs the CPU for a while. Perhaps the&lt;br /&gt;
disk arm is in a different position, and a disk read by one thread takes a&lt;br /&gt;
little longer this time than it did the last. You cannot even assume that&lt;br /&gt;
only the highest priority runnable thread is executing because a multiple-&lt;br /&gt;
CPU system may execute the N highest priority threads.&lt;br /&gt;
     The only safe assumption is that all threads are executing&lt;br /&gt;
simultaneously and that--in the absence of explicit interlocking or&lt;br /&gt;
semaphore calls--each thread is always doing the &amp;quot;worst possible thing&amp;quot; in&lt;br /&gt;
terms of simultaneously updating static data or structures. Writing to&lt;br /&gt;
looser standards and then testing the program &amp;quot;to see if it's OK&amp;quot; is&lt;br /&gt;
unacceptable. The very nature of such race conditions, as they are called,&lt;br /&gt;
makes them extremely difficult to find during testing. Murphy's law says&lt;br /&gt;
that such problems are rare during testing and become a plague only after&lt;br /&gt;
the program is released.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.1  Local Variables&lt;br /&gt;
The best way to avoid a collision of threads over static data is to write&lt;br /&gt;
your program to minimize static data. Because each thread has its own&lt;br /&gt;
stack, each thread has its own stack frame in which to store local&lt;br /&gt;
variables. For example, if one thread opens and reads a file and no other&lt;br /&gt;
thread ever manipulates that file, the memory location where that file's&lt;br /&gt;
handle is stored should be in the thread's stack frame, not in static&lt;br /&gt;
memory. Likewise, buffers and work areas that are private to a thread&lt;br /&gt;
should be on that thread's stack frame. Stack variables that are local to&lt;br /&gt;
the current procedure are easily referenced in high-level languages and in&lt;br /&gt;
assembly language. Data items that are referenced by multiple procedures&lt;br /&gt;
can still be located on the stack. Pascal programs can address such items&lt;br /&gt;
directly via the data scope mechanism. C and assembly language programs&lt;br /&gt;
will need to pass pointers to the items into the procedures that use them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.2  RAM Semaphores&lt;br /&gt;
Although using local variables on stack frames greatly reduces problems&lt;br /&gt;
among threads, there will always be at least a few cases in which more than&lt;br /&gt;
one thread needs to access a static data item or a static resource such as&lt;br /&gt;
a file handle. In this situation, write the code that manipulates the&lt;br /&gt;
static item as a critical section (a body of code that manipulates a data&lt;br /&gt;
resource in a nonreentrant way) and then use RAM semaphores to reserve each&lt;br /&gt;
critical section before it is executed. This procedure guarantees that only&lt;br /&gt;
one thread at a time is in a critical section. See 16.2 Data Integrity for&lt;br /&gt;
a more detailed discussion.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.3  DosSuspendThread&lt;br /&gt;
In some situations it may be possible to enumerate all threads that might&lt;br /&gt;
enter a critical section. In these cases, a process's thread can use&lt;br /&gt;
DosSuspendThread to suspend the execution of the other thread(s) that might&lt;br /&gt;
enter the critical section. The DosSuspendThread call can only be used to&lt;br /&gt;
suspend threads that belong to the process making the call; it cannot be&lt;br /&gt;
used to suspend a thread that belongs to another process. Multiple threads&lt;br /&gt;
can be suspended by making multiple DosSuspendThread calls, one per thread.&lt;br /&gt;
If a just-suspended thread is in the middle of a system call, work on that&lt;br /&gt;
system call may or may not proceed. In either case, there will be no&lt;br /&gt;
further execution of application (ring 3) code by a suspended thread.&lt;br /&gt;
     It is usually better to protect critical sections with a RAM semaphore&lt;br /&gt;
than to use DosSuspendThread. Using a semaphore to protect a critical&lt;br /&gt;
section is analogous to using a traffic light to protect an intersection&lt;br /&gt;
(an automotive &amp;quot;critical section&amp;quot; because conflicting uses must be&lt;br /&gt;
prevented). Using DosSuspendThread, on the other hand, is analogous to your&lt;br /&gt;
stopping the other cars each time you go through an intersection; you're&lt;br /&gt;
interfering with the operation of the other cars just in case they might be&lt;br /&gt;
driving through the same intersection as you, presumably an infrequent&lt;br /&gt;
situation. Furthermore, you need a way to ensure that another vehicle isn't&lt;br /&gt;
already in the middle of the intersection when you stop it. Getting back to&lt;br /&gt;
software, you need to ensure that the thread you're suspending isn't&lt;br /&gt;
already executing the critical section at the time that you suspend it. We&lt;br /&gt;
recommend that you avoid DosSuspendThread when possible because of its&lt;br /&gt;
adverse effects on process performance and because of the difficulty in&lt;br /&gt;
guaranteeing that all the necessary threads have been suspended, especially&lt;br /&gt;
when a program undergoes future maintenance and modification.&lt;br /&gt;
     A DosResumeThread call restores the normal operation of a suspended&lt;br /&gt;
thread.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.4  DosEnterCritSec/DosExitCritSec&lt;br /&gt;
DosSuspendThread suspends the execution of a single thread within a&lt;br /&gt;
process. DosEnterCritSec suspends all threads in a process except the one&lt;br /&gt;
making the DosEnterCritSec call. Except for the scope of their operation,&lt;br /&gt;
DosEnterCritSec and DosExitCritSec are similar to DosSuspendThead and&lt;br /&gt;
DosResumeThread, and the same caveats and observations apply.&lt;br /&gt;
     DosExitCritSec will not undo a DosSuspendThread that was already in&lt;br /&gt;
effect. It releases only those threads that were suspended by&lt;br /&gt;
DosEnterCritSec.&lt;br /&gt;
&lt;br /&gt;
====5.1.4  Thread 1====&lt;br /&gt;
Each thread in a process has an associated thread ID. A thread's ID is a&lt;br /&gt;
magic cookie. Its value has no intrinsic meaning to the application; it&lt;br /&gt;
has meaning only as a name for a thread in an operating system call. The&lt;br /&gt;
one exception to this is the process's first thread, whose thread ID is&lt;br /&gt;
always 1.&lt;br /&gt;
     Thread 1 is special: It is the thread that is interrupted when a&lt;br /&gt;
process receives a signal. See Chapter 12, Signals, for further details.&lt;br /&gt;
&lt;br /&gt;
====5.1.5  Thread Death====&lt;br /&gt;
A thread can die in two ways. First, it can terminate itself with the&lt;br /&gt;
DosExit call. Second, when any thread in a process calls DosExit with the&lt;br /&gt;
&amp;quot;exit entire process&amp;quot; argument, all threads belonging to that process are&lt;br /&gt;
terminated &amp;quot;as soon as possible.&amp;quot; If they were executing application code&lt;br /&gt;
at the time DosExit was called, they terminate immediately. If they were in&lt;br /&gt;
the middle of a system call, they terminate &amp;quot;very quickly.&amp;quot; If the system&lt;br /&gt;
call executes quickly enough, its function may complete (although the CPU&lt;br /&gt;
will not return from the system call itself); but if the system call&lt;br /&gt;
involves delays of more than 1 second, it will terminate without&lt;br /&gt;
completing. Whether a thread's last system call completes is usually moot,&lt;br /&gt;
but in a few cases, such as writes to some types of devices, it may be&lt;br /&gt;
noticed that the last write was only partially completed.&lt;br /&gt;
     When a process wants to terminate, it should use the &amp;quot;terminate entire&lt;br /&gt;
process&amp;quot; form of DosExit rather than the &amp;quot;terminate this thread&amp;quot; form.&lt;br /&gt;
Unbeknownst to the calling process, some dynlink packages, including some&lt;br /&gt;
OS/2 system calls, may create threads. These threads are called captive&lt;br /&gt;
threads because only the original calling thread returns from the dynlink&lt;br /&gt;
call; the created thread remains &amp;quot;captive&amp;quot; inside the dynlink package. If a&lt;br /&gt;
program attempts to terminate by causing all its known threads to use the&lt;br /&gt;
DosExit &amp;quot;terminate this thread&amp;quot; form, the termination may not be successful&lt;br /&gt;
because of such captive threads.&lt;br /&gt;
     Of course, if the last remaining thread of a process calls DosExit&lt;br /&gt;
&amp;quot;terminate this thread,&amp;quot; OS/2 terminates the process.&lt;br /&gt;
&lt;br /&gt;
====5.1.6  Performance Characteristics====&lt;br /&gt;
Threads are intended to be fast and cheap. In OS/2 version 1.0, each&lt;br /&gt;
additional thread that is created consumes about 1200 bytes of memory&lt;br /&gt;
inside the OS/2 kernel for its kernel mode stack. This is in addition to&lt;br /&gt;
the 2048 bytes of user mode stack space that we recommend you provide from&lt;br /&gt;
the process's data area. Terminating a thread does not release the kernel&lt;br /&gt;
stack memory, but subsequently creating another thread reuses this memory.&lt;br /&gt;
In other words, the system memory that a process's threads consume is the&lt;br /&gt;
maximum number of threads simultaneously alive times 1200 bytes. This&lt;br /&gt;
figure is exclusive of each thread's stack, which is provided by the&lt;br /&gt;
process from its own memory.&lt;br /&gt;
     The time needed to create a new thread depends on the process's&lt;br /&gt;
previous thread behavior. Creating a thread that will reuse the internal&lt;br /&gt;
memory area created for a previous thread that has terminated takes&lt;br /&gt;
approximately 3 milliseconds.&lt;br /&gt;
2. All timings in this book refer to a 6 mHz IBM AT with one&lt;br /&gt;
wait-state memory. This represents a worst case performance level.&lt;br /&gt;
2 A request to create a new thread that&lt;br /&gt;
extends the process's &amp;quot;thread count high-water mark&amp;quot; requires an internal&lt;br /&gt;
memory allocation operation. This operation may trigger a memory compaction&lt;br /&gt;
or even a segment swapout, so its time cannot be accurately predicted.&lt;br /&gt;
     It takes about 1 millisecond for the system to begin running an&lt;br /&gt;
unblocked thread. In other words, if a lower-priority thread releases a RAM&lt;br /&gt;
semaphore that is being waited on by a higher-priority thread,&lt;br /&gt;
approximately 1 millisecond passes between the lower-priority thread's call&lt;br /&gt;
to release the semaphore and the return of the higher-priority thread from&lt;br /&gt;
its DosSemRequest call.&lt;br /&gt;
     Threads are a key feature of OS/2; they will receive strong support in&lt;br /&gt;
future versions of OS/2 and will play an increasingly important&lt;br /&gt;
architectural role. You can, therefore, expect thread costs and performance&lt;br /&gt;
to be the same or to improve in future releases.&lt;br /&gt;
&lt;br /&gt;
===5.2  Scheduler/Priorities===&lt;br /&gt;
&lt;br /&gt;
A typical running OS/2 system contains a lot of threads. Frequently,&lt;br /&gt;
several threads are ready to execute at any one time. The OS/2 scheduler&lt;br /&gt;
decides which thread to run next and how long to run it before assigning&lt;br /&gt;
the CPU to another thread. OS/2's scheduler is a priority-based&lt;br /&gt;
scheduler; it assigns each thread a priority and then uses that priority to&lt;br /&gt;
decide which thread to run. The OS/2 scheduler is also a preemptive&lt;br /&gt;
scheduler. If a higher-priority thread is ready to execute, OS/2 does not&lt;br /&gt;
wait for the lower-priority thread to finish with the CPU before&lt;br /&gt;
reassigning the CPU; the lower-priority thread is preempted--the CPU is&lt;br /&gt;
summarily yanked away. Naturally, the state of the preempted thread is&lt;br /&gt;
recorded so that its execution can resume later without ill effect.&lt;br /&gt;
     The scheduler's dispatch algorithm is very straightforward: It&lt;br /&gt;
executes the highest-priority runnable thread for as long as the thread&lt;br /&gt;
wants the CPU. When that thread gives up the CPU--perhaps by waiting for an&lt;br /&gt;
I/O operation--that thread is no longer runnable, and the scheduler&lt;br /&gt;
executes the thread with the highest priority that is runnable. If a&lt;br /&gt;
blocked thread becomes runnable and it has a higher priority than the&lt;br /&gt;
thread currently running, the CPU is immediately preempted and assigned to&lt;br /&gt;
the higher-priority thread. In summary, the CPU is always running the&lt;br /&gt;
highest-priority runnable thread.&lt;br /&gt;
     The scheduler's dispatcher is simplicity itself: It's blindly priority&lt;br /&gt;
based. Although the usual focus for OS/2 activities is the process--a&lt;br /&gt;
process lives, dies, opens files, and so on--the scheduler components of&lt;br /&gt;
OS/2 know little about processes. Because the thread is the dispatchable&lt;br /&gt;
entity, the scheduler is primarily thread oriented. If you're not used to&lt;br /&gt;
thinking in terms of threads, you can mentally substitute the word process&lt;br /&gt;
for the word thread in the following discussion. In practice, all of a&lt;br /&gt;
process's threads typically share the same priority, so it's not too&lt;br /&gt;
inaccurate to view the system as being made up of processes that compete&lt;br /&gt;
for CPU resources.&lt;br /&gt;
     In OS/2 threads are classified and run in three categories: general&lt;br /&gt;
priority, time-critical priority, and low priority. These categories are&lt;br /&gt;
further divided into subcategories. Figure 5-1 shows the relationship of&lt;br /&gt;
the three priority categories and their subcategories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   High&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³        ³            /ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³        ³          /  ³   Foreground   ³&lt;br /&gt;
³ Force  ³        /    ³     screen     ³&lt;br /&gt;
³  run   ³      /      ³     group      ³&lt;br /&gt;
³        ³    /        ³                ³&lt;br /&gt;
³        ³  /          ³  interactive   ³&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄ´/            ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
³        ³             ³   Foreground   ³&lt;br /&gt;
³        ³             ³     screen     ³&lt;br /&gt;
³ Normal ³             ³     group      ³&lt;br /&gt;
³        ³             ³                ³&lt;br /&gt;
³        ³             ³ noninteractive ³&lt;br /&gt;
³        ³             ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄ´\            ³                ³&lt;br /&gt;
³        ³  \          ³   Background   ³&lt;br /&gt;
³        ³    \        ³     screen     ³&lt;br /&gt;
³  Idle  ³      \      ³     group      ³&lt;br /&gt;
³  time  ³        \    ³                ³&lt;br /&gt;
³        ³          \  ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³        ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
   Low&lt;br /&gt;
&lt;br /&gt;
Figure 5-1.  Priority categories.&lt;br /&gt;
&lt;br /&gt;
====5.2.1  General Priority Category====&lt;br /&gt;
The majority of threads in the system run in the general priority category&lt;br /&gt;
and belong to one of three subcategories: background, foreground, or&lt;br /&gt;
interactive. To a limited extent, OS/2 dynamically modifies the priorities&lt;br /&gt;
of threads in the general priority category.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.1  Background Subcategory&lt;br /&gt;
The purpose of the OS/2 priority design is to optimize response rather than&lt;br /&gt;
throughput. In other words, the system is not concerned about ensuring that&lt;br /&gt;
all runnable threads get at least some CPU time, and the system is not&lt;br /&gt;
primarily concerned about trying to keep the disks busy when the highest-&lt;br /&gt;
priority thread is compute bound. Instead, OS/2 is concerned about keeping&lt;br /&gt;
less important work from delaying or slowing more important work. This is&lt;br /&gt;
the reason for the background subcategory. The word background has been&lt;br /&gt;
used in many different ways to describe how tasks are performed in many&lt;br /&gt;
operating systems; we use the word to indicate processes that are&lt;br /&gt;
associated with a screen group not currently being displayed.&lt;br /&gt;
     For example, a user is working with a word-processing program but then&lt;br /&gt;
switches from that program to a spreadsheet program. The word-processing&lt;br /&gt;
program becomes background, and the spreadsheet program is promoted from&lt;br /&gt;
background to foreground. When the user selects different screen groups,&lt;br /&gt;
threads change from foreground to background or background to foreground.&lt;br /&gt;
Background threads have the lowest priority in the general priority&lt;br /&gt;
category. Background applications get the CPU (and, through it, the disks)&lt;br /&gt;
only when all foreground threads are idle. As soon as a foreground thread&lt;br /&gt;
is runnable, the CPU is preempted from the background thread. Background&lt;br /&gt;
threads can use leftover machine time, but they can never compete with&lt;br /&gt;
foreground threads.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.2  Foreground and Interactive Subcategories&lt;br /&gt;
All processes associated with the currently active screen group are made&lt;br /&gt;
members of the foreground subcategory. The process that is currently&lt;br /&gt;
interacting with the keyboard is promoted to the interactive subcategory.&lt;br /&gt;
This ensures that the user will get the fastest possible response to a&lt;br /&gt;
command. When the interactive process's threads release the CPU (via&lt;br /&gt;
blocking on some OS/2 call), the noninteractive foreground threads get the&lt;br /&gt;
next crack at it because those threads are usually doing work on behalf of&lt;br /&gt;
the interactive process or work that is in some way related. If no&lt;br /&gt;
foreground thread needs the CPU, background threads may run.&lt;br /&gt;
     Although the scheduler concerns itself with threads rather than&lt;br /&gt;
processes, it's processes that switch between categories--foreground,&lt;br /&gt;
background, and interactive. When a process changes category--for example,&lt;br /&gt;
when a process shows itself to be in the interactive subcategory by doing&lt;br /&gt;
keyboard I/O--the priorities of all its threads are adjusted&lt;br /&gt;
appropriately.&lt;br /&gt;
     Because background threads are the &amp;quot;low men on the totem pole&amp;quot; that is&lt;br /&gt;
composed of quite a few threads, it may seem that they'll never get to run.&lt;br /&gt;
This isn't the case, though, over a long enough period of time. Yes, a&lt;br /&gt;
background thread can be totally starved for CPU time during a 5-second&lt;br /&gt;
interval, but it would be very rare if it received no service during a 1-&lt;br /&gt;
minute interval. Interactive application commands that take more than a few&lt;br /&gt;
seconds of CPU time are rare. Commands involving disk transfer may take&lt;br /&gt;
longer, but the CPU is available for lower-priority threads while the&lt;br /&gt;
interactive process is waiting for disk operations. Finally, a user rarely&lt;br /&gt;
keeps an interactive application fully busy; the normal &amp;quot;type, look, and&lt;br /&gt;
think&amp;quot; cycle has lots of spare time in it for background threads to&lt;br /&gt;
run.&lt;br /&gt;
     But how does this apply to the presentation manager? The presentation&lt;br /&gt;
manager runs many independent interactive tasks within the same screen&lt;br /&gt;
group, so are they all foreground threads? How does OS/2 know which is the&lt;br /&gt;
interactive process? The answer is that the presentation manager advises&lt;br /&gt;
the scheduler. When the presentation manager screen group is displayed, all&lt;br /&gt;
threads within that screen group are placed in the foreground category.&lt;br /&gt;
When the user selects a particular window to receive keyboard or mouse&lt;br /&gt;
events, the presentation manager tells the scheduler that the process using&lt;br /&gt;
that window is now the interactive process. As a result, the system's&lt;br /&gt;
interactive performance is preserved in the presentation manager's screen&lt;br /&gt;
group.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.3  Throughput Balancing&lt;br /&gt;
We mentioned that some operating systems try to optimize system throughput&lt;br /&gt;
by trying to run CPU-bound and I/O-bound applications at the same time. The&lt;br /&gt;
theory is that the I/O-bound application ties up the disk but needs little&lt;br /&gt;
CPU time, so the disk work can be gotten out of the way while the CPU is&lt;br /&gt;
running the CPU-bound task. If the disk thread has the higher priority, the&lt;br /&gt;
tasks run in tandem. Each time the disk operation is completed, the I/O-&lt;br /&gt;
bound thread regains the CPU and issues another disk operation. Leftover&lt;br /&gt;
CPU time goes to the CPU-bound task that, in this case, has a lower&lt;br /&gt;
priority.&lt;br /&gt;
     This won't work, however, if the CPU-bound thread has a higher&lt;br /&gt;
priority than the I/O-bound thread. The CPU-bound thread will tend to hold&lt;br /&gt;
the CPU, and the I/O-bound thread won't get even the small amount of CPU&lt;br /&gt;
time that it needs to issue another I/O request. Traditionally, schedulers&lt;br /&gt;
have been designed to deal with this problem by boosting the priority of&lt;br /&gt;
I/O-bound tasks and lowering the priority of CPU-bound tasks so that,&lt;br /&gt;
eventually, the I/O-bound thread gets enough service to make its I/O&lt;br /&gt;
requests.&lt;br /&gt;
     The OS/2 scheduler incorporates this design to a limited extent. Each&lt;br /&gt;
time a thread issues a system call that blocks, the scheduler looks at the&lt;br /&gt;
period between the time the CPU was assigned to the thread and the time the&lt;br /&gt;
thread blocked itself with a system call.&lt;br /&gt;
3. We use &amp;quot;blocking&amp;quot; rather than &amp;quot;requesting an I/O operation&amp;quot; as&lt;br /&gt;
a test of I/O boundedness because nearly all blocking operations&lt;br /&gt;
wait for I/O. If a thread's data were all in the buffer cache,&lt;br /&gt;
the thread could issue many I/O requests and still be compute&lt;br /&gt;
bound. In other words, when we speak of I/O-bound threads, we&lt;br /&gt;
really mean device bound--not I/O request bound.&lt;br /&gt;
3 If that period of time is short,&lt;br /&gt;
the thread is considered I/O bound, and its priority receives a small&lt;br /&gt;
increment. If a thread is truly I/O bound, it soon receives several such&lt;br /&gt;
increments and, thus, a modest priority promotion. On the other hand, if&lt;br /&gt;
the thread held the CPU for a longer period of time, it is considered CPU&lt;br /&gt;
bound, and its priority receives a small decrement.&lt;br /&gt;
     The I/O boundedness priority adjustment is small. No background&lt;br /&gt;
thread, no matter how I/O bound, can have its priority raised to the point&lt;br /&gt;
where it has a higher priority than any foreground thread, no matter how&lt;br /&gt;
CPU bound. This throughput enhancing optimization applies only to &amp;quot;peer&amp;quot;&lt;br /&gt;
threads--threads with similar priorities. For example, the threads of a&lt;br /&gt;
single process generally have the same base priority, so this adjustment&lt;br /&gt;
helps optimize the throughput of that process.&lt;br /&gt;
&lt;br /&gt;
====5.2.2  Time-Critical Priority Category====&lt;br /&gt;
Foreground threads, particularly interactive foreground threads, receive&lt;br /&gt;
CPU service whenever they want it. Noninteractive foreground threads and,&lt;br /&gt;
particularly, background threads may not receive any CPU time for periods&lt;br /&gt;
of arbitrary length. This approach improves system response, but it's not&lt;br /&gt;
always a good thing. For example, you may be running a network or a&lt;br /&gt;
telecommunications application that drops its connection if it can't&lt;br /&gt;
respond to incoming packets in a timely fashion. Also, you may want to make&lt;br /&gt;
an exception to the principle of &amp;quot;response, not throughput&amp;quot; when it comes&lt;br /&gt;
to printers. Most printers are much slower than their users would like, and&lt;br /&gt;
most printer spooler programs require little in the way of CPU time; so the&lt;br /&gt;
OS/2 print spooler (the program that prints queued output on the printer)&lt;br /&gt;
would like to run at a high priority to keep the printer busy.&lt;br /&gt;
     Time-critical applications are so called because the ability to run in&lt;br /&gt;
a timely fashion is critical to their well-being. Time-critical&lt;br /&gt;
applications may or may not be interactive, and they may be in the&lt;br /&gt;
foreground or in a background screen group, but this should not affect&lt;br /&gt;
their high priority. The OS/2 scheduler contains a time-critical priority&lt;br /&gt;
category to deal with time-critical applications. A thread running in this&lt;br /&gt;
priority category has a higher priority than any non-time-critical thread&lt;br /&gt;
in the system, including interactive threads. Unlike priorities in the&lt;br /&gt;
general category, a time-critical priority is never adjusted; once given a&lt;br /&gt;
time-critical priority, a thread's priority remains fixed until a system&lt;br /&gt;
call changes it.&lt;br /&gt;
     Naturally, time-critical threads should consume only modest amounts of&lt;br /&gt;
CPU time. If an application has a time-critical thread that consumes&lt;br /&gt;
considerable CPU time--say, more than 20 percent--the foreground&lt;br /&gt;
interactive application will be noticeably slowed or even momentarily&lt;br /&gt;
stopped. System usability is severely affected when the interactive&lt;br /&gt;
application can't get service. The screen output stutters and stumbles,&lt;br /&gt;
characters are dropped when commands are typed, and, in general, the&lt;br /&gt;
computer becomes unusable.&lt;br /&gt;
     Not all threads in a process have to be of the same priority. An&lt;br /&gt;
application may need time-critical response for only some of its work; the&lt;br /&gt;
other work can run at a normal priority. For example, in a&lt;br /&gt;
telecommunications program a &amp;quot;receive incoming data&amp;quot; thread might run at a&lt;br /&gt;
time-critical priority but queue messages in memory for processing by a&lt;br /&gt;
normal-priority thread. If the time-critical thread finds that the normal-&lt;br /&gt;
priority thread has fallen behind, it can send a &amp;quot;wait for me&amp;quot; message to&lt;br /&gt;
the sending program.&lt;br /&gt;
     We strongly recommend that processes that use monitors run the monitor&lt;br /&gt;
thread, and only the monitor thread, at a time-critical priority. This&lt;br /&gt;
prevents delayed device response because of delays in processing the&lt;br /&gt;
monitor data stream. See 16.1 Device Monitors for more information.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.3  Low Priority Category&lt;br /&gt;
If you picture the general priority category as a range of priorities, with&lt;br /&gt;
the force run priority category as a higher range, there is a third range,&lt;br /&gt;
called the low priority category, that is lower in priority than the&lt;br /&gt;
general priority category. As a result, threads in this category get CPU&lt;br /&gt;
service only when no other thread in the other categories needs it. This&lt;br /&gt;
category is a mirror image of the time-critical priority category in that&lt;br /&gt;
the system call that sets the thread fixes the priority; OS/2 never changes&lt;br /&gt;
a low priority.&lt;br /&gt;
     I don't expect the low priority category to be particularly popular.&lt;br /&gt;
It's in the system primarily because it falls out for free, as a mirror&lt;br /&gt;
image of the time-critical category. Turnkey systems may want to run some&lt;br /&gt;
housekeeping processes at this priority. Some users enjoy computing PI,&lt;br /&gt;
doing cryptographic analysis, or displaying fractal images; these&lt;br /&gt;
recreations are good candidates for soaking up leftover CPU time. On a more&lt;br /&gt;
practical level, you could run a program that counts seconds of CPU time&lt;br /&gt;
and yields a histogram of CPU utilization during the course of a day.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.4  Setting Process/Thread Priorities&lt;br /&gt;
We've discussed at some length the effect of the various priorities, but we&lt;br /&gt;
haven't discussed how to set these priorities. Because inheritance is an&lt;br /&gt;
important OS/2 concept, how does a parent's priority affect that of the&lt;br /&gt;
child? Finally, although we said that priority is a thread issue rather&lt;br /&gt;
than a process one, we kept bringing up processes anyway. How does all this&lt;br /&gt;
work?&lt;br /&gt;
     Currently, whenever a thread is created, it inherits the priority of&lt;br /&gt;
its creator thread. In the case of DosCreateThread, the thread making the&lt;br /&gt;
call is the creator thread. In the case of thread 1, the thread in the&lt;br /&gt;
parent process that is making the DosExecPgm call is the creator thread.&lt;br /&gt;
When a process makes a DosSetPrty call to change the priority of one of its&lt;br /&gt;
own threads, the new priority always takes effect. When a process uses&lt;br /&gt;
DosSetPrty to change the priority of another process, only the threads in&lt;br /&gt;
that other process which have not had their priorities explicitly set from&lt;br /&gt;
within their own process are changed. This prevents a parent process from&lt;br /&gt;
inadvertently lowering the priority of, say, a time-critical thread by&lt;br /&gt;
changing the base priority of a child process.&lt;br /&gt;
     In a future release, we expect to improve this algorithm so that each&lt;br /&gt;
process has a base priority. A new thread will inherit its creator's base&lt;br /&gt;
priority. A process's thread priorities that are in the general priority&lt;br /&gt;
category will all be relative to the process's base priority so that a&lt;br /&gt;
change in the base priority will raise or lower the priority of all the&lt;br /&gt;
process's general threads while retaining their relative priority&lt;br /&gt;
relationships. Threads in the time-critical and low priority categories&lt;br /&gt;
will continue to be unaffected by their process's base priority.&lt;br /&gt;
&lt;br /&gt;
==6  The User Interface==&lt;br /&gt;
&lt;br /&gt;
OS/2 contains several important subsystems: the file system, the memory&lt;br /&gt;
management subsystem, the multitasking subsystem, and the user interface&lt;br /&gt;
subsystem--the presentation manager. MS-DOS does not define or support a&lt;br /&gt;
user interface subsystem; each application must provide its own. MS-DOS&lt;br /&gt;
utilities use a primitive line-oriented interface, essentially unchanged&lt;br /&gt;
from the interface provided by systems designed to interface with TTYs.&lt;br /&gt;
     OS/2 is intended to be a graphics-oriented operating system, and as&lt;br /&gt;
such it needs to provide a standard graphical user interface (GUI)&lt;br /&gt;
subsystem--for several reasons. First, because such systems are complex to&lt;br /&gt;
create, to expect that each application provide its own is unreasonable.&lt;br /&gt;
Second, a major benefit of a graphical user interface is that applications&lt;br /&gt;
can be intermingled. For example, their output windows can share the&lt;br /&gt;
screen, and the user can transfer data between applications using visual&lt;br /&gt;
metaphors. If each application had its own GUI package, such sharing would&lt;br /&gt;
be impossible. Third, a graphical user interface is supposed to make the&lt;br /&gt;
machine easier to use, but this will be so only if the user can learn one&lt;br /&gt;
interface that will work with all applications.&lt;br /&gt;
&lt;br /&gt;
===6.1  VIO User Interface===&lt;br /&gt;
&lt;br /&gt;
The full graphical user interface subsystem will not ship with OS/2 version&lt;br /&gt;
1.0, so the initial release will contain the character-oriented VIO/KBD/MOU&lt;br /&gt;
subsystem (see Chapter 13, The Presentation Manager and VIO, for more&lt;br /&gt;
details). Although VIO doesn't provide any graphical services, it does&lt;br /&gt;
allow applications to sidestep VIO and construct their own. The VIO&lt;br /&gt;
screen group interface is straightforward. When the machine is booted up,&lt;br /&gt;
the screen displays the screen group list. The user can select an existing&lt;br /&gt;
screen group or create a new one. From within a screen group, the user can&lt;br /&gt;
type a magic key sequence to return the screen to the screen group list.&lt;br /&gt;
Another magic key sequence allows the user to toggle through all existing&lt;br /&gt;
screen groups. One screen group is identified in the screen group list as&lt;br /&gt;
the real mode screen group.&lt;br /&gt;
&lt;br /&gt;
===6.2  The Presentation Manager User Interface===&lt;br /&gt;
&lt;br /&gt;
The OS/2 presentation manager is a powerful and flexible graphical user&lt;br /&gt;
interface. It supports such features as windowing, drop-down and pop-up&lt;br /&gt;
menus, and scroll bars. It works best with a graphical pointing device such&lt;br /&gt;
as a mouse, but it can be controlled exclusively from the keyboard.&lt;br /&gt;
     The presentation manager employs screen windows to allow multiple&lt;br /&gt;
applications to use the screen and keyboard simultaneously. Each&lt;br /&gt;
application uses one or more windows to display its information; the user&lt;br /&gt;
can size and position each window, overlapping some and perhaps shrinking&lt;br /&gt;
others to icons. Mouse and keyboard commands change the input focus between&lt;br /&gt;
windows; this allows the presentation manager to route keystrokes and mouse&lt;br /&gt;
events to the proper application.&lt;br /&gt;
     Because of its windowing capability, the presentation manager doesn't&lt;br /&gt;
need to use the underlying OS/2 screen group mechanism to allow the user to&lt;br /&gt;
switch between running applications. The user starts an application by&lt;br /&gt;
pointing to its name on a menu display; for most applications the&lt;br /&gt;
presentation manager creates a new window and assigns it to the new&lt;br /&gt;
process. Some applications may decline to use the presentation manager's&lt;br /&gt;
graphical user interface and prefer to take direct control of the display.&lt;br /&gt;
When such an application is initiated, the presentation manager creates a&lt;br /&gt;
private screen group for it and switches to that screen group. The user can&lt;br /&gt;
switch away by entering a special key sequence that brings up a menu which&lt;br /&gt;
allows the user to select any running program. If the selected program is&lt;br /&gt;
using the standard presentation manager GUI, the screen is switched to the&lt;br /&gt;
screen group shared by those programs. Otherwise, the screen is switched to&lt;br /&gt;
the private screen group that the specified application is using.&lt;br /&gt;
     To summarize, only the real mode application and applications that&lt;br /&gt;
take direct control of the display hardware need to run in their own screen&lt;br /&gt;
groups. The presentation manager runs all other processes in a single&lt;br /&gt;
screen group and uses its windowing facilities to share the screen among&lt;br /&gt;
them. The user can switch between applications via a special menu; if both&lt;br /&gt;
the previous and the new application are using the standard interface, the&lt;br /&gt;
user can switch the focus directly without going though the menu.&lt;br /&gt;
&lt;br /&gt;
===6.3  Presentation Manager and VIO Compatibility===&lt;br /&gt;
&lt;br /&gt;
In OS/2 version 1.1 and in all subsequent releases, the presentation&lt;br /&gt;
manager will replace and superset the VIO interface. Applications that use&lt;br /&gt;
the character mode VIO interface will continue to work properly as&lt;br /&gt;
windowable presentation manager applications, as will applications that use&lt;br /&gt;
the STDIN and STDOUT file handles for interactive I/O. Applications that&lt;br /&gt;
use the VIO interface to obtain direct access to the graphical display&lt;br /&gt;
hardware will also be supported; as described above, the presentation&lt;br /&gt;
manager will run such applications in their own screen group.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==7  Dynamic Linking==&lt;br /&gt;
&lt;br /&gt;
A central component of OS/2 is dynamic linking. Dynamic links play several&lt;br /&gt;
critical architectural roles. Before we can discuss them at such an&lt;br /&gt;
abstract level, however, we need to understand the nuts and bolts of their&lt;br /&gt;
workings.&lt;br /&gt;
&lt;br /&gt;
===7.1  Static Linking===&lt;br /&gt;
&lt;br /&gt;
A good preliminary to the study of dynamic links (called dynlinks, for&lt;br /&gt;
short) is a review of their relative, static links. Every programmer who&lt;br /&gt;
has gone beyond interpreter-based languages such as BASIC is familiar with&lt;br /&gt;
static links. You code a subroutine or a procedure call to a routine that&lt;br /&gt;
is not present in that compiland (or source file), which we'll call Foo.&lt;br /&gt;
The missing routine is declared external so that the assembler or compiler&lt;br /&gt;
doesn't flag it as an undefined symbol. At linktime, you present the linker&lt;br /&gt;
with the .OBJ file that you created from your compiland, and you also&lt;br /&gt;
provide a .OBJ file&lt;br /&gt;
1. Or a .LIB library file that contains the .OBJ file as a part of it.&lt;br /&gt;
1 that contains the missing routine Foo. The linker&lt;br /&gt;
combines the compilands into a final executable image--the .EXE file--that&lt;br /&gt;
contains the routine Foo as well as the routines that call it. During the&lt;br /&gt;
combination process, the linker adjusts the calls to Foo, which had been&lt;br /&gt;
undefined external references, to point to the place in the .EXE file where&lt;br /&gt;
the linker relocated the Foo routine. This process is diagramed in Figure&lt;br /&gt;
7-1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      .OBJ                            .LIB&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³               ³               ³  Foo  ÄÄÄÄÄÄ  ³&lt;br /&gt;
³   Call Foo    ³               ³       ÄÄÄÄÄÄ  ³&lt;br /&gt;
³               ³               ³       ÄÄÄÄÄÄ  ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ               ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ      .EXE file&lt;br /&gt;
        ³                               ³          ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ÀÄÄÄÄÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÙ          ³               ³&lt;br /&gt;
                  ³     +     ³                    ³   Call �ÄÄÄÄÄÄÅÄ¿&lt;br /&gt;
                ÚÄ�ÄÄÄÄÄÄÄÄÄÄÄ�Ä¿                  ³               ³ ³&lt;br /&gt;
                ³               ³                  ³               ³ ³&lt;br /&gt;
                ³    Linker     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³               ³ ³&lt;br /&gt;
                ³               ³                  ³               ³ ³&lt;br /&gt;
                ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ                  ³               ³ ³&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ �ÄÄÄÄÄÄÅÄÙ&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ        ³&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ        ³&lt;br /&gt;
                                                   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-1.  Static linking.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     In other words, with static linking you can write a program in pieces.&lt;br /&gt;
You can compile one piece at a time by having it refer to the other pieces&lt;br /&gt;
as externals. A program called a linker or a link editor combines these&lt;br /&gt;
pieces into one final .EXE image, fixing up the external references (that&lt;br /&gt;
is, references between one piece and another) that those pieces contain.&lt;br /&gt;
     Writing and compiling your program piecemeal is useful, but the&lt;br /&gt;
primary advantage of static linking is that you can use it to reference a&lt;br /&gt;
standard set of subroutines--a subroutine library--without compiling or&lt;br /&gt;
even possessing the source code for those subroutines. Nearly all high-&lt;br /&gt;
level language packages come with one or more standard runtime libraries&lt;br /&gt;
that contain various useful subroutines that the compiler can call&lt;br /&gt;
implicitly and that the programmer can call explicitly. Source for these&lt;br /&gt;
runtime libraries is rarely provided; the language supplier provides only&lt;br /&gt;
the .OBJ object files, typically in library format.&lt;br /&gt;
     To summarize, in traditional static linking the target code (that is,&lt;br /&gt;
the external subroutine) must be present at linktime and is built into the&lt;br /&gt;
final .EXE module. This makes the .EXE file larger, naturally, but more&lt;br /&gt;
important, the target code can't be changed or upgraded without relinking&lt;br /&gt;
to the main program's .OBJ files. Because the personal computer field is&lt;br /&gt;
built on commercial software whose authors don't release source or .OBJ&lt;br /&gt;
files, this relinking is out of the question for the typical end user.&lt;br /&gt;
Finally, the target code can't be shared among several (different)&lt;br /&gt;
applications that use the same library routines. This is true for two&lt;br /&gt;
reasons. First, the target code was relocated differently by the linker for&lt;br /&gt;
each client; so although the code remains logically the same for each &lt;br /&gt;
application, the address components of the binary instructions are&lt;br /&gt;
different in each .EXE file. Second, the operating system has no way of&lt;br /&gt;
knowing that these applications are using the same library, and it has no&lt;br /&gt;
way of knowing where that library is in each .EXE file. Therefore, it can't&lt;br /&gt;
avoid having duplicate copies of the library in memory.&lt;br /&gt;
&lt;br /&gt;
===7.2  Loadtime Dynamic Linking===&lt;br /&gt;
&lt;br /&gt;
The mechanical process of loadtime dynamic linking is the same as that of&lt;br /&gt;
static linking. The programmer makes an external reference to a subroutine&lt;br /&gt;
and at linktime specifies a library file (or a .OBJ file) that defines the&lt;br /&gt;
reference. The linker produces a .EXE file that OS/2 then loads and&lt;br /&gt;
executes. Behind the scenes, however, things are very much different.&lt;br /&gt;
     Step 1 is the same for both kinds of linking. The external reference&lt;br /&gt;
is compiled or assembled, resulting in a .OBJ file that contains an&lt;br /&gt;
external reference fixup record. The assembler or compiler doesn't know&lt;br /&gt;
about dynamic links; the .OBJ file that an assembler or a compiler produces&lt;br /&gt;
may be used for static links, dynamic links, or, more frequently, a&lt;br /&gt;
combination of both (some externals become dynamic links, others become&lt;br /&gt;
static links).&lt;br /&gt;
     In static linking, the linker finds the actual externally referenced&lt;br /&gt;
subroutine in the library file. In dynamic linking, the linker finds a&lt;br /&gt;
special record that defines a module name string and an entry point name&lt;br /&gt;
string. For example, in our hypothetical routine Foo, the library file&lt;br /&gt;
contains only these two name strings, not the code for Foo itself. (The&lt;br /&gt;
entry point name string doesn't have to be the name by which programs&lt;br /&gt;
called the routine.) The resultant .EXE file doesn't contain the code for&lt;br /&gt;
Foo; it contains a special dynamic link record that specifies these module&lt;br /&gt;
and entry point names for Foo. This is illustrated in Figure 7-2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿     ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     .OBJ      ³ Extern  ³     .LIB      ³     ³     .EXE      ³&lt;br /&gt;
³               ÃÄÄÄÄÄÄÄÄ�³ Foo:          ³     ³               ³&lt;br /&gt;
³   Call Foo    ³         ³ Module dlpack ³     ³      ÚÄÄÄÄ¿   ³&lt;br /&gt;
³               ³         ³ entry Foo     ³     ³ Call ³????ÃÄÄÄÅÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ         ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ     ³      ÀÄÄÄÄÙ   ³  ³&lt;br /&gt;
        ³                         ³         ÚÄÄ�ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³&lt;br /&gt;
        ³                         ³         ³   ³ Reference to  ³�ÄÙ&lt;br /&gt;
        ÀÄÄÄÄÄÄÄÄÄ�  +  �ÄÄÄÄÄÄÄÄÄÙ         ³   ³ dlpack: Foo   ³&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄ¿                 ³   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                ³  Link   ³                 ³&lt;br /&gt;
                ÀÄÄÄÄÂÄÄÄÄÙ                 ³&lt;br /&gt;
                     ³                      ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-2.  Dynamic linking.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When this .EXE file is run, OS/2 loads the code in the .EXE file into&lt;br /&gt;
memory and discovers the dynamic link record(s). For each dynamic link&lt;br /&gt;
module that is named, OS/2 locates the code in the system's dynamic link&lt;br /&gt;
library directory and loads it into memory (unless the module is already in&lt;br /&gt;
use; see below). The system then links the external references in the&lt;br /&gt;
application to the addresses of the called entry points. This process is&lt;br /&gt;
diagramed in Figure 7-3.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿                                    ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     .EXE      ³                                    ³     .DLL      ³&lt;br /&gt;
³               ³                                    ³  dlpack: Foo  ³&lt;br /&gt;
³     Call ÄÄÄÄÄÅÄÄ¿                                 ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³                                 ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³  dlpack: Foo  ³�ÄÙ Disk                            ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ                                    ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ&lt;br /&gt;
        ³                                                    ³&lt;br /&gt;
        ³                                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
        ³                                 ³&lt;br /&gt;
Ä Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä&lt;br /&gt;
        ³                                 ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄ�ÄÄÄÄÄÄÄ¿                 ÚÄÄÄÄÄÄÄ�ÄÄÄÄÄÄÄ¿&lt;br /&gt;
³               ³      RAM        ³               ³&lt;br /&gt;
³               ³      Fixup      ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³               ³     by OS/2     ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³     Call  ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³               ³                 ³               ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-3.  Loadtime dynlink fixups.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To summarize, instead of linking in the target code at linktime, the linker&lt;br /&gt;
places a module name and an entry point name into the .EXE file. When the&lt;br /&gt;
program is loaded (that is, executed), OS/2 locates the target code, loads&lt;br /&gt;
it, and does the necessary linking. Although all we're doing is postponing&lt;br /&gt;
the linkage until loadtime, this technique has several important&lt;br /&gt;
ramifications. First, the target code is not in the .EXE file but in a&lt;br /&gt;
separate dynamic link library (.DLL) file. Thus, the .EXE file is smaller&lt;br /&gt;
because it contains only the name of the target code, not the code itself.&lt;br /&gt;
You can change or upgrade the target code at any time simply by replacing&lt;br /&gt;
this .DLL file. The next time a referencing application is loaded,&lt;br /&gt;
2. With some restrictions. See 7.11.2 Dynlink Life, Death, and Sharing.&lt;br /&gt;
2 it is&lt;br /&gt;
linked to the new version of the target code. Finally, having the target&lt;br /&gt;
code in a .DLL file paves the way for automatic code sharing. OS/2 can&lt;br /&gt;
easily understand that two applications are using the same dynlink code&lt;br /&gt;
because it loaded and linked that code, and it can use this knowledge to&lt;br /&gt;
share the pure segments of that dynlink package rather than loading&lt;br /&gt;
duplicate copies.&lt;br /&gt;
     A final advantage of dynamic linking is that it's totally invisible to&lt;br /&gt;
the user, and it can even be invisible to the programmer. You need to&lt;br /&gt;
understand dynamic linking to create a dynamic link module, but you can use&lt;br /&gt;
one without even knowing that it's not an ordinary static link. The one&lt;br /&gt;
disadvantage of dynamic linking is that programs sometimes take longer to&lt;br /&gt;
load into memory than do those linked with static linking. The good news&lt;br /&gt;
about dynamic linking is that the target code(s) are separate from the main&lt;br /&gt;
.EXE file; this is also the bad news. Because the target code(s) are&lt;br /&gt;
separate from the main .EXE file, a few more disk operations may be&lt;br /&gt;
necessary to load them.&lt;br /&gt;
     The actual performance ramifications depend on the kind of dynlink&lt;br /&gt;
module that is referenced and whether this .EXE file is the first to&lt;br /&gt;
reference the module. This is discussed in more detail in 7.11&lt;br /&gt;
Implementation Details.&lt;br /&gt;
     Although this discussion has concentrated on processes calling dynlink&lt;br /&gt;
routines, dynlink routines can in fact be called by other dynlink routines.&lt;br /&gt;
When OS/2 loads a dynlink routine in response to a process's request, it&lt;br /&gt;
examines that routine to see if it has any dynlink references of its own.&lt;br /&gt;
Any such referenced dynlink routines are also loaded and so on until no&lt;br /&gt;
unsatisfied dynlink references remain.&lt;br /&gt;
&lt;br /&gt;
===7.3  Runtime Dynamic Linking===&lt;br /&gt;
&lt;br /&gt;
The dynamic linking that we have been describing is called load-time&lt;br /&gt;
dynamic linking because it occurs when the .EXE file is loaded. All dynamic&lt;br /&gt;
link names need not appear in the .EXE file at loadtime; a process can link&lt;br /&gt;
itself to a dynlink package at runtime as well. Runtime dynamic linking&lt;br /&gt;
works exactly like loadtime dynamic linking except that the process creates&lt;br /&gt;
the dynlink module and entry point names at runtime and then passes them to&lt;br /&gt;
OS/2 so that OS/2 can locate and load the specified dynlink code.&lt;br /&gt;
     Runtime linking takes place in four steps.&lt;br /&gt;
&lt;br /&gt;
     1.  The process issues a DosLoadModule call to tell OS/2 to locate and&lt;br /&gt;
         load the dynlink code into memory.&lt;br /&gt;
&lt;br /&gt;
     2.  The DosGetProcAddr call is used to obtain the addresses of the&lt;br /&gt;
         routines that the process wants to call.&lt;br /&gt;
&lt;br /&gt;
     3.  The process calls the dynlink library entry points by means of an&lt;br /&gt;
         indirect call through the address returned by DosGetProcAddr.&lt;br /&gt;
&lt;br /&gt;
     4.  When the process has no more use for the dynlink code, it can call&lt;br /&gt;
         DosFreeModule to release the dynlink code. After this call, the&lt;br /&gt;
         process will still have the addresses returned by DosGetProcAddr,&lt;br /&gt;
         but they will be illegal addresses; referencing them will cause a&lt;br /&gt;
         GP fault.&lt;br /&gt;
&lt;br /&gt;
     Runtime dynamic links are useful when a program knows that it will&lt;br /&gt;
want to call some dynlink routines but doesn't know which ones. For&lt;br /&gt;
example, a charting program may support four plotters, and it may want to&lt;br /&gt;
use dynlink plotter driver packages. It doesn't make sense for the&lt;br /&gt;
application to contain loadtime dynamic links to all four plotters because&lt;br /&gt;
only one will be used and the others will take up memory and swap space.&lt;br /&gt;
Instead, the charting program can wait until it learns which plotter is&lt;br /&gt;
installed and then use the runtime dynlink facility to load the appropriate&lt;br /&gt;
package. The application need not even call DosLoadModule when it&lt;br /&gt;
initializes; it can wait until the user issues a plot command before it&lt;br /&gt;
calls DosLoadModule, thereby reducing memory demands on the system.&lt;br /&gt;
     The application need not even be able to enumerate all the modules or&lt;br /&gt;
entry points that may be called. The application can learn the names of the&lt;br /&gt;
dynlink modules from another process or by looking in a configuration file.&lt;br /&gt;
This allows the user of our charting program, for example, to install&lt;br /&gt;
additional plotter drivers that didn't even exist at the time that the&lt;br /&gt;
application was written. Of course, in this example the calling sequences&lt;br /&gt;
of the dynlink plotter driver must be standardized, or the programmer must&lt;br /&gt;
devise a way for the application to figure out the proper way to call these&lt;br /&gt;
newly found routines.&lt;br /&gt;
     Naturally, a process is not limited to one runtime dynlink module;&lt;br /&gt;
multiple calls to DosLoadModule can be used to link to several dynlink&lt;br /&gt;
modules simultaneously. Regardless of the number of modules in use,&lt;br /&gt;
DosFreeModule should be used if the dynlink module will no longer be used&lt;br /&gt;
and the process intends to continue executing. Issuing DosFreeModules is&lt;br /&gt;
unnecessary if the process is about to terminate; OS/2 releases all dynlink&lt;br /&gt;
modules at process termination time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.4  Dynlinks, Processes, and Threads&lt;br /&gt;
&lt;br /&gt;
Simply put, OS/2 views dynlinks as a fancy subroutine package. Dynlinks&lt;br /&gt;
aren't processes, and they don't own any resources. A dynlink executes only&lt;br /&gt;
because a thread belonging to a client process called the dynlink code. The&lt;br /&gt;
dynlink code is executing as the client thread and process because, in the&lt;br /&gt;
eyes of the system, the dynlink is merely a subroutine that process has&lt;br /&gt;
called. Before the client process can call a dynlink package, OS/2 ensures&lt;br /&gt;
that the dynlink's segments are in the address space of the client. No ring&lt;br /&gt;
transition or context switching overhead occurs when a client calls a&lt;br /&gt;
dynlink routine; the far call to a dynlink entry point is just that--an&lt;br /&gt;
ordinary far call to a subroutine in the process's address space.&lt;br /&gt;
     One side effect is that dynlink calls are very fast; little CPU time&lt;br /&gt;
is spent getting to the dynlink package. Another side effect is no&lt;br /&gt;
separation between a client's segments and a dynlink package's segments&lt;br /&gt;
3. Subsystem dynlink packages may be sensitive to this. For&lt;br /&gt;
detailed information, see 7.11.1 Dynlink Data Security.&lt;br /&gt;
3&lt;br /&gt;
because segments belong to processes and only one process is running both&lt;br /&gt;
the client and the dynlink code. The same goes for file handles,&lt;br /&gt;
semaphores, and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5  Data&lt;br /&gt;
&lt;br /&gt;
The careful reader will have noticed something missing in this discussion&lt;br /&gt;
of dynamic linking: We've said nothing about how to handle a dynlink&lt;br /&gt;
routine's data. Subroutines linked with static links have no problem with&lt;br /&gt;
having their own static data; when the linker binds the external code with&lt;br /&gt;
the main code, it sees how much static data the external code needs and&lt;br /&gt;
allocates the necessary space in the proper data segment(s). References&lt;br /&gt;
that the external code makes to its data are then fixed up to point&lt;br /&gt;
to the proper location. Because the linker is combining all the .OBJ&lt;br /&gt;
files into a .EXE file, it can easily divide the static data segment(s)&lt;br /&gt;
among the various compilands.&lt;br /&gt;
     This technique doesn't work for dynamic link routines because their&lt;br /&gt;
code and therefore their data requirements aren't present at linktime. It's&lt;br /&gt;
possible to extend the special dynlink .OBJ file to describe the amount of&lt;br /&gt;
static data that the dynlink package will need, but it won't work.&lt;br /&gt;
4. And even if it did work, it would be a poor design because it&lt;br /&gt;
would restrict our ability to upgrade the dynlink code in the field.&lt;br /&gt;
4&lt;br /&gt;
Because the main code in each application uses different amounts of static&lt;br /&gt;
data, the data area reserved for the dynlink package would end up at a&lt;br /&gt;
different offset in each .EXE file that was built. When these .EXE files&lt;br /&gt;
were executed, the one set of shared dynlink code segments would need to&lt;br /&gt;
reference the data that resides at different addresses for each different&lt;br /&gt;
client. Relocating the static references in all dynlink code modules at&lt;br /&gt;
each occurrence of a context switch is clearly out of the question.&lt;br /&gt;
     An alternative to letting dynamic link routines have their own static&lt;br /&gt;
data is to require that their callers allocate the necessary data areas and&lt;br /&gt;
pass pointers to them upon every call. We easily rejected this scheme: It's&lt;br /&gt;
cumbersome; call statements must be written differently if they're for a&lt;br /&gt;
dynlink routine; and, finally, this hack wouldn't support subsystems, which&lt;br /&gt;
are discussed below.&lt;br /&gt;
     Instead, OS/2 takes advantage of the segmented architecture of the&lt;br /&gt;
80286. Each dynamic link routine can use one or more data segments to hold&lt;br /&gt;
its static data. Each client process has a separate set of these segments.&lt;br /&gt;
Because these segments hold only the dynlink routine's data and none of the&lt;br /&gt;
calling process's data, the offsets of the data items within that segment&lt;br /&gt;
will be the same no matter which client process is calling the dynlink&lt;br /&gt;
code. All we need do to solve our static data addressability problem is&lt;br /&gt;
ensure that the segment selectors of the dynlink routine's static data&lt;br /&gt;
segments are the same for each client process.&lt;br /&gt;
     OS/2 ensures that the dynlink library's segment selectors are the same&lt;br /&gt;
for each client process by means of a technique called the disjoint LDT&lt;br /&gt;
space. I won't attempt a general introduction to the segmented architecture&lt;br /&gt;
of the 80286, but a brief summary is in order. Each process in 80286&lt;br /&gt;
protect mode can have a maximum of 16,383 segments. These segments are&lt;br /&gt;
described in two tables: the LDT (Local Descriptor Table) and the GDT&lt;br /&gt;
(Global Descriptor Table). An application can't read from or write to these&lt;br /&gt;
tables. OS/2 manages them, and the 80286 microprocessor uses their contents&lt;br /&gt;
when a process loads selectors into its segment registers.&lt;br /&gt;
     In practice, the GDT is not used for application segments, which&lt;br /&gt;
leaves the LDT 8192 segments--or, more precisely, 8192 segment selectors,&lt;br /&gt;
which OS/2 can set up to point to memory segments. The 80286 does not&lt;br /&gt;
support efficient position-independent code, so 80286 programs contain&lt;br /&gt;
within them, as part of the instruction stream, the particular segment&lt;br /&gt;
selector needed to access a particular memory location, as well as an&lt;br /&gt;
offset within that segment. This applies to both code and data&lt;br /&gt;
references.&lt;br /&gt;
     When OS/2 loads a program into memory, the .EXE file describes the&lt;br /&gt;
number, type, and size of the program's segments. OS/2 creates these&lt;br /&gt;
segments and allocates a selector for each from the 8192 possible LDT&lt;br /&gt;
selectors. There isn't any conflict with other processes in the system, at&lt;br /&gt;
this point, because each process has its own LDT and its own private set of&lt;br /&gt;
8192 LDT selectors. After OS/2 chooses a selector for each segment, both&lt;br /&gt;
code and data, it uses a table of addresses provided in the .EXE file to&lt;br /&gt;
relocate each segment reference in the program, changing the place holder&lt;br /&gt;
value put there by the linker into the proper segment selector value. OS/2&lt;br /&gt;
never combines or splits segments, so it never has to relocate the offset&lt;br /&gt;
part of addresses, only the segment parts. Address offsets are more common&lt;br /&gt;
than segment references. Because the segment references are relatively few,&lt;br /&gt;
this relocation process is not very time-consuming.&lt;br /&gt;
     If OS/2 discovers that the process that it's loading references a&lt;br /&gt;
dynlink routine--say, our old friend Foo--the situation is more complex.&lt;br /&gt;
For example, suppose that the process isn't the first caller of Foo; Foo is&lt;br /&gt;
already in memory and already relocated to some particular LDT slots in the&lt;br /&gt;
LDT of the earlier client of Foo. OS/2 has to fill in those same slots in&lt;br /&gt;
the new process's LDT with pointers to Foo; it can't assign different LDT&lt;br /&gt;
slots because Foo's code and data have already been relocated to the&lt;br /&gt;
earlier process's slots. If the new process is already using Foo's slot&lt;br /&gt;
numbers for something else, then we are in trouble. This is a problem with&lt;br /&gt;
all of Foo's segments, both data segments and code segments.&lt;br /&gt;
     This is where the disjoint LDT space comes in. OS/2 reserves many of&lt;br /&gt;
each process's LDT slots&lt;br /&gt;
5. In version 1.0, more than half the LDT slots are reserved for&lt;br /&gt;
this disjoint area.&lt;br /&gt;
5 for the disjoint space. The same slot numbers are&lt;br /&gt;
reserved in every process's LDT. When OS/2 allocates an LDT selector for a&lt;br /&gt;
memory segment that may be shared between processes, it allocates an entry&lt;br /&gt;
from the disjoint LDT space. After a selector is allocated, that same slot&lt;br /&gt;
in all other LDTs in the system is reserved. The slot either remains empty&lt;br /&gt;
(that is, invalid) or points to this shared segment; it can have no other&lt;br /&gt;
use. This guarantees that a process that has been running for hours and&lt;br /&gt;
that has created dozens of segments can still call DosLoadModule to get&lt;br /&gt;
access to a dynlink routine; OS/2 will find that the proper slots in this&lt;br /&gt;
process's LDT are ready and waiting. The disjoint LDT space is used for all&lt;br /&gt;
shared memory objects, not just dynlink routines. Shared memory data&lt;br /&gt;
segments are also allocated from the disjoint LDT space. A process's code&lt;br /&gt;
segments are not allocated in the disjoint LDT space, yet they can still be&lt;br /&gt;
shared.&lt;br /&gt;
6. The sharing of pure segments between multiple copies of the&lt;br /&gt;
same program is established when the duplicate copies are loaded.&lt;br /&gt;
OS/2 will use the same selector to do segment mapping as it did&lt;br /&gt;
when it loaded the first copy, so these segments can be shared&lt;br /&gt;
even though their selectors are not in the disjoint space.&lt;br /&gt;
6 Figure 7-4 illustrates the disjoint LDT concept. Bullets in the&lt;br /&gt;
shaded selectors denote reserved but invalid disjoint selectors. These are&lt;br /&gt;
reserved in case that process later requests access to the shared memory&lt;br /&gt;
segments that were assigned those disjoint slots. Only process A is using&lt;br /&gt;
the dynlink package DLX, so its assigned disjoint LDT slots are reserved&lt;br /&gt;
for it in Process B's LDT as well as in the LDT of all other processes in&lt;br /&gt;
the system. Both processes are using the dynlink package DLY.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 Process A                         Process B&lt;br /&gt;
Segment table                     Segment table&lt;br /&gt;
 (LDT) for A                       (LDT) for B&lt;br /&gt;
  ÚÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄ¿ Process    ÚÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³       ÃÄÄÄ´        ³ A's        ³       ³ ÚÄ´        ³ Process&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ segments   ÃÄÄÄÄÄÄÄ´ ³ ³        ³ B's&lt;br /&gt;
  ³°°°°°°°³   ÀÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄ¿ ³°°°°°°°³ ³ ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ÚÄÄÄÄÄÄÄÄÄÄÄÄ´        ³ ÃÄÄÄÄÄÄÄ´ ³            ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³       ³ ³            ³        ³ ³       ÃÄÙ            ³        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³ ÚÄÄÄÄÄÄÄÄ¿ ÀÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄ´    ÚÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ³°°°°°°°³ ³Ú´        ³            ³°°°°°°°³    ³         ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³³³        ³            ÃÄÄÄÄÄÄÄ´    ³&lt;br /&gt;
  ³       ÃÄÙ³ÀÄÄÄÄÄÄÄÄÙ            ³       ÃÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´  ³                      ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°³  ³                      ³°°°°°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´  ³                      ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ÃÄÄÙ                      ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°�°°°³                         ³°°°°°°°ÃÄÄÄ´        ³ Dynlink&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´   ³        ³ DLZ's&lt;br /&gt;
  ³       ³                         ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°�°°°³                         ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´              ³        ³&lt;br /&gt;
  ³       ³                         ³       ³              ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿ Dynlink    ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄ´        ³ DLX's      ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ segments   ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³   ÀÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄ¿ ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´       ÚÄÄÄÄÄÄ´        ³ ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄÄÄÄÄÙ      ³        ³ ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿ ÀÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³ ÚÄ´        ³            ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³ ³        ³            ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÙ ÀÄÄÄÄÄÄÄÄÙ            ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³                         ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°³                         ³°°°°°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³                         ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿            ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄ´        ³ Dynlink    ³°°°°°°°ÃÄÄÄ´        ³ Dynlink&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ DLY's      ÃÄÄÄÄÄÄÄ´   ³        ³ DLY's&lt;br /&gt;
  ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments   ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿ ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³ ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´              ³        ³ ÃÄÄÄÄÄÄÄ´              ³        ³&lt;br /&gt;
  ³       ³              ÀÄÄÄÄÄÄÄÄÙ ³       ³              ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÀÄÄÄÄÄÄÄÙ                         ÀÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-4.  The disjoint LDT space.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5.1  Instance Data&lt;br /&gt;
OS/2 supports two types of data segments for dynlink routines--instance&lt;br /&gt;
and global. Instance data segments hold data specific to each instance of&lt;br /&gt;
the dynlink routine. In other words, a dynlink routine has a separate set&lt;br /&gt;
of instance data segments for each process using it. The dynlink code has&lt;br /&gt;
no difficulty addressing its data; the code can reference the data segment&lt;br /&gt;
selectors as immediate values. The linker and OS/2's loader conspire so&lt;br /&gt;
that the proper selector value is in place when the code executes.&lt;br /&gt;
     The use of instance data segments is nearly invisible both to the&lt;br /&gt;
client process and to the dynlink code. The client process simply calls the&lt;br /&gt;
dynlink routine, totally unaffected by the presence or absence of the&lt;br /&gt;
routine's instance data segment(s). A dynlink routine can even return&lt;br /&gt;
addresses of items in its data segments to the client process. The client&lt;br /&gt;
cannot distinguish between a dynlink routine and a statically linked one.&lt;br /&gt;
Likewise, the code that makes up the dynlink routine doesn't need to do&lt;br /&gt;
anything special to use its instance data segments. The dynlink code was&lt;br /&gt;
assembled or compiled with its static data in one or more segments; the&lt;br /&gt;
code itself references those segments normally. The linker and OS/2 handle&lt;br /&gt;
all details of allocating the disjoint LDT selectors, loading the segments,&lt;br /&gt;
fixing up the references, and so on.&lt;br /&gt;
     A dynlink routine that uses only instance data segments (or no data&lt;br /&gt;
segments at all) can be written as a single client package, as would be a&lt;br /&gt;
statically linked subroutine. Although such a dynlink routine may have&lt;br /&gt;
multiple clients, the presence of multiple clients is invisible to the&lt;br /&gt;
routine itself. Each client has a separate copy of the instance data&lt;br /&gt;
segment(s). When a new client is created, OS/2 loads virgin copies of the&lt;br /&gt;
instance data segments from the .DLL file. The fact that OS/2 is sharing&lt;br /&gt;
the pure code segments of the routine has no effect on the operation of the&lt;br /&gt;
routine itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5.2  Global Data&lt;br /&gt;
The second form of data segment available to a dynlink routine is a global&lt;br /&gt;
data segment. A global data segment, as the name implies, is not duplicated&lt;br /&gt;
for each client process. There is only one copy of each dynlink module's&lt;br /&gt;
global data segment(s); each client process is given shared access to that&lt;br /&gt;
segment. The segment is loaded only once--when the dynlink package is first&lt;br /&gt;
brought into memory to be linked with its first client process. Global data&lt;br /&gt;
segments allow a dynlink routine to be explicitly aware of its multiple&lt;br /&gt;
clients because changes to a global segment made by calls from one client&lt;br /&gt;
process are visible to the dynlink code when called from another client&lt;br /&gt;
process. Global data segments are provided to support subsystems, which are&lt;br /&gt;
discussed later. Figure 7-5 illustrates a dynlink routine with both&lt;br /&gt;
instance and global data segments.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³                                             ³&lt;br /&gt;
³               Code Segment(s)               ³&lt;br /&gt;
³                                             ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³              ³              ÚÁÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³&lt;br /&gt;
³    Global    ³             ÚÁÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³³&lt;br /&gt;
³     data     ³             ³              ³³³&lt;br /&gt;
³  segment(s)  ³             ³   Instance   ³³³&lt;br /&gt;
³              ³             ³     data     ³³³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ             ³  segment(s)  ³ÃÙ&lt;br /&gt;
                             ³              ÃÙ&lt;br /&gt;
                             ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-5.  Dynlink segments.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.6  Dynamic Link Packages As Subroutines&lt;br /&gt;
&lt;br /&gt;
Dynamic link subroutines (or packages) generally fall into two categories--&lt;br /&gt;
subroutines and subsystems. As we discussed earlier, a dynamic link&lt;br /&gt;
subroutine is written and executes in much the same way as a statically&lt;br /&gt;
linked subroutine. The only difference is in the preparation of the dynamic&lt;br /&gt;
link library file, which contains the actual subroutines, and in the&lt;br /&gt;
preparation of the special .OBJ file, to which client programs can link.&lt;br /&gt;
During execution, both the dynlink routines and the client routines can use&lt;br /&gt;
their own static data freely, and they can pass pointers to their data&lt;br /&gt;
areas back and forth to each other. The only difference between static&lt;br /&gt;
linking and dynamic linking, in this model, is that the dynlink routine&lt;br /&gt;
cannot reference any external symbols that the client code defines, nor can&lt;br /&gt;
the client externally reference any dynlink package symbols other than the&lt;br /&gt;
module entry points. Figure 7-6 illustrates a dynamic link routine being&lt;br /&gt;
used as a subroutine. The execution environment is nearly identical to that&lt;br /&gt;
of a traditional statically linked subroutine; the client and the&lt;br /&gt;
subroutine each reference their own static data areas, all of which are&lt;br /&gt;
contained in the process's address space. Note that a dynlink package can&lt;br /&gt;
reference the application's data and the application can reference the&lt;br /&gt;
dynlink package's data, but only if the application or the dynlink package&lt;br /&gt;
passes a pointer to its data to the other.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                         Process address space&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³            far calls                           far calls            ³&lt;br /&gt;
³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ far ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³&lt;br /&gt;
³ ³            ÃÄÄ�³            ³calls³  Dynlink   ÃÄÄ�³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP code  ³   ³  APP code  ÃÄÄÄÄ�³   code     ³   ³   code     ³ ³&lt;br /&gt;
³ ³ segment #1 ³�ÄÄ´ segment #n ³     ³ segment #1 ³�ÄÄ´ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ     ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ ³&lt;br /&gt;
³    ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³           ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³    ³&lt;br /&gt;
³    ³     ³ ³     references³           ³     ³ ³     references³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³           ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³    ³&lt;br /&gt;
³ ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÚÄÄÄ�ÄÄÄÄÄ�ÄÄ¿     ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÚÄÄÄ�ÄÄÄÄÄ�ÄÄ¿ ³&lt;br /&gt;
³ ³            ³   ³            ³     ³  Dynlink   ³   ³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP data  ³   ³  APP data  ³     ³   data     ³   ³   data     ³ ³&lt;br /&gt;
³ ³ segment #1 ³   ³ segment #n ³     ³ segment #1 ³   ³ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-6.  Dynamic link routines as subroutines.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.7  Subsystems&lt;br /&gt;
&lt;br /&gt;
The term dynlink subsystems refers to the design and intended function of a&lt;br /&gt;
particular style of dynlink package and is somewhat artificial. Although&lt;br /&gt;
OS/2 provides special features to help support subsystems, OS/2 does not&lt;br /&gt;
actually classify dynlink modules as subroutines or subsystems; subsystem&lt;br /&gt;
is merely a descriptive term.&lt;br /&gt;
     The term subsystem refers to a dynlink module that provides a set of&lt;br /&gt;
services built around a resource.&lt;br /&gt;
7. In the most general sense of the word. I don't mean a&lt;br /&gt;
&amp;quot;presentation manager resource object.&amp;quot;&lt;br /&gt;
7 For example, OS/2's VIO dynlink entry&lt;br /&gt;
points are considered a dynlink subsystem because they provide a set of&lt;br /&gt;
services to manage the display screen. A subsystem usually has to manage a&lt;br /&gt;
limited resource for an effectively unlimited number of clients; VIO does&lt;br /&gt;
this, managing a single physical display controller and a small number of&lt;br /&gt;
screen groups for an indefinite number of clients.&lt;br /&gt;
     Because subsystems generally manage a limited resource, they have one&lt;br /&gt;
or more global data segments that they use to keep information about the&lt;br /&gt;
state of the resource they're controlling; they also have buffers, flags,&lt;br /&gt;
semaphores, and so on. Per-client work areas are generally kept in instance&lt;br /&gt;
data segments; it's best to reserve the global data segment(s) for global&lt;br /&gt;
information. Figure 7-7 illustrates a dynamic link routine being used as a&lt;br /&gt;
subsystem. A dynlink subsystem differs from a dynlink being used as a&lt;br /&gt;
subroutine only by the addition of a static data segment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                         Process address space&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³            far calls                           far calls            ³&lt;br /&gt;
³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ far ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³&lt;br /&gt;
³ ³            ÃÄÄ�³            ³calls³  Dynlink   ÃÄÄ�³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP code  ³   ³  APP code  ÃÄÄÄÄ�³   code     ³   ³   code     ³ ³&lt;br /&gt;
³ ³ segment #1 ³�ÄÄ´ segment #n ³     ³ segment #1 ³�ÄÄ´ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ     ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ ³&lt;br /&gt;
³    ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³           ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³    ³&lt;br /&gt;
³    ³     ³ ³     references³           ³     ³ ³     references³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³           ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³    ³&lt;br /&gt;
³ ÚÍÍ�ÍÍÍÍÍ�ÍÍÍ»   ÚÍÍÍ�ÍÍÍÍÍ�ÍÍ»     ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÖÄÄÄÁÄÄÄÄÄÁÄÄ¿ ³&lt;br /&gt;
³ ³            º   ³            º     ³  global    ³   º instance   ³ ³&lt;br /&gt;
³ ³  APP data  º   ³  APP data  º     ³   data     ³   º   data     ³ ³&lt;br /&gt;
³ ³ segment #1 º   ³ segment #n º     ³ segment    ³   º segment    ³ ³&lt;br /&gt;
³ ÀÄÄÄÄÄÄÄÄÄÄÄÄ½   ÀÄÄÄÄÄÄÄÄÄÄÄÄ½     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÈÍÍÍÍÍÍÍÍÍÍÍÍÙ ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-7.  Dynamic link routines as subsystems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.7.1  Special Subsystem Support&lt;br /&gt;
Two OS/2 features are particularly valuable to subsystems: global data&lt;br /&gt;
segments (which we've already discussed) and special client initialization&lt;br /&gt;
and termination support. Clearly, if a subsystem is going to manage a&lt;br /&gt;
resource, keeping track of its clients in a global data segment, it needs&lt;br /&gt;
to know when new clients arrive and when old clients terminate. The simple&lt;br /&gt;
dynlink subroutine model doesn't provide this information in a reliable&lt;br /&gt;
fashion. A subsystem undoubtedly has initialize and terminate entry points,&lt;br /&gt;
but client programs may terminate without having called a subsystem's&lt;br /&gt;
terminate entry point. Such a failure may be an error on the part of the&lt;br /&gt;
client, but the system architecture decrees that errors should be&lt;br /&gt;
localized; it's not acceptable for a bug in a client process to be able to&lt;br /&gt;
hang up a subsystem and thus all its clients as well.&lt;br /&gt;
     The two forms of subsystem initialization are global and instance. A&lt;br /&gt;
subsystem can specify either service but not both. If global initialization&lt;br /&gt;
is specified, the initialization entry point is called only once per&lt;br /&gt;
activation of the subsystem. When the subsystem dynlink package is first&lt;br /&gt;
referenced, OS/2 allocates the subsystem's global data segment(s), taking&lt;br /&gt;
their initial values from the .DLL file. OS/2 then calls the subsystem's&lt;br /&gt;
global initialization entry point so that the module can do its one-time&lt;br /&gt;
initialization. The thread that is used to call the initialization entry&lt;br /&gt;
point belongs to that first client process,&lt;br /&gt;
8. The client process doesn't explicitly call a dynlink package's&lt;br /&gt;
initialization entry points. OS/2 uses its godlike powers to&lt;br /&gt;
borrow a thread for the purpose. The mechanism is invisible to the&lt;br /&gt;
client program. It goes without saying, we hope, that it would be&lt;br /&gt;
extremely rude to the client process, not to say damaging, were&lt;br /&gt;
the dynlink package to refuse to return that initialization thread&lt;br /&gt;
or if it were to damage it in some way, such as lowering its&lt;br /&gt;
priority or calling DosExit with it!&lt;br /&gt;
8 so the first client's instance&lt;br /&gt;
data segments are also set up and may be used by the global initialization&lt;br /&gt;
process. This means that although the dynlink subsystem is free to open&lt;br /&gt;
files, read and write their contents, and close them again, it may not open&lt;br /&gt;
a handle to a file, store the handle number in a global data segment, and&lt;br /&gt;
expect to use that handle in the future.&lt;br /&gt;
     Remember, subsystems don't own resources; processes own resources.&lt;br /&gt;
When a dynlink package opens a file, that file is open only for that one&lt;br /&gt;
client process. That handle has meaning only when that particular client is&lt;br /&gt;
calling the subsystem code. If a dynlink package were to store process A's&lt;br /&gt;
handle number in a global data segment and then attempt to do a read from&lt;br /&gt;
that handle when running as process B, at best the read would fail with&lt;br /&gt;
&amp;quot;invalid handle&amp;quot;; at worst some unrelated file of B's would be molested.&lt;br /&gt;
And, of course, when client process A eventually terminates, the handle&lt;br /&gt;
becomes invalid for all clients.&lt;br /&gt;
     The second form of initialization is instance initialization. The&lt;br /&gt;
instance initialization entry point is called in the same way as the global&lt;br /&gt;
initialization entry point except that it is called for every new client&lt;br /&gt;
when that client first attaches to the dynlink package. Any instance data&lt;br /&gt;
segments that exist will already be allocated and will have been given&lt;br /&gt;
their initial values from the .DLL file. The initialization entry point for&lt;br /&gt;
a loadtime dynlink is called before the client's code begins executing. The&lt;br /&gt;
initialization entry point for a runtime dynlink is called when the client&lt;br /&gt;
calls the DosLoadModule function. A dynlink package may not specify both&lt;br /&gt;
global and instance initialization; if it desires both, it should specify&lt;br /&gt;
instance initialization and use a counter in one of its global data&lt;br /&gt;
segments to detect the first instance initialization.&lt;br /&gt;
     Even more important than initialization control is termination&lt;br /&gt;
control. In its global data area, a subsystem may have records, buffers, or&lt;br /&gt;
semaphores on behalf of a client process. It may have queued-up requests&lt;br /&gt;
from that client that it needs to purge when the client terminates. The&lt;br /&gt;
dynlink package need not release instance data segments; because these&lt;br /&gt;
belong to the client process, they are destroyed when the client&lt;br /&gt;
terminates. The global data segments themselves are released if this is the&lt;br /&gt;
dynlink module's last client, so the module may want to take this last&lt;br /&gt;
chance to update a log file, release a system semaphore, and so on.&lt;br /&gt;
     Because a dynlink routine runs as the calling client process, it could&lt;br /&gt;
use DosSetSigHandler to intercept the termination signal. This should never&lt;br /&gt;
be done, however, because the termination signal is not activated for all&lt;br /&gt;
causes of process termination. For example, if the process calls DosExit,&lt;br /&gt;
the termination signal is not sent. Furthermore, there can be only one&lt;br /&gt;
handler per signal type per process. Because client processes don't and&lt;br /&gt;
shouldn't know what goes on inside a dynlink routine, the client process&lt;br /&gt;
and a dynlink routine may conflict in the use of the signal. Such a&lt;br /&gt;
conflict may also occur between two dynlink packages.&lt;br /&gt;
     Using DosExitList service prevents such a collision. DosExitList&lt;br /&gt;
allows a process to specify one or more subroutine addresses that will be&lt;br /&gt;
called when the process terminates. Addresses can be added to and removed&lt;br /&gt;
from the list. DosExitList is ideally suited for termination control. There&lt;br /&gt;
can be many such addresses, and the addresses are called under all&lt;br /&gt;
termination conditions. Both the client process and the subsystem dynlinks&lt;br /&gt;
that it calls can have their own termination routine or routines.&lt;br /&gt;
DosExitList is discussed in more detail in 16.2 Data Integrity.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.8  Dynamic Links As Interfaces to Other Processes&lt;br /&gt;
&lt;br /&gt;
Earlier, I mentioned that dynlink subsystems have difficulty dealing with&lt;br /&gt;
resources--other than global memory--because resource ownership and access&lt;br /&gt;
are on a per-process basis. Life as a dynlink subsystem can be&lt;br /&gt;
schizophrenic. Which files are open, which semaphores are owned and so on&lt;br /&gt;
depends on which client is running your code at the moment. Global memory&lt;br /&gt;
is different; it's the one resource that all clients own jointly. The&lt;br /&gt;
memory remains as long as the client count doesn't go to zero.&lt;br /&gt;
     One way to deal with resource issues is for a dynlink package to act&lt;br /&gt;
as a front end for a server process. During module initialization, the&lt;br /&gt;
dynlink module can check a system semaphore to see whether the server&lt;br /&gt;
process is already running and, if not, start it up. It needs to do this&lt;br /&gt;
with the &amp;quot;detach&amp;quot; form of DosExecPgm so that the server process doesn't&lt;br /&gt;
appear to the system as a child of the subsystem's first client. Such a&lt;br /&gt;
mistake could mean that the client's parent thinks that the command subtree&lt;br /&gt;
it founded by running the client never terminates because the server&lt;br /&gt;
process appears to be part of the command subtree (see Figure 7-8).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
   ³   Grandparent   ³       ³   Grandparent   ³&lt;br /&gt;
   ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
            ³                         ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿      ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³°°°°°°°°°°°³°°°°°°°°°°°³ ³°°°°°°°°°°°³°°°°°°°°°°°³   Ú �³  Daemon  ³&lt;br /&gt;
³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³ ³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³   |  ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³°°³     Process     ³°°³ ³°°³     Process     ³Ä Å Ä Ù   &lt;br /&gt;
³°°ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ°°³ ³°°ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ°°³&lt;br /&gt;
³°°°°°°°°°°°³°°°°°°°°°°°³ ³°°° Command subtree °°°³&lt;br /&gt;
³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°³      Daemon     ³°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°° Command subtree °°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°°°°°°°°°°°°°°°°°°°°°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-8.  Dynlink daemon initiation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When the server process is running, the dynlink subsystem can forward&lt;br /&gt;
some or all requests to it by one of the many IPC facilities. For example,&lt;br /&gt;
a database subsystem might want to use a dedicated server process to hold&lt;br /&gt;
open the database file and do reads and writes to it. It might keep buffers&lt;br /&gt;
and ISAM directories in a shared memory segment to which the dynlink&lt;br /&gt;
subsystem requests access for each of its clients; then requests that can&lt;br /&gt;
be satisfied by data from these buffers won't require the IPC to the server&lt;br /&gt;
process.&lt;br /&gt;
     The only function of some dynlink packages is to act as a procedural&lt;br /&gt;
interface to another process. For example, a spreadsheet program might&lt;br /&gt;
provide an interface through which other applications can retrieve data&lt;br /&gt;
values from a spreadsheet. The best way to do this is for the spreadsheet&lt;br /&gt;
package to contain a dynamic link library that provides clients a&lt;br /&gt;
procedural interface to the spreadsheet process. The library routine itself&lt;br /&gt;
will invoke a noninteractive copy (perhaps a special subset .EXE) of the&lt;br /&gt;
spreadsheet to recover the information, passing it back to the client via&lt;br /&gt;
IPC. Alternatively, the retrieval code that understands the spreadsheet&lt;br /&gt;
data formats could be in the dynlink package itself because that package&lt;br /&gt;
ships with the spreadsheet and will be upgraded when the spreadsheet is. In&lt;br /&gt;
this case, the spreadsheet itself could use the package instead of&lt;br /&gt;
duplicating the functionality in its own .EXE file. In any case, the&lt;br /&gt;
implementation details are hidden from the client process; the client&lt;br /&gt;
process simply makes a procedure call that returns the desired data.&lt;br /&gt;
     Viewed from the highest level, this arrangement is simple: A client&lt;br /&gt;
process uses IPC to get service from a server process via a subroutine&lt;br /&gt;
library. From the programmer's point of view, though, the entire mechanism&lt;br /&gt;
is encapsulated in the dynlink subsystem's interface. A future upgrade to&lt;br /&gt;
the dynlink package may use an improved server process and different forms&lt;br /&gt;
of IPC to talk to it but retain full binary compatibility with the existing&lt;br /&gt;
client base. Figure 7-9 illustrates a dynlink package being used as an&lt;br /&gt;
interface to a daemon process. The figure shows the dynlink package&lt;br /&gt;
interfacing with the daemon process by means of a shared memory segment and&lt;br /&gt;
some other form of IPC, perhaps a named pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                                   client process   |   daemon process&lt;br /&gt;
                                                    |&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿    Far call     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿       |        ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³    APP     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³  Dynlink   ³�ÄÄÄÄÄÄ|ÄÄÄÄÄÄÄÄ´   Daemon   ³&lt;br /&gt;
³    code    ³                 ³    code    ³      IPC       ³    code    ³&lt;br /&gt;
³ segment(s) ³                 ³ segment(s) ÃÄÄÄÄÄÄÄ|ÄÄÄÄÄÄÄ�³ segment(s) ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÂÄÄÄÂÄÄÙ       |        ÀÄÄÂÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
      ³                              ³   ³          |           ³   ³&lt;br /&gt;
      ³                              ³   ÀÄÄÄÄÄÄÄ¿  |   ÚÄÄÄÄÄÄÄÙ   ³&lt;br /&gt;
      ³                              ³           ³  |   ³           ³&lt;br /&gt;
ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿                 ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿ ÚÄÄ�ÄÄ|ÄÄÄ�ÄÄ¿ ÚÄÄÄÄÄÄ�ÄÄÄÄÄ¿&lt;br /&gt;
³    APP     ³                 ³  Dynlink   ³ ³     |      ³ ³   Daemon   ³&lt;br /&gt;
³    data    ³                 ³    data    ³ ³   Shared   ³ ³    data    ³&lt;br /&gt;
³ segment(s) ³                 ³ segment(s) ³ ³   memory   ³ ³ segment(s) ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄ|ÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                                    |&lt;br /&gt;
&lt;br /&gt;
Figure 7-9.  Dynamic link routines as daemon interfaces.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.9  Dynamic Links As Interfaces to the Kernel&lt;br /&gt;
&lt;br /&gt;
We've seen how dynlink libraries can serve as simple subroutine libraries,&lt;br /&gt;
how they can serve as subsystems, and how they can serve as interfaces to&lt;br /&gt;
other processes. OS/2 has one more trick up its sleeve: Dynlink libraries&lt;br /&gt;
can also serve as interfaces to OS/2 itself.&lt;br /&gt;
     Some OS/2 calls are actually implemented as simple library routines.&lt;br /&gt;
For example, DosErrClass is implemented in OS/2 version 1.0 as a simple&lt;br /&gt;
library routine. It takes an error code and locates, in a table, an&lt;br /&gt;
explanatory text string, an error classification, and a recommended action.&lt;br /&gt;
Services such as these were traditionally part of the kernel of operating&lt;br /&gt;
systems, not because they needed to use privileged instructions, but&lt;br /&gt;
because their error tables needed to be changed each time an upgrade to the&lt;br /&gt;
operating system was released. If the service has been provided as a&lt;br /&gt;
statically linked subroutine, older applications running on newer releases&lt;br /&gt;
would receive new error codes that would not be in the library code's&lt;br /&gt;
tables.&lt;br /&gt;
     Although OS/2 implements DosErrClass as a library routine, it's a&lt;br /&gt;
dynlink library routine, and the .DLL file is bundled with the operating&lt;br /&gt;
system itself. Any later release of the system will contain an upgraded&lt;br /&gt;
version of the DosErrClass routine, one that knows about new error codes.&lt;br /&gt;
Consequently, the dynlink facility provides OS/2 with a great deal of&lt;br /&gt;
flexibility in packaging its functionality.&lt;br /&gt;
     Some functions, such as &amp;quot;open file&amp;quot; or &amp;quot;allocate memory,&amp;quot; can't be&lt;br /&gt;
implemented as ordinary subroutines. They need access to key internal data&lt;br /&gt;
structures, and these structures are of course protected so that they can't&lt;br /&gt;
be changed by unprivileged code. To get these services, the processor must&lt;br /&gt;
make a system call, entering the kernel code in a very controlled fashion&lt;br /&gt;
and there running with sufficient privilege to do its work. This privilege&lt;br /&gt;
transition is via a call gate--a feature of the 80286/80386 hardware. A&lt;br /&gt;
program calls a call gate exactly as it performs an ordinary far call;&lt;br /&gt;
special flags in the GDT and LDT tell the processor that this is a call&lt;br /&gt;
gate rather than a regular call.&lt;br /&gt;
     In OS/2, system calls are indistinguishable from ordinary dynlink&lt;br /&gt;
calls. All OS/2 system calls are defined in a dynlink module called&lt;br /&gt;
DosCalls. When OS/2 fixes up dynlink references to this module, it consults&lt;br /&gt;
a special table, built into OS/2, of resident functions. If the function is&lt;br /&gt;
not listed in this table, then an ordinary dynlink is set up. If the&lt;br /&gt;
function is in the table, OS/2 sets up a call gate call in place of the&lt;br /&gt;
ordinary dynlink call. The transparency between library and call gate&lt;br /&gt;
functions explains why passing an invalid address to an OS/2 system call&lt;br /&gt;
causes the calling process to GP fault. Because the OS/2 kernel code&lt;br /&gt;
controls and manages the GP fault mechanism, OS/2 calls that are call gates&lt;br /&gt;
could easily return an error code if an invalid address causes a GP fault.&lt;br /&gt;
If this were done, however, the behavior of OS/2 calls would differ&lt;br /&gt;
depending on their implementation: Dynlink entry points would GP fault for&lt;br /&gt;
invalid addresses;&lt;br /&gt;
9. The LAR and LSL instructions are not sufficient to prevent&lt;br /&gt;
this because another thread in that process may free a segment&lt;br /&gt;
after the LAR but before the reference.&lt;br /&gt;
9 call gate entries would return an error code. OS/2&lt;br /&gt;
prevents this dichotomy and preserves its freedom to, in future releases,&lt;br /&gt;
move function between dynlink and call gate entries by providing a uniform&lt;br /&gt;
reaction to invalid addresses. Because non-call-gate dynlink routines must&lt;br /&gt;
generate GP faults, call gate routines produce them as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.10  The Architectural Role of Dynamic Links&lt;br /&gt;
&lt;br /&gt;
Dynamic links play three major roles in OS/2: They provide the system&lt;br /&gt;
interface; they provide a high-bandwidth device interface; and they support&lt;br /&gt;
open architecture nonkernel service packages.&lt;br /&gt;
     The role of dynamic links as the system interface is clear. They&lt;br /&gt;
provide a uniform, high-efficiency interface to the system kernel as well&lt;br /&gt;
as a variety of nonkernel services. The interface is directly compatible&lt;br /&gt;
with high-level languages, and it takes advantage of special speed-&lt;br /&gt;
enhancing features of the 80286 and 80386 microprocessors.&lt;br /&gt;
10. Specifically, automatic argument passing on calls to the ring&lt;br /&gt;
0 kernel code.&lt;br /&gt;
10 It provides a&lt;br /&gt;
wide and convenient name space, and it allows the distribution of function&lt;br /&gt;
between library code and kernel code. Finally, it provides an essentially&lt;br /&gt;
unlimited expansion capability.&lt;br /&gt;
     But dynamic links do much more than act as system calls. You'll recall&lt;br /&gt;
that in the opening chapters I expressed a need for a device interface that&lt;br /&gt;
was as device independent as device drivers but without their attendant&lt;br /&gt;
overhead. Dynamic links provide this interface because they allow&lt;br /&gt;
applications to make a high-speed call to a subroutine package that can&lt;br /&gt;
directly manipulate the device (see Chapter 18, I/O Privilege Mechanism&lt;br /&gt;
and Debugging/Ptrace). The call itself is fast, and the package can specify&lt;br /&gt;
an arbitrarily wide set of parameters. No privilege or ring transition is&lt;br /&gt;
needed, and the dynlink package can directly access its client's data&lt;br /&gt;
areas. Finally, the dynlink package can use subsystem support features to&lt;br /&gt;
virtualize the device or to referee its use among multiple clients. Device&lt;br /&gt;
independence is provided because a new version of the dynlink interface can&lt;br /&gt;
be installed whenever new hardware is installed. VIO and the presentation&lt;br /&gt;
manager are examples of this kind of dynlink use. Dynlink packages have an&lt;br /&gt;
important drawback when they are being used as device driver replacements:&lt;br /&gt;
They cannot receive hardware interrupts. Some devices, such as video&lt;br /&gt;
displays, do not generate interrupts. Interrupt-driven devices, though,&lt;br /&gt;
require a true device driver. That driver can contain all of the device&lt;br /&gt;
interface function, or the work can be split between a device driver and a&lt;br /&gt;
dynlink package that acts as a front end for that device driver. See&lt;br /&gt;
Chapters 17 and 18 for further discussion of this.&lt;br /&gt;
     Dynlink routines can also act as nonkernel service packages--as an&lt;br /&gt;
open system architecture for software. Most operating systems&lt;br /&gt;
are like the early versions of the Apple Macintosh computer: They are&lt;br /&gt;
closed systems; only their creators can add features to them. Because of&lt;br /&gt;
OS/2's open system architecture, third parties and end users can add system&lt;br /&gt;
services simply by plugging in dynlink modules, just as hardware cards plug&lt;br /&gt;
into an open hardware system. The analogy extends further: Some hardware&lt;br /&gt;
cards become so popular that their interface defines a standard. Examples&lt;br /&gt;
are the Hayes modem and the Hercules Graphics Card. Third-party dynlink&lt;br /&gt;
packages will, over time, establish similar standards. Vendors will offer,&lt;br /&gt;
for example, improved database dynlink routines that are advertised as plug&lt;br /&gt;
compatible with the standard database dynlink interface, but better,&lt;br /&gt;
cheaper, and faster.&lt;br /&gt;
     Dynlinks allow third parties to add interfaces to OS/2; they also&lt;br /&gt;
allow OS/2's developers to add future interfaces. The dynlink interface&lt;br /&gt;
model allows additional functionality to be implemented as subroutines or&lt;br /&gt;
processes or even to be distributed across a network environment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11  Implementation Details&lt;br /&gt;
&lt;br /&gt;
Although dynlink routines often act very much like traditional static&lt;br /&gt;
subroutines, a programmer must be aware of some special considerations&lt;br /&gt;
involved. This section discusses some issues that must be dealt with to&lt;br /&gt;
produce a good dynlink package.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.1  Dynlink Data Security&lt;br /&gt;
We have discussed how a dynlink package runs as a subroutine of the client&lt;br /&gt;
process and that the client process has access to the dynlink package's&lt;br /&gt;
instance and global data segments.&lt;br /&gt;
11. A client process has memory access (addressability) to all of&lt;br /&gt;
the package's global segments but only to those instance data&lt;br /&gt;
segments associated with that process.&lt;br /&gt;
11 This use of the dynlink interface is&lt;br /&gt;
efficient and thus advantageous, but it's also disadvantageous because&lt;br /&gt;
aberrant client processes can damage the dynlink package's global data&lt;br /&gt;
segments.&lt;br /&gt;
     In most circumstances, accidental damage to a dynlink package's data&lt;br /&gt;
segments is rare. Unless the dynlink package returns pointers into its data&lt;br /&gt;
segments to the client process, the client doesn't &amp;quot;know&amp;quot; the dynlink&lt;br /&gt;
package's data segment selectors. The only way such a process could access&lt;br /&gt;
the dynlink's segments would be to accidentally create a random selector&lt;br /&gt;
value that matched one belonging to a dynlink package. Because the&lt;br /&gt;
majority of selector values are illegal, a process would have to be&lt;br /&gt;
very &amp;quot;lucky&amp;quot; to generate a valid dynlink package data selector before it&lt;br /&gt;
generated an unused or code segment selector.&lt;br /&gt;
12.Because if a process generates and writes with a selector that&lt;br /&gt;
is invalid or points to a code segment, the process will be&lt;br /&gt;
terminated immediately with a GP fault.&lt;br /&gt;
12 Naturally, dynlink packages&lt;br /&gt;
shouldn't use global data segments to hold sensitive data because a&lt;br /&gt;
malicious application can figure out the proper selector values.&lt;br /&gt;
     The measures a programmer takes to deal with the security issue depend&lt;br /&gt;
on the nature and sensitivity of the dynlink package. Dynlink packages that&lt;br /&gt;
don't have global data segments are at no risk; an aberrant program can&lt;br /&gt;
damage its instance data segments and thereby fail to run correctly, but&lt;br /&gt;
that's the expected outcome of a program bug. A dynlink package with global&lt;br /&gt;
data segments can minimize the risk by never giving its callers pointers&lt;br /&gt;
into its (the dynlink package's) global data segment. If the amount of&lt;br /&gt;
global data is small and merely detecting damage is sufficient, the global&lt;br /&gt;
data segments could be checksummed.&lt;br /&gt;
     Finally, if accidental damage would be grave, a dynlink package can&lt;br /&gt;
work in conjunction with a special dedicated process, as described above.&lt;br /&gt;
The dedicated process can keep the sensitive data and provide it on a per-&lt;br /&gt;
client basis to the dynlink package in response to an IPC request. Because&lt;br /&gt;
the dedicated process is a separate process, its segments are fully&lt;br /&gt;
protected from the client process as well as from all others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.2  Dynlink Life, Death, and Sharing&lt;br /&gt;
Throughout this discussion, I have referred to sharing pure segments. The&lt;br /&gt;
ability to share pure segments is an optimization that OS/2 makes for all&lt;br /&gt;
memory segments whether they are dynlink segments or an application's .EXE&lt;br /&gt;
file segments. A pure segment is one that is never modified during its&lt;br /&gt;
lifetime. All code segments (except for those created by DosCreateCSAlias)&lt;br /&gt;
are pure; read-only data segments are also pure. When OS/2 notices that&lt;br /&gt;
it's going to load two copies of the same pure segment, it performs a&lt;br /&gt;
behind-the-scenes optimization and gives the second client access to the&lt;br /&gt;
earlier copy of the segment instead of wasting memory with a duplicate&lt;br /&gt;
version.&lt;br /&gt;
     For example, if two copies of a program are run, all code segments are&lt;br /&gt;
pure; at most, only one copy of each code segment will be in memory. OS/2&lt;br /&gt;
flags these segments as &amp;quot;internally shared&amp;quot; and doesn't release them until&lt;br /&gt;
the last user has finished with the segment. This is not the same as&lt;br /&gt;
&amp;quot;shared memory&amp;quot; as it is generally defined in OS/2. Because pure segments&lt;br /&gt;
can only be read, never written, no process can tell that pure segments are&lt;br /&gt;
being shared or be affected by that sharing. Although threads from two or&lt;br /&gt;
more processes may execute the same shared code segment at the same time,&lt;br /&gt;
this is not the same as a multithreaded process. Each copy of a program has&lt;br /&gt;
its own data areas, its own stack, its own file handles, and so on. They&lt;br /&gt;
are totally independent of one another even if OS/2 is quietly sharing&lt;br /&gt;
their pure code segments among them. Unlike multiple threads within a&lt;br /&gt;
single process, threads from different processes cannot affect one another;&lt;br /&gt;
the programmer can safely ignore their possible existence in shared code&lt;br /&gt;
segments.&lt;br /&gt;
     Because the pure segments of a dynlink package are shared, the second&lt;br /&gt;
and subsequent clients of a dynlink package can load much more quickly&lt;br /&gt;
(because these pure segments don't have to be loaded from the .DLL disk&lt;br /&gt;
file). This doesn't mean that OS/2 doesn't have to &amp;quot;hit the disk&amp;quot; at all:&lt;br /&gt;
Many dynlink packages use instance data segments, and OS/2 loads a fresh&lt;br /&gt;
copy of the initial values for these segments from the .DLL file.&lt;br /&gt;
     A dynlink package's second client is its second simultaneous client.&lt;br /&gt;
Under OS/2, only processes have a life of their own. Objects such as&lt;br /&gt;
dynlink packages and shared memory segments exist only as possessions of&lt;br /&gt;
processes. When the last client process of such an object dies or otherwise&lt;br /&gt;
releases the object, OS/2 destroys it and frees up the memory. For example,&lt;br /&gt;
when the first client (since bootup) of a dynlink package references it,&lt;br /&gt;
OS/2 loads the package's code and data segments. Then OS/2 calls the&lt;br /&gt;
package's initialization routine--if the package has one. OS/2 records in&lt;br /&gt;
an internal data structure that this dynlink package has one client. If&lt;br /&gt;
additional clients come along while the first is still using the dynlink&lt;br /&gt;
package, OS/2 increments the package's user count appropriately. Each time&lt;br /&gt;
a client disconnects or dies, the user count is decremented. As long as the&lt;br /&gt;
user count remains nonzero, the package remains in existence, each client&lt;br /&gt;
sharing the original global data segments. When the client count goes to&lt;br /&gt;
zero, OS/2 discards the dynlink package's code and global data segments and&lt;br /&gt;
in effect forgets all about the package. When another client comes along,&lt;br /&gt;
OS/2 reloads the package and reloads its global data segment as if the&lt;br /&gt;
earlier use had never occurred.&lt;br /&gt;
     This mechanism affects a dynlink package only in the management of the&lt;br /&gt;
package's global data segment. The package's code segments are pure, so it&lt;br /&gt;
doesn't matter if they are reloaded from the .DLL file. The instance data&lt;br /&gt;
segments are always reinitialized for each new client, but the data in a&lt;br /&gt;
package's global data segment remains in existence only as long as the&lt;br /&gt;
package has at least one client process. When the last client releases the&lt;br /&gt;
package, the global data segment is discarded. If this is a problem for a&lt;br /&gt;
dynlink package, an associated &amp;quot;dummy&amp;quot; process (which the dynlink package&lt;br /&gt;
could start during its loadtime initialization) can reference the dynlink&lt;br /&gt;
package. As long as this process stays alive, the dynlink package and its&lt;br /&gt;
global data segments stay alive.&lt;br /&gt;
13. If you use this technique, be sure to use the detached form&lt;br /&gt;
of DosExec; see the warning in 7.8 Dynamic Links As&lt;br /&gt;
Interfaces to Other Processes.&lt;br /&gt;
13&lt;br /&gt;
     An alternative is for the dynlink package to keep track of the count&lt;br /&gt;
of its clients and save the contents of its global data segments to a disk&lt;br /&gt;
file when the last client terminates, but this is tricky. Because a process&lt;br /&gt;
may fail to call a dynlink package's &amp;quot;I'm finished&amp;quot; entry point (presumably&lt;br /&gt;
part of the dynlink package's interface) before it terminates, the dynlink&lt;br /&gt;
package must get control to write its segment via DosExitList. If the&lt;br /&gt;
client process is connected to the dynlink package via DosLoadModule (that&lt;br /&gt;
is, via runtime dynamic linking), it cannot disconnect from the package via&lt;br /&gt;
DosFreeModule as long as a DosExitList address points into the dynlink&lt;br /&gt;
package. An attempt to do so returns an error code. Typically, one would&lt;br /&gt;
expect the application to ignore this error code; but because the dynlink&lt;br /&gt;
package is still attached to the client process, it will receive&lt;br /&gt;
DosExitList service when the client eventually terminates. It's important&lt;br /&gt;
that dynlink packages which maintain client state information and therefore&lt;br /&gt;
need DosExitList also offer an &amp;quot;I'm finished&amp;quot; function. When a client calls&lt;br /&gt;
this function, the package should close it out and then remove its&lt;br /&gt;
processing address from DosExitList so that DosFreeModule can take effect&lt;br /&gt;
if the client wishes.&lt;br /&gt;
     Note that OS/2's habit of sharing in-use dynlink libraries has&lt;br /&gt;
implications for the replacement of dynlink packages. Specifically, OS/2&lt;br /&gt;
holds the dynlink .DLL file open for as long as that library has any&lt;br /&gt;
clients. To replace a dynlink library with an upgraded version,&lt;br /&gt;
you must first ensure that all clients of the old package have been&lt;br /&gt;
terminated.&lt;br /&gt;
     While we're on the subject, I'll point out that dynlink segments, like&lt;br /&gt;
.EXE file segments, can be marked (by the linker) as &amp;quot;preload&amp;quot; or &amp;quot;load on&lt;br /&gt;
demand.&amp;quot; When a dynlink module or a .EXE file is loaded, OS/2 immediately&lt;br /&gt;
loads all segments marked &amp;quot;preload&amp;quot; but usually&lt;br /&gt;
14. Segments that are loaded from removable media will be fully&lt;br /&gt;
loaded, regardless of the &amp;quot;load on demand&amp;quot; bit.&lt;br /&gt;
14 does not load any&lt;br /&gt;
segments marked &amp;quot;load on demand.&amp;quot; These segments are loaded only when (and&lt;br /&gt;
if) they are referenced. This mechanism speeds process and library loading&lt;br /&gt;
and reduces swapping by leaving infrequently used segments out of memory&lt;br /&gt;
until they are needed. Once a segment is loaded, its &amp;quot;preload&amp;quot; or &amp;quot;load on&lt;br /&gt;
demand&amp;quot; status has no further bearing; the segment will be swapped or&lt;br /&gt;
discarded without consideration for these bits.&lt;br /&gt;
     Finally, special OS/2 code keeps track of dynamic link &amp;quot;circular&lt;br /&gt;
references.&amp;quot; Because dynlink packages can call other dynlink packages,&lt;br /&gt;
package A can call package B, and package B can call package A. Even if the&lt;br /&gt;
client process C terminates, packages A and B might appear to be in use by&lt;br /&gt;
each other, and they would both stay in memory. OS/2 keeps a graph of&lt;br /&gt;
dynlink clients, both processes and other dynlink packages. When a process&lt;br /&gt;
can no longer reach a dynlink package over this graph--in other words, when&lt;br /&gt;
a package doesn't have a process for a client and when none of its client&lt;br /&gt;
packages have processes for clients and so on--the dynlink package is&lt;br /&gt;
released. Figure 7-10 illustrates a dynamic link circular reference. PA&lt;br /&gt;
and PB are two processes, and LA through LG are dynlink library routines.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿                        ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³°°°°°°°°°°°°³                        ³°°°°°°°°°°°°³&lt;br /&gt;
³°°°°°PA°°°°°³                        ³°°°°°PB°°°°°³&lt;br /&gt;
³°°°°°°°°°°°°³                        ³°°°°°°°°°°°°³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ                        ÀÄÂÄÄÄÄÄÄÄÄÂÄÙ&lt;br /&gt;
      ³                                 ³        ³&lt;br /&gt;
ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿                          ³  ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿&lt;br /&gt;
³     LA     ³�ÄÄÄÄÄÄÄ¿                 ³  ³     LF     ³&lt;br /&gt;
ÀÄÄÂÄÄÄÄÄÄÂÄÄÙ        ³                 ³  ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
   ³   ÚÄÄÁÄÄÄÄÄÄÄÄÄ¿ ³                 ³        ³&lt;br /&gt;
   ³   ³     LB     ÃÄÙ                 ³  ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿&lt;br /&gt;
   ³   ÀÄÂÄÄÄÄÄÄÄÄÄÄÙ                   ³  ³     LG     ³&lt;br /&gt;
   ³     ³  ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ  ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄ�ÄÄÄÄÄ�ÄÄ�¿           ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿          ³&lt;br /&gt;
³     LC     ÃÄÄÄÄÄÄÄÄÄÄ�³     LD     ³�ÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÄ�ÄÄÄÄÄÄÙ           ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
      ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-10.  Dynamic link circular references.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.3  Dynlink Side Effects&lt;br /&gt;
A well-written dynlink library needs to adhere to the OS/2 religious tenet&lt;br /&gt;
of zero side effects. A dynlink library should export to the client process&lt;br /&gt;
only its functional interface and not accidentally export side effects that&lt;br /&gt;
may interfere with the consistent execution of the client.&lt;br /&gt;
     Some possible side effects are obvious: A dynlink routine shouldn't&lt;br /&gt;
close any file handles that it didn't itself open. The same applies to&lt;br /&gt;
other system resources that the client process may be accessing, and it&lt;br /&gt;
applies in the inverse, as well: A dynlink routine that obtains resources&lt;br /&gt;
for itself, in the guise of the client process, should do so in a way that&lt;br /&gt;
doesn't affect the client code. For example, consuming many of the&lt;br /&gt;
available file handles would be a side effect because the client would then&lt;br /&gt;
unexpectedly be short of available file handles. A dynlink package with a&lt;br /&gt;
healthy file handle appetite should be sure to call OS/2 to raise the&lt;br /&gt;
maximum number of file handles so that the client process isn't&lt;br /&gt;
constrained. Finally, the amount of available stack space is a resource&lt;br /&gt;
that a dynlink package must not exhaust. A dynlink routine should try to&lt;br /&gt;
minimize its stack needs, and an upgrade to an existing dynlink package&lt;br /&gt;
must not consume much more stack space than did the earlier version, lest&lt;br /&gt;
the upgrade cause existing clients to fail in the field.&lt;br /&gt;
     Dynlink routines can also cause side effects by issuing some kinds of&lt;br /&gt;
system calls. Because a dynlink routine runs as a subroutine of the client&lt;br /&gt;
process, it must be sure that calls that it makes to OS/2 on behalf of the&lt;br /&gt;
client process don't affect the client application. For example, each&lt;br /&gt;
signal event can have only one handler address; if a dynlink routine&lt;br /&gt;
establishes a signal handler, then that signal handler preempts any handler&lt;br /&gt;
set up by the client application. Likewise, if a dynlink routine changes&lt;br /&gt;
the priority of the thread with which it was called, the dynlink routine&lt;br /&gt;
must be sure to restore that priority before it returns to its caller.&lt;br /&gt;
Several other system functions such as DosError and DosSetVerify also cause&lt;br /&gt;
side effects that can affect the client process.&lt;br /&gt;
     Enumerating all forms of side effects is not possible; it's up to the&lt;br /&gt;
programmer to take the care needed to ensure that a dynlink module is&lt;br /&gt;
properly house-trained. A dynlink module should avoid the side effects&lt;br /&gt;
mentioned as well as similar ones, and, most important, it should behave&lt;br /&gt;
consistently so that if a client application passes its acceptance tests in&lt;br /&gt;
the lab it won't mysteriously fail in the field. This applies doubly to&lt;br /&gt;
upgrades for existing dynlink routines. Upgrades must be written so that if&lt;br /&gt;
a client application works with the earlier release of the dynlink package&lt;br /&gt;
it will work with the new release; obviously the author of the application&lt;br /&gt;
will not have an opportunity to retest existing copies of the application&lt;br /&gt;
against the new release of the dynlink module.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.12  Dynlink Names&lt;br /&gt;
&lt;br /&gt;
Each dynlink entry point has three names associated with it: an external&lt;br /&gt;
name, a module name, and an entry point name. The name the client program&lt;br /&gt;
calls as an external reference is the external name. The programmer works&lt;br /&gt;
with this name, and its syntax and form must be compatible with the&lt;br /&gt;
assembler or compiler being used. The name should be simple and explanatory&lt;br /&gt;
yet unlikely to collide with another external name in the client code or in&lt;br /&gt;
another library. A name such as READ or RESET is a poor choice because of&lt;br /&gt;
the collision possibilities; a name such as XR23P11 is obviously hard to&lt;br /&gt;
work with.&lt;br /&gt;
     The linker replaces the external name with a module name and an entry&lt;br /&gt;
point name, which are embedded in the resultant .EXE file. OS/2 uses the&lt;br /&gt;
module name to locate the dynlink .DLL file; the code for module modname is&lt;br /&gt;
in file MODNAME.DLL. The entry point name specifies the entry point in the&lt;br /&gt;
module; the entry point name need not be the same as the external name. For&lt;br /&gt;
modules with a lot of entry points, the client .EXE file size can be&lt;br /&gt;
minimized and the loading speed maximized by using entry ordinals in place&lt;br /&gt;
of entry point names. See the OS/2 technical reference literature for&lt;br /&gt;
details.&lt;br /&gt;
     Runtime dynamic links are established by using the module name and the&lt;br /&gt;
entry point name; the external name is not used.&lt;br /&gt;
&lt;br /&gt;
==8  File System Name Space==&lt;br /&gt;
&lt;br /&gt;
File system name space is a fancy term for how names of objects are defined&lt;br /&gt;
in OS/2. The words file system are a hint that OS/2 uses one naming scheme&lt;br /&gt;
both for files and for everything else with a name in ASCII format--system&lt;br /&gt;
semaphores, named shared memory, and so forth. First, we'll discuss the&lt;br /&gt;
syntax of names and how to manipulate them; we'll wind up with a discussion&lt;br /&gt;
of how and why we use one naming scheme for all named objects.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.1  Filenames&lt;br /&gt;
&lt;br /&gt;
Before we discuss OS/2 filenames, let's review the format of filenames&lt;br /&gt;
under MS-DOS. In MS-DOS, filenames are required to fit the 8.3 format: a&lt;br /&gt;
name field (which can contain a maximum of 8 characters) and an extension&lt;br /&gt;
field (which can contain a maximum of 3 characters).&lt;br /&gt;
1. As an aside, these sizes date from a tradition established&lt;br /&gt;
many years ago by Digital Equipment Corporation. Digital's very&lt;br /&gt;
early computers used a technique called RAD50 to store 3&lt;br /&gt;
uppercase letters in one 16-bit word, so their file systems&lt;br /&gt;
allowed a 6-character filename and a 3-character extension. CP/M&lt;br /&gt;
later picked up this filename structure. CP/M didn't use RAD50,&lt;br /&gt;
so, in a moment of generosity, it allowed 8-character filenames;&lt;br /&gt;
but the 3-character extension was kept.&lt;br /&gt;
1 The period character&lt;br /&gt;
(.) between the name and the extension is not part of the filename; it's a&lt;br /&gt;
separator character. The filename can consist of uppercase characters only.&lt;br /&gt;
If a user or an application creates a filename that contains lowercase&lt;br /&gt;
characters or a mixture of uppercase and lowercase, MS-DOS converts the&lt;br /&gt;
filename to all uppercase. If an application presents a filename whose name&lt;br /&gt;
or extension field exceeds the allotted length, MS-DOS silently truncates&lt;br /&gt;
the name to the 8.3 format before using it. MS-DOS establishes and enforces&lt;br /&gt;
these rules and maintains the file system structure on the disks. The file&lt;br /&gt;
system that MS-DOS version 3.x supports is called the FAT (File Allocation&lt;br /&gt;
Table) file system. The following are typical MS-DOS and OS/2 filenames:&lt;br /&gt;
&lt;br /&gt;
   \FOOTBALL\SRC\KERNEL\SCHED.ASM&lt;br /&gt;
&lt;br /&gt;
Football is a development project, so this name describes the source for&lt;br /&gt;
the kernel scheduler for the football project.&lt;br /&gt;
&lt;br /&gt;
   \MEMOS\286\MODESWIT.DOC&lt;br /&gt;
&lt;br /&gt;
is a memo discussing 80286 mode switching.&lt;br /&gt;
&lt;br /&gt;
   \\HAGAR\SCRATCH\GORDONL\FOR_MARK&lt;br /&gt;
&lt;br /&gt;
is a file in my scratch directory on the network server HAGAR, placed there&lt;br /&gt;
for use by Mark.&lt;br /&gt;
     The OS/2 architecture views file systems quite differently. As&lt;br /&gt;
microcomputers become more powerful and are used in more and more ways,&lt;br /&gt;
file system characteristics will be needed that might not be met by a&lt;br /&gt;
built-in OS/2 file system. Exotic peripherals, such as WORM&lt;br /&gt;
2. Write Once, Read Many disks. These are generally laser disks of&lt;br /&gt;
very high capacity, but once a track is written, it cannot be erased.&lt;br /&gt;
These disks can appear to be erasable by writing new copies of files&lt;br /&gt;
and directories each time a change is made, abandoning the old ones.&lt;br /&gt;
2 drives,&lt;br /&gt;
definitely require special file systems to meet their special&lt;br /&gt;
characteristics. For this reason, the file system is not built into OS/2&lt;br /&gt;
but is a closely allied component--an installable file system (IFS). An IFS&lt;br /&gt;
is similar to a device driver; it's a body of code that OS/2 loads at boot&lt;br /&gt;
time. The code talks to OS/2 via a standard interface and provides the&lt;br /&gt;
software to manage a file system on a storage device, including the ability&lt;br /&gt;
to create and maintain directories, to allocate disk space, and so&lt;br /&gt;
on.&lt;br /&gt;
     If you are familiar with OS/2 version 1.0, this information may be&lt;br /&gt;
surprising because you have seen no mention of an IFS in the reference&lt;br /&gt;
manuals. That's because the implementation hasn't yet caught up with the&lt;br /&gt;
architecture. We designed OS/2, from the beginning, to support installable&lt;br /&gt;
file systems, one of which would of course be the familiar FAT file system.&lt;br /&gt;
We designed the file system calls, such as DosOpen and DosClose, with this&lt;br /&gt;
in mind. Although scheduling pressures forced us to ship OS/2 version 1.0&lt;br /&gt;
with only the FAT file system--still built in--a future release will&lt;br /&gt;
include the full IFS package. Although at this writing the IFS release of&lt;br /&gt;
OS/2 has not been announced, this information is included here so that you&lt;br /&gt;
can understand the basis for the system name architecture. Also, this&lt;br /&gt;
information will help you write programs that work well under the new&lt;br /&gt;
releases of OS/2 that contain the IFS.&lt;br /&gt;
     Because the IFS will interpret filenames and pathnames and because&lt;br /&gt;
installable file systems can vary considerably, OS/2&lt;br /&gt;
3. Excluding the IFS part.&lt;br /&gt;
3 doesn't contain much&lt;br /&gt;
specific information about the format and meaning of filenames and&lt;br /&gt;
pathnames. In general, the form and meaning of filenames and pathnames are&lt;br /&gt;
private matters between the user and the IFS; both the application and OS/2&lt;br /&gt;
are simply go-betweens. Neither should attempt to parse or understand&lt;br /&gt;
filenames and pathnames. Applications shouldn't parse names because some&lt;br /&gt;
IFSs will support names in formats other than the 8.3 format. Applications&lt;br /&gt;
shouldn't even assume a specific length for a filename or a pathname. All&lt;br /&gt;
OS/2 filename and pathname interfaces, such as DosOpen, DosFindNext, and so&lt;br /&gt;
on, are designed to take name strings of arbitrary length. Applications&lt;br /&gt;
should use name buffers of at least 256 characters to ensure that a long&lt;br /&gt;
name is not truncated.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.2  Network Access&lt;br /&gt;
&lt;br /&gt;
Two hundred and fifty-six characters may seem a bit extreme for the length&lt;br /&gt;
of a filename, and perhaps it is. But OS/2 filenames are often pathnames,&lt;br /&gt;
and pathnames can be quite lengthy. To provide transparent access to files&lt;br /&gt;
on a LAN (local area network), OS/2 makes the network part of the file&lt;br /&gt;
system name space. In other words, a file's pathname can specify a machine&lt;br /&gt;
name as well as a directory path. An application can issue an open to a&lt;br /&gt;
name string such as \WORK\BOOK.DAT or \\VOGON\TEMP\RECALC.ASM. The first&lt;br /&gt;
name specifies the file BOOK.DAT in the directory WORK on the current drive&lt;br /&gt;
of the local machine; the second name specifies the file RECALC.ASM in the&lt;br /&gt;
directory TEMP on the machine VOGON.&lt;br /&gt;
4. Network naming is a bit more complex than this; the name TEMP&lt;br /&gt;
on the machine VOGON actually refers to an offered network&lt;br /&gt;
resource and might appear in any actual disk directory.&lt;br /&gt;
4 Future releases of the Microsoft LAN&lt;br /&gt;
Manager will make further use of the file system name space, so filenames,&lt;br /&gt;
especially program-generated filenames, can easily become very long.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.3  Name Generation and Compatibility&lt;br /&gt;
&lt;br /&gt;
Earlier, I said that applications should pass on filenames entered by the&lt;br /&gt;
user, ignoring their form. This is, of course, a bit unrealistic. Programs&lt;br /&gt;
often need to generate filenames--to hold scratch files, to hold derivative&lt;br /&gt;
filenames (for example, FOO.OBJ derived from FOO.ASM), and so forth. How&lt;br /&gt;
can an application generate or permute such filenames and yet ensure&lt;br /&gt;
compatibility with all installable file systems? The answer is, of course:&lt;br /&gt;
Use the least common denominator approach. In other words, you can safely&lt;br /&gt;
assume that a new IFS must accept the FAT file system's names (the 8.3&lt;br /&gt;
format) because otherwise it would be incompatible with too many programs.&lt;br /&gt;
So if an application sticks to the 8.3 rules when it creates names, it can&lt;br /&gt;
be sure that it is compatible with future file systems. Unlike MS-DOS,&lt;br /&gt;
OS/2&lt;br /&gt;
5. More properly, the FAT installable file system installed in OS/2.&lt;br /&gt;
5 will not truncate name or extension fields that are too long;&lt;br /&gt;
instead, an error will be returned. The case of a filename will continue to&lt;br /&gt;
be insignificant. Some operating systems, such as UNIX, are case sensitive;&lt;br /&gt;
for example, in UNIX the names &amp;quot;foo&amp;quot; and &amp;quot;Foo&amp;quot; refer to different files.&lt;br /&gt;
This works fine for a system used primarily by programmers, who know that a&lt;br /&gt;
lowercase f is ASCII 66 (SUB16) and that an uppercase F is ASCII&lt;br /&gt;
46 (SUB 16). Nonprogrammers, on the other hand, tend to see f and F as the&lt;br /&gt;
same character. Because most OS/2 users are nonprogrammers, OS/2&lt;br /&gt;
installable file systems will continue to be case insensitive.&lt;br /&gt;
     I said that it was safe if program-generated names adhered to the 8.3&lt;br /&gt;
rule. Program-permuted names are likewise safe if they only substitute&lt;br /&gt;
alphanumeric characters for other alphanumeric characters, for example,&lt;br /&gt;
FOO.OBJ for FOO.ASM. Lengthening filenames is also safe (for example,&lt;br /&gt;
changing FOO.C to FOO.OBJ) if your program checks for &amp;quot;invalid name&amp;quot; error&lt;br /&gt;
codes for the new name and has some way to deal with that possibility. In&lt;br /&gt;
any case, write your program so that it isn't confused by enhanced&lt;br /&gt;
pathnames; in the above substitution cases, the algorithm should work from&lt;br /&gt;
the end of the path string and ignore what comes before.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.4  Permissions&lt;br /&gt;
&lt;br /&gt;
Future releases of OS/2 will use the file system name space for more than&lt;br /&gt;
locating a file; it will also contain the permissions for the file. A&lt;br /&gt;
uniform mechanism will associate an access list with every entry in the&lt;br /&gt;
file system name space. This list will prevent unauthorized access--&lt;br /&gt;
accidental or deliberate--to the named file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.5  Other Objects in the File System Name Space&lt;br /&gt;
&lt;br /&gt;
As we've seen, the file system name space is a valuable device in several&lt;br /&gt;
aspects. First, it allows the generation of a variety of names. You can&lt;br /&gt;
group names together (by putting them in the same directory), and you can&lt;br /&gt;
generate entire families of unique names (by creating a new subdirectory).&lt;br /&gt;
Second, the name space can encompass all files and devices on the local&lt;br /&gt;
machine as well as files and devices on remote machines. Finally, file&lt;br /&gt;
system names will eventually support a flexible access and protection&lt;br /&gt;
mechanism.&lt;br /&gt;
     Thus, it comes as no surprise that when the designers of OS/2 needed a&lt;br /&gt;
naming mechanism to deal with nonfile objects, such as shared memory,&lt;br /&gt;
system semaphores, and named pipes, we chose to use the file system name&lt;br /&gt;
space. One small disadvantage to this decision is that a shared memory&lt;br /&gt;
object cannot have a name identical to that of a system semaphore, a named&lt;br /&gt;
pipe, or a disk file. This drawback is trivial, however, compared with the&lt;br /&gt;
benefits of sharing the file system name space. And, of course, you can use&lt;br /&gt;
separate subdirectory names for each type of object, thus preventing name&lt;br /&gt;
collision.&lt;br /&gt;
     Does this mean that system semaphores, shared memory, and pipes have&lt;br /&gt;
actual file system entries on a disk somewhere? Not yet. The FAT file&lt;br /&gt;
system does not support special object names in its directories. Although&lt;br /&gt;
changing it to do so would be easy, the file system would no longer be&lt;br /&gt;
downward compatible with MS-DOS. (MS-DOS 3.x could not read such disks&lt;br /&gt;
written under OS/2.) Because only the FAT file system is available with&lt;br /&gt;
OS/2 version 1.0, that release keeps special RAM-resident pseudo&lt;br /&gt;
directories to hold the special object names. These names must start with&lt;br /&gt;
\SEM\, \SHAREMEM\, \QUEUES\, and \DEV\ to minimize the chance of name&lt;br /&gt;
collision with a real file when they do become special pseudo files in a&lt;br /&gt;
future release of OS/2.&lt;br /&gt;
     Although all file system name space features--networking and (in the&lt;br /&gt;
future) permissions--apply to all file system name space objects from an&lt;br /&gt;
architectural standpoint, not all permutations may be supported.&lt;br /&gt;
Specifically, supporting named shared memory across the network is very&lt;br /&gt;
costly&lt;br /&gt;
6. The entire shared memory segment must be transferred across&lt;br /&gt;
the network each time any byte within it is changed. Some clever&lt;br /&gt;
optimizations can reduce this cost, but none works well enough to&lt;br /&gt;
be feasible.&lt;br /&gt;
6 and won't be implemented.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==9  Memory Management==&lt;br /&gt;
&lt;br /&gt;
A primary function of any multitasking operating system is to allocate&lt;br /&gt;
system resources to each process according to its need. The scheduler&lt;br /&gt;
allocates CPU time among processes (actually, among threads); the memory&lt;br /&gt;
manager allocates both physical memory and virtual memory.&lt;br /&gt;
&lt;br /&gt;
===9.1  Protection Model===&lt;br /&gt;
&lt;br /&gt;
Although MS-DOS provided a simple form of memory management, OS/2 provides&lt;br /&gt;
memory protection. Under MS-DOS 3.x, for example, a program should ask the&lt;br /&gt;
operating system to allocate a memory area before the program uses it.&lt;br /&gt;
Under OS/2, a program must ask the operating system to allocate a memory&lt;br /&gt;
area before the program uses it. As we discussed earlier, the 80286&lt;br /&gt;
microprocessor contains special memory protection hardware. Each memory&lt;br /&gt;
reference that a program makes explicitly or implicitly references a&lt;br /&gt;
segment selector. The segment selector, in turn, references an entry in the&lt;br /&gt;
GDT or the LDT, depending on the form of the selector. Before any program,&lt;br /&gt;
including OS/2 itself, can reference a memory location, that memory&lt;br /&gt;
location must be described in an LDT or a GDT entry, and the selector for&lt;br /&gt;
that entry must be loaded into one of the four segment registers.&lt;br /&gt;
     This hardware design places some restrictions on how programs can use&lt;br /&gt;
addresses.&lt;br /&gt;
&lt;br /&gt;
     þ  A program cannot address memory not set up for it in the LDT or&lt;br /&gt;
        GDT. The only way to address memory is via the LDT and GDT.&lt;br /&gt;
&lt;br /&gt;
     þ  Each segment descriptor in the LDT and GDT contains the physical&lt;br /&gt;
        address and the length of that segment. A program cannot reference&lt;br /&gt;
        an offset into a segment beyond that segment's length.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't put garbage (arbitrary values) into a segment&lt;br /&gt;
        register. Each time a segment register is loaded, the hardware&lt;br /&gt;
        examines the corresponding LDT and GDT to see if the entry is&lt;br /&gt;
        valid. If a program puts an arbitrary value--for example, the lower&lt;br /&gt;
        half of a floating point number--into a segment register, the&lt;br /&gt;
        arbitrary value will probably point to an invalid LDT or GDT entry,&lt;br /&gt;
        causing a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't execute instructions from within a data segment.&lt;br /&gt;
        Attempting to load a data segment selector into the CS register&lt;br /&gt;
        (usually via a far call or a far jump) causes a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't write into a code segment. Attempting to do so&lt;br /&gt;
        causes a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't perform segment arithmetic. Segment arithmetic&lt;br /&gt;
        refers to activities made possible by the addressing mechanism of&lt;br /&gt;
        the 8086 and 8088 microprocessors. Although they are described as&lt;br /&gt;
        having a segment architecture, they are actually linear address&lt;br /&gt;
        space machines that use offset registers--the so-called segment&lt;br /&gt;
        registers. An 8086 can address 1 MB of memory, which requires a 20-&lt;br /&gt;
        bit address. The processor creates this address by multiplying the&lt;br /&gt;
        16-bit segment value by 16 and adding it to the 16-bit offset&lt;br /&gt;
        value. The result is an address between 0 and 1,048,575 (that is, 1&lt;br /&gt;
        MB).&lt;br /&gt;
1. Actually, it's possible to produce addresses beyond 1 MB&lt;br /&gt;
(2^20) by this method if a large enough segment and offset value&lt;br /&gt;
are chosen. The 8086 ignores the carry into the nonexistent 21st&lt;br /&gt;
address bit, effectively wrapping around such large addresses&lt;br /&gt;
into the first 65 KB-16 bytes of physical memory.&lt;br /&gt;
1 The reason these are not true segments is that they don't&lt;br /&gt;
        have any associated length and their names (that is, their&lt;br /&gt;
        selectors) aren't names at all but physical addresses divided by&lt;br /&gt;
        16. These segment values are actually scaled offsets. An address&lt;br /&gt;
        that has a segment value of 100 and an offset value of 100 (shown&lt;br /&gt;
        as 100(SUB10):100(SUB10)), and the address (99(SUB10):116(SUB10))&lt;br /&gt;
        both refer to the same memory location.&lt;br /&gt;
           Many real mode programs take advantage of this situation. Some&lt;br /&gt;
        programs that keep a great many pointers store them as 20-bit&lt;br /&gt;
        values, decomposing those values into the segment:offset form only&lt;br /&gt;
        when they need to de-reference the pointer. To ensure that certain&lt;br /&gt;
        objects have a specific offset value, other programs choose a&lt;br /&gt;
        matching segment value so that the resultant 20-bit address is&lt;br /&gt;
        correct. Neither technique works in OS/2 protect mode. Each segment&lt;br /&gt;
        selector describes its own segment, a segment with a length and an&lt;br /&gt;
        address that are independent of the numeric value of the segment&lt;br /&gt;
        selector. The memory described by segment N has nothing in common&lt;br /&gt;
        with the memory described by segment N+4 or by any other segment&lt;br /&gt;
        unless OS/2 explicitly sets it up that way.&lt;br /&gt;
&lt;br /&gt;
     The segmentation and protection hardware allows OS/2 to impose further&lt;br /&gt;
restrictions on processes.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot edit or examine the contents of the LDT or the&lt;br /&gt;
        GDT. OS/2 simply declines to build an LDT or GDT selector that a&lt;br /&gt;
        process can use to access the contents of those tables. Certain LDT&lt;br /&gt;
        and GDT selectors describe the contents of those tables themselves,&lt;br /&gt;
        but OS/2 sets them up so that they can only be used by ring 0 (that&lt;br /&gt;
        is, privileged) code.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot hook interrupt vectors. MS-DOS version 3.x&lt;br /&gt;
        programs commonly hook interrupt vectors by replacing the address&lt;br /&gt;
        of the interrupt handler with an address from their own code. Thus,&lt;br /&gt;
        these programs can monitor or intercept system calls made via INT&lt;br /&gt;
        21h, BIOS calls also made via interrupts, and hardware interrupts&lt;br /&gt;
        such as the keyboard and the system clock. OS/2 programs cannot do&lt;br /&gt;
        this. OS/2 declines to set up a segment selector that processes can&lt;br /&gt;
        use to address the interrupt vector table.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot call the ROM BIOS code because no selector&lt;br /&gt;
        addresses the ROM BIOS code. Even if such a selector were&lt;br /&gt;
        available, it would be of little use. The ROM BIOS is coded for&lt;br /&gt;
        real mode execution and performs segment arithmetic operations that&lt;br /&gt;
        are no longer legal. If OS/2 provided a ROM BIOS selector, calls to&lt;br /&gt;
        the ROM BIOS would usually generate GP faults.&lt;br /&gt;
&lt;br /&gt;
     þ  Finally, processes cannot run in ring 0, that is, in privileged&lt;br /&gt;
        mode. Both OS/2 and the 80286 hardware are designed to prevent an&lt;br /&gt;
        application program from ever executing in ring 0. Code running in&lt;br /&gt;
        ring 0 can manipulate the LDT and GDT tables as well as other&lt;br /&gt;
        hardware protection features. If OS/2 allowed processes to run in&lt;br /&gt;
        ring 0, the system could never be stable or secure. OS/2 obtains&lt;br /&gt;
        its privileged (literally) state by being the first code loaded at&lt;br /&gt;
        boot time. The boot process takes place in ring 0 and grants ring 0&lt;br /&gt;
        permission to OS/2 by transferring control to OS/2 while remaining&lt;br /&gt;
        in ring 0. OS/2 does not, naturally, extend this favor to the&lt;br /&gt;
        application programs it loads; it ensures that applications can&lt;br /&gt;
        only run in ring 3 user mode.&lt;br /&gt;
2. Applications can also run in ring 2 (see 18.1 I/O Privilege&lt;br /&gt;
Mechanism).&lt;br /&gt;
2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2  Memory Management API&lt;br /&gt;
&lt;br /&gt;
OS/2 provides an extensive memory management API. This book is not a&lt;br /&gt;
reference manual, so I won't cover all the calls. Instead, I'll focus on&lt;br /&gt;
areas that may not be completely self-explanatory.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.1  Shared Memory&lt;br /&gt;
OS/2 supports two kinds of shared memory--named shared memory and giveaway&lt;br /&gt;
shared memory. In both, the memory object shared is a segment. Only an&lt;br /&gt;
entire segment can be shared; sharing part of a segment is not possible.&lt;br /&gt;
Named shared memory is volatile because neither the name of the named&lt;br /&gt;
shared memory nor the memory itself can exist on the FAT file system. When&lt;br /&gt;
the number of processes using a shared memory segment goes to zero, the&lt;br /&gt;
memory is released. Shared memory can't stay around in the absence of&lt;br /&gt;
client processes; it must be reinitialized via DosAllocShrSeg after a&lt;br /&gt;
period of nonuse.&lt;br /&gt;
     Giveaway shared memory allows processes to share access to the same&lt;br /&gt;
segment. Giveaway shared memory segments don't have names because processes&lt;br /&gt;
can't ask to have access to them; a current user of the segment has to give&lt;br /&gt;
access to the segment to a new client process. The term giveaway is a bit&lt;br /&gt;
of a misnomer because the giving process retains access to the memory--the&lt;br /&gt;
access is &amp;quot;given&amp;quot; but not especially &amp;quot;away.&amp;quot; Giveaway shared memory is not&lt;br /&gt;
as convenient as named shared memory. The owner&lt;br /&gt;
3. One of the owners. Anyone with access to a giveaway shared&lt;br /&gt;
segment can give it away itself.&lt;br /&gt;
3 has to know the PID of the&lt;br /&gt;
recipient and  then communicate the recipient's segment selector (returned&lt;br /&gt;
by DosGiveSeg) to that recipient process via some form of IPC.&lt;br /&gt;
     Despite its limitations, giveaway shared memory has important virtues.&lt;br /&gt;
It's a fast and efficient way for one process to transfer data to another;&lt;br /&gt;
and because access is passed &amp;quot;hand to hand,&amp;quot; the wrong process cannot&lt;br /&gt;
accidentally or deliberately gain access to the segment. Most clients of&lt;br /&gt;
giveaway shared memory don't retain access to the segment once they've&lt;br /&gt;
passed it off; they typically call DosFreeSeg on their handle after they've&lt;br /&gt;
called DosGiveSeg. For example, consider the design of a database dynlink&lt;br /&gt;
subsystem that acts as a front end for a database serving process. As part&lt;br /&gt;
of the dynlink initialization process, the package arranged for its client&lt;br /&gt;
process to share a small named shared memory segment with the database&lt;br /&gt;
process. It might be best to use a named pipe or named shared memory--&lt;br /&gt;
created by the database process--to establish initial communication and&lt;br /&gt;
then use this interface only to set up a private piece of giveaway shared&lt;br /&gt;
memory for all further transactions between the client process (via the&lt;br /&gt;
dynlink subsystem) and the database process. Doing it this way, rather than&lt;br /&gt;
having one named shared segment hold service requests from all clients,&lt;br /&gt;
provides greater security. Because each client has its own separate shared&lt;br /&gt;
memory communications area, an amok client can't damage the communications&lt;br /&gt;
of other clients.&lt;br /&gt;
     When a client process asks the database process to read it a record,&lt;br /&gt;
the database process must use a form of IPC to transfer the data to the&lt;br /&gt;
client. Pipes are too slow for the volume of data that our example&lt;br /&gt;
anticipates; shared memory is the best technique. If we were to use named&lt;br /&gt;
shared memory, the database package would have to create a unique shared&lt;br /&gt;
memory name for each record, allocate the memory, and then communicate the&lt;br /&gt;
name to the client (actually, to the dynlink subsystem called by the&lt;br /&gt;
client) so that it can request access. This process has some drawbacks:&lt;br /&gt;
&lt;br /&gt;
     þ  A new unique shared memory name must be created for each request.&lt;br /&gt;
        We could reuse a single shared memory segment, but this would force&lt;br /&gt;
        the client to copy the data out of the segment before it could make&lt;br /&gt;
        another request--too costly a process for an application that must&lt;br /&gt;
        handle a high volume of data.&lt;br /&gt;
&lt;br /&gt;
     þ  Creating named shared memory segments is generally slower than&lt;br /&gt;
        creating giveaway shared memory segments, especially if a large&lt;br /&gt;
        number of named shared memory objects exist, as would be the case&lt;br /&gt;
        in this scenario. The client spends more time when it then requests&lt;br /&gt;
        access to the segment. Creating named shared memory segments is&lt;br /&gt;
        plenty fast enough when it's done once in a while, but in a high-&lt;br /&gt;
        frequency application such as our example, it could become a&lt;br /&gt;
        bottleneck.&lt;br /&gt;
&lt;br /&gt;
     Instead, the database process can create a giveaway shared memory&lt;br /&gt;
segment, load the data into it, and then give it to the client process. The&lt;br /&gt;
database process can easily learn the client's PID; the dynlink interface,&lt;br /&gt;
which runs as the client process, can include it as part of the data&lt;br /&gt;
request. Likewise, the database process can easily return the new client&lt;br /&gt;
selector to the client. This process is fast and efficient and doesn't bog&lt;br /&gt;
down the system by forcing it to deal with a great many name strings.&lt;br /&gt;
     Note that you must specify, at the time of the DosAllocSeg, that the&lt;br /&gt;
segment might be &amp;quot;given away.&amp;quot; Doing so allows OS/2 to allocate the&lt;br /&gt;
selector in the disjoint space, as we discussed earlier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.2  Huge Memory&lt;br /&gt;
The design of the 80286 microprocessor specifies the maximum size of a&lt;br /&gt;
memory segment as 64 KB. For many programs, this number is far too small.&lt;br /&gt;
For example, the internal representation of a large spreadsheet commonly&lt;br /&gt;
takes up 256 KB or more. OS/2 can do nothing to set up a segment that is&lt;br /&gt;
truly larger than 64 KB, but the OS/2 facility called huge segments&lt;br /&gt;
provides a reasonable emulation of segments larger than 64 KB. The trick is&lt;br /&gt;
that a huge segment of, for example, 200 KB is not a single segment but a&lt;br /&gt;
group of four segments, three of which are 64 KB and a fourth of 8 KB. With&lt;br /&gt;
minimal programming burden, OS/2 allows an application to treat the group&lt;br /&gt;
of four segments as a single huge segment.&lt;br /&gt;
     When a process calls DosAllocHuge to allocate a huge segment, OS/2&lt;br /&gt;
allocates several physical segments, the sum of whose size equals the size&lt;br /&gt;
of the virtual huge segment. All component segments are 64 KB, except&lt;br /&gt;
possibly the last one. Unlike an arbitrary collection of segment selectors,&lt;br /&gt;
DosAllocHuge guarantees that the segment selectors it returns are spaced&lt;br /&gt;
uniformly from each other. The selector of the N+1th component segment is&lt;br /&gt;
that of the Nth segment plus i, where i is a power of two. The value of i&lt;br /&gt;
is constant for any given execution of OS/2, but it may vary between&lt;br /&gt;
releases of OS/2 or as a result of internal configuration during bootup. In&lt;br /&gt;
other words, a program must learn the factor i every time it executes; it&lt;br /&gt;
must not hard code the value. There are three ways to learn this value.&lt;br /&gt;
First, a program can call DosGetHugeShift; second, it can read this value&lt;br /&gt;
from the global infoseg; and third, it can reference this value as the&lt;br /&gt;
undefined absolute externals DOSHUGESHIFT (log(SUB2)(i)) or&lt;br /&gt;
DOSHUGEINCR (i). OS/2 will insert the proper value for these&lt;br /&gt;
externals at loadtime. This last method is the most efficient and is&lt;br /&gt;
recommended. Family API programs should call DosGetHugeShift. Figure 9-1&lt;br /&gt;
illustrates the layout of a 200 KB huge memory object. Selectors n + 4i and&lt;br /&gt;
n + 5i are currently invalid but are reserved for future growth of the&lt;br /&gt;
huge object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ÚÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´                              ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³                              ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+0i ³       ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³               ³       ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³      ³               ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³               ³&lt;br /&gt;
n+1i ³       ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄ¿       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³       ³       ³       ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³      ³       ³       ³       ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³       ³       ÀÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+2i ³       ÃÄÄÄÄÄÄÙ       ³               ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
n+3i ³       ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³&lt;br /&gt;
n+4i ³   �   ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³              ³               ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+5i ³   �   ³                              ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´                              ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÀÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 9-1.  Huge memory objects.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Once an application has the first segment selector of the huge segment&lt;br /&gt;
group, called the base segment, and the value log(SUB2)(i), computing the&lt;br /&gt;
address of the Nth byte in the huge segment is easy. Take the high-order&lt;br /&gt;
word of the value of N (that is, N/64 KB), shift it left by log(SUB2)(i)&lt;br /&gt;
(that is, by the DosHugeShift value), and add the base segment selector&lt;br /&gt;
returned by DosAllocHuge. The resultant value is the segment selector for&lt;br /&gt;
the proper component segment; the low-order 16 bits of i are the offset&lt;br /&gt;
into that segment. This computation is reasonably quick to perform since it&lt;br /&gt;
involves only a shift and an addition.&lt;br /&gt;
     Huge segments can be shrunk or grown via DosReallocHuge. If the huge&lt;br /&gt;
segment is to be grown, creating more component physical segments may be&lt;br /&gt;
necessary. Because the address generation rules dictate which selector this&lt;br /&gt;
new segment may have, growing the huge segment may not be possible if that&lt;br /&gt;
selector has already been allocated for another purpose. DosAllocHuge takes&lt;br /&gt;
a maximum growth parameter; it uses this value to reserve sufficient&lt;br /&gt;
selectors to allow the huge segment to grow that big. Applications should&lt;br /&gt;
not provide an unrealistically large number for this argument because doing&lt;br /&gt;
so will waste LDT selectors.&lt;br /&gt;
     The astute reader will notice that the segment arithmetic of the 8086&lt;br /&gt;
environment is not dead; in a sense, it's been resurrected by the huge&lt;br /&gt;
segment mechanism. Applications written for the 8086 frequently use this&lt;br /&gt;
technique to address memory regions greater than 64 KB, using a shift value&lt;br /&gt;
of 12. In other words, if you add 2^12 to an 8086 segment register value,&lt;br /&gt;
the segment register will point to an address 2^12*16, or 64 KB, further in&lt;br /&gt;
physical memory. The offset value between the component segment values was&lt;br /&gt;
always 4096 because of the way the 8086 generated addresses. Although the&lt;br /&gt;
steps involved in computing the segment value are the same in protect mode,&lt;br /&gt;
what's actually happening is considerably different. When you do this&lt;br /&gt;
computation in protect mode, the segment selector value has no inherent&lt;br /&gt;
relationship to the other selectors that make up the huge object. The trick&lt;br /&gt;
only works because OS/2 has arranged for equally spaced-out selectors to&lt;br /&gt;
exist and for each to point to an area of physical memory of the&lt;br /&gt;
appropriate size. Figure 9-2 illustrates the similarities and differences&lt;br /&gt;
between huge model addressing in real and protect modes. The application&lt;br /&gt;
code sequence is identical: A segment selector is computed by adding N*i to&lt;br /&gt;
the base selector. In real mode i is always 4096; in protect mode OS/2&lt;br /&gt;
provides i.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
          REAL MODE        |             PROTECT MODE&lt;br /&gt;
                           |&lt;br /&gt;
           Physical        |&lt;br /&gt;
            Memory         |            Segment&lt;br /&gt;
           ÚÄÄÄÄÄÄ¿        |            Selector         Physical&lt;br /&gt;
           ³      ³        |             Table            Memory&lt;br /&gt;
           ³      ³        |            ÚÄÄÄÄÄÄ¿         ÚÄÄÄÄÄÄ¿&lt;br /&gt;
n+0i ÄÄÄÄÄ�³      ³±       |            ³      ³  ÚÄÄÄÄÄ�³      ³&lt;br /&gt;
           ³      ³±�64 KB | n+0i ÄÄÄÄÄ�³      ÃÄÄÅÄÄÄ¿  ÀÄÄÄÄÄÄÙ&lt;br /&gt;
n+1i ÄÄÄÄÄ�³      ³±       |            ³      ³  ³   ³&lt;br /&gt;
           ³      ³        | n+1i ÄÄÄÄÄ�³      ÃÄÄÅÄ¿ ³  ÚÄÄÄÄÄÄ¿&lt;br /&gt;
n+2i ÄÄÄÄÄ�³      ³        |            ³      ³  ³ ³ ÀÄ�³      ³&lt;br /&gt;
           ³      ³        | n+2i ÄÄÄÄÄ�³      ÃÄÄÙ ³    ÀÄÄÄÄÄÄÙ&lt;br /&gt;
n+3i ÄÄÄÄÄ�³      ³        |            ³      ³    ³    ÚÄÄÄÄÄÄ¿&lt;br /&gt;
           ³      ³        | n+3i ÄÄÄÄÄ�³      ÃÄÄÄÄÅÄÄÄ�³      ³&lt;br /&gt;
           ³      ³        |            ³      ³    ³    ÀÄÄÄÄÄÄÙ&lt;br /&gt;
           ³      ³        |            ³      ³    ³    ÚÄÄÄÄÄÄ¿&lt;br /&gt;
           ³      ³        |            ÀÄÄÄÄÄÄÙ    ÀÄÄÄ�³      ³&lt;br /&gt;
           ³      ³        |        i is set by OS/2     ÀÄÄÄÄÄÄÙ&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ÀÄÄÄÄÄÄÙ        |&lt;br /&gt;
           i = 4096        |&lt;br /&gt;
&lt;br /&gt;
Figure 9-2.  Huge model addressing in real mode and in protect mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Although the similarity between 8086 segment arithmetic and OS/2 huge&lt;br /&gt;
segments is only apparent, it does make it easy to write a program as a&lt;br /&gt;
dual mode application. By using the shift value of 12 in real mode and&lt;br /&gt;
using the OS/2 supplied value in protect mode, the same code functions&lt;br /&gt;
correctly in either mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.3  Executing from Data Segments&lt;br /&gt;
We saw that OS/2 provides the huge segment mechanism to get around the&lt;br /&gt;
segment size restriction imposed by the hardware. OS/2 likewise circumvents&lt;br /&gt;
another hardware restriction--the inability to execute code from data&lt;br /&gt;
segments. Although the demand loading, the discarding, and the swapping of&lt;br /&gt;
code segments make one use of running code from data segments--code&lt;br /&gt;
overlays--obsolete, the capability is still needed. Some high-performance&lt;br /&gt;
programs--the presentation manager, for example--compile &amp;quot;on the fly&amp;quot;&lt;br /&gt;
special code to perform time-critical tasks, such as flipping bits in EGA&lt;br /&gt;
display memory. The optimal sequence may differ depending on several&lt;br /&gt;
factors, so a program may need to compile such code and execute it, gaining&lt;br /&gt;
a significant increase in efficiency over some other approach. OS/2&lt;br /&gt;
supports this need by means of the DosCreateCSAlias call.&lt;br /&gt;
     When DosCreateCSAlias is called with a selector for a data segment, it&lt;br /&gt;
creates a totally different code segment selector (in the eyes of 80286&lt;br /&gt;
hardware) that by some strange coincidence points to exactly the same&lt;br /&gt;
memory locations as does the data segment selector. As a result, code is&lt;br /&gt;
not actually executing from a data segment but from a code segment. Because&lt;br /&gt;
the code segment exactly overlaps that other data segment, the desired&lt;br /&gt;
effect is achieved. The programmer need only be careful to use the data&lt;br /&gt;
selector when writing the segment and to use the code selector when&lt;br /&gt;
executing it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.4  Memory Suballocation&lt;br /&gt;
All memory objects discussed so far have been segments. OS/2 provides a&lt;br /&gt;
facility called memory suballocation that allocates pieces of memory from&lt;br /&gt;
within an application's segment. Pieces of memory can be suballocated from&lt;br /&gt;
within a segment, grown, shrunk, and released. OS/2 uses a classic heap&lt;br /&gt;
algorithm to do this. The DosSubAlloc call uses space made available from&lt;br /&gt;
earlier DosSubFrees when possible, growing the segment as necessary when&lt;br /&gt;
the free heap space is insufficient. We will call the pieces of memory&lt;br /&gt;
returned by DosSubAlloc heap objects.&lt;br /&gt;
     The memory suballocation package works within the domain of a process.&lt;br /&gt;
The suballocation package doesn't allocate the memory from some &amp;quot;system&lt;br /&gt;
pool&amp;quot; outside the process's address space, as does the segment allocator.&lt;br /&gt;
The suballocation package doesn't even allocate segments; it manages only&lt;br /&gt;
segments supplied by and owned by (or at least accessible to) the caller.&lt;br /&gt;
This is a feature because memory protection is on a per-segment basis. If&lt;br /&gt;
the suballocation package were to get its space from some system global&lt;br /&gt;
segment, a process that overwrote its heap object could damage one&lt;br /&gt;
belonging to another process. Figure 9-3 illustrates memory suballocation.&lt;br /&gt;
It shows a segment j being suballocated. H is the suballocation header; the&lt;br /&gt;
shaded areas are free space.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       ÚÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
       ³  Segment  ³       ³  Segment  ³       ³  Segment  ³&lt;br /&gt;
       ³     i     ³       ³     j     ³       ³     k     ³&lt;br /&gt;
       ÀÄÄÄÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                          /            \&lt;br /&gt;
                        /                \&lt;br /&gt;
                      /                    \&lt;br /&gt;
                    /                        \&lt;br /&gt;
                  /                            \&lt;br /&gt;
                /                                \&lt;br /&gt;
              /                                    \&lt;br /&gt;
            /                                        \&lt;br /&gt;
          /                                            \&lt;br /&gt;
        /                                                \&lt;br /&gt;
      /                                                    \&lt;br /&gt;
    /                                                        \&lt;br /&gt;
  /                                                            \&lt;br /&gt;
ÚÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÂÂÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÂÄÄÄÄÄÄÄÂÄÄÄÂÄÄÄÄÂÄÄÄÂÄÄÄÄÄÄÄ¿&lt;br /&gt;
³H ³  ³     ³°°°³³°°°°³           ³ ³°°°°°°°³   ³    ³   ³°°°°°°°³&lt;br /&gt;
³  ³  ³     ³°°°³³°°°°³           ³ ³°°°°°°°³   ³    ³   ³°°°°°°°³&lt;br /&gt;
ÀÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÁÁÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÁÄÄÄÁÄÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 9-3.  Memory suballocation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     We said that the memory suballocator subdivides segments that are&lt;br /&gt;
accessible to the client process. This means that you can use it to&lt;br /&gt;
subdivide space in a shared memory segment. Such a technique can be handy&lt;br /&gt;
when two or more processes are using a shared memory segment for&lt;br /&gt;
intercommunication, but there is risk because an error in one process can&lt;br /&gt;
easily corrupt the heap objects of another.&lt;br /&gt;
     Earlier, in the discussion of dynamic link subsystems, we described&lt;br /&gt;
facilities and techniques for writing a reliable subsystem. The OS/2 memory&lt;br /&gt;
suballocation package is a good example of such a subsystem, so let's look&lt;br /&gt;
at its workings more closely. The first try at the suballocation package&lt;br /&gt;
produced a straightforward heap allocator, much like the one in a C or&lt;br /&gt;
Pascal runtime library. It maintained a free chain of heap objects and&lt;br /&gt;
allocated them at its client's request. If the closest-size free heap&lt;br /&gt;
object was still bigger than the request, it was split into an allocated&lt;br /&gt;
part and a free part. Freed heap objects were coalesced with any adjacent&lt;br /&gt;
free objects. The suballocation package took a segment pointer and some&lt;br /&gt;
other arguments and returned some values--an offset and the changed data in&lt;br /&gt;
the segment itself where the heap headers were stored. If we stretch things&lt;br /&gt;
a little and consider the changed state of the supplied data segment as a&lt;br /&gt;
returned value, then the suballocation package at this stage is much like a&lt;br /&gt;
function: It has no state of its own; it merely returns values computed&lt;br /&gt;
only from the input arguments. This simple suballocation dynlink routine&lt;br /&gt;
uses no global data segments and doesn't even need an instance data&lt;br /&gt;
segment.&lt;br /&gt;
     This simple implementation has an important drawback: More than one&lt;br /&gt;
process can't safely use it to manage a shared memory segment; likewise,&lt;br /&gt;
multiple threads within one process can't use it. The heap free list is a&lt;br /&gt;
critical section; if multiple threads call the suballocator on the same&lt;br /&gt;
segment, the heap free list can become corrupted. This problem necessitated&lt;br /&gt;
upgrading the suballocation package to use a semaphore to protect the&lt;br /&gt;
critical section. If we didn't want to support suballocation of shared&lt;br /&gt;
memory and were only worried about multiple threads within a task, we could&lt;br /&gt;
use RAM semaphores located in the managed segment itself to protect the&lt;br /&gt;
critical section. The semaphore might be left set if the process died&lt;br /&gt;
unexpectedly, but the managed segment isn't shared. It's going to be&lt;br /&gt;
destroyed in any case, so we don't care.&lt;br /&gt;
     But, even in this simple situation of managing only privately owned&lt;br /&gt;
segments, we must concern ourselves with some special situations. One&lt;br /&gt;
problem is signals: What if the suballocator is called with thread 1, and a&lt;br /&gt;
signal (such as SIGINT, meaning that the user pressed Ctrl-C) comes in?&lt;br /&gt;
Thread 1 is interrupted from the suballocation critical section to execute&lt;br /&gt;
the signal handler. Often signal handlers return to the interrupted code,&lt;br /&gt;
and all is well. But what if the signal handler does not return but jumps&lt;br /&gt;
to the application's command loop? Or what if it does return, but before it&lt;br /&gt;
does so calls the memory suballocator? In these two cases, we'd have a&lt;br /&gt;
deadlock on the critical section. We can solve these problems by using the&lt;br /&gt;
DosHoldSignal function. DosHoldSignal does for signals what the CLI&lt;br /&gt;
instruction does for hardware interrupts: It holds them off for a short&lt;br /&gt;
time. Actually, it holds them off forever unless the application releases&lt;br /&gt;
them, but holding signals for more than a second or two is poor practice.&lt;br /&gt;
If you precede the critical section's semaphore claim call with a signal&lt;br /&gt;
hold and follow the critical section's semaphore release call with a signal&lt;br /&gt;
release, you're protected from deadlocks caused by signal handling.&lt;br /&gt;
     Note that unlike the CLI instruction, DosHoldSignal calls nest. OS/2&lt;br /&gt;
counts the number of DosHoldSignal &amp;quot;hold&amp;quot; calls made and holds signals off&lt;br /&gt;
until an equal number of &amp;quot;release&amp;quot; calls are issued. This means that a&lt;br /&gt;
routine can safely execute a hold/release pair without affecting the state&lt;br /&gt;
of its calling code. If the caller had signals held at the time of the&lt;br /&gt;
call, they will remain held. If signals were free at the time of the call,&lt;br /&gt;
the callee's &amp;quot;release&amp;quot; call restores them to that state.&lt;br /&gt;
     Whenever dynlink packages make any call that changes the state of the&lt;br /&gt;
process or the thread, they must be sure to restore that state before they&lt;br /&gt;
return to their caller. Functions that nest, such as DosHoldSignal,&lt;br /&gt;
accomplish this automatically. For other functions, the dynlink package&lt;br /&gt;
should explicitly discover and remember the previous state so that it can&lt;br /&gt;
be restored.&lt;br /&gt;
     Our problems aren't over though. A second problem is brought about by&lt;br /&gt;
the DosExitList facility. If a client process's thread is in the&lt;br /&gt;
suballocation package's critical section and the client terminates&lt;br /&gt;
suddenly--it could be killed externally or have a GP fault--the process&lt;br /&gt;
might not die immediately. If any DosExitList handlers are registered, they&lt;br /&gt;
will be called. They might call the memory suballocator, and once again we&lt;br /&gt;
face deadlock. We could solve this situation with the classic approach of&lt;br /&gt;
making a bug into a feature: Document that the suballocator can't be called&lt;br /&gt;
at exitlist time. This may make sense for some dynlink subsystems, but it's&lt;br /&gt;
too restrictive for an important OS/2 facility. We've got to deal with this&lt;br /&gt;
problem too.&lt;br /&gt;
     The DosHoldSignal trick won't help us here. It would indeed prevent&lt;br /&gt;
external kills, but it would not prevent GP faults and the like. We could&lt;br /&gt;
say, &amp;quot;A program that GP faults is very sick, so all bets are off.&amp;quot; This&lt;br /&gt;
position is valid, except that if the program or one of its dynlink&lt;br /&gt;
subsystems uses DosExitList and the DosExitList handler tries to allocate&lt;br /&gt;
or release a heap object, the process will hang and never terminate&lt;br /&gt;
correctly. This is unacceptable because the user would be forced to reboot&lt;br /&gt;
to get rid of the moribund application. The answer is to use a system&lt;br /&gt;
semaphore rather than a RAM semaphore to protect the memory segment. System&lt;br /&gt;
semaphores are a bit slower than RAM semaphores, but they have some extra&lt;br /&gt;
features. One is that they can be made exclusive; only the thread that owns&lt;br /&gt;
the semaphore can release it. Coupled with this is an &amp;quot;owner death&amp;quot;&lt;br /&gt;
notification facility that allows a process's DosExitList handler an&lt;br /&gt;
opportunity to determine that one of its threads has orphaned a semaphore&lt;br /&gt;
(see 16.2 Data Integrity for details). Our suballocation package can now&lt;br /&gt;
protect itself by using exclusive system semaphores to protect its critical&lt;br /&gt;
section and by registering a DosExitList handler to release that semaphore.&lt;br /&gt;
The exitlist code can discover if a thread in its process has orphaned the&lt;br /&gt;
semaphore and, if so, can release it. Of course, releasing the semaphore&lt;br /&gt;
won't help if the heap headers are in an inconsistent state. You can write&lt;br /&gt;
the suballocation package so that the heap is never in an inconsistent&lt;br /&gt;
state, or you can write it to keep track of the modified state so that the&lt;br /&gt;
exitlist handler can repair the heap structure.&lt;br /&gt;
     In this later case, be sure the DosExitList handler you establish to&lt;br /&gt;
clean up the heap is called first (see DosExitList documentation).&lt;br /&gt;
     Finally, even if we decide that the client application won't be&lt;br /&gt;
allowed to issue suballocation requests during its own exitlist processing,&lt;br /&gt;
we want the memory suballocator to support allocating a shared segment&lt;br /&gt;
among many different processes. Because of this, the actual OS/2&lt;br /&gt;
suballocation package makes use of DosExitList so that the suballocation&lt;br /&gt;
structure and semaphores can be cleaned up should a client thread terminate&lt;br /&gt;
while in the suballocation critical section.&lt;br /&gt;
     The suballocation dynlink package does more than illustrate subsystem&lt;br /&gt;
design; it also illustrates the value of a system architecture that uses&lt;br /&gt;
dynlinks as a standard system interface, regardless of the type of code&lt;br /&gt;
that provides the service. As you have seen, the memory suballocation&lt;br /&gt;
package released with OS/2 version 1.0 doesn't reside in the kernel; it's&lt;br /&gt;
effectively a subroutine package. OS/2 in an 80286 environment will&lt;br /&gt;
undoubtedly preserve this approach in future releases, but a forthcoming&lt;br /&gt;
80386 version of OS/2 may not. The 80386 architecture supports paged&lt;br /&gt;
virtual memory, so memory swapping (actually, paging) can take place on&lt;br /&gt;
part of a segment. This future paging environment may precipitate some&lt;br /&gt;
changes in the memory suballocator. Perhaps we'll want to rearrange the&lt;br /&gt;
heap for better efficiency with paging, or perhaps the OS/2 kernel will&lt;br /&gt;
want to become involved so that it can better anticipate paging demands. In&lt;br /&gt;
any case, any future release of OS/2 has complete flexibility to upgrade&lt;br /&gt;
the memory suballocation package in any externally compatible fashion,&lt;br /&gt;
thanks to the standard interface provided by dynamic links.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.3  Segment Swapping&lt;br /&gt;
&lt;br /&gt;
One of the most important features of the 80286 memory management hardware&lt;br /&gt;
is swapping support. Swapping is a technique by which some code or data&lt;br /&gt;
segments in memory are written to a disk file, thus allowing the memory&lt;br /&gt;
they were using to be reclaimed for another purpose. Later, the swapped-out&lt;br /&gt;
code or data is reloaded into memory. This technique lets you run more&lt;br /&gt;
programs than can simultaneously fit in memory; all you need is enough&lt;br /&gt;
memory to hold the programs that are running at that particular moment.&lt;br /&gt;
Quiescent programs can be swapped to disk to make room for active ones.&lt;br /&gt;
Later, when the swapped programs become active, OS/2 reads them in and&lt;br /&gt;
resumes them. If necessary OS/2 first makes memory available by swapping&lt;br /&gt;
out another quiescent program.&lt;br /&gt;
     Although I used the word program above, swapping is actually done on a&lt;br /&gt;
segment basis. Segments are swapped out individually and completely; the&lt;br /&gt;
OS/2 swapping code doesn't pay attention to relationships between segments&lt;br /&gt;
(they aren't swapped in groups), and the 80286 hardware does not allow only&lt;br /&gt;
part of a segment to be swapped. I simplified the concept a bit in the&lt;br /&gt;
above paragraph. You need not swap out an entire process; you can swap out&lt;br /&gt;
some segments and leave others in memory. OS/2 can and commonly does run a&lt;br /&gt;
process when some of its segments are swapped out. As long as a process&lt;br /&gt;
does not try to use the swapped-out segments, it runs unhindered. If a&lt;br /&gt;
process references a swapped-out segment, the 80286 hardware generates a&lt;br /&gt;
special trap that OS/2 intercepts. The segment fault trap handler swaps in&lt;br /&gt;
the missing segment, first swapping out some other if need be, and then the&lt;br /&gt;
process resumes where it left off. Segment faulting is invisible to a&lt;br /&gt;
process; the process executes normally, except that a segment load&lt;br /&gt;
instruction takes on the order of 30 milliseconds instead of the usual 3&lt;br /&gt;
microseconds.&lt;br /&gt;
     When memory is depleted and a segment must be swapped, OS/2 has to&lt;br /&gt;
choose one to swap out. Making the right choice is important; for example,&lt;br /&gt;
consider a process that alternates references between segment A and segment&lt;br /&gt;
B. If A is swapped out, a poorly designed system might choose B to swap out&lt;br /&gt;
to make room for A. After a few instructions are executed, B has to be&lt;br /&gt;
swapped in. If A is in turn swapped out to make room for B, the system&lt;br /&gt;
would soon spend all its time swapping A and B to and from the disk. This&lt;br /&gt;
is called thrashing, and thrashing can destroy system performance. In other&lt;br /&gt;
words, the effect of swapping is to make some segment loads take 10,000&lt;br /&gt;
times longer than they would if the segment were in memory. Although the&lt;br /&gt;
number 10,000 seems very large, the actual time of about 30 milliseconds is&lt;br /&gt;
not, as long as we don't have to pay those 30 milliseconds very often.&lt;br /&gt;
     A lot hinges on choosing segments to swap out that won't be referenced&lt;br /&gt;
in the near future. OS/2 uses the LRU (Least Recently Used) scheme to&lt;br /&gt;
determine which segment it will swap out. The ideal choice is the segment--&lt;br /&gt;
among those currently in memory--that will be referenced last because this&lt;br /&gt;
postpones the swap-in of that segment as long as possible. Unfortunately,&lt;br /&gt;
it's mathematically provable that no operating system can predict the&lt;br /&gt;
behavior of arbitrary processes. Instead, operating systems try to make an&lt;br /&gt;
educated guess as to which segment in memory is least likely to be&lt;br /&gt;
referenced in the immediate future. The LRU scheme is precisely that--a&lt;br /&gt;
good guess. OS/2 figures that if a segment hasn't been used in a long time&lt;br /&gt;
then it probably won't be used for a long time yet, so it swaps out the&lt;br /&gt;
segment that was last used the longest time ago--in other words, the least&lt;br /&gt;
recently used segment.&lt;br /&gt;
     Of course, it's easy to construct an example where the LRU decision is&lt;br /&gt;
the wrong one or even the worst one. The classic example is a program that&lt;br /&gt;
references, round robin, N segments when there is room in memory for only&lt;br /&gt;
N-1. When you attempt to make room for segment I, the least recently used&lt;br /&gt;
segment will be I+1, which in fact is the segment that will next be used. A&lt;br /&gt;
discussion of reference locality and working set problems, as these are&lt;br /&gt;
called, is beyond the scope of this book. Authors of programs that will&lt;br /&gt;
make repetitious accesses to large bodies of data or code should study the&lt;br /&gt;
available literature on virtual memory systems. Remember, on an 80286, OS/2&lt;br /&gt;
swaps only on a segment basis. A future 80386 release of OS/2 will swap, or&lt;br /&gt;
page, on a 4 KB page basis.&lt;br /&gt;
     The swapping algorithm is strictly LRU among all swap-eligible&lt;br /&gt;
segments in the system. Thread/process priority is not considered; system&lt;br /&gt;
segments that are marked swappable get no special treatment. Some system&lt;br /&gt;
segments are marked nonswappable, however. For example, swapping out the&lt;br /&gt;
OS/2 code that performs swap-ins would be embarrassing. Likewise, the disk&lt;br /&gt;
driver code for the swapping disk must not be swapped out. Some kernel and&lt;br /&gt;
device driver code is called at interrupt time; this is never swapped&lt;br /&gt;
because of the swap-in delay and because of potential interference between&lt;br /&gt;
the swapped-out interrupt handling code and the interrupt handling code of&lt;br /&gt;
the disk driver that will do the swap-in. Finally, some kernel code is&lt;br /&gt;
called in real mode in response to requests from the 3x box. No real mode&lt;br /&gt;
code can be swapped because the processor does not support segment faults&lt;br /&gt;
when running in real mode.&lt;br /&gt;
     The technique of running more programs then there is RAM to hold them&lt;br /&gt;
is called memory overcommit. OS/2 has to keep careful track of the degree&lt;br /&gt;
of overcommit so that it doesn't find itself with too much of a good thing-&lt;br /&gt;
-not enough free RAM, even with swapping, to swap in a swapped-out process.&lt;br /&gt;
Such a situation is doubly painful: Not only can the user not access or&lt;br /&gt;
save the data that he or she has spent the last four hours working on, but&lt;br /&gt;
OS/2 can't even tell the program what's wrong because it can't get the&lt;br /&gt;
program into memory to run it. To prevent this, OS/2 keeps track of its&lt;br /&gt;
commitments and overcommitments in two ways. First, before it starts a&lt;br /&gt;
process, OS/2 ensures that there is enough swap space to run it. Second, it&lt;br /&gt;
ensures that there is always enough available RAM to execute a swapped-out&lt;br /&gt;
process.&lt;br /&gt;
     At first glance, knowing if RAM is sufficient to run a process seems&lt;br /&gt;
simple--either the process fits into memory or it doesn't. Life is a bit&lt;br /&gt;
more complicated than that under OS/2 because the segments of a program or&lt;br /&gt;
a dynlink library may be marked for demand loading. This means that they&lt;br /&gt;
won't come in when the program starts executing but may be called in later.&lt;br /&gt;
Obviously, once a program starts executing, it can make nearly unlimited&lt;br /&gt;
demands for memory. When a program requests a memory allocation, however,&lt;br /&gt;
OS/2 can return an error code if available memory is insufficient. The&lt;br /&gt;
program can then deal with the problem: make do with less, refuse the&lt;br /&gt;
user's command, and so forth.&lt;br /&gt;
     OS/2 isn't concerned about a program's explicit memory requests&lt;br /&gt;
because they can always be refused; the implicit memory requests are the&lt;br /&gt;
problem--faulting in a demand load segment, for example. Not only is there&lt;br /&gt;
no interface to give the program an error code,&lt;br /&gt;
4. A demand load segment is faulted in via a &amp;quot;load segment register&amp;quot;&lt;br /&gt;
instruction. These CPU instructions don't return error codes!&lt;br /&gt;
4 but the program may be&lt;br /&gt;
unable to proceed without the segment. As a result, when a program is first&lt;br /&gt;
loaded (via a DosExecPgm call), OS/2 sums the size of all its impure&lt;br /&gt;
segments even if they are marked for &amp;quot;load on demand.&amp;quot; The same computation&lt;br /&gt;
is done for all the loadtime dynlink libraries it references and for all&lt;br /&gt;
the libraries they reference and so on. This final number, plus the&lt;br /&gt;
internal system per-process overhead, is the maximum implicit memory demand&lt;br /&gt;
of the program. If that much free swap space is available, the program can&lt;br /&gt;
start execution.&lt;br /&gt;
     You have undoubtedly noticed that I said we could run the program if&lt;br /&gt;
there was enough swap space. But a program must be in RAM to execute,&lt;br /&gt;
so why don't we care about the amount of available RAM space? We&lt;br /&gt;
do care. Not about the actual amount of free RAM when we start a program,&lt;br /&gt;
but about the amount of RAM that can be made free--by swapping--if needed.&lt;br /&gt;
If some RAM contains a swappable segment, then we can swap it&lt;br /&gt;
because we set aside enough swap space for the task. Pure segments, by the&lt;br /&gt;
way, are not normally swapped. In lieu of a swap-out, OS/2 simply discards&lt;br /&gt;
them. When it's time to swap them in, OS/2 reloads them from their original&lt;br /&gt;
.EXE or .DLL files.&lt;br /&gt;
5. An exception to this is programs that were executed from removable&lt;br /&gt;
media. OS/2 preloads all pure segments from such .EXE and .DLL files&lt;br /&gt;
and swaps them as necessary. This prevents certain deadlock problems&lt;br /&gt;
involving the hard error daemon and the volume management code.&lt;br /&gt;
5&lt;br /&gt;
     Because not all segments of a process need to be in memory for the&lt;br /&gt;
process to execute, we don't have to ensure enough free RAM for the entire&lt;br /&gt;
process, just enough so that we can simultaneously load six 64 KB segments-&lt;br /&gt;
-the maximum amount of memory needed to run any process. The numbers 6 and&lt;br /&gt;
64 KB are derived from the design of the 80286. To execute even a single&lt;br /&gt;
instruction of a process, all the segments selected by the four segment&lt;br /&gt;
registers must be in memory. The other two necessary segments come from the&lt;br /&gt;
worst case scenario of a program trying to execute a far return instruction&lt;br /&gt;
from a ring 2 segment (see 18.1 I/O Privilege Mechanism). The four&lt;br /&gt;
segments named in the registers must be present for the instruction to&lt;br /&gt;
start, and the two new segments--CS and SS--that the far return instruction&lt;br /&gt;
will reference must be present for the instruction to complete. That makes&lt;br /&gt;
six; the 64 KB comes from the maximum size a segment can reach. As a&lt;br /&gt;
result, as long as OS/2 can free up those six 64 KB memory regions, by&lt;br /&gt;
swapping and discarding if necessary, any swapped-out program can execute.&lt;br /&gt;
     Naturally, if that were the only available memory and it had to be&lt;br /&gt;
shared by all running processes, system response would be very poor.&lt;br /&gt;
Normally, much more RAM space is available. The memory overcommit code is&lt;br /&gt;
concerned only that all processes can run; it won't refuse to start a&lt;br /&gt;
process because it might execute slowly. It could be that the applications&lt;br /&gt;
that a particular user runs and their usage pattern are such that the user&lt;br /&gt;
finds the performance acceptable and thus hasn't bought more memory. Or&lt;br /&gt;
perhaps the slowness is a rare occurrence, and the user is willing to&lt;br /&gt;
accept it just this once. In general, if the system thrashes--&lt;br /&gt;
spends too much time swapping--it's a soft failure: The user knows what's&lt;br /&gt;
wrong, the user knows what to do to make it get better (run fewer programs&lt;br /&gt;
or buy more memory), and the user can meanwhile continue to work.&lt;br /&gt;
     Clearly, because all segments of the applications are swappable and&lt;br /&gt;
because we've ensured that the swap space is sufficient for all of them,&lt;br /&gt;
initiating a new process doesn't consume any of our free or freeable RAM.&lt;br /&gt;
It's the device drivers and their ability to allocate nonswappable segments&lt;br /&gt;
that can drain the RAM pool. For this reason, OS/2 may refuse to load a&lt;br /&gt;
device driver or to honor a device driver's memory allocation request if to&lt;br /&gt;
do so would leave less than six 64 KB areas of RAM available.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.3.1  Swapping Miscellany&lt;br /&gt;
The system swap space consists of a special file, called SWAPPER.DAT,&lt;br /&gt;
created at boot time. The location of the file is described in the&lt;br /&gt;
CONFIG.SYS file. OS/2 may not allocate the entire maximum size of the swap&lt;br /&gt;
file initially; instead, it may allocate a smaller size and grow the swap&lt;br /&gt;
file to its maximum size if needed. The swap file may grow, but in OS/2&lt;br /&gt;
version 1.0 it never shrinks.&lt;br /&gt;
     The available swap space in the system is more than the maximum size&lt;br /&gt;
of the swap file; it also includes extra RAM. Clearly, a system with 8 MB&lt;br /&gt;
of RAM and a 200 KB swap file should be able to run programs that consume&lt;br /&gt;
more than 200 KB. After setting aside the memory consumed by nonswappable&lt;br /&gt;
segments and our six 64 KB reserved areas, the remaining RAM is considered&lt;br /&gt;
part of the swap file for memory overcommit accounting purposes.&lt;br /&gt;
     We mentioned in passing that memory used in real mode can't be&lt;br /&gt;
swapped. This means that the entire 3x box memory area is nonswappable. In&lt;br /&gt;
fact, the casual attitude of MS-DOS applications toward memory allocation&lt;br /&gt;
forces OS/2 to keep a strict boundary between real mode and protect mode&lt;br /&gt;
memory. Memory below the RMSIZE value specified in CONFIG.SYS belongs&lt;br /&gt;
exclusively to the real mode program, minus that consumed by the device&lt;br /&gt;
drivers and the parts of the OS/2 kernel that run in real mode.&lt;br /&gt;
     Early in the development of OS/2, attempts were made to put protect&lt;br /&gt;
mode segments into any unused real mode memory, but we abandoned this&lt;br /&gt;
approach. First, because the risk was great that the real mode program&lt;br /&gt;
might overwrite part of the segment. Although this is technically a bug on&lt;br /&gt;
the part of the real mode application, such bugs generally do not affect&lt;br /&gt;
program execution in an MS-DOS environment because that memory is unused at&lt;br /&gt;
the time. Thus, such bugs undoubtedly exist unnoticed in today's MS-DOS&lt;br /&gt;
applications, waiting to wreak havoc in the OS/2 environment.&lt;br /&gt;
     A second reason concerns existing real mode applications having been&lt;br /&gt;
written for a single-tasking environment. Such an application commonly asks&lt;br /&gt;
for 1 MB of memory, a request that must be refused. The refusal, however,&lt;br /&gt;
also specifies the amount of memory available at the time of the call. Real&lt;br /&gt;
mode applications then turn around and ask for that amount, but they don't&lt;br /&gt;
check to see if an &amp;quot;insufficient memory&amp;quot; error code was returned from the&lt;br /&gt;
second call. After all, how could such a code be returned? The operating&lt;br /&gt;
system has just said that the memory was available. This coding sequence&lt;br /&gt;
can cause disaster in a multitasking environment where the memory might&lt;br /&gt;
have been allocated elsewhere between the first and second call from the&lt;br /&gt;
application. This is another reason OS/2 sets aside a fixed region of&lt;br /&gt;
memory for the 3x box and never uses it for other purposes, even if it&lt;br /&gt;
appears to be idle.&lt;br /&gt;
     We mentioned that OS/2's primary concern is that programs be able to&lt;br /&gt;
execute at all; whether they execute well is the user's problem. This&lt;br /&gt;
approach is acceptable because OS/2 is a single-user system. Multiuser&lt;br /&gt;
systems need to deal with thrashing situations because the users that&lt;br /&gt;
suffer from thrashing may not be the ones who created it and may be&lt;br /&gt;
powerless to alleviate it. In a single-user environment, however, the user&lt;br /&gt;
is responsible for the load that caused the thrashing, the user is the one&lt;br /&gt;
who is suffering from it, and the user is the one who can fix the situation&lt;br /&gt;
by buying more RAM or terminating a few applications. Nevertheless,&lt;br /&gt;
applications with considerable memory needs should be written so as to&lt;br /&gt;
minimize their impact on the system swapper.&lt;br /&gt;
     Fundamentally, all swapping optimization techniques boil down to one&lt;br /&gt;
issue: locality of reference. This means keeping the memory locations that&lt;br /&gt;
are referenced near one another in time and in space. If your program&lt;br /&gt;
supports five functions, put the code of each function in a separate&lt;br /&gt;
segment, with another segment holding common code. The user can then work&lt;br /&gt;
with one function, and the other segments can be swapped. If each function&lt;br /&gt;
had some code in each of five segments, all segments would have to be in&lt;br /&gt;
memory at all times.&lt;br /&gt;
     A large body of literature deals with these issues because of the&lt;br /&gt;
prevalence of virtual memory systems in the mainframe environment. Most of&lt;br /&gt;
this work was done when RAM was very expensive. To precisely determine&lt;br /&gt;
which segments or pages should be resident and which should be swapped was&lt;br /&gt;
worth a great deal of effort. Memory was costly, and swapping devices were&lt;br /&gt;
fast, so algorithms were designed to &amp;quot;crank the screws down tight&amp;quot; and free&lt;br /&gt;
up as much memory as possible. After all, if they misjudged and swapped&lt;br /&gt;
something that was needed soon, it could be brought back in quickly. The&lt;br /&gt;
OS/2 environment is inverted: RAM is comparatively cheap, and the swapping&lt;br /&gt;
disk, being the regular system hard disk, is comparatively slow.&lt;br /&gt;
Consequently, OS/2's swapping strategy is to identify segments that are&lt;br /&gt;
clearly idle and swap them (because cheap RAM doesn't mean free RAM) but&lt;br /&gt;
not to judge things so closely that segments are frequently swapped when&lt;br /&gt;
they should not be.&lt;br /&gt;
     A key concept derived from this classic virtual memory work is that of&lt;br /&gt;
the working set. A thread's working set is the set of segments it will&lt;br /&gt;
reference &amp;quot;soon&amp;quot;--in the next several seconds or few minutes. Programmers&lt;br /&gt;
should analyze their code to determine its working sets; obviously the set&lt;br /&gt;
of segments in the working set will vary with the work the application is&lt;br /&gt;
doing. Code and data should be arranged between segments so that the size&lt;br /&gt;
of each common working set consists of a minimum amount of memory. For&lt;br /&gt;
example, if a program contains extensive code and data to deal with&lt;br /&gt;
uncommon error situations, these items should reside in separate segments&lt;br /&gt;
so that they aren't resident except when needed. You don't want to burden&lt;br /&gt;
the system with too many segments; two functions that are frequently used&lt;br /&gt;
together should occupy the same segment, but large unrelated bodies of code&lt;br /&gt;
and data should have their own segments or be grouped with other items that&lt;br /&gt;
are in their working set. Consider segment size when packing items into&lt;br /&gt;
segments. Too many small segments increase system overhead; large segments&lt;br /&gt;
decrease the efficiency of the swap mechanism. Splitting a segment in two&lt;br /&gt;
doesn't make sense if all code in the segment belongs to the same working&lt;br /&gt;
set, but it does make sense to split large bodies of unrelated code and&lt;br /&gt;
data.&lt;br /&gt;
     As we said before, an exhaustive discussion of these issues is beyond&lt;br /&gt;
the scope of this book. Programmers writing memory-intensive applications&lt;br /&gt;
should study the literature and their programs to optimize their&lt;br /&gt;
performance in an OS/2 environment. Minimizing an application's memory&lt;br /&gt;
requirements is more than being a &amp;quot;good citizen&amp;quot;; the smaller a program's&lt;br /&gt;
working set, the better it will run when the system load picks up.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.4  Status and Information&lt;br /&gt;
&lt;br /&gt;
OS/2 takes advantage of the 80286 LDT and GDT architecture in providing two&lt;br /&gt;
special segments, called infosegs, that contain system information. OS/2&lt;br /&gt;
updates these segments when changes take place, so their information is&lt;br /&gt;
always current. One infoseg is global, and the other is local. The global&lt;br /&gt;
infoseg contains information about the system as a whole; the local infoseg&lt;br /&gt;
contains process specific data. Naturally, the global infoseg is read only&lt;br /&gt;
and is shared among all processes. Local infosegs are also read only, but&lt;br /&gt;
each process has its own.&lt;br /&gt;
     The global infoseg contains time and date information. The &amp;quot;seconds&lt;br /&gt;
elapsed since 1970&amp;quot; field is particularly useful for time-stamping events&lt;br /&gt;
because calculating the interval between two times is easy. Simply subtract&lt;br /&gt;
and then divide by the number of seconds in the unit of time in which&lt;br /&gt;
you're interested. It's important that you remember that the date/time&lt;br /&gt;
fields are 32-bit fields but the 80286 reads data 16 bits at a time. Thus,&lt;br /&gt;
if an application reads the two time-stamp words at the same time as they&lt;br /&gt;
are being updated, it may read a bad value--not a value off by 1, but a&lt;br /&gt;
value that is off by 63335. The easiest way to deal with this is to read&lt;br /&gt;
the value and then compare the just read value with the infoseg contents.&lt;br /&gt;
If they are the same, your read value is correct. If they differ, continue&lt;br /&gt;
reading and comparing until the read and infoseg  values agree. The RAS&lt;br /&gt;
6. Reliability, Availability, and Serviceability. A buzzword that refers&lt;br /&gt;
to components intended to aid field diagnosis of system malfunctions.&lt;br /&gt;
6&lt;br /&gt;
information is used for field system diagnosis and is not of general&lt;br /&gt;
interest to programmers.&lt;br /&gt;
     The local infoseg segment contains process and thread information. The&lt;br /&gt;
information is accurate for the currently executing thread. The subscreen&lt;br /&gt;
group value is used by the presentation manager subsystem and is not of&lt;br /&gt;
value to applications. For more information on global and local infosegs,&lt;br /&gt;
see the OS/2 reference manual.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==10  Environment Strings==&lt;br /&gt;
&lt;br /&gt;
A major requirement of OS/2 is the ability to support logical device and&lt;br /&gt;
directory names. For example, a program needs to write a temporary scratch&lt;br /&gt;
file to the user's fastest disk. Which disk is that? Is it drive C, the&lt;br /&gt;
hard disk? Some machines don't have a hard disk. Is it drive B, the floppy&lt;br /&gt;
drive? Some machines don't have a drive B. And even if a hard disk on drive&lt;br /&gt;
C exists, maybe drive D also exists and has more free space. Or perhaps&lt;br /&gt;
drive E is preferred because it's a RAM disk. Perhaps it's not, though,&lt;br /&gt;
because the user wants the scratch file preserved when the machine is&lt;br /&gt;
powered down. This program needs the ability to specify a logical&lt;br /&gt;
directory--the scratch file directory--rather than a physical drive and&lt;br /&gt;
directory such as A:\ or C:\TEMP. The user could then specify the physical&lt;br /&gt;
location (drive and directory) that corresponds to the logical&lt;br /&gt;
directory.&lt;br /&gt;
     Another example is a spell-checker program that stores two&lt;br /&gt;
dictionaries on a disk. Presumably, the dictionary files were copied to a&lt;br /&gt;
hard disk when the program was installed, but on which drive and directory?&lt;br /&gt;
The checker's author could certainly hard code a directory such as&lt;br /&gt;
C:\SPELLCHK\DICT1. But what if the user doesn't have a C drive, or what if&lt;br /&gt;
drive C is full and the user wants to use drive D instead? How can this&lt;br /&gt;
program offer the user the flexibility of putting the dictionary files&lt;br /&gt;
where they best fit and yet still find them when it needs them?&lt;br /&gt;
     The answer to these problems is a logical device and directory name&lt;br /&gt;
facility. Such a facility should have three characteristics:&lt;br /&gt;
&lt;br /&gt;
     þ  It should allow the user to map the logical directories onto the&lt;br /&gt;
        actual (physical) devices and directories at will. It should be&lt;br /&gt;
        possible to change these mappings without changing the programs&lt;br /&gt;
        that use them.&lt;br /&gt;
&lt;br /&gt;
     þ  The set of possible logical devices and directories should be very&lt;br /&gt;
        large and arbitrarily expandable. Some new program, such as our&lt;br /&gt;
        spelling checker, will always need a new logical directory.&lt;br /&gt;
&lt;br /&gt;
     þ  The name set should be large and collision free. Many programs will&lt;br /&gt;
        want to use logical directory names. If all names must come from a&lt;br /&gt;
        small set of possibilities, such as X1, X2, X3, and so on, two&lt;br /&gt;
        applications, written independently, may each choose the same name&lt;br /&gt;
        for conflicting uses.&lt;br /&gt;
&lt;br /&gt;
     The original version of MS-DOS did not provide for logical devices and&lt;br /&gt;
directories. In those days a maximum PC configuration consisted of two&lt;br /&gt;
floppy disks. Operating the machine entailed playing a lot of &amp;quot;disk jockey&amp;quot;&lt;br /&gt;
as the user moved system, program, and data disks in and out of the drives.&lt;br /&gt;
The user was the only one who could judge which drive should contain which&lt;br /&gt;
floppy and its associated data, and data files moved from drive to drive&lt;br /&gt;
dynamically. A logical device mechanism would have been of little use.&lt;br /&gt;
Logical directories were not needed because MS-DOS version 1.0 didn't&lt;br /&gt;
support directories. MS-DOS versions 2.x and 3.x propagated the &amp;quot;physical&lt;br /&gt;
names only&amp;quot; architecture because of memory limitations and because of the&lt;br /&gt;
catch-22 of new operating system features: Applications won't take&lt;br /&gt;
advantage of the new feature because many machines are running older&lt;br /&gt;
versions of MS-DOS without that new feature.&lt;br /&gt;
     None of these reasons holds true for OS/2. All OS/2 protect mode&lt;br /&gt;
applications will be rewritten. OS/2 has access to plenty of memory.&lt;br /&gt;
Finally, OS/2 needs a logical drive/directory mechanism: All OS/2 machines&lt;br /&gt;
have hard disks or similar facilities, and all OS/2 machines will run a&lt;br /&gt;
variety of sophisticated applications that need access to private files and&lt;br /&gt;
work areas. As a result, the environment string mechanism in MS-DOS has&lt;br /&gt;
been expanded to serve as the logical name in OS/2.&lt;br /&gt;
     Because of the memory allocation techniques employed by MS-DOS&lt;br /&gt;
programs and because of the lack of segment motion and swapping in real&lt;br /&gt;
mode, the MS-DOS environment list was very limited in size. The size of the&lt;br /&gt;
environment segment was easily exceeded. OS/2 allows environment segments&lt;br /&gt;
to be grown arbitrarily, at any time, subject only to the hardware's 64 KB&lt;br /&gt;
length limitation. In keeping with the OS/2 architecture, each process has&lt;br /&gt;
its own environment segment. By default, the child inherits a copy of the&lt;br /&gt;
parent's segment, but the parent can substitute other environment values at&lt;br /&gt;
DosExecPgm time.&lt;br /&gt;
     Using the environment string facility to provide logical names is&lt;br /&gt;
straightforward. If a convention for the logical name that you need doesn't&lt;br /&gt;
already exist, you must choose a meaningful name. Your installation&lt;br /&gt;
instructions or software should document how to use the environment string;&lt;br /&gt;
the application should display an error message or use an appropriate&lt;br /&gt;
default if the logical names do not appear in the environment string.&lt;br /&gt;
Because each process has its own environment segment that it inherited from&lt;br /&gt;
its parent, batch files, startup scripts, and initiator programs that load&lt;br /&gt;
applications can conveniently set up the necessary strings. This also&lt;br /&gt;
allows several applications or multiple copies of the same application to&lt;br /&gt;
define the same logical name differently.&lt;br /&gt;
     The existing conventions are:&lt;br /&gt;
&lt;br /&gt;
     PATH=&lt;br /&gt;
     PATH defines a list of directories that CMD.EXE searches when it has&lt;br /&gt;
     been instructed to execute a program. The directories are searched&lt;br /&gt;
     from left to right and are separated by semicolons. For example,&lt;br /&gt;
&lt;br /&gt;
        PATH=C:\BIN;D:\TOOLS;.&lt;br /&gt;
&lt;br /&gt;
     means search C:\BIN first, D:\TOOLS second, and the current working&lt;br /&gt;
     directory third.&lt;br /&gt;
&lt;br /&gt;
     DPATH=&lt;br /&gt;
     DPATH defines a list of directories that programs may search to locate&lt;br /&gt;
     a data file. The directories are searched from left to right and are&lt;br /&gt;
     separated by semicolons. For example:&lt;br /&gt;
&lt;br /&gt;
        DPATH=C:\DBM;D:\TEMP;.&lt;br /&gt;
&lt;br /&gt;
Applications use DPATH as a convenience to the user: A user can work from&lt;br /&gt;
one directory and reference data files in another directory, named in the&lt;br /&gt;
DPATH string, without specifying the full path names of the data files.&lt;br /&gt;
Obviously, applications and users must use this technique with care.&lt;br /&gt;
Searching too widely for a filename is extremely dangerous; the wrong file&lt;br /&gt;
may be found because filenames themselves are often duplicated in different&lt;br /&gt;
directories. To use the DPATH string, an application must first use&lt;br /&gt;
DosScanEnv to locate the DPATH string, and then it must use DosSearchPath&lt;br /&gt;
to locate the data file.&lt;br /&gt;
&lt;br /&gt;
     INCLUDE=&lt;br /&gt;
     The INCLUDE name defines the drive and directory where compiler and&lt;br /&gt;
     assembler standard include files are located.&lt;br /&gt;
&lt;br /&gt;
     INIT=&lt;br /&gt;
     The INIT name defines the drive and directory that contains&lt;br /&gt;
     initialization and configuration information for the application. For&lt;br /&gt;
     example, some applications define files that contain the user's&lt;br /&gt;
     preferred defaults. These files might be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
     LIB=&lt;br /&gt;
     The LIB name defines the drive and directory where the standard&lt;br /&gt;
     language library modules are kept.&lt;br /&gt;
&lt;br /&gt;
     PROMPT=&lt;br /&gt;
     The PROMPT name defines the CMD.EXE prompt string. Special character&lt;br /&gt;
     sequences are defined so that the CMD.EXE prompt can contain the&lt;br /&gt;
     working directory, the date and time, and so on. See CMD.EXE&lt;br /&gt;
     documentation for details.&lt;br /&gt;
&lt;br /&gt;
     TEMP=&lt;br /&gt;
     The TEMP name defines the drive and directory for temporary files.&lt;br /&gt;
     This directory is on a device that is relatively fast and has&lt;br /&gt;
     sufficient room for scratch files. The TEMP directory should be&lt;br /&gt;
     considered volatile; its contents can be lost during a reboot&lt;br /&gt;
     operation.&lt;br /&gt;
&lt;br /&gt;
     The environment segment is a very flexible tool that you can use to&lt;br /&gt;
customize the environment of an application or a group of applications. For&lt;br /&gt;
example, you can use environment strings to specify default options for&lt;br /&gt;
applications. Users can use the same systemwide default or change that&lt;br /&gt;
value for a particular screen group or activation of the application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==11  Interprocess Communication==&lt;br /&gt;
&lt;br /&gt;
Interprocess Communication (IPC) is central to OS/2. As we discussed&lt;br /&gt;
earlier, effective IPC is needed to support both the tool-based&lt;br /&gt;
architecture and the dynlink interface for interprocess services. Because&lt;br /&gt;
IPC is so important, OS/2 provides several forms to fulfill a variety of&lt;br /&gt;
needs.&lt;br /&gt;
&lt;br /&gt;
===11.1  Shared Memory===&lt;br /&gt;
&lt;br /&gt;
Shared memory has already been discussed in some detail. To summarize, the&lt;br /&gt;
two forms are named shared memory (access is requested by the client by&lt;br /&gt;
name) and giveaway shared memory (a current owner gives access to another&lt;br /&gt;
process). Shared memory is the most efficient form of IPC because no data&lt;br /&gt;
copying or calls to the operating system kernel are involved once the&lt;br /&gt;
shared memory has been set up. Shared memory does require more effort on&lt;br /&gt;
the part of the client processes; a protocol must be established,&lt;br /&gt;
semaphores and flags are usually needed, and exposure to amok programs and&lt;br /&gt;
premature termination must be considered. Applications that expect to deal&lt;br /&gt;
with a low volume of data may want to consider using named pipes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2  Semaphores&lt;br /&gt;
&lt;br /&gt;
A semaphore is a flag or a signal. In its basic form a semaphore has only&lt;br /&gt;
two states--on and off or stop and go. A railroad semaphore, for example,&lt;br /&gt;
is either red or green--stop or go. In computer software, a semaphore is a&lt;br /&gt;
flag or a signal used by one thread of execution to flag or signal&lt;br /&gt;
another. Often it's for purposes of mutual exclusion: &amp;quot;I'm in here, stay&lt;br /&gt;
out.&amp;quot; But sometimes it can be used to indicate other events: &amp;quot;Your data is&lt;br /&gt;
ready.&amp;quot;&lt;br /&gt;
     OS/2 supports two kinds of semaphores, each of which can be used in&lt;br /&gt;
two different ways. The two kinds of semaphores--RAM semaphores and system&lt;br /&gt;
semaphores--have a lot in common, and the same system API is used to&lt;br /&gt;
manipulate both. A RAM semaphore, as its name implies, uses a 4-byte data&lt;br /&gt;
structure kept in a RAM location that must be accessible to all threads&lt;br /&gt;
that use it. The system API that manipulates RAM semaphores is located in a&lt;br /&gt;
dynlink subsystem. This code claims semaphores with an atomic test-and-set&lt;br /&gt;
operation,&lt;br /&gt;
1. An atomic operation is one that is indivisible and therefore&lt;br /&gt;
cannot be interrupted in the middle.&lt;br /&gt;
1 so it need not enter the kernel (ring 0) to protect itself&lt;br /&gt;
against preemption. As a result, the most common tasks--claiming a free&lt;br /&gt;
semaphore and freeing a semaphore that has no waiters--are very fast, on&lt;br /&gt;
the order of 100 microseconds on a 6-MHz 1-wait-state IBM PC/AT.&lt;br /&gt;
2. This is the standard environment when quoting speeds; it's&lt;br /&gt;
both the common case and the worst case. Machines that don't run&lt;br /&gt;
at this speed run faster.&lt;br /&gt;
2 If the&lt;br /&gt;
semaphore is already claimed and the caller must block or if another thread&lt;br /&gt;
is waiting on the semaphore, the semaphore dynlink package must enter&lt;br /&gt;
kernel mode.&lt;br /&gt;
     System semaphores, on the other hand, use a data structure that is&lt;br /&gt;
kept in system memory outside the address space of any process. Therefore,&lt;br /&gt;
system semaphore operations are slower than RAM semaphore operations, on&lt;br /&gt;
the order of 350 microseconds for an uncontested semaphore claim. Some&lt;br /&gt;
important advantages offset this operating speed however. System semaphores&lt;br /&gt;
support mechanisms that prevent deadlock by crashing programs, and system&lt;br /&gt;
semaphores support exclusivity and counting features. As a general rule,&lt;br /&gt;
you should use RAM semaphores when the requirement is wholly contained&lt;br /&gt;
within one process. When multiple processes may be involved, use system&lt;br /&gt;
semaphores.&lt;br /&gt;
     The first step, regardless of the type or use of the semaphore, is to&lt;br /&gt;
create it. An application creates RAM semaphores simply by allocating a 4-&lt;br /&gt;
byte area of memory initialized to zero. The far address of this area is&lt;br /&gt;
the RAM semaphore handle. The DosCreateSem call creates system semaphores.&lt;br /&gt;
(The DosCreateSem call takes an exclusivity argument, which we'll discuss&lt;br /&gt;
later.) Although semaphores control thread execution, semaphore handles&lt;br /&gt;
are owned by the process. Once a semaphore is created and its handle&lt;br /&gt;
obtained, all threads in that process can use that handle. Other&lt;br /&gt;
processes must open the semaphore via DosOpenSem. There is no explicit&lt;br /&gt;
open for a RAM semaphore. To be useful for IPC, the RAM semaphore must&lt;br /&gt;
be in a shared memory segment so that another process can access it;&lt;br /&gt;
the other process simply learns the far address of the RAM semaphore. A RAM&lt;br /&gt;
semaphore is initialized by zeroing out its 4-byte memory area.&lt;br /&gt;
     Except for opening and closing, RAM and system semaphores use exactly&lt;br /&gt;
the same OS/2 semaphore calls. Each semaphore call takes a semaphore handle&lt;br /&gt;
as an argument. A RAM semaphore's handle is its address; a system&lt;br /&gt;
semaphore's handle was returned by the create or open call. The OS/2&lt;br /&gt;
semaphore routines can distinguish between RAM and system semaphores by&lt;br /&gt;
examining the handle they are passed. Because system semaphores and their&lt;br /&gt;
names are kept in an internal OS/2 data area, they are a finite resource;&lt;br /&gt;
the number of RAM semaphores is limited only by the amount of available RAM&lt;br /&gt;
to hold them.&lt;br /&gt;
     The most common use of semaphores is to protect critical sections. To&lt;br /&gt;
reiterate, a critical section is a body of code that manipulates a data&lt;br /&gt;
resource in a nonreentrant way. In other words, a critical section will&lt;br /&gt;
screw up if two threads call it at the same time on the same data resource.&lt;br /&gt;
A critical section can cover more than one section of code; if one&lt;br /&gt;
subroutine adds entries to a table and another subroutine removes entries,&lt;br /&gt;
both subroutines are in the table's critical section. A critical section is&lt;br /&gt;
much like an airplane washroom, and the semaphore is like the sign that&lt;br /&gt;
says &amp;quot;Occupied.&amp;quot; The first user sets the semaphore and starts manipulating&lt;br /&gt;
the resource; meanwhile others arrive, see that the semaphore is set, and&lt;br /&gt;
block (that is, wait) outside. When the critical section becomes available&lt;br /&gt;
and the semaphore is cleared, only one of the waiting threads gets to claim&lt;br /&gt;
it; the others keep on waiting.&lt;br /&gt;
     Using semaphores to protect critical sections is straightforward. At&lt;br /&gt;
the top of a section of code that will manipulate the critical resource,&lt;br /&gt;
insert a call to DosSemRequest. When this call returns, the semaphore is&lt;br /&gt;
claimed, and the code can proceed. When the code is finished and the&lt;br /&gt;
critical section is &amp;quot;clean,&amp;quot; call DosSemClear. DosSemClear releases the&lt;br /&gt;
semaphore and reactivates any thread waiting on it.&lt;br /&gt;
     System semaphores are different from RAM semaphores in this&lt;br /&gt;
application in one critical respect. If a system semaphore is created for&lt;br /&gt;
exclusive use, it can be used as a counting semaphore. Exclusive use means&lt;br /&gt;
that only the thread that set the semaphore can clear it;&lt;br /&gt;
3. This is a departure from the principle of resource ownership&lt;br /&gt;
by process, not by thread. The thread, not the process, owns the&lt;br /&gt;
privilege to clear a set &amp;quot;exclusive use&amp;quot; semaphore.&lt;br /&gt;
3 this is expected&lt;br /&gt;
when protecting critical sections. A counting semaphore can be set many&lt;br /&gt;
times but must be released an equal number of times before it becomes free.&lt;br /&gt;
For example, an application contains function A and function B, each of&lt;br /&gt;
which manipulates the same critical section. Each claims the semaphore at&lt;br /&gt;
its beginning and releases it at its end. However, under some&lt;br /&gt;
circumstances, function A may need to call function B. Function A can't&lt;br /&gt;
release the semaphore before it calls B because it's still in the critical&lt;br /&gt;
section and the data is in an inconsistent state. But when B issues&lt;br /&gt;
DosSemRequest on the semaphore, it blocks because the semaphore was already&lt;br /&gt;
set by A.&lt;br /&gt;
     A counting semaphore solves this problem. When function B makes the&lt;br /&gt;
second, redundant DosSemRequest call, OS/2 recognizes it as the same thread&lt;br /&gt;
that already owns the semaphore, and instead of blocking the thread, it&lt;br /&gt;
increments a counter to show that the semaphore has been claimed twice.&lt;br /&gt;
Later, when function B releases the semaphore, OS/2 decrements the counter.&lt;br /&gt;
Because the counter is not at zero, the semaphore is not really clear and&lt;br /&gt;
thus not released. The semaphore is truly released only after function B&lt;br /&gt;
returns to function A, and A, finishing its work, releases the semaphore a&lt;br /&gt;
second time.&lt;br /&gt;
     A second major use of semaphores is signaling (unrelated to the signal&lt;br /&gt;
facility of OS/2). Signaling is using semaphores to notify threads that&lt;br /&gt;
certain events or activities have taken place. For example, consider a&lt;br /&gt;
multithreaded application that uses one thread to communicate over a serial&lt;br /&gt;
port and another thread to compute with the results of that communication.&lt;br /&gt;
The computing thread tells the communication thread to send a message and&lt;br /&gt;
get a reply, and then it goes about its own business. Later, the computing&lt;br /&gt;
thread wants to block until the reply is received but only if the reply&lt;br /&gt;
hasn't already been received--it may have already arrived, in which case&lt;br /&gt;
the computing thread doesn't want to block.&lt;br /&gt;
     You can handle this by using a semaphore as a flag. The computing&lt;br /&gt;
thread sets the semaphore via DosSemSet before it gives the order to the&lt;br /&gt;
communications thread. When the computing thread is ready to wait for the&lt;br /&gt;
reply, it does a DosSemWait on the semaphore it set earlier. When the&lt;br /&gt;
communications thread receives the reply, it clears the semaphore. When the&lt;br /&gt;
computing thread calls DosSemWait, it will continue without&lt;br /&gt;
delay if the semaphore is already clear. Otherwise, the computing thread&lt;br /&gt;
blocks until the semaphore is cleared. In this example, we aren't&lt;br /&gt;
protecting a critical section; we're using the semaphore transition&lt;br /&gt;
from set to clear to flag an event between multiple threads. Our needs are&lt;br /&gt;
the opposite of a critical section semaphore: We don't want the semaphore&lt;br /&gt;
to be exclusively owned; if it were, the communications thread couldn't&lt;br /&gt;
release it. We also don't want the semaphore to be counting. If it counts,&lt;br /&gt;
the computing thread won't block when it does the DosSemWait; OS/2 would&lt;br /&gt;
recognize that it did the DosSemSet earlier and would increment the&lt;br /&gt;
semaphore counter.&lt;br /&gt;
     OS/2 itself uses semaphore signaling in this fashion when asynchronous&lt;br /&gt;
communication is needed. For example, asynchronous I/O uses semaphores in&lt;br /&gt;
the signaling mode to indicate that an I/O operation has completed. The&lt;br /&gt;
system timer services use semaphores in the signaling mode to indicate that&lt;br /&gt;
the specified time has elapsed. OS/2 supports a special form of semaphore&lt;br /&gt;
waiting, called DosMuxSemWait, which allows a thread to wait on more than&lt;br /&gt;
one semaphore at one time. As soon as any specified semaphore becomes&lt;br /&gt;
clear, DosMuxSemWait returns. DosMuxSemWait, like DosSemWait, only waits&lt;br /&gt;
for a semaphore to become clear; it doesn't set or claim the semaphore as&lt;br /&gt;
does DosSemRequest. DosMuxSemWait allows a thread to wait on a variety of&lt;br /&gt;
events and to wake up whenever one of those events occurs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2.1  Semaphore Recovery&lt;br /&gt;
We discussed earlier some difficulties that can arise if a semaphore is&lt;br /&gt;
left set &amp;quot;orphaned&amp;quot; when its owner terminates unexpectedly. We'll review&lt;br /&gt;
the topic because it's critical that applications handle the situation&lt;br /&gt;
correctly and because that correctness generally has to be demonstrable by&lt;br /&gt;
inspection. It's very difficult to demonstrate and fix timing-related bugs&lt;br /&gt;
by just testing a program.&lt;br /&gt;
     Semaphores can become orphaned in at least four ways:&lt;br /&gt;
&lt;br /&gt;
     1.  An incoming signal can divert the CPU, and the signal handler can&lt;br /&gt;
         fail to return to the point of interruption.&lt;br /&gt;
&lt;br /&gt;
     2.  A process can kill another process without warning.&lt;br /&gt;
&lt;br /&gt;
     3.  A process can incur a GP fault, which is fatal.&lt;br /&gt;
&lt;br /&gt;
     4.  A process can malfunction because of a coding error and fail to&lt;br /&gt;
         release a semaphore.&lt;br /&gt;
&lt;br /&gt;
     The action to take in such events depends on how semaphores are being&lt;br /&gt;
used. In some situations, no action is needed. Our example of the computing&lt;br /&gt;
and communications threads is such a situation. If the process dies, the&lt;br /&gt;
semaphore and all its users die. Special treatment is necessary only if the&lt;br /&gt;
application uses DosExitList to run code that needs to use the semaphore.&lt;br /&gt;
This should rarely be necessary because semaphores are used within a&lt;br /&gt;
process to coordinate multiple threads and only one thread remains&lt;br /&gt;
when the exitlist is activated. Likewise, a process can receive signals&lt;br /&gt;
only if it has asked for them, so an application that does not use signals&lt;br /&gt;
need not worry about their interrupting its critical sections. An&lt;br /&gt;
application that does use signals can use DosHoldSignal, always return&lt;br /&gt;
from a signal handler, or prevent thread 1 (the signal-handling thread)&lt;br /&gt;
from entering critical sections.&lt;br /&gt;
     In other situations, the semaphore can protect a recoverable resource.&lt;br /&gt;
For example, you can use a system semaphore to protect access to a printer&lt;br /&gt;
that for some reason is being dealt with directly by applications rather&lt;br /&gt;
than by the system spooler. If the owner of the &amp;quot;I'm using the printer&amp;quot;&lt;br /&gt;
system semaphore dies unexpectedly, the next thread that tries to claim the&lt;br /&gt;
semaphore will be able to do so but will receive a special error code that&lt;br /&gt;
says, &amp;quot;The owner of this semaphore died while holding it.&amp;quot; In such a case,&lt;br /&gt;
the application can simply write a form feed or two to the printer and&lt;br /&gt;
continue. Other possible actions are to clean up the protected resource or&lt;br /&gt;
to execute a process that will do so. Finally, an application can display a&lt;br /&gt;
message to the user saying, &amp;quot;Gee, this database is corrupt! You better do&lt;br /&gt;
something,&amp;quot; and then terminate. In this case, the application should&lt;br /&gt;
deliberately terminate while holding the semaphore so that any other&lt;br /&gt;
threads waiting on it will receive the &amp;quot;owner died&amp;quot; message. Once the&lt;br /&gt;
&amp;quot;owner died&amp;quot; code is received, that state is cleared; so if the recipient&lt;br /&gt;
of the code releases the semaphore without fixing the inconsistencies in&lt;br /&gt;
the critical section, problems will result.&lt;br /&gt;
     Additional matters must be considered if a process intends to clean up&lt;br /&gt;
its own semaphores by means of a DosExitList handler. First, exclusive&lt;br /&gt;
(that is, counting) semaphores must be used. Although an exitlist routine&lt;br /&gt;
can tell that a RAM or nonexclusive system semaphore is reserved, it cannot&lt;br /&gt;
tell whether it is the process that reserved it. You may be tempted simply&lt;br /&gt;
to keep a flag byte that is set each time the semaphore is claimed and&lt;br /&gt;
cleared each time the semaphore is released, but that solution contains a&lt;br /&gt;
potentially deadly window of failure. If the thread sets the &amp;quot;I own it&amp;quot;&lt;br /&gt;
flag before it calls DosSemRequest, the thread could terminate between&lt;br /&gt;
setting the flag and receiving the semaphore. In that case, the exitlist&lt;br /&gt;
routine would believe, wrongly, that it owns the semaphore and would&lt;br /&gt;
therefore release it--a very unpleasant surprise for the true owner of the&lt;br /&gt;
semaphore. Conversely, if the thread claims the semaphore and then sets the&lt;br /&gt;
flag, a window exists in which the semaphore is claimed but the flag does&lt;br /&gt;
not say so. This is also disastrous.&lt;br /&gt;
     Using exclusive system semaphores solves these problems. As I&lt;br /&gt;
mentioned earlier, when the thread that has set a system semaphore dies&lt;br /&gt;
with the semaphore set, the semaphore is placed into a special &amp;quot;owner died&amp;quot;&lt;br /&gt;
state so that the next thread to attempt to claim the semaphore is informed&lt;br /&gt;
of its orphan status. There is an extra twist to this for exclusive-use&lt;br /&gt;
system semaphores. Should the process die due to an external cause or due&lt;br /&gt;
to a DosExit call and that process has a DosExitList handler, all orphaned&lt;br /&gt;
system semaphores are placed in a special &amp;quot;owner died&amp;quot; state so that only&lt;br /&gt;
that process's remaining thread--the one executing the DosExitList&lt;br /&gt;
handlers--can claim the semaphore. When it does so, it still receives the&lt;br /&gt;
special &amp;quot;owner died&amp;quot; code. The exitlist handler can use DosSemWait with a&lt;br /&gt;
timeout value of 0 to see if the semaphore is set. If the &amp;quot;owner died&amp;quot; code&lt;br /&gt;
is returned, then the DosExitList handler cleans up the resource and then&lt;br /&gt;
issues DosSemClear to clear the semaphore. If a thread terminates by&lt;br /&gt;
explicitly calling DosExit with the &amp;quot;terminate this thread&amp;quot; subcode, any&lt;br /&gt;
exclusive-use system semaphores that it has set will not enter this special&lt;br /&gt;
&amp;quot;owner died&amp;quot; state but will instead assume the general &amp;quot;owner died&amp;quot; state&lt;br /&gt;
that allows any thread in the system to claim the semaphore and receive the&lt;br /&gt;
&amp;quot;owner died&amp;quot; code. Likewise, any semaphores in the special &amp;quot;owner died&amp;quot;&lt;br /&gt;
state that are not cleared by the DosExitList handlers become normal &amp;quot;owner&lt;br /&gt;
died&amp;quot; semaphores when the process completely terminates.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2.2  Semaphore Scheduling&lt;br /&gt;
Although multiple threads can wait for a semaphore, only one thread gets&lt;br /&gt;
the semaphore when it becomes available. OS/2 schedules semaphore grants&lt;br /&gt;
based on CPU priority: The highest-priority waiting thread claims the&lt;br /&gt;
semaphore. If several waiting threads are at the highest priority, OS/2&lt;br /&gt;
distributes the grants among them evenly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.3  Named Pipes&lt;br /&gt;
&lt;br /&gt;
We've already discussed anonymous pipes--stream oriented IPC mechanisms&lt;br /&gt;
that work via the DosRead and DosWrite calls. Two processes can communicate&lt;br /&gt;
via anonymous pipes only if one is a descendant of the other and if the&lt;br /&gt;
descendant has inherited the parent's handle to the pipe. Anonymous pipes&lt;br /&gt;
are used almost exclusively to transfer input and output data to and from a&lt;br /&gt;
child process or to and from a subtree of child processes.&lt;br /&gt;
     OS/2 supports another form of pipes called named pipes. Named pipes&lt;br /&gt;
are not available in OS/2 version 1.0; they will be available in a later&lt;br /&gt;
release. I discuss them here because of their importance in the system&lt;br /&gt;
architecture. Also, because of the extensible nature of OS/2, it's possible&lt;br /&gt;
that named pipe functionality will be added to the system by including the&lt;br /&gt;
function in some other Microsoft system software package that, when it runs&lt;br /&gt;
under OS/2, installs the capability. In such a case, application programs&lt;br /&gt;
will be unable to distinguish the &amp;quot;add-on&amp;quot; named pipe facility from the&lt;br /&gt;
&amp;quot;built-in&amp;quot; version that will eventually be included in OS/2.&lt;br /&gt;
     Named pipes are much like anonymous pipes in that they're a serial&lt;br /&gt;
communications channel between two processes and they use the DosRead and&lt;br /&gt;
DosWrite interface. They are different, however, in several important&lt;br /&gt;
ways.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes have names in the file system name space. Users of a&lt;br /&gt;
        named pipe need not be related; they need only know the name of a&lt;br /&gt;
        pipe to access it.&lt;br /&gt;
&lt;br /&gt;
     þ  Because named pipes use the file system name space and because that&lt;br /&gt;
        name space can describe machines on a network, named pipes work&lt;br /&gt;
        both locally (within a single machine) and remotely (across a&lt;br /&gt;
        network).&lt;br /&gt;
&lt;br /&gt;
     þ  An anonymous pipe is a byte-stream mechanism. The system considers&lt;br /&gt;
        the data sent through an anonymous pipe as an undifferentiated&lt;br /&gt;
        stream of bytes. The writer can write a 100-byte block of data, and&lt;br /&gt;
        the reader can read the data with two 30-byte reads and one 40-byte&lt;br /&gt;
        read. If the byte stream contains individual messages, the&lt;br /&gt;
        recipient must determine where they start and stop. Named pipes can&lt;br /&gt;
        be used in this byte-stream mode, but named pipes also support&lt;br /&gt;
        support message mode, in which processes read and write streams&lt;br /&gt;
        of messages. When the named pipe is in message mode, OS/2&lt;br /&gt;
        (figuratively!) separates the messages from each other with pieces&lt;br /&gt;
        of waxed paper so that the reader can ask for &amp;quot;the next message&amp;quot;&lt;br /&gt;
        rather than for &amp;quot;the next 100 bytes.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes are full duplex, whereas anonymous pipes are actually a&lt;br /&gt;
        pair of pipes, each half duplex. When an anonymous pipe is created,&lt;br /&gt;
        two handles are returned--a read handle and a write handle. An open&lt;br /&gt;
        of a named pipe returns a single handle, which may (depending on&lt;br /&gt;
        the mode of the DosOpen) be both read and written. Although a full&lt;br /&gt;
        duplex named pipe is accessed via a single handle, the data moving&lt;br /&gt;
        in each direction is kept totally separate. A named pipe should be&lt;br /&gt;
        viewed as two separate pipes between the reader and the writer--one&lt;br /&gt;
        holds data going in, the other holds data coming back. For example,&lt;br /&gt;
        if a thread writes to a named pipe handle and then reads from that&lt;br /&gt;
        handle, the thread will not read back the data it just wrote. The&lt;br /&gt;
        data the thread just wrote in is in the outgoing side; the read&lt;br /&gt;
        reads from the incoming side.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes are frequently used to communicate with processes that&lt;br /&gt;
        provide a service to one or more clients, usually simultaneously.&lt;br /&gt;
        The named pipe API contains special functions to facilitate such&lt;br /&gt;
        use: pipe reusability, multiple pipes with identical names, and so&lt;br /&gt;
        on. These are discussed below.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes support transaction I/O calls that provide an efficient&lt;br /&gt;
        way to implement local and remote procedure call dialogs between&lt;br /&gt;
        processes.&lt;br /&gt;
&lt;br /&gt;
     þ  Programs running on MS-DOS version 3.x workstations can access&lt;br /&gt;
        named pipes on an OS/2 server to conduct dialogs with server&lt;br /&gt;
        applications because, to a client, a named pipe looks exactly like&lt;br /&gt;
        a file.&lt;br /&gt;
&lt;br /&gt;
     You'll recall that the creator of an anonymous pipe uses a special&lt;br /&gt;
interface (DosMakePipe) to create the pipe but that the client process&lt;br /&gt;
can use the DosRead and DosWrite functions, remaining ignorant of the&lt;br /&gt;
nature of the handle. The same holds true for named pipes when they&lt;br /&gt;
are used in stream mode. The creator of a named pipe uses a special API&lt;br /&gt;
to set it up, but its clients can use the pipe while remaining ignorant&lt;br /&gt;
of its nature as long as that use is serial.&lt;br /&gt;
4. Random access, using DosSeek, is not supported for&lt;br /&gt;
pipes and will cause an error code to be returned.&lt;br /&gt;
4 Named pipes are created by&lt;br /&gt;
the DosMakeNmPipe call. Once the pipe is created, one of the serving&lt;br /&gt;
process's threads must wait via the DosConnectNmPipe call for the client&lt;br /&gt;
to open the pipe. The client cannot successfully open the pipe until a&lt;br /&gt;
DosConnectNmPipe has been issued to it by the server process.&lt;br /&gt;
     Although the serving process understands that it's using a named pipe&lt;br /&gt;
and can therefore call a special named pipe API, the client process need&lt;br /&gt;
not be aware that it's using a named pipe because the normal DosOpen call&lt;br /&gt;
is used to open the pipe. Because named pipes appear in the file system&lt;br /&gt;
name space, the client can, for example, open a file called \PIPE\STATUS,&lt;br /&gt;
unaware that it's a named pipe being managed by another process. The&lt;br /&gt;
DosMakeNmPipe call returns a handle to the serving end of the pipe; the&lt;br /&gt;
DosOpen call returns a handle to the client end. As soon as the client&lt;br /&gt;
opens a pipe, the DosConnectNmPipe call returns to the serving process.&lt;br /&gt;
     Communication over a named pipe is similar to that over an anonymous&lt;br /&gt;
pipe: The client and server each issue reads and writes to the handle, as&lt;br /&gt;
appropriate for the mode of the open. When a process at one end of the pipe&lt;br /&gt;
closes it, the process at the other end gets an error code in response to&lt;br /&gt;
write operations and an EOF indication in response to read operations.&lt;br /&gt;
     The scenario just described is simple enough, but that's the problem:&lt;br /&gt;
It's too simple. In real life, a serving process probably stays around so&lt;br /&gt;
that it can serve the next client. This is the purpose behind the&lt;br /&gt;
DosConnectNmPipe call. After the first client closes its end of the named&lt;br /&gt;
pipe and the server end sees the EOF on the pipe, the server end issues a&lt;br /&gt;
DosDisconnectNmPipe call to acknowledge that the client has closed the pipe&lt;br /&gt;
(either explicitly or via termination). It can then issue another&lt;br /&gt;
DosConnectNmPipe call to reenable that pipe for reopening by another client&lt;br /&gt;
or by the same client. In other words, the connect and disconnect&lt;br /&gt;
operations allow a server to let clients, one by one, connect to it via a&lt;br /&gt;
single named pipe. The DosDisconnectNmPipe call can be used to forcibly&lt;br /&gt;
disconnect a client. This action is appropriate if a client makes an&lt;br /&gt;
invalid request or otherwise shows signs of ill health.&lt;br /&gt;
     We can serve multiple clients, one at a time, but what about serving&lt;br /&gt;
them in parallel? As we've described it so far, our serving process handles&lt;br /&gt;
only one client. A client's DosOpen call fails if the named pipe already&lt;br /&gt;
has a client user or if the server process hasn't issued the&lt;br /&gt;
DosConnectNmPipe call. This is where the instancing parameter, supplied to&lt;br /&gt;
DosMakeNmPipe, comes in.&lt;br /&gt;
     When a named pipe is first opened,&lt;br /&gt;
5. Like other non-file-system resident named objects, a named pipe&lt;br /&gt;
remains known to the system only as long as a process has it open.&lt;br /&gt;
When all handles to a named pipe are closed, OS/2 forgets all&lt;br /&gt;
information concerning the named pipe. The next DosMakeNmPipe&lt;br /&gt;
call recreates the named pipe from ground zero.&lt;br /&gt;
5 the instance count parameter is&lt;br /&gt;
specified in the pipe flag's word. If this count is greater than 1, the&lt;br /&gt;
pipe can be opened by a server process more than once. Additional opens are&lt;br /&gt;
done via DosMakeNmPipe, which returns another handle to access the new&lt;br /&gt;
instance of the pipe. Obviously the pipe isn't being &amp;quot;made&amp;quot; for the second&lt;br /&gt;
and subsequent calls to DosMakeNmPipe, but the DosOpen call can't be used&lt;br /&gt;
instead because it opens the client end of the named pipe, not the server&lt;br /&gt;
end. The instance count argument is ignored for the second and subsequent&lt;br /&gt;
DosMakeNmPipe calls. Extra instances of a named pipe can be created by the&lt;br /&gt;
same process that created the first instance, or they can be created by&lt;br /&gt;
other processes. Figure 11-1 illustrates multiple instances of a named&lt;br /&gt;
pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿              \\pipe\pipename      &lt;br /&gt;
³  Client  ÃÄÄÄÄÄ¿               �                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     A    ³     ³     °°°°°°°°°°°°°°°°°°°°°°     ³           ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ     ³               ÚÄÄÄÄÄÄÄÄÄÄ¿     ³           ³&lt;br /&gt;
                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´          ³     ³           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿                  ÚÄÄÁÄÄÄÄÄÄÄ¿  ³     ³           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´          ³  ³     ³           ³&lt;br /&gt;
³     B    ³               ÚÄÄÁÄÄÄÄÄÄÄ¿  ³  ³     ³  Server   ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ     ÚÄÄÄÄÄÄÄÄÄ´          ³  ³  ÃÄÄÄÄÄ´  Process  ³&lt;br /&gt;
                 ³      ÚÄÄÁÄÄÄÄÄÄÄ¿  ³  ÃÄÄÙ     ³           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿     ³  ÚÄÄÄ´          ³  ³  ÃÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÙ  ³   ³          ³  ÃÄÄÙ        ³           ³&lt;br /&gt;
³     C    ³        ³   ³          ³  ÃÄÄÄÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ        ³   ³          ÃÄÄÙ           ³           ³&lt;br /&gt;
                    ³   ³          ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿        ³   ÀÄÄÄÄÄÄÄÄÄÄÙ              ³           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÄÄÄÙ                             ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³     D    ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 11-1.  Multiple instances of a named pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When a client process does a DosOpen on a named pipe that has multiple&lt;br /&gt;
instances, OS/2 connects it to any server instance of the pipe that has&lt;br /&gt;
issued a DosConnectNmPipe call. If no instances are available and enabled,&lt;br /&gt;
the client receives an error code. OS/2 makes no guarantees about&lt;br /&gt;
distributing the incoming work evenly across all server instances; it&lt;br /&gt;
assumes that all server threads that issued a DosConnectNmPipe call are&lt;br /&gt;
equal.&lt;br /&gt;
     The multiple instance capability allows a single server process or&lt;br /&gt;
perhaps multiple server processes to handle many clients simultaneously.&lt;br /&gt;
One process using four threads can serve four clients as rapidly as four&lt;br /&gt;
processes, each with one thread, can do the job. As long as threads don't&lt;br /&gt;
interfere with one another by blocking on critical sections, a multiprocess&lt;br /&gt;
server has no inherent efficiency advantage over a multithread server.&lt;br /&gt;
     The OS/2 named pipe package includes some composite operations for&lt;br /&gt;
client processes: DosTransactNmPipe and DosCallNmPipe. DosTransactNmPipe is&lt;br /&gt;
much like a DosWrite followed by a DosRead: It sends a message to the&lt;br /&gt;
server end of the named pipe and then reads a reply. DosCallNmPipe does&lt;br /&gt;
the same on an unopened named pipe: It has the combined effect of a&lt;br /&gt;
DosOpen, a DosTransactNmPipe, and a DosClose. These calls are of little&lt;br /&gt;
value if the client and server processes are on the same machine; the&lt;br /&gt;
client could easily build such subroutines itself by appropriately&lt;br /&gt;
combining DosOpen, DosClose, DosRead, and DosWrite. These calls are in the&lt;br /&gt;
named pipe package because they provide significant performance savings in&lt;br /&gt;
a networked environment. If the server process is on a different machine&lt;br /&gt;
from the client process, OS/2 and the network transport can use a&lt;br /&gt;
datagramlike mechanism to implement these calls in a network-efficient&lt;br /&gt;
fashion. Because named pipes work invisibly across the network, any client&lt;br /&gt;
process that performs these types of operations should use these composite&lt;br /&gt;
calls, even if the author of the program didn't anticipate the program&lt;br /&gt;
being used in a networked environment. Using the composite calls will&lt;br /&gt;
 ensure the performance gains if a user decides to use a server process&lt;br /&gt;
located across the network. Readers familiar with network architecture will&lt;br /&gt;
recognize the DosCallNmPipe function as a form of remote procedure call. In&lt;br /&gt;
effect, it allows a process to make a procedure call to another process,&lt;br /&gt;
even a process on another machine.&lt;br /&gt;
     The OS/2 named pipe facility contains a great many features, as befits&lt;br /&gt;
its importance in realizing the OS/2 tool-based architecture. This book is&lt;br /&gt;
not intended to provide an exhaustive coverage of features, but a few other&lt;br /&gt;
miscellaneous items merit mention.&lt;br /&gt;
     Our above discussion concentrated on stream-based communications,&lt;br /&gt;
which can be convenient because they allow a client process to use a named&lt;br /&gt;
pipe while ignorant of its nature. For example, you can write a spooler&lt;br /&gt;
package for a device not supported by the system spoolers--say, for a&lt;br /&gt;
plotter device. Input to the spooler can be via a named pipe, perhaps&lt;br /&gt;
\PIPE\PLOTOUT. An application could then be told to write its plotter&lt;br /&gt;
output to a file named \PIPE\PLOTOUT or even \\PLOTMACH\PIPE\PLOTOUT&lt;br /&gt;
(across a network). The application will then use the spooler at the&lt;br /&gt;
other end of the named pipe.&lt;br /&gt;
     Sometimes, though, the client process does understand that it's&lt;br /&gt;
talking to a named pipe, and the information exchanged is a series of&lt;br /&gt;
messages rather than a long stream of plotter data. In this case, the named&lt;br /&gt;
pipe can be configured as a message stream in which each message is&lt;br /&gt;
indivisible and atomic at the interface. In other words, when a process&lt;br /&gt;
reads from a named pipe, it gets only one message per read, and it gets the&lt;br /&gt;
entire message. Messages can queue up in the pipe, but OS/2 remembers the&lt;br /&gt;
message boundaries so that it can split them apart as they are read.&lt;br /&gt;
Message streams can be used effectively in a networking environment because&lt;br /&gt;
the network transport can better judge how to assemble packets.&lt;br /&gt;
     Although our examples have shown the client and server processes&lt;br /&gt;
issuing calls and blocking until they are done, named pipes can be&lt;br /&gt;
configured to operate in a nonblocking fashion. This allows a server or a&lt;br /&gt;
client to test a pipe to see if it's ready for a particular operation,&lt;br /&gt;
thereby guaranteeing that the process won't be held up for some period&lt;br /&gt;
waiting for a request to complete. Processes can also use DosPeekNmPipe, a&lt;br /&gt;
related facility that returns a peek at any data (without consuming the&lt;br /&gt;
data) currently waiting to be read in the pipe interface. Servers can use&lt;br /&gt;
this to scan a client's request to see if they're interested in handling it&lt;br /&gt;
at that time.&lt;br /&gt;
     Finally, we mentioned that a process that attempts a DosOpen to a&lt;br /&gt;
named pipe without any available instances is returned an error code.&lt;br /&gt;
Typically, a client in this situation wants to wait for service to become&lt;br /&gt;
available, and it doesn't want to sit in a polling loop periodically &lt;br /&gt;
testing for server availability. The DosWaitNmPipe call is provided for&lt;br /&gt;
this situation; it allows a client to block until an instance of the named&lt;br /&gt;
pipe becomes available. When DosWaitNmPipe returns, the client must still&lt;br /&gt;
do a DosOpen. The DosOpen can fail, however, if another process has taken&lt;br /&gt;
the pipe instance in the time between the &amp;quot;wait&amp;quot; and the &amp;quot;open&amp;quot; calls. But&lt;br /&gt;
because multiple waiters for a named pipe are serviced in priority order,&lt;br /&gt;
such a &amp;quot;race&amp;quot; condition is uncommon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.4  Queues&lt;br /&gt;
&lt;br /&gt;
Queues are another form of IPC. In many ways they are similar to named&lt;br /&gt;
pipes, but they are also significantly different. Like named pipes, they&lt;br /&gt;
use the file system name space, and they pass messages rather than byte&lt;br /&gt;
streams. Unlike named pipes, queues allow multiple writes to a single queue&lt;br /&gt;
because the messages bring with them information about their sending&lt;br /&gt;
process that enables the queue reader to distinguish between messages from&lt;br /&gt;
different senders. Named pipes are strictly FIFO, whereas queue messages&lt;br /&gt;
can be read in a variety of orders. Finally, queues use shared memory as a&lt;br /&gt;
transfer mechanism; so although they're faster than named pipes for higher&lt;br /&gt;
volume data transfers on a single machine, they don't work across the&lt;br /&gt;
network.&lt;br /&gt;
     The interface to the queue package is similar but not identical to&lt;br /&gt;
that of the named pipe interface. Like named pipes, each queue has a single&lt;br /&gt;
owner that creates it. Clients open and close the queue while the owner,&lt;br /&gt;
typically, lives on. Unlike named pipes, the client process must use a&lt;br /&gt;
special queue API (DosReadQueue, DosWriteQueue, and so on) and thus must be&lt;br /&gt;
written especially to use the queue package. Although each queue has a&lt;br /&gt;
single owner, each queue can have multiple clients; so the queue mechanism&lt;br /&gt;
doesn't need a facility to have multiple queues of the same name, nor does&lt;br /&gt;
it need a DosWaitNmPipe equivalent.&lt;br /&gt;
     Queue messages are somewhat different from named pipe messages. In&lt;br /&gt;
addition to carrying the body of the message, each queue message carries&lt;br /&gt;
two additional pieces of information. One is the PID of the sender; OS/2&lt;br /&gt;
provides this information, and the sender cannot affect it. The other is a&lt;br /&gt;
word value that the sender supplied and that OS/2 and the queue package do&lt;br /&gt;
not interpret. Queue servers and clients can use this information as they&lt;br /&gt;
wish to facilitate communication.&lt;br /&gt;
     The queue package also contains a peek facility, similar to that of&lt;br /&gt;
named pipes but with an interesting twist. If a process peeks the named&lt;br /&gt;
pipe and then later reads from it, it can be sure that the message it reads&lt;br /&gt;
is the same one that it peeked because named pipes are always FIFO. Queues,&lt;br /&gt;
however, allow records to be read in different orders of priority. If a&lt;br /&gt;
queue is being read in priority order, a process might well peek a message,&lt;br /&gt;
but by the time the process issues the queue read, some other message of&lt;br /&gt;
higher priority may have arrived and thus be at the front of the list. To&lt;br /&gt;
get around this problem, when the queue package peeks a message, it returns&lt;br /&gt;
a magic cookie to the caller along with the message. The caller can supply&lt;br /&gt;
this cookie to a subsequent DosReadQueue call to ensure that the peeked&lt;br /&gt;
message is the one read, overriding the normal message-ranking process.&lt;br /&gt;
This magic cookie can also be supplied to the DosPeekQueue call to peek the&lt;br /&gt;
second and subsequent records in the queue.&lt;br /&gt;
     Finally, one extremely important difference between queues and named&lt;br /&gt;
pipes is that named pipes transfer (that is, copy) the data from the client&lt;br /&gt;
to the server process. Queues transfer only the address of the data; the&lt;br /&gt;
queue package does not touch the data itself. Thus, the data body of the&lt;br /&gt;
queue message must be addressable to both the client and the serving&lt;br /&gt;
process. This is straightforward if both the client and serving threads&lt;br /&gt;
belong to the same process. If the client and serving threads are from&lt;br /&gt;
different processes, however, the data body of the queue message must be in&lt;br /&gt;
a shared memory segment that is addressable to both the client and the&lt;br /&gt;
server.&lt;br /&gt;
     A related issue is buffer reusability. An application can reuse a&lt;br /&gt;
memory area immediately after its thread returns from the named pipe call&lt;br /&gt;
that wrote the data from that area; but when using a queue, the sender must&lt;br /&gt;
not overwrite the message area until it's sure the reading process is&lt;br /&gt;
finished with the message.&lt;br /&gt;
     One way to kill both these birds--the shared memory and the memory&lt;br /&gt;
reuse problems--with one stone is to use the memory suballocation package.&lt;br /&gt;
Both the client and the queue server need to have shared access to a memory&lt;br /&gt;
segment that is then managed by the memory suballocation package. The&lt;br /&gt;
client allocates a memory object to hold the queue message and write it to&lt;br /&gt;
the queue. The queue server can address that queue message because it's in&lt;br /&gt;
the shared memory segment. When the queue manager is finished with the&lt;br /&gt;
message, it calls the memory suballocator to release the memory object. The&lt;br /&gt;
client need not worry about when the server is finished with the message&lt;br /&gt;
because the client allocates a new message buffer for each new message,&lt;br /&gt;
relying on the server to return the messages fast enough so that the memory&lt;br /&gt;
suballocator doesn't run out of available space.&lt;br /&gt;
     A similar technique on a segment level is to use giveaway shared&lt;br /&gt;
memory. The client allocates a giveaway segment for each message content,&lt;br /&gt;
creates the message, gives away a shared addressability to the segment to&lt;br /&gt;
the server process, and then writes the message (actually, the message's&lt;br /&gt;
address) to the queue. Note that the sender uses the recipient's selector&lt;br /&gt;
as the data address in this case, not its own selector. When the thread&lt;br /&gt;
returns from that DosWriteQueue call, the client releases its access to the&lt;br /&gt;
segment via DosFreeSeg. When the server process is finished with the&lt;br /&gt;
message, it also releases the memory segment. Because the queue server is&lt;br /&gt;
the last process with access to that segment, the segment is then returned&lt;br /&gt;
to the free pool.&lt;br /&gt;
     Software designers need to consider carefully the tradeoffs between&lt;br /&gt;
queues, named pipes, and other forms of IPC. Queues are potentially very&lt;br /&gt;
fast because only addresses are copied, not the data itself; but the work&lt;br /&gt;
involved in managing and reusing the shared memory may consume the time&lt;br /&gt;
savings if the messages are small. In general, small messages that are&lt;br /&gt;
always read FIFO should go by named pipes, as should applications that&lt;br /&gt;
communicate with clients and servers across a network. Very large or high&lt;br /&gt;
data rate messages may be better suited to queues.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.5  Dynamic Data Exchange (DDE)&lt;br /&gt;
&lt;br /&gt;
DDE is a form of IPC available to processes that use the presentation&lt;br /&gt;
manager API. The presentation manager's interface is message oriented; that&lt;br /&gt;
is, the primary means of communication between a process and the&lt;br /&gt;
presentation manager is the passing of messages. The presentation manager&lt;br /&gt;
message interface allows applications to define private messages that have&lt;br /&gt;
a unique meaning throughout the PC. DDE is, strictly speaking, a protocol&lt;br /&gt;
that defines new messages for communication between applications that use&lt;br /&gt;
it.&lt;br /&gt;
     DDE messages can be directed at a particular recipient or broadcast to&lt;br /&gt;
all presentation manager applications on a particular PC. Typically, a&lt;br /&gt;
client process broadcasts a message that says, &amp;quot;Does anyone out there have&lt;br /&gt;
this information?&amp;quot; or &amp;quot;Does anyone out there provide this service?&amp;quot; If no&lt;br /&gt;
response is received, the answer is taken to be no. If a response is&lt;br /&gt;
received, it contains an identifying code&lt;br /&gt;
6.A window handle.&lt;br /&gt;
6 that allows the two processes to&lt;br /&gt;
communicate privately.&lt;br /&gt;
     DDE's broadcast mechanism and message orientation gives it a lot of&lt;br /&gt;
flexibility in a multiprocessing environment. For example, a specialized&lt;br /&gt;
application might be scanning stock quotes that are arriving via a special&lt;br /&gt;
link. A spreadsheet program could use DDE to tell this scanner application&lt;br /&gt;
to notify it whenever the quotes change for certain stocks that are&lt;br /&gt;
mentioned in its spreadsheet. Another application, perhaps called Market&lt;br /&gt;
Alert, might ask the scanner to notify it of trades in a different set of&lt;br /&gt;
stocks so that the alert program can flash a banner if those stocks trade&lt;br /&gt;
outside a prescribed range. DDEs can be used only by presentation manager&lt;br /&gt;
applications to communicate with the same.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.6  Signaling&lt;br /&gt;
&lt;br /&gt;
Signals are asynchronous notification mechanisms that operate in a fashion&lt;br /&gt;
analogous to hardware interrupts. Like hardware interrupts, when a signal&lt;br /&gt;
arrives at a process, that process's thread 1 stops after the instruction&lt;br /&gt;
it is executing and begins executing at a specified handler address. The&lt;br /&gt;
many special considerations to take into account when using signals are&lt;br /&gt;
discussed in Chapter 12. This section discusses their use as a form of&lt;br /&gt;
IPC.&lt;br /&gt;
     Processes typically receive signals in response to external events&lt;br /&gt;
that must be serviced immediately. Examples of such events are the user&lt;br /&gt;
pressing Ctrl-C or a process being killed. Three signals (flag A, flag B,&lt;br /&gt;
and flag C), however, are caused by another process&lt;br /&gt;
7. This is the typical case; but like all other system calls that affect&lt;br /&gt;
processes, a thread can make such a call to affect its own process.&lt;br /&gt;
7 issuing an explicit&lt;br /&gt;
DosFlagProcess API. DosFlagProcess is a unique form of IPC because it's&lt;br /&gt;
asynchronous. The recipient doesn't have to block or poll waiting for the&lt;br /&gt;
event; it finds out about it (by discovering itself to be executing the&lt;br /&gt;
signal handler) as soon as the scheduler gives it CPU time.&lt;br /&gt;
     DosFlagProcess, however, has some unique drawbacks. First, a signal&lt;br /&gt;
carries little information with it: only the number of the signal and a&lt;br /&gt;
single argument word. Second, signals can interrupt and interfere with some&lt;br /&gt;
system calls. Third, OS/2 views signals more as events than as messages; so&lt;br /&gt;
if signals are sent faster than the recipient can process them, OS/2&lt;br /&gt;
discards some of the overrun. These disadvantages (discussed in Chapter 12)&lt;br /&gt;
restrict signals to a rather specialized role as an IPC mechanism.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.7  Combining IPC Forms&lt;br /&gt;
&lt;br /&gt;
We've discussed each form of IPC, listing its strengths and weaknesses. If&lt;br /&gt;
you use forms in conjunction, however, you benefit from their combined&lt;br /&gt;
strengths. For example, a process can use named pipes or DDE to establish&lt;br /&gt;
contact with another process and then agree with it to send a high volume&lt;br /&gt;
of data via shared memory. An application that provides an IPC interface&lt;br /&gt;
should also provide a dynlink package to hide the details of the IPC. This&lt;br /&gt;
gives designers the flexibility to improve the IPC component of their&lt;br /&gt;
package in future releases while still maintaining interface compatibility&lt;br /&gt;
with their clients.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==12  Signals==&lt;br /&gt;
&lt;br /&gt;
The OS/2 signal mechanism is similar, but not identical, to the UNIX signal&lt;br /&gt;
mechanism. A signal is much like a hardware interrupt except that it is&lt;br /&gt;
initiated and implemented in software. Just as a hardware interrupt causes&lt;br /&gt;
the CS, IP, and Flags registers to be saved on the stack and execution to&lt;br /&gt;
begin at a handler address, a signal causes the application's CS, IP, and&lt;br /&gt;
Flags registers to be saved on the stack and execution to begin at a&lt;br /&gt;
signal-handler address. An IRET instruction returns control to the&lt;br /&gt;
interrupted address in both cases. Signals are different from hardware&lt;br /&gt;
interrupts in that they are a software construct and don't involve&lt;br /&gt;
privilege transitions, stack switches, or ring 0 code.&lt;br /&gt;
     OS/2 supports six signals--three common signals (Ctrl-C, Ctrl-Break,&lt;br /&gt;
and program termination) and three general-purpose signals. The Ctrl-C and&lt;br /&gt;
Ctrl-Break signals occur in response to keyboard activity; the program&lt;br /&gt;
termination signal occurs when a process is killed via the DosKill call.&lt;br /&gt;
1. The process termination signal handler is not called under&lt;br /&gt;
all conditions of process termination, only in response to&lt;br /&gt;
DosKill. Normal exits, GP faults, and so on do not&lt;br /&gt;
activate the process termination signal handler.&lt;br /&gt;
1&lt;br /&gt;
The three general-purpose signals are generated by an explicit call from a&lt;br /&gt;
thread, typically a thread from another process. A signal handler is in the&lt;br /&gt;
form of a far subroutine, that is, a subroutine that returns with a far&lt;br /&gt;
return instruction. When a signal arrives, the process's thread 1 is&lt;br /&gt;
interrupted from its current location and made to call the signal-handler&lt;br /&gt;
procedure with an argument provided by the signal generator. The signal-&lt;br /&gt;
handler code can return,&lt;br /&gt;
2. OS/2 interposes a code thunk, so the signal handler need not&lt;br /&gt;
concern itself with executing an IRET instruction, which language&lt;br /&gt;
compilers usually won't generate. When the signal handler is&lt;br /&gt;
entered, its return address points to a piece of OS/2 code that&lt;br /&gt;
contains the IRET instruction.&lt;br /&gt;
2 in which case the CPU returns from where it was&lt;br /&gt;
interrupted, or the signal handler can clean its stack and jump into the&lt;br /&gt;
process's code at some other spot, as in the C language's longjmp facility.&lt;br /&gt;
     The analogy between signals and hardware interrupts holds still&lt;br /&gt;
further. As it does in a hardware interrupt, the system blocks further&lt;br /&gt;
interrupts from the same source so that the signal handler won't be&lt;br /&gt;
arbitrarily reentered. The signal handler must issue a special form of&lt;br /&gt;
DosSetSigHandler to dismiss the signal and allow further signals to occur.&lt;br /&gt;
Typically this is done at the end of the signal-handling routine unless the&lt;br /&gt;
signal handler is reentrant. Also, like hardware interrupts, the equivalent&lt;br /&gt;
of the CLI instruction--the DosHoldSignal call--is used to protect critical&lt;br /&gt;
sections from being interrupted via a signal.&lt;br /&gt;
     Unlike hardware interrupts, signals have no interrupt priority. As&lt;br /&gt;
each enabled signal occurs, the signal handler is entered, even if another&lt;br /&gt;
signal handler must be interrupted. New signal events that come in while&lt;br /&gt;
that signal is still being processed from an earlier event--before the&lt;br /&gt;
signal has been dismissed by the handler--are held until the previous&lt;br /&gt;
signal event has been dismissed. Like hardware interrupts, this is a&lt;br /&gt;
pending-signal flag, not a counter. If three signals of the same kind are&lt;br /&gt;
held off, only one signal event occurs when that signal becomes&lt;br /&gt;
reenabled.&lt;br /&gt;
     A signal event occurs in the context of a process whose thread of&lt;br /&gt;
execution is interrupted for the signal handler; a signal doesn't cause the&lt;br /&gt;
CPU to stop executing another process in order to execute the first&lt;br /&gt;
process's signal handler. When OS/2 &amp;quot;posts&amp;quot; a signal to a process, it&lt;br /&gt;
simply makes a mark that says, &amp;quot;The next time we run this guy, store his&lt;br /&gt;
CS, IP, and Flags values on the stack and start executing here instead.&amp;quot;&lt;br /&gt;
The system uses its regular priority rules to assign the CPU to threads;&lt;br /&gt;
when the scheduler next runs the signaled thread, the dispatcher code that&lt;br /&gt;
sends the CPU into the application's code reads the &amp;quot;posted signal&amp;quot; mark&lt;br /&gt;
and does the required work.&lt;br /&gt;
     Because a signal &amp;quot;pseudo interrupt&amp;quot; is merely a trick of the&lt;br /&gt;
dispatcher, signal handlers don't run in ring 0 as do hardware interrupt&lt;br /&gt;
handlers; they run in ring 3 as do all application threads. In general, as&lt;br /&gt;
far as OS/2 is concerned, the process isn't in any sort of special state&lt;br /&gt;
when it's executing a signal handler, and no special rules govern what a&lt;br /&gt;
thread can and cannot do in a signal handler.&lt;br /&gt;
     Receiving a signal when thread 1 is executing an application or&lt;br /&gt;
dynlink code is straightforward: The system saves CS, IP, and Flags, and&lt;br /&gt;
the signal handler saves the rest. The full register complement can be&lt;br /&gt;
restored after the signal has been processed, and thread 1's normal&lt;br /&gt;
execution resumes without incident. If thread 1 is executing a system call&lt;br /&gt;
that takes the CPU inside the kernel, the situation is more complex. OS/2&lt;br /&gt;
can't emulate an interrupt from the system ring 0 code to the application's&lt;br /&gt;
ring 3 code, nor can OS/2 take the chance that the signal handler never&lt;br /&gt;
returns from the signal&lt;br /&gt;
3.It's acceptable for a signal handler to clean up thread 1's&lt;br /&gt;
stack, dismiss the signal, jump to another part of the&lt;br /&gt;
application, and never return from the signal. For example, an&lt;br /&gt;
application can jump into its &amp;quot;prompt and command loop&amp;quot; in&lt;br /&gt;
response to the press of Ctrl-C.&lt;br /&gt;
3 and therefore leaves OS/2's internals in an&lt;br /&gt;
intermediate state. Instead, when a signal is posted and thread 1 is&lt;br /&gt;
executing ring 0 OS/2 code, the system either completes its operations&lt;br /&gt;
before recognizing the signal or aborts the operation and then recognizes&lt;br /&gt;
the signal. If the operation is expected to take place &amp;quot;quickly,&amp;quot; the&lt;br /&gt;
system completes the operation, and the signal is recognized at the point&lt;br /&gt;
where the CPU resumes executing the application's ring 3 code.&lt;br /&gt;
     All non-I/O operations are deemed to complete &amp;quot;quickly,&amp;quot; with the&lt;br /&gt;
exception of the explicit blocking operations such as DosSleep, DosSemWait,&lt;br /&gt;
and so on. I/O operations depend on the specific device. Disk I/O completes&lt;br /&gt;
quickly, but keyboard and serial I/O generally do not. Clearly, if we wait&lt;br /&gt;
for the user to finish typing a line before we recognize a signal, we might&lt;br /&gt;
never recognize it--especially if the signal is Ctrl-C! In the case of&lt;br /&gt;
&amp;quot;slow devices,&amp;quot; OS/2 or the device driver terminates the operation and&lt;br /&gt;
returns to the application with an error code. The signal is recognized&lt;br /&gt;
when the CPU is about to resume executing the ring 3 application code that&lt;br /&gt;
follows the system call that was interrupted.&lt;br /&gt;
     Although the application is given an error code to explain that the&lt;br /&gt;
system call was interrupted, the application may be unable to reissue the&lt;br /&gt;
system call to complete the work. In the case of device I/O, the&lt;br /&gt;
application typically can't tell how much, if any, of the requested output&lt;br /&gt;
or input took place before the signal interrupted the operation. If an&lt;br /&gt;
output operation is not reissued, some data at the end of the write may be&lt;br /&gt;
missing. If an output operation is restarted, then some data at the&lt;br /&gt;
beginning of the write may be written twice. In the case of DosSleep, the&lt;br /&gt;
application cannot tell how much of the requested sleep has elapsed. These&lt;br /&gt;
issues are not usually a problem; it's typically keyboard input that is&lt;br /&gt;
interrupted. In the case of the common signals (Ctrl-C, Ctrl-Break, and&lt;br /&gt;
process killed) the application typically flushes partial keyboard input&lt;br /&gt;
anyway. Applications that use other &amp;quot;slow&amp;quot; devices or the IPC flag signals&lt;br /&gt;
need to deal with this, however.&lt;br /&gt;
     Although a process can have multiple threads, only thread 1 is used to&lt;br /&gt;
execute the signal handler.&lt;br /&gt;
4. For this reason, a process should not terminate thread 1 and&lt;br /&gt;
continue executing with others; then it cannot receive signals.&lt;br /&gt;
4 This leads to an obvious solution to the&lt;br /&gt;
interrupted system call problem: Applications that will be inconvenienced&lt;br /&gt;
by interrupted system calls due to signals should dedicate thread 1 to work&lt;br /&gt;
that doesn't make interruptible system calls and use other thread(s) for&lt;br /&gt;
that work. In the worst case, thread 1 can be totally dedicated to waiting&lt;br /&gt;
for signals: It can block on a RAM semaphore that is never released, or it&lt;br /&gt;
can execute a DosSleep loop.&lt;br /&gt;
     A couple of practical details about signals are worth noting. First,&lt;br /&gt;
the user of a high-level language such as C need not worry about saving the&lt;br /&gt;
registers inside the signal-handler routine. The language runtimes&lt;br /&gt;
typically provide code to handle all these details; as far as the&lt;br /&gt;
application program is concerned, the signal handler is asynchronously far&lt;br /&gt;
called, and it can return from the signal by the return() statement. Also,&lt;br /&gt;
no application can receive a signal without first requesting it, so you&lt;br /&gt;
need not worry about setting up signal handlers if your application doesn't&lt;br /&gt;
explicitly ask to use them. A process can have only one signal-handling&lt;br /&gt;
address for each signal, so general-purpose dynlink routines (ones that&lt;br /&gt;
might be called by applications that aren't bundled with the dynlink&lt;br /&gt;
package) should never set a signal handler; doing so might override a&lt;br /&gt;
handler established by the client program code.&lt;br /&gt;
     Signals interact with critical sections in much the same way as&lt;br /&gt;
interrupts do. If a signal arrives while thread 1 is executing a critical&lt;br /&gt;
section that is protected by a semaphore and if that signal handler never&lt;br /&gt;
returns to the interrupted location, the critical section's semaphore will&lt;br /&gt;
be left jammed on. Even if the signal handler eventually returns, deadlock&lt;br /&gt;
occurs if it attempts to enter the critical section during processing of&lt;br /&gt;
the signal (perhaps it called a dynlink package, unaware that the package&lt;br /&gt;
contained a critical section). Dynlink packages must deal with this problem&lt;br /&gt;
by means of the DosHoldSignal call, which is analogous to the CLI/STI&lt;br /&gt;
instructions for hardware interrupts: The DosHoldSignal holds off arriving&lt;br /&gt;
signals until they are released. Held-off signals should be released within&lt;br /&gt;
a second or two so that the user won't be pounding Ctrl-C and thinking that&lt;br /&gt;
the application has crashed. Applications can use DosHoldSignal, or they&lt;br /&gt;
can simply ensure that thread 1 never enters critical sections, perhaps by&lt;br /&gt;
reserving it for signal handling, as discussed above.&lt;br /&gt;
     Ctrl-C and Ctrl-Break are special, device-specific operations. Setting&lt;br /&gt;
a signal handler for these signals is a form of I/O to the keyboard device;&lt;br /&gt;
applications must never do this until they have verified that they have&lt;br /&gt;
been assigned the keyboard device. See Chapter 14, Interactive Programs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==13  The Presentation Manager and VIO==&lt;br /&gt;
&lt;br /&gt;
In the early chapters of this book, I emphasized the importance of a high-&lt;br /&gt;
powered, high-bandwidth graphical user interface. It's a lot of work for an&lt;br /&gt;
application to manage graphical rendition, windowing, menus, and so on, and&lt;br /&gt;
it's hard for the user to learn a completely different interface for each&lt;br /&gt;
application. Therefore, OS/2 contains a subsystem called the presentation&lt;br /&gt;
manager (PM) that provides these services and more. The presentation&lt;br /&gt;
manager is implemented as a dynlink subsystem and daemon process&lt;br /&gt;
combination, and it provides:&lt;br /&gt;
&lt;br /&gt;
* High-performance graphical windowing.&lt;br /&gt;
* A powerful user interface model, including drop-down menus, scroll bars, icons, and mouse and keyboard interfaces. Most of these facilities are optional to the application; it can choose the standard services or &amp;quot;roll its own.&amp;quot;&lt;br /&gt;
* Device independence. The presentation manager contains a sophisticated multilevel device interface so that as much work as possible is pushed down to &amp;quot;smart&amp;quot; graphics cards to optimize performance.&lt;br /&gt;
&lt;br /&gt;
     Interfacing an application with the presentation manager involves a&lt;br /&gt;
degree of effort that not all programmers may want to put forth. The&lt;br /&gt;
interface to the application may be so simple that the presentation&lt;br /&gt;
manager's features are of little value, or the programmer may want to port&lt;br /&gt;
an MS-DOS application to OS/2 with the minimum degree of change. For these&lt;br /&gt;
reasons, OS/2 provides a second interface package called VIO,&lt;br /&gt;
1. VIO is a convenience term that encompasses three dynlink&lt;br /&gt;
subsystems: KBD (keyboard), VIO (Video I/O; the display adapter),&lt;br /&gt;
and MOU (mouse).&lt;br /&gt;
1 which is&lt;br /&gt;
primarily character oriented and looks much like the MS-DOS ROM BIOS video&lt;br /&gt;
interface. The initial release of OS/2 contains only VIO, implemented as a&lt;br /&gt;
separate package. The next release will contain the presentation manager,&lt;br /&gt;
and VIO will then become an alternate interface to the presentation&lt;br /&gt;
manager.&lt;br /&gt;
     Fundamentally, the presentation manager and VIO are the equivalent of&lt;br /&gt;
device drivers. They are implemented as dynlink packages because they are&lt;br /&gt;
device dependent and need to be replaced if different devices are used.&lt;br /&gt;
Dynlinks are used instead of true device drivers because they can provide&lt;br /&gt;
high throughput for the screen device: A simple call is made directly to&lt;br /&gt;
the code that paints the pixels on the screen. Also, the dynlink interface&lt;br /&gt;
allows the presentation manager to be implemented partially as a dynlink&lt;br /&gt;
subsystem and partially as a daemon process accessed by that subsystem.&lt;br /&gt;
     These packages are complex; explaining them in detail is beyond the&lt;br /&gt;
scope of this book. Instead, I will discuss from a general perspective the&lt;br /&gt;
special issues for users of this package.&lt;br /&gt;
     VIO is essentially character oriented. It supports graphics-based&lt;br /&gt;
applications, but only to the extent of allowing them to manipulate the&lt;br /&gt;
display controller directly so that they can &amp;quot;go around&amp;quot; VIO and provide&lt;br /&gt;
special interfaces related to screen switching of graphics applications&lt;br /&gt;
(see below). The base VIO package plays a role similar to that of the ROM&lt;br /&gt;
BIOS INT 10/INT 16 interface used in MS-DOS. It contains some useful&lt;br /&gt;
enhancements but in general is a superset of the ROM BIOS functions, so INT&lt;br /&gt;
10-based real mode applications can be quickly adjusted to use VIO instead.&lt;br /&gt;
VIO is replaceable, in whole or in part, to allow applications being run&lt;br /&gt;
with VIO to be managed later by the presentation manager package.&lt;br /&gt;
     The presentation manager is entirely different from VIO. It offers an&lt;br /&gt;
extremely rich and powerful set of functions that support windowing, and it&lt;br /&gt;
offers a full, device-independent graphics facility. Its message-oriented&lt;br /&gt;
architecture is well suited to interactive applications. Once the&lt;br /&gt;
presentation manager programming model is learned and the key elements of&lt;br /&gt;
its complex interface are understood, a programmer can take advantage of a&lt;br /&gt;
very sexy user interface with comparatively little effort. The presentation&lt;br /&gt;
manager also replaces the existing VIO/KBD/MOU calls in order to support&lt;br /&gt;
older programs that use these interfaces.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.1  Choosing Between PM and VIO&lt;br /&gt;
&lt;br /&gt;
The roles of VIO and the presentation manager sometimes cause confusion:&lt;br /&gt;
Which should you use for your application? The default interface for a new&lt;br /&gt;
application should be the presentation manager. It's not in OS/2 version&lt;br /&gt;
1.0 because of scheduling restrictions; owners of version 1.0 will receive&lt;br /&gt;
the presentation manager as soon as it is available, and all future&lt;br /&gt;
releases will be bundled with the presentation manager. The presentation&lt;br /&gt;
manager will be present on essentially all personal computer OS/2&lt;br /&gt;
installations, so you are not restricting the potential market for an&lt;br /&gt;
application if you write it for the presentation manager.&lt;br /&gt;
     The presentation manager interface allows an application to utilize a&lt;br /&gt;
powerful, graphical user interface. In general form it's standardized for&lt;br /&gt;
ease of use, but it can be customized in a specific implementation so that&lt;br /&gt;
an application can provide important value-added features. On the other&lt;br /&gt;
hand, if you are porting an application from the real mode environment, you&lt;br /&gt;
will find it easier to use the VIO interface. Naturally, such programs run&lt;br /&gt;
well under the presentation manager, but they forgo the ability to use&lt;br /&gt;
graphics and to interact with the presentation manager. The user can still&lt;br /&gt;
&amp;quot;window&amp;quot; the VIO application's screen image, but without the application's&lt;br /&gt;
knowledge or cooperation. To summarize, you have three choices when writing&lt;br /&gt;
an application:&lt;br /&gt;
&lt;br /&gt;
     1.  Only use the VIO interface in character mode. This works well in a&lt;br /&gt;
         presentation manager environment and is a good choice for ported&lt;br /&gt;
         real mode applications. The VIO interface is also supported by the&lt;br /&gt;
         Family API mechanism. This mode is compatible with the Family API&lt;br /&gt;
         facility.&lt;br /&gt;
&lt;br /&gt;
     2.  Use the special VIO interface facilities to sidestep VIO and&lt;br /&gt;
         directly manipulate the display screen in either character or &lt;br /&gt;
         graphics mode. This also works in a presentation manager&lt;br /&gt;
         environment, but the application will not be able to run in a &lt;br /&gt;
         window. This approach can be compatible with the Family API if it &lt;br /&gt;
         is carefully implemented.&lt;br /&gt;
&lt;br /&gt;
     3.  Use the presentation manager interface--the most sophisticated&lt;br /&gt;
         interface for the least effort. The presentation manager interface&lt;br /&gt;
         provides a way to &amp;quot;operate&amp;quot; applications that will become a widely&lt;br /&gt;
         known user standard because of the capabilities of the interface,&lt;br /&gt;
         because of the support it receives from key software vendors, and&lt;br /&gt;
         because it's bundled with OS/2. The user is obviously at an&lt;br /&gt;
         advantage if he or she does not have to spend time learning a new&lt;br /&gt;
         interface and operational metaphors to use your application.&lt;br /&gt;
         Finally, Microsoft is a strong believer in the power of a&lt;br /&gt;
         graphical user interface; future releases of OS/2 will contain&lt;br /&gt;
         &amp;quot;more-faster-better&amp;quot; presentation manager features. Many of these&lt;br /&gt;
         improvements will apply to existing presentation manager&lt;br /&gt;
         applications; others will expand the interface API. The standard&lt;br /&gt;
         of performance for application interfaces, as well as for&lt;br /&gt;
         application performance, continues to evolve. The rudimentary&lt;br /&gt;
         interfaces and function of the first-generation PC software are no&lt;br /&gt;
         longer considered competitive. Although OS/2 can do nothing to&lt;br /&gt;
         alleviate the developer's burden of keeping an application's&lt;br /&gt;
         function competitive, the presentation manager is a great help in&lt;br /&gt;
         keeping the application's interface state of the art.&lt;br /&gt;
&lt;br /&gt;
     Clearly, using the presentation manager interface is the best&lt;br /&gt;
strategy for new or extensively reworked applications. The presentation&lt;br /&gt;
manager API will be expanded and improved; the INT 10-like VIO functions&lt;br /&gt;
and the VIO direct screen access capabilities will be supported for the&lt;br /&gt;
foreseeable future, but they're an evolutionary dead end. Given that, you&lt;br /&gt;
may want to use the VIO mechanism or the Family API facilities to quickly&lt;br /&gt;
port an application from a real mode version and then use the presentation&lt;br /&gt;
manager in a product upgrade release.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.2  Background I/O&lt;br /&gt;
&lt;br /&gt;
A process is in the background when it is no longer interacting directly&lt;br /&gt;
with the user. In a presentation manager environment, this means that none&lt;br /&gt;
of the process's windows are the keyboard focus. The windows themselves may&lt;br /&gt;
still be visible, or they may be obscured or iconic. In a VIO environment,&lt;br /&gt;
a process is in the background when the user has selected another screen&lt;br /&gt;
group. In this case, the application's screen display is not visible.&lt;br /&gt;
     A presentation manager application can easily continue to update its&lt;br /&gt;
window displays when it is in the background; the application can continue&lt;br /&gt;
to call the presentation manager to change its window contents in any way&lt;br /&gt;
it wishes. A presentation manager application can arrange to be informed&lt;br /&gt;
when it enters and leaves the background (actually, receives and loses the&lt;br /&gt;
keyboard focus), or it can simply carry on with its work, oblivious to the&lt;br /&gt;
issue. Background I/O can continue regardless of whether I/O form is&lt;br /&gt;
character or graphics based.&lt;br /&gt;
     VIO applications can continue to do I/O in background mode as well.&lt;br /&gt;
The VIO package maintains a logical video buffer for each screen group;&lt;br /&gt;
when VIO calls are made to update the display of a screen group that is in&lt;br /&gt;
the background, VIO makes the requested changes to the logical video&lt;br /&gt;
buffer. When the screen group is restored to the foreground, the updated&lt;br /&gt;
contents of the logical video buffer are copied to the display's physical&lt;br /&gt;
video buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.3  Graphics Under VIO&lt;br /&gt;
&lt;br /&gt;
VIO is a character-oriented package and provides character mode&lt;br /&gt;
applications with a variety of services. As we have just seen, when a&lt;br /&gt;
screen switch takes place, VIO automatically handles saving the old screen&lt;br /&gt;
image and restoring the new. VIO does provide a mechanism to allow an&lt;br /&gt;
application to sidestep VIO and directly manipulate the physical video&lt;br /&gt;
buffer, where it is then free to use any graphical capability of the&lt;br /&gt;
hardware. There are two major disadvantages to sidestepping VIO for&lt;br /&gt;
graphics rather than using the presentation manager services:&lt;br /&gt;
&lt;br /&gt;
     1.  The application is device dependent because it must manipulate the&lt;br /&gt;
         video display hardware directly.&lt;br /&gt;
&lt;br /&gt;
     2.  VIO can no longer save or restore the state of the physical video&lt;br /&gt;
         buffer during screen switch operations. The application must use a&lt;br /&gt;
         special VIO interface to provide these functions itself.&lt;br /&gt;
&lt;br /&gt;
     The following discussion applies only to applications that want to&lt;br /&gt;
sidestep the presentation manager and VIO interfaces and interact directly&lt;br /&gt;
with the display hardware.&lt;br /&gt;
     Gaining access to the video hardware is easy; the VIO call VioGetBuf&lt;br /&gt;
provides a selector to the video buffer and also gives the application's&lt;br /&gt;
ring 2 code segments, if any, permission to program the video controller's&lt;br /&gt;
registers. The complication arises from the screen-switching capabilities&lt;br /&gt;
of OS/2. When the user switches the application into a background screen&lt;br /&gt;
group, the contents of the video memory belong to someone else; the&lt;br /&gt;
application's video memory is stored somewhere in RAM. It is disastrous&lt;br /&gt;
when an application doesn't pay attention to this process and accidentally&lt;br /&gt;
updates the video RAM or the video controller while they are assigned to&lt;br /&gt;
another screen group.&lt;br /&gt;
     Two important issues are connected with screen switching: (1) How does&lt;br /&gt;
an application find out that it's in background mode? (2) Who saves and&lt;br /&gt;
restores its screen image and where? The VioScrLock call handles screen&lt;br /&gt;
access. Before every access to the display memory or the display&lt;br /&gt;
controller, an application must first issue the VioScrLock call. While the&lt;br /&gt;
call is in effect, OS/2 cannot perform any screen switches. Naturally, the&lt;br /&gt;
application must do its work and quickly release the screen switch lockout.&lt;br /&gt;
Failure to release the lock in a timely fashion has the effect of hanging&lt;br /&gt;
the system, not only for a user's explicit screen switch commands, but also&lt;br /&gt;
for other facilities that use the screen switch mechanism, such as the hard&lt;br /&gt;
error handler. Hard errors can't be presented to the user while the screen&lt;br /&gt;
lock is in effect. If OS/2 needs to switch screens, and an aberrant&lt;br /&gt;
application has the screen lock set, OS/2 will cancel the lock and perform&lt;br /&gt;
the screen switch after a period (currently 30 seconds). This is still a&lt;br /&gt;
disaster scenario, although a mollified one, because the application that&lt;br /&gt;
was summarily &amp;quot;delocked&amp;quot; will probably end up with a trashed screen image.&lt;br /&gt;
The screen lock and unlock calls execute relatively rapidly, so they can be&lt;br /&gt;
called frequently to protect only the actual write-to-screen operation,&lt;br /&gt;
leaving the screen unlocked during computation. Basically, an application&lt;br /&gt;
should use VioScrLock to protect a block of I/O that can be written, in its&lt;br /&gt;
entirety, without significant recomputation. Examples of such blocks are a&lt;br /&gt;
screen scroll, a screen erase, and a write to a cell in a spreadsheet&lt;br /&gt;
program.&lt;br /&gt;
     VioScrLock must be used to protect code sequences that program the&lt;br /&gt;
display hardware as well as code sequences that write to video memory. Some&lt;br /&gt;
peripheral programming sequences are noninterruptible. For example, a two-&lt;br /&gt;
step programming sequence in which the first I/O write selects a&lt;br /&gt;
multiplexed register and the second write modifies that register is&lt;br /&gt;
uninterruptible because the first write placed the peripheral device into a&lt;br /&gt;
special state. Such sequences must be protected within one lock/unlock&lt;br /&gt;
pair.&lt;br /&gt;
     Sometimes when an application calls VioScrLock, it receives a special&lt;br /&gt;
error code that says, &amp;quot;The screen is unavailable.&amp;quot; This means that the&lt;br /&gt;
screen has been switched into the background and that the application may&lt;br /&gt;
not--and must not--manipulate the display hardware. Typically, the program&lt;br /&gt;
issues a blocking form of VioScrLock that suspends the thread until the&lt;br /&gt;
screen is again in the foreground and the video display buffers contain&lt;br /&gt;
that process's image.&lt;br /&gt;
     An application that directly manipulates the video hardware must do&lt;br /&gt;
more than simply lay low when it is in the background. It must also save&lt;br /&gt;
and restore the entire video state--the contents of the display buffer and&lt;br /&gt;
the modes, palates, cursors, and so on of the display controller. VIO does&lt;br /&gt;
not provide this service to direct-screen manipulation processes for two&lt;br /&gt;
reasons. First, the process is very likely using the display in a graphics&lt;br /&gt;
mode. Some display cards contain a vast amount of video memory, and VIO&lt;br /&gt;
would be forced to save it all just in case the application was using it&lt;br /&gt;
all. Second, many popular display controllers such as the EGA and&lt;br /&gt;
compatibles contain many write-only control registers. This means that VIO&lt;br /&gt;
cannot read the controller state back from the card in order to save it for&lt;br /&gt;
a later restoration. The only entity that understands the state of the&lt;br /&gt;
card, and therefore the only entity that can restore that state, is the&lt;br /&gt;
code that programmed it--the application itself.&lt;br /&gt;
     But how does the system notify the process when it's time to save or&lt;br /&gt;
restore? Processes can call the system in many ways, but the system can't&lt;br /&gt;
call processes. OS/2 deals with this situation by inverting the usual&lt;br /&gt;
meaning of call and return. When a process first decides to refresh its own&lt;br /&gt;
screen, it creates an extra thread and uses that thread to call the&lt;br /&gt;
VioSavRedrawWait function. The thread doesn't return from this call right&lt;br /&gt;
away; instead, VIO holds the thread &amp;quot;captive&amp;quot; until it's time for a screen&lt;br /&gt;
switch. To notify the process that it must now save its screen image, VIO&lt;br /&gt;
allows the captive thread to return from the VioSavRedrawWait call. The&lt;br /&gt;
process then saves the display state and screen contents, typically using&lt;br /&gt;
the returned thread. When the save operation is complete, VioSavRedrawWait&lt;br /&gt;
is called again. This notifies VIO that the save is complete and that the&lt;br /&gt;
screen can now be switched; it also resets the cycle so that the process&lt;br /&gt;
can again be notified when it's time to restore its saved screen image. In&lt;br /&gt;
effect, this mechanism makes the return from VioSavRedrawWait analogous to&lt;br /&gt;
a system-to-process call, and it makes the later call to VioSavRedrawWait&lt;br /&gt;
analogous to a return from process to system.&lt;br /&gt;
     The design of OS/2 generally avoids features in which the system calls&lt;br /&gt;
a process to help a system activity such as screen switching. This is&lt;br /&gt;
because a tenet of the OS/2 design religion is that an aberrant process&lt;br /&gt;
should not be able to crash the system. Clearly, we're vulnerable to that&lt;br /&gt;
in this case. VIO postpones the screen switch until the process saves its&lt;br /&gt;
screen image, but what if the process somehow hangs up and doesn't complete&lt;br /&gt;
the save? The screen is in an indeterminate state, and no process can use&lt;br /&gt;
the screen and keyboard. As far as the user is concerned, the system has&lt;br /&gt;
crashed. True, other processes in the system are alive and well, but if the&lt;br /&gt;
user can't get to them, even to save his or her work, their continued&lt;br /&gt;
health is of little comfort.&lt;br /&gt;
     The designers of OS/2 were stuck here, between a rock and a hard&lt;br /&gt;
place: Applications had to be able to save their screen image if they were&lt;br /&gt;
to have direct video access, but such a facility violated the &amp;quot;no crashing&amp;quot;&lt;br /&gt;
tenet of the design religion. Because the video access had to be supported&lt;br /&gt;
and the system had to be crash resistant, we found a two-part workaround.&lt;br /&gt;
     The first part concerns the most common cause of a process hanging up&lt;br /&gt;
in its screen-save operation: hard errors. When a hard error occurs, the&lt;br /&gt;
hard error daemon uses the screen switch mechanism to take control of the&lt;br /&gt;
screen and the keyboard. The hard error daemon saves the existing screen&lt;br /&gt;
image and keeps the application that was in the foreground at the time of&lt;br /&gt;
the hard error from fighting with the daemon over control of the screen and&lt;br /&gt;
the keyboard. However, if the hard error daemon uses the screen-switching&lt;br /&gt;
mechanism and if the screen-switching mechanism allows the foreground&lt;br /&gt;
process to save its own screen image, that process might, while saving its&lt;br /&gt;
screen image, try to use the device that has the hard error and thus&lt;br /&gt;
deadlock the system. The device in error won't service more requests until&lt;br /&gt;
the hard error is cleared, but the hard error can't be cleared until the&lt;br /&gt;
daemon takes control. The daemon can't take control until the foreground&lt;br /&gt;
process is through saving, and the foreground process can't complete saving&lt;br /&gt;
until the device services its request. Note that this deadlock doesn't&lt;br /&gt;
require an explicit I/O operation on the part of the foreground process;&lt;br /&gt;
simply allocating memory or referencing a segment might cause swapping or&lt;br /&gt;
loading activity on the device that is experiencing the hard error.&lt;br /&gt;
     A two-part approach is used to solve this problem. First, deadlocks&lt;br /&gt;
involving the hard error daemon are managed by having the hard error screen&lt;br /&gt;
switch do a partial screen save. When I said earlier that VIO would not&lt;br /&gt;
save the video memory of direct access screen groups, I was lying a bit.&lt;br /&gt;
When the system is doing a hard error screen switch, VIO will save the&lt;br /&gt;
first part (typically 4 KB) of the video memory--enough to display a page&lt;br /&gt;
of text. We don't have to worry about how much video RAM the application&lt;br /&gt;
was using because the video display will be switched to character mode and&lt;br /&gt;
the hard error daemon will overwrite only a small part of video memory.&lt;br /&gt;
Naturally, this means that the hard error daemon must always restore the&lt;br /&gt;
original screen group; it can't switch to a third screen group because the&lt;br /&gt;
first one's video memory wasn't fully saved.&lt;br /&gt;
     VIO and the hard error daemon keep enough free RAM around to save this&lt;br /&gt;
piece of the video memory so that a hard error screen switch can always&lt;br /&gt;
take place without the need for memory swapping. When the hard error daemon&lt;br /&gt;
is finished with the screen, the overwritten video memory is restored from&lt;br /&gt;
the buffer. As we discussed above, however, VIO can't restore the state of&lt;br /&gt;
the video controller itself; only the application can do that. The&lt;br /&gt;
VioModeWait function is used to notify the application that it must restore&lt;br /&gt;
the screen state.&lt;br /&gt;
     In summary, any application that directly accesses the video hardware&lt;br /&gt;
must provide captive threads to VioSavRedrawWait and to VioModeWait.&lt;br /&gt;
VioSavRedrawWait will return when the application is to save or to restore&lt;br /&gt;
the video memory. VioModeWait will return when the application is to&lt;br /&gt;
restore the state of the video controller from the application's own record&lt;br /&gt;
of the controller's state.&lt;br /&gt;
     The second part of the &amp;quot;application hangs while saving screen and&lt;br /&gt;
hangs system&amp;quot; solution is unfortunately ad hoc: If the application does not&lt;br /&gt;
complete its screen save operation within approximately 30 seconds, the&lt;br /&gt;
system considers it hung and switches the screen anyway. The hung process&lt;br /&gt;
is suspended while it's in background so that it won't suddenly &amp;quot;come&lt;br /&gt;
alive&amp;quot; and manipulate the screen. When the process is again in the&lt;br /&gt;
foreground, the system unsuspends it and hopes that it will straighten&lt;br /&gt;
itself out. In such a case, the application's screen image may be trashed.&lt;br /&gt;
At best, the user can enter a &amp;quot;repaint screen&amp;quot; command to the application&lt;br /&gt;
and all will be well; at worst, the application is hung up, but the system&lt;br /&gt;
itself is alive and well. Building a system that can detect and correct&lt;br /&gt;
errors on the part of an application is impossible; the best that we can&lt;br /&gt;
hope to do is to keep an aberrant application from damaging the rest of the&lt;br /&gt;
system.&lt;br /&gt;
     I hope that this long and involved discussion of rules, regulations,&lt;br /&gt;
doom, and disaster has not frightened you into contemplating programs that&lt;br /&gt;
communicate by Morse code. You need be concerned with these issues only if&lt;br /&gt;
you write applications that manipulate the display device directly,&lt;br /&gt;
circumventing either VIO or the presentation manager interfaces. These&lt;br /&gt;
concerns are not an issue when you are writing ordinary text applications&lt;br /&gt;
that use VIO or the presentation manager or graphics applications that use&lt;br /&gt;
the presentation manager. VIO and the presentation manager handle screen&lt;br /&gt;
saving and support background display writing. Finally, I'll point out, as&lt;br /&gt;
a curiosity, that even processes that use handle operations only to write&lt;br /&gt;
to STDOUT use VIO or the presentation manager. When STDOUT points to the&lt;br /&gt;
screen device, the operating system routes STDOUT writes to the&lt;br /&gt;
VIO/presentation manager packages. This is, of course, invisible to the&lt;br /&gt;
application; it need not concern itself with foreground/background, EGA&lt;br /&gt;
screen modes, hard error screen restorations, and the like.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==14  Interactive Programs==&lt;br /&gt;
&lt;br /&gt;
A great many applications interact with the user via the screen and the&lt;br /&gt;
keyboard. Because the primary function of most desktop computers is to run&lt;br /&gt;
interactive applications, OS/2 contains a variety of services that make&lt;br /&gt;
interaction powerful and efficient.&lt;br /&gt;
     As we've seen in earlier chapters, interactive programs can use the&lt;br /&gt;
presentation manager to manage their interface, or they can do it&lt;br /&gt;
themselves, using VIO or direct video access for their I/O. The&lt;br /&gt;
presentation manager provides a great deal of function and automatically&lt;br /&gt;
solves a great many problems. For example, a presentation manager&lt;br /&gt;
application doesn't have to concern itself with the sharing of the single&lt;br /&gt;
keyboard among all processes in its screen group. The presentation manager&lt;br /&gt;
takes care of that by handling the keyboard and by simply sending keyboard&lt;br /&gt;
events to each process, as appropriate.&lt;br /&gt;
     If you're writing an application that uses the presentation manager,&lt;br /&gt;
then you can skip this chapter. If you're writing an application that does&lt;br /&gt;
not use the presentation manager but that may be used in an interactive&lt;br /&gt;
fashion, it's very important that you understand the issues discussed in&lt;br /&gt;
this chapter. They apply to all programs that use VIO or the STDIN/STDOUT&lt;br /&gt;
handles to do interactive I/O, even if such programs are being run via the&lt;br /&gt;
presentation manager.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
14.1  I/O Architecture&lt;br /&gt;
&lt;br /&gt;
Simply put, the system I/O architecture says that all programs read their&lt;br /&gt;
main input from the STDIN handle and write their main output to the STDOUT&lt;br /&gt;
handle. This applies to all non-presentation manager applications, but&lt;br /&gt;
especially to interactive applications. The reason is that OS/2 and&lt;br /&gt;
program-execution utilities such as CMD.EXE (shell programs) cooperate to&lt;br /&gt;
use the STDIN/STDOUT mechanism to control access to the screen and&lt;br /&gt;
keyboard. For example, if two processes read from the keyboard at the same&lt;br /&gt;
time, some keys go to one process, and the rest go to the other in an&lt;br /&gt;
unpredictable fashion. Likewise, it is a bad idea for more than one process&lt;br /&gt;
to write to the screen at the same time.&lt;br /&gt;
1. Within the same screen group and/or window, of course.&lt;br /&gt;
Applications that use different virtual screens can each write to&lt;br /&gt;
their own screen without regard for other virtual screens.&lt;br /&gt;
1 Clearly, you don't want too many&lt;br /&gt;
processes doing keyboard/screen I/O within a single screen group, but you&lt;br /&gt;
also don't want too few. It would be embarrassing if a user terminated one&lt;br /&gt;
interactive application in a screen group, such as a program run from&lt;br /&gt;
CMD.EXE, and CMD.EXE failed to resume use of the keyboard/screen to print a&lt;br /&gt;
prompt.&lt;br /&gt;
     So how will we handle this? We might be running a great many processes&lt;br /&gt;
in a screen group. For example, you could use CMD.EXE to execute a&lt;br /&gt;
spreadsheet program, which was told to execute a subshell--another copy of&lt;br /&gt;
CMD.EXE. The user could then execute a program to interpret a special batch&lt;br /&gt;
script, which in turn executes an editor. And this editor was told to run a&lt;br /&gt;
copy of a C compiler to scan the source being edited for errors. Oh, yes,&lt;br /&gt;
and we forgot to mention that the top level CMD.EXE was told to run a copy&lt;br /&gt;
of the assembler in parallel with all these other operations (similar to&lt;br /&gt;
the UNIX &amp;quot;&amp;amp;&amp;quot; operation).&lt;br /&gt;
     Many processes are running in this screen group; some of them are&lt;br /&gt;
interactive, and some are not, and at any time only one is using the&lt;br /&gt;
keyboard and the screen. Although it would be handy to declare that the&lt;br /&gt;
most recently executed process will use the keyboard and the screen, you&lt;br /&gt;
can't: The most recently executed program was the C compiler, and it's not&lt;br /&gt;
even interactive. OS/2 cannot decide which process should be using the&lt;br /&gt;
screen and keyboard because OS/2 lacks any knowledge of the function of&lt;br /&gt;
each process. OS/2 knows only their child-parent relationships, and the&lt;br /&gt;
situation can be far too complex for that information to be sufficient.&lt;br /&gt;
     Because OS/2 can't determine which process should be using the screen&lt;br /&gt;
and the keyboard, it doesn't try. The processes themselves make the&lt;br /&gt;
determination. The rule is simple: The process that is currently using the&lt;br /&gt;
screen and the keyboard can grant access to a child process, or it can keep&lt;br /&gt;
access for itself. If a process grants access to a child process, then it&lt;br /&gt;
must keep off the screen and the keyboard until that child terminates. Once&lt;br /&gt;
the child process is granted use of the screen and the keyboard, the child&lt;br /&gt;
process is free to do as it wishes, perhaps granting access to its own&lt;br /&gt;
children. Until that child process terminates, the parent must avoid device&lt;br /&gt;
conflict by staying quiet.&lt;br /&gt;
     Let's look at how this works in real life. For example, CMD.EXE, the&lt;br /&gt;
first process in the screen group, starts up with STDIN open on the&lt;br /&gt;
keyboard and STDOUT open on the screen. (The system did this by magic.)&lt;br /&gt;
When this copy of CMD.EXE is told to execute the spreadsheet program,&lt;br /&gt;
CMD.EXE doesn't know if the spreadsheet program is interactive or not, so&lt;br /&gt;
it lets the child process--the spreadsheet program--inherit its STDIN and&lt;br /&gt;
STDOUT handles, which point to the keyboard and to the screen. Because&lt;br /&gt;
CMD.EXE granted access to the screen and the keyboard to the child, CMD.EXE&lt;br /&gt;
can't use STDIN or STDOUT until that child process terminates. Typically,&lt;br /&gt;
at this point CMD.EXE would DosCWait on its child process.&lt;br /&gt;
     Now the spreadsheet program comes alive. It writes to STDOUT, which is&lt;br /&gt;
the screen, and it reads from STDIN, which is the keyboard. When the&lt;br /&gt;
spreadsheet program is instructed to run CMD.EXE, it does so, presuming, as&lt;br /&gt;
did its parent, that CMD.EXE is interactive and therefore letting CMD.EXE&lt;br /&gt;
inherit its STDIN and STDOUT handles. Now the spreadsheet must avoid any&lt;br /&gt;
STDIN/STDOUT I/O until its child--CMD.EXE--terminates. As long as these&lt;br /&gt;
processes continue to run interactive children, things are going to work&lt;br /&gt;
out OK. When the children start to die and execution starts popping back up&lt;br /&gt;
the tree, applications restart, using the screen and the keyboard in the&lt;br /&gt;
proper order.&lt;br /&gt;
     But what about the detached assembly that CMD.EXE started before it&lt;br /&gt;
ran the spreadsheet? In this case, the user has explicitly told CMD.EXE&lt;br /&gt;
that it wants the application run &amp;quot;detached&amp;quot; from the keyboard. If the user&lt;br /&gt;
specified a STDIN for the assembler--perhaps a file--then CMD.EXE sets that&lt;br /&gt;
up for the child's STDIN. If the user didn't specify an alternate STDIN,&lt;br /&gt;
CMD.EXE opens STDIN on the  NULL device so that an application that reads&lt;br /&gt;
it will receive EOF. In this way, CMD.EXE (which knew that the application&lt;br /&gt;
wasn't to use the keyboard because the user gave explicit instructions) did&lt;br /&gt;
not let the child process inherit STDIN, so CMD.EXE continues to use it,&lt;br /&gt;
printing a new prompt and reading a new command. Figure 14-1 shows a&lt;br /&gt;
typical process tree. The shaded processes have inherited a STDIN, which&lt;br /&gt;
points to the keyboard, and a STDOUT, which points to the screen. All such&lt;br /&gt;
processes must lie on a single path if the rules are followed because each&lt;br /&gt;
process has the option of allowing a maximum of one child to inherit its&lt;br /&gt;
STDIN and STDOUT handles unchanged.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ÀÄÄÂÄÄÄÄÄÄÄÄÂÄÄÙ                 ÚÄÄÄÄ¿    Processes&lt;br /&gt;
             ÚÄÄÄÄÄÄÙ        ÀÄÄÄÄÄÄ¿             ³°°°°³  = using the&lt;br /&gt;
             ³                      ³             ÀÄÄÄÄÙ    keyboard &lt;br /&gt;
         ÚÄÄÄÁÄÄ¿                ÚÄÄÁÄÄÄ¿&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ÀÄÂÄÄÄÄÙ                ÀÄÂÄÄÂÄÙ&lt;br /&gt;
        ÚÄÄÙ                    ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
    ÚÄÄÄÁÄÄ¿                 ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
    ³      ³                 ³°°°°³    ³    ³&lt;br /&gt;
    ³      ³                 ÀÂÄÄÂÙ    ÀÄÄÄÂÙ&lt;br /&gt;
    ÀÄÂÄÄÂÄÙ               ÚÄÄÙ  ÀÄÄ¿      ÀÄÄ¿&lt;br /&gt;
   ÚÄÄÙ  ÀÄÄ¿           ÚÄÄÁÄ¿    ÚÄÁÄÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
ÚÄÄÁÄ¿    ÚÄÁÄÄ¿        ³°°°°³    ³    ³    ³    ³&lt;br /&gt;
³    ³    ³    ³        ÀÂÄÄÂÙ    ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÙ    ÀÄÄÄÄÙ      ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
                   ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
                   ³    ³    ³°°°°³&lt;br /&gt;
                   ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 14-1.  Processes using the keyboard.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     You are undoubtedly becoming a bit concerned at this point: &amp;quot;Does this&lt;br /&gt;
mean I'm forced to use the limited, serial STDIN/STDOUT interface for my&lt;br /&gt;
high-resolution graphics output?&amp;quot; I'm glad you asked. What we've been&lt;br /&gt;
discussing is the architectural model that must be followed because it's&lt;br /&gt;
used systemwide to avoid screen and keyboard conflicts. However,&lt;br /&gt;
applications can and should use special services to optimize their&lt;br /&gt;
interactive I/O as long as they do so according to the architectural model.&lt;br /&gt;
Specifically, OS/2 provides the KBD, VIO, and MOU dynlink packages. These&lt;br /&gt;
high-performance programs interface directly with the hardware, avoiding&lt;br /&gt;
the STDIN/STDOUT limited interfaces. The key is &amp;quot;directly with the&lt;br /&gt;
hardware&amp;quot;: A process is welcome to use hardware-specific interfaces&lt;br /&gt;
to optimize performance, but only after it has ensured that the&lt;br /&gt;
architectural model grants it access to that device.&lt;br /&gt;
     In practice, this is straightforward. Any interactive program that&lt;br /&gt;
wants to use KBD first ensures (via DosQHandType) that its STDIN handle is&lt;br /&gt;
open on the keyboard. If STDIN is not open on the keyboard, the keyboard&lt;br /&gt;
belongs to another program, and the interactive program must not burst in&lt;br /&gt;
on the rightful owner by using KBD. All dynlink device interfaces are&lt;br /&gt;
trusting souls and won't check your bona fides before they do their stuff,&lt;br /&gt;
so the application must look before it leaps. The same applies to STDOUT,&lt;br /&gt;
to the keyboard device, and to the VIO package. All applications must&lt;br /&gt;
verify that STDIN and STDOUT point to the keyboard and the screen before&lt;br /&gt;
they use any device-direct interface, which includes VIO, KBD, MOU, and&lt;br /&gt;
direct device access.&lt;br /&gt;
     What's an interactive program to do if it finds that STDIN or STDOUT&lt;br /&gt;
doesn't point to the keyboard and screen devices? I don't know, but the&lt;br /&gt;
author of the application does. Some applications might not be truly&lt;br /&gt;
interactive and therefore would work fine. For example, Microsoft Macro&lt;br /&gt;
Assembler (MASM) can prompt the user for the names of source, object, and&lt;br /&gt;
listing files. Although MASM is technically interacting with the user, MASM&lt;br /&gt;
is not an interactive application because it doesn't depend on the ability&lt;br /&gt;
to interact to do its work. If STDIN points to a file, MASM is perfectly&lt;br /&gt;
happy reading the filenames from that file. MASM doesn't need to see if&lt;br /&gt;
STDIN points to the keyboard because MASM doesn't need to use the KBD&lt;br /&gt;
package. Instead, MASM reads its names from STDIN and takes what it&lt;br /&gt;
gets.&lt;br /&gt;
     Other programs may not require an interactive interface, but when they&lt;br /&gt;
are interacting, they may want to use KBD or VIO to improve performance.&lt;br /&gt;
Such applications should test STDIN and STDOUT to see if they point to&lt;br /&gt;
the appropriate devices. If they do, applications can circumvent the&lt;br /&gt;
STDIN/STDOUT limitations and use KBD and VIO. If they don't, the&lt;br /&gt;
applications are stuck with STDIN and STDOUT. Finally, many interactive&lt;br /&gt;
applications make no sense at all in a noninteractive environment. These&lt;br /&gt;
applications need to check STDIN and STDOUT, and, if they don't point to&lt;br /&gt;
the devices, the applications should write an error message to STDERR and&lt;br /&gt;
terminate. Admittedly, the user is in error if he or she attempts to run an&lt;br /&gt;
interactive application, such as a WYSIWYG editor, detached, but printing&lt;br /&gt;
an error message is far better than trashing the display screen and&lt;br /&gt;
fighting with CMD.EXE over the keyboard. The screen group would then be&lt;br /&gt;
totally unusable, and the user might not even be able to terminate the&lt;br /&gt;
editor if he or she can't get the terminate command through the keyboard&lt;br /&gt;
contention.&lt;br /&gt;
     It's technically possible, although highly unusual, for an application&lt;br /&gt;
to inherit access to the keyboard yet not have access to the screen. More&lt;br /&gt;
commonly, an application has access to the screen but not to the keyboard.&lt;br /&gt;
Although most users would find it confusing, power users can detach&lt;br /&gt;
programs such as compilers so that any output summary or error messages&lt;br /&gt;
they produce appear on the screen. Although the user may end up with&lt;br /&gt;
intermingled output, he or she may like the instant notification. Each&lt;br /&gt;
application that wants to use VIO, KBD, or the environment manager needs to&lt;br /&gt;
check STDIN and STDOUT individually for access to the appropriate device.&lt;br /&gt;
     Earlier in this section, we talked about how applications work when&lt;br /&gt;
they create children that inherit the screen and the keyboard, and it&lt;br /&gt;
probably sounded complicated. In practice, it can be simple. For example,&lt;br /&gt;
the technique used to DosExecPgm a child that will inherit the keyboard can&lt;br /&gt;
be used when the parent itself doesn't have the keyboard and thus can't&lt;br /&gt;
bequeath it. Therefore, the parent doesn't need to check its STDIN status&lt;br /&gt;
during the DosExecPgm. To summarize, here are the rules:&lt;br /&gt;
&lt;br /&gt;
     Executing Programs&lt;br /&gt;
&lt;br /&gt;
     þ  If the child process is to inherit the STDIN handle, the parent&lt;br /&gt;
        process must not access that handle any further until the child&lt;br /&gt;
        process terminates.&lt;br /&gt;
&lt;br /&gt;
     þ  If the child process is not to inherit the STDIN handle (so that&lt;br /&gt;
        your program can continue to interact), then the child process&lt;br /&gt;
        STDIN must be opened on a file or on the NULL device. Don't rely on&lt;br /&gt;
        the child not to use the handle; the child might DosExecPgm a&lt;br /&gt;
        grandchild that is not so well mannered.&lt;br /&gt;
&lt;br /&gt;
     þ  A process can let only one child at a time inherit its STDIN. If a&lt;br /&gt;
        process is going to run multiple child processes in parallel, only&lt;br /&gt;
        one can inherit STDIN; the others must use alternative STDIN&lt;br /&gt;
        sources.&lt;br /&gt;
&lt;br /&gt;
     þ  All these rules apply to STDINs open on pipes and files as well as&lt;br /&gt;
        to KBD, so your application needn't check the source of STDIN.&lt;br /&gt;
&lt;br /&gt;
     All Processes&lt;br /&gt;
&lt;br /&gt;
     þ  Verify that the STDIN handle points to the keyboard before using&lt;br /&gt;
        KBD, SIGBRK, or SIGCTLC (see below). You must not use these direct&lt;br /&gt;
        device facilities if STDIN is not open on the keyboard.&lt;br /&gt;
&lt;br /&gt;
     þ  Verify that the STDOUT handle points to the screen before using&lt;br /&gt;
        VIO. You must not use direct device facilities if STDOUT is not&lt;br /&gt;
        open on the screen.&lt;br /&gt;
&lt;br /&gt;
     þ  If the process executes any child that inherits its STDIN, it must&lt;br /&gt;
        not terminate itself until that child process terminates. This is&lt;br /&gt;
        because the parent will assume that the termination of the direct&lt;br /&gt;
        child means that the STDIN handle is now available.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
14.2  Ctrl-C and Ctrl-Break Handling&lt;br /&gt;
&lt;br /&gt;
Just when you think that it's safe to go back into the operating system,&lt;br /&gt;
one more device and process tree issue needs to be discussed: the handling&lt;br /&gt;
of Ctrl-C and Ctrl-Break. (Once again, this discussion applies only to&lt;br /&gt;
programs that don't explicitly use the presentation manager facility. Those&lt;br /&gt;
applications that do use the presentation manager have all these issues&lt;br /&gt;
handled for them automatically.) These two events are tied to the keyboard&lt;br /&gt;
hardware, so their routing has a great deal in common with the above&lt;br /&gt;
discussion. The fundamental problem is simple: When the user presses Ctrl-C&lt;br /&gt;
or Ctrl-Break, what's the operating system to do? Clearly, a process or&lt;br /&gt;
processes or perhaps an entire subtree of processes must be killed or&lt;br /&gt;
signaled. But do we kill or signal? And which one(s)?&lt;br /&gt;
     OS/2 defines a convention that allows the processes themselves to&lt;br /&gt;
decide. Consider a type of application--a &amp;quot;command application&amp;quot;--that runs&lt;br /&gt;
in command mode. In command mode, a command application reads a command,&lt;br /&gt;
executes the command, and then typically returns to command mode.&lt;br /&gt;
Furthermore, when the user presses Ctrl-Break, the command application&lt;br /&gt;
doesn't want to terminate but to stop what it's doing and return to command&lt;br /&gt;
mode. This is the style of most interactive applications but not that of&lt;br /&gt;
most noninteractive applications. For example, if the user types MASM to&lt;br /&gt;
CMD.EXE, the CMD.EXE program runs MASM as a child process. CMD.EXE is a&lt;br /&gt;
command application, but MASM is not. The distinction between &amp;quot;command&lt;br /&gt;
application&amp;quot; and &amp;quot;noncommand application&amp;quot; is not made by OS/2 but is merely&lt;br /&gt;
descriptive terminology that is useful in this discussion.&lt;br /&gt;
     The system convention is that Ctrl-C and Ctrl-Break mean &amp;quot;Stop what&lt;br /&gt;
you're doing.&amp;quot; OS/2 generates signals in response to Ctrl-C and Ctrl-Break;&lt;br /&gt;
it never directly kills a process. OS/2 can easily decide which process to&lt;br /&gt;
signal when Ctrl-C or Ctrl-Break is pressed: It signals the lowest command&lt;br /&gt;
process in the process tree in that screen group. At first glance, this may&lt;br /&gt;
not seem easy. How can OS/2 distinguish command processes, and how can it&lt;br /&gt;
determine the &amp;quot;lowest&amp;quot;? The total process tree in a screen group may be&lt;br /&gt;
very complex; some processes in it may have died, creating multiple now-&lt;br /&gt;
independent &amp;quot;treelets.&amp;quot;&lt;br /&gt;
     The process tree may be complex, but the tree of processes using the&lt;br /&gt;
keyboard is simpler because a process can't let multiple children&lt;br /&gt;
simultaneously inherit its STDIN. A process can only inherit the&lt;br /&gt;
keyboard,&lt;br /&gt;
2. Actually, a process should only inherit the&lt;br /&gt;
keyboard. The keyboard device can be opened explicitly, but doing&lt;br /&gt;
so when a process's inherited STDIN doesn't point to the keyboard&lt;br /&gt;
device would be a serious error.&lt;br /&gt;
2 not open it explicitly; so a single path down the tree must&lt;br /&gt;
intersect (or contain) all command processes. This single path can't be&lt;br /&gt;
fragmented because of missing processes due to child death because a&lt;br /&gt;
process that has let a child inherit its STDIN must not terminate until the&lt;br /&gt;
child does. So, all OS/2 needs is to find any command process in the&lt;br /&gt;
command subtree and then look at its descendants for another command&lt;br /&gt;
process and so on. The bottommost process receives the signal.&lt;br /&gt;
3. Actually, OS/2 uses a more efficient algorithm than this; I'm&lt;br /&gt;
merely illustrating that finding the lowest command process is&lt;br /&gt;
not difficult.&lt;br /&gt;
3&lt;br /&gt;
     Figure 14-2 illustrates a possible process tree. The shaded processes&lt;br /&gt;
have inherited handles to the keyboard and screen; those marked with C are&lt;br /&gt;
command processes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ³°°°°°°c°°°°°°°³&lt;br /&gt;
                 ÀÄÄÂÄÄÄÄÄÄÄÄÂÄÄÙ                ÚÄÄÄÄ¿    Processes&lt;br /&gt;
             ÚÄÄÄÄÄÄÙ        ÀÄÄÄÄÄÄ¿            ³°°°°³  = using the&lt;br /&gt;
             ³                      ³            ÀÄÄÄÄÙ    keyboard &lt;br /&gt;
         ÚÄÄÄÁÄÄ¿                ÚÄÄÁÄÄÄ¿&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ³      ³                ³°°c°°°³&lt;br /&gt;
         ÀÄÂÄÄÄÄÙ                ÀÄÂÄÄÂÄÙ&lt;br /&gt;
        ÚÄÄÙ                    ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
    ÚÄÄÄÁÄÄ¿                 ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
    ³      ³                 ³°c°°³    ³    ³&lt;br /&gt;
    ³      ³                 ÀÂÄÄÂÙ    ÀÄÄÄÂÙ&lt;br /&gt;
    ÀÄÂÄÄÂÄÙ               ÚÄÄÙ  ÀÄÄ¿      ÀÄÄ¿&lt;br /&gt;
   ÚÄÄÙ  ÀÄÄ¿           ÚÄÄÁÄ¿    ÚÄÁÄÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
ÚÄÄÁÄ¿    ÚÄÁÄÄ¿        ³°c°°³    ³    ³    ³    ³&lt;br /&gt;
³    ³    ³    ³        ÀÂÄÄÂÙ    ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÙ    ÀÄÄÄÄÙ      ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
                   ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
                   ³    ³    ³°°°°³&lt;br /&gt;
                   ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 14-2.  Ctrl-C routing in a process tree.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This now begs the final question: How can OS/2 tell if an application&lt;br /&gt;
is a command process or not? It can tell because all command&lt;br /&gt;
processes/command applications do something that other processes never do.&lt;br /&gt;
By definition, a command process doesn't want to be summarily killed when&lt;br /&gt;
the user presses Ctrl-C or Ctrl-Break, so all command processes establish&lt;br /&gt;
signal handlers for Ctrl-C and Ctrl-Break. Because all command processes&lt;br /&gt;
intercept Ctrl-C and Ctrl-Break, all we need now is to establish the&lt;br /&gt;
convention that only command processes intercept Ctrl-C and Ctrl-Break.&lt;br /&gt;
This hearkens back to our earlier discussion of checking STDIN before&lt;br /&gt;
directly using the keyboard device. Telling the keyboard device that you&lt;br /&gt;
want the Ctrl-C or Ctrl-Break signals routed to your process is a form of&lt;br /&gt;
I/O with the keyboard device, and it must only be done if your program has&lt;br /&gt;
verified that STDIN points to the keyboard device. Furthermore,&lt;br /&gt;
intercepting Ctrl-C or Ctrl-Break just so that your program can clean up&lt;br /&gt;
during unexpected termination is unnecessary and insufficient. The SIGTERM&lt;br /&gt;
signal or, better, the exitlist mechanism provides this capability and&lt;br /&gt;
covers causes of death other than the keyboard. So all processes that&lt;br /&gt;
intercept Ctrl-C and Ctrl-Break have access to the keyboard, and they want&lt;br /&gt;
to do something other than die when the user presses Ctrl-C or Ctrl-Break.&lt;br /&gt;
They fit the command process definition.&lt;br /&gt;
     Now that we've exhaustively shown how OS/2 finds which process to send&lt;br /&gt;
a Ctrl-C signal, what should the process do when it gets the signal? Obey&lt;br /&gt;
the system convention and stop what it's doing as quickly as is wise. If&lt;br /&gt;
the application isn't working on a command, the application typically&lt;br /&gt;
flushes the keyboard type-ahead buffer and reprompts. If the application is&lt;br /&gt;
working on a command that is implemented in code within the application,&lt;br /&gt;
the application jumps from the signal handler to its command loop or, more&lt;br /&gt;
commonly, sets a flag to terminate the current command prematurely.&lt;br /&gt;
4. Occasionally, terminating a command halfway through could&lt;br /&gt;
leave the user's work trashed. In such a case, finishing the&lt;br /&gt;
command is prudent.&lt;br /&gt;
4&lt;br /&gt;
     Finally, if the application is running a child process, it typically&lt;br /&gt;
stops what it's doing by issuing a DosKill on that child command subtree.&lt;br /&gt;
This, then, is how Ctrl-C can kill a program such as MASM. Ctrl-C is sent&lt;br /&gt;
to MASM's closest ancestor that is a command process,&lt;br /&gt;
5. Often, CMD.EXE is MASM's direct ancestor, but other programs, such&lt;br /&gt;
as a build utility, could have come between CMD.EXE and MASM.&lt;br /&gt;
5 which in turn issues&lt;br /&gt;
a DosKill on MASM's subtree. MASM does any exitlist cleanup that it&lt;br /&gt;
wishes (probably deleting scratch files) and then terminates. When the&lt;br /&gt;
command process that ran MASM, typically CMD.EXE, sees that MASM has&lt;br /&gt;
terminated, it prints ^C on the screen, followed by a new prompt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==15  The File System==&lt;br /&gt;
&lt;br /&gt;
The file system in OS/2 version 1.0 is little changed from that of MS-DOS,&lt;br /&gt;
partially in an effort to preserve compatibility with MS-DOS programs and&lt;br /&gt;
partially due to limitations imposed by the project's schedule. When the&lt;br /&gt;
schedule for an &amp;quot;all singing, all dancing&amp;quot; OS/2 was shown to be too long,&lt;br /&gt;
planned file system improvements were moved to a future release. To explain&lt;br /&gt;
the rationale for postponing something so useful, I'll digress a little.&lt;br /&gt;
     The microcomputer industry developed around the dual concepts of mass&lt;br /&gt;
market software and standards. Because software is mass marketed, you can&lt;br /&gt;
buy some very sophisticated and useful programs for a modest sum of money--&lt;br /&gt;
at least modest in comparison to the development cost, which is often&lt;br /&gt;
measured in millions of dollars. Mass marketing encourages standards&lt;br /&gt;
because users don't want to buy machines, peripherals, and systems that&lt;br /&gt;
don't run these programs. Likewise, the acceptance of the standards&lt;br /&gt;
encourages the development of mass market software because standards make&lt;br /&gt;
it possible for a single binary program to execute correctly on a great&lt;br /&gt;
many machines and thus provide a market big enough to repay the development&lt;br /&gt;
costs of a major application.&lt;br /&gt;
     This synergy, or positive feedback, between standards and mass market&lt;br /&gt;
software affected the process of developing operating systems. At first&lt;br /&gt;
glance, adding new features to an operating system seems straightforward.&lt;br /&gt;
The developers create new features in a new release, and then applications&lt;br /&gt;
are written to use those new features. However, with mass market software,&lt;br /&gt;
it doesn't work that way. Microsoft could indeed release a new version of&lt;br /&gt;
OS/2 with new features (and, in fact, we certainly will do so), but&lt;br /&gt;
initially few new applications will use said new features. This is&lt;br /&gt;
because of the initial limited market penetration of the new release. For&lt;br /&gt;
example, let's assume that at a certain time after the availability of a&lt;br /&gt;
new release, 10 percent of OS/2 users have upgraded. An ISV (Independent&lt;br /&gt;
Software Vendor) is planning its next new product--one it hopes will be a&lt;br /&gt;
bestseller. The ISV must decide whether to use the new feature and&lt;br /&gt;
automatically lock itself out of 90 percent of the potential market or to&lt;br /&gt;
use the common subset of features contained in the earlier OS/2 release and&lt;br /&gt;
be able to run on all machines, including the 10 percent running the OS/2&lt;br /&gt;
upgrade. In general, ISVs won't use a nonvital feature until the great&lt;br /&gt;
majority of existing systems support that feature.&lt;br /&gt;
     The key to introducing new features in an operating system isn't that&lt;br /&gt;
they be available and useful; it's that the release which contains those&lt;br /&gt;
new features sees widespread use as quickly as possible. If this doesn't&lt;br /&gt;
happen, then the new feature pretty much dies stillborn. This is why each&lt;br /&gt;
MS-DOS release that contained major new functionality coincided with a&lt;br /&gt;
release that was required to use new hardware. MS-DOS version 2.0 was&lt;br /&gt;
required for the IBM XT product line; MS-DOS version 3.0 was required for&lt;br /&gt;
the IBM AT product line. And OS/2 is no exception: It wasn't required for a&lt;br /&gt;
new &amp;quot;box,&amp;quot; but it was required to bring out the protect mode machine lying&lt;br /&gt;
fallow inside 80286-based machines. If a new release of a system doesn't&lt;br /&gt;
provide a new feature that makes people want it or need it badly, then&lt;br /&gt;
market penetration will be slow. People will pay the cost and endure the&lt;br /&gt;
hassle of upgrading only if their applications require it, and those&lt;br /&gt;
applications dare require it only if most people have already upgraded.&lt;br /&gt;
     Because of this, the initial release of OS/2 is &amp;quot;magical&amp;quot; in the eyes&lt;br /&gt;
of its developers. It provides a window of opportunity in which to&lt;br /&gt;
introduce new features into the PC operating system standard, a window that&lt;br /&gt;
won't be open quite as wide again for a long time. And this postponed major&lt;br /&gt;
file system enhancements: The file system can be enhanced in a later&lt;br /&gt;
release and benefit existing applications without any change on their part,&lt;br /&gt;
whereas many other OS/2 features needed to be in the first release or they&lt;br /&gt;
might never be available.&lt;br /&gt;
&lt;br /&gt;
===15.1  The OS/2 File System===&lt;br /&gt;
&lt;br /&gt;
Although OS/2 version 1.0 contains little in the way of file system&lt;br /&gt;
improvements, it does contain two that are significant. The first is&lt;br /&gt;
asynchronous I/O. Asynchronous I/O consists of two functions, DosReadAsync&lt;br /&gt;
and DosWriteAsync. These functions are identical to DosRead and DosWrite&lt;br /&gt;
except that they return to the caller immediately, usually before the I/O&lt;br /&gt;
operation has completed. Each takes the handle of a semaphore that is&lt;br /&gt;
cleared when the I/O operation completes. The threads of the calling&lt;br /&gt;
process can use this semaphore to poll for operation complete, or they can&lt;br /&gt;
wait for the operation to complete. The DosMuxSemWait call is particularly&lt;br /&gt;
useful in this regard because it allows a process to wait for several&lt;br /&gt;
semaphore events, which can be asynchronous I/O events, IPC events, and&lt;br /&gt;
timer events, intermingled as the programmer wishes.&lt;br /&gt;
     The second file system feature is extended partitioning; it supports&lt;br /&gt;
dividing large physical disks into multiple sections, several of which may&lt;br /&gt;
contain FAT file systems. In effect, it causes OS/2 to treat a large hard&lt;br /&gt;
disk as two or more smaller ones, each of which meets the file system's&lt;br /&gt;
size limits. It's widely believed that MS-DOS is limited to disks less than&lt;br /&gt;
32 MB in size. This isn't strictly true. The limitation is that a disk can&lt;br /&gt;
have no more than 65,535 sectors; the standard sector size is 512 bytes,&lt;br /&gt;
which gives the 32 MB value. Furthermore, each disk is limited to 32,768&lt;br /&gt;
clusters. A sector is the unit of disk storage; disks can read and write&lt;br /&gt;
only integral sectors. A sector's size is established when the disk is&lt;br /&gt;
formatted. A cluster is the unit of disk space allocation for files and&lt;br /&gt;
directories. It may be as small as one sector, or it may be four sectors,&lt;br /&gt;
eight sectors, or some other size. Because the MS-DOS file system supports&lt;br /&gt;
a maximum of 65 KB sectors but only 32 KB clusters, a 32 MB disk must be&lt;br /&gt;
allocated in two-sector (or bigger) clusters. It's possible to write a&lt;br /&gt;
device driver that uses a sector size that is a multiple of 512 bytes,&lt;br /&gt;
which gets around the 65 KB sector restriction and allows the use of a disk&lt;br /&gt;
greater than 32 MB. This trick works for MS-DOS and for OS/2, but it's not&lt;br /&gt;
optimal because it doesn't do anything to increase the maximum number of&lt;br /&gt;
allocation clusters from the existing 32 KB value,&lt;br /&gt;
1. The FAT file system can deal with a maximum of 32 KB allocation&lt;br /&gt;
units, or clusters. No matter what the size of the disk, all files&lt;br /&gt;
must consume disk space in increments of no smaller than 1/32Kth of&lt;br /&gt;
the total disk size. This means that a 60 MB disk, using 1024 byte&lt;br /&gt;
sectors, allocates space in 2048-byte increments.&lt;br /&gt;
1 which means that&lt;br /&gt;
because many disk files are small a lot of space is wasted due to internal&lt;br /&gt;
fragmentation.&lt;br /&gt;
     The OS/2 version 1.0 extended partitioning feature provides an interim&lt;br /&gt;
solution that is not quite as convenient as large sectors but that reduces&lt;br /&gt;
the wastage from internal fragmentation: It allows more than one disk&lt;br /&gt;
partition to contain a FAT file system. Multipartitioned disks are possible&lt;br /&gt;
under MS-DOS, but only one partition can be an MS-DOS (that is, FAT) file&lt;br /&gt;
system. This restriction has been relaxed in OS/2 so that, for example, a&lt;br /&gt;
60 MB disk can be partitioned into two separate logical disks (for example,&lt;br /&gt;
C and D), each 30 MB.&lt;br /&gt;
&lt;br /&gt;
===15.2  Media Volume Management===&lt;br /&gt;
&lt;br /&gt;
The multitasking capability of OS/2 necessitated major file system&lt;br /&gt;
enhancements in the area of volume management. A disk volume is the name&lt;br /&gt;
given to the file system and files on a particular disk medium. A disk&lt;br /&gt;
drive that contains a fixed medium always contains the same volume, but a&lt;br /&gt;
disk drive from which the media (such as floppy disks) can be removed will&lt;br /&gt;
contain whatever disk--whatever volume--the user has in it at the time.&lt;br /&gt;
That volumes can change becomes a problem in a multitasking environment.&lt;br /&gt;
For example, suppose a user is using a word processor to edit a file on a&lt;br /&gt;
floppy disk in drive A. The editor has opened the file and is keeping it&lt;br /&gt;
open for the duration of the edit. Without closing the file or terminating&lt;br /&gt;
the editor, the user can switch to a screen group in which a spreadsheet&lt;br /&gt;
program is running. The user might then need to insert a different disk&lt;br /&gt;
into drive A--one that contains data needed by the spreadsheet. If the user&lt;br /&gt;
then switches back to the word processor without remembering to change the&lt;br /&gt;
floppy disk, disaster will strike. Pressing the Page Down key will cause&lt;br /&gt;
the editor to try to read another sector from its already open disk file.&lt;br /&gt;
The operating system knows--because of FAT information stored in RAM&lt;br /&gt;
buffers_ that the next sector in the text file is sector N, and it will&lt;br /&gt;
issue a read to sector N on the wrong medium--the spreadsheet floppy disk--&lt;br /&gt;
and return that to the word processor program as the next sector of the&lt;br /&gt;
text file. And, at that, the user is getting off lightly; he or she might&lt;br /&gt;
just as easily have given the word processor a command that caused it to&lt;br /&gt;
write a sector to the disk, which would do double damage. A file on the&lt;br /&gt;
spreadsheet floppy would be destroyed by the &amp;quot;random&amp;quot; write, and the text&lt;br /&gt;
file would be corrupted as well because it's missing a sector write that it&lt;br /&gt;
should have received.&lt;br /&gt;
     We can't solve this problem by admonishing the user to be careful;&lt;br /&gt;
many programs read from and write to disk without direct user intervention.&lt;br /&gt;
For example, the word processor might save work in progress to disk every&lt;br /&gt;
two minutes. If this time interval elapses while the user is still working&lt;br /&gt;
with the spreadsheet program on the spreadsheet floppy disk, our&lt;br /&gt;
hypothetical &amp;quot;flawless&amp;quot; user is still S.O.L.&lt;br /&gt;
2. Severely out of luck.&lt;br /&gt;
2&lt;br /&gt;
     OS/2 resolves these problems by recognizing that when an application&lt;br /&gt;
does I/O to an open file the I/O is not really aimed at drive A; it's aimed&lt;br /&gt;
at a particular floppy disk volume--the one containing the open file. Each&lt;br /&gt;
disk volume, removable or not, has a volume name stored in its root&lt;br /&gt;
directory and a unique 32-bit volume identifier stored in its boot sector.&lt;br /&gt;
Figure 15-1 illustrates the two volume names--one for computer use and one&lt;br /&gt;
for human use. Each file handle is associated with a particular 32-bit&lt;br /&gt;
volume ID. When an I/O request is made for a file handle, OS/2 checks to&lt;br /&gt;
see if the proper volume is in the drive by comparing the 32-bit value of&lt;br /&gt;
the request with that of the medium currently spinning. If they match, the&lt;br /&gt;
operation completes. If the mounted volume is different from the requested&lt;br /&gt;
volume, OS/2 uses the hard error daemon mechanism to prompt the user to&lt;br /&gt;
insert the correct volume in the drive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                            VOLUME LABELS&lt;br /&gt;
Sector                                                          Sector&lt;br /&gt;
  0                                                               N&lt;br /&gt;
  ÚÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³     ³             ³      ³                                    ³&lt;br /&gt;
  ³     ³             ³      ³                                    ³&lt;br /&gt;
  ÀÄÄ�ÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄ�ÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ÀÄ 32-bit volume ID  ÀÄÄÄÄÄÄÄ volume name in&lt;br /&gt;
        in boot sector             home directory&lt;br /&gt;
&lt;br /&gt;
Figure 15-1.  Volume ID and volume name location.&lt;br /&gt;
&lt;br /&gt;
     Three problems must be overcome to make this scheme practical. First,&lt;br /&gt;
checking the volume ID of the medium must be fast. You can't afford to read&lt;br /&gt;
the boot sector each time you do an I/O operation if doing so halves the&lt;br /&gt;
speed of disk I/O. Second, you need assurance that volume IDs are unique.&lt;br /&gt;
Third, you need a plan to deal with a volume that doesn't have a volume ID.&lt;br /&gt;
     Keeping down the cost of volume verification is easy if you know when&lt;br /&gt;
a volume is changed; obviously, the ID is read from the boot sector only&lt;br /&gt;
when a medium has been changed. But how can OS/2 tell that a media change&lt;br /&gt;
has occurred? It can't; that's a device driver issue.&lt;br /&gt;
     For starters, if the device contains a nonremovable medium, rechecking&lt;br /&gt;
its volume ID is never necessary. The device driver understands this, and&lt;br /&gt;
when it is asked the status of the medium, it responds, &amp;quot;Unchanged.&amp;quot; Some&lt;br /&gt;
removable media drives have a flag bit that warns the driver that the door&lt;br /&gt;
has been opened. In this case, when asked, the device driver tells OS/2&lt;br /&gt;
that the medium is &amp;quot;uncertain.&amp;quot; The driver doesn't know for sure that it&lt;br /&gt;
was really changed, but it may have been; so OS/2 rechecks the volume ID.&lt;br /&gt;
Rechecking the volume ID is more difficult when a removable media device&lt;br /&gt;
has no such indicator.&lt;br /&gt;
     In this case, the author of the device driver uses device-specific&lt;br /&gt;
knowledge to decide on a minimum possible time to effect a media change. If&lt;br /&gt;
the device is ready, yet less than the minimum possible time has elapsed&lt;br /&gt;
since the last operation, the driver knows that the same medium must be in&lt;br /&gt;
the drive. If more than the minimum possible time has elapsed, the driver&lt;br /&gt;
returns &amp;quot;medium uncertain,&amp;quot; and OS/2 rechecks the volume label. This time&lt;br /&gt;
interval is typically 2 seconds for floppy disk drives, so effectively an&lt;br /&gt;
extra disk read is done after every idle period; for any given episode of&lt;br /&gt;
disk I/O, however, no extra reads are needed.&lt;br /&gt;
     Ensuring that a volume ID is unique is another problem. Simply&lt;br /&gt;
lecturing the user on the wisdom of unique IDs is inadequate; the user will&lt;br /&gt;
still label three disks &amp;quot;temp&amp;quot; or number them all as &amp;quot;10.&amp;quot; And even the&lt;br /&gt;
hypothetical perfect user might borrow from a neighbor a disk whose name is&lt;br /&gt;
the same as one the user already owns. OS/2 deals with this problem by&lt;br /&gt;
using a 32-bit randomized value for disk volume IDs. When a disk is&lt;br /&gt;
formatted, the user enters a supposedly unique name. This name is&lt;br /&gt;
checksummed, and the result, combined with the number of seconds between&lt;br /&gt;
the present and 1980, is used to seed a random number generator. This&lt;br /&gt;
generator returns a 32-bit volume ID. Although accidentally duplicating a&lt;br /&gt;
volume ID is obviously possible, the four billion possible codes make it&lt;br /&gt;
quite unlikely.&lt;br /&gt;
     The name the user enters is used only to prompt the user to insert the&lt;br /&gt;
volume when necessary, so it need not be truly unique for the volume&lt;br /&gt;
management system to work. If the user names several disks WORK, OS/2 still&lt;br /&gt;
sees them as independent volumes because their volume IDs are different. If&lt;br /&gt;
the user inserts the wrong WORK disk in response to a prompt, OS/2&lt;br /&gt;
recognizes it as the wrong disk and reissues the &amp;quot;Insert disk WORK&amp;quot; prompt.&lt;br /&gt;
After trying each WORK volume in turn, the user will probably decide to&lt;br /&gt;
relabel the disks!&lt;br /&gt;
     The thorniest problem arises from unlabeled disks--disks formatted&lt;br /&gt;
with MS-DOS. Forcing the user to label these disks is unacceptable, as is&lt;br /&gt;
having OS/2 automatically label them with volume IDs: The disk may be read-&lt;br /&gt;
only, perhaps permanently so. Even if the disk is not read-only, the&lt;br /&gt;
problem of low density and high density raises its ugly head. Low-density&lt;br /&gt;
disks can be read in a high-density drive, but writes made to a low-density&lt;br /&gt;
disk from a high-density drive can only be read on high-density drives. If&lt;br /&gt;
a low-density disk is placed in a high-density drive and then labeled by&lt;br /&gt;
OS/2, its boot sector is no longer readable when the disk is placed in a&lt;br /&gt;
low-density drive.&lt;br /&gt;
     For volumes without a proper volume ID, OS/2 attempts to create a&lt;br /&gt;
unique substitute volume ID by checksumming parts of the volume's root&lt;br /&gt;
directory and its FAT table. OS/2 uses the existing volume name if one&lt;br /&gt;
exists; if there is no volume name, OS/2 attempts to describe the disk.&lt;br /&gt;
None of these techniques is foolproof, and they require extra disk&lt;br /&gt;
operations every time the medium is identified. Therefore, software&lt;br /&gt;
distributors and users should make every effort to label disks that OS/2&lt;br /&gt;
systems are to use. OS/2 labels are backward compatible with MS-DOS version&lt;br /&gt;
3.x labels.&lt;br /&gt;
     The OS/2 DISKCOPY command makes a byte-by-byte verbatim copy of a&lt;br /&gt;
floppy disk, except that the duplicate disk has a different volume ID value&lt;br /&gt;
in the boot sector (the volume label name is not changed). OS/2 users can't&lt;br /&gt;
tell this, however, because the DISKCOMP utility lies, and if two disks are&lt;br /&gt;
identical in every byte except for the volume ID, it reports that the disks&lt;br /&gt;
are identical. However, if the user uses DISKCOPY to duplicate the disk&lt;br /&gt;
under OS/2 and then compares the two with DISKCOMP under MS-DOS 3.x, a&lt;br /&gt;
difference is reported.&lt;br /&gt;
     Our discussion so far has centered on file reads and writes to an open&lt;br /&gt;
handle. Reads and writes are volume-oriented operations because they're&lt;br /&gt;
aimed at the volume on which the file resides. DosOpens, on the other hand,&lt;br /&gt;
are drive oriented because they search the default or specified drive for&lt;br /&gt;
the file in question (or create it) regardless of the volume in the drive.&lt;br /&gt;
All handle operations are volume oriented, and all name-based calls are&lt;br /&gt;
drive oriented. Currently, you cannot specify that a given file is to be&lt;br /&gt;
opened on or created on a specific volume. To ensure that a scratch or&lt;br /&gt;
output file is created on a certain volume, arrange to have a file open on&lt;br /&gt;
that volume and issue a write to that file immediately before doing the&lt;br /&gt;
file open. The write operation followed by a DosBufReset will ensure that&lt;br /&gt;
the particular medium is in the drive at that time.&lt;br /&gt;
&lt;br /&gt;
===15.3  I/O Efficiency===&lt;br /&gt;
&lt;br /&gt;
OS/2 provides full blocking and deblocking services for all disk I/O&lt;br /&gt;
requests. A program can read or write any number of bytes, and OS/2 will&lt;br /&gt;
read the proper sectors into internal buffers so that only the specified&lt;br /&gt;
bytes are affected. Naturally, every DosRead or DosWrite call takes time to&lt;br /&gt;
execute, so if your program makes few I/O calls, each for large amounts of&lt;br /&gt;
data, it will execute faster.&lt;br /&gt;
     I/O performance can be further improved by making sector aligned&lt;br /&gt;
calls, that is, by requesting a transfer of an integral multiple of 512&lt;br /&gt;
bytes to or from a file seek position that is itself a multiple of 512.&lt;br /&gt;
OS/2 reads and writes entire disk sectors directly from and to the device&lt;br /&gt;
hardware without an intermediate copy step through system buffers. Because&lt;br /&gt;
the file system keeps logically adjacent sectors physically adjacent on the&lt;br /&gt;
disk, disk seek times and rotational latency are such that one can read or&lt;br /&gt;
write four sectors of data (2048 bytes) in essentially the same time needed&lt;br /&gt;
to read or write one sector (512 bytes).&lt;br /&gt;
     Even if the length or the file position of the request isn't a&lt;br /&gt;
multiple of 512, OS/2 performs the initial fraction of the request via its&lt;br /&gt;
buffers, directly transfers any whole sectors out of the middle of the&lt;br /&gt;
request, and uses the buffers for the fractional remainder. Even if your&lt;br /&gt;
requests aren't sector aligned, making them as large as feasible is&lt;br /&gt;
beneficial.&lt;br /&gt;
     To summarize, I/O is most efficient when requests are large and sector&lt;br /&gt;
aligned. Even misaligned requests can be almost optimally serviced if they&lt;br /&gt;
are large. Programs that cannot naturally make aligned requests and that&lt;br /&gt;
are not I/O intensive should take advantage of the blocking and deblocking&lt;br /&gt;
services that OS/2 provides. Likewise, programs that need to make large,&lt;br /&gt;
unaligned requests should use OS/2's blocking management. Programs that&lt;br /&gt;
need to make frequent, small, nonaligned requests will perform best if they&lt;br /&gt;
read blocks of sectors into internal buffers and deblock the data&lt;br /&gt;
themselves, avoiding the overhead of frequent DosRead or DosWrite calls.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==16  Device Monitors, Data Integrity, and Timer Services==&lt;br /&gt;
&lt;br /&gt;
In discussing the design goals of OS/2, I mentioned continuing to support&lt;br /&gt;
the kinds of functionality found in MS-DOS, even when that functionality&lt;br /&gt;
was obtained by going around the operating system. A good example of such&lt;br /&gt;
functionality is device data manipulation, a technique that usually&lt;br /&gt;
involves hooking interrupt vectors and that is used by many application&lt;br /&gt;
programs. For example, pop-up programs such as SideKick have become very&lt;br /&gt;
popular. These programs get into memory via the terminate and stay resident&lt;br /&gt;
mechanism and then edit the keyboard interrupt vector to point to their&lt;br /&gt;
code. These programs examine each keystroke to see if it is their special&lt;br /&gt;
activate key. If not, they transfer control to the original interrupt&lt;br /&gt;
handler. If the keystroke is their special activate key, they retain&lt;br /&gt;
control of the CPU and display, or &amp;quot;pop up,&amp;quot; a message or a menu on the&lt;br /&gt;
screen. Other programs hook the keyboard vector to provide spell checking&lt;br /&gt;
or keyboard macro expansion. Some programs also hook the BIOS entry vector&lt;br /&gt;
that commands the printer, either to substitute alternate printer driver&lt;br /&gt;
code or to manipulate the data sent to the printer. Programs that turn a&lt;br /&gt;
spreadsheet's output sideways are an example of this.&lt;br /&gt;
     In general, these programs edit, or hook, the interrupt vectors that&lt;br /&gt;
receive device interrupts and communicate with device driver routines in&lt;br /&gt;
the ROM BIOS. The functions provided by such programs and their evident&lt;br /&gt;
popularity among users demonstrate a need for programs to be able to&lt;br /&gt;
monitor and/or modify device data streams. The OS/2 mechanism that does&lt;br /&gt;
this is called a device monitor.&lt;br /&gt;
&lt;br /&gt;
===16.1  Device Monitors===&lt;br /&gt;
&lt;br /&gt;
The design of device monitors had to meet the general requirements and&lt;br /&gt;
religion of OS/2. Specifically, the MS-DOS technique of letting&lt;br /&gt;
applications receive interrupts by editing the interrupt vectors could not&lt;br /&gt;
be allowed because doing so would destroy the system's ability to provide a&lt;br /&gt;
stable environment. Furthermore, unlike MS-DOS, OS/2 doesn't use the ROM&lt;br /&gt;
BIOS as a form of device driver, so hooking the BIOS communication vectors&lt;br /&gt;
would not provide access to the device data stream. In addition, allowing&lt;br /&gt;
an application to arbitrarily interfere with a device driver's operation is&lt;br /&gt;
contrary to OS/2 design principles; the device driver is the architectural&lt;br /&gt;
embodiment of knowledge about the device, and it must be involved in and&lt;br /&gt;
&amp;quot;aware&amp;quot; of any external manipulation of the data stream. The result is an&lt;br /&gt;
OS/2 device monitor mechanism that allows processes, running in their&lt;br /&gt;
normal ring 3 state, to monitor and edit device data streams with the prior&lt;br /&gt;
permission and knowledge of the appropriate device driver.&lt;br /&gt;
     Specifically, a process registers itself as a device monitor by&lt;br /&gt;
calling the appropriate device driver via a DosMonReg call.&lt;br /&gt;
1. Which is a dynlink package that eventually calls the device&lt;br /&gt;
driver via a DosDevIOCtl call.&lt;br /&gt;
1 The process&lt;br /&gt;
also provides two data buffers, one for incoming monitor data and another&lt;br /&gt;
for outgoing monitor data. Processes can easily call OS/2, but OS/2 has no&lt;br /&gt;
way to call processes.&lt;br /&gt;
2. Signals are a partial exception to this, but signals have&lt;br /&gt;
limitations, as discussed earlier.&lt;br /&gt;
2 OS/2 gets around this by inverting the normal&lt;br /&gt;
sense of a call and return sequence. When OS/2 needs to &amp;quot;call&amp;quot; a process,&lt;br /&gt;
it requires that process to call OS/2 beforehand with one of its threads.&lt;br /&gt;
OS/2 holds this thread captive until the callback event takes place. OS/2&lt;br /&gt;
then accomplishes a call to the process by releasing the thread so that it&lt;br /&gt;
returns from the holding system call and resumes execution within the&lt;br /&gt;
process. When the process is ready to &amp;quot;return&amp;quot; to OS/2, it recalls the&lt;br /&gt;
holding entry point (see Figure 16-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      Process calls OS/2     |        OS/2 &amp;quot;calls&amp;quot; Process&lt;br /&gt;
                             | &lt;br /&gt;
Process  call                |  Process  call           FCN    call&lt;br /&gt;
    ÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄ    |      ÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
          �       �          |            �       �             �&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
  Ä Ä Ä Ä Å Ä Ä Ä Å Ä Ä Ä    |    Ä Ä Ä Ä Å Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Å Ä Ä&lt;br /&gt;
 OS/2     ³       ³          |   OS/2     ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          �       �          |            �       �             �&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³  FCN  ³          |            ³       ³             ³&lt;br /&gt;
          ÀÄÄÄÄÄÄÄÙ          |            ÀÄÄ&amp;lt; &amp;lt;ÄÄÙ             ÀÄÄÄ&amp;lt;&lt;br /&gt;
                Return       |                &amp;gt; &amp;gt; Return             &amp;gt;&lt;br /&gt;
&lt;br /&gt;
Figure 16-1.  &amp;quot;Calling&amp;quot; a process from OS/2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 uses this technique for monitors as well. A monitoring process is&lt;br /&gt;
required to call the OS/2 entry point directly after registering itself as&lt;br /&gt;
a device monitor. OS/2 notifies the monitor process of the presence of data&lt;br /&gt;
in the incoming buffer by allowing this thread to return to the process.&lt;br /&gt;
Figure 16-2 illustrates a device with two monitoring processes, X and Y.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿              ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³         Monitor          ³              ³         Monitor          ³&lt;br /&gt;
³        Process X         ³              ³        Process Y         ³&lt;br /&gt;
³                          ³              ³                          ³&lt;br /&gt;
³ DosMonRead   DosMonWrite ³              ³ DosMonRead   DosMonWrite ³&lt;br /&gt;
ÀÄÄÄÄÄ�ÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ              ÀÄÄÄÄÄ�ÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ&lt;br /&gt;
      ÀÄ¿          ÀÄÄÄÄÄÄÄÄÄÄÄ¿      ÚÄÄÄÄÄÄÄÄÄÙ            ³&lt;br /&gt;
        ³                      ³      ³                      ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄ¿&lt;br /&gt;
³       ³                      ³      ³                      ³       ³&lt;br /&gt;
³   ÚÄÄÄÁÄÄÄÄ¿                Ú�ÄÄÄÄÄÄÁ¿                ÚÄÄÄÄ�ÄÄÄ¿   ³&lt;br /&gt;
³   ³ Buffer ³                ³ Buffer ³                ³ Buffer ³   ³&lt;br /&gt;
³   ³   1    ³                ³   2    ³                ³   3    ³   ³&lt;br /&gt;
³   ÀÄÄÄ�ÄÄÄÄÙ                ÀÄÄÄÄÄÄÄÄÙ                ÀÄÄÄÄÂÄÄÄÙ   ³&lt;br /&gt;
³  ÚÄÄÄÄÙ                        OS/2                        ÀÄÄÄÄ¿  ³&lt;br /&gt;
ÀÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÙ&lt;br /&gt;
   ³ Data in                                             Data out ³&lt;br /&gt;
   ³                                                              ³&lt;br /&gt;
ÚÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄ¿&lt;br /&gt;
³  ³                        Device driver                         �  ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 16-2.  Device monitors.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     But we need to discuss a few additional details. First, because OS/2&lt;br /&gt;
strives to make processes see a consistent environment regardless of the&lt;br /&gt;
presence of other processes, each device can have as many monitors as the&lt;br /&gt;
device driver allows. OS/2 connects multiple device monitors into a chain&lt;br /&gt;
so that the device data stream is passed through the first monitor in the&lt;br /&gt;
chain, then through the second monitor, and so on. When a process registers&lt;br /&gt;
itself as a monitor, it specifies whether it wants to be first in the chain&lt;br /&gt;
or last in the chain; some applications are sensitive to this. The first&lt;br /&gt;
monitor to register itself as first is truly first; the next monitor to ask&lt;br /&gt;
for first actually becomes second, and so forth. The same algorithm applies&lt;br /&gt;
to monitors that want to be last: The first to so request becomes the last,&lt;br /&gt;
the second to request last becomes next to last, and so forth.&lt;br /&gt;
     The actual format of the monitor data stream is device specific; the&lt;br /&gt;
device driver decrees the format. Some device drivers have special rules&lt;br /&gt;
and requirements. For example, the keyboard device driver allows monitoring&lt;br /&gt;
processes to insert the &amp;quot;screen switch&amp;quot; key sequence into the data stream,&lt;br /&gt;
whereupon it is recognized as if the user had typed it at the physical&lt;br /&gt;
keyboard. But the device driver will not pass such sequences that really&lt;br /&gt;
were typed through the monitor chain; they are directly obeyed instead.&lt;br /&gt;
     This approach prevents an amok keyboard monitor from effectively&lt;br /&gt;
crashing the system by intercepting and consuming all attempts by the user&lt;br /&gt;
to switch screen groups. The screen device driver does not allow device&lt;br /&gt;
monitors, not because the performance impact would be too big (as it, in&lt;br /&gt;
fact, would be) but because the VIO and presentation manager dynlink&lt;br /&gt;
packages totally circumvent the screen device driver so that it never sees&lt;br /&gt;
any screen data being written.&lt;br /&gt;
     The DosMonRead call holds the device's thread until incoming data is&lt;br /&gt;
available. The DosMonWrite call returns the CPU to the process as soon as&lt;br /&gt;
it is able. The same thread that calls DosMonRead need not be the one to&lt;br /&gt;
call DosMonWrite (see below).&lt;br /&gt;
     Because monitors are an important component of OS/2, an application&lt;br /&gt;
must be very careful to use them properly; therefore, some caveats are in&lt;br /&gt;
order. First, monitors are inserted into the device data chain, with&lt;br /&gt;
obvious effects on the data throughput rate of the device. Each time the&lt;br /&gt;
user presses a key, for example, a packet must pass through every monitor&lt;br /&gt;
in the keyboard chain before the application can read the key and obey or&lt;br /&gt;
echo it. Clearly, any sluggishness on the part of a monitor or the presence&lt;br /&gt;
of too many monitors in a chain will adversely affect system response. The&lt;br /&gt;
thread involved in reading, processing, and writing monitor data should be&lt;br /&gt;
set at a high priority. We recommend the lowest of the force run priority&lt;br /&gt;
categories. Furthermore, the monitor component of a monitoring application&lt;br /&gt;
must contain no critical sections or other events that could slow or&lt;br /&gt;
suspend its operation. In addition, if a monitor data stream will be&lt;br /&gt;
extensively processed, a normal-priority thread must be used to handle that&lt;br /&gt;
processing so that the high-priority thread can continue to transfer&lt;br /&gt;
monitor data in and out without impediment. For example, an auxiliary&lt;br /&gt;
thread and buffer must be used if a keyboard monitor is to write all&lt;br /&gt;
keystrokes to a disk buffer.&lt;br /&gt;
     Finally, if a monitor process terminates abnormally although OS/2&lt;br /&gt;
properly unlinks it from the monitor chain, the data in the process's&lt;br /&gt;
monitor buffers is lost. Clearly, losing an unspecified amount of data&lt;br /&gt;
without warning from the keyboard data stream or perhaps from printer&lt;br /&gt;
output will upset the user no little amount. Monitoring processes must be&lt;br /&gt;
written carefully so that they minimize this risk.&lt;br /&gt;
     The device monitor feature threatens OS/2's fundamental architectural&lt;br /&gt;
principles more than any other. Thus, its presence in the system testifies&lt;br /&gt;
to its importance. Specifically, device monitors violate the design&lt;br /&gt;
principle of minimizing interference between processes, a.k.a.&lt;br /&gt;
encapsulation. Clearly, a process that is monitoring a device's data stream&lt;br /&gt;
can affect the output of or input to a great many processes other than&lt;br /&gt;
itself. This is sometimes called a feature, not a bug. For example, the&lt;br /&gt;
printer spooler uses monitors to intercept output aimed at the printer,&lt;br /&gt;
storing it on disk, and to feed data from those disk files to the actual&lt;br /&gt;
printer device. Clearly, spooling printer output interferes with another&lt;br /&gt;
process, but the interference is valuable. Designers of monitoring&lt;br /&gt;
applications must ensure that their applications damage neither the&lt;br /&gt;
system's performance nor its stability.&lt;br /&gt;
&lt;br /&gt;
===16.2  Data Integrity===&lt;br /&gt;
&lt;br /&gt;
I've discussed data integrity in a multitasking environment several times.&lt;br /&gt;
This section does not review that material in detail but brings together&lt;br /&gt;
all the elements and introduces a few related system facilities.&lt;br /&gt;
     The first problem in a multitasking system is that multiple processes,&lt;br /&gt;
or multiple threads within a process, may try to simultaneously manipulate&lt;br /&gt;
the same resource--a file, a device, a data structure in memory, or even a&lt;br /&gt;
single byte of memory. When the manipulation of a resource must be&lt;br /&gt;
serialized to work correctly, that manipulation is called a critical&lt;br /&gt;
section. This term refers to the act of manipulating the resource, but not&lt;br /&gt;
particularly to the code that does so. Clearly, if any of four subroutines&lt;br /&gt;
can manipulate a particular resource, entering any of the four is entering&lt;br /&gt;
the critical section.&lt;br /&gt;
     The problem is more pervasive than a programmer unfamiliar with the&lt;br /&gt;
issue might assume. For example, even the simple act of testing a word to&lt;br /&gt;
see if it holds the value 4 and incrementing it if it doesn't is a critical&lt;br /&gt;
section. If only one thread in one process can access this word, then the&lt;br /&gt;
critical section is serialized. But if more than one thread can access the&lt;br /&gt;
word, then more than one thread could be in the critical section at the&lt;br /&gt;
same time, with disastrous results. Specifically, consider the assembly&lt;br /&gt;
language sequence shown in Listing 16-1. It looks simple enough: Test to&lt;br /&gt;
see if COUNT holds 4; if it doesn't, increment it; if it does, jump to the&lt;br /&gt;
label COMPLETE. Listing 16-2 shows what might go wrong in a multithreaded&lt;br /&gt;
environment: Thread A checks the value to see if it's 4, but it's 3. Right&lt;br /&gt;
after the compare instruction, a context switch takes place, and thread B&lt;br /&gt;
is executed. Thread B also performs the compare, sees the value as 3, and&lt;br /&gt;
increments it. Later, thread A resumes execution, after the compare&lt;br /&gt;
instruction, at a location where it believes the COUNT value to be 3; so it&lt;br /&gt;
also increments the value of COUNT. The value is now 5 and will continue to&lt;br /&gt;
be incremented way past the value of 4 that was supposed to be its upper&lt;br /&gt;
limit. The label COMPLETE may never be reached.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
COUNT        DW      0               ; Event counter&lt;br /&gt;
&lt;br /&gt;
        .&lt;br /&gt;
        .&lt;br /&gt;
        CMP     COUNT,4        ; is this the 4th?&lt;br /&gt;
        JE      COMPLETE       ; yes, we're done&lt;br /&gt;
        INC     COUNT          ; count event&lt;br /&gt;
        .&lt;br /&gt;
        .&lt;br /&gt;
&lt;br /&gt;
Listing 16-1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        Thread A                                     Thread B&lt;br /&gt;
&lt;br /&gt;
             .&lt;br /&gt;
             .&lt;br /&gt;
             CMP     COUNT,4  [count is now 3]&lt;br /&gt;
             -------------------context switch---&amp;gt;&lt;br /&gt;
                                    CMP     COUNT,4 [count is 3]&lt;br /&gt;
                                    JE      COMPLETE&lt;br /&gt;
                                    INC     COUNT   [count is 4]&lt;br /&gt;
                                              .&lt;br /&gt;
                                              .&lt;br /&gt;
             &amp;lt;-----------context switch--------&lt;br /&gt;
             JE      COMPLETE [jmp not taken]&lt;br /&gt;
             INC     COUNT    [count is now 5]&lt;br /&gt;
&lt;br /&gt;
Listing 16-2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     I apologize for again lecturing on this topic, but such problems are&lt;br /&gt;
very nonobvious, rarely turn up in testing, are nearly impossible to find&lt;br /&gt;
in the field, and the very possibility of their existence is new with OS/2.&lt;br /&gt;
Thus, &amp;quot;too much is not enough,&amp;quot; caveat-wise. Now that we've reviewed the&lt;br /&gt;
problems, let's look at the solutions.&lt;br /&gt;
     A programmer inexperienced with a multitasking environment might&lt;br /&gt;
protest that this scenario is unlikely, and indeed it is. Maybe the chances&lt;br /&gt;
are only 1 in 1 million that it would happen. But because a microprocessor&lt;br /&gt;
executes 1 million instructions a second, it might not be all that long&lt;br /&gt;
before the 1-in-1-million unlucky chance comes true. Furthermore, an&lt;br /&gt;
incorrect program normally has multiple unprotected critical sections, many&lt;br /&gt;
of which are larger than the 2-instruction window in our simple example.&lt;br /&gt;
     The program must identify and protect all critical sections; a program&lt;br /&gt;
that fails to do so will randomly fail. You can't take solace in there&lt;br /&gt;
being only one CPU and assuming that OS/2 probably won't context switch in&lt;br /&gt;
the critical section. OS/2 can context switch at any time, and because&lt;br /&gt;
context switching can be triggered by unpredictable external events, such&lt;br /&gt;
as serial port I/O and rotational latency on a disk, no amount of testing&lt;br /&gt;
can prove that an unprotected critical section is safe. In reality, a test&lt;br /&gt;
environment is often relatively simple; context switching tends to occur at&lt;br /&gt;
consistent intervals, which means that such problems tend not to turn up&lt;br /&gt;
during program test. Instead, they turn up in the real world, and give your&lt;br /&gt;
program a reputation for instability.&lt;br /&gt;
     Naturally, testing has its place, but the only sure way to deal with&lt;br /&gt;
critical sections is to examine your code carefully while assuming that all&lt;br /&gt;
threads in the system are executing simultaneously.&lt;br /&gt;
3. This is more than a Gedankenexperiment. Multiple&lt;br /&gt;
processor machines will be built, and when they are, OS/2 will execute&lt;br /&gt;
multiple threads, even within one process, truly simultaneously.&lt;br /&gt;
3 Furthermore, when&lt;br /&gt;
examining a code sequence, always assume that the CPU will reschedule in&lt;br /&gt;
the worst way. If there is any possible window, reality will find it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.1  Semaphores&lt;br /&gt;
The traditional solution for protecting critical sections is the semaphore.&lt;br /&gt;
The two OS/2 semaphores--RAM and system--each have advantages, and the&lt;br /&gt;
operation of each is guaranteed to be completely immune to critical section&lt;br /&gt;
problems. In the jargon, their operation is guaranteed atomic. Whenever a&lt;br /&gt;
thread is going to manipulate a critical resource, it first claims the&lt;br /&gt;
semaphore that protects the resource. Only after it controls the semaphore&lt;br /&gt;
does it look at the resource because the resource's values may have changed&lt;br /&gt;
between the time the semaphore was requested and the time it was granted.&lt;br /&gt;
After the thread completes its manipulation of the resource, it releases&lt;br /&gt;
the semaphore.&lt;br /&gt;
     The semaphore mechanism protects well against all cooperating&lt;br /&gt;
4. Obviously, if some thread refuses to claim the semaphore,&lt;br /&gt;
nothing can be done.&lt;br /&gt;
4&lt;br /&gt;
threads, whether they belong to the same process or to different processes.&lt;br /&gt;
Another OS/2 mechanism, called DosEnterCritSec, can be used to protect a&lt;br /&gt;
critical section that is accessed only by threads belonging to a single&lt;br /&gt;
process. When a thread issues the DosEnterCritSec call, OS/2 suspends&lt;br /&gt;
execution of all other threads in that process until a subsequent&lt;br /&gt;
DosEnterCritSec call is issued. Naturally, only threads executing in&lt;br /&gt;
application mode are suspended; threads executing inside the OS/2 kernel&lt;br /&gt;
are not suspended until they attempt to return to application mode.&lt;br /&gt;
5. I leave as an exercise to the reader to explain why the&lt;br /&gt;
DosEnterCritSec call is not safe unless all other threads&lt;br /&gt;
in the process make use of it for that critical section as well.&lt;br /&gt;
5 The&lt;br /&gt;
use of DosEnterCritSec is dangerous because the process's other threads may&lt;br /&gt;
be suspended while they are holding a critical section. If the thread that&lt;br /&gt;
issued the DosEnterCritSec then also tries to enter that critical section,&lt;br /&gt;
the process will deadlock. If a dynlink package is involved, it may have&lt;br /&gt;
created extra threads unbeknownst to the client process so that the client&lt;br /&gt;
may not even be aware that such a critical section exists and might be in&lt;br /&gt;
use. For this reason, DosEnterCritSec is safe only when used to protect&lt;br /&gt;
short sections of code that can't block or deadlock and that don't call any&lt;br /&gt;
dynlink modules.&lt;br /&gt;
     Still another OS/2 critical section facility is file sharing and&lt;br /&gt;
record locking, which can be used to protect critical sections when they&lt;br /&gt;
consist of files or parts of files. For example, a database program&lt;br /&gt;
certainly considers its master database file a critical section, and it&lt;br /&gt;
doesn't want anyone messing with it while the database application has it&lt;br /&gt;
open. It can open the file with the file-sharing mode set to &amp;quot;allow no&lt;br /&gt;
(other) readers, allow no writers.&amp;quot; As long as the database application&lt;br /&gt;
keeps the file open, OS/2 prevents any other process from opening (or&lt;br /&gt;
deleting!) that file.&lt;br /&gt;
     The record-locking mechanism can be used to provide a smaller&lt;br /&gt;
granularity of protection. A process can lock a range of bytes within a&lt;br /&gt;
file, and while that lock is in effect, OS/2 prevents any other process&lt;br /&gt;
from reading or writing those bytes. These two specialized forms of&lt;br /&gt;
critical section protection are unique in that they protect a process&lt;br /&gt;
against all other processes, even &amp;quot;uncooperating&amp;quot; ones that don't protect&lt;br /&gt;
their own access to the critical section. Unfortunately, the file-sharing&lt;br /&gt;
and record-locking mechanisms don't contain any provision for blocking&lt;br /&gt;
until the conflict is released. Applications that want to wait for the&lt;br /&gt;
conflict to clear must use a polling loop. Use DosSleep to block for at&lt;br /&gt;
least a half second between each poll.&lt;br /&gt;
     Unfortunately, although semaphores protect critical sections well,&lt;br /&gt;
sometimes they bring problems of their own. Specifically, what happens if&lt;br /&gt;
an asynchronous event, such as program termination or a signal, pulls the&lt;br /&gt;
CPU away from inside a critical section and the CPU never returns to&lt;br /&gt;
release the semaphore? The answers range from &amp;quot;moot&amp;quot; to &amp;quot;disaster,&amp;quot;&lt;br /&gt;
depending on the circumstances. The possibilities are so manifold that&lt;br /&gt;
I'll group some of them.&lt;br /&gt;
     What can you do if the CPU is pulled away inside a critical&lt;br /&gt;
section?&lt;br /&gt;
&lt;br /&gt;
     þ  Ignore it. This is fine if the critical section is wholly accessed&lt;br /&gt;
        by a single process and that process doesn't use signals to modify&lt;br /&gt;
        the normal path of execution and if neither the process nor its&lt;br /&gt;
        dynlink routines attempt to enter the critical section during&lt;br /&gt;
        DosExitList processing.&lt;br /&gt;
&lt;br /&gt;
     þ  Clear the semaphore. This is an option if you know that the&lt;br /&gt;
        resource protected by the semaphore has no state, such as a&lt;br /&gt;
        semaphore that protects the right to be writing to the screen. The&lt;br /&gt;
        trick is to ensure that the interrupted thread set the semaphore&lt;br /&gt;
        and that you don't accidentally clear the semaphore when you don't&lt;br /&gt;
        set it. For example, if the semaphore is wholly used within a&lt;br /&gt;
        single process but that process's DosExitList handlers may use it,&lt;br /&gt;
        they can force the semaphore clear when they are entered.&lt;br /&gt;
&lt;br /&gt;
     þ  Detect the situation and repair the critical section. This&lt;br /&gt;
        detection can be made for RAM semaphores only during process&lt;br /&gt;
        termination and only if the semaphore is solely used by that&lt;br /&gt;
        process. In such a case, you know that a thread in the process set&lt;br /&gt;
        the semaphore, and you know that the thread is no longer executing&lt;br /&gt;
        the critical section because all threads are terminated. You can&lt;br /&gt;
        test the semaphore by using a nonblocking DosSemSet; if it's set,&lt;br /&gt;
        &amp;quot;recover&amp;quot; the resource.&lt;br /&gt;
&lt;br /&gt;
        System semaphores are generally better suited for this. When the&lt;br /&gt;
        owning thread of a system semaphore dies, the semaphore is given a&lt;br /&gt;
        special mark. The next attempt to set the semaphore returns with a&lt;br /&gt;
        code that tells the new owner that the previous owner died within&lt;br /&gt;
        the critical section. The new owner has the option of cleaning up&lt;br /&gt;
        the resource.&lt;br /&gt;
&lt;br /&gt;
     Another possibility is to try to prevent the CPU from being yanked out&lt;br /&gt;
of a critical section. Signals can be momentarily delayed with the&lt;br /&gt;
DosHoldSignal mechanism. Process termination that results from an external&lt;br /&gt;
kill can be postponed by setting up a signal handler for the KILL signal&lt;br /&gt;
and then using DosHoldSignal. This last technique doesn't protect you&lt;br /&gt;
against termination due to GP fault and the like however.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.2  DosBufReset&lt;br /&gt;
One remaining data integrity issue--disk data synchronization--is not&lt;br /&gt;
related to critical sections. Often, when a DosWrite call is made, OS/2&lt;br /&gt;
holds the data in a buffer rather than writing it immediately to disk.&lt;br /&gt;
Naturally, any subsequent calls made to read this data are satisfied&lt;br /&gt;
correctly, so an application cannot see that the data has not yet been&lt;br /&gt;
written unless the reading application uses direct physical access to the &lt;br /&gt;
volume (that is, raw media reads). This case explains why CHKDSK may&lt;br /&gt;
erroneously report errors that run on a volume that has open files.&lt;br /&gt;
     OS/2 eventually writes the data to the disk, so this buffering is of&lt;br /&gt;
concern only when the system crashes with unwritten buffered data.&lt;br /&gt;
Naturally, such crashes are expected to be rare, but some applications may&lt;br /&gt;
find the possibility so threatening that they want to take protective&lt;br /&gt;
steps. The two OS/2 functions for this purpose are flushing and&lt;br /&gt;
writethroughs. The flush operation--DosBufReset--writes all dirty buffers--&lt;br /&gt;
those with changed but unwritten data in them--to the disk. When the call&lt;br /&gt;
returns, the data is on the disk. Use this call sparingly; although its&lt;br /&gt;
specification promises only that it will flush buffers associated with the&lt;br /&gt;
specified file handle(s), for most file systems it writes all dirty buffers&lt;br /&gt;
in the system to disk. Moreover, if file handles are open to a server&lt;br /&gt;
machine on the network, most or all of that server's buffers get flushed,&lt;br /&gt;
even those that were used by other client machines on the network. Because&lt;br /&gt;
of these costs, applications should use this operation judiciously.&lt;br /&gt;
     Note that it's not true that a flush operation simply causes a write&lt;br /&gt;
to be done sooner rather than later. A flush operation may also cause extra&lt;br /&gt;
disk writes. For example, consider an application that is writing data 10&lt;br /&gt;
bytes at a time. In this case, OS/2 buffers the data until it has a full&lt;br /&gt;
sector's worth. A series of buffer flush operations arriving at this time&lt;br /&gt;
would cause the assembly buffer to be written to the disk many extra and&lt;br /&gt;
unnecessary times.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.3  Writethroughs&lt;br /&gt;
Buffer flushes are expensive, and unless they are used frequently, they&lt;br /&gt;
don't guarantee a particular write ordering. Some applications, such as&lt;br /&gt;
database managers, may want to guarantee that data be written to the disk&lt;br /&gt;
in exactly the same order in which it was given to OS/2 via DosWrite. For&lt;br /&gt;
example, an application may want to guarantee that the data is in place in&lt;br /&gt;
a database before the allocation chain is written and that the chain be&lt;br /&gt;
written before the database directory is updated. Such an ordering may make&lt;br /&gt;
it easy for the package to recover the database in case of a crash.&lt;br /&gt;
     The OS/2 mechanism for doing this is called writethrough--a status bit&lt;br /&gt;
that can be set for individual file handles. If a writethrough is in effect&lt;br /&gt;
for a handle to which the write is issued, OS/2 guarantees that the data&lt;br /&gt;
will be written to the disk before the DosWrite operation returns.&lt;br /&gt;
Obviously, applications using writethrough should write their data in large&lt;br /&gt;
chunks; writing many small chunks of data to a file marked for writethrough&lt;br /&gt;
is very inefficient.&lt;br /&gt;
&lt;br /&gt;
     Three caveats are associated with writethroughs:&lt;br /&gt;
&lt;br /&gt;
     þ  If writethrough is set on a file after it is open, all subsequent&lt;br /&gt;
        writes are written through, but data from previous writes may still&lt;br /&gt;
        be in dirty buffers.&lt;br /&gt;
&lt;br /&gt;
     þ  If a writethrough file is being shared by multiple processes or is&lt;br /&gt;
        open on multiple handles, all instances of that file should be&lt;br /&gt;
        marked writethrough. Data written to a handle not marked&lt;br /&gt;
        writethrough may go into the buffers.&lt;br /&gt;
&lt;br /&gt;
     þ  The operation of data writethroughs has some nonintuitive surprises&lt;br /&gt;
        when used with the current FAT file system. Specifically, although&lt;br /&gt;
        this feature works as advertised to place the file's data sectors&lt;br /&gt;
        on the disk, it does not update the directory entry that specifies&lt;br /&gt;
        the size of the file. Thus, if you extend a file by 10 sectors and&lt;br /&gt;
        the system crashes before you close the file, the data in those 10&lt;br /&gt;
        sectors is lost. If you had writethrough set, then those 10 sectors&lt;br /&gt;
        of data were indeed written to the disk; but because the directory&lt;br /&gt;
        entry wasn't updated, CHKDSK will return those sectors to the free&lt;br /&gt;
        list.&lt;br /&gt;
&lt;br /&gt;
        The writethrough operation protects the file's data but not the&lt;br /&gt;
        directory or allocation information. This is not a concern as long&lt;br /&gt;
        as you write over a portion of the file that has been already&lt;br /&gt;
        extended, but any writes that extend the file are not protected.&lt;br /&gt;
        The good news is that the data will be on the disk, as guaranteed,&lt;br /&gt;
        but the bad news is that the directory entry won't be updated; if&lt;br /&gt;
        the system crashes, file extensions cannot be recovered. The&lt;br /&gt;
        recommended solution is to use DosNewSize to extend the file as&lt;br /&gt;
        needed, followed by DosBufReset to update the directory information&lt;br /&gt;
        on the disk, and then to writethrough the data as needed.&lt;br /&gt;
        Overextending the file size is better than doing too many&lt;br /&gt;
        NewSize/BufReset combinations; if you overextend, you can always&lt;br /&gt;
        shrink the file before closing it with a final DosNewSize.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.3  Timer Services&lt;br /&gt;
&lt;br /&gt;
Frequently, applications want to keep track of the passage of real time. A&lt;br /&gt;
game program may want events to occur asynchronously with the user's input;&lt;br /&gt;
a telecommunications program may want to track how long a response takes&lt;br /&gt;
and perhaps declare a link timed-out after some interval. Other programs&lt;br /&gt;
may need to pace the display of a demonstration or assume a default action&lt;br /&gt;
if the user doesn't respond in a reasonable amount of time. OS/2 provides&lt;br /&gt;
several facilities to track the passage of real time; applications should&lt;br /&gt;
use these facilities and shun polling and timing loops because the timing&lt;br /&gt;
of such loops depends on the system's workload and the CPU's speed and&lt;br /&gt;
because they totally lock out from execution any thread of a lower&lt;br /&gt;
priority.&lt;br /&gt;
     Time intervals in OS/2 are discussed in terms of milliseconds to&lt;br /&gt;
isolate the concept of a time interval from the physical mechanism&lt;br /&gt;
(periodic clock interrupts) that measures time intervals. Although you can&lt;br /&gt;
specify a time interval down to the millisecond, the system does not&lt;br /&gt;
guarantee any such accuracy.&lt;br /&gt;
     On most hardware, OS/2 version 1.0 uses a periodic system clock&lt;br /&gt;
interrupt of 32 Hz (32 times a second). This means that OS/2 measures time&lt;br /&gt;
intervals with a quantum size of 31.25 milliseconds. As a result, any&lt;br /&gt;
timeout value is subject to quantization error of this order. For example,&lt;br /&gt;
if a process asks to sleep for 25 milliseconds, OS/2 knows that the request&lt;br /&gt;
was made at some time after the most recent clock tick, but it cannot tell&lt;br /&gt;
how long after, other than that less than 31.25 milliseconds had elapsed&lt;br /&gt;
between the previous clock tick and the sleep request. After the sleep&lt;br /&gt;
request is made, another clock tick occurs. Once again, OS/2 can't tell how&lt;br /&gt;
much time has elapsed since the sleep request and the new clock tick, other&lt;br /&gt;
than that it was less than 31.25 milliseconds. Lacking this knowledge, OS/2&lt;br /&gt;
uses a simple algorithm: At each clock tick, OS/2 decrements each timeout&lt;br /&gt;
value in the system by the clock tick interval (generally 31.25&lt;br /&gt;
milliseconds). Thus, our 25-millisecond sleep request may come back in 1&lt;br /&gt;
millisecond or less or in 31.25 milliseconds. A request to block for 33&lt;br /&gt;
milliseconds could come back in 32 milliseconds or in 62.5 milliseconds.&lt;br /&gt;
     Clearly, the OS/2 timer functions are intended for human-scale timing,&lt;br /&gt;
in which the 1/32-second quantization error is not noticeable, and not for&lt;br /&gt;
high-precision timing of fast events. Regardless of the resolution of the&lt;br /&gt;
timer, the system's preemptive scheduler prevents the implementation of&lt;br /&gt;
high-accuracy short-interval timing. Even if a timer system call were to&lt;br /&gt;
time out after a precise interval, the calling thread might not resume&lt;br /&gt;
execution immediately because a higher-priority thread might be executing&lt;br /&gt;
elsewhere.&lt;br /&gt;
     One form of OS/2 timer services is built into some system calls. For&lt;br /&gt;
example, all semaphore blocking calls support an argument that allows the&lt;br /&gt;
caller to specify a timeout value. When the specified time has elapsed, the&lt;br /&gt;
call returns with a &amp;quot;call timed out&amp;quot; error code. Some threads use this&lt;br /&gt;
facility to guard against being indefinitely locked out; if the semaphore&lt;br /&gt;
call times out, the thread can give up, display an error message, or try&lt;br /&gt;
another tactic. Other threads may use the facility expecting to be timed&lt;br /&gt;
out: They use the timeout facility to perform periodic tasks and use the&lt;br /&gt;
semaphore just as an emergency flag. Another thread in the system can&lt;br /&gt;
provide an emergency wakeup for the timer thread simply by clearing the&lt;br /&gt;
semaphore.&lt;br /&gt;
     Blocking on a semaphore merely to delay for a specific interval is&lt;br /&gt;
unnecessary; the DosSleep call allows a thread to block unconditionally for&lt;br /&gt;
an arbitrary length of time, subject, of course, to the timer's&lt;br /&gt;
quantization error.&lt;br /&gt;
6. And subject to the fact that if thread 1 is doing the DosSleeping&lt;br /&gt;
the sleep will be interrupted if a signal is taken.&lt;br /&gt;
6 DosSleep measures time intervals in a synchronous&lt;br /&gt;
fashion: The thread is held inside the operating system until the time&lt;br /&gt;
interval has elapsed. OS/2 provides an asynchronous timer service that&lt;br /&gt;
allows timing to take place in parallel with a thread's normal execution.&lt;br /&gt;
Specifically, the DosTimerAsync call is made with a timeout interval, such&lt;br /&gt;
as DosSleep, and also with the handle of a system semaphore.&lt;br /&gt;
7. Unlike most semaphore applications, the timer functions work&lt;br /&gt;
only with system semaphores. RAM semaphores may not be used&lt;br /&gt;
because of the difficulty in posting a RAM semaphore at interrupt&lt;br /&gt;
time; the RAM that contains the semaphore may be swapped out.&lt;br /&gt;
7 The&lt;br /&gt;
DosTimerAsync call returns immediately; later, when the time interval has&lt;br /&gt;
elapsed, the system semaphore is cleared. The process can poll the&lt;br /&gt;
semaphore to see if the time is up, and/or it can block on the semaphore to&lt;br /&gt;
wait for the time to elapse. Of course, if a process contains multiple&lt;br /&gt;
threads, some can poll and others can block.&lt;br /&gt;
     The DosTimerStart call is identical to the DosTimerAsync call except&lt;br /&gt;
that the semaphore is repeatedly cleared at the specified interval until a&lt;br /&gt;
corresponding DosTimerStop call is made. DosTimerStart clears the&lt;br /&gt;
semaphore; the process must set it again after it's been cleared.&lt;br /&gt;
     None of the above-mentioned facilities is completely accurate for&lt;br /&gt;
tracking the time of day or the amount of elapsed time. As we mentioned, if&lt;br /&gt;
a higher-priority thread is consuming enough CPU time, unpredictable delays&lt;br /&gt;
occur. Even DosTimerStart is susceptible to losing ticks because if the CPU&lt;br /&gt;
is unavailable for a long enough period the process won't be able to reset&lt;br /&gt;
the semaphore soon enough to prevent missing its next clearing.&lt;br /&gt;
Applications that want a precise measurement of elapsed time should use the&lt;br /&gt;
time values stored in the global infoseg. We also recommend that&lt;br /&gt;
applications with a critical need to manage timeouts, even if they are&lt;br /&gt;
executing in the lower-priority background, dedicate a thread to managing&lt;br /&gt;
the time-critical work and elevate that thread to a higher priority. This&lt;br /&gt;
will ensure that time-critical events aren't missed because a high-priority&lt;br /&gt;
foreground thread is going through a period of intensive CPU usage. Of&lt;br /&gt;
course, such an application must be designed so that the high-priority&lt;br /&gt;
timer event thread does not itself consume significant CPU time; it should&lt;br /&gt;
simply log the timer events and rely on its fellow normal-priority threads&lt;br /&gt;
to handle the major work involved.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==17  Device Drivers and Hard Errors==&lt;br /&gt;
&lt;br /&gt;
The multitasking nature of OS/2 makes OS/2 device drivers considerably more&lt;br /&gt;
complex than MS-DOS device drivers. Furthermore, whenever you have devices,&lt;br /&gt;
you must deal with device failures--the infamous hard errors. The handling&lt;br /&gt;
of hard errors in a multitasking environment is likewise considerably more&lt;br /&gt;
complex than it was under MS-DOS.&lt;br /&gt;
&lt;br /&gt;
===17.1  Device Drivers===&lt;br /&gt;
&lt;br /&gt;
This section gives an overview of device drivers, paying special attention&lt;br /&gt;
to their key architectural elements. Writing a device driver is a complex&lt;br /&gt;
task that must be undertaken with considerable care; a great many caveats&lt;br /&gt;
and &amp;quot;gotchas&amp;quot; lie in wait for the unsuspecting programmer. Many of these&lt;br /&gt;
&amp;quot;gotchas&amp;quot; are of that most favorite breed: ones that never show up in&lt;br /&gt;
testing, only in the field. This section is by no means an exhaustive&lt;br /&gt;
discussion of device drivers, nor is it a how-to guide. Study the OS/2&lt;br /&gt;
device driver reference documentation carefully before setting out to write&lt;br /&gt;
your own.&lt;br /&gt;
     In Chapter 2 I briefly discussed device independence and the role&lt;br /&gt;
that device drivers play in bringing it about. I said that a device driver&lt;br /&gt;
is a package of code that transforms I/O requests made in standard, device-&lt;br /&gt;
independent fashion into the operations necessary to make a specific piece&lt;br /&gt;
of hardware fulfill that request. A device driver takes data and status&lt;br /&gt;
information from the hardware, in the hardware-specific format, and&lt;br /&gt;
massages that information into the form that the operating system expects&lt;br /&gt;
to receive.&lt;br /&gt;
     The device driver architecture has two key elements. First, each&lt;br /&gt;
hardware device has its own device driver to hide the specific details of&lt;br /&gt;
the device from the operating system. Second, device drivers are not hard-&lt;br /&gt;
wired into the operating system when it is manufactured; they are&lt;br /&gt;
dynamically installed at boot time. This second point is the interesting&lt;br /&gt;
one. If all device drivers were hard-wired into OS/2, the technique of&lt;br /&gt;
encapsulating device-dependent code into specific packages would be good&lt;br /&gt;
engineering practice but of little interest to the user. OS/2 would run&lt;br /&gt;
only on a system configured with a certain magic set of peripheral devices.&lt;br /&gt;
But because device drivers are dynamically installable at boot time, OS/2&lt;br /&gt;
can work with a variety of devices, even ones that didn't exist when OS/2&lt;br /&gt;
was written, as long as a proper device driver for that device is installed&lt;br /&gt;
at boot time. Note that device drivers can be installed only at boot time;&lt;br /&gt;
they cannot be installed after the system has completed booting up. This is&lt;br /&gt;
because in a future secure environment the ability to dynamically install a&lt;br /&gt;
device driver would give any application the ability to violate system&lt;br /&gt;
security.&lt;br /&gt;
     Saying that device drivers merely translate between the operating&lt;br /&gt;
system and the device is a bit of oversimplification; in reality, they are&lt;br /&gt;
responsible for encapsulating, or owning, nearly all device-specific&lt;br /&gt;
knowledge about the device. Device drivers service the interrupts that&lt;br /&gt;
their devices generate, and they work at task time (that is, at&lt;br /&gt;
noninterrupt time). If a device monitor is necessary for a device, the&lt;br /&gt;
device driver writer decides that and provides the necessary support. If an&lt;br /&gt;
application needs direct access to a device's I/O ports or to its special&lt;br /&gt;
mapped memory,&lt;br /&gt;
1. For example, the display memory of a CGA or an EGA card.&lt;br /&gt;
1 the driver offers those services to processes. The device&lt;br /&gt;
driver also knows whether multiple processes should simultaneously use the&lt;br /&gt;
device and either allows or disallows this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.1  Device Drivers and OS/2 Communication&lt;br /&gt;
Because device drivers need to call and be called by OS/2 efficiently and&lt;br /&gt;
because device drivers must handle hardware interrupts efficiently, they&lt;br /&gt;
must run at ring 0. This means that device drivers must be trusted and must&lt;br /&gt;
be trustworthy. A flaky device driver--or worse, a malicious one--can do&lt;br /&gt;
unlimited and nearly untraceable damage to any application or data file in&lt;br /&gt;
the system.&lt;br /&gt;
     OS/2 can easily call a device driver. Because OS/2 loaded the device&lt;br /&gt;
driver into memory, it knows the address of its entry point and can call it&lt;br /&gt;
directly. For the device driver to call OS/2 is trickier because the driver&lt;br /&gt;
doesn't know the memory locations that OS/2 occupies nor does it have any&lt;br /&gt;
control over the memory descriptor tables (LDT and GDT). When the device&lt;br /&gt;
driver is initialized, OS/2 supplies the device driver with the address of&lt;br /&gt;
the OS/2 DevHlp entry point. Device drivers call this address to access a&lt;br /&gt;
variety of OS/2 services, called DevHlp services. The OS/2 DevHlp address&lt;br /&gt;
references a GDT selector so that the DevHlp address is valid at all times--&lt;br /&gt;
in protected mode, in real mode,&lt;br /&gt;
2. A GDT selector cannot literally be valid in real mode because the&lt;br /&gt;
GDT is not in use. OS/2 uses a technique called tiling so that the&lt;br /&gt;
selector, when used as a segment address in real mode, addresses&lt;br /&gt;
the same physical memory as does the protect mode segment.&lt;br /&gt;
2 at interrupt time, and during device&lt;br /&gt;
driver initialization. Some DevHlp functions are only valid in certain&lt;br /&gt;
modes, but the DevHlp facility is always available.&lt;br /&gt;
     Why don't device drivers simply use dynamic links to access OS/2&lt;br /&gt;
services, the way that applications do? The OS/2 kernel dynlink interface&lt;br /&gt;
is designed for processes running in user mode, at ring 3, to call the ring&lt;br /&gt;
0 kernel. In other words, it's designed for outsiders to call in, but&lt;br /&gt;
device drivers are already inside. They run at ring 0, in kernel mode, and&lt;br /&gt;
at interrupt time. One, of course, could kludge things so that device&lt;br /&gt;
drivers make dynlink calls, and then special code at those OS/2 entry&lt;br /&gt;
points would recognize a device driver request and do all the special&lt;br /&gt;
handling. But every system call from a normal application would be slowed&lt;br /&gt;
by this extra code, and every service call from a device driver would&lt;br /&gt;
likewise be slowed. As a result, device drivers have their own private,&lt;br /&gt;
high-efficiency &amp;quot;backdoor&amp;quot; entry into OS/2. Figure 17-1 illustrates&lt;br /&gt;
the call linkages between OS/2 and a device driver. OS/2 calls only one&lt;br /&gt;
entry point in the device driver, providing a function code that the device&lt;br /&gt;
driver uses to address a dispatch table. OS/2 learns the address of&lt;br /&gt;
thisentry point when it loads the device driver. The device driver in turn&lt;br /&gt;
calls only one OS/2 address, the DevHlp entry point. It also supplies a&lt;br /&gt;
function code that is used to address a dispatch table. The device driver&lt;br /&gt;
is told this address when it receives its initialize call from OS/2. Not&lt;br /&gt;
shown is the device driver's interrupt entry point.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
           OS/2                                   Device driver&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Far call (FCN)   ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³                        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄ¿    ³  Function              ³&lt;br /&gt;
³               DevHlp   ³             ³    ³   Table                ³&lt;br /&gt;
³         Function Table ³             ³    ³  ÚÄÄÄÄÄÄ¿  ÚÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄ¿  ÚÄÄÄÄÄÄ¿  ³             ÀÄÄÄÄÅÄ�³      ÃÄÄÙ           ³&lt;br /&gt;
³           ÀÄÄ´      ³�ÄÅÄÄÄÄÄÄÄÄÄÄ¿       ³  ³      ÃÄÄÄÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄÄÄÄ´      ³  ³ DevHlp   ³       ³  ³      ÃÄÄ¿           ³&lt;br /&gt;
³           ÚÄÄ´      ³  ³ address  ³       ³  ÀÄÄÄÄÄÄÙ  ÀÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄÙ  ÀÄÄÄÄÄÄÙ  ³          ³       ³                        ³&lt;br /&gt;
³                        ³          ÀÄÄÄÄÄÄÄ´                        ³&lt;br /&gt;
³                        ³       Far call   ³                        ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ     (DevHlp FCN) ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 17-1.  Device driver call linkages.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.2  Device Driver Programming Model&lt;br /&gt;
The programming model for device drivers under MS-DOS is simple. Device&lt;br /&gt;
drivers are called to perform a function, and they return when that&lt;br /&gt;
function is complete or they encounter an unrecoverable error. If the&lt;br /&gt;
device is interrupt driven, the CPU hangs in a loop inside the device&lt;br /&gt;
driver while waiting for the driver's interrupt handler to be entered; when&lt;br /&gt;
the operation is complete, the interrupt handler sets a private flag to&lt;br /&gt;
break the task-time CPU out of its wait loop.&lt;br /&gt;
     The OS/2 device driver model is considerably more complicated because&lt;br /&gt;
OS/2 is a multitasking system. Even if the thread that calls the device&lt;br /&gt;
driver with a request has nothing better to do than wait for the operation&lt;br /&gt;
to complete, other threads in the system could make good use of the time.&lt;br /&gt;
Another effect of the OS/2 multitasking architecture is that two or more&lt;br /&gt;
threads can simultaneously call a device driver. To explore this last issue&lt;br /&gt;
fully, we'll digress for a moment and discuss the OS/2 internal execution&lt;br /&gt;
model.&lt;br /&gt;
     By design, OS/2 acts more like a subroutine library than like a&lt;br /&gt;
process. The only dispatchable entities in the system are threads, and all&lt;br /&gt;
threads belong to processes. When a process's thread calls OS/2, that&lt;br /&gt;
thread executes OS/2's code.&lt;br /&gt;
3. The few exceptions to this don't affect the issues discussed here.&lt;br /&gt;
3 It's like walking up to the counter at a&lt;br /&gt;
fast-food restaurant and placing your order. You then slip on an apron, run&lt;br /&gt;
around behind the counter, and prepare your own order. When the food is&lt;br /&gt;
ready, you take off the apron, run back around to the front of the counter,&lt;br /&gt;
and pick up the food. The counter represents the boundary between ring 0&lt;br /&gt;
(kernel mode) and ring 3 (application mode), and the apron represents the&lt;br /&gt;
privileged state necessary to work behind the counter.&lt;br /&gt;
     Naturally, OS/2 is reentrant; at any one time many threads are&lt;br /&gt;
executing inside OS/2, but each is doing work for only one process--the&lt;br /&gt;
process to whom that thread belongs. Behind the counter are several folks&lt;br /&gt;
wearing aprons, but each is working only on his or her own order. This&lt;br /&gt;
approach simplifies the internals of OS/2: Each instance of a section of&lt;br /&gt;
code is doing only one thing for one client. If a section of code must wait&lt;br /&gt;
for something, it simply blocks (analogous to a semaphore wait) as long as&lt;br /&gt;
it has to and resumes when it can. Threads within the kernel that are&lt;br /&gt;
competing for a single resource do so by internal semaphores, and they are&lt;br /&gt;
given access to these semaphores on a priority basis, just as they are when&lt;br /&gt;
executing in application mode.&lt;br /&gt;
     OS/2 makes little distinction between a thread running inside the&lt;br /&gt;
kernel and one running outside, in the application's code itself: The&lt;br /&gt;
process's LDT remains valid, and the thread, while inside the kernel, can&lt;br /&gt;
access any memory location that was accessible to the process in&lt;br /&gt;
application mode, in addition to being able to access restricted ring 0&lt;br /&gt;
memory. The only distinction the scheduler makes between threads inside and&lt;br /&gt;
those outside the kernel is that the scheduler never preempts a thread&lt;br /&gt;
running inside the kernel. This greatly relaxes the rigor with which kernel&lt;br /&gt;
code needs to protect its critical sections: When the CPU is executing&lt;br /&gt;
kernel code, the scheduler performs a context switch only when the CPU&lt;br /&gt;
voluntarily blocks itself. As long as kernel code doesn't block itself,&lt;br /&gt;
wait on a semaphore, or call a subroutine that waits on a semaphore, it&lt;br /&gt;
needn't worry about any other thread entering its critical section.&lt;br /&gt;
4. Although hardware interrupts still occur; any critical section&lt;br /&gt;
modified at interrupt time is still vulnerable.&lt;br /&gt;
4&lt;br /&gt;
     When OS/2 calls a device driver at task time, it does so with the&lt;br /&gt;
thread that was executing OS/2--the thread that belongs to the client&lt;br /&gt;
process and that made the original service call. Thus, the task-time part&lt;br /&gt;
of a device driver is running, at ring 0, in the client's context. The&lt;br /&gt;
client's LDT is active, all the client's addresses are active, and the&lt;br /&gt;
device driver is immune from being preempted by other task-time threads&lt;br /&gt;
(but not by interrupt service) until it blocks via a DevHlp&lt;br /&gt;
function or returns to OS/2.&lt;br /&gt;
     OS/2 device drivers are divided into two general categories: those for&lt;br /&gt;
character mode devices and those for block mode devices. This terminology&lt;br /&gt;
is traditional, but don't take it too literally because character mode&lt;br /&gt;
operations can be done to block mode devices. The actual distinction is&lt;br /&gt;
that character mode device drivers do I/O synchronously; that is, they do&lt;br /&gt;
operations in first in, first out order. Block mode device drivers can be&lt;br /&gt;
asynchronous; they can perform I/O requests in an order different from the&lt;br /&gt;
one in which they received them. A traditional serial character device,&lt;br /&gt;
such as a printer, must not change the order of its requests; doing so&lt;br /&gt;
scrambles the output. A block device, such as a disk, can reverse the order&lt;br /&gt;
of two sector reads without problems.&lt;br /&gt;
     Figure 17-2 shows an algorithm for character mode device drivers.&lt;br /&gt;
5. See the device driver reference manual for more details.&lt;br /&gt;
5&lt;br /&gt;
OS/2 calls the device driver with a request, as shown at the top of the&lt;br /&gt;
figure. If the device driver is busy with another request, the new&lt;br /&gt;
requesting thread should block on a RAM semaphore until the device is&lt;br /&gt;
available. When the device is free, the task-time thread does the requested&lt;br /&gt;
operation. Sometimes the work can be done at task time (such as an IOCTL&lt;br /&gt;
call asking about the number of characters in an input buffer), but more&lt;br /&gt;
frequently the task-time thread initiates the operation, and the work is&lt;br /&gt;
completed at interrupt time. If the task-time thread needs to wait for&lt;br /&gt;
interrupt service, it should block on a RAM semaphore&lt;br /&gt;
6. Located in the device driver data area.&lt;br /&gt;
6 that the interrupt-&lt;br /&gt;
time code will clear. When the last associated device interrupt takes place&lt;br /&gt;
and the operation is complete, the interrupt code releases the RAM&lt;br /&gt;
semaphore. The task-time thread awakens and returns to OS/2 with the status&lt;br /&gt;
bits properly set in the request block.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       OS/2 code            Device driver           Device interrupt&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Issue request to&lt;br /&gt;
device driver. ÄÄÄÄÄÄ� Block until device is&lt;br /&gt;
                       available.&lt;br /&gt;
&lt;br /&gt;
                       Peform request. Block&lt;br /&gt;
                       until interrupts&lt;br /&gt;
                       complete if necessary.  ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ Device interrupt (if any)&lt;br /&gt;
                                               ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ Perform next step in I/O&lt;br /&gt;
                                               ³ operation.&lt;br /&gt;
                                               ³&lt;br /&gt;
                                               ³ When done, use DevHlp&lt;br /&gt;
                                               ³ ProcRun to unblock task&lt;br /&gt;
                                               ³ time thread.&lt;br /&gt;
                                               ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ End of interrupt&lt;br /&gt;
                       Complete request and    ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                       return to OS/2&lt;br /&gt;
Request now complete.&lt;br /&gt;
Continue.&lt;br /&gt;
&lt;br /&gt;
Figure 17-2.  Simplified character mode device driver model.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Figure 17-3 shows an algorithm for block devices. The general outline&lt;br /&gt;
is the same as that for character devices but more complicated because of&lt;br /&gt;
the asynchronous nature of random-access devices. Because requests can be&lt;br /&gt;
processed in any order, most block device drivers maintain an internal work&lt;br /&gt;
queue to which they add each new request. They usually use a special DevHlp&lt;br /&gt;
function to sort the work queue in sector number order so that disk head&lt;br /&gt;
motion is minimized.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       OS/2 code             Device driver           Device interrupt&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Issue request to&lt;br /&gt;
device driver. ÄÄÄÄÄÄÄ� Add request to device&lt;br /&gt;
                        request list. Fire up&lt;br /&gt;
                        device if not active.&lt;br /&gt;
&lt;br /&gt;
                        Return to OS/2 with&lt;br /&gt;
                        DONE clear. ÄÄ¿&lt;br /&gt;
           ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
           �                                    ³ Device interrupt&lt;br /&gt;
OS/2 thread(s) block on                         ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
incomplete request when                         ³ Perform next step in&lt;br /&gt;
they can proceed no                             ³ I/O operation.&lt;br /&gt;
further without the                             ³&lt;br /&gt;
data.                                           ³ If request is done&lt;br /&gt;
                                                ³    Pull request from&lt;br /&gt;
                                                ³    list&lt;br /&gt;
Any threads blocked ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
on this request are                             ³    Use DevHlp DevDone&lt;br /&gt;
awakened.                                       ³    to tell OS/2 that&lt;br /&gt;
                                                ³    the I/O is done.&lt;br /&gt;
                                                ³&lt;br /&gt;
                                                ³    If further work on&lt;br /&gt;
                                                ³    queue, start work&lt;br /&gt;
                                                ³    on next item.&lt;br /&gt;
                                                ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                                ³ End of interrupt&lt;br /&gt;
Request now complete.                           ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Continue.&lt;br /&gt;
&lt;br /&gt;
Figure 17-3.  Block mode device driver model.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The easiest way to understand this figure is to think of a block mode&lt;br /&gt;
device driver as being made up of N threads: one interrupt-time thread does&lt;br /&gt;
the actual work, and all the others are task-time threads that queue the&lt;br /&gt;
work. As each request comes into the driver via a task-time thread, that&lt;br /&gt;
thread simply puts the request on the queue and returns to OS/2. Later, the&lt;br /&gt;
device driver's interrupt service routine calls the DevHlp DevDone function&lt;br /&gt;
to tell OS/2 that the operation is complete. Returning to OS/2 with the&lt;br /&gt;
operation incomplete is permissible because the request block status bits&lt;br /&gt;
show that the operation is incomplete.&lt;br /&gt;
     Sometimes, OS/2 needs to wait for the operation (such as a read from a&lt;br /&gt;
directory); so when the device driver returns with the operation&lt;br /&gt;
incomplete, OS/2 simply waits for it to finish. In other circumstances,&lt;br /&gt;
such as flushing the cache buffers, OS/2 may not wait around for the&lt;br /&gt;
operation to complete. It may go on about its business or even issue a new&lt;br /&gt;
request to the driver, using, of course, a new request block because the&lt;br /&gt;
old one is still in use. This design gives the system a great deal of&lt;br /&gt;
parallelism and thereby improves throughput.&lt;br /&gt;
     I said that a block mode device driver consists of several task-time&lt;br /&gt;
threads and one interrupt-time thread. The term interrupt-time thread is a&lt;br /&gt;
bit misleading, however, because it's not a true thread managed by the&lt;br /&gt;
scheduler but a pseudo thread created by the hardware interrupt mechanism.&lt;br /&gt;
For example, a disk device driver has four requests queued up, and the READ&lt;br /&gt;
operation for the first request is in progress. When it completes, the&lt;br /&gt;
driver's interrupt service routine is entered by the hardware interrupt&lt;br /&gt;
generated by the disk controller. That interrupt-time thread, executing the&lt;br /&gt;
driver's interrupt service routine, checks the status, verifies that all is&lt;br /&gt;
OK, and calls various DevHlp routines to post the request as complete and&lt;br /&gt;
to remove it from the queue. It then notes that requests remain on the&lt;br /&gt;
queue and starts work on the next one, which involves a seek operation. The&lt;br /&gt;
driver's interrupt-time code issues the seek command to the hardware and&lt;br /&gt;
then returns from the interrupt. When the disk stops seeking, another&lt;br /&gt;
interrupt is generated; the interrupt-time code notes the successful seek,&lt;br /&gt;
issues the read or write operation to the controller, and exits.&lt;br /&gt;
     As you can see, the repeated activation of the device driver's&lt;br /&gt;
interrupt service routine is much like a thread, but with two major&lt;br /&gt;
differences. First, every time an interrupt service routine is entered, it&lt;br /&gt;
has a fresh stack. A task-time thread has register contents and a stack&lt;br /&gt;
that are preserved by the system; neither is preserved for an interrupt&lt;br /&gt;
service routine between interrupts. A task-time thread keeps track of what&lt;br /&gt;
it was doing by its CS:IP address, its register contents, and its stack&lt;br /&gt;
contents. An interrupt service routine must keep track of its work by means&lt;br /&gt;
of static values stored in the device driver's data segment. Typically,&lt;br /&gt;
interrupt service routines implement a state machine and maintain the&lt;br /&gt;
current state in the driver's data segment. Second, a true thread remains&lt;br /&gt;
in existence until explicitly terminated; an interrupt service thread is an&lt;br /&gt;
illusion of a thread that is maintained by repeated interrupts. If any one&lt;br /&gt;
execution of the interrupt service routine fails to give the hardware a&lt;br /&gt;
command that will generate another interrupt, the interrupt pseudo thread&lt;br /&gt;
will no longer exist after the interrupt service routine returns.&lt;br /&gt;
     The block mode driver algorithm description left out a detail. If the&lt;br /&gt;
disk is idle when a new request comes in, the request is put on the queue,&lt;br /&gt;
but there is no interrupt-time pseudo thread to service the request. Thus,&lt;br /&gt;
both the task-time and interrupt-time parts of a device driver must be able&lt;br /&gt;
to initiate an operation. The recommended approach is to use a software&lt;br /&gt;
state machine to control the hardware and to ensure that the state machine,&lt;br /&gt;
at least the start operation part of it, is callable at both task and&lt;br /&gt;
interrupt time. The algorithm above is then modified so that after the&lt;br /&gt;
task-time part of a block device driver puts its request on the driver's&lt;br /&gt;
internal queue it verifies that the device (or state machine or interrupt&lt;br /&gt;
pseudo thread) is active. If the device has been idle, the task-time thread&lt;br /&gt;
in the device driver initiates the operation by calling the initial state&lt;br /&gt;
of the state machine; it then returns to OS/2. This primes the pump;&lt;br /&gt;
the interrupt pseudo thread now continues to run until the request queue is&lt;br /&gt;
empty.&lt;br /&gt;
     Figure 17-4 shows an overview of the OS/2 device driver architecture.&lt;br /&gt;
Each device driver consists of a task-time part, an interrupt-time part (if&lt;br /&gt;
the device generates interrupts), and the start-operation code that is&lt;br /&gt;
executed in either mode. The driver's data area typically contains state&lt;br /&gt;
information, flags, and semaphores to handle communication between the&lt;br /&gt;
task-time part and the interrupt. Figure 17-4 also shows that the task-time&lt;br /&gt;
part of a device driver can have multiple instances. It can be called by&lt;br /&gt;
several threads at the same time, just as a shared dynlink library routine&lt;br /&gt;
might be. Unlike a dynlink library, a device driver has no instance data&lt;br /&gt;
segment; the device driver's data segment is a global data segment,&lt;br /&gt;
accessible to all execution instances of the device driver's task-time&lt;br /&gt;
component. Just as a dynlink package uses semaphores to protect critical&lt;br /&gt;
data areas in its global data segment, a device driver uses semaphores to&lt;br /&gt;
protect the critical data values in its data segment. Unlike dynlink&lt;br /&gt;
routines, the device driver has an additional special thread--the interrupt&lt;br /&gt;
service thread. A device driver can't protect critical sections that are&lt;br /&gt;
accessed at interrupt time by using semaphores because an interrupt service&lt;br /&gt;
thread cannot block. It must complete the interrupt service and exit--&lt;br /&gt;
quickly, at that. When you write device drivers, you must minimize the&lt;br /&gt;
critical sections that are entered by the interrupt service thread and&lt;br /&gt;
protect them via the CLI/STI instruction sequence.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
         ³              &amp;quot;D&amp;quot;³&lt;br /&gt;
      ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ³                ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
      ³              &amp;quot;C&amp;quot;³  ³                ³   Device driver   ³&lt;br /&gt;
   ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ÃÄÄÙ                ³ interrupt service ³&lt;br /&gt;
   ³              &amp;quot;B&amp;quot;³  ³                   ÀÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ÃÄÄÙ                     ³           ³&lt;br /&gt;
³  Instance &amp;quot;A&amp;quot;   ³  ³                        ³           ³&lt;br /&gt;
³  device driver  ÃÄÄÙ                ÚÄÄÄÄÄÄÄ�ÄÄÄ¿       ³&lt;br /&gt;
³  task-time code ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³ Start I/O ³       ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ                   ³   code    ³       ³&lt;br /&gt;
         ³                            ÀÄÄÄÄÄÂÄÄÄÄÄÙ       ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                         ÚÄÄÄÄÄÄÄÄ�ÄÄÄÄÄÄ¿      ³&lt;br /&gt;
         ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³ Device driver ³�ÄÄÄÄÄÙ&lt;br /&gt;
                                   ³     data      ³&lt;br /&gt;
                                   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 17-4.  Device driver code structure.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.3  Device Management&lt;br /&gt;
Device drivers do more than talk to the device; they also manage it for the&lt;br /&gt;
system. Device drivers are called each time a process opens or closes a&lt;br /&gt;
device; device drivers determine whether a device can be used by more than&lt;br /&gt;
one process simultaneously. Likewise, device drivers receive device monitor&lt;br /&gt;
requests from applications via the IOCTL interface and, when appropriate,&lt;br /&gt;
call OS/2 via the DevHlp interface to perform the bulk of the monitor work.&lt;br /&gt;
Finally, device drivers can grant processes access to the device's I/O&lt;br /&gt;
ports, to the device's mapped memory, and/or to special control areas in&lt;br /&gt;
the device driver's data area itself. Once again, processes ask for these&lt;br /&gt;
features via IOCTL; the device driver grants the requests via a DevHlp&lt;br /&gt;
dialog with OS/2. Some device drivers are degenerate; they don't actually&lt;br /&gt;
transfer data but exist solely to manage these other tasks. The screen&lt;br /&gt;
device driver is an example. Screen data is always written directly to the&lt;br /&gt;
display buffer by VIO, the application, or the presentation manager. The&lt;br /&gt;
screen device driver exists to grant direct access, manage screen groups,&lt;br /&gt;
and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.4  Dual Mode&lt;br /&gt;
The last key architectural feature of device drivers is that they are&lt;br /&gt;
written in dual mode: The driver code, both task time and interrupt time,&lt;br /&gt;
must be able to execute in protected mode and real mode. The process of&lt;br /&gt;
mode switching between protected mode and real mode is quite slow--about&lt;br /&gt;
800 microseconds. If we decreed that all device drivers run only in&lt;br /&gt;
protected mode and that service interrupts run only in protected mode, a&lt;br /&gt;
disk request from a real mode program might require six or more mode&lt;br /&gt;
switches--one for the request, and five for the interrupts--for a penalty&lt;br /&gt;
of almost 5 milliseconds. Consequently, device drivers must run in whatever&lt;br /&gt;
mode the CPU is in when the request comes along or the interrupt arrives.&lt;br /&gt;
     At first glance, this seems easy enough: As long as the device driver&lt;br /&gt;
refrains from computing its own segment selectors, it can execute in either&lt;br /&gt;
mode. The catch is that OS/2 may switch between modes at every call and/or&lt;br /&gt;
interrupt, and the addresses of code and data items are different in each&lt;br /&gt;
mode. A device driver might be called in protected mode with an address in&lt;br /&gt;
the client process's address space. When the &amp;quot;data ready&amp;quot; interrupt&lt;br /&gt;
arrives, however, the CPU may be running in real mode, and that client's&lt;br /&gt;
address is no longer valid--for two reasons. One, the segment selector part&lt;br /&gt;
of a memory address has a different meaning in real mode than it does in&lt;br /&gt;
protected mode; and, two, the client's selector was in the LDT, and the LDT&lt;br /&gt;
is invalid at interrupt time.&lt;br /&gt;
7. See the device driver reference manual for more details.&lt;br /&gt;
7 OS/2 helps device drivers deal with&lt;br /&gt;
addressing in a dual mode environment in three ways:&lt;br /&gt;
&lt;br /&gt;
     1.  Some addresses are the same in both modes and in either protected&lt;br /&gt;
         mode or real mode. The DevHlp entry point, the global infoseg&lt;br /&gt;
         address, the request packet address, and any addresses returned&lt;br /&gt;
         via the DevHlp GetDosVar function are valid at all times and in&lt;br /&gt;
         both modes.&lt;br /&gt;
&lt;br /&gt;
     2.  Although the segment selector value for the device driver's code&lt;br /&gt;
         and data segments is different in each mode, OS/2 loads the proper&lt;br /&gt;
         values into CS and DS before it calls the device driver's task-&lt;br /&gt;
         time or interrupt-time entry points. As long as a device driver is&lt;br /&gt;
         careful not to &amp;quot;remember&amp;quot; and reuse these values, it won't notice&lt;br /&gt;
         that they (possibly) change at every call.&lt;br /&gt;
&lt;br /&gt;
     3.  OS/2 provides a variety of DevHlp functions that allow a device&lt;br /&gt;
         driver to convert a selector:offset pair into a physical address&lt;br /&gt;
         and then later convert this physical address back into a&lt;br /&gt;
         selector:offset pair that is valid at that particular time. This&lt;br /&gt;
         allows device drivers to convert addresses that are outside their&lt;br /&gt;
         own segments into physical addresses and then, upon each task-time&lt;br /&gt;
         or interrupt-time call to the driver, convert that physical&lt;br /&gt;
         address back into one that is usable in the current mode. This&lt;br /&gt;
         avoids the problem of recording a selector:offset pair in protect&lt;br /&gt;
         mode and then trying to use it as a segment:offset pair in real&lt;br /&gt;
         mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2  Hard Errors&lt;br /&gt;
&lt;br /&gt;
Sometimes the system encounters an error that it can neither ignore nor&lt;br /&gt;
correct but which the user can correct. A classic example is the user&lt;br /&gt;
leaving ajar the door to the floppy drive; the system can do nothing to&lt;br /&gt;
access that floppy disk until someone closes the door. Such an error is&lt;br /&gt;
called a hard error. The term originated to describe an error that won't go&lt;br /&gt;
away when the operation is retried, but it also aptly describes the effort&lt;br /&gt;
involved in the design of OS/2 to deal with such errors.&lt;br /&gt;
     The manner in which MS-DOS handles hard errors is straightforward. In&lt;br /&gt;
our drive door example, MS-DOS discovers the problem when it is deep inside&lt;br /&gt;
the bowels of the system, communicating with the disk driver. The driver&lt;br /&gt;
reports the problem, and MS-DOS displays some text on the screen--the&lt;br /&gt;
infamous &amp;quot;Abort, Retry, Ignore?&amp;quot; message. Typically, the user fixes the&lt;br /&gt;
problem and replies; MS-DOS then takes the action specified by the user,&lt;br /&gt;
finishes its work, and returns to the application. Often, applications&lt;br /&gt;
didn't want the system to handle hard errors automatically. Perhaps they&lt;br /&gt;
were concerned about data integrity and wanted to be aware of a disk&lt;br /&gt;
writing problem, or they wanted to prevent the user from specifying&lt;br /&gt;
&amp;quot;Ignore,&amp;quot;&lt;br /&gt;
8. This response is classic. Sophisticated users understand the likely&lt;br /&gt;
consequences of such a reply, but most users would interpret &amp;quot;Ignore&amp;quot;&lt;br /&gt;
as &amp;quot;Make the problem go away&amp;quot;--an apparently ideal solution!&lt;br /&gt;
8 or they didn't want MS-DOS to write over their screen display&lt;br /&gt;
without their knowing. To handle these situations, MS-DOS lets applications&lt;br /&gt;
store the address of a hard error handler in the INT 24 vector; if a&lt;br /&gt;
handler is present, MS-DOS calls it instead of its own handler.&lt;br /&gt;
     The system is in an unusual state while processing an MS-DOS hard&lt;br /&gt;
error. The application originally calls MS-DOS via the INT 21 vector. MS-&lt;br /&gt;
DOS then calls several levels deep within itself, whereupon an internal MS-&lt;br /&gt;
DOS routine calls the hard error handler back in the application. Because&lt;br /&gt;
MS-DOS is not generally reentrant, the application cannot recall MS-DOS via&lt;br /&gt;
INT 21 at this point; doing so would mean that it has called MS-DOS twice&lt;br /&gt;
at the same time. The application probably needs to do screen and keyboard&lt;br /&gt;
I/O when handling the hard error, so MS-DOS was made partially reentrant.&lt;br /&gt;
The original call involves disk I/O, so MS-DOS can be reentered via a&lt;br /&gt;
screen/keyboard I/O call without problem.&lt;br /&gt;
     Several problems prevented us from adopting a similar scheme for OS/2.&lt;br /&gt;
First, unlike the single-tasking MS-DOS, OS/2 cannot suspend operations&lt;br /&gt;
while the operating system calls an application--a call that might not&lt;br /&gt;
return for a long time. Second, major technical and security problems are&lt;br /&gt;
involved with calling from ring 0 (the privileged kernel mode) to ring 3&lt;br /&gt;
(the application mode). Also, in the MS-DOS environment, deciding which&lt;br /&gt;
process was responsible for the operation that triggered the hard error is&lt;br /&gt;
easy: Only one application is running. OS/2 may have a hard time&lt;br /&gt;
determining which process to alert because more than one process may have&lt;br /&gt;
caused a disk FAT sector or a disk directory to be edited. The improved&lt;br /&gt;
buffering techniques employed by OS/2 may cause a hard error to occur at a&lt;br /&gt;
time when no process is doing any I/O. Finally, even if we solve all these&lt;br /&gt;
problems, the application that triggers the hard error may be running in a&lt;br /&gt;
background screen group and be unable to display a message or use the&lt;br /&gt;
keyboard. Even if the application is in the foreground screen group, it&lt;br /&gt;
can't use the screen and keyboard if it's not the process currently&lt;br /&gt;
controlling them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2.1  The Hard Error Daemon&lt;br /&gt;
This last problem yields a clue to the solution. OS/2 supports multiple&lt;br /&gt;
screen groups, and its screen group mechanism manages multiple simultaneous&lt;br /&gt;
use of the screen and the keyboard, keeping the current users--one in each&lt;br /&gt;
screen group--isolated from one another. Clearly, we need to use screen&lt;br /&gt;
groups to allow a hard error dialog to be completed with the user without&lt;br /&gt;
interfering with the current foreground application. Doing so solves the&lt;br /&gt;
problem of writing on another application's screen image and therefore&lt;br /&gt;
removes most of the need for notifying an application that a hard error has&lt;br /&gt;
occurred.&lt;br /&gt;
     Specifically, OS/2 always has running a process called the hard error&lt;br /&gt;
daemon. When a hard error occurs, OS/2 doesn't attempt to figure out which&lt;br /&gt;
process caused it; instead, it notifies the hard error daemon. The hard&lt;br /&gt;
error daemon performs a special form of screen group switch to the reserved&lt;br /&gt;
hard error screen group and then displays its message and reads its input.&lt;br /&gt;
Because the hard error daemon is the only process in this screen group,&lt;br /&gt;
screen and keyboard usage do not conflict. The previous foreground process&lt;br /&gt;
is now temporarily in the background; the screen group mechanism keeps it&lt;br /&gt;
at bay.&lt;br /&gt;
     Meanwhile, the process thread that encountered the hard error in the&lt;br /&gt;
kernel is blocked there, waiting for the hard error daemon to get a&lt;br /&gt;
response from the user. The thread that handles the hard error is never the&lt;br /&gt;
thread that caused the hard error, and the kernel is already fully&lt;br /&gt;
reentrant for different threads; so the hard error daemon thread is free to&lt;br /&gt;
call OS/2 at will. When the user corrects the problem and responds to the&lt;br /&gt;
hard error daemon, the hard error daemon sends the response back to the&lt;br /&gt;
kernel, which allows the thread that encountered the error to take the&lt;br /&gt;
specified action. That thread either retries the operation or produces an&lt;br /&gt;
error code; the hard error daemon returns the system to the original&lt;br /&gt;
screen group. The screen group code then does its usual trick of&lt;br /&gt;
restoring the screen image to its previous state. Figure 17-5 illustrates&lt;br /&gt;
the hard error handling sequence. A process thread encounters a hard error&lt;br /&gt;
while in the OS/2 kernel. The thread blocks at that point while the hard&lt;br /&gt;
error daemon's previously captured thread is released. The hard error&lt;br /&gt;
daemon performs a special modified screen switch at (1), displays its&lt;br /&gt;
message, gets the user's response, restores the application screen group at&lt;br /&gt;
(2), and reenters the OS/2 kernel. The response code is then passed to the&lt;br /&gt;
blocked application thread, which then resumes execution.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
             DosCall&lt;br /&gt;
   Process  ÄÄÄÄ¿                                        ÚÄÄÄÄÄÄÄÄ&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³  OS/2 kernel  ³                                        ³  Return   ³&lt;br /&gt;
³               ³ Hard                                   ³  to       ³&lt;br /&gt;
³               ³ error                                  ³  process  ³&lt;br /&gt;
³               ÀÄÄÄÄÄÄ´                      ÃÄÄÄ&amp;gt;Ä&amp;gt;ÄÄÄÄÙ           ³&lt;br /&gt;
³                                                &amp;lt; &amp;lt;                 ³&lt;br /&gt;
³                      ÃÄ¿                  ÚÄ´                      ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                         �                  �&lt;br /&gt;
                 returns ³                  ³ calls&lt;br /&gt;
                         ³                  ³&lt;br /&gt;
   Hard error daemon     ³ Display message  ³&lt;br /&gt;
                         ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                      (1)    Get response    (2)&lt;br /&gt;
 &lt;br /&gt;
                                    TimeÄÄÄÄÄÄ�&lt;br /&gt;
&lt;br /&gt;
Figure 17-5.  Hard error handling.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Although the most common cause of hard errors is a disk problem for&lt;br /&gt;
example, an open drive door or a medium error--other events that require&lt;br /&gt;
user intervention or user notification use the hard error mechanism. For&lt;br /&gt;
example, the volume management package (see 15.2 Media Volume Management)&lt;br /&gt;
uses the hard error mechanism to display its &amp;quot;Insert volume &amp;lt;name&amp;gt;&amp;quot;&lt;br /&gt;
messages. As I mentioned earlier, MS-DOS applications running in the&lt;br /&gt;
compatibility box can encounter problems, such as locked files, that they&lt;br /&gt;
can't understand. Rather than have these applications fail mysteriously,&lt;br /&gt;
OS/2 uses the hard error daemon mechanism to inform the user of the cause&lt;br /&gt;
of the real mode application's difficulties. Although the application&lt;br /&gt;
running in the compatibility box sees an operating system that acts like&lt;br /&gt;
MS-DOS, the operating system is actually OS/2. Because of this, hard errors&lt;br /&gt;
encountered by a real mode process are handled by an amalgam of the MS-DOS&lt;br /&gt;
INT 24 mechanism and the OS/2 hard error daemon. See Chapter 19, The 3X&lt;br /&gt;
Box.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2.2  Application Hard Error Handling&lt;br /&gt;
In some cases an application doesn't want the system to handle its hard&lt;br /&gt;
errors. For example, an application designed for unattended or remote&lt;br /&gt;
operation, such as a network server, may want to pass notification of hard&lt;br /&gt;
errors to a remote correspondent rather than hanging up forever with a&lt;br /&gt;
message on a screen that might not be read for hours. Another example is a&lt;br /&gt;
database program concerned about the integrity of its master file; it may&lt;br /&gt;
want to know about hard errors so that it can take some special action or&lt;br /&gt;
perhaps use an alternative master file on another device. OS/2 allows a&lt;br /&gt;
process to disable automatic hard error handling on a per file basis. Our&lt;br /&gt;
network example will want to disable hard error pop-ups for anything the&lt;br /&gt;
process does; our database example may want to disable hard error pop-ups&lt;br /&gt;
only for its master file, keeping their convenience for any other files&lt;br /&gt;
that it might access. When a hard error occurs on behalf of a process or a&lt;br /&gt;
handle that has hard error pop-ups disabled, OS/2 assumes that a FAIL&lt;br /&gt;
response was entered to a hypothetical hard error pop-up and returns to the&lt;br /&gt;
application with a special error code. The application must analyze the&lt;br /&gt;
code and take the necessary actions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==18  I/O Privilege Mechanism and Debugging/Ptrace==&lt;br /&gt;
&lt;br /&gt;
The earlier chapters of this book focused on the &amp;quot;captains and kings&amp;quot; of&lt;br /&gt;
the operating system world, the major architectural features. But like any&lt;br /&gt;
real world operating system, OS/2 contains a variety of miscellaneous&lt;br /&gt;
facilities that have to be there to get the work done. Although these&lt;br /&gt;
facilities may not be major elements in some architectural grand scheme,&lt;br /&gt;
they still have to obey the principles of the design religion. Two of them&lt;br /&gt;
are the I/O privilege mechanism and the debugging facility.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
18.1  I/O Privilege Mechanism&lt;br /&gt;
&lt;br /&gt;
Earlier I discussed the need for a mechanism that allows applications high-&lt;br /&gt;
speed direct access to devices. But the mechanism must control access in&lt;br /&gt;
such a way that the system's stability isn't jeopardized and in such a way&lt;br /&gt;
that applications don't fight over device control. OS/2 meets this&lt;br /&gt;
requirement with its I/O privilege mechanism. This facility allows a&lt;br /&gt;
process to ask a device driver for direct access to the device's I/O&lt;br /&gt;
ports and any dedicated or mapped memory locations it has. The I/O&lt;br /&gt;
privilege mechanism can be used directly by an application, which&lt;br /&gt;
necessarily makes it device dependent, or indirectly by a dynlink package.&lt;br /&gt;
The dynlink package can act as a kind of device driver; a new version can&lt;br /&gt;
be shipped with new hardware to maintain application compatibility. This&lt;br /&gt;
pseudo device driver is normally much faster than a true device driver&lt;br /&gt;
because of the customized procedural interface; not entering ring 0 and the&lt;br /&gt;
OS/2 kernel code saves much time.&lt;br /&gt;
     Unfortunately, this isn't a free lunch. Dynlink pseudo device drivers&lt;br /&gt;
can do everything that true device drivers can except handle interrupts.&lt;br /&gt;
Because hardware interrupts must be handled at ring 0, the handler must be&lt;br /&gt;
part of a true device driver. Frequently, a compromise is in order: Both a&lt;br /&gt;
dynlink package and a true device driver are provided. The true device&lt;br /&gt;
driver handles the interrupts, and the dynlink package does the rest of the&lt;br /&gt;
work. The two typically communicate via shared memory and/or private&lt;br /&gt;
IOCTLs. An example of such a compromise is the system KBD dynlink package.&lt;br /&gt;
The system VIO package doesn't need a device driver to handle interrupts&lt;br /&gt;
because the display device doesn't generate any.&lt;br /&gt;
     The two components in the OS/2 I/O access model are access to the&lt;br /&gt;
device's memory and access to its I/O ports. Granting and controlling&lt;br /&gt;
access to a device's mapped memory is easy because the 80286 protect mode&lt;br /&gt;
supports powerful memory management facilities. First, a process asks the&lt;br /&gt;
device driver for access to the device's memory, for example, to the memory&lt;br /&gt;
buffer of a CGA board. Typically, a dynlink package, rather than an&lt;br /&gt;
application, does this via the DosDevIOCtl call. If the device driver&lt;br /&gt;
approves the request, it asks OS/2 via the DevHlp interface to set up an&lt;br /&gt;
LDT memory descriptor to the proper physical memory locations. OS/2 returns&lt;br /&gt;
the resultant selector to the device driver, which returns it to the&lt;br /&gt;
calling process. This technique isn't limited to memory-mapped device&lt;br /&gt;
memory; device drivers can use it to allow their companion dynlink packages&lt;br /&gt;
direct access to a piece of the device driver's data segment. In this way,&lt;br /&gt;
a combination dynlink/device driver device interface can optimize&lt;br /&gt;
communication between the dynlink package and the device driver.&lt;br /&gt;
     Providing I/O port access to a process is more difficult because it is&lt;br /&gt;
supported more modestly by the 80286 processor. The 80286 uses its ring&lt;br /&gt;
protection mechanism to control I/O access; the system can grant code&lt;br /&gt;
running at a certain ring privilege access to all I/O ports, but it can't&lt;br /&gt;
grant access to only some I/O ports. It's too dangerous to grant an&lt;br /&gt;
application access to all I/O ports simply because it uses VIO and VIO&lt;br /&gt;
needs direct port access for the display adapter. This solution would mean&lt;br /&gt;
that OS/2's I/O space is effectively unprotected because almost all&lt;br /&gt;
programs use VIO or the presentation manager directly or indirectly.&lt;br /&gt;
     Instead, OS/2 was designed to allow, upon request from the device&lt;br /&gt;
driver, any code segments marked&lt;br /&gt;
1. This is done via a special command to the linker.&lt;br /&gt;
1 to execute at ring 2 to have I/O access.&lt;br /&gt;
The bad news is that access to all I/O ports must be granted&lt;br /&gt;
indiscriminately, but the good news is that the system is vulnerable to&lt;br /&gt;
program bugs only when those ring 2 segments are being executed. The&lt;br /&gt;
capabilities of ring 2 code, as it's called, are restricted: Ring 2 code&lt;br /&gt;
cannot issue dynlink calls to the system. This is partly a result of ring&lt;br /&gt;
architecture (supporting ring 2 system calls would require significant&lt;br /&gt;
additional overhead) and partly to discourage lazy programmers from&lt;br /&gt;
flagging their entire process as ring 2 to avoid sequestering their I/O&lt;br /&gt;
routines.&lt;br /&gt;
     As I said, in OS/2 version 1.0 the ring mechanism can restrict I/O&lt;br /&gt;
access only to a limited degree. Any malicious program and some buggy&lt;br /&gt;
programs can still damage system stability by manipulating the system's&lt;br /&gt;
peripherals. Furthermore, a real mode application can issue any I/O&lt;br /&gt;
instruction at any time. A future release of OS/2 that runs only on the&lt;br /&gt;
80386 processor will solve these problems. The 80386 hardware is&lt;br /&gt;
specifically designed to allow processes access to some I/O ports but not&lt;br /&gt;
to others through a bit map the system maintains. This map, which of course&lt;br /&gt;
the application cannot directly change, tells the 80386 which port&lt;br /&gt;
addresses may be accessed and which must be refused. This map applies&lt;br /&gt;
equally to protect mode and real mode applications.&lt;br /&gt;
2. Actually, to &amp;quot;virtual real mode&amp;quot; applications. This is&lt;br /&gt;
functionally the same as real mode on earlier processors.&lt;br /&gt;
2 OS/2 will use the&lt;br /&gt;
port addresses supplied by the device driver to allow access only to the&lt;br /&gt;
I/O ports associated with the device(s) to which the process has been&lt;br /&gt;
granted access. This release will not support application code segments&lt;br /&gt;
running at ring 2; any segments so marked will be loaded and run at ring 3.&lt;br /&gt;
The change will be invisible to all applications that use only the&lt;br /&gt;
proper I/O ports. Applications that request access to one device and then&lt;br /&gt;
use their I/O permissions to program another device will fail.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
18.2  Debugging/Ptrace&lt;br /&gt;
&lt;br /&gt;
Because OS/2 goes to a great deal of effort to keep one application from&lt;br /&gt;
interfering with another, special facilities were built to allow debugging&lt;br /&gt;
programs to manipulate and examine a debuggee (the process being debugged).&lt;br /&gt;
Because a debugger is available for OS/2 and writing your own is laborious,&lt;br /&gt;
we expect few programmers to write debuggers. This discussion is included,&lt;br /&gt;
nevertheless, because it further illuminates the OS/2 architectural&lt;br /&gt;
approach.&lt;br /&gt;
     The first concern of a debugger is that it be able to read and write&lt;br /&gt;
the debuggee's code and data segments as well as intercept traps, signals,&lt;br /&gt;
breakpoints, and the like. All these capabilities are strictly in the&lt;br /&gt;
domain of OS/2, so OS/2 must &amp;quot;export&amp;quot; them to the debugger program. A&lt;br /&gt;
second concern is system security: Obviously, the debug interface provides&lt;br /&gt;
a golden opportunity for &amp;quot;cracker&amp;quot; programs to manipulate any other&lt;br /&gt;
program, thereby circumventing passwords, encryption, or any other&lt;br /&gt;
protection scheme. OS/2 prevents this by requiring that the debuggee&lt;br /&gt;
process be flagged as a debug target when it is initially executed; a&lt;br /&gt;
debugger can't latch onto an already-running process. Furthermore, when&lt;br /&gt;
secure versions of OS/2 are available, processes executed under control of&lt;br /&gt;
a debugger will be shorn of any permissions they might have that are in&lt;br /&gt;
excess of those owned by the debugger.&lt;br /&gt;
     Before we examine the debugging interface, we should digress for a&lt;br /&gt;
moment and discuss the OS/2 approach to forcing actions upon threads and&lt;br /&gt;
processes. Earlier I described the process of kernel execution. I mentioned&lt;br /&gt;
that when a process thread makes a kernel request that thread itself enters&lt;br /&gt;
kernel mode and services its own request. This arrangement simplified the&lt;br /&gt;
design of the kernel because a function is coded to perform one action for&lt;br /&gt;
one client in a serial, synchronous fashion. Furthermore, nothing is ever&lt;br /&gt;
forced on a thread that is in kernel mode; any action taken on a thread in&lt;br /&gt;
kernel mode is taken by that thread itself. For example, if a process is to&lt;br /&gt;
be killed and one of its threads is in kernel mode, OS/2 doesn't terminate&lt;br /&gt;
that thread; it sets a flag that says, &amp;quot;Please kill yourself at your&lt;br /&gt;
earliest convenience.&amp;quot; Consequently, OS/2 doesn't need special code to&lt;br /&gt;
enumerate and release any internal flags or resources that a killed kernel&lt;br /&gt;
mode thread might leave orphaned, and in general no thread need&lt;br /&gt;
&amp;quot;understand&amp;quot; the state of any other. The thread to be killed cleans itself&lt;br /&gt;
up, releasing resources, flags, and whatever before it obligingly commits&lt;br /&gt;
suicide.&lt;br /&gt;
     But when is the thread's &amp;quot;earliest convenience&amp;quot;? Thread termination is&lt;br /&gt;
a forced event, and all threads check for any pending forced events&lt;br /&gt;
immediately before they leave kernel mode and reenter application mode.&lt;br /&gt;
This transition takes place frequently: not only when a system call returns&lt;br /&gt;
to the calling application, but also each time a context switch takes&lt;br /&gt;
place.&lt;br /&gt;
     Although it may appear that forced events might languish unprocessed,&lt;br /&gt;
they are serviced rapidly. For example, when a process issues a DosKill&lt;br /&gt;
function on its child process, each thread in the child process is marked&lt;br /&gt;
&amp;quot;kill yourself.&amp;quot; Because the parent process had the CPU, obviously, when it&lt;br /&gt;
issued the DosKill, each of the child's threads is in kernel mode, either&lt;br /&gt;
because the thread is working on a system call or because it was&lt;br /&gt;
artificially placed in kernel mode when the scheduler preempted it. Before&lt;br /&gt;
any of those now-marked threads can execute even a single instruction of&lt;br /&gt;
the child application's code, they must go through OS/2's dispatch routine.&lt;br /&gt;
The &amp;quot;kill yourself&amp;quot; flag is noted, and the thread terminates itself instead&lt;br /&gt;
of returning to application mode. As you can see, the final effect of this&lt;br /&gt;
approach is far from slow: The DosKill takes effect immediately--not one&lt;br /&gt;
more instruction of the child process is executed.&lt;br /&gt;
3. Excepting any SIGKILL handlers and&lt;br /&gt;
DosExitList handlers, of course.&lt;br /&gt;
3 The only significant&lt;br /&gt;
delay in recognizing a forced event occurs when a system call takes a long&lt;br /&gt;
time to process. OS/2 is not very CPU bound, so any call that takes a &amp;quot;long&lt;br /&gt;
time&amp;quot; (1 second or more) must be blocked for most of that time.&lt;br /&gt;
     When a kernel thread issues a block call for an event that might take&lt;br /&gt;
a long time--such as waiting for a keystroke or waiting for a semaphore to&lt;br /&gt;
clear--it uses a special form of block called an interruptible block. When&lt;br /&gt;
OS/2 posts a force flag against a thread, it checks to see if that thread&lt;br /&gt;
is blocking interruptibly. If it is, that thread is released from its block&lt;br /&gt;
with a special code that says, &amp;quot;You were awakened not because the event has&lt;br /&gt;
come to pass but because a forced event was posted.&amp;quot; That thread must then&lt;br /&gt;
finish the system call quickly (generally by declaring an error) so that&lt;br /&gt;
the thread can go through the dispatch routine and recognize the force&lt;br /&gt;
flag. I described this mechanism in Chapter 12 when I talked about another&lt;br /&gt;
kind of forced event--the OS/2 signal mechanism. An incoming signal is a&lt;br /&gt;
forced event for a process's thread 1; it therefore receives the same&lt;br /&gt;
timely response and has the same effect of aborting a slow system call.&lt;br /&gt;
     I've gone through this long discussion of forced events and how&lt;br /&gt;
they're processed because the internal debugging facility is based on one&lt;br /&gt;
giant special forced event. When a process is placed in debug state, a&lt;br /&gt;
trace force flag is permanently set for the initial thread of that process&lt;br /&gt;
and for any other threads it creates. When any of those threads are in&lt;br /&gt;
kernel mode--and they enter kernel mode whenever anything of interest takes&lt;br /&gt;
place--they execute the debuggee half of the OS/2 trace code. The debugger&lt;br /&gt;
half is executed by a debugger thread that issues special DosPtrace calls;&lt;br /&gt;
the two halves of the package communicate through a shared memory area&lt;br /&gt;
built into OS/2.&lt;br /&gt;
     When the debuggee encounters a special event (for example, a Ctrl-C&lt;br /&gt;
signal or a GP fault), the trace force event takes precedence over any&lt;br /&gt;
other, and the debuggee's thread executes the debuggee half of the&lt;br /&gt;
DosPtrace code. This code writes a record describing the event into a&lt;br /&gt;
communications buffer, wakes up the debugger thread, which is typically&lt;br /&gt;
blocked in the debugger's part of the DosPtrace code, and blocks, awaiting&lt;br /&gt;
a reply. The debugger's thread wakes up and returns to the debugger with&lt;br /&gt;
the event information. When the debugger recalls DosPtrace with a command,&lt;br /&gt;
the command is written into the communications area, and the debuggee is&lt;br /&gt;
awakened to read and obey. The command might be &amp;quot;Resume normal execution,&amp;quot;&lt;br /&gt;
&amp;quot;Process the event as you normally would,&amp;quot; or &amp;quot;Give me the contents of&lt;br /&gt;
these locations in your address space,&amp;quot; whereupon the debuggee thread&lt;br /&gt;
replies and remains in the DosPtrace handler.&lt;br /&gt;
     This approach is simple to implement, does the job well, and takes&lt;br /&gt;
advantage of existing OS/2 features. For example, no special code is needed&lt;br /&gt;
to allow the debugger access to the debuggee's address space because the&lt;br /&gt;
debuggee itself, unwittingly in the DosPtrace code, reads and writes its&lt;br /&gt;
own address space. Credit goes to the UNIX ptrace facility, upon which this&lt;br /&gt;
facility was closely modeled.&lt;br /&gt;
     Finally, here are a few incidental facts that the readers of this&lt;br /&gt;
book, being likely users of debugging facilities, should know. OS/2&lt;br /&gt;
maintains a linkage between the debugger process and the debuggee process.&lt;br /&gt;
When the debugger process terminates, the debuggee process also terminates&lt;br /&gt;
if it has not already done so. The debuggee program need not be a direct&lt;br /&gt;
child of the debugger; when the debugger process makes its initial&lt;br /&gt;
DosPtrace call, OS/2 connects it to the last process that was executed with&lt;br /&gt;
the special tracing option. If a process is executed with the tracing&lt;br /&gt;
option but no debugger process subsequently issues a DosPtrace function,&lt;br /&gt;
the jilted debuggee process is terminated in about two minutes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==19  The 3x Box==&lt;br /&gt;
&lt;br /&gt;
It's of critical importance that OS/2 do a good job of running existing MS-&lt;br /&gt;
DOS applications, but as we've discussed, this is a difficult task. To&lt;br /&gt;
offer the official MS-DOS interfaces under OS/2 and therefore claim upward&lt;br /&gt;
compatibility would be easy; unfortunately, few popular applications would&lt;br /&gt;
run successfully in such an environment. Most sophisticated applications&lt;br /&gt;
take direct control of the machine environment and use MS-DOS for tasks the&lt;br /&gt;
application doesn't want to bother with, such as file I/O, keyboard&lt;br /&gt;
buffering, and so forth. If we're to run existing applications&lt;br /&gt;
successfully, we must provide a close facsimile to a real mode PC running&lt;br /&gt;
MS-DOS in all respects, not just the INT 21 program interface.&lt;br /&gt;
     OS/2 provides such a highly compatible environment, called the real&lt;br /&gt;
mode screen group, the compatibility box, or simply the 3x box. The 3x box&lt;br /&gt;
is an environment that emulates an 8086-based PC running MS-DOS version&lt;br /&gt;
3.3.&lt;br /&gt;
1. For OS/2 version 1.0, the 3x box is compatible with MS-DOS&lt;br /&gt;
version 3.3.&lt;br /&gt;
1 MS-DOS programs execute in real mode, and because emulating real&lt;br /&gt;
mode from within protected mode is prohibitively slow, OS/2 physically&lt;br /&gt;
switches into real mode to execute MS-DOS applications. Because MS-DOS&lt;br /&gt;
programs are well aware of the MS-DOS memory layout, this layout is&lt;br /&gt;
replicated for the OS/2 3x box. The first N bytes (typically 640 KB) are&lt;br /&gt;
reserved for the exclusive use of the low-memory parts of OS/2 and the 3x&lt;br /&gt;
box; protected mode applications never use any of this memory. Thus,&lt;br /&gt;
programs that are careless about memory allocation or that make single-&lt;br /&gt;
tasking assumptions about the availability of memory can run in a&lt;br /&gt;
multitasking environment. Figure 19-1 illustrates the OS/2 memory layout.&lt;br /&gt;
The low bytes of memory are reserved for the device drivers and portions of&lt;br /&gt;
OS/2 that must run in real mode. The remainder of the space, up to the&lt;br /&gt;
RMSIZE value, is dedicated to the 3x box. Memory from 640 KB to 1 MB is&lt;br /&gt;
reserved for ROMs and video display buffers. Memory above 1 MB holds the&lt;br /&gt;
remainder of OS/2 and all protect mode applications. Nonswappable, fixed&lt;br /&gt;
segments are kept at one end of this memory to reduce fragmentation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      N ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ³    Protect     ³&lt;br /&gt;
        ³      mode      ³&lt;br /&gt;
        ³  applications  ³&lt;br /&gt;
        ³   (movable)    ³&lt;br /&gt;
        &amp;lt;                &amp;lt;&lt;br /&gt;
         &amp;gt;                &amp;gt;&lt;br /&gt;
        &amp;lt;                &amp;lt;&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³     Fixed      ³&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³      OS/2      ³&lt;br /&gt;
   1 MB ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
 640 KB ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ³      Real      ³&lt;br /&gt;
        ³      mode      ³&lt;br /&gt;
        ³  application   ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³   Additional   ³&lt;br /&gt;
        ³ device drivers ³&lt;br /&gt;
  ~100k ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ³    Low OS/2    ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
   90:0 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³    Bios ROM    ³&lt;br /&gt;
      0 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 19-1.  System memory layout.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 uses the screen group mechanism to provide a user interface to&lt;br /&gt;
the 3x box. One screen group is designated the real mode screen group;&lt;br /&gt;
automatically, OS/2 executes COMMAND.COM in that screen group when it is&lt;br /&gt;
first selected. The user accesses the real mode environment by selecting&lt;br /&gt;
that screen group and returns to the protected mode environment by&lt;br /&gt;
selecting another screen group. OS/2 version 1.0 supports a single real&lt;br /&gt;
mode screen group because the real mode compatibility is provided by&lt;br /&gt;
actually running the application in real mode. Thus, only one 640 KB area&lt;br /&gt;
is reserved for all real mode applications, and adjudicating between the&lt;br /&gt;
conflicting hardware manipulations of multiple real mode applications&lt;br /&gt;
without any assistance from the 80286 microprocessor hardware would be&lt;br /&gt;
prohibitively difficult. The 80386 microprocessor, however, provides a&lt;br /&gt;
special hardware facility called virtual 8086 mode that will allow a future&lt;br /&gt;
release of OS/2 to support multiple real mode screen groups, but only on an&lt;br /&gt;
80386-based machine.&lt;br /&gt;
     The operating system that services the 3x application's INT 21&lt;br /&gt;
requests is not an exact copy of MS-DOS; it's actually a low-memory&lt;br /&gt;
extension of OS/2 itself. Because OS/2 is derived from MS-DOS, OS/2&lt;br /&gt;
executes MS-DOS functions in a manner identical to that of the real MS-DOS.&lt;br /&gt;
OS/2 supports the non-MS-DOS functions mentioned above by staying out of&lt;br /&gt;
the way as much as possible and letting the 3x application &amp;quot;party hearty&amp;quot;&lt;br /&gt;
with the hardware. For example, hooking most interrupt vectors is&lt;br /&gt;
supported, as is hooking INT 21 and the ROM BIOS INT vectors. The ROM BIOS&lt;br /&gt;
calls themselves are fully supported. Frequently, staying out of the way is&lt;br /&gt;
not as easy as it may sound. For example, OS/2 must intercept and monitor&lt;br /&gt;
real mode calls made to the disk driver part of the ROM BIOS so that it can&lt;br /&gt;
prevent conflict with ongoing, asynchronous protect-mode disk I/O. OS/2 may&lt;br /&gt;
find it necessary to momentarily block a real mode application's BIOS call&lt;br /&gt;
until the protect mode device driver can release the hardware. Once the&lt;br /&gt;
real mode application is in the BIOS, the same interlock mechanism prevents&lt;br /&gt;
the protect mode device driver from entering the disk I/O critical&lt;br /&gt;
section.&lt;br /&gt;
     Hard errors encountered by the real mode application are handled by a&lt;br /&gt;
hybrid of the OS/2 hard error daemon and the 3x box INT 24 mechanism in a&lt;br /&gt;
three-step process, as follows:&lt;br /&gt;
&lt;br /&gt;
     1:  Hard error codes caused by events unique to the OS/2 environment--&lt;br /&gt;
         such as a volume manager media change request--activate the hard&lt;br /&gt;
         error daemon so that the user can get an accurate explanation&lt;br /&gt;
         of the problem. The user's response to the hard error is saved&lt;br /&gt;
         but is not yet acted upon. Hard error codes, which are also&lt;br /&gt;
         present in MS-DOS version 3.3, skip this step and start at&lt;br /&gt;
         step 2.&lt;br /&gt;
&lt;br /&gt;
     2:  If the real mode application has installed its own hard error&lt;br /&gt;
         handler via the INT 24 vector, it is called. If step 1 was&lt;br /&gt;
         skipped, the code should be known to the application, and it is&lt;br /&gt;
         presented unchanged. If step 1 was taken, the error code is&lt;br /&gt;
         transformed to ERROR_I24_GEN_FAILURE for this step. The response&lt;br /&gt;
         returned by the program, if valid for this class of hard error, is&lt;br /&gt;
         acted upon. This means that hard errors new to OS/2 can actually&lt;br /&gt;
         generate two pop-ups--one from the hard error daemon with an&lt;br /&gt;
         accurate message and one from the application itself with a&lt;br /&gt;
         General Failure message. This allows the user to understand the&lt;br /&gt;
         true cause of the hard error and yet notifies the application that&lt;br /&gt;
         a hard error has occurred. In such a case, the action specified by&lt;br /&gt;
         the application when it returned from its own hard error handler&lt;br /&gt;
         is the one taken, not the action specified by the user to the&lt;br /&gt;
         initial hard error daemon pop-up.&lt;br /&gt;
&lt;br /&gt;
3:       If the real mode application has not registered its hard error&lt;br /&gt;
         handler via the INT 24 mechanism, OS/2 provides a default handler&lt;br /&gt;
         that uses the hard error daemon. If step 1 was taken and the hard&lt;br /&gt;
         error daemon has already run, it is not run again; OS/2 takes the&lt;br /&gt;
         action specified in response to the hard error pop-up that was&lt;br /&gt;
         displayed. If step 1 was not taken because the hard error code is&lt;br /&gt;
         MS-DOS 3.x compatible and if step 2 was not taken because the&lt;br /&gt;
         application did not provide its own handler, then OS/2 activates&lt;br /&gt;
         the hard error daemon in step 3 to present the message and receive&lt;br /&gt;
         a reply.&lt;br /&gt;
&lt;br /&gt;
     The 3x box supports only MS-DOS functionality; no new OS/2 features&lt;br /&gt;
are available to 3x box applications--no new API, no multiple threads, no&lt;br /&gt;
IPC, no semaphores, and so on.&lt;br /&gt;
2. There are two exceptions. The OPEN function was&lt;br /&gt;
extended, and an INT 2F multiplex function was added to notify&lt;br /&gt;
real mode applications of screen switches.&lt;br /&gt;
2 This decision was made for two reasons.&lt;br /&gt;
First, although any real mode application can damage the system's&lt;br /&gt;
stability, allowing real mode applications to access some protect mode&lt;br /&gt;
features may aggravate the problem. For example, terminate and stay&lt;br /&gt;
resident programs may manipulate the CPU in such a way as to make it&lt;br /&gt;
impossible for a real mode application to protect a critical section with&lt;br /&gt;
semaphores and yet guarantee that it won't leave the semaphore orphaned.&lt;br /&gt;
Second, because OS/2 has only one real mode box and it labors under a 640&lt;br /&gt;
KB memory ceiling, it doesn't make sense to develop new real mode&lt;br /&gt;
applications that use new OS/2 functions and thus require OS/2.&lt;br /&gt;
     The 3x box emulation extends to interrupts. OS/2 continues to context&lt;br /&gt;
switch the CPU when the 3x box is active; that is, the 3x box application&lt;br /&gt;
is the foreground application. Because the foreground process receives a&lt;br /&gt;
favorable priority, its CPU is preempted only when a time-critical protect&lt;br /&gt;
mode application needs to run or when the real mode application blocks. If&lt;br /&gt;
the CPU is running a protect mode application when a device interrupt comes&lt;br /&gt;
in, OS/2 switches to real mode so that a real mode application that is&lt;br /&gt;
hooking the interrupt vectors can receive the interrupt in real mode. When&lt;br /&gt;
the interrupt is complete, OS/2 switches back to protected mode and resumes&lt;br /&gt;
the protected application.&lt;br /&gt;
     Although protected mode applications can continue to run when the 3x&lt;br /&gt;
box is in the foreground, the reverse is not true. When the 3x box screen&lt;br /&gt;
group is in the background, all 3x box execution is suspended, including&lt;br /&gt;
interrupts. Unlike protected mode applications, real mode applications&lt;br /&gt;
cannot be trusted to refrain from manipulating the screen hardware when&lt;br /&gt;
they are in a background screen group. Normally, a real mode application&lt;br /&gt;
doesn't notice its suspension when it's in background mode; the only thing&lt;br /&gt;
it might notice is that the system time-of-day has apparently &amp;quot;jumped&lt;br /&gt;
forward.&amp;quot; Because mode switching is a slow process and leaves interrupts&lt;br /&gt;
disabled for almost 1 millisecond, mode switching can cause interrupt&lt;br /&gt;
overruns on fast devices such as serial ports. The best way to deal with&lt;br /&gt;
this is to switch the real mode application into a background screen group;&lt;br /&gt;
with no more real mode programs to execute, OS/2 does no further mode&lt;br /&gt;
switching.&lt;br /&gt;
     Some OS/2 utility programs such as FIND are packaged as Family API&lt;br /&gt;
applications. A single binary can run in both protected mode and real mode,&lt;br /&gt;
and the user is saved the inconvenience of switching from real mode to&lt;br /&gt;
protected mode to do simple utility functions. This works well for simple&lt;br /&gt;
utility programs without full screen or graphical interfaces and for&lt;br /&gt;
programs that have modest memory demands and that in other ways have little&lt;br /&gt;
need of OS/2's extended capabilities. Obviously, if an application can make&lt;br /&gt;
good use of OS/2's protect mode features, it should be written to be&lt;br /&gt;
protect mode only so that it can take advantage of those features.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==20  Family API==&lt;br /&gt;
&lt;br /&gt;
When a new release of a PC operating system is announced, application&lt;br /&gt;
writers face a decision: Should they write a new application to use some of&lt;br /&gt;
the new features or should they use only the features in earlier releases?&lt;br /&gt;
If they go for the sexy new features, their product might do more, be&lt;br /&gt;
easier to write, or be more efficient; but when the program hits the&lt;br /&gt;
market, only 10 percent of existing PCs may be running the new release. Not&lt;br /&gt;
all of the existing machines have the proper processor to be able to run&lt;br /&gt;
the new system, and, of those, many of their users haven't seen the need to&lt;br /&gt;
go to the expense and endure the hassle of upgrading their operating&lt;br /&gt;
system. If it's viable to write the new application so that it requires&lt;br /&gt;
only the old operating system (and therefore runs in compatibility mode&lt;br /&gt;
under the new operating system), then it's tempting to do so. Even though&lt;br /&gt;
the product is not as good as it might be, it can sell to 100 percent of&lt;br /&gt;
the installed base of machines--10 times as many as it would if it required&lt;br /&gt;
the new operating system.&lt;br /&gt;
     And here you have the classic &amp;quot;catch-22&amp;quot; of software standards: If&lt;br /&gt;
users don't see a need, they won't use the new system. If they don't use&lt;br /&gt;
the new system, applications will not be written explicitly for it; so the&lt;br /&gt;
users never see a need. Without some way to prime the pump, it will be a&lt;br /&gt;
long time before a comprehensive set of applications are available that use&lt;br /&gt;
the new system's features.&lt;br /&gt;
     OS/2 tackles this problem in several ways. The software bundled with&lt;br /&gt;
OS/2 runs in protected mode, and OS/2 attempts to include as much&lt;br /&gt;
additional user function as possible to increase its value to a user who&lt;br /&gt;
initially owns no protected mode applications. The most important user&lt;br /&gt;
acceptance feature of OS/2, however, is called Family API. Family API is a&lt;br /&gt;
special subset of the OS/2 protected mode API. Using special tools included&lt;br /&gt;
in the OS/2 developer's kit, you can build applications that use only the&lt;br /&gt;
Family API. The resultant .EXE file(s) run unchanged in OS/2 protect mode&lt;br /&gt;
or on an 8086 running MS-DOS 2.x or 3.x.&lt;br /&gt;
1. Of course, they also run in the MS-DOS 3.x compatible screen group&lt;br /&gt;
under OS/2; but except for convenience utilities, it's generally a&lt;br /&gt;
waste to dedicate the one real mode screen group to running an appl-&lt;br /&gt;
cation that could run in any of the many protect mode screen groups.&lt;br /&gt;
1 Thus, developers don't have to&lt;br /&gt;
choose between writing applications that are OS/2 protect mode and writing&lt;br /&gt;
applications that are MS-DOS compatible; they can use the Family API&lt;br /&gt;
mechanism and do both. Your applications will run as protected mode&lt;br /&gt;
applications under OS/2 and as MS-DOS applications under a true MS-DOS&lt;br /&gt;
system.&lt;br /&gt;
     Clearly, the Family API is a noteworthy feature. It offers some OS/2&lt;br /&gt;
functions, together with the dynamic link system interface, to programs&lt;br /&gt;
that run under MS-DOS without a copy of OS/2 anywhere in sight. It does&lt;br /&gt;
this by providing an OS/2 compatibility library that accepts the OS/2&lt;br /&gt;
system interface calls and implements them itself, calling the underlying&lt;br /&gt;
MS-DOS system via INT 21 as necessary. This information should give you a&lt;br /&gt;
big head start in figuring out which OS/2 functions are included in the&lt;br /&gt;
Family API: Clearly all functions that have similar INT 21 functions--such&lt;br /&gt;
as DosOpen, DosRead, and DosAllocSeg--are supported. Also present are&lt;br /&gt;
functions, such as DosSubAlloc, that can be supported directly by the&lt;br /&gt;
special Family API library. Features that are extremely difficult to&lt;br /&gt;
support in a true MS-DOS environment, such as multiple threads and&lt;br /&gt;
asynchronous I/O, are not present in the Family API.&lt;br /&gt;
     Where does this library come from? And how does it get loaded by MS-&lt;br /&gt;
DOS to satisfy the OS/2 executable's dynlink requests? It's all done with&lt;br /&gt;
mirrors, as the expression goes, and the &amp;quot;mirrors&amp;quot; must be built into the&lt;br /&gt;
application's .EXE file because that file is all that's present when a&lt;br /&gt;
Family API application is executed under MS-DOS. Figure 20-1 shows the&lt;br /&gt;
layout of a Family API .EXE file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         Family API&lt;br /&gt;
            .EXE&lt;br /&gt;
     ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³&lt;br /&gt;
     ³°°°MS-DOS 3.x°°°°³&lt;br /&gt;
     ³°°°.EXE header°ÄÄÅÄÄ¿&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³  ³&lt;br /&gt;
     ³°°°Family API°°°°³  ³ Shaded area is&lt;br /&gt;
     ³°°°°°loader°°°°°°³  ³ read by MS-DOS as&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³ a real mode&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³  ³ application&lt;br /&gt;
     ³°°°Family API°°°°³  ³&lt;br /&gt;
     ³°°°°°library°°°°°³  ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´�ÄÙ&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³      OS/2       ³&lt;br /&gt;
     ³   .EXE header   ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³      OS/2       ³&lt;br /&gt;
     ³   application   ³&lt;br /&gt;
     ³    segments     ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³     Dynlink     ³&lt;br /&gt;
     ³      names      ³&lt;br /&gt;
     ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 20-1.  Family API executable (.EXE) format.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 needed to define a new .EXE file because the existing MS-DOS .EXE&lt;br /&gt;
file format contained too little information for the OS/2 protect mode&lt;br /&gt;
segmented environment. Because, as we've discussed, the 8086 memory&lt;br /&gt;
architecture is--despite the terminology normally used--a linear memory&lt;br /&gt;
architecture, the MS-DOS .EXE format described only a single hunk of memory&lt;br /&gt;
that was to be loaded contiguously. OS/2 needs each segment described&lt;br /&gt;
separately, with information on its status: read only, code or data, demand&lt;br /&gt;
load or preload, and so on. Naturally, OS/2 also needs a .EXE format with&lt;br /&gt;
special records to describe loadtime dynamic links. This new .EXE format&lt;br /&gt;
was defined so that its initial bytes look exactly like those of the old&lt;br /&gt;
MS-DOS .EXE file header. A special flag bit is set in this fake .EXE&lt;br /&gt;
 header that is ignored by all releases of MS-DOS but that OS/2 recognizes&lt;br /&gt;
to mean &amp;quot;It's not true. I'm really an OS/2 .EXE file. Seek to this location&lt;br /&gt;
to find the true, new-style .EXE header.&amp;quot;&lt;br /&gt;
     When an MS-DOS system is told to load this .EXE file, it sees and&lt;br /&gt;
believes the old .EXE file header. This header does not describe the&lt;br /&gt;
application itself but a body of special code built into the .EXE file&lt;br /&gt;
before the actual application's code: the Family API loader and library. In&lt;br /&gt;
other words, to MS-DOS this .EXE file looks like a valid, executable&lt;br /&gt;
program, and that program is the Family API loader and library. The Family&lt;br /&gt;
API loader and library are loaded into memory, and execution begins. MS-DOS&lt;br /&gt;
doesn't load in the body of the application itself because it wasn't&lt;br /&gt;
described as part of the load image in the special MS-DOS .EXE file header.&lt;br /&gt;
As soon as it starts to execute, the Family API loader begins reading in&lt;br /&gt;
the application's segments, performs a loader's general relocation chores,&lt;br /&gt;
and fixes up dynlink references to the proper entry points in the Family&lt;br /&gt;
API library package. When the application is loaded, the Family API loader&lt;br /&gt;
block moves the application to its final execution address, which overlays&lt;br /&gt;
most of the Family API loader to reclaim that space, and execution&lt;br /&gt;
begins.&lt;br /&gt;
     All OS/2 .EXE files have this fake MS-DOS .EXE format header. In non-&lt;br /&gt;
Family API executables, the Family API loader and library are missing, and&lt;br /&gt;
by default the header describes an impossibly big MS-DOS executable. Should&lt;br /&gt;
the application be accidentally run under a non-OS/2 system or in the OS/2&lt;br /&gt;
compatibility screen group, MS-DOS will refuse to load the program.&lt;br /&gt;
Optionally, the programmer can link in a small stub program that goes where&lt;br /&gt;
the Family API loader would and that prints a more meaningful error&lt;br /&gt;
message. As we said earlier, the old-style .EXE headers on the front of the&lt;br /&gt;
file contain a flag bit to alert OS/2 to the presence of a new-style .EXE&lt;br /&gt;
header further into the file. Because this header doesn't describe the&lt;br /&gt;
Family API loader and library parts of the file, OS/2 ignores their&lt;br /&gt;
presence when it loads a Family API application in protected mode; the&lt;br /&gt;
application's dynlink references are fixed up to the normal dynlink&lt;br /&gt;
libraries, and the Family API versions of those libraries are ignored.&lt;br /&gt;
     There Ain't No Such Thing As A Free Lunch, and the same unfortunately&lt;br /&gt;
applies to the Family API mechanism. First, although the Family API allows&lt;br /&gt;
dynlink calls to be used in an MS-DOS environment, this is not true&lt;br /&gt;
dynlinking; it's quasi dynlinking. Obviously, runtime dynlinking is not&lt;br /&gt;
supported, but even loadtime dynlinking is special because the dynlink&lt;br /&gt;
target library is bound into the .EXE file. One of the advantages of&lt;br /&gt;
dynlinks is that the target code is not part of the .EXE file and can&lt;br /&gt;
therefore be changed and upgraded without changing the .EXE file. This is&lt;br /&gt;
not true of the dynlink emulation library used by the Family API because&lt;br /&gt;
it is built into the .EXE file. Fortunately, this disadvantage isn't&lt;br /&gt;
normally a problem. Dynlink libraries are updated either to improve&lt;br /&gt;
their implementation or to add new features. The Family API library can't&lt;br /&gt;
be improved very much because its environment--MS-DOS--is limited and&lt;br /&gt;
unchanging. If new Family API features were added, loading that new library&lt;br /&gt;
with preexisting Family API .EXE files would make no sense; those programs&lt;br /&gt;
wouldn't be calling the new features.&lt;br /&gt;
     A more significant drawback is the size and speed hit that the Family&lt;br /&gt;
API introduces. Clearly, the size of a Family API .EXE file is extended by&lt;br /&gt;
the size of the Family API loader and the support library. The tools used&lt;br /&gt;
to build Family API executables include only those library routines used by&lt;br /&gt;
the program, but even so the library and the loader add up to a nontrivial&lt;br /&gt;
amount of memory--typically 10 KB to 14 KB in the .EXE file and perhaps&lt;br /&gt;
9KB (the loader is not included) in RAM. Finally, loading a Family API&lt;br /&gt;
application under MS-DOS is slower than loading a true MS-DOS .EXE file.&lt;br /&gt;
Comparing loadtime against the loadtime of MS-DOS is tough for any&lt;br /&gt;
operating system because loading faster than MS-DOS is difficult. The .EXE&lt;br /&gt;
file consists of a single lump of contiguous data that can be read into&lt;br /&gt;
memory in a single disk read operation. A relocation table must also be&lt;br /&gt;
read, but it's typically very small. It's hard for any system to be faster&lt;br /&gt;
than this. Clearly, loading a Family API application is slower because the&lt;br /&gt;
loader and library must be loaded, and then they must, a segment at a time,&lt;br /&gt;
bring in the body of the application.&lt;br /&gt;
     Although the Family API makes dual environment applications possible,&lt;br /&gt;
it can't totally hide from an application the difference between the MS-DOS&lt;br /&gt;
3.x and the OS/2 execution environment. For example, the Family API&lt;br /&gt;
supports only the DosFindFirst function for a single search handle at a&lt;br /&gt;
time. An application that wants to perform multiple directory searches&lt;br /&gt;
simultaneously should use DosGetMachineMode to determine its environment&lt;br /&gt;
and then use the unrestricted DosFindFirst function if running in protect&lt;br /&gt;
mode or use the INT 21 functions if running in real mode. Likewise, an&lt;br /&gt;
application that wants to manipulate printer data needs to contain version-&lt;br /&gt;
specific code to hook INT 17 or to use device monitors, depending on the&lt;br /&gt;
environment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Part III  The Future&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==21  The Future==&lt;br /&gt;
&lt;br /&gt;
This chapter is difficult to write because describing the second version of&lt;br /&gt;
OS/2 becomes uninteresting when that version is released. Furthermore,&lt;br /&gt;
preannouncing products is bad practice; the trade-offs between schedule and&lt;br /&gt;
customer demand can accelerate the inclusion of some features and postpone&lt;br /&gt;
others, often late in the development cycle. If we talk explicitly about&lt;br /&gt;
future features, developers may plan their work around the availability of&lt;br /&gt;
those features and be left high and dry if said features are postponed. As&lt;br /&gt;
a result, this chapter is necessarily vague about both the functional&lt;br /&gt;
details and the release schedule, discussing future goals for features&lt;br /&gt;
rather than the features themselves. Design your application, not so that&lt;br /&gt;
it depends on the features described here, but so that it is compatible&lt;br /&gt;
with them.&lt;br /&gt;
     OS/2 version 1.0 is the first standard MS-DOS-compatible operating&lt;br /&gt;
system that unlocks the memory-addressing potential of the 80286--a &amp;quot;train&amp;quot;&lt;br /&gt;
that will &amp;quot;pull&amp;quot; a great many APIs into the standard. On the other hand,&lt;br /&gt;
foreseeable future releases cannot expect such penetration, so the&lt;br /&gt;
designers of OS/2 version 1.0 focused primarily on including a full set of&lt;br /&gt;
APIs. Major performance improvements were postponed for future releases&lt;br /&gt;
simply because such improvements can be easily added later, whereas new&lt;br /&gt;
APIs cannot. Most of the planned work is to take further advantage of&lt;br /&gt;
existing interfaces, not to create new ones.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.1  File System&lt;br /&gt;
&lt;br /&gt;
Clearly, the heart of the office automation environment is data--lots of&lt;br /&gt;
data--searching for it, reading it, and, less frequently, writing it. A&lt;br /&gt;
machine's raw number-crunching capacity is relatively uninteresting in this&lt;br /&gt;
milieu; the important issue is how fast the machine can get the data and&lt;br /&gt;
manipulate it. Certainly, raw CPU power is advantageous; it allows the use&lt;br /&gt;
of a relatively compute bound graphical user interface, for example. But&lt;br /&gt;
I/O performance is becoming the limiting factor, especially in a&lt;br /&gt;
multitasking environment. Where does this data come from? If it's from the&lt;br /&gt;
keyboard, no problem; human typing speeds are glacially slow to a computer.&lt;br /&gt;
If the data is from a non-mass-storage device, OS/2's direct device access&lt;br /&gt;
facilities should provide sufficient throughput. That leaves the file&lt;br /&gt;
system for local disks and the network for remote data. The file system is&lt;br /&gt;
a natural for a future release upgrade. Its interface is generic so that&lt;br /&gt;
applications written for the first release will work compatibly with new&lt;br /&gt;
file systems in subsequent releases.&lt;br /&gt;
     Talking about pending file system improvements is relatively easy&lt;br /&gt;
because the weaknesses in the current FAT file system are obvious.&lt;br /&gt;
&lt;br /&gt;
     þ  Large Disk Support&lt;br /&gt;
        Clearly, a new file system will support arbitrarily large&lt;br /&gt;
        disks without introducing prohibitive allocation fragmentation.&lt;br /&gt;
        Allocation fragmentation refers to the minimum amount of disk space&lt;br /&gt;
        that a file system can allocate to a small file--the allocation&lt;br /&gt;
        unit. If the allocation unit is size N, the average file on the&lt;br /&gt;
        disk is expected to waste N/2 bytes of disk space because each file&lt;br /&gt;
        has a last allocation unit and on the average that unit will be&lt;br /&gt;
        only half filled. Actually, if the allocation unit is large, say&lt;br /&gt;
        more than 2 KB, the average fragmentation loss is greater than this&lt;br /&gt;
        estimate because a disproportionate number of files are small.&lt;br /&gt;
           The existing MS-DOS FAT file system can handle large disks,&lt;br /&gt;
        but at the cost of using very large allocation units. Depending on&lt;br /&gt;
        the number and the size of the files, a 100 MB disk might be as&lt;br /&gt;
        much as 50 percent wasted by this fragmentation. The new Microsoft&lt;br /&gt;
        file system will support a very small allocation unit--probably 512&lt;br /&gt;
        bytes--to reduce this fragmentation, and this small allocation unit&lt;br /&gt;
        size will not adversely affect the performance of the file system.&lt;br /&gt;
&lt;br /&gt;
     þ  File Protection&lt;br /&gt;
        A new file system also must support file access protection as part&lt;br /&gt;
        of the move toward a fully secure environment. File protection is&lt;br /&gt;
        typically a feature of multiuser operating systems; the MS-DOS FAT&lt;br /&gt;
        file system was designed for a single-user environment and contains&lt;br /&gt;
        no protection facilities. So why do we need them now? One reason is&lt;br /&gt;
        that a networked PC is physically a single-user machine, but&lt;br /&gt;
        logically it's a multiuser machine because multiple users can&lt;br /&gt;
        access the same files over the network. Also, as we shall see, it&lt;br /&gt;
        is sometimes useful to be able to protect your own files from&lt;br /&gt;
        access by yourself.&lt;br /&gt;
           Today, most network installations consist of server machines&lt;br /&gt;
        and client machines, with client machines able to access only files&lt;br /&gt;
        on server machines. MSNET and PCNET servers have a rudimentary form&lt;br /&gt;
        of file protection, but it needs improvement (see below). In the&lt;br /&gt;
        future, as machines become bigger and as products improve, files on&lt;br /&gt;
        client machines will also be available across the network. Clearly,&lt;br /&gt;
        a strong protection mechanism is needed to eliminate risks to a&lt;br /&gt;
        client machine's files. Finally, a file protection mechanism can be&lt;br /&gt;
        useful even on a single-user machine that is not accessible from a&lt;br /&gt;
        network. Today a variety of &amp;quot;Trojan&amp;quot; programs claim to be one thing&lt;br /&gt;
        but actually are another. In a nonnetworked environment, these&lt;br /&gt;
        programs are generally examples of mindless vandalism; typically,&lt;br /&gt;
        they purge the contents of the victim's hard disk. In a future&lt;br /&gt;
        office environment, they might edit payroll files or send sensitive&lt;br /&gt;
        data to someone waiting across the network. If you, as a user, can&lt;br /&gt;
        put sensitive files under password protection, they are safe even&lt;br /&gt;
        from yourself when you unwittingly run a Trojan program. That&lt;br /&gt;
        program doesn't know the password, and you certainly will decline&lt;br /&gt;
        to supply it. Self-protection also prevents someone from sitting&lt;br /&gt;
        down at your PC while you are at lunch or on vacation and wreaking&lt;br /&gt;
        havoc with your files.&lt;br /&gt;
           Protection mechanisms take two general forms:&lt;br /&gt;
        capability tokens and access lists. capability token gives access&lt;br /&gt;
        to an object if the requestor can supply the proper token,&lt;br /&gt;
        which itself can take a variety of forms. A per-file or per-&lt;br /&gt;
        directory password, such as is available on existing MSNET&lt;br /&gt;
        and PCNET products, is a kind of  capability token: If you&lt;br /&gt;
        can present the password, you can access the file. Note that&lt;br /&gt;
        the password is associated with the item, not the user. The&lt;br /&gt;
        front door key to your house is a good example of a&lt;br /&gt;
        capability token, and it shows the features and limitations&lt;br /&gt;
        of the approach very well. Access to your house depends on&lt;br /&gt;
        owning the capability token--the key--and not on who you&lt;br /&gt;
        are. If you don't have your key, you can't get in, even if&lt;br /&gt;
        it's your own house. Anybody that does have the key can get&lt;br /&gt;
        in, no matter who they are. A key can sometimes be handy:&lt;br /&gt;
        You can loan it to someone for a day and then get it back.&lt;br /&gt;
        You can give it to the plumber's office, for example, and&lt;br /&gt;
        the office can give it to the plumber, who can in turn give&lt;br /&gt;
        it to an assistant. Capability tokens are flexible because&lt;br /&gt;
        you can pass them around without notifying the owner of the&lt;br /&gt;
        protected object.&lt;br /&gt;
           This benefit is also the major drawback of capability token&lt;br /&gt;
        systems: The capabilities can be passed around willy-nilly&lt;br /&gt;
        and, like a key, can be duplicated. Once you give your key&lt;br /&gt;
        out, you never know if you've gotten &amp;quot;them&amp;quot; back again. You&lt;br /&gt;
        can't enumerate who has access to your house, and if they&lt;br /&gt;
        refuse to return a key or if they've duplicated it, you&lt;br /&gt;
        can't withdraw access to your house. The only way to regain&lt;br /&gt;
        control over your house is to change the lock, which means&lt;br /&gt;
        that you have to reissue keys to everybody who should get&lt;br /&gt;
        access. In the world of houses and keys, this isn't much of&lt;br /&gt;
        a problem because keys aren't given out that much and it's&lt;br /&gt;
        easy to contact the few people who should have them.&lt;br /&gt;
        Changing the capability &amp;quot;lock&amp;quot; on a computer file is much&lt;br /&gt;
        more difficult, however, because it may mean updating a&lt;br /&gt;
        great many programs that are allowed access, and they all&lt;br /&gt;
        have to be updated simultaneously so that none is&lt;br /&gt;
        accidentally locked out. And, of course, the distribution of&lt;br /&gt;
        the new capability token must be carried out securely; you&lt;br /&gt;
        must ensure that no &amp;quot;bad guy&amp;quot; gets a chance to see and copy&lt;br /&gt;
        the token.&lt;br /&gt;
           And, finally, because a separate capability token, or&lt;br /&gt;
        password, needs to be kept for each file or directory, you can't&lt;br /&gt;
        possibly memorize them all. Instead, they get built into programs,&lt;br /&gt;
        stored in files, entered into batch scripts, and so on. All these&lt;br /&gt;
        passwords--the ones that are difficult to change because of the&lt;br /&gt;
        hassle of updating everybody--are being kept around in &amp;quot;plain text&amp;quot;&lt;br /&gt;
        in standardized locations, an invitation for pilferage. And just as&lt;br /&gt;
        the lock on your door won't tell you how many keys exist, a&lt;br /&gt;
        capability token system won't be able to warn you that someone has&lt;br /&gt;
        stolen a copy of the capability token.&lt;br /&gt;
           An alternative approach is the access list mechanism. It is&lt;br /&gt;
        equivalent to the guard at the movie studio gate who has a&lt;br /&gt;
        list of people on his clipboard. Each protected object is&lt;br /&gt;
        associated with a list of who is allowed what kind of access. It's&lt;br /&gt;
        easy to see who has access--simply look at the list. It's easy to&lt;br /&gt;
        give or take away access--simply edit the list. Maintaining the&lt;br /&gt;
        list is easy because no change is made unless someone is to be&lt;br /&gt;
        added or removed, and the list can contain group names, such as&lt;br /&gt;
        &amp;quot;anyone from the production department&amp;quot; or &amp;quot;all vice presidents.&amp;quot;&lt;br /&gt;
           The fly in this particular ointment--and the reason that&lt;br /&gt;
        MSNET didn't use this approach--is in authenticating the&lt;br /&gt;
        identification of the person who wants access. In our movie studio,&lt;br /&gt;
        a picture badge is probably sufficient.&lt;br /&gt;
1. Note that the photo on the badge, together with a hard-to duplicate&lt;br /&gt;
design, keeps the badge from being just another capability token.&lt;br /&gt;
1 With the computer, we use&lt;br /&gt;
        a personal password. This password doesn't show that you have&lt;br /&gt;
        access to a particular file; it shows that you are who you claim to&lt;br /&gt;
        be. Also, because you have only one password, you can memorize it;&lt;br /&gt;
        it needn't be written on any list. Finally, you can change the&lt;br /&gt;
        password frequently because only one person--the one changing it,&lt;br /&gt;
        you--needs to be notified. Once the computer system knows that&lt;br /&gt;
        you're truly Hiram G. Hornswoggle, it grants or refuses access&lt;br /&gt;
        based on whether you're on an access list or belong to a group that&lt;br /&gt;
        is on an access list. MS-DOS can't use this approach because it's&lt;br /&gt;
        an unprotected system; whatever flag it sets in memory to say that&lt;br /&gt;
        you have properly authenticated yourself can be set by a cheater&lt;br /&gt;
        program. OS/2 is a protect mode operating system and is secure from&lt;br /&gt;
        such manipulation provided that no untrusted real mode applications&lt;br /&gt;
        are executed.&lt;br /&gt;
2. A future OS/2 release will take advantage of the 80386&lt;br /&gt;
processor's virtual real mode facility to make it safe to run&lt;br /&gt;
untrusted real mode programs on an 80386.&lt;br /&gt;
2 A networking environment provides an extra&lt;br /&gt;
        challenge because you can write a program--perhaps running&lt;br /&gt;
        on an MS-DOS machine to avoid protection mechanisms--that &amp;quot;sniffs&amp;quot;&lt;br /&gt;
        the network, examining every communication. A client machine can't&lt;br /&gt;
        send a plain-text password over the network to authenticate its&lt;br /&gt;
        user because a sniffer could see it. And it certainly can't send a&lt;br /&gt;
        message saying, &amp;quot;I'm satisfied that this is really Hiram.&amp;quot; The&lt;br /&gt;
        client machine may be running bogus software that will lie and say&lt;br /&gt;
        that when it isn't true. In other words, a network authentication&lt;br /&gt;
        protocol must assume that &amp;quot;bad guys&amp;quot; can read all net transmissions&lt;br /&gt;
        and can generate any transmission they wish.&lt;br /&gt;
           As should be clear by now, a future OS/2 file system will&lt;br /&gt;
        support per-object permission lists. OS/2 will be enhanced&lt;br /&gt;
        to support users' identifying themselves by means of personal&lt;br /&gt;
        passwords. Future network software will support a secure network&lt;br /&gt;
        authentication protocol.&lt;br /&gt;
&lt;br /&gt;
     A new file system will do more than support access lists; it will also&lt;br /&gt;
support filenames longer than the FAT 8.3 convention, and it will support&lt;br /&gt;
extended file attributes. The FAT file system supports a very limited set&lt;br /&gt;
of attributes, each of which are binary flags--system, hidden, read-only,&lt;br /&gt;
and so on. Extended attributes allow an arbitrary set of attributes,&lt;br /&gt;
represented as text strings, to be associated with each file. Individual&lt;br /&gt;
applications will be able to define specific attributes, set them on files,&lt;br /&gt;
and later query their values. Extended attributes can be used, for example,&lt;br /&gt;
to name the application that created the file. This would allow a user to&lt;br /&gt;
click the mouse over the filename on a directory display and have the&lt;br /&gt;
presentation manager bring up the proper application on that file.&lt;br /&gt;
     Finally, although this file system wish list looks pretty good, how do&lt;br /&gt;
we know that we've covered all the bases? And will our new file system work&lt;br /&gt;
well with CD-ROM&lt;br /&gt;
3. Special versions of compact discs that contain digital data instead&lt;br /&gt;
of digitized music. When accessed via a modified CD player, they&lt;br /&gt;
provide approximately 600 MB of read-only storage.&lt;br /&gt;
3 disks and WORM drives? The answers are &amp;quot;We don't&amp;quot; and&lt;br /&gt;
&amp;quot;It doesn't,&amp;quot; so a future OS/2 release will support installable file&lt;br /&gt;
systems. An installable file system is similar to an installable device&lt;br /&gt;
driver. When the system is initialized, not only device drivers but new&lt;br /&gt;
file system management packages can be installed into OS/2. This will allow&lt;br /&gt;
specialized file systems to handle specialized devices such as CD-ROMs and&lt;br /&gt;
WORM, as well as providing an easy interface to media written on foreign&lt;br /&gt;
file systems that are on non-MS-DOS or non-OS/2 systems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2  The 80386&lt;br /&gt;
&lt;br /&gt;
Throughout this book, the name 80386 keeps cropping up, almost as a kind of&lt;br /&gt;
magical incantation. To a system designer, it is a magical device. It&lt;br /&gt;
provides the protection facilities of the 80286, but it also provides three&lt;br /&gt;
other key features.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.1  Large Segments&lt;br /&gt;
The 80386 has a segmented architecture very much like that of the 80286,&lt;br /&gt;
but 80286 segments are limited to 64 KB. On the 80386, segments can be as&lt;br /&gt;
large as 4 million KB; segments can be so large that an entire program can&lt;br /&gt;
run in 2 segments (one code and one data) and essentially ignore the&lt;br /&gt;
segmentation facilities of the processor. This is called flat model.&lt;br /&gt;
Writing programs that deal with large structures is easier using flat&lt;br /&gt;
model, and because compilers have a hard time generating optimal segmented&lt;br /&gt;
code, converting 8086/80286 large model programs to 80386 flat model can&lt;br /&gt;
produce dramatic increases in execution speed.&lt;br /&gt;
     Although a future release of OS/2 will certainly support large&lt;br /&gt;
segments and applications that use flat model internally, OS/2 will not&lt;br /&gt;
necessarily provide a flat model API. The system API for 32-bit&lt;br /&gt;
applications may continue to use segmented (that is, 48-bit) addresses.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.2  Multiple Real Mode Boxes&lt;br /&gt;
The 80386 provides a mode of execution called virtual real mode. Processes&lt;br /&gt;
that run in this mode execute instructions exactly as they would in real&lt;br /&gt;
mode, but they are not truly in real mode; they are in a special 8086-&lt;br /&gt;
compatible protected mode. The additional memory management and protection&lt;br /&gt;
facilities that this mode provides allow a future version of OS/2 to&lt;br /&gt;
support more than one real mode box at the same time; multiple real mode&lt;br /&gt;
applications will be able to execute simultaneously and to continue&lt;br /&gt;
executing while in background mode. The virtual real mode eliminates the&lt;br /&gt;
need for mode switching; thus, the user can execute real mode applications&lt;br /&gt;
while running communications applications that have a high interrupt&lt;br /&gt;
rate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.3  Full Protection Capability&lt;br /&gt;
The virtual real mode capability, coupled with the 80386's ability to&lt;br /&gt;
allow/disallow I/O access on a port-by-port basis, provides the hardware&lt;br /&gt;
foundation for a future OS/2 that is fully secure. In a fully secure OS/2,&lt;br /&gt;
the modules loaded in during bootup--the operating system itself, device&lt;br /&gt;
drivers, installable file systems, and so on--must be trusted, but no other&lt;br /&gt;
program can accidentally or deliberately damage others, read protected&lt;br /&gt;
files, or otherwise access or damage restricted data. The only damage an&lt;br /&gt;
aberrant or malicious program will be able to do is to slow down the&lt;br /&gt;
machine by hogging the resources, such as consuming most of the RAM or CPU&lt;br /&gt;
time. This is relatively harmless; the user can simply kill the offending&lt;br /&gt;
program and not run it anymore.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.4  Other Features&lt;br /&gt;
The 80386 contains other significant features besides speed, such as paged&lt;br /&gt;
virtual memory, that don't appear in an API or in a specific user benefit.&lt;br /&gt;
For this reason, we won't discuss them here other than to state the&lt;br /&gt;
obvious: An 80386 machine is generally considerably faster than an 80286-&lt;br /&gt;
based one.&lt;br /&gt;
     So what do these 80386 features mean for the 80286? What role will it&lt;br /&gt;
play in the near and far future? Should a developer write for the 80286 or&lt;br /&gt;
the 80386? First, OS/2 for the 80386&lt;br /&gt;
4. A product still under development at the time of this writing.&lt;br /&gt;
All releases of OS/2 will run on the 80386, but the initial OS/2&lt;br /&gt;
release treats the 80386 as a &amp;quot;fast 80286.&amp;quot; The only 80386&lt;br /&gt;
feature it uses is the faster mode-switching capability.&lt;br /&gt;
4 is the same operating system,&lt;br /&gt;
essentially, as OS/2 for the 80286. The only new API in 80386 OS/2 will be&lt;br /&gt;
the 32-bit wide one for 32-bit mode 80386-only binaries. The other&lt;br /&gt;
features--such as virtual memory, I/O permission mapping, and multiple real&lt;br /&gt;
mode boxes--are of value to the user but don't present any new APIs and&lt;br /&gt;
therefore are compatible with all applications. Certainly, taking advantage&lt;br /&gt;
of the 80386's new instruction order codes and 2^32-byte-length segments&lt;br /&gt;
will require a new API; in fact, a program must be specially written and&lt;br /&gt;
compiled for that environment. Only applications that can't function at all&lt;br /&gt;
using the smaller 80286-compatible segments need to become 80386 dependent;&lt;br /&gt;
80286 protect mode programs will run without change and without any&lt;br /&gt;
disadvantage on the 80386, taking advantage of its improved speed.&lt;br /&gt;
     To summarize, there is only one operating system, OS/2. OS/2 supports&lt;br /&gt;
16-bit protected mode applications that run on all machines, and OS/2 will&lt;br /&gt;
support 32-bit protected mode applications that will run only on 80386&lt;br /&gt;
machines. A developer should consider writing an application for the&lt;br /&gt;
32-bit model&lt;br /&gt;
5. When it is announced and documented.&lt;br /&gt;
5 only if the application performs so poorly in the 16-bit&lt;br /&gt;
model that a 16-bit version is worthless. Otherwise, one should develop&lt;br /&gt;
applications for the 16-bit model; such applications will run well on all&lt;br /&gt;
existing OS/2-compatible machines and on all OS/2 releases. Later, when the&lt;br /&gt;
80386 and OS/2-386 have sufficient market penetration, you may want to&lt;br /&gt;
release higher-performance upgrades to products that require the 80386.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.3  The Next Ten Years&lt;br /&gt;
&lt;br /&gt;
Microsoft believes that OS/2 will be a major influence in the personal&lt;br /&gt;
computer industry for roughly the next ten years. The standardization of&lt;br /&gt;
computing environments that mass market software brings about gives such&lt;br /&gt;
standards abnormal longevity, while the incredible rate of hardware&lt;br /&gt;
improvements brings on great pressure to change. As a result, we expect&lt;br /&gt;
OS/2 to live long and prosper, where long is a relative term in an industry&lt;br /&gt;
in which nothing can survive more than a decade. What might OS/2's&lt;br /&gt;
successor system look like? If we could answer that today, a successor&lt;br /&gt;
system would be unnecessary. Clearly, the increases in CPU performance will&lt;br /&gt;
continue. Personal computers will undoubtedly follow in the footsteps of&lt;br /&gt;
their supercomputer brethren and become used for more than calculation, but&lt;br /&gt;
also for simulation, modeling, and expert systems, not only in the&lt;br /&gt;
workplace but also in the home. The future will become clearer, over time,&lt;br /&gt;
as this most wonderful of tools continues to change its users.&lt;br /&gt;
     The development of OS/2 is, to date, the largest project that&lt;br /&gt;
Microsoft has ever taken on. From an initially very small group of&lt;br /&gt;
Microsoft engineers, to a still small joint Microsoft-IBM design team, to&lt;br /&gt;
finally a great many developers, builders, testers, and documenters from&lt;br /&gt;
both Microsoft and IBM, the project became known affectionately as the&lt;br /&gt;
&amp;quot;Black Hole.&amp;quot;&lt;br /&gt;
     As I write this, OS/2 is just weeks away from retail sale. It's been a&lt;br /&gt;
great pleasure for me and for the people who worked with me to see our&lt;br /&gt;
black hole begin to give back to our customers the fruits of the labors&lt;br /&gt;
that were poured into it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Glossary&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
anonymous pipe&lt;br /&gt;
a data storage buffer that OS/2 maintains in RAM; used for interprocess&lt;br /&gt;
communications.&lt;br /&gt;
&lt;br /&gt;
Applications Program Interface (API)&lt;br /&gt;
the set of calls a program uses to obtain services from the operating&lt;br /&gt;
system. The term API denotes a service interface, whatever its form.&lt;br /&gt;
&lt;br /&gt;
background category&lt;br /&gt;
a classification of processes that consists of those associated with a&lt;br /&gt;
screen group not currently being displayed.&lt;br /&gt;
&lt;br /&gt;
call gate&lt;br /&gt;
a special LDT or GDT entry that describes a subroutine entry point rather&lt;br /&gt;
than a memory segment. A far call to a call gate selector will cause a&lt;br /&gt;
transfer to the entry point specified in the call gate. This is a feature&lt;br /&gt;
of the 80286/80386 hardware and is normally used to provide a transition&lt;br /&gt;
from a lower privilege state to a higher one.&lt;br /&gt;
&lt;br /&gt;
captive thread&lt;br /&gt;
a thread that has been created by a dynlink package and that stays within&lt;br /&gt;
the dynlink code, never transferring back to the client process's code;&lt;br /&gt;
also a thread that is used to call a service entry point and that will&lt;br /&gt;
never return or that will return only if some specific event occurs.&lt;br /&gt;
&lt;br /&gt;
child process&lt;br /&gt;
a process created by another process (its parent process).&lt;br /&gt;
&lt;br /&gt;
closed system&lt;br /&gt;
hardware or software design that cannot be enhanced in the field by third-&lt;br /&gt;
party suppliers.&lt;br /&gt;
&lt;br /&gt;
command subtree&lt;br /&gt;
a process and all its descendants.&lt;br /&gt;
&lt;br /&gt;
context switch&lt;br /&gt;
the act of switching the CPU from the execution of one thread to another,&lt;br /&gt;
which may belong to the same process or to a different one.&lt;br /&gt;
&lt;br /&gt;
cooked mode&lt;br /&gt;
a mode established by programs for keyboard input. In cooked mode, OS/2&lt;br /&gt;
handles the line-editing characters such as the back space.&lt;br /&gt;
&lt;br /&gt;
critical section&lt;br /&gt;
a body of code that manipulates a data resource in a non-reentrant way.&lt;br /&gt;
&lt;br /&gt;
daemon program&lt;br /&gt;
a process that performs a utility function without interaction with the&lt;br /&gt;
user. For example, the swapper process is a daemon program.&lt;br /&gt;
&lt;br /&gt;
debuggee&lt;br /&gt;
the program being debugged.&lt;br /&gt;
&lt;br /&gt;
debugger&lt;br /&gt;
a program that helps the programmer locate the source of problems found&lt;br /&gt;
during runtime testing of a program.&lt;br /&gt;
&lt;br /&gt;
device driver&lt;br /&gt;
a program that transforms I/O requests made in a standard, device-&lt;br /&gt;
independent fashion into the operations necessary to make a specific piece&lt;br /&gt;
of hardware fulfill that request.&lt;br /&gt;
&lt;br /&gt;
device monitor&lt;br /&gt;
a mechanism that allows processes to track and/or modify device data&lt;br /&gt;
streams.&lt;br /&gt;
&lt;br /&gt;
disjoint LDT space&lt;br /&gt;
the LDT selectors reserved for memory objects that are shared or that may&lt;br /&gt;
be shared among processes.&lt;br /&gt;
&lt;br /&gt;
dynamic link&lt;br /&gt;
a method of postponing the resolution of external references until loadtime&lt;br /&gt;
or runtime. A dynamic link allows the called subroutines to be packaged,&lt;br /&gt;
dist ributed, and maintained independently of their callers. OS/2 extends&lt;br /&gt;
the dynamic link (or dynlink) mechanism to serve as the primary method by&lt;br /&gt;
which all system and nonsystem services are obtained.&lt;br /&gt;
&lt;br /&gt;
dynlink&lt;br /&gt;
see dynamic link.&lt;br /&gt;
&lt;br /&gt;
dynlink library&lt;br /&gt;
a file, in a special format, that contains the binary code for a group of&lt;br /&gt;
dynamically linked subroutines.&lt;br /&gt;
&lt;br /&gt;
dynlink routine&lt;br /&gt;
see dynamic link.&lt;br /&gt;
&lt;br /&gt;
dynlink subsystem&lt;br /&gt;
a dynlink module that provides a set of services built around a resource.&lt;br /&gt;
&lt;br /&gt;
encapsulation&lt;br /&gt;
the principle of hiding the internal implementation of a program, function,&lt;br /&gt;
or service so that its clients can tell what it does but not how it does&lt;br /&gt;
it.&lt;br /&gt;
&lt;br /&gt;
environment strings&lt;br /&gt;
a series of user-definable and program-definable strings that are&lt;br /&gt;
associated with each process. The initial values of environment strings are&lt;br /&gt;
established by a process's parent.&lt;br /&gt;
&lt;br /&gt;
exitlist&lt;br /&gt;
a list of subroutines that OS/2 calls when a process has terminated. The&lt;br /&gt;
exitlist is executed after process termination but before the process is&lt;br /&gt;
actually destroyed.&lt;br /&gt;
&lt;br /&gt;
Family Applications Program Interface (Family API)&lt;br /&gt;
a standard execution environment under MS-DOS versions 2.x and 3.x and&lt;br /&gt;
OS/2. The programmer can use the Family API to create an application that&lt;br /&gt;
uses a subset of OS/2 functions (but a superset of MS-DOS 3.x functions)&lt;br /&gt;
and that runs in a binary-compatible fashion under MS-DOS versions 2.x and&lt;br /&gt;
3.x and OS/2.&lt;br /&gt;
&lt;br /&gt;
file handle&lt;br /&gt;
a binary value that represents an open file; used in all file I/O calls.&lt;br /&gt;
&lt;br /&gt;
file locking&lt;br /&gt;
an OS/2 facility that allows one program to temporarily prevent other&lt;br /&gt;
programs from reading and/or writing a particular file.&lt;br /&gt;
&lt;br /&gt;
file system name space&lt;br /&gt;
names that have the format of filenames. All such names will eventually&lt;br /&gt;
represent disk &amp;quot;files&amp;quot;--data or special. Initially, some of these names are&lt;br /&gt;
kept in internal OS/2 RAM tables and are not present on any disk volume.&lt;br /&gt;
&lt;br /&gt;
forced event&lt;br /&gt;
an event or action that is forced upon a thread or a process from an&lt;br /&gt;
external source; for example, a Ctrl-C or a DosKill command.&lt;br /&gt;
&lt;br /&gt;
foreground category&lt;br /&gt;
a classification of processes that consists of those associated with the&lt;br /&gt;
currently active screen group.&lt;br /&gt;
&lt;br /&gt;
GDT&lt;br /&gt;
see global descriptor table.&lt;br /&gt;
&lt;br /&gt;
general priority category&lt;br /&gt;
the OS/2 classification of threads that consists of three subcategories:&lt;br /&gt;
background, foreground, and interactive.&lt;br /&gt;
&lt;br /&gt;
general protection (GP) fault&lt;br /&gt;
an error that occurs when a program accesses invalid memory locations or&lt;br /&gt;
accesses valid locations in an invalid way (such as writing into read-only&lt;br /&gt;
memory areas).&lt;br /&gt;
&lt;br /&gt;
giveaway shared memory&lt;br /&gt;
a shared memory mechanism in which a process that already has access to the&lt;br /&gt;
segment can grant access to another process. Processes cannot obtain access&lt;br /&gt;
for themselves; access must be granted by another process that already has&lt;br /&gt;
access.&lt;br /&gt;
&lt;br /&gt;
global data segment&lt;br /&gt;
a data segment that is shared among all instances of a dynlink routine; in&lt;br /&gt;
other words, a single segment that is accessible to all processes that call&lt;br /&gt;
a particular dynlink routine.&lt;br /&gt;
&lt;br /&gt;
global descriptor table (GDT)&lt;br /&gt;
an element of the 80286/80386 memory management hardware. The GDT holds the&lt;br /&gt;
descriptions of as many as 4095 global segments. A global segment is&lt;br /&gt;
accessible to all processes.&lt;br /&gt;
&lt;br /&gt;
global subsystem initialization&lt;br /&gt;
a facility that allows a dynlink routine to specify that its initialize&lt;br /&gt;
entry point should be called when the dynlink package is loaded on behalf&lt;br /&gt;
of its first client.&lt;br /&gt;
&lt;br /&gt;
grandparent process&lt;br /&gt;
the parent process of a process that created a process.&lt;br /&gt;
&lt;br /&gt;
handle&lt;br /&gt;
an arbitrary integer value that OS/2 returns to a process so that the&lt;br /&gt;
process can return it to OS/2 on subsequent calls; known to programmers as&lt;br /&gt;
a magic cookie.&lt;br /&gt;
&lt;br /&gt;
hard error&lt;br /&gt;
an error that the system detects but which it cannot correct without user&lt;br /&gt;
intervention.&lt;br /&gt;
&lt;br /&gt;
hard error daemon&lt;br /&gt;
a daemon process that services hard errors. The hard error daemon may be an&lt;br /&gt;
independent process, or it may be a thread that belongs to the session&lt;br /&gt;
manager or to the presentation manager.&lt;br /&gt;
&lt;br /&gt;
huge segments&lt;br /&gt;
a software technique that allows the creation and use of pseudo segments&lt;br /&gt;
larger than 65 KB.&lt;br /&gt;
&lt;br /&gt;
installable file system (IFS)&lt;br /&gt;
a body of code that OS/2 loads at boot time and that provides the software&lt;br /&gt;
to manage a file system on a storage device, including the ability to&lt;br /&gt;
create and maintain directories, allocate disk space, and so on.&lt;br /&gt;
&lt;br /&gt;
instance data segment&lt;br /&gt;
a memory segment that holds data specific to each instance of the dynlink&lt;br /&gt;
routine.&lt;br /&gt;
&lt;br /&gt;
instance subsystem initialization&lt;br /&gt;
a service that dynlink routines can request. A dynlink routine's initialize&lt;br /&gt;
entry point is called each time a new client is linked to the routine.&lt;br /&gt;
&lt;br /&gt;
interactive category&lt;br /&gt;
a classification of processes that consists of the process currently&lt;br /&gt;
interacting with the keyboard.&lt;br /&gt;
&lt;br /&gt;
interactive program&lt;br /&gt;
a program whose function is to obey commands from a user, such as an editor&lt;br /&gt;
or a spreadsheet program. Programs such as compilers may literally interact&lt;br /&gt;
by asking for filenames and compilation options, but they are considered&lt;br /&gt;
noninteractive because their function is to compile a source program, not&lt;br /&gt;
to provide answers to user-entered commands.&lt;br /&gt;
&lt;br /&gt;
interprocess communications (IPC)&lt;br /&gt;
the ability of processes and threads to transfer data and messages among&lt;br /&gt;
themselves; used to offer services to and receive services from other&lt;br /&gt;
programs.&lt;br /&gt;
&lt;br /&gt;
interruptible block&lt;br /&gt;
a special form of a blocking operation used inside the OS/2  kernel so that&lt;br /&gt;
events such as process kill and Ctrl-C can interrupt a thread that is&lt;br /&gt;
waiting, inside OS/2, for an event.&lt;br /&gt;
&lt;br /&gt;
I/O privilege mechanism&lt;br /&gt;
a facility that allows a process to ask a device driver for direct access&lt;br /&gt;
to the device's I/O ports and any dedicated or mapped memory locations it&lt;br /&gt;
has. The I/O privilege mechanism can be used directly by an application or&lt;br /&gt;
indirectly by a dynlink package.&lt;br /&gt;
&lt;br /&gt;
IPC&lt;br /&gt;
see interprocess communications.&lt;br /&gt;
&lt;br /&gt;
KBD&lt;br /&gt;
an abbreviated name for the dynlink package that manages the keyboard&lt;br /&gt;
device. All its entry points start with Kbd.&lt;br /&gt;
&lt;br /&gt;
kernel&lt;br /&gt;
the central part of OS/2. It resides permanently in fixed memory locations&lt;br /&gt;
and executes in the privileged ring 0 state.&lt;br /&gt;
&lt;br /&gt;
LDT&lt;br /&gt;
see local descriptor table.&lt;br /&gt;
&lt;br /&gt;
loadtime dynamic linking&lt;br /&gt;
the act of connecting a client process to dynamic link libraries when the&lt;br /&gt;
process is first loaded into memory.&lt;br /&gt;
&lt;br /&gt;
local descriptor table (LDT)&lt;br /&gt;
an element of the 80286/80386 memory management hardware. The LDT holds the&lt;br /&gt;
descriptions of as many as 4095 local segments. Each process has its own&lt;br /&gt;
LDT and cannot access the LDTs of other processes.&lt;br /&gt;
&lt;br /&gt;
logical device&lt;br /&gt;
a symbolic name for a device that the user can cause to be mapped to any&lt;br /&gt;
physical (actual) device.&lt;br /&gt;
&lt;br /&gt;
logical directory&lt;br /&gt;
a symbolic name for a directory that the user can cause to be mapped to any&lt;br /&gt;
actual drive and directory.&lt;br /&gt;
&lt;br /&gt;
low priority category&lt;br /&gt;
a classification of processes that consists of processes that get CPU time&lt;br /&gt;
only when no other thread in the other categories needs it; this category&lt;br /&gt;
is lower in priority than the general priority category.&lt;br /&gt;
&lt;br /&gt;
magic cookie&lt;br /&gt;
see handle.&lt;br /&gt;
&lt;br /&gt;
memory manager&lt;br /&gt;
the section of OS/2 that allocates both physical memory and virtual memory.&lt;br /&gt;
&lt;br /&gt;
memory overcommit&lt;br /&gt;
allocating more memory to the running program than physically exists.&lt;br /&gt;
&lt;br /&gt;
memory suballocation&lt;br /&gt;
the OS/2 facility that allocates pieces of memory from within an&lt;br /&gt;
application's segment.&lt;br /&gt;
&lt;br /&gt;
MOU&lt;br /&gt;
an abbreviated name for the dynlink package that manages the mouse device.&lt;br /&gt;
All its entry points start with Mou.&lt;br /&gt;
&lt;br /&gt;
multitasking operating system&lt;br /&gt;
an operating system in which two or more programs/threads can execute&lt;br /&gt;
simultaneously.&lt;br /&gt;
&lt;br /&gt;
named pipe&lt;br /&gt;
a data storage buffer that OS/2 maintains in RAM; used for interprocess&lt;br /&gt;
communication.&lt;br /&gt;
&lt;br /&gt;
named shared memory&lt;br /&gt;
a memory segment that can be accessed simultaneously by more than one&lt;br /&gt;
process. Its name allows processes to request access to it.&lt;br /&gt;
&lt;br /&gt;
open system&lt;br /&gt;
hardware or software design that allows third-party additions and upgrades&lt;br /&gt;
in the field.&lt;br /&gt;
&lt;br /&gt;
object name buffer&lt;br /&gt;
the area in which OS/2 returns a character string if the DosExecPgm&lt;br /&gt;
function fails.&lt;br /&gt;
&lt;br /&gt;
parallel multitasking&lt;br /&gt;
the process whereby programs execute simultaneously.&lt;br /&gt;
&lt;br /&gt;
parent process&lt;br /&gt;
a process that creates another process, which is called the child process.&lt;br /&gt;
&lt;br /&gt;
physical memory&lt;br /&gt;
the RAM (Random Access Memory) physically present inside the machine.&lt;br /&gt;
&lt;br /&gt;
PID (Process Identification Number)&lt;br /&gt;
a unique code that OS/2 assigns to a process when the process is created.&lt;br /&gt;
The PID may be any value except 0.&lt;br /&gt;
&lt;br /&gt;
pipe&lt;br /&gt;
see anonymous pipe; named pipe.&lt;br /&gt;
&lt;br /&gt;
presentation manager&lt;br /&gt;
the graphical user interface for OS/2.&lt;br /&gt;
&lt;br /&gt;
priority&lt;br /&gt;
(also known as CPU priority) the numeric value assigned to each runnable&lt;br /&gt;
thread in the system. Threads with a higher priority are assigned the CPU&lt;br /&gt;
in preference to those with a lower priority.&lt;br /&gt;
&lt;br /&gt;
privilege mode&lt;br /&gt;
a special execution mode (also known as ring 0) supported by the&lt;br /&gt;
80286/80386 hardware. Code executing in this mode can execute restricted&lt;br /&gt;
instructions that are used to manipulate key system structures and tables.&lt;br /&gt;
Only the OS/2 kernel and device drivers run in this mode.&lt;br /&gt;
&lt;br /&gt;
process&lt;br /&gt;
the executing instance of a binary file. In OS/2, the terms task and&lt;br /&gt;
process are used interchangeably. A process is the unit of ownership, and&lt;br /&gt;
processes own resources such as memory, open files, dynlink libraries, and&lt;br /&gt;
semaphores.&lt;br /&gt;
&lt;br /&gt;
protect mode&lt;br /&gt;
the operating mode of the 80286 microprocessor that allows the operating&lt;br /&gt;
system to use features that protect one application from another; also&lt;br /&gt;
called protected mode.&lt;br /&gt;
&lt;br /&gt;
queue&lt;br /&gt;
an orderly list of elements waiting for processing.&lt;br /&gt;
&lt;br /&gt;
RAM semaphore&lt;br /&gt;
a kind of semaphore that is based in memory accessible to a thread; fast,&lt;br /&gt;
but with limited functionality. See system semaphore.&lt;br /&gt;
&lt;br /&gt;
raw mode&lt;br /&gt;
a mode established by programs for keyboard input. In raw mode OS/2 passes&lt;br /&gt;
to the caller each character typed immediately as it is typed. The caller&lt;br /&gt;
is responsible for handling line-editing characters such as the back space.&lt;br /&gt;
&lt;br /&gt;
real mode&lt;br /&gt;
the operating mode of the 80286 microprocessor that runs programs designed&lt;br /&gt;
for the 8086/8088 microprocessor.&lt;br /&gt;
&lt;br /&gt;
record locking&lt;br /&gt;
the mechanism that allows a process to lock a range of bytes within a file.&lt;br /&gt;
While the lock is in effect, no other process can read or write those&lt;br /&gt;
bytes.&lt;br /&gt;
&lt;br /&gt;
ring 3&lt;br /&gt;
the privilege level that is used to run applications. Code executing at&lt;br /&gt;
this level cannot modify critical system structures.&lt;br /&gt;
&lt;br /&gt;
runtime dynamic linking&lt;br /&gt;
the act of establishing a dynamic link after a process has begun execution.&lt;br /&gt;
This is done by providing OS/2 with the module and entry point names; OS/2&lt;br /&gt;
returns the address of the routine.&lt;br /&gt;
&lt;br /&gt;
scheduler&lt;br /&gt;
the part of OS/2 that decides which thread to run and how long to run it&lt;br /&gt;
before assigning the CPU to another thread; also, the part of OS/2 that&lt;br /&gt;
determines the priority value for each thread.&lt;br /&gt;
&lt;br /&gt;
screen group&lt;br /&gt;
a group of one or more processes that share (generally in a serial fashion)&lt;br /&gt;
a single logical screen and keyboard.&lt;br /&gt;
&lt;br /&gt;
semaphore&lt;br /&gt;
a software flag or signal used to coordinate the activities of two or more&lt;br /&gt;
threads; commonly used to protect a critical section.&lt;br /&gt;
&lt;br /&gt;
serial multitasking&lt;br /&gt;
the process whereby multiple programs execute, but only one at a time.&lt;br /&gt;
&lt;br /&gt;
session manager&lt;br /&gt;
a system utility that manages screen group switching. The session manager&lt;br /&gt;
is used only in the absence of the presentation manager; the presentation&lt;br /&gt;
manager replaces the session manager.&lt;br /&gt;
&lt;br /&gt;
shared memory&lt;br /&gt;
a memory segment that can be accessed simultaneously by more than one&lt;br /&gt;
process.&lt;br /&gt;
&lt;br /&gt;
signaling&lt;br /&gt;
using semaphores to notify threads that certain events or activities have&lt;br /&gt;
taken place.&lt;br /&gt;
&lt;br /&gt;
signals&lt;br /&gt;
notification mechanisms implemented in software that operate in a fashion&lt;br /&gt;
analogous to hardware interrupts.&lt;br /&gt;
&lt;br /&gt;
software tools approach&lt;br /&gt;
a design philosophy in which each program and application in a package is&lt;br /&gt;
dedicated to performing a specific task and doing that task very well. See&lt;br /&gt;
also encapsulation.&lt;br /&gt;
&lt;br /&gt;
stack frame&lt;br /&gt;
a portion of a thread's stack that contains a procedure's local variables&lt;br /&gt;
and parameters.&lt;br /&gt;
&lt;br /&gt;
static linking&lt;br /&gt;
the combining of multiple compilands into a single executable file, thereby&lt;br /&gt;
resolving undefined external references.&lt;br /&gt;
&lt;br /&gt;
single-tasking&lt;br /&gt;
a computer environment in which only one program runs at a time.&lt;br /&gt;
&lt;br /&gt;
swapping&lt;br /&gt;
the technique by which some code or data in memory is written to a disk&lt;br /&gt;
file, thus allowing the memory it was using to be reused for another&lt;br /&gt;
purpose.&lt;br /&gt;
&lt;br /&gt;
system semaphore&lt;br /&gt;
a semaphore that is implemented in OS/2's internal memory area; somewhat&lt;br /&gt;
slower than RAM semaphores, but providing more features.&lt;br /&gt;
&lt;br /&gt;
System File Table (SFT)&lt;br /&gt;
an internal OS/2 table that contains an entry for every file currently&lt;br /&gt;
open.&lt;br /&gt;
&lt;br /&gt;
task&lt;br /&gt;
see process.&lt;br /&gt;
&lt;br /&gt;
thread&lt;br /&gt;
the OS/2 mechanism that allows more than one path of execution through the&lt;br /&gt;
same instance of an application program.&lt;br /&gt;
&lt;br /&gt;
thread ID&lt;br /&gt;
the handle of a particular thread within a process.&lt;br /&gt;
&lt;br /&gt;
thread of execution&lt;br /&gt;
the passage of the CPU through the instruction sequence.&lt;br /&gt;
&lt;br /&gt;
time-critical priority&lt;br /&gt;
a classification of processes that may be interactive or noninteractive, in&lt;br /&gt;
the foreground or background screen group, which have a higher priority&lt;br /&gt;
than any non-time-critical thread in the system.&lt;br /&gt;
&lt;br /&gt;
time slice&lt;br /&gt;
the amount of execution time that the scheduler will give a thread before&lt;br /&gt;
reassigning the CPU to another thread of equal priority.&lt;br /&gt;
&lt;br /&gt;
VIO&lt;br /&gt;
an abbreviated name of the dynlink package that manages the display device.&lt;br /&gt;
All its entry points start with Vio.&lt;br /&gt;
&lt;br /&gt;
virtual memory&lt;br /&gt;
the memory space allocated to and used by a process. At the time it is&lt;br /&gt;
being referenced, the virtual memory must be present in physical memory,&lt;br /&gt;
but otherwise it may be swapped to a disk file.&lt;br /&gt;
&lt;br /&gt;
virtualization&lt;br /&gt;
the general technique of hiding a complicated actual situation behind a&lt;br /&gt;
simple, standard interface.&lt;br /&gt;
&lt;br /&gt;
writethrough&lt;br /&gt;
an option available when a file write operation is performed which&lt;br /&gt;
specifies that the normal caching mechanism is to be sidestepped and the&lt;br /&gt;
data is to be written through to the disk surface immediately.&lt;br /&gt;
&lt;br /&gt;
3x box&lt;br /&gt;
the OS/2 environment that emulates an 8086-based PC running MS-DOS versions&lt;br /&gt;
2.x or 3.x.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Index&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Symbols&lt;br /&gt;
----&lt;br /&gt;
3x box&lt;br /&gt;
80286 processor&lt;br /&gt;
   bugs in&lt;br /&gt;
   I/O access control in&lt;br /&gt;
   segmented architecture of&lt;br /&gt;
   size of segments&lt;br /&gt;
80386 processor&lt;br /&gt;
   I/O access control in&lt;br /&gt;
   key features of&lt;br /&gt;
8080 processor&lt;br /&gt;
8086/8088 processor&lt;br /&gt;
   memory limitations of&lt;br /&gt;
   real mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A&lt;br /&gt;
----&lt;br /&gt;
Abort, Retry, Ignore message (MS-DOS)&lt;br /&gt;
access lists&lt;br /&gt;
addresses&lt;br /&gt;
   invalid&lt;br /&gt;
   subroutine&lt;br /&gt;
addressing, huge model&lt;br /&gt;
address offsets&lt;br /&gt;
address space, linear&lt;br /&gt;
allocation. See also memory&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
anonymous pipes&lt;br /&gt;
API&lt;br /&gt;
   80386 processor&lt;br /&gt;
   Family&lt;br /&gt;
   memory management&lt;br /&gt;
Apple Macintosh&lt;br /&gt;
application environment. See environment&lt;br /&gt;
application mode&lt;br /&gt;
applications&lt;br /&gt;
   ``combo''&lt;br /&gt;
   command&lt;br /&gt;
   communicating between&lt;br /&gt;
   compatibility with MS-DOS&lt;br /&gt;
   designing for both OS/2 and MS-DOS&lt;br /&gt;
   device monitors and&lt;br /&gt;
   dual mode&lt;br /&gt;
   I/O-bound&lt;br /&gt;
   protecting&lt;br /&gt;
   real mode&lt;br /&gt;
   running MS-DOS&lt;br /&gt;
   time-critical&lt;br /&gt;
Applications Program Interface. See API&lt;br /&gt;
   Family&lt;br /&gt;
architecture&lt;br /&gt;
   80386 processor&lt;br /&gt;
   design concepts of OS/2&lt;br /&gt;
   device driver&lt;br /&gt;
   I/O&lt;br /&gt;
   segmented&lt;br /&gt;
arguments&lt;br /&gt;
   DosCWait&lt;br /&gt;
   DosExecPgm&lt;br /&gt;
ASCII text strings&lt;br /&gt;
ASCIIZ strings&lt;br /&gt;
asynchronous I/O&lt;br /&gt;
asynchronous processing&lt;br /&gt;
atomic operation&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
B&lt;br /&gt;
----&lt;br /&gt;
background&lt;br /&gt;
   category&lt;br /&gt;
   I/O&lt;br /&gt;
   processing&lt;br /&gt;
   threads and applications&lt;br /&gt;
base segment&lt;br /&gt;
BAT files, MS-DOS&lt;br /&gt;
BIOS entry vector, hooking the&lt;br /&gt;
blocking services&lt;br /&gt;
block mode&lt;br /&gt;
   device drivers&lt;br /&gt;
   driver algorithm&lt;br /&gt;
blocks, interruptible&lt;br /&gt;
boot process&lt;br /&gt;
boot time, installing device drivers at&lt;br /&gt;
breakthroughs, technological&lt;br /&gt;
buffer reusability&lt;br /&gt;
buffers&lt;br /&gt;
   flushing&lt;br /&gt;
   monitor&lt;br /&gt;
bugs&lt;br /&gt;
   80286 processor&lt;br /&gt;
   program&lt;br /&gt;
byte-stream mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
C&lt;br /&gt;
----&lt;br /&gt;
call gate&lt;br /&gt;
call and return sequence, OS/2&lt;br /&gt;
call statements, writing for dynamic link routines&lt;br /&gt;
call timed out error&lt;br /&gt;
capability tokens&lt;br /&gt;
captive threads&lt;br /&gt;
categories, priority&lt;br /&gt;
Central Processing Unit. See CPU&lt;br /&gt;
character mode&lt;br /&gt;
   device driver model for&lt;br /&gt;
child processes&lt;br /&gt;
   controlling&lt;br /&gt;
CHKDSK&lt;br /&gt;
circular references&lt;br /&gt;
CLI instruction&lt;br /&gt;
client processes&lt;br /&gt;
closed system&lt;br /&gt;
clusters&lt;br /&gt;
CMD.EXE&lt;br /&gt;
   I/O architecture and&lt;br /&gt;
   logical device and directory names&lt;br /&gt;
code, swapping&lt;br /&gt;
CodeView&lt;br /&gt;
command application&lt;br /&gt;
COMMAND.COM&lt;br /&gt;
command mode&lt;br /&gt;
command processes&lt;br /&gt;
command subtrees&lt;br /&gt;
   controlling&lt;br /&gt;
command threads&lt;br /&gt;
communication, interprocess&lt;br /&gt;
compatibility&lt;br /&gt;
   downward&lt;br /&gt;
   functional&lt;br /&gt;
   levels of&lt;br /&gt;
   MS-DOS&lt;br /&gt;
   name generation and&lt;br /&gt;
   VIO and presentation manager&lt;br /&gt;
compatibility box&lt;br /&gt;
compatibility issues, OS/2&lt;br /&gt;
compatibility mode&lt;br /&gt;
computers&lt;br /&gt;
   mental work and&lt;br /&gt;
   multiple CPU&lt;br /&gt;
   networked (see also networks)&lt;br /&gt;
   Von Neumann&lt;br /&gt;
CONFIG.SYS file&lt;br /&gt;
consistency, design&lt;br /&gt;
context switching&lt;br /&gt;
conventions, system&lt;br /&gt;
cooked mode&lt;br /&gt;
CP/M, compatibility with&lt;br /&gt;
CP/M-80&lt;br /&gt;
CPU See also 8086/8088 processor, 80286 processor, 80386 processor&lt;br /&gt;
   priority&lt;br /&gt;
crashes, system&lt;br /&gt;
critical sections&lt;br /&gt;
   protecting&lt;br /&gt;
   signals and&lt;br /&gt;
CS register&lt;br /&gt;
Ctrl-Break and Ctrl-C&lt;br /&gt;
customized environment&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
D&lt;br /&gt;
----&lt;br /&gt;
daemon, hard error&lt;br /&gt;
daemon interfaces, dynamic links as&lt;br /&gt;
daemon program&lt;br /&gt;
data&lt;br /&gt;
   global&lt;br /&gt;
   handling in dynamic linking&lt;br /&gt;
   instance&lt;br /&gt;
   instructions and&lt;br /&gt;
   swapped-out&lt;br /&gt;
data integrity&lt;br /&gt;
data segments, executing from&lt;br /&gt;
data streams, monitoring&lt;br /&gt;
debuggee&lt;br /&gt;
debugger&lt;br /&gt;
   system&lt;br /&gt;
debugging&lt;br /&gt;
demand loading&lt;br /&gt;
demand load segment&lt;br /&gt;
densities, disk&lt;br /&gt;
design, concepts of OS/2&lt;br /&gt;
design goals&lt;br /&gt;
DevHlp&lt;br /&gt;
device data streams&lt;br /&gt;
device drivers&lt;br /&gt;
   architecture of&lt;br /&gt;
   block mode&lt;br /&gt;
   character mode&lt;br /&gt;
   code structure&lt;br /&gt;
   definition of&lt;br /&gt;
   dynamic link pseudo&lt;br /&gt;
   OS/2 communication and&lt;br /&gt;
   programming model for&lt;br /&gt;
device independence&lt;br /&gt;
   definition of&lt;br /&gt;
device management&lt;br /&gt;
device monitors&lt;br /&gt;
device names&lt;br /&gt;
devices&lt;br /&gt;
   direct access of&lt;br /&gt;
   logical&lt;br /&gt;
device-specific code, encapsulating&lt;br /&gt;
Digital Research&lt;br /&gt;
direct device access&lt;br /&gt;
directories&lt;br /&gt;
   ISAM&lt;br /&gt;
   logical&lt;br /&gt;
   working&lt;br /&gt;
directory names&lt;br /&gt;
directory tree hierarchy&lt;br /&gt;
disjoint LDT space&lt;br /&gt;
disk data synchronization&lt;br /&gt;
disk I/O requests&lt;br /&gt;
disk seek times&lt;br /&gt;
disk space, allocation of&lt;br /&gt;
DISKCOMP&lt;br /&gt;
DISKCOPY&lt;br /&gt;
disks&lt;br /&gt;
   laser&lt;br /&gt;
   unlabeled&lt;br /&gt;
dispatcher&lt;br /&gt;
display device, manipulating the&lt;br /&gt;
display memory&lt;br /&gt;
.DLL files&lt;br /&gt;
DosAllocHuge&lt;br /&gt;
DosAllocSeg&lt;br /&gt;
DosAllocShrSeg&lt;br /&gt;
DosBufReset&lt;br /&gt;
DosCallNmPipe&lt;br /&gt;
DosCalls&lt;br /&gt;
DosClose&lt;br /&gt;
DosConnectNmPipe&lt;br /&gt;
DosCreateCSAlias&lt;br /&gt;
DosCreateSem&lt;br /&gt;
DosCreateThread&lt;br /&gt;
DosCWait&lt;br /&gt;
DosDevIOCtl&lt;br /&gt;
DosDupHandle&lt;br /&gt;
DosEnterCritSec&lt;br /&gt;
DosErrClass&lt;br /&gt;
DosError&lt;br /&gt;
DosExecPgm&lt;br /&gt;
DosExit&lt;br /&gt;
DosExitCritSec&lt;br /&gt;
DosExitList&lt;br /&gt;
DosFindFirst&lt;br /&gt;
DosFindNext&lt;br /&gt;
DosFlagProcess&lt;br /&gt;
DosFreeModule&lt;br /&gt;
DosFreeSeg&lt;br /&gt;
DosGetHugeShift&lt;br /&gt;
DosGetMachineMode&lt;br /&gt;
DosGetProcAddr&lt;br /&gt;
DosGiveSeg&lt;br /&gt;
DosHoldSignal&lt;br /&gt;
DosKill&lt;br /&gt;
DosKillProcess&lt;br /&gt;
DosLoadModule&lt;br /&gt;
DosMakeNmPipe&lt;br /&gt;
DosMakePipe&lt;br /&gt;
DosMonRead&lt;br /&gt;
DosMonReq&lt;br /&gt;
DosMonWrite&lt;br /&gt;
DosMuxSemWait&lt;br /&gt;
DosNewSize&lt;br /&gt;
DosOpen&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosPeekNmPipe&lt;br /&gt;
DosPtrace&lt;br /&gt;
DosRead&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosReadQueue&lt;br /&gt;
DosReallocHuge&lt;br /&gt;
DosResumeThread&lt;br /&gt;
DosScanEnv&lt;br /&gt;
DosSearchPath&lt;br /&gt;
DosSemClear&lt;br /&gt;
DosSemRequest&lt;br /&gt;
DosSemSet&lt;br /&gt;
DosSemWait&lt;br /&gt;
DosSetFHandState&lt;br /&gt;
DosSetPrty&lt;br /&gt;
DosSetSigHandler&lt;br /&gt;
DosSetVerify&lt;br /&gt;
DosSleep&lt;br /&gt;
DosSubAlloc&lt;br /&gt;
DosSubFrees&lt;br /&gt;
DosSuspendThread&lt;br /&gt;
DosTimerAsync&lt;br /&gt;
DosTimerStart&lt;br /&gt;
DosTimerStop&lt;br /&gt;
DosTransactNmPipe&lt;br /&gt;
DosWrite&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosWriteQueue&lt;br /&gt;
downward compatibility&lt;br /&gt;
DPATH&lt;br /&gt;
drive-oriented operations&lt;br /&gt;
drives, high- and low-density&lt;br /&gt;
dual mode&lt;br /&gt;
Dynamic Data Exchange (DDE)&lt;br /&gt;
dynamic linking&lt;br /&gt;
   Family API use of&lt;br /&gt;
   loadtime&lt;br /&gt;
   runtime&lt;br /&gt;
dynamic link libraries&lt;br /&gt;
dynamic link routines, calling&lt;br /&gt;
dynamic links&lt;br /&gt;
   architectural role of&lt;br /&gt;
   circular references in&lt;br /&gt;
   details on implementing&lt;br /&gt;
   device drivers and&lt;br /&gt;
   interfaces to other processes&lt;br /&gt;
   naming&lt;br /&gt;
   side effects of&lt;br /&gt;
   using for pseudo device drivers&lt;br /&gt;
dynlink See also dynamic links&lt;br /&gt;
   routines&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
E&lt;br /&gt;
----&lt;br /&gt;
encapsulation&lt;br /&gt;
   device driver&lt;br /&gt;
entry ordinals&lt;br /&gt;
entry point name&lt;br /&gt;
environment&lt;br /&gt;
   customized&lt;br /&gt;
   dual mode&lt;br /&gt;
   protected&lt;br /&gt;
   single-tasking&lt;br /&gt;
   single-tasking versus multitasking&lt;br /&gt;
   stable&lt;br /&gt;
   stand-alone&lt;br /&gt;
   virtualizing the&lt;br /&gt;
environment block&lt;br /&gt;
environment strings&lt;br /&gt;
EnvPointer&lt;br /&gt;
EnvString&lt;br /&gt;
error codes, compatibility mode&lt;br /&gt;
errors&lt;br /&gt;
   general protection fault&lt;br /&gt;
   hard&lt;br /&gt;
   localization of&lt;br /&gt;
   program&lt;br /&gt;
   timed out&lt;br /&gt;
.EXE files&lt;br /&gt;
   executing&lt;br /&gt;
   Family API&lt;br /&gt;
EXEC function (MS-DOS)&lt;br /&gt;
execute threads&lt;br /&gt;
executing programs&lt;br /&gt;
execution speed&lt;br /&gt;
exitlist&lt;br /&gt;
expandability&lt;br /&gt;
extended file attributes&lt;br /&gt;
extended partitioning&lt;br /&gt;
extension, filename&lt;br /&gt;
external name, dynamic link&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
F&lt;br /&gt;
----&lt;br /&gt;
Family API&lt;br /&gt;
   drawbacks of&lt;br /&gt;
Family Applications Program Interface (Family API)&lt;br /&gt;
far return instruction&lt;br /&gt;
fault, memory not present&lt;br /&gt;
fault errors, general protection&lt;br /&gt;
faults, GP&lt;br /&gt;
features&lt;br /&gt;
   additional&lt;br /&gt;
   introducing new operating system&lt;br /&gt;
file&lt;br /&gt;
   allocation, improving&lt;br /&gt;
   attributes, extended&lt;br /&gt;
   handles&lt;br /&gt;
   I/O&lt;br /&gt;
   limits, floppy disk&lt;br /&gt;
   locking&lt;br /&gt;
   protection&lt;br /&gt;
   seek position&lt;br /&gt;
   sharing&lt;br /&gt;
   system&lt;br /&gt;
File Allocation Table&lt;br /&gt;
filenames, OS/2 and MS-DOS&lt;br /&gt;
files&lt;br /&gt;
   .DLL&lt;br /&gt;
   .EXE&lt;br /&gt;
   linking&lt;br /&gt;
   naming&lt;br /&gt;
   .OBJ&lt;br /&gt;
file system&lt;br /&gt;
   future of&lt;br /&gt;
   hierarchical&lt;br /&gt;
   installable&lt;br /&gt;
file system name space&lt;br /&gt;
   named pipes and&lt;br /&gt;
file utilization&lt;br /&gt;
FIND utility program&lt;br /&gt;
flags register&lt;br /&gt;
flat model, 80386 processor&lt;br /&gt;
flexibility, maximum&lt;br /&gt;
flush operation, buffer&lt;br /&gt;
forced events&lt;br /&gt;
foreground&lt;br /&gt;
   category&lt;br /&gt;
   processing&lt;br /&gt;
   threads and applications&lt;br /&gt;
fprintf&lt;br /&gt;
fragmentation&lt;br /&gt;
   disk&lt;br /&gt;
   internal&lt;br /&gt;
functions&lt;br /&gt;
   addition of&lt;br /&gt;
   resident&lt;br /&gt;
future versions of OS/2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
G&lt;br /&gt;
----&lt;br /&gt;
garbage collection&lt;br /&gt;
garbage handles&lt;br /&gt;
general failure error&lt;br /&gt;
general priority category&lt;br /&gt;
general protection fault (GP fault)&lt;br /&gt;
   errors&lt;br /&gt;
GetDosVar&lt;br /&gt;
giveaway shared memory&lt;br /&gt;
global data&lt;br /&gt;
global data segments&lt;br /&gt;
Global Descriptor Table (GDT)&lt;br /&gt;
global subsystem initialization&lt;br /&gt;
glossary&lt;br /&gt;
goals&lt;br /&gt;
   design&lt;br /&gt;
   OS/2&lt;br /&gt;
GP faults&lt;br /&gt;
grandparent processes&lt;br /&gt;
graphical user interface&lt;br /&gt;
   standard&lt;br /&gt;
graphics, VIO and&lt;br /&gt;
graphics devices&lt;br /&gt;
graphics drivers, device-independent&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
H&lt;br /&gt;
----&lt;br /&gt;
handles&lt;br /&gt;
   closing&lt;br /&gt;
   closing with dynamic links&lt;br /&gt;
   duplicated and inherited&lt;br /&gt;
   garbage&lt;br /&gt;
   semaphore&lt;br /&gt;
hard error daemon&lt;br /&gt;
hard errors&lt;br /&gt;
   handling in real mode&lt;br /&gt;
hardware, nonstandard&lt;br /&gt;
hardware devices. See devices&lt;br /&gt;
hardware interrupts&lt;br /&gt;
   device drivers and&lt;br /&gt;
hardware-specific interfaces&lt;br /&gt;
Hayes modems&lt;br /&gt;
heap algorithm&lt;br /&gt;
heap objects&lt;br /&gt;
Hercules Graphics Card&lt;br /&gt;
hierarchical file system&lt;br /&gt;
hooking the keyboard vector&lt;br /&gt;
huge memory&lt;br /&gt;
huge model addressing&lt;br /&gt;
huge segments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I&lt;br /&gt;
----&lt;br /&gt;
IBM PC/AT&lt;br /&gt;
INCLUDE&lt;br /&gt;
Independent Software Vendors (ISVs)&lt;br /&gt;
industrial revolution, second&lt;br /&gt;
infosegs&lt;br /&gt;
inheritance&lt;br /&gt;
INIT&lt;br /&gt;
initialization&lt;br /&gt;
   device driver&lt;br /&gt;
   global subsystem&lt;br /&gt;
   instance subsystem&lt;br /&gt;
initialization entry points, dynamic link&lt;br /&gt;
input/output. See I/O&lt;br /&gt;
installable file system (IFS)&lt;br /&gt;
instance data&lt;br /&gt;
   segment&lt;br /&gt;
instance subsystem initialization&lt;br /&gt;
instructions&lt;br /&gt;
   sequence of&lt;br /&gt;
insufficient memory&lt;br /&gt;
INT 21&lt;br /&gt;
INT 2F multiplex function&lt;br /&gt;
integrated applications&lt;br /&gt;
integrity, data&lt;br /&gt;
Intel. See also 8086/8088 processor, 80286 processor, 80386 processor&lt;br /&gt;
   80286 processor&lt;br /&gt;
   80386 processor&lt;br /&gt;
   8080 processor&lt;br /&gt;
   8086/8088 processor&lt;br /&gt;
interactive&lt;br /&gt;
   category&lt;br /&gt;
   processes&lt;br /&gt;
   programs&lt;br /&gt;
interface, user&lt;br /&gt;
interlocking&lt;br /&gt;
internal fragmentation&lt;br /&gt;
interprocess communication (IPC)&lt;br /&gt;
interrupt handling code&lt;br /&gt;
interruptible block&lt;br /&gt;
interrupts&lt;br /&gt;
   3x box emulation&lt;br /&gt;
   hardware&lt;br /&gt;
   signals and&lt;br /&gt;
interrupt service routine, device driver&lt;br /&gt;
interrupt-time thread&lt;br /&gt;
interrupt vectors&lt;br /&gt;
   hooking&lt;br /&gt;
I/O&lt;br /&gt;
   architecture&lt;br /&gt;
   asynchronous&lt;br /&gt;
   background&lt;br /&gt;
   disk requests&lt;br /&gt;
   efficiency&lt;br /&gt;
   file&lt;br /&gt;
   port access&lt;br /&gt;
   privilege mechanism&lt;br /&gt;
   requesting an operation&lt;br /&gt;
   signals and&lt;br /&gt;
   video&lt;br /&gt;
I/O-bound applications&lt;br /&gt;
IOCTL call&lt;br /&gt;
IP register&lt;br /&gt;
IPC See also interprocess communication&lt;br /&gt;
   combining forms of&lt;br /&gt;
   using with dynamic links&lt;br /&gt;
IRET instruction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
K&lt;br /&gt;
----&lt;br /&gt;
KBD&lt;br /&gt;
kernel&lt;br /&gt;
   interfacing with dynamic links&lt;br /&gt;
   mode&lt;br /&gt;
keyboard, processes using the&lt;br /&gt;
keyboard mode, cooked and raw&lt;br /&gt;
keyboard vector, hooking the&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&lt;br /&gt;
----&lt;br /&gt;
label names, volume&lt;br /&gt;
large disk support&lt;br /&gt;
LAR instruction&lt;br /&gt;
latency, rotational&lt;br /&gt;
Least Recently Used (LRU) scheme&lt;br /&gt;
levels, compatibility&lt;br /&gt;
LIB&lt;br /&gt;
libraries&lt;br /&gt;
   dynamic link&lt;br /&gt;
   sharing dynamic link&lt;br /&gt;
   subroutine and runtime&lt;br /&gt;
linear address space&lt;br /&gt;
linking&lt;br /&gt;
   dynamic&lt;br /&gt;
   static&lt;br /&gt;
loadtime dynamic linking&lt;br /&gt;
local area networks. See networks&lt;br /&gt;
Local Descriptor Table (LDT)&lt;br /&gt;
locality of reference&lt;br /&gt;
localization of errors&lt;br /&gt;
local variables&lt;br /&gt;
locking, file. See file and record locking&lt;br /&gt;
logical&lt;br /&gt;
   device and directory name facility&lt;br /&gt;
   devices&lt;br /&gt;
   directories&lt;br /&gt;
   disks, partitioning&lt;br /&gt;
low priority category&lt;br /&gt;
LSL instruction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
M&lt;br /&gt;
----&lt;br /&gt;
magic cookie&lt;br /&gt;
mass marketing, software and&lt;br /&gt;
media volume management&lt;br /&gt;
memory&lt;br /&gt;
   display&lt;br /&gt;
   huge&lt;br /&gt;
   insufficient&lt;br /&gt;
   layout of system&lt;br /&gt;
   limitations of in 8088 processor&lt;br /&gt;
   named shared&lt;br /&gt;
   physical&lt;br /&gt;
   protection&lt;br /&gt;
   shared&lt;br /&gt;
   shared giveaway&lt;br /&gt;
   swapping&lt;br /&gt;
   swapping segments in&lt;br /&gt;
   swapped-out&lt;br /&gt;
   utilization&lt;br /&gt;
   video&lt;br /&gt;
   virtual&lt;br /&gt;
memory management&lt;br /&gt;
   API&lt;br /&gt;
   hardware&lt;br /&gt;
memory manager&lt;br /&gt;
   definition of&lt;br /&gt;
memory not present fault&lt;br /&gt;
memory objects, tracking&lt;br /&gt;
memory overcommit&lt;br /&gt;
memory segments, allocating&lt;br /&gt;
memory suballocation&lt;br /&gt;
memory unit&lt;br /&gt;
mental work, mechanizing&lt;br /&gt;
message mode&lt;br /&gt;
Microsoft, vision of&lt;br /&gt;
Microsoft Macro Assembler (MASM)&lt;br /&gt;
model, architectural&lt;br /&gt;
modes&lt;br /&gt;
   incompatible&lt;br /&gt;
   keyboard&lt;br /&gt;
modular tools&lt;br /&gt;
module name&lt;br /&gt;
monitors, device&lt;br /&gt;
MOU&lt;br /&gt;
MS-DOS&lt;br /&gt;
   compatibility with&lt;br /&gt;
   environment list&lt;br /&gt;
   expandability of&lt;br /&gt;
   filenames in&lt;br /&gt;
   history of&lt;br /&gt;
   memory allocation in&lt;br /&gt;
   running applications in OS/2&lt;br /&gt;
   version 1.0&lt;br /&gt;
   version 2.0&lt;br /&gt;
   version 3.0&lt;br /&gt;
   version 4.0&lt;br /&gt;
   version 5.0&lt;br /&gt;
   versions of&lt;br /&gt;
multitasking&lt;br /&gt;
   data integrity and&lt;br /&gt;
   definition of&lt;br /&gt;
   device drivers and&lt;br /&gt;
   full&lt;br /&gt;
   MS-DOS version&lt;br /&gt;
   parallel&lt;br /&gt;
   serial&lt;br /&gt;
multiuser systems, thrashing and&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
N&lt;br /&gt;
----&lt;br /&gt;
named pipes&lt;br /&gt;
   multiple instances of&lt;br /&gt;
named shared memory&lt;br /&gt;
name generation, compatibility and&lt;br /&gt;
names, dynamic link&lt;br /&gt;
name set&lt;br /&gt;
network piggybacking&lt;br /&gt;
networks&lt;br /&gt;
   access to&lt;br /&gt;
   compatibility on&lt;br /&gt;
   file protection on&lt;br /&gt;
   importance of security on&lt;br /&gt;
network virtual circuit&lt;br /&gt;
NUMADD program&lt;br /&gt;
NUMARITH application&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
O&lt;br /&gt;
----&lt;br /&gt;
.OBJ files&lt;br /&gt;
obj namebuf&lt;br /&gt;
object name buffer&lt;br /&gt;
objects&lt;br /&gt;
   defining&lt;br /&gt;
   file system name space and&lt;br /&gt;
office automation&lt;br /&gt;
   objective of&lt;br /&gt;
   operating system for&lt;br /&gt;
offset registers&lt;br /&gt;
OPEN function&lt;br /&gt;
open system&lt;br /&gt;
operating systems&lt;br /&gt;
   multitasking&lt;br /&gt;
   other&lt;br /&gt;
ordinals, entry&lt;br /&gt;
orphaned semaphores&lt;br /&gt;
overlays, code&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
P&lt;br /&gt;
----&lt;br /&gt;
packet, request&lt;br /&gt;
paged virtual memory&lt;br /&gt;
paperless office&lt;br /&gt;
parallel multitasking&lt;br /&gt;
parent processes&lt;br /&gt;
partitioning, extended&lt;br /&gt;
passwords, file&lt;br /&gt;
Paterson, Tim&lt;br /&gt;
PATH&lt;br /&gt;
pathnames&lt;br /&gt;
peripherals, high-bandwidth&lt;br /&gt;
permissions&lt;br /&gt;
physical memory&lt;br /&gt;
PID&lt;br /&gt;
piggybacking&lt;br /&gt;
pipes&lt;br /&gt;
   anonymous&lt;br /&gt;
   named&lt;br /&gt;
pointer, seek&lt;br /&gt;
preemptive scheduler&lt;br /&gt;
presentation manager&lt;br /&gt;
   choosing between VIO and&lt;br /&gt;
   DDE and&lt;br /&gt;
priority&lt;br /&gt;
   categories of&lt;br /&gt;
   time-critical&lt;br /&gt;
priority scheduler&lt;br /&gt;
   semaphore&lt;br /&gt;
privilege mechanism, I/O&lt;br /&gt;
privilege mode&lt;br /&gt;
privilege transition&lt;br /&gt;
process termination signal handler&lt;br /&gt;
processes&lt;br /&gt;
   calling from OS/2&lt;br /&gt;
   child&lt;br /&gt;
   client&lt;br /&gt;
   command&lt;br /&gt;
   daemon&lt;br /&gt;
   dynamic linking and&lt;br /&gt;
   foreground and background&lt;br /&gt;
   grandparent&lt;br /&gt;
   interactive&lt;br /&gt;
   interfacing with dynamic links&lt;br /&gt;
   parent&lt;br /&gt;
   problems with multiple&lt;br /&gt;
   threads and&lt;br /&gt;
processing&lt;br /&gt;
   asynchronous&lt;br /&gt;
   foreground and background&lt;br /&gt;
processing unit&lt;br /&gt;
process tree&lt;br /&gt;
programming model, device drivers&lt;br /&gt;
programs&lt;br /&gt;
   debugger&lt;br /&gt;
   executing&lt;br /&gt;
   interactive&lt;br /&gt;
   running on OS/2&lt;br /&gt;
program termination signal&lt;br /&gt;
PROMPT&lt;br /&gt;
protected environment&lt;br /&gt;
protection&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
   model&lt;br /&gt;
   side-effects&lt;br /&gt;
protect mode&lt;br /&gt;
   huge model addressing in&lt;br /&gt;
   real mode versus&lt;br /&gt;
protocol, DDE&lt;br /&gt;
pseudo interrupts&lt;br /&gt;
Ptrace&lt;br /&gt;
pure segment&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Q, R&lt;br /&gt;
----&lt;br /&gt;
queues&lt;br /&gt;
RAM See also memory&lt;br /&gt;
   available&lt;br /&gt;
   semaphores&lt;br /&gt;
Random Access Memory. See RAM&lt;br /&gt;
random selector values&lt;br /&gt;
RAS information&lt;br /&gt;
raw mode&lt;br /&gt;
real mode&lt;br /&gt;
   80386 processor&lt;br /&gt;
   8086 processor&lt;br /&gt;
   applications&lt;br /&gt;
   compatibility box&lt;br /&gt;
   device drivers in&lt;br /&gt;
   hard errors in&lt;br /&gt;
   huge model addressing in&lt;br /&gt;
   protect mode versus&lt;br /&gt;
   screen group&lt;br /&gt;
   swapping in&lt;br /&gt;
real time, tracking passage of&lt;br /&gt;
record locking&lt;br /&gt;
redirector code (MS-DOS)&lt;br /&gt;
reference locality&lt;br /&gt;
registers&lt;br /&gt;
   offset&lt;br /&gt;
   segment&lt;br /&gt;
   signals and&lt;br /&gt;
Reliability, Availability, and Serviceability (RAS)&lt;br /&gt;
religion, OS/2&lt;br /&gt;
remote procedure call&lt;br /&gt;
request packet&lt;br /&gt;
resident functions&lt;br /&gt;
resources, manipulating&lt;br /&gt;
return()&lt;br /&gt;
ring 3&lt;br /&gt;
ring transition&lt;br /&gt;
ROM BIOS&lt;br /&gt;
root directory&lt;br /&gt;
rotational latency&lt;br /&gt;
runtime dynamic linking&lt;br /&gt;
runtime library&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
S&lt;br /&gt;
----&lt;br /&gt;
scheduler&lt;br /&gt;
   preemptive&lt;br /&gt;
   priority&lt;br /&gt;
   semaphore&lt;br /&gt;
SCP-DOS&lt;br /&gt;
screen device, handle for&lt;br /&gt;
screen groups&lt;br /&gt;
   multiple&lt;br /&gt;
   using in 3x box&lt;br /&gt;
screen images, manipulating&lt;br /&gt;
screen-save operation&lt;br /&gt;
screen switch&lt;br /&gt;
screen updates, requirements for&lt;br /&gt;
Seattle Computer Products&lt;br /&gt;
sector aligned calls&lt;br /&gt;
sectors&lt;br /&gt;
   blocks of&lt;br /&gt;
security, dynamic link&lt;br /&gt;
seek pointer&lt;br /&gt;
segment arithmetic&lt;br /&gt;
segment fault&lt;br /&gt;
segments&lt;br /&gt;
   80286 architecture&lt;br /&gt;
   80386 architecture&lt;br /&gt;
   data&lt;br /&gt;
   dynamic link&lt;br /&gt;
   executing from data&lt;br /&gt;
   global data&lt;br /&gt;
   huge&lt;br /&gt;
   internally shared&lt;br /&gt;
   nonswappable&lt;br /&gt;
   pure&lt;br /&gt;
   sharing&lt;br /&gt;
   stack&lt;br /&gt;
   status and information&lt;br /&gt;
segment selectors&lt;br /&gt;
segment swapping&lt;br /&gt;
semaphore handles&lt;br /&gt;
semaphores&lt;br /&gt;
   data integrity and&lt;br /&gt;
   file system name space and&lt;br /&gt;
   orphaned&lt;br /&gt;
   RAM&lt;br /&gt;
   recovering&lt;br /&gt;
   scheduling&lt;br /&gt;
   system&lt;br /&gt;
serial multitasking&lt;br /&gt;
session manager&lt;br /&gt;
SetSignalHandler&lt;br /&gt;
shared memory&lt;br /&gt;
   giveaway&lt;br /&gt;
   named&lt;br /&gt;
sharing segments&lt;br /&gt;
side effects&lt;br /&gt;
   controlling&lt;br /&gt;
   dynamic link&lt;br /&gt;
   protection&lt;br /&gt;
signal handler address&lt;br /&gt;
signal handlers&lt;br /&gt;
signaling&lt;br /&gt;
signals&lt;br /&gt;
   holding&lt;br /&gt;
SIGTERM signal&lt;br /&gt;
single-tasking&lt;br /&gt;
single-tasking environment, containing errors in&lt;br /&gt;
software design, OS/2 concepts of&lt;br /&gt;
software tools approach&lt;br /&gt;
speed execution&lt;br /&gt;
stable environment&lt;br /&gt;
stack frames&lt;br /&gt;
stacks, thread&lt;br /&gt;
stack segments&lt;br /&gt;
standard error. See STDERR&lt;br /&gt;
standard file handles&lt;br /&gt;
standard input. See STDIN&lt;br /&gt;
standard output. See STDOUT&lt;br /&gt;
standards, software&lt;br /&gt;
static data segments, adding&lt;br /&gt;
static links&lt;br /&gt;
status and information, segment&lt;br /&gt;
STDERR&lt;br /&gt;
STDIN&lt;br /&gt;
   compatibility with presentation manager&lt;br /&gt;
STDIN/STDOUT mechanism, I/O&lt;br /&gt;
STDOUT&lt;br /&gt;
   compatibility with presentation manager&lt;br /&gt;
strings, environment&lt;br /&gt;
suballocation, memory&lt;br /&gt;
subroutine library&lt;br /&gt;
subroutines, dynamic link packages as&lt;br /&gt;
subsystems&lt;br /&gt;
   dynamic link&lt;br /&gt;
   special support&lt;br /&gt;
subtask model&lt;br /&gt;
subtrees, command&lt;br /&gt;
   controlling&lt;br /&gt;
swapped-out memory&lt;br /&gt;
SWAPPER.DAT&lt;br /&gt;
swapping&lt;br /&gt;
   segment&lt;br /&gt;
system&lt;br /&gt;
   conventions&lt;br /&gt;
   crashes&lt;br /&gt;
   diagnosis&lt;br /&gt;
   File Table (SFT)&lt;br /&gt;
   memory layout&lt;br /&gt;
   security, debuggers and&lt;br /&gt;
   semaphores&lt;br /&gt;
system, hanging the&lt;br /&gt;
systems&lt;br /&gt;
   closed and open&lt;br /&gt;
   file&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
T&lt;br /&gt;
----&lt;br /&gt;
task See also processes&lt;br /&gt;
task-time thread&lt;br /&gt;
technological breakthroughs&lt;br /&gt;
TEMP&lt;br /&gt;
terminate and stay resident mechanism&lt;br /&gt;
thrashing&lt;br /&gt;
thread 1&lt;br /&gt;
thread death&lt;br /&gt;
thread of execution&lt;br /&gt;
thread ID&lt;br /&gt;
threads&lt;br /&gt;
   background&lt;br /&gt;
   captive&lt;br /&gt;
   collisions of&lt;br /&gt;
   dynamic linking and&lt;br /&gt;
   foreground and background processing with&lt;br /&gt;
   foreground and interactive&lt;br /&gt;
   from different processes&lt;br /&gt;
   I/O-bound&lt;br /&gt;
   interrupt-time&lt;br /&gt;
   low priority&lt;br /&gt;
   multiple&lt;br /&gt;
   organizing programs using&lt;br /&gt;
   performance characteristics of&lt;br /&gt;
   priority categories of&lt;br /&gt;
   problems with multiple&lt;br /&gt;
   switching the CPU among&lt;br /&gt;
   task-time&lt;br /&gt;
   working sets and&lt;br /&gt;
thread stacks&lt;br /&gt;
throughput balancing&lt;br /&gt;
time-critical priority category&lt;br /&gt;
time intervals&lt;br /&gt;
timer services&lt;br /&gt;
time slice&lt;br /&gt;
timing, thread&lt;br /&gt;
tools, software&lt;br /&gt;
TOPS-10&lt;br /&gt;
TREE&lt;br /&gt;
tree, process&lt;br /&gt;
tree-structured file system&lt;br /&gt;
Trojan programs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
U&lt;br /&gt;
----&lt;br /&gt;
UNIX&lt;br /&gt;
   naming conventions in&lt;br /&gt;
   ptrace facility&lt;br /&gt;
upper- and lowercase&lt;br /&gt;
upward compatibility&lt;br /&gt;
user interface&lt;br /&gt;
   graphical&lt;br /&gt;
   presentation manager&lt;br /&gt;
   VIO&lt;br /&gt;
utilization&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
V&lt;br /&gt;
----&lt;br /&gt;
variables, local&lt;br /&gt;
versions, future OS/2&lt;br /&gt;
video hardware, accessing&lt;br /&gt;
VIO,&lt;br /&gt;
   choosing between presentation manager and&lt;br /&gt;
   dynamic link entry points&lt;br /&gt;
   graphics under&lt;br /&gt;
   subsystem&lt;br /&gt;
   user interface&lt;br /&gt;
VioGetBuf&lt;br /&gt;
VioModeWait&lt;br /&gt;
VioSavRedrawWait&lt;br /&gt;
VioScrLock&lt;br /&gt;
virtual circuit, network&lt;br /&gt;
virtual display device&lt;br /&gt;
virtualization&lt;br /&gt;
   device&lt;br /&gt;
virtualizing the environment&lt;br /&gt;
virtual memory&lt;br /&gt;
   concepts of&lt;br /&gt;
   paged&lt;br /&gt;
virtual real mode (80386)&lt;br /&gt;
volume, disk&lt;br /&gt;
volume ID&lt;br /&gt;
volume-oriented operations&lt;br /&gt;
Von Neumann, John&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
W&lt;br /&gt;
----&lt;br /&gt;
windowing interface&lt;br /&gt;
working directories&lt;br /&gt;
working set&lt;br /&gt;
WORM drives&lt;br /&gt;
writethrough&lt;br /&gt;
WYSIWYG&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
X&lt;br /&gt;
----&lt;br /&gt;
XENIX&lt;br /&gt;
&lt;br /&gt;
Z&lt;br /&gt;
----&lt;br /&gt;
zero-terminated strings&lt;br /&gt;
&lt;br /&gt;
[[Category: OS/2]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=Inside_OS/2&amp;diff=36398</id>
		<title>Inside OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=Inside_OS/2&amp;diff=36398"/>
				<updated>2025-06-13T12:08:48Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the text from the book.&lt;br /&gt;
&lt;br /&gt;
INSIDE OS/2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
INSIDE OS/2 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Gordon Letwin &lt;br /&gt;
Chief Architect, Systems Software, Microsoft(R)&lt;br /&gt;
&lt;br /&gt;
Foreword by Bill Gates &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
PUBLISHED BY&lt;br /&gt;
Microsoft Press&lt;br /&gt;
A Division of Microsoft Corporation&lt;br /&gt;
16011 NE 36th Way, Box 97017, Redmond, Washington 98073-9717&lt;br /&gt;
&lt;br /&gt;
Copyright (C) 1988 by Microsoft Press&lt;br /&gt;
All rights reserved. No part of the contents of this book may be&lt;br /&gt;
reproduced or transmitted in any form or by any means without the written&lt;br /&gt;
permission of the publisher.&lt;br /&gt;
&lt;br /&gt;
Library of Congress Cataloging in Publication Data&lt;br /&gt;
Letwin, Gordon.&lt;br /&gt;
Inside OS/2.&lt;br /&gt;
&lt;br /&gt;
Includes index.&lt;br /&gt;
1. MS OS/2 (Computer operating system) I. Title.&lt;br /&gt;
II. Title: Inside OS/Two.&lt;br /&gt;
QA76.76.063L48    1988    005.4'46    87-31579&lt;br /&gt;
ISBN 1-55615-117-9&lt;br /&gt;
&lt;br /&gt;
Printed and bound in the United States of America&lt;br /&gt;
&lt;br /&gt;
1 2 3 4 5 6 7 8 9 MLML 8 9 0 9 8&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade in the United States by Harper &amp;amp; Row.&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade in Canada by General Publishing Company, Ltd.&lt;br /&gt;
&lt;br /&gt;
Distributed to the book trade outside the United States and Canada by&lt;br /&gt;
Penguin Books Ltd.&lt;br /&gt;
&lt;br /&gt;
Penguin Books Ltd., Harmondsworth, Middlesex, England&lt;br /&gt;
Penguin Books Australia Ltd., Ringwood, Victoria, Australia&lt;br /&gt;
Penguin Books N.Z. Ltd., 182-190 Wairau Road, Auckland 10, New Zealand&lt;br /&gt;
&lt;br /&gt;
British Cataloging in publication Data available&lt;br /&gt;
&lt;br /&gt;
Editor: Patricia Pratt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                              Dedication&lt;br /&gt;
&lt;br /&gt;
                               To R.P.W.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Contents&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Foreword by Bill Gates&lt;br /&gt;
&lt;br /&gt;
Introduction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part I: The Project&lt;br /&gt;
&lt;br /&gt;
Chapter 1.  History of the Project&lt;br /&gt;
             1.1  MS-DOS version 1.0&lt;br /&gt;
             1.2  MS-DOS version 2.0&lt;br /&gt;
             1.3  MS-DOS version 3.0&lt;br /&gt;
             1.4  MS-DOS version 4.0&lt;br /&gt;
&lt;br /&gt;
Chapter 2.  Goals and Compatibility Issues&lt;br /&gt;
             2.1  Goals&lt;br /&gt;
                   2.1.1  Graphical User Interface&lt;br /&gt;
                   2.1.2  Multitasking&lt;br /&gt;
                   2.1.3  Memory Management&lt;br /&gt;
                   2.1.4  Protection&lt;br /&gt;
                   2.1.5  Encapsulation&lt;br /&gt;
                   2.1.6  Interprocess Communication (IPC)&lt;br /&gt;
                   2.1.7  Direct Device Access&lt;br /&gt;
             2.2  Compatibility Issues&lt;br /&gt;
                   2.2.1  Real Mode vs Protect Mode&lt;br /&gt;
                   2.2.2  Running Applications in Real (Compatibility) Mode&lt;br /&gt;
                           2.2.2.1  Memory Utilization&lt;br /&gt;
                           2.2.2.2  File Locking&lt;br /&gt;
                           2.2.2.3  Network Piggybacking&lt;br /&gt;
                   2.2.3  Popular Function Compatibility&lt;br /&gt;
                   2.2.4  Downward Compatibility&lt;br /&gt;
                           2.2.4.1  Family API&lt;br /&gt;
                           2.2.4.2  Network Server-Client Compatibility&lt;br /&gt;
&lt;br /&gt;
Chapter 3.  The OS/2 Religion&lt;br /&gt;
             3.1  Maximum Flexibility&lt;br /&gt;
             3.2  Stable Environment&lt;br /&gt;
                   3.2.1  Memory Protection&lt;br /&gt;
                   3.2.2  Side-Effects Protection&lt;br /&gt;
             3.3  Localization of Errors&lt;br /&gt;
             3.4  Software Tools Approach&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part II: The Architecture&lt;br /&gt;
&lt;br /&gt;
Chapter 4.  Multitasking&lt;br /&gt;
             4.1  Subtask Model&lt;br /&gt;
                   4.1.1  Standard File Handles&lt;br /&gt;
                   4.1.2  Anonymous Pipes&lt;br /&gt;
                   4.1.3  Details, Details&lt;br /&gt;
             4.2  PIDs and Command Subtrees&lt;br /&gt;
             4.3  DosExecPgm&lt;br /&gt;
             4.4  DosCWait&lt;br /&gt;
             4.5  Control of Child Tasks and Command Subtrees&lt;br /&gt;
                   4.5.1  DosKillProcess&lt;br /&gt;
                   4.5.2  DosSetPriority&lt;br /&gt;
&lt;br /&gt;
Chapter 5.  Threads and Scheduler/Priorities&lt;br /&gt;
             5.1  Threads&lt;br /&gt;
                   5.1.1  Thread Stacks&lt;br /&gt;
                   5.1.2  Thread Uses&lt;br /&gt;
                           5.1.2.1  Foreground and Background Work&lt;br /&gt;
                           5.1.2.2  Asynchronous Processing&lt;br /&gt;
                           5.1.2.3  Speed Execution&lt;br /&gt;
                           5.1.2.4  Organizing Programs&lt;br /&gt;
                   5.1.3  Interlocking&lt;br /&gt;
                           5.1.3.1  Local Variables&lt;br /&gt;
                           5.1.3.2  RAM Semaphores&lt;br /&gt;
                           5.1.3.3  DosSuspendThread&lt;br /&gt;
                           5.1.3.4  DosEnterCritSec/DosExitCritSec&lt;br /&gt;
                   5.1.4  Thread 1&lt;br /&gt;
                   5.1.5  Thread Death&lt;br /&gt;
                   5.1.6  Performance Characteristics&lt;br /&gt;
             5.2  Scheduler/Priorities&lt;br /&gt;
                   5.2.1  General Priority Category&lt;br /&gt;
                           5.2.1.1  Background Subcategory&lt;br /&gt;
                           5.2.1.2  Foreground and Interactive&lt;br /&gt;
                                     Subcategories&lt;br /&gt;
                           5.2.1.3  Throughput Balancing&lt;br /&gt;
                   5.2.2  Time-Critical Priority Category&lt;br /&gt;
                   5.2.3  Force Background Priority Category&lt;br /&gt;
                   5.2.4  Setting Process/Thread Priorities&lt;br /&gt;
&lt;br /&gt;
Chapter 6.  The User Interface&lt;br /&gt;
             6.1  VIO User Interface&lt;br /&gt;
             6.2  The Presentation Manager User Interface&lt;br /&gt;
             6.3  Presentation Manager and VIO Compatibility&lt;br /&gt;
&lt;br /&gt;
Chapter 7.  Dynamic Linking&lt;br /&gt;
             7.1  Static Linking&lt;br /&gt;
             7.2  Loadtime Dynamic Linking&lt;br /&gt;
             7.3  Runtime Dynamic Linking&lt;br /&gt;
             7.4  Dynlinks, Processes, and Threads&lt;br /&gt;
             7.5  Data&lt;br /&gt;
                   7.5.1  Instance Data&lt;br /&gt;
                   7.5.2  Global Data&lt;br /&gt;
             7.6  Dynamic Link Packages As Subroutines&lt;br /&gt;
             7.7  Subsystems&lt;br /&gt;
                   7.7.1  Special Subsystem Support&lt;br /&gt;
             7.8  Dynamic Links As Interfaces to Other Processes&lt;br /&gt;
             7.9  Dynamic Links As Interfaces to the Kernel&lt;br /&gt;
             7.10 The Architectural Role of Dynamic Links&lt;br /&gt;
             7.11 Implementation Details&lt;br /&gt;
                   7.11.1  Dynlink Data Security&lt;br /&gt;
                   7.11.2  Dynlink Life, Death, and Sharing&lt;br /&gt;
                   7.11.3  Dynlink Side Effects&lt;br /&gt;
             7.12 Dynlink Names&lt;br /&gt;
&lt;br /&gt;
Chapter 8.  File System Name Space&lt;br /&gt;
             8.1  Filenames&lt;br /&gt;
             8.2  Network Access&lt;br /&gt;
             8.3  Name Generation and Compatibility&lt;br /&gt;
             8.4  Permissions&lt;br /&gt;
             8.5  Other Objects in the File System Name Space&lt;br /&gt;
&lt;br /&gt;
Chapter 9.  Memory Management&lt;br /&gt;
             9.1  Protection Model&lt;br /&gt;
             9.2  Memory Management API&lt;br /&gt;
                   9.2.1  Shared Memory&lt;br /&gt;
                   9.2.2  Huge Memory&lt;br /&gt;
                   9.2.3  Executing from Data Segments&lt;br /&gt;
                   9.2.4  Memory Suballocation&lt;br /&gt;
             9.3  Segment Swapping&lt;br /&gt;
                   9.3.1  Swapping Miscellany&lt;br /&gt;
             9.4  Status and Information&lt;br /&gt;
&lt;br /&gt;
Chapter 10. Environment Strings&lt;br /&gt;
&lt;br /&gt;
Chapter 11. Interprocess Communication (IPC)&lt;br /&gt;
             11.1  Shared Memory&lt;br /&gt;
             11.2  Semaphores&lt;br /&gt;
                    11.2.1  Semaphore Recovery&lt;br /&gt;
                    11.2.2  Semaphore Scheduling&lt;br /&gt;
             11.3  Named Pipes&lt;br /&gt;
             11.4  Queues&lt;br /&gt;
             11.5  Dynamic Data Exchange (DDE)&lt;br /&gt;
             11.6  Signals&lt;br /&gt;
             11.7  Combining IPC Forms&lt;br /&gt;
&lt;br /&gt;
Chapter 12. Signals&lt;br /&gt;
&lt;br /&gt;
Chapter 13. The Presentation Manager and VIO&lt;br /&gt;
             13.1  Choosing Between PM and VIO&lt;br /&gt;
             13.2  Background I/O&lt;br /&gt;
             13.3  Graphics Under VIO&lt;br /&gt;
&lt;br /&gt;
Chapter 14. Interactive Programs&lt;br /&gt;
             14.1  I/O Architecture&lt;br /&gt;
             14.2  Ctrl-C and Ctrl-Break Handling&lt;br /&gt;
&lt;br /&gt;
Chapter 15. The File System&lt;br /&gt;
             15.1  The OS/2 File System&lt;br /&gt;
             15.2  Media Volume Management&lt;br /&gt;
             15.3  I/O Efficiency&lt;br /&gt;
&lt;br /&gt;
Chapter 16. Device Monitors, Data Integrity, and Timer Services&lt;br /&gt;
             16.1  Device Monitors&lt;br /&gt;
             16.2  Data Integrity&lt;br /&gt;
                    16.2.1  Semaphores&lt;br /&gt;
                    16.2.2  DosBufReset&lt;br /&gt;
                    16.2.3  Writethroughs&lt;br /&gt;
             16.3  Timer Services&lt;br /&gt;
&lt;br /&gt;
Chapter 17. Device Drivers and Hard Errors&lt;br /&gt;
             17.1  Device Drivers&lt;br /&gt;
                    17.1.1  Device Drivers and OS/2 Communication&lt;br /&gt;
                    17.1.2  Device Driver Programming Model&lt;br /&gt;
                    17.1.3  Device Management&lt;br /&gt;
                    17.1.4  Dual Mode&lt;br /&gt;
             17.2  Hard Errors&lt;br /&gt;
                    17.2.1  The Hard Error Daemon&lt;br /&gt;
                    17.2.2  Application Hard Error Handling&lt;br /&gt;
&lt;br /&gt;
Chapter 18. I/O Privilege Mechanism and Debugging/Ptrace&lt;br /&gt;
             18.1  I/O Privilege Mechanism&lt;br /&gt;
             18.2  Debugging/Ptrace&lt;br /&gt;
&lt;br /&gt;
Chapter 19. The 3x Box&lt;br /&gt;
&lt;br /&gt;
Chapter 20. Family API&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part III: The Future&lt;br /&gt;
&lt;br /&gt;
Chapter 21. The Future&lt;br /&gt;
             21.1  File System&lt;br /&gt;
             21.2  The 80386&lt;br /&gt;
                    21.2.1  Large Segments&lt;br /&gt;
                    21.2.2  Multiple Real Mode Boxes&lt;br /&gt;
                    21.2.3  Full Protection Capability&lt;br /&gt;
                    21.2.4  Other Features&lt;br /&gt;
             21.3  The Next Ten Years&lt;br /&gt;
&lt;br /&gt;
Glossary&lt;br /&gt;
&lt;br /&gt;
Index&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Acknowledgments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Although a book can have a single author, a work such as OS/2 necessarily&lt;br /&gt;
owes its existence to the efforts of a great many people. The architecture&lt;br /&gt;
described herein was hammered out by a joint Microsoft/ IBM design team:&lt;br /&gt;
Ann, Anthony, Carolyn, Ed, Gordon, Jerry, Mark, Mike, Ray, and Ross. This&lt;br /&gt;
team accomplished a great deal of work in a short period of time.&lt;br /&gt;
     The bulk of the credit, and my thanks, go to the engineers who&lt;br /&gt;
designed and implemented the code and made it work. The size of the teams&lt;br /&gt;
involved throughout the project prevents me from listing all the names&lt;br /&gt;
here. It's hard for someone who has not been involved in a software project&lt;br /&gt;
of this scope to imagine the problems, pressure, chaos, and &amp;quot;reality&lt;br /&gt;
shifts&amp;quot; that arise in a never-ending stream. These people deserve great&lt;br /&gt;
credit for their skill and determination in making OS/2 come to pass.&lt;br /&gt;
     Thanks go to the OS/2 development staffers who found time, in the heat&lt;br /&gt;
of the furnace, to review and critique this book: Ian Birrell, Ross Cook,&lt;br /&gt;
Rick Dewitt, Dave Gilman, Vic Heller, Mike McLaughlin, Jeff Parsons, Ray&lt;br /&gt;
Pedrizetti, Robert Reichel, Rajen Shah, Anthony Short, Ben Slivka, Pete&lt;br /&gt;
Stewart, Indira Subramanian, Bryan Willman, and Mark Zbikowski.&lt;br /&gt;
     I'd like to give special thanks to Mark Zbikowski and Aaron Reynolds,&lt;br /&gt;
the &amp;quot;gurus of DOS.&amp;quot; Without their successes there would never have been an&lt;br /&gt;
opportunity for a product such as OS/2.&lt;br /&gt;
     And finally I'd like to thank Bill Gates for creating and captaining&lt;br /&gt;
one hell of a company, thereby making all this possible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Foreword&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 is destined to be a very important piece of software. During the&lt;br /&gt;
next 10 years, millions of programmers and users will utilize this system.&lt;br /&gt;
From time to time they will come across a feature or a limitation and&lt;br /&gt;
wonder why it's there. The best way for them to understand the overall&lt;br /&gt;
philosophy of the system will be to read this book. Gordon Letwin is&lt;br /&gt;
Microsoft's architect for OS/2. In his very clear and sometimes humorous&lt;br /&gt;
way, Gordon has laid out in this book why he included what he did and why&lt;br /&gt;
he didn't include other things.&lt;br /&gt;
     The very first generation of microcomputers were 8-bit machines, such&lt;br /&gt;
as the Commodore Pet, the TRS-80, the Apple II, and the CPM 80 based&lt;br /&gt;
machines. Built into almost all of them was Microsoft's BASIC Interpreter.&lt;br /&gt;
I met Gordon Letwin when I went to visit Heath's personal computer group&lt;br /&gt;
(now part of Zenith). Gordon had written his own BASIC as well as an&lt;br /&gt;
operating system for the Heath system, and he wasn't too happy that his&lt;br /&gt;
management was considering buying someone else's. In a group of about 15&lt;br /&gt;
people, he bluntly pointed out the limitations of my BASIC versus his.&lt;br /&gt;
After Heath licensed my BASIC, I convinced Gordon that Microsoft was the&lt;br /&gt;
place to be if you wanted your great software to be popular, and so he&lt;br /&gt;
became one of Microsoft's first 10 programmers. His first project was to&lt;br /&gt;
single-handedly write a compiler for Microsoft BASIC. He put a sign on his&lt;br /&gt;
door that read&lt;br /&gt;
&lt;br /&gt;
        Do not disturb, feed, poke, tease...the animal&lt;br /&gt;
&lt;br /&gt;
and in 5 months wrote a superb compiler that is still the basis for all our&lt;br /&gt;
BASIC compilers. Unlike the code that a lot of superstar programmers write,&lt;br /&gt;
Gordon's source code is a model of readability and includes precise&lt;br /&gt;
explanations of algorithms and why they were chosen.&lt;br /&gt;
     When the Intel 80286 came along, with its protected mode completely&lt;br /&gt;
separate from its compatible real mode, we had no idea how we were going to&lt;br /&gt;
get at its new capabilities. In fact, we had given up until Gordon came up&lt;br /&gt;
with the patented idea described in this book that has been referred to as&lt;br /&gt;
&amp;quot;turning the car off and on at 60 MPH.&amp;quot; When we first explained the idea to&lt;br /&gt;
Intel and many of its customers, they were sure it wouldn't work. Even&lt;br /&gt;
Gordon wasn't positive it would work until he wrote some test programs that&lt;br /&gt;
proved it did.&lt;br /&gt;
     Gordon's role as an operating systems architect is to overview our&lt;br /&gt;
designs and approaches and make sure they are as simple and as elegant as&lt;br /&gt;
possible. Part of this job includes reviewing people's code. Most&lt;br /&gt;
programmers enjoy having Gordon look over their code and point out how it&lt;br /&gt;
could be improved and simplified. A lot of programs end up about half as&lt;br /&gt;
big after Gordon has explained a better way to write them. Gordon doesn't&lt;br /&gt;
mince words, however, so in at least one case a particularly sensitive&lt;br /&gt;
programmer burst into tears after reading his commentary. Gordon isn't&lt;br /&gt;
content to just look over other people's code. When a particular project&lt;br /&gt;
looks very difficult, he dives in. Currently, Gordon has decided to&lt;br /&gt;
personally write most of our new file system, which will be dramatically&lt;br /&gt;
faster than our present one. On a recent &amp;quot;vacation&amp;quot; he wrote more than 50&lt;br /&gt;
pages of source code.&lt;br /&gt;
     This is Gordon's debut as a book author, and like any good designer he&lt;br /&gt;
has already imagined what bad reviews might say. I think this book is both&lt;br /&gt;
fun and important. I hope you enjoy it as much as I have.&lt;br /&gt;
&lt;br /&gt;
Bill Gates&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Introduction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technological breakthroughs develop in patterns that are distinct from&lt;br /&gt;
patterns of incremental advancements. An incremental advancement--an&lt;br /&gt;
improvement to an existing item--is straightforward and unsurprising. An&lt;br /&gt;
improvement is created; people see the improvement, know what it will do&lt;br /&gt;
for them, and start using it.&lt;br /&gt;
     A major advance without closely related antecedents--a technological&lt;br /&gt;
breakthrough--follows a different pattern. The field of communication is a&lt;br /&gt;
good example. Early in this century, a large infrastructure existed to&lt;br /&gt;
facilitate interpersonal communication. Mail was delivered twice a day, and&lt;br /&gt;
a variety of efficient services relayed messages. A businessman dictated a&lt;br /&gt;
message to his secretary, who gave it to a messenger service. The service&lt;br /&gt;
carried the message to its nearby destination, where a secretary delivered&lt;br /&gt;
it to the recipient.&lt;br /&gt;
     Into this environment came a technological breakthrough--the&lt;br /&gt;
telephone. The invention of the telephone was a breakthrough, not an&lt;br /&gt;
incremental advance, because it provided an entirely new way to communicate.&lt;br /&gt;
It wasn't an improvement over an existing method. That it was&lt;br /&gt;
a breakthrough development impeded its acceptance. Most business people&lt;br /&gt;
considered it a newfangled toy, of little practical use. &amp;quot;What good does it&lt;br /&gt;
do me? By the time I dictate the message, and my secretary writes it down&lt;br /&gt;
and gives it to the mailroom, and they phone the addressee's mailroom, and&lt;br /&gt;
the message is copied--perhaps incorrectly--and delivered to the&lt;br /&gt;
addressee's secretary, it would have been as fast to have it delivered by&lt;br /&gt;
messenger! All my correspondents are close by, and, besides, with&lt;br /&gt;
messengers I don't have to pay someone to sit by the telephone all day in&lt;br /&gt;
case a message comes in.&amp;quot;&lt;br /&gt;
     This is a classic example of the earliest stages of breakthrough&lt;br /&gt;
technology--potential users evaluate it by trying to fit it into present&lt;br /&gt;
work patterns. Our example businessman has not yet realized that he needn't&lt;br /&gt;
write the message down anymore and that it needn't be copied down at the&lt;br /&gt;
destination. He also doesn't realize that the reason his recipients are&lt;br /&gt;
close by is that they have to be for decent messenger delivery. The&lt;br /&gt;
telephone relaxed this requirement, allowing more efficient locations near&lt;br /&gt;
factories and raw materials or where office space was cheaper. But it&lt;br /&gt;
was necessary for the telephone to be accepted before these advantages &lt;br /&gt;
could be realized.&lt;br /&gt;
     Another impedance to the acceptance of a breakthrough technology is&lt;br /&gt;
that the necessary new infrastructure is not in place. A telephone did&lt;br /&gt;
little good if your intended correspondent didn't have one. The nature of&lt;br /&gt;
telephones required a standard; until that standard was set, your&lt;br /&gt;
correspondent might own a phone, but it could be connected to a network&lt;br /&gt;
unreachable by you. Furthermore, because the technology was in its infancy,&lt;br /&gt;
the facilities were crude.&lt;br /&gt;
     These obstacles were not insurmountable. The communications&lt;br /&gt;
requirements of some people were so critical that they were willing to&lt;br /&gt;
invent new procedures and to put up with the problems of the early stages.&lt;br /&gt;
Some people, because of their daring or ambition, used the new system to&lt;br /&gt;
augment their existing system. And finally, because the new technology was&lt;br /&gt;
so powerful, some used it to enhance the existing technology. For example,&lt;br /&gt;
a messenger service might establish several offices with telephone linkage&lt;br /&gt;
between them and use the telephones to speed delivery of short messages by&lt;br /&gt;
phoning them to the office nearest the destination, where they were copied&lt;br /&gt;
down and delivered normally. Using the telephone in this fashion was&lt;br /&gt;
wasteful, but where demand for the old service was high enough, any&lt;br /&gt;
improvement, however &amp;quot;wasteful,&amp;quot; was welcome.&lt;br /&gt;
     After it has a foot in the door, a breakthrough technology is&lt;br /&gt;
unstoppable. After a time, standards are established, the bugs are worked&lt;br /&gt;
out, and, most important, the tool changes its users. Once the telephone&lt;br /&gt;
became available, business and personal practices developed in new&lt;br /&gt;
patterns, patterns that were not considered before because they were not&lt;br /&gt;
possible. Messenger services used to be fast enough, but only because,&lt;br /&gt;
before the telephone, the messenger service was the fastest technology&lt;br /&gt;
available. The telephone changed the life-style of its users.&lt;br /&gt;
     This change in the structure of human activity explains why an&lt;br /&gt;
intelligent person could say, &amp;quot;Telephones are silly gadgets,&amp;quot; and a few&lt;br /&gt;
years later say, &amp;quot;Telephones are indispensable.&amp;quot; This change in the tool&lt;br /&gt;
user--caused by the tool itself--also makes predicting the ultimate effect&lt;br /&gt;
of the new technology difficult. Extrapolating from existing trends is&lt;br /&gt;
wildly inaccurate because the new tool destroys many practices and creates&lt;br /&gt;
wholly unforeseen ones. It's great fun to read early, seemingly silly&lt;br /&gt;
predictions of life in the future and to laugh at the predictors, but the&lt;br /&gt;
predictors were frequently intelligent and educated. Their only mistake was&lt;br /&gt;
in treating the new development as an incremental advance rather than as a&lt;br /&gt;
breakthrough technology. They saw how the new development would improve&lt;br /&gt;
their current practices, but they couldn't see how it would replace those&lt;br /&gt;
practices.&lt;br /&gt;
     Digital computers are an obvious breakthrough technology, and they've&lt;br /&gt;
shared the classic three-stage pattern: &amp;quot;exotic toys,&amp;quot; &amp;quot;limited use,&amp;quot; and&lt;br /&gt;
&amp;quot;indispensable.&amp;quot; Mainframe computers have gone the full route, in the&lt;br /&gt;
milieu of business and scientific computing. IBM's initial estimate of the&lt;br /&gt;
computer market was a few dozen machines. But, as the technology and the&lt;br /&gt;
support infrastructure grew, and as people's ways of working adapted to&lt;br /&gt;
computers, the use of computers grew--from the census bureau, to life&lt;br /&gt;
insurance companies, to payroll systems, and finally to wholly new&lt;br /&gt;
functions such as MIS (Management Information Sciences) systems and airline&lt;br /&gt;
reservation networks.&lt;br /&gt;
     Microcomputers are in the process of a similar development. The&lt;br /&gt;
&amp;quot;exotic toy&amp;quot; stage has already given way to the &amp;quot;limited use&amp;quot; stage. We're&lt;br /&gt;
just starting to develop standards and infrastructure and are only a few&lt;br /&gt;
years from the &amp;quot;indispensable&amp;quot; stage. In anticipation of this stage,&lt;br /&gt;
Microsoft undertook the design and the development of OS/2.&lt;br /&gt;
     Although studying the mainframe computer revolution helps in trying to&lt;br /&gt;
predict the path of the microcomputer revolution, microcomputers are more&lt;br /&gt;
than just &amp;quot;cheap mainframes.&amp;quot; The microcomputer revolution will follow the&lt;br /&gt;
tradition of breakthroughs, creating new needs and new uses that cannot be&lt;br /&gt;
anticipated solely by studying what happened with mainframe systems.&lt;br /&gt;
     This book was written because of the breakthrough nature of the&lt;br /&gt;
microcomputer and the impact of the coming second industrial revolution.&lt;br /&gt;
The designers of OS/2 tried to anticipate, to the greatest extent possible,&lt;br /&gt;
the demands that would be placed on the system when the tool--the personal&lt;br /&gt;
computer--and the tool user reached their new equilibrium. A knowledge of&lt;br /&gt;
MS-DOS and a thorough reading of the OS/2 reference manuals will not, in&lt;br /&gt;
themselves, clarify the key issues of the programming environment that OS/2&lt;br /&gt;
was written to support. This is true not only because of the complexity of&lt;br /&gt;
the product but because many design elements were chosen to provide&lt;br /&gt;
services that from a prebreakthrough perspective--don't seem needed and&lt;br /&gt;
solve problems that haven't yet arisen.&lt;br /&gt;
     Other books provide reference information and detailed how-to&lt;br /&gt;
instructions for writing OS/2 programs. This book describes the underlying&lt;br /&gt;
architectural models that make up OS/2 and discusses how those models are&lt;br /&gt;
expected to meet the foreseen and unforeseen requirements of the oncoming&lt;br /&gt;
office automation revolution. It focuses on the general issues, problems,&lt;br /&gt;
and solutions that all OS/2 programs encounter regardless of the&lt;br /&gt;
programming and interface models that a programmer may employ.&lt;br /&gt;
     As is often the case in a technical discussion, everything in OS/2 is&lt;br /&gt;
interconnected in some fashion to everything else. A discussion on the&lt;br /&gt;
shinbone naturally leads to a discussion of the thighbone and so on. The&lt;br /&gt;
author and the editor of this book have tried hard to group the material&lt;br /&gt;
into a logical progression without redundancy, but the very nature of the&lt;br /&gt;
material makes complete success at this impossible. It's often desirable,&lt;br /&gt;
in fact, to repeat material, perhaps from a different viewpoint or with a&lt;br /&gt;
different emphasis. For these reasons, the index references every mention&lt;br /&gt;
of an item or a topic, however peripheral. Having too many references&lt;br /&gt;
(including a few worthless ones) is far better than having too few&lt;br /&gt;
references. When you're looking for information about a particular subject,&lt;br /&gt;
I recommend that you first consult the contents page to locate the major&lt;br /&gt;
discussion and then peruse the index to pick up references that may appear&lt;br /&gt;
in unexpected places.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part I  The Project&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1  History of the Project&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
Microsoft was founded to realize a vision of a microcomputer on every&lt;br /&gt;
desktop--a vision of the second industrial revolution. The first industrial&lt;br /&gt;
revolution mechanized physical work. Before the eighteenth century, nearly&lt;br /&gt;
all objects were created and constructed by human hands, one at a time.&lt;br /&gt;
With few exceptions, such as animal-powered plowing and cartage, all power&lt;br /&gt;
was human muscle power. The second industrial revolution will mechanize&lt;br /&gt;
routine mental work. Today, on the verge of the revolution, people are&lt;br /&gt;
still doing &amp;quot;thought work,&amp;quot; one piece at a time.&lt;br /&gt;
     Certain tasks--those massive in scope and capable of being rigidly&lt;br /&gt;
described, such as payroll calculations--have been automated, but the&lt;br /&gt;
majority of &amp;quot;thought work&amp;quot; is still done by people, not by computers. We&lt;br /&gt;
have the computer equivalent of the plow horse, but we don't have the&lt;br /&gt;
computer equivalent of the electric drill or the washing machine.&lt;br /&gt;
     Of course, computers cannot replace original thought and creativity&lt;br /&gt;
(at least, not in the near future) any more than machines have replaced&lt;br /&gt;
design and creativity in the physical realm. But the bulk of the work in a&lt;br /&gt;
white-collar office involves routine manipulation of information. The&lt;br /&gt;
second industrial revolution will relieve us of the &amp;quot;grunt work&amp;quot;--routine&lt;br /&gt;
data manipulation, analysis, and decisions--freeing us to deal only with&lt;br /&gt;
those situations that require human judgment.&lt;br /&gt;
     Most people do not recognize the inevitability of the second&lt;br /&gt;
industrial revolution. They can't see how a computer could do 75 percent of&lt;br /&gt;
their work because their work was structured in the absence of computers.&lt;br /&gt;
But, true to the pattern for technological breakthroughs, the tremendous&lt;br /&gt;
utility of the microcomputer will transform its users and the way they do&lt;br /&gt;
their work.&lt;br /&gt;
     For example, a great deal of work is hard to computerize because the&lt;br /&gt;
input information arrives on paper and it would take too long to type it&lt;br /&gt;
all in. Ten years ago, computer proponents envisioned the &amp;quot;paperless&lt;br /&gt;
office&amp;quot; as a solution for this problem: All material would be generated by&lt;br /&gt;
computer and then transferred electronically or via disk to other&lt;br /&gt;
computers. Offices are certainly becoming more paperless, and the arrival&lt;br /&gt;
of powerful networking systems will accelerate this, but paper continues to&lt;br /&gt;
be a very useful medium. As a result, in recent years growth has occurred&lt;br /&gt;
in another direction--incorporating paper as a computer input and output&lt;br /&gt;
device. Powerful laser printers, desktop publishing systems, and optical&lt;br /&gt;
scanners and optical character recognition will make it more practical to&lt;br /&gt;
input from and output to paper.&lt;br /&gt;
     Although the founders of Microsoft fully appreciate the impact of the&lt;br /&gt;
second industrial revolution, nobody can predict in detail how the&lt;br /&gt;
revolution will unfold. Instead, Microsoft bases its day-to-day decisions&lt;br /&gt;
on dual sets of goals: short-term goals, which are well known, and a long-&lt;br /&gt;
term goal--our vision of the automated office. Each decision has to meet&lt;br /&gt;
our short-term goals, and it must be consonant with our long-term vision, a&lt;br /&gt;
vision that becomes more precise as the revolution progresses.&lt;br /&gt;
     When 16-bit microprocessors were first announced, Microsoft knew that&lt;br /&gt;
the &amp;quot;iron&amp;quot; was now sufficiently powerful to begin to realize this vision.&lt;br /&gt;
But a powerful computer environment requires both strong iron and a&lt;br /&gt;
sophisticated operating system. The iron was becoming available, but the&lt;br /&gt;
operating system that had been standard for 8-bit microprocessors was&lt;br /&gt;
inadequate. This is when and why Microsoft entered the operating system&lt;br /&gt;
business: We knew that we needed a powerful operating system to realize our&lt;br /&gt;
vision and that the only way to guarantee its existence and suitability was&lt;br /&gt;
to write it ourselves.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.1  MS-DOS version 1.0&lt;br /&gt;
&lt;br /&gt;
MS-DOS got its start when IBM asked Microsoft to develop a disk operating&lt;br /&gt;
system for a new product that IBM was developing, the IBM Personal Computer&lt;br /&gt;
(PC). Microsoft's only operating system product at that time was XENIX, a&lt;br /&gt;
licensed version of AT&amp;amp;T's UNIX  operating system. XENIX/UNIX requires a&lt;br /&gt;
processor with memory management and protection facilities. Because the&lt;br /&gt;
8086/8088 processors had neither and because XENIX/UNIX memory&lt;br /&gt;
requirements--modest by minicomputer standards of the day--were nonetheless&lt;br /&gt;
large by microcomputer standards, a different operating system had to be&lt;br /&gt;
developed.&lt;br /&gt;
     CP/M-80, developed by Digital Research, Incorporated (DRI), had been&lt;br /&gt;
the standard 8-bit operating system, and the majority of existing&lt;br /&gt;
microcomputer software had been written to run on CP/M-80. For this reason,&lt;br /&gt;
Microsoft decided to make MS-DOS version 1.0 as compatible as possible with&lt;br /&gt;
CP/M-80. The 8088 processor would not run the existing CP/M-80 programs,&lt;br /&gt;
which were written for the 8080 processor, but because 8080 programs could&lt;br /&gt;
be easily and semiautomatically converted to run on the 8088, Microsoft&lt;br /&gt;
felt that minimizing adaptation hassles by minimizing operating system&lt;br /&gt;
incompatibility would hasten the acceptance of MS-DOS on the IBM PC.&lt;br /&gt;
     A major software product requires a great deal of development time,&lt;br /&gt;
and IBM was in a hurry to introduce its PC. Microsoft, therefore, looked&lt;br /&gt;
around for a software product to buy that could be built onto to create MS-&lt;br /&gt;
DOS version 1.0. Such a product was found at Seattle Computer Products. Tim&lt;br /&gt;
Paterson, an engineer there, had produced a CP/M-80 &amp;quot;clone,&amp;quot; called SCP-&lt;br /&gt;
DOS, that ran on the 8088 processor. Microsoft purchased full rights to&lt;br /&gt;
this product and to its source code and used the product as a starting&lt;br /&gt;
point in the development of MS-DOS version 1.0.&lt;br /&gt;
     MS-DOS version 1.0 was released in August 1981. Available only for the&lt;br /&gt;
IBM PC, it consisted of 4000 lines of assembly-language source code and ran&lt;br /&gt;
in 8 KB of memory. MS-DOS version 1.1 was released in 1982 and worked with&lt;br /&gt;
double-sided 320 KB floppy disks.&lt;br /&gt;
     Microsoft's goal was that MS-DOS version 1.0 be highly CP/M&lt;br /&gt;
compatible, and it was. Ironically, it was considerably more compatible&lt;br /&gt;
than DRI's own 8088 product, CP/M-86. As we shall see later, this CP/M&lt;br /&gt;
compatibility, necessary at the time, eventually came to cause Microsoft&lt;br /&gt;
engineers a great deal of difficulty.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.2  MS-DOS version 2.0&lt;br /&gt;
&lt;br /&gt;
In early 1982, IBM disclosed to Microsoft that it was developing a hard&lt;br /&gt;
disk-based personal computer, the IBM XT. Microsoft began work on MS-DOS&lt;br /&gt;
version 2.0 to provide support for the new disk hardware. Changes were&lt;br /&gt;
necessary because MS-DOS, in keeping with its CP/M-80 compatible heritage,&lt;br /&gt;
had been designed for a floppy disk environment. A disk could contain only&lt;br /&gt;
one directory, and that directory could contain a maximum of 64 files. This&lt;br /&gt;
decision was reasonable when first made because floppy disks held only&lt;br /&gt;
about 180 KB of data.&lt;br /&gt;
     For the hard disk, however, the 64-file limit was much too small, and&lt;br /&gt;
using a single directory to manage perhaps hundreds of files was&lt;br /&gt;
clumsy. Therefore, the MS-DOS version 2.0 developers--Mark Zbikowski, Aaron&lt;br /&gt;
Reynolds, Chris Peters, and Nancy Panners--added a hierarchical file&lt;br /&gt;
system. In a hierarchical file system a directory can contain other&lt;br /&gt;
directories and files. In turn, those directories can con- tain a mixture&lt;br /&gt;
of files and directories and so on. A hierarchically designed system starts&lt;br /&gt;
with the main, or &amp;quot;root,&amp;quot; directory, which itself can contain (as seen in&lt;br /&gt;
Figure 1-1) a tree-structured collection of files and directories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                directory WORK&lt;br /&gt;
                           ADMIN&lt;br /&gt;
               ÚÄÄÄÄÄÄÄÄÄÄ BUDGET&lt;br /&gt;
               ³           CALENDAR&lt;br /&gt;
               ³           LUNCH.DOC&lt;br /&gt;
               ³           PAYROLL ÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
               ³           PHONE.LST        ³&lt;br /&gt;
               ³           SCHED.DOC        ³&lt;br /&gt;
               ³                            ³&lt;br /&gt;
           directory BUDGET              directory PAYROLL&lt;br /&gt;
                      MONTH                         ADDRESSES&lt;br /&gt;
                      QUARTER                       MONTHLY&lt;br /&gt;
                      YEAR                          NAMES&lt;br /&gt;
                      1986 ÄÄÄÄÄÄÄÄ¿                RETIRED ÄÄÄÄÄ¿&lt;br /&gt;
            ÚÄÄÄÄÄÄÄÄ 1985         ³                VACATION     ³&lt;br /&gt;
            ³                      ³                WEEKLY       ³&lt;br /&gt;
            ³                      ³                             ³&lt;br /&gt;
     directory 1985         directory 1986             directory RETIRED&lt;br /&gt;
            ³                      ³                             ³&lt;br /&gt;
&lt;br /&gt;
Figure 1-1.  A directory tree hierarchy. Within the WORK directory&lt;br /&gt;
are five files (ADMIN, CALENDAR, LUNCH.DOC, PHONE.LST, SCHED.DOC) and two&lt;br /&gt;
subdirectories (BUDGET, PAYROLL). Each subdirectory has its own&lt;br /&gt;
subdirectories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.3  MS-DOS version 3.0&lt;br /&gt;
&lt;br /&gt;
MS-DOS version 3.0 was introduced in August 1984, when IBM announced the&lt;br /&gt;
IBM PC/AT. The AT contains an 80286 processor, but, when running DOS, it&lt;br /&gt;
uses the 8086 emulation mode built into the chip and runs as a &amp;quot;fast 8086.&amp;quot;&lt;br /&gt;
The chip's extended addressing range and its protected mode architecture&lt;br /&gt;
sit unused.&lt;br /&gt;
1. Products such as Microsoft XENIX/UNIX run on the PC/AT and&lt;br /&gt;
compatibles, using the processor's protected mode. This is possible&lt;br /&gt;
because XENIX/UNIX and similar systems had no preexisting real mode&lt;br /&gt;
applications that needed to be supported.&lt;br /&gt;
1&lt;br /&gt;
     MS-DOS version 3.1 was released in November 1984 and contained&lt;br /&gt;
networking support. In January 1986, MS-DOS version 3.2--a minor revision--&lt;br /&gt;
was released. This version supported 3-1/2-inch floppy disks and contained&lt;br /&gt;
the formatting function for a device in the device driver. In 1987, MS-DOS&lt;br /&gt;
version 3.3 followed; the primary enhancement of this release was support&lt;br /&gt;
for the IBM PS/2 and compatible hardware.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1.4  MS-DOS version 4.0&lt;br /&gt;
&lt;br /&gt;
Microsoft started work on a multitasking version of MS-DOS in January 1983.&lt;br /&gt;
At the time, it was internally called MS-DOS version 3.0. When a new&lt;br /&gt;
version of the single-tasking MS-DOS was shipped under the name MS-DOS&lt;br /&gt;
version 3.0, the multitasking version was renamed, internally, to MS-DOS&lt;br /&gt;
version 4.0. A version of this product--a multitasking, real-mode only MS-&lt;br /&gt;
DOS--was shipped as MS-DOS version 4.0. Because MS-DOS version 4.0 runs&lt;br /&gt;
only in real mode, it can run on 8088 and 8086 machines as well as on 80286&lt;br /&gt;
machines. The limitations of the real mode environment make MS-DOS version&lt;br /&gt;
4.0 a specialized product. Although MS-DOS version 4.0 supports full&lt;br /&gt;
preemptive multitasking, system memory is limited to the 640 KB available&lt;br /&gt;
in real mode, with no swapping.&lt;br /&gt;
2. It is not feasible to support general purpose swapping without&lt;br /&gt;
memory management hardware that is unavailable in 8086 real mode.&lt;br /&gt;
2 This means that all processes have to fit&lt;br /&gt;
into the single 640 KB memory area. Only one MS-DOS version 3.x compatible&lt;br /&gt;
real mode application can be run; the other processes must be special MS-&lt;br /&gt;
DOS version 4.0 processes that understand their environment and cooperate&lt;br /&gt;
with the operating system to coexist peacefully with the single MS-DOS&lt;br /&gt;
version 3.x real mode application.&lt;br /&gt;
     Because of these restrictions, MS-DOS version 4.0 was not intended for&lt;br /&gt;
general release, but as a platform for specific OEMs to support extended PC&lt;br /&gt;
architectures. For example, a powerful telephone management system could be&lt;br /&gt;
built into a PC by using special MS-DOS version 4.0 background processes to&lt;br /&gt;
control the telephone equipment. The resulting machine could then be&lt;br /&gt;
marketed as a &amp;quot;compatible MS-DOS 3 PC with a built-in superphone.&amp;quot;&lt;br /&gt;
     Although MS-DOS version 4.0 was released as a special OEM product, the&lt;br /&gt;
project--now called MS-DOS version 5.0--continued. The goal was to take&lt;br /&gt;
advantage of the protected mode of the 80286 to provide full general&lt;br /&gt;
purpose multitasking without the limitations--as seen in MS-DOS version&lt;br /&gt;
4.0--of a real-mode only environment. Soon, Microsoft and IBM signed a&lt;br /&gt;
Joint Development Agreement that provided for the design and development of&lt;br /&gt;
MS-DOS version 5.0 (now called CP/DOS). The agreement is complex, but it&lt;br /&gt;
basically provides for joint development and then subsequent joint&lt;br /&gt;
ownership, with both companies holding full rights to the resulting&lt;br /&gt;
product.&lt;br /&gt;
     As the project neared completion, the marketing staffs looked at&lt;br /&gt;
CP/DOS, nee DOS 5, nee DOS 4, nee DOS 3, and decided that it needed...you&lt;br /&gt;
guessed it...a name change. As a result, the remainder of this book will&lt;br /&gt;
discuss the design and function of an operating system called OS/2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2  Goals and Compatibility Issues&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
OS/2 is similar to traditional multitasking operating systems in many ways:&lt;br /&gt;
It provides multitasking, scheduling, disk management, memory management,&lt;br /&gt;
and so on. But it is also different in many ways, because a personal&lt;br /&gt;
computer is very different from a multiuser minicomputer. The designers of&lt;br /&gt;
OS/2 worked from two lists: a set of goals and a set of compatibility&lt;br /&gt;
issues. This chapter describes those goals and compatibility issues and&lt;br /&gt;
provides the context for a later discussion of the design itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.1  Goals&lt;br /&gt;
&lt;br /&gt;
The primary goal of OS/2 is to be the ideal office automation operating&lt;br /&gt;
system. The designers worked toward this goal by defining the following&lt;br /&gt;
intermediate and, seemingly, contradictory goals:&lt;br /&gt;
&lt;br /&gt;
     þ  To provide device-independent graphics drivers without introducing&lt;br /&gt;
        any significant overhead.&lt;br /&gt;
&lt;br /&gt;
     þ  To allow applications direct access to high-bandwidth peripherals&lt;br /&gt;
        but maintain the ability to virtualize or apportion the usage of&lt;br /&gt;
        those peripherals.&lt;br /&gt;
&lt;br /&gt;
     þ  To provide multitasking without reducing the performance and&lt;br /&gt;
        response available from a single-tasking system.&lt;br /&gt;
&lt;br /&gt;
     þ  To provide a fully customized environment for each program and its&lt;br /&gt;
        descendants yet also provide a standard environment that is&lt;br /&gt;
        unaffected by other programs in the system.&lt;br /&gt;
&lt;br /&gt;
     þ  To provide a protected environment to ensure system stability yet&lt;br /&gt;
        one that will not constrain applications from the capabilities they&lt;br /&gt;
        have under nonprotected systems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.1.1  Graphical User Interface&lt;br /&gt;
By far the fastest and easiest way people receive information is through&lt;br /&gt;
the eye. We are inherently visual creatures. Our eyes receive information&lt;br /&gt;
rapidly; they can &amp;quot;seek&amp;quot; to the desired information and &amp;quot;zoom&amp;quot; their&lt;br /&gt;
attention in and out with small, rapid movements of the eye muscles. A&lt;br /&gt;
large part of the human brain is dedicated to processing visual&lt;br /&gt;
information. People abstract data and meaning from visual material--from&lt;br /&gt;
text to graphics to motion pictures--hundreds of times faster than from any&lt;br /&gt;
other material.&lt;br /&gt;
     As a result, if an office automation system is to provide quantities&lt;br /&gt;
of information quickly and in a form in which it can be easily absorbed, a&lt;br /&gt;
powerful graphics capability is essential. Such capabilities were rare in&lt;br /&gt;
earlier minicomputer operating systems because of the huge memory and&lt;br /&gt;
compute power costs of high-resolution displays. Today's microcomputers&lt;br /&gt;
have the memory to contain the display information, they have the CPU power&lt;br /&gt;
to create and manipulate that information, and they have no better use for&lt;br /&gt;
those capabilities than to support powerful, easy-to-use graphical&lt;br /&gt;
applications.&lt;br /&gt;
     Graphics can take many forms--pictures, tables, drawings, charts--&lt;br /&gt;
perhaps incorporating color and even animation. All are powerful adjuncts&lt;br /&gt;
to the presentation of alphanumeric text. Graphical applications don't&lt;br /&gt;
necessarily employ charts and pictures. A WYSIWYG (What You See Is What You&lt;br /&gt;
Get) typesetting program may display only text, but if that text is drawn&lt;br /&gt;
in graphics mode, the screen can show any font, in any type size, with&lt;br /&gt;
proportional spacing, kerning, and so on.&lt;br /&gt;
     The screen graphics components of OS/2 need to be device independent;&lt;br /&gt;
that is, an application must display the proper graphical &amp;quot;picture&amp;quot; without&lt;br /&gt;
relying on the specific characteristics of any particular graphical display&lt;br /&gt;
interface board. Each year the state of the art in displays gets better; it&lt;br /&gt;
would be extremely shortsighted to tie applications to a particular display&lt;br /&gt;
board, for no matter how good it is, within a couple of years it will be&lt;br /&gt;
obsolete.&lt;br /&gt;
     The idea is to encapsulate device-specific code by requiring that each&lt;br /&gt;
device come with a software package called a device driver. The application&lt;br /&gt;
program issues commands for a generic device, and the device driver then&lt;br /&gt;
translates those commands to fit the characteristics of the actual device.&lt;br /&gt;
The result is that the manufacturer of a new graphics display board needs&lt;br /&gt;
to write an appropriate device driver and supply it with the board. The&lt;br /&gt;
application program doesn't need to know anything about the device, and the&lt;br /&gt;
device driver doesn't need to know anything about the application, other&lt;br /&gt;
than the specification of the common interface they share. This common&lt;br /&gt;
interface describes a virtual display device; the general technique of&lt;br /&gt;
hiding a complicated actual situation behind a simple, standard interface&lt;br /&gt;
is called &amp;quot;virtualization.&amp;quot;&lt;br /&gt;
     Figure 2-1 shows the traditional operating system device driver&lt;br /&gt;
architecture. Applications don't directly call device drivers because&lt;br /&gt;
device drivers need to execute in the processor's privilege mode to&lt;br /&gt;
manipulate their device; the calling application must run in normal mode.&lt;br /&gt;
In the language of the 80286/80386 family of processors, privilege mode is&lt;br /&gt;
called ring 0, and normal mode is called ring 3. The operating system&lt;br /&gt;
usually acts as a middleman: It receives the request, validates it, deals&lt;br /&gt;
with issues that arise when there is only one device but multiple&lt;br /&gt;
applications are using it, and then passes the request to the device&lt;br /&gt;
driver. The device driver's response or return of data takes the reverse&lt;br /&gt;
path, winding its way through the operating system and back to the&lt;br /&gt;
application program.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         Application                      Kernel             Device driver&lt;br /&gt;
          (ring 3)                       (ring 0)               (ring 0)&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                   Request&lt;br /&gt;
                             ³     packet:                  ³ ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                                ÚÄÄÄÄÄÄÄÄÄÄ¿    Device        ³  Device  ³&lt;br /&gt;
                             ³  ³          ³    descriptor ÄÅÄ´  driver  ³&lt;br /&gt;
Call deviceio (arg 1...argn)    ³ function ³    #1            ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                             ³  ³   arg1   ³          ú     ³&lt;br /&gt;
        ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³    ù     ÃÄ¿        ú&lt;br /&gt;
                  Ring       ³  ³   argn   ³ ³        ú     ³&lt;br /&gt;
                  transition    ³          ³ ³        ú       ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                             ³  ÀÄÄÄÄÄÄÄÄÄÄÙ À� Device      ³ ³  Device  ³&lt;br /&gt;
                                                descriptor ÄÄÄ´  driver  ³&lt;br /&gt;
                             ³                  #N          ³ ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 2-1.  Traditional device driver architecture. When an application&lt;br /&gt;
wants to do device I/0, it calls the operating system, which builds a&lt;br /&gt;
device request packet, determines the target device, and delivers the&lt;br /&gt;
packet. The device driver's response follows the opposite route through the&lt;br /&gt;
kernel back to the application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This approach solves the device virtualization problem, but at a cost&lt;br /&gt;
in performance. The interface between the application and the device driver&lt;br /&gt;
is narrow; that is, the form messages can take is usually restricted.&lt;br /&gt;
Commonly, the application program is expected to build a request block that&lt;br /&gt;
contains all the information and data that the device driver needs to&lt;br /&gt;
service the request; the actual call to the operating system is simply&lt;br /&gt;
&amp;quot;pass this request block to the device driver.&amp;quot; Setting up this block takes&lt;br /&gt;
time, and breaking it down in the device driver again takes time. More time&lt;br /&gt;
is spent on the reply; the device driver builds, the operating system&lt;br /&gt;
copies, and the application breaks down. Further time is spent calling down&lt;br /&gt;
through the internal layers of the operating system, examining and copying&lt;br /&gt;
the request block, routing to the proper device driver, and so forth.&lt;br /&gt;
Finally, the transition between rings (privilege and normal mode) is also&lt;br /&gt;
time-consuming, and two such transitions occur--to privilege mode and back&lt;br /&gt;
again.&lt;br /&gt;
     Such a cost in performance was acceptable in nongraphics-based systems&lt;br /&gt;
because, typically, completely updating a screen required only 1920 (or&lt;br /&gt;
fewer) bytes of data. Today's graphics devices can require 256,000 bytes or&lt;br /&gt;
more per screen update, and future devices will be even more demanding.&lt;br /&gt;
Furthermore, applications may expect to update these high-resolution&lt;br /&gt;
screens several times a second.&lt;br /&gt;
1. It's not so much the amount of data that slows the traditional&lt;br /&gt;
device driver model, but the number of requests and replies. Disk&lt;br /&gt;
devices work well through the traditional model because disk&lt;br /&gt;
requests tend to be large (perhaps 40,000 bytes). Display devices&lt;br /&gt;
tend to be written piecemeal--a character, a word, or a line at a&lt;br /&gt;
time. It is the high rate of these individual calls that slows the&lt;br /&gt;
device driver model, not the number of bytes written to the screen.&lt;br /&gt;
1&lt;br /&gt;
     OS/2 needed powerful, device-independent graphical display support&lt;br /&gt;
that had a wide, efficient user interface--one that did not involve ring&lt;br /&gt;
transitions, the operating system, or other unnecessary overhead. As we'll&lt;br /&gt;
see later, OS/2 meets this requirement by means of a mechanism called&lt;br /&gt;
dynamic linking.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.1.2  Multitasking&lt;br /&gt;
To be really useful, a personal computer must be able to do more than one&lt;br /&gt;
chore at a time--an ability called multitasking. We humans multitask all&lt;br /&gt;
the time. For example, you may be involved in three projects at work, be&lt;br /&gt;
halfway through a novel, and be taking Spanish lessons. You pick up each&lt;br /&gt;
task in turn, work on it for a while, and then put it down and work on&lt;br /&gt;
something else. This is called serial multitasking. Humans can also do some&lt;br /&gt;
tasks simultaneously, such as driving a car and talking. This is called&lt;br /&gt;
parallel multitasking.&lt;br /&gt;
     In a serial multitasking computer environment, a user can switch&lt;br /&gt;
activities at will, working for a while at each. For example, a user can&lt;br /&gt;
leave a word-processing program without terminating it, consult a&lt;br /&gt;
spreadsheet, and then return to the waiting word-processing program. Or, if&lt;br /&gt;
someone telephones and requests an appointment, the user can switch from a&lt;br /&gt;
spreadsheet to a scheduling program, consult the calendar, and then return&lt;br /&gt;
to the spreadsheet.&lt;br /&gt;
     The obvious value of multitasking makes it another key requirement for&lt;br /&gt;
OS/2: Many programs or applications can run at the same time. But&lt;br /&gt;
multitasking is useful for more than just switching between applications:&lt;br /&gt;
Parallel multitasking allows an application to do work by itself--perhaps&lt;br /&gt;
print a large file or recalculate a large spreadsheet--while the user&lt;br /&gt;
works with another application. Because OS/2 supports full multitasking,&lt;br /&gt;
it can execute programs in addition to the application(s) the user is&lt;br /&gt;
running, providing advanced services such as network mail without&lt;br /&gt;
interrupting or interfering with the user's work.&lt;br /&gt;
2. Present-day machines contain only one CPU, so at any instant&lt;br /&gt;
only one program can be executing. At this microscopic level,&lt;br /&gt;
OS/2 is a serial multitasking system. It is not considered serial&lt;br /&gt;
multitasking, however, because it performs preemptive scheduling.&lt;br /&gt;
At any time, OS/2 can remove the CPU from the currently running&lt;br /&gt;
program and assign it to another program. Because these&lt;br /&gt;
rescheduling events may occur many times a second at totally&lt;br /&gt;
unpredictable places within the running programs, it is accurate&lt;br /&gt;
to view the system as if each program truly runs simultaneously&lt;br /&gt;
with other programs.&lt;br /&gt;
2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.1.3  Memory Management&lt;br /&gt;
Multitasking is fairly easy to achieve. All that's necessary is a source of&lt;br /&gt;
periodic hardware interrupts, such as a clock circuit, to enable the&lt;br /&gt;
operating system to effect a &amp;quot;context switch,&amp;quot; or to reschedule. To be&lt;br /&gt;
useful, however, a multitasking system needs an effective memory management&lt;br /&gt;
system. For example, a user wants to run two applications on a system. Each&lt;br /&gt;
starts at as low a memory location as possible to maximize the amount of&lt;br /&gt;
memory it can use. Unfortunately, if the system supports multitasking and&lt;br /&gt;
the user tries to run both applications simultaneously, each attempts to&lt;br /&gt;
use the same memory cells, and the applications destroy each other.&lt;br /&gt;
     A memory management system solves this problem by using special&lt;br /&gt;
hardware facilities built into 80286/80386 processors (for example, IBM&lt;br /&gt;
PC/AT machines and compatibles and 80386-based machines).&lt;br /&gt;
3. Earlier 8086/8088 processors used in PCs, PC/XTs, and similar&lt;br /&gt;
machines lack this hardware. This is why earlier versions of MS-DOS&lt;br /&gt;
didn't support multitasking and why OS/2 won't run on such machines.&lt;br /&gt;
3 The memory&lt;br /&gt;
management system uses the hardware to virtualize the memory of the machine&lt;br /&gt;
so that each program appears to have all memory to itself.&lt;br /&gt;
     Memory management is more than keeping programs out of each other's&lt;br /&gt;
way. The system must track the owner or user(s) of each piece of memory so&lt;br /&gt;
that the memory space can be reclaimed when it is no longer needed, even if&lt;br /&gt;
the owner of the memory neglects to explicitly release it. Some operating&lt;br /&gt;
systems avoid this work by assuming that no application will ever fail to&lt;br /&gt;
return its memory when done or by examining the contents of memory and&lt;br /&gt;
ascertaining from those contents whether the memory is still being used.&lt;br /&gt;
(This is called &amp;quot;garbage collection.&amp;quot;) Neither alternative was acceptable&lt;br /&gt;
for OS/2. Because OS/2 will run a variety of programs written by many&lt;br /&gt;
vendors, identifying free memory by inspection is impossible, and assuming&lt;br /&gt;
perfection from the applications themselves is unwise. Tracking the&lt;br /&gt;
ownership and usage of memory objects can be complex, as we shall see in&lt;br /&gt;
our discussion on dynamic link libraries.&lt;br /&gt;
     Finally, the memory management system must manage memory overcommit.&lt;br /&gt;
The multitasking capability of OS/2 allows many applications to be run&lt;br /&gt;
simultaneously; thus, RAM must hold all these programs and their data.&lt;br /&gt;
Although RAM becomes cheaper every year, buying enough to hold all of one's&lt;br /&gt;
applications at one time is still prohibitive. Furthermore, although RAM&lt;br /&gt;
prices continue to drop, the memory requirements of applications will&lt;br /&gt;
continue to rise. Consequently, OS/2 must contain an effective mechanism to&lt;br /&gt;
allocate more memory to the running programs than in fact physically&lt;br /&gt;
exists. This is called memory overcommit.&lt;br /&gt;
     OS/2 accomplishes this magic with the classic technique of swapping.&lt;br /&gt;
OS/2 periodically examines each segment of memory to see if it has been&lt;br /&gt;
used recently. When a request is made for RAM and none is available, the&lt;br /&gt;
least recently used segment of memory (the piece that has been unused for&lt;br /&gt;
the longest time) is written to a disk file, and the RAM it occupied is&lt;br /&gt;
made available. Later, if a program attempts to use the swapped-out memory,&lt;br /&gt;
a &amp;quot;memory not present&amp;quot; fault occurs. OS/2 intercepts the fault and reloads&lt;br /&gt;
the memory information from the disk into memory, swapping out some other&lt;br /&gt;
piece of memory, if necessary, to make room. This whole process is&lt;br /&gt;
invisible to the application that uses the swapped memory area; the only&lt;br /&gt;
impact is a small delay while the needed memory is read back from the&lt;br /&gt;
disk.&lt;br /&gt;
     The fundamental concepts of memory overcommit and swapping are simple,&lt;br /&gt;
but a good implementation is not. OS/2 must choose the right piece of&lt;br /&gt;
memory to swap out, and it must swap it out efficiently. Not only must care&lt;br /&gt;
be taken that the swap file doesn't grow too big and consume all the free&lt;br /&gt;
disk space but also that deadlocks don't occur. For example, if all the&lt;br /&gt;
disk swap space is filled, it may be impossible to swap into RAM a piece of&lt;br /&gt;
memory because no free RAM is available, and OS/2 can't free up RAM because&lt;br /&gt;
no swap space exists to write it out to. Naturally, the greater the load on&lt;br /&gt;
the system, the slower the system will be, but the speed degradation must&lt;br /&gt;
be gradual and acceptable, and the system must never deadlock.&lt;br /&gt;
     The issues involved in memory management and the memory management&lt;br /&gt;
facilities that OS/2 provides are considerably more complex than this&lt;br /&gt;
overview. We'll return to the subject of memory management in detail in&lt;br /&gt;
Chapter 9.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.1.4  Protection&lt;br /&gt;
I mentioned earlier that OS/2 cannot trust applications to behave&lt;br /&gt;
correctly. I was talking about memory management, but this concern&lt;br /&gt;
generalizes into the next key requirement: OS/2 must protect applications &lt;br /&gt;
from the proper or improper actions of other applications that may be&lt;br /&gt;
running on the system.&lt;br /&gt;
     Because OS/2 will run applications and programs from a variety of&lt;br /&gt;
vendors, every user's machine will execute a different set of applications,&lt;br /&gt;
running in different ways on different data. No software vendor can fully&lt;br /&gt;
test a product in all possible environments. This makes it critical that an&lt;br /&gt;
error on the part of one program does not crash the system or some other&lt;br /&gt;
program or, worse, corrupt data and not bring down the system. Even if no&lt;br /&gt;
data is damaged, system crashes are unacceptable. Few users have the&lt;br /&gt;
background or equipment even to diagnose which application caused the&lt;br /&gt;
problem.&lt;br /&gt;
     Furthermore, malice, as well as accident, is a concern. Microsoft's&lt;br /&gt;
vision of the automated office cannot be realized without a system that is&lt;br /&gt;
secure from deliberate attack. No corporation will be willing to base its&lt;br /&gt;
operations on a computer network when any person in that company--with the&lt;br /&gt;
help of some &amp;quot;cracker&amp;quot; programs bought from the back of a computer&lt;br /&gt;
magazine--can see and change personnel or payroll files, billing notices,&lt;br /&gt;
or strategic planning memos.&lt;br /&gt;
     Today, personal computers are being used as a kind of super-&lt;br /&gt;
sophisticated desk calculator. As such, data is secured by traditional&lt;br /&gt;
means--physical locks on office doors, computers, or file cabinets that&lt;br /&gt;
store disks. Users don't see a need for a protected environment because&lt;br /&gt;
their machine is physically protected. This lack of interest in protection&lt;br /&gt;
is another example of the development of a breakthrough technology.&lt;br /&gt;
Protection is not needed because the machine is secure and operates on data&lt;br /&gt;
brought to it by traditional office channels. In the future, however,&lt;br /&gt;
networked personal computers will become universal and will act both as the&lt;br /&gt;
processors and as the source (via the network) of the data. Thus, in this&lt;br /&gt;
role, protection is a key requirement and is indeed a prerequisite for&lt;br /&gt;
personal computers to assume that central role.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.1.5  Encapsulation&lt;br /&gt;
When a program runs in a single-tasking system such as MS-DOS version 3.x,&lt;br /&gt;
its environment is always constant--consisting of the machine and MS-DOS.&lt;br /&gt;
The program can expect to get the same treatment from the system and to&lt;br /&gt;
provide exactly the same interaction with the user each time it runs. In a&lt;br /&gt;
multitasking environment, however, many programs can be running. Each&lt;br /&gt;
program can be using files and devices in different ways; each program can&lt;br /&gt;
be using the mouse, each program can have the screen display in a different&lt;br /&gt;
mode, and so on. OS/2 must encapsulate, or isolate, each program so that it&lt;br /&gt;
&amp;quot;sees&amp;quot; a uniform environment each time it runs, even though the computer&lt;br /&gt;
environment itself may be different each time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.1.6  Interprocess Communication (IPC)&lt;br /&gt;
In a single-tasking environment such as MS-DOS version 3.x, each program&lt;br /&gt;
stands alone. If it needs a particular service not provided by the&lt;br /&gt;
operating system, it must provide that service itself. For example, every&lt;br /&gt;
application that needs a sort facility must contain its own.&lt;br /&gt;
     Likewise, if a spreadsheet needs to access values from a database, it&lt;br /&gt;
must contain the code to do so. This extra code complicates the spreadsheet&lt;br /&gt;
program, and it ties the program to a particular database product or&lt;br /&gt;
format. A user might be unable to switch to a better product because the&lt;br /&gt;
spreadsheet is unable to understand the new database's file formats.&lt;br /&gt;
     A direct result of such a stand-alone environment is the creation of&lt;br /&gt;
very large and complex &amp;quot;combo&amp;quot; packages such as Lotus Symphony. Because&lt;br /&gt;
every function that the user may want must be contained within one program,&lt;br /&gt;
vendors supply packages that attempt to contain everything.&lt;br /&gt;
     In practice, such chimeric programs tend to be large and cumbersome,&lt;br /&gt;
and their individual functional components (spreadsheets, word processors,&lt;br /&gt;
and databases, for example) are generally more difficult to use and less&lt;br /&gt;
sophisticated than individual applications that specialize in a single&lt;br /&gt;
function.&lt;br /&gt;
     The stand-alone environment forces the creation of larger and more&lt;br /&gt;
complex programs, each of which typically understands only its own file&lt;br /&gt;
formats and works poorly, if at all, with data produced by other programs.&lt;br /&gt;
This vision of personal computer software growing monstrous until&lt;br /&gt;
collapsing from its own weight brings about another OS/2 requirement:&lt;br /&gt;
Applications must be able to communicate, easily and efficiently, with&lt;br /&gt;
other applications.&lt;br /&gt;
     More specifically, an application must be able to find (or name) the&lt;br /&gt;
application that provides the information or service that the client needs,&lt;br /&gt;
and it must be able to establish efficient communication with the provider&lt;br /&gt;
program without requiring that either application have specific knowledge&lt;br /&gt;
of the internal workings of the other. Thus, a spreadsheet program must be&lt;br /&gt;
able to communicate with a database program and access the values it needs.&lt;br /&gt;
The spreadsheet program is therefore not tied to any particular database&lt;br /&gt;
program but can work with any database system that recognizes OS/2 IPC&lt;br /&gt;
requests.&lt;br /&gt;
     Applications running under OS/2 not only retain their full power as&lt;br /&gt;
individual applications but also benefit from cross-application&lt;br /&gt;
communication. Furthermore, the total system can be enhanced by upgrading&lt;br /&gt;
an application that provides services to others. When a new, faster, or&lt;br /&gt;
more fully featured database package is installed, not only is the user's&lt;br /&gt;
database application improved but the database functions of the spreadsheet&lt;br /&gt;
program are improved as well.&lt;br /&gt;
     The OS/2 philosophy is that no program should reinvent the wheel.&lt;br /&gt;
Programs should be written to offer their services to other programs and to&lt;br /&gt;
take advantage of the offered services of other programs. The result is a&lt;br /&gt;
maximally effective and efficient system.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.1.7  Direct Device Access&lt;br /&gt;
Earlier, we discussed the need for a high-performance graphical interface&lt;br /&gt;
and the limitations of the traditional device driver architecture. OS/2&lt;br /&gt;
contains a built-in solution for the screen graphical interface, but what&lt;br /&gt;
about other, specialized devices that may require a higher bandwidth&lt;br /&gt;
interface than device drivers provide? The one sure prediction about the&lt;br /&gt;
future of a technological breakthrough is that you can't fully predict it.&lt;br /&gt;
For this reason, the final key requirement for OS/2 is that it contain an&lt;br /&gt;
&amp;quot;escape hatch&amp;quot; in anticipation of devices that have performance needs too&lt;br /&gt;
great for a device driver model.&lt;br /&gt;
     OS/2 provides this expandability by allowing applications direct&lt;br /&gt;
access to hardware devices--both the I/O ports and any device memory. This&lt;br /&gt;
must be done, of course, in such a way that only devices which are intended&lt;br /&gt;
to be used in this fashion can be so accessed. Applications are prevented&lt;br /&gt;
from using this access technique on devices that are being managed by the&lt;br /&gt;
operating system or by a device driver. This facility gives applications&lt;br /&gt;
the ability to take advantage of special nonstandard hardware such as OCRs&lt;br /&gt;
(Optical Character Readers), digitizer tablets, Fax equipment, special&lt;br /&gt;
purpose graphics cards, and the like.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2  Compatibility Issues&lt;br /&gt;
&lt;br /&gt;
But OS/2 has to do more than meet the goals we've discussed: It must be&lt;br /&gt;
compatible with 8086/8088 and 80286 architecture, and it must be compatible&lt;br /&gt;
with MS-DOS. By far the easiest solution would have been to create a new&lt;br /&gt;
multitasking operating system that would not be compatible with MS-DOS, but&lt;br /&gt;
such a system is unacceptable. Potential users may be excited about the new&lt;br /&gt;
system, but they won't buy it until applications are available. Application&lt;br /&gt;
writers may likewise be excited, but they won't adapt their products for it&lt;br /&gt;
until the system has sold enough copies to gain significant market share.&lt;br /&gt;
This &amp;quot;catch 22&amp;quot; means that the only people who will buy the new operating&lt;br /&gt;
system are the developers' mothers, and they probably get it at a discount&lt;br /&gt;
anyway.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.1  Real Mode vs Protect Mode&lt;br /&gt;
The first real mode compatibility issue relates to the design of the 80286&lt;br /&gt;
microprocessor--the &amp;quot;brain&amp;quot; of an MS-DOS computer. This chip has two&lt;br /&gt;
incompatible modes--real (compatibility) mode and protect mode. Real mode&lt;br /&gt;
is designed to run programs in exactly the same manner as they run on the&lt;br /&gt;
8086/8088 processor. In other words, when the 80286 is in real mode, it&lt;br /&gt;
&amp;quot;looks&amp;quot; to the operating system and programs exactly like a fast&lt;br /&gt;
8088.&lt;br /&gt;
     But the designers of the 80286 wanted it to be more than a fast 8088.&lt;br /&gt;
They wanted to add such features as memory management, memory protection,&lt;br /&gt;
and the ring protection mechanism, which allows the operating system to&lt;br /&gt;
protect one application from another. They weren't able to do this while&lt;br /&gt;
remaining fully compatible with the earlier 8088 chip, so they added a&lt;br /&gt;
second mode to the 80286--protect mode. When the processor is running in&lt;br /&gt;
protect mode, it provides these important new features, but it will not run&lt;br /&gt;
most programs written for the 8086/8088.&lt;br /&gt;
     In effect, an 80286 is two separate microprocessors in one package. It&lt;br /&gt;
can act like a very fast 8088--compatible, but with no new capabilities--or&lt;br /&gt;
it can act like an 80286--incompatible, but providing new features.&lt;br /&gt;
Unfortunately, the designers of the chip didn't appreciate the importance&lt;br /&gt;
of compatibility in the MS-DOS marketplace, and they designed the 80286 so&lt;br /&gt;
that it can run in either mode but can't switch back and forth at will.&lt;br /&gt;
4. The 80286 initializes itself in real mode. There is a command&lt;br /&gt;
to switch from real mode to protect mode, but there is no command&lt;br /&gt;
to switch back.&lt;br /&gt;
4&lt;br /&gt;
In other words, an 80286 was designed to run only old 8086/8088 programs,&lt;br /&gt;
or it can run only new 80286 style programs, but never both at the same&lt;br /&gt;
time.&lt;br /&gt;
     In summary, OS/2 was required to do something that the 80286 was not&lt;br /&gt;
designed for--execute both 8086/8088 style (real mode) and 80286 style&lt;br /&gt;
(protect) mode programs at the same time. The existence of this book should&lt;br /&gt;
lead you to believe that this problem was solved, and indeed it was.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2  Running Applications in Real (Compatibility) Mode&lt;br /&gt;
Solving the real mode vs protect mode problem, however, presented other&lt;br /&gt;
problems. In general, the problems came about because the real mode&lt;br /&gt;
programs were written for MS-DOS versions 2.x or 3.x, both of which are&lt;br /&gt;
single-tasking environments.&lt;br /&gt;
     Although MS-DOS is normally spoken of as an operating system, it could&lt;br /&gt;
just as accurately be called a &amp;quot;system executive.&amp;quot; Because it runs in an&lt;br /&gt;
unprotected environment, applications are free to edit interrupt vectors,&lt;br /&gt;
manipulate peripherals, and in general take over from MS-DOS wherever they&lt;br /&gt;
wish. This flexibility is one reason for the success of MS-DOS; if MS-DOS&lt;br /&gt;
doesn't offer the service your program needs, you can always help yourself.&lt;br /&gt;
Developers were free to explore new possibilities, often with great&lt;br /&gt;
success. Most applications view MS-DOS as a program loader and as a set of&lt;br /&gt;
file system subroutines, interfacing directly with the hardware for all&lt;br /&gt;
their other needs, such as intercepting interrupt vectors, editing disk&lt;br /&gt;
controller parameter tables, and so on.&lt;br /&gt;
     It may seem that if a popular application &amp;quot;pokes&amp;quot; the operating system&lt;br /&gt;
and otherwise engages in unsavory practices that the authors or users of&lt;br /&gt;
the application will suffer because a future release, such as OS/2, may not&lt;br /&gt;
run the application correctly. To the contrary, the market dynamics state&lt;br /&gt;
that the application has now set a standard, and it's the operating system&lt;br /&gt;
developers who suffer because they must support that standard. Usually,&lt;br /&gt;
that &amp;quot;standard&amp;quot; operating system interface is not even known; a great deal&lt;br /&gt;
of experimentation is necessary to discover exactly which undocumented side&lt;br /&gt;
effects, system internals, and timing relationships the application is&lt;br /&gt;
dependent on.&lt;br /&gt;
     Offering an MS-DOS-compatible Applications Program Interface (API)&lt;br /&gt;
provides what we call level 1 compatibility. Allowing applications to&lt;br /&gt;
continue to manipulate system hardware provides level 2 compatibility.&lt;br /&gt;
Level 3 issues deal with providing an execution environment that supports&lt;br /&gt;
the hidden assumptions that programs written for a single-tasking&lt;br /&gt;
environment may make. Three are discussed below by way of illustration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.1  Memory Utilization&lt;br /&gt;
The existing real mode applications that OS/2 must support were written for&lt;br /&gt;
an environment in which no other programs are running. As a result,&lt;br /&gt;
programs typically consume all available memory in the system in the belief&lt;br /&gt;
that, since no other program is around to use any leftover memory, they&lt;br /&gt;
might as well use it all. If a program doesn't ask for all available memory&lt;br /&gt;
at first, it may ask for the remainder at some later time. Such a&lt;br /&gt;
subsequent request could never be refused under MS-DOS versions 2.x and&lt;br /&gt;
3.x, and applications were written to depend on this. Therefore, such a&lt;br /&gt;
request must be satisfied under OS/2 to maintain full compatibility.&lt;br /&gt;
     Even the manner of a memory request depends on single-tasking&lt;br /&gt;
assumptions. Programs typically ask for all memory in two steps. First,&lt;br /&gt;
they ask for the maximum amount of memory that an 8088 can provide--1 MB.&lt;br /&gt;
The application's programmer knew that the request would be refused because&lt;br /&gt;
1 MB is greater than the 640 KB maximum supported by MS-DOS; but when MS-&lt;br /&gt;
DOS refuses the request, it tells the application exactly how much memory&lt;br /&gt;
is available. Programs then ask for that amount of memory. The programmer&lt;br /&gt;
knew that MS-DOS would not refuse the second memory request for&lt;br /&gt;
insufficient memory because when MS-DOS responded to the first request it&lt;br /&gt;
told the application exactly how much memory was available. Consequently,&lt;br /&gt;
programmers rarely included a check for an &amp;quot;insufficient memory&amp;quot; error from&lt;br /&gt;
the second call.&lt;br /&gt;
     This shortcut introduces problems in the OS/2 multitasking&lt;br /&gt;
environment. When OS/2 responded to the first too-large request, it would&lt;br /&gt;
return the amount of memory available at that exact moment. Other programs&lt;br /&gt;
are simultaneously executing; by the time our real mode program makes its&lt;br /&gt;
second request, some more memory may have been given out, and the second&lt;br /&gt;
request may also be too large. It won't do any good for OS/2 to respond&lt;br /&gt;
with an error code, however, because the real mode application does not&lt;br /&gt;
check for one (it was written in the belief that it is impossible to get&lt;br /&gt;
such a code on the second call). The upshot is that even if OS/2 refused&lt;br /&gt;
the second call the real mode application would assume that it had been&lt;br /&gt;
given the memory, would use it, and in the process would destroy the other&lt;br /&gt;
program(s) that were the true owners of that memory.&lt;br /&gt;
     Obviously, OS/2 must resolve this and similar issues to support the&lt;br /&gt;
existing base of real mode applications.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.2  File Locking&lt;br /&gt;
Because multitasking systems run more than one program at the same time,&lt;br /&gt;
two programs may try to write or to modify the same file at the same time.&lt;br /&gt;
Or one may try to read a file while another is changing that file's&lt;br /&gt;
contents. Multitasking systems usually solve this problem by means of a&lt;br /&gt;
file-locking mechanism, which allows one program to temporarily prevent&lt;br /&gt;
other programs from reading and/or writing a particular file.&lt;br /&gt;
     An application may find that a file it is accessing has been locked by&lt;br /&gt;
some other application in the system. In such a situation, OS/2 normally&lt;br /&gt;
returns a &amp;quot;file locked&amp;quot; error code, and the application typically gives up&lt;br /&gt;
or waits and retries the operation later. OS/2 cannot return a &amp;quot;file&lt;br /&gt;
locked&amp;quot; error to an old-style real mode application, though, because when&lt;br /&gt;
the application was written (for MS-DOS versions 2.x or 3.x) no such error&lt;br /&gt;
code existed because no such error was possible. Few real mode applications&lt;br /&gt;
even bother to check their read and write operations for error codes, and&lt;br /&gt;
those that do wouldn't &amp;quot;understand&amp;quot; the error code and wouldn't handle it&lt;br /&gt;
correctly.&lt;br /&gt;
     OS/2 cannot compromise the integrity of the file-locking mechanism by&lt;br /&gt;
allowing the real mode application to ignore locks, but it cannot report&lt;br /&gt;
that the file is locked to the application either. OS/2 must determine the&lt;br /&gt;
proper course of action and then take that action on behalf of the real&lt;br /&gt;
mode application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.2.3  Network Piggybacking&lt;br /&gt;
Running under MS-DOS version 3.1, an application can use an existing&lt;br /&gt;
network virtual circuit to communicate with an application running on the&lt;br /&gt;
server machine to which the virtual circuit is connected. This is called&lt;br /&gt;
&amp;quot;piggybacking&amp;quot; the virtual circuit because the applications on each end are&lt;br /&gt;
borrowing a circuit that the network redirector established for other&lt;br /&gt;
purposes. The two sets of programs can use a single circuit for two&lt;br /&gt;
different purposes without confusion under MS-DOS version 3.1 because of&lt;br /&gt;
its single-tasking nature. The redirector only uses the circuit when the&lt;br /&gt;
application calls MS-DOS to perform a network function. Because the CPU is&lt;br /&gt;
inside MS-DOS, it can't be executing the application software that sends&lt;br /&gt;
private messages, which leaves the circuit free for use by the&lt;br /&gt;
redirector.&lt;br /&gt;
     Conversely, if the application is sending its own private messages--&lt;br /&gt;
piggybacking--then it can't be executing MS-DOS, and therefore the&lt;br /&gt;
redirector code (which is built into MS-DOS) can't be using the virtual&lt;br /&gt;
circuit.&lt;br /&gt;
     This is no longer the case in OS/2. OS/2 is a multitasking system, and&lt;br /&gt;
one application can use the redirector at the same time that the real mode&lt;br /&gt;
application is piggybacking the circuit. OS/2 must somehow interlock access&lt;br /&gt;
to network virtual circuits so that multiple users of a network virtual&lt;br /&gt;
circuit do not conflict.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.3  Popular Function Compatibility&lt;br /&gt;
We've discussed some issues of binary compatibility, providing applications&lt;br /&gt;
the internal software interfaces they had in MS-DOS. This is because it is&lt;br /&gt;
vitally important that existing applications run correctly, unchanged,&lt;br /&gt;
under the new operating system.&lt;br /&gt;
5. An extremely high degree of compatibility is required for&lt;br /&gt;
virtually any application to run because a typical application&lt;br /&gt;
uses a great many documented and undocumented interfaces and&lt;br /&gt;
features of the earlier system. If any one of those interfaces&lt;br /&gt;
is not supplied, the application will not run correctly.&lt;br /&gt;
Consequently, we cannot provide 90 percent compatibility and&lt;br /&gt;
expect to run 90 percent of existing applications; 99.9 percent&lt;br /&gt;
compatibility is required for such a degree of success.&lt;br /&gt;
5 OS/2 also needs to provide functional&lt;br /&gt;
compatibility; it has to allow the creation of protect mode applications&lt;br /&gt;
that provide the functions that users grew to know and love in real mode&lt;br /&gt;
applications.&lt;br /&gt;
     This can be difficult because many popular applications (for example,&lt;br /&gt;
&amp;quot;terminate and stay resident loadable helper&amp;quot; routines such as SideKick)&lt;br /&gt;
were written for a single-tasking, unprotected environment without regard&lt;br /&gt;
to the ease with which their function could be provided in a protected&lt;br /&gt;
environment. For example, a popular application may implement some of its&lt;br /&gt;
features by patching (that is, editing) MS-DOS itself. This cannot be&lt;br /&gt;
allowed in OS/2 (the reason is discussed in Chapter 4), so OS/2 must&lt;br /&gt;
provide alternative mechanisms for protect mode applications to provide&lt;br /&gt;
services that users have grown to expect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4  Downward Compatibility&lt;br /&gt;
So far, our discussion on compatibility has focused exclusively on upward&lt;br /&gt;
compatibility--old programs must run in the new system but not vice versa.&lt;br /&gt;
Downward compatibility--running new programs under MS-DOS--is also&lt;br /&gt;
important. Developers are reluctant to write OS/2-only applications until&lt;br /&gt;
OS/2 has achieved major penetration of the market, yet this very&lt;br /&gt;
unavailability of software slows such penetration. If it's possible to&lt;br /&gt;
write applications that take advantage of OS/2's protect mode yet also run&lt;br /&gt;
unchanged under MS-DOS version 3.x, ISVs (Independent Software Vendors) can&lt;br /&gt;
write their products for OS/2 without locking themselves out of the&lt;br /&gt;
existing MS-DOS market.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4.1  Family API&lt;br /&gt;
To provide downward compatibility for applications, OS/2 designers&lt;br /&gt;
integrated a Family&lt;br /&gt;
6.Family refers to the MS-DOS/OS/2 family of&lt;br /&gt;
operating systems.&lt;br /&gt;
6 Applications Program Interface (Family API) into the&lt;br /&gt;
OS/2 project. The Family API provides a standard execution environment&lt;br /&gt;
under MS-DOS version 3.x and OS/2. Using the Family API, a programmer can&lt;br /&gt;
create an application that uses a subset of OS/2 functions (but a superset&lt;br /&gt;
of MS-DOS version 3.x functions) and that runs in a binary compatible&lt;br /&gt;
fashion under MS-DOS version 3.x and OS/2. In effect, some OS/2 functions&lt;br /&gt;
can be retrofitted into an MS-DOS version 3.x environment by means of the&lt;br /&gt;
Family API.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2.2.4.2  Network Server-Client Compatibility&lt;br /&gt;
Another important form of upward and downward compatibility is the network&lt;br /&gt;
system. You can expect any OS/2 system to be on a network, communicating&lt;br /&gt;
not only with MS-DOS 3.x systems but, one day, with a new version of OS/2&lt;br /&gt;
as well. The network interface must be simultaneously upwardly and&lt;br /&gt;
downwardly compatible with all past and future versions of networking MS-&lt;br /&gt;
DOS.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3  The OS/2 Religion&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
Religion, in the context of software design, is a body of beliefs about&lt;br /&gt;
design rights and design wrongs. A particular design is praised or&lt;br /&gt;
criticized on the basis of fact--it is small or large, fast or slow--and&lt;br /&gt;
also on the basis of religion--it is good or bad, depending on how well it&lt;br /&gt;
obeys the religious precepts. Purpose and consistency underlie the design&lt;br /&gt;
religion as a whole; its influence is felt in every individual judgment.&lt;br /&gt;
     The purpose of software design religion is to specify precepts that&lt;br /&gt;
designers can follow when selecting an approach from among the many&lt;br /&gt;
possibilities before them. A project of the size and scope of OS/2 needed a&lt;br /&gt;
carefully thought out religion because OS/2 will dramatically affect this&lt;br /&gt;
and future generations of operating systems. It needed a strong religion&lt;br /&gt;
for another reason: to ensure consistency among wide-ranging features&lt;br /&gt;
implemented by a large team of programmers. Such consistency is very&lt;br /&gt;
important; if one programmer optimizes design to do A well, at the expense&lt;br /&gt;
of doing B less well, and another programmer--in the absence of religious&lt;br /&gt;
guidance--does the opposite, the end result is a product that does neither&lt;br /&gt;
A nor B well.&lt;br /&gt;
     This chapter discusses the major architectural dogmas of the OS/2&lt;br /&gt;
religion: maximum flexibility, a stable environment, localization of&lt;br /&gt;
errors, and the software tools approach.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.1  Maximum Flexibility&lt;br /&gt;
&lt;br /&gt;
The introduction to this book discusses the process of technological&lt;br /&gt;
breakthroughs. I have pointed out that one of the easiest predictions about&lt;br /&gt;
breakthroughs is that fully predicting their course is impossible. For&lt;br /&gt;
example, the 8088 microprocessor is designed to address 1 MB of memory, but&lt;br /&gt;
the IBM PC and compatible machines are designed so that addressable memory&lt;br /&gt;
is limited to 640 KB. When this decision was made, 640 KB was ten times the&lt;br /&gt;
memory that the then state-of-the-art 8080 machines could use; the initial&lt;br /&gt;
PCs were going to ship with 16 KB in them, and it seemed to all concerned&lt;br /&gt;
that 640 KB was overly generous. Yet it took only a few years before 640 KB&lt;br /&gt;
became the typical memory complement of a machine, and within another year&lt;br /&gt;
that amount of memory was viewed as pitifully small.&lt;br /&gt;
     OS/2's design religion addresses the uncertain future by decreeing&lt;br /&gt;
that--to the extent compatible with other elements in the design religion--&lt;br /&gt;
OS/2 shall be as flexible as possible. The tenet of flexibility is that&lt;br /&gt;
each component of OS/2 should be designed as if massive changes will occur&lt;br /&gt;
in that area in a future release. In other words, the current component&lt;br /&gt;
should be designed in a way that does not restrict new features and in a&lt;br /&gt;
way that can be easily supported by a new version of OS/2, one that might&lt;br /&gt;
differ dramatically in internal design.&lt;br /&gt;
     Several general principles result from a design goal of flexibility.&lt;br /&gt;
All are intended to facilitate change, which is inevitable in the general&lt;br /&gt;
yet unpredictable in the specific:&lt;br /&gt;
&lt;br /&gt;
     1.  All OS/2 features should be sufficiently elemental (simple) that&lt;br /&gt;
         they can be easily supported in any future system, including&lt;br /&gt;
         systems fundamentally different in design from OS/2. Either the&lt;br /&gt;
         features themselves are this simple, or the features are built&lt;br /&gt;
         using base features that are this simple. The adjective simple&lt;br /&gt;
         doesn't particularly refer to externals--a small number of&lt;br /&gt;
         functions and options--but to internals. The internal operating&lt;br /&gt;
         system infrastructure necessary to provide a function should be&lt;br /&gt;
         either very simple or so fundamental to the nature of operating&lt;br /&gt;
         systems that it is inevitable in future releases.&lt;br /&gt;
            By way of analogy, as time travelers we may be able to guess&lt;br /&gt;
         very little about the twenty-first century, but we do know that &lt;br /&gt;
         people will still need to eat. The cuisine of the twenty-first&lt;br /&gt;
         century may be unguessable, but certainly future kitchens will&lt;br /&gt;
         contain facilities to cut and heat food. If we bring food that&lt;br /&gt;
         needs only those two operations, we'll find that even if there's&lt;br /&gt;
         nothing to our liking on the twenty-first-century standard menu&lt;br /&gt;
         the kitchen can still meet our needs.&lt;br /&gt;
            We've seen how important upward compatibility is for computer&lt;br /&gt;
         operating systems, so we can rest assured that the future MS-DOS&lt;br /&gt;
         &amp;quot;kitchen&amp;quot; will be happy to make the necessary effort to support&lt;br /&gt;
         old programs. All we have to do today is to ensure that such&lt;br /&gt;
         support is possible. Producing a compatible line of operating&lt;br /&gt;
         system releases means more than looking backward; it also means&lt;br /&gt;
         looking forward.&lt;br /&gt;
&lt;br /&gt;
     2.  All system interfaces need to support expansion in a future&lt;br /&gt;
         release. For example, if a call queries the status of a disk file,&lt;br /&gt;
         then in addition to passing the operating system a pointer to a&lt;br /&gt;
         structure to fill in with the information, the application must&lt;br /&gt;
         also pass in the length of that structure. Although the current&lt;br /&gt;
         release of the operating system returns N bytes of information, a&lt;br /&gt;
         future release may support new kinds of disk files and may return&lt;br /&gt;
         M bytes of information. Because the application tells the&lt;br /&gt;
         operating system, via the buffer length parameter, which version&lt;br /&gt;
         of the information structure that the application understands (the&lt;br /&gt;
         old short version or the new longer version), the operating system&lt;br /&gt;
         can support both old programs and new programs simultaneously.&lt;br /&gt;
            In general, all system interfaces should be designed to support&lt;br /&gt;
         the current feature set without restraining the addition of&lt;br /&gt;
         features to the interfaces in future releases. Extra room should&lt;br /&gt;
         be left in count and flag arguments for future expansion, and all&lt;br /&gt;
         passed and returned structures need either to be self-sizing or to&lt;br /&gt;
         include a size argument.&lt;br /&gt;
            One more interface deserves special mention--the file system&lt;br /&gt;
         interface. Expanding the capabilities of the file system, such as&lt;br /&gt;
         allowing filenames longer than eight characters, is difficult&lt;br /&gt;
         because many old applications don't know how to process filenames&lt;br /&gt;
         that are longer than eight characters or they regard the longer&lt;br /&gt;
         names as illegal and reject them. OS/2 solves this and similar&lt;br /&gt;
         problems by specifying that all filenames supplied to or returned&lt;br /&gt;
         from the operating system be zero-terminated strings (ASCIIZ&lt;br /&gt;
         strings) of arbitrary length.&lt;br /&gt;
            Programmers are specifically cautioned against parsing or&lt;br /&gt;
         otherwise &amp;quot;understanding&amp;quot; filenames. Programs should consider file&lt;br /&gt;
         system pathnames as &amp;quot;magic cookies&amp;quot; to be passed to and from the&lt;br /&gt;
         operating system, but not to be parsed by the program. The details&lt;br /&gt;
         of this interface and other expandable interfaces are discussed in&lt;br /&gt;
         later chapters.&lt;br /&gt;
&lt;br /&gt;
     3.  OS/2 needs to support the addition of functions at any time. The&lt;br /&gt;
         implementation details of these functions need to be hidden from&lt;br /&gt;
         the client applications so that those details can be changed at&lt;br /&gt;
         any time. Indeed, OS/2 should disguise even the source of a&lt;br /&gt;
         feature. Some APIs are serviced by kernel code, others are&lt;br /&gt;
         serviced by subroutine libraries, and still others may be serviced&lt;br /&gt;
         by other processes running in the system. Because a client&lt;br /&gt;
         application can't tell the difference, the system designers are&lt;br /&gt;
         free to change the implementation of an API as necessary. For&lt;br /&gt;
         example, an OS/2 kernel API might be considerably changed in a&lt;br /&gt;
         future release. The old API can continue to be supported by the&lt;br /&gt;
         creation of a subroutine library routine. This routine would take&lt;br /&gt;
         the old form of the API, convert it to the new form, call the OS/2&lt;br /&gt;
         kernel, and then backconvert the result. Such a technique allows&lt;br /&gt;
         future versions of OS/2 to support new features while continuing&lt;br /&gt;
         to provide the old features to existing programs. These techniques&lt;br /&gt;
         are discussed in detail in Chapter 7, Dynamic Linking.&lt;br /&gt;
&lt;br /&gt;
     4.  Finally, to provide maximum flexibility, the operating system&lt;br /&gt;
         should be extensible and expandable in a piecemeal fashion out in&lt;br /&gt;
         the field. In other words, a user should be able to add functions&lt;br /&gt;
         to the system--for example, a database engine--or to upgrade or&lt;br /&gt;
         replace system components--such as a new graphics display driver--&lt;br /&gt;
         without a new release from Microsoft. A microcomputer design that&lt;br /&gt;
         allows third-party hardware additions and upgrades in the field is&lt;br /&gt;
         called an open system. The IBM PC line is a classic example of an&lt;br /&gt;
         open system. A design that contains no provisions for such&lt;br /&gt;
         enhancements is called a closed system. The earliest version of&lt;br /&gt;
         the Apple Macintosh is an example. At first glance, MS-DOS appears&lt;br /&gt;
         to be a closed software system because it contains no provisions&lt;br /&gt;
         for expansion. In practice, its unprotected environment makes MS-&lt;br /&gt;
         DOS the king of the open software systems because every&lt;br /&gt;
         application is free to patch the system and access the hardware as&lt;br /&gt;
         it sees fit. Keeping a software system open is as important as&lt;br /&gt;
         keeping a hardware system open. Because OS/2 is a protected&lt;br /&gt;
         operating system, explicit features, such as dynamic linking, are&lt;br /&gt;
         provided to allow system expansion by Microsoft, other software&lt;br /&gt;
         vendors, and users themselves. The topic of open systems is&lt;br /&gt;
         discussed more fully in Chapter 7.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.2  A Stable Environment&lt;br /&gt;
&lt;br /&gt;
An office automation operating system has to provide its users--the&lt;br /&gt;
application programs and the human operator--with a stable environment.&lt;br /&gt;
Every application should work the same way each time it's run; and each&lt;br /&gt;
time an application is given the same data, it should produce the same&lt;br /&gt;
result. The normal operation of one application should not affect any other&lt;br /&gt;
application. Even a program error (bug) should not affect other programs in&lt;br /&gt;
the system. Finally, if a program has bugs, the operating system should&lt;br /&gt;
detect those bugs whenever possible and report them to the user. These&lt;br /&gt;
certainly are obvious goals, but the nature of present-day computers makes&lt;br /&gt;
them surprisingly difficult to achieve.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.2.1  Memory Protection&lt;br /&gt;
Modern computers are based on the Von Neumann design--named after John Von&lt;br /&gt;
Neumann, the pioneering Hungarian-born American mathematician and computer&lt;br /&gt;
scientist. A Von Neumann computer consists of only two parts: a memory unit&lt;br /&gt;
and a processing unit. The memory unit contains both the data to be&lt;br /&gt;
operated on and the instructions (or program) that command the processing&lt;br /&gt;
unit. The processing unit reads instructions from memory; these&lt;br /&gt;
instructions may tell it to issue further reads to memory to retrieve data,&lt;br /&gt;
to operate on data retrieved earlier, or to store data back into&lt;br /&gt;
memory.&lt;br /&gt;
     A Von Neumann computer does not distinguish between instructions and&lt;br /&gt;
data; both are stored in binary code in the computer's memory. Individual&lt;br /&gt;
programs are responsible for keeping track of which memory locations hold&lt;br /&gt;
instructions and which hold data, and each program uses the memory in a&lt;br /&gt;
different way. Because the computer does not distinguish between&lt;br /&gt;
instructions and data, a program may operate on its own instructions&lt;br /&gt;
exactly as it operates on data. A program can read, modify, and write&lt;br /&gt;
computer instructions at will.&lt;br /&gt;
1. This is a simplification. OS/2 and the 80286 CPU contain&lt;br /&gt;
features that do distinguish somewhat between instructions and&lt;br /&gt;
data and that limit the ability of programs to modify their own&lt;br /&gt;
instructions. See 9.1 Protection Model for more information.&lt;br /&gt;
1&lt;br /&gt;
     This is exactly what OS/2 does when it is commanded to run a program:&lt;br /&gt;
It reads the program into memory by treating it as data, and then it causes&lt;br /&gt;
the data in those locations to be executed. It is even possible for a&lt;br /&gt;
program to dynamically &amp;quot;reprogram&amp;quot; itself by manipulating its own&lt;br /&gt;
instructions.&lt;br /&gt;
     Computer programs are extremely complex, and errors in their logic can&lt;br /&gt;
cause the program to unintentionally modify data or instructions in memory.&lt;br /&gt;
For example, a carelessly written program might contain a command buffer 80&lt;br /&gt;
bytes in size because it expects no commands longer than 80 bytes. If a&lt;br /&gt;
user types a longer command, perhaps in error, and the program does not&lt;br /&gt;
contain a special check for this circumstance, the program will overwrite&lt;br /&gt;
the memory beyond the 80-byte command buffer, destroying the data or&lt;br /&gt;
instructions placed there.&lt;br /&gt;
2. This is a simplified example. Rarely would a present-day,&lt;br /&gt;
well-tested application contain such a naive error, but errors of&lt;br /&gt;
this type--albeit in a much more complex form--exist in nearly&lt;br /&gt;
all software.&lt;br /&gt;
2&lt;br /&gt;
     In a single-tasking environment such as MS-DOS, only one application&lt;br /&gt;
runs at a time. An error such as our example could damage memory belonging&lt;br /&gt;
to MS-DOS, the application, or memory that is not in use. In practice (due&lt;br /&gt;
to memory layout conventions) MS-DOS is rarely damaged. An aberrant program&lt;br /&gt;
typically damages itself or modifies memory not in use. In any case, the&lt;br /&gt;
error goes undetected, the program produces an incorrect result, or the&lt;br /&gt;
system crashes. In the last two cases, the user loses work, but it is clear&lt;br /&gt;
which application is in error--the one executing at the time of the crash.&lt;br /&gt;
(For completeness, I'll point out that it is possible for an aberrant&lt;br /&gt;
application to damage MS-DOS subtly enough so that the application itself&lt;br /&gt;
completes correctly, but the next time an application runs, it fails. This&lt;br /&gt;
is rare, and the new application generally fails immediately upon startup;&lt;br /&gt;
so after a few such episodes with different applications, the user&lt;br /&gt;
generally identifies the true culprit.)&lt;br /&gt;
     As we have seen, errors in programs are relatively well contained in a&lt;br /&gt;
single-tasking system. MS-DOS cannot, unfortunately, correct the error, nor&lt;br /&gt;
can it very often detect the error (these tasks can be shown to be&lt;br /&gt;
mathematically impossible, in the general case). But at least the errors&lt;br /&gt;
are contained within the aberrant application; and should errors in data or&lt;br /&gt;
logic become apparent, the user can identify the erring application. When&lt;br /&gt;
we execute a second program in memory alongside the first, the situation&lt;br /&gt;
becomes more complicated.&lt;br /&gt;
     The first difficulty arises because the commonest error for a program&lt;br /&gt;
to make is to use memory that MS-DOS has not allocated to it. In a single-&lt;br /&gt;
tasking environment these memory locations are typically unused, but in a&lt;br /&gt;
multitasking environment the damaged location(s) probably belong to some&lt;br /&gt;
other program. That program will then either give incorrect results, damage&lt;br /&gt;
still other memory locations, crash, or some combination of these. In&lt;br /&gt;
summary, a memory addressing error is more dangerous because there is more&lt;br /&gt;
in memory to damage and that damage will have a more severe effect.&lt;br /&gt;
     The second difficulty arises, not from explicit programming errors,&lt;br /&gt;
but from conflicts in the normal operation of two or more co-resident&lt;br /&gt;
programs that are in some fashion incompatible. A simple example is called&lt;br /&gt;
&amp;quot;hooking the keyboard vector&amp;quot; (see Figure 3-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
BEFORE&lt;br /&gt;
                      Vector table                    Keyboard device&lt;br /&gt;
                     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿                   driver routine&lt;br /&gt;
  Device interrupt   ³            ³        ÚÄÄÄÄÄÄÄÄ� x x x x: ÄÄÄÄÄÄ&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÃÄÄÄÄÄÄÄÄÄÄÄÄ´        ³                   ÄÄÄÄÄÄ&lt;br /&gt;
             ÀÄÄÄÄÄ� ³  x x x x   ÃÄÄÄÄÄÄÄÄÙ                   ÄÄÄÄÄÄ&lt;br /&gt;
                     ÃÄÄÄÄÄÄÄÄÄÄÄÄ´                            ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³                            ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³&lt;br /&gt;
                     ³            ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
AFTER                                 Application      Keyboard device&lt;br /&gt;
                      Vector table    edits table      driver&lt;br /&gt;
                     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿    ³            Ú� x x x x: ÄÄÄÄÄÄ&lt;br /&gt;
  Device interrupt   ³            ³    ³            ³           ÄÄÄÄÄÄ&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÃÄÄÄÄÄÄÄÄÄÄÄÄ´ �ÄÄÙ            ÀÄÄÄÄÄÄÄÄÄ¿ ÄÄÄÄÄÄ&lt;br /&gt;
             ÀÄÄÄÄÄ� ³  y y y y   ÃÄ¿   Application           ³ ÄÄÄÄÄÄ&lt;br /&gt;
                     ÃÄÄÄÄÄÄÄÄÄÄÄÄ´ ³   routine               ³ ÄÄÄÄÄÄ&lt;br /&gt;
                     ³            ³ ÀÄ� y y y y: ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ³            ³              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ³            ³              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ              ÄÄÄÄÄÄ       ³&lt;br /&gt;
                                                 ÄÄÄÄÄÄ       ³&lt;br /&gt;
                                                 jmp x x x x ÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 3-1.  Hooking the keyboard vector.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     In this case, an application modifies certain MS-DOS memory locations&lt;br /&gt;
so that when a key is pressed the application code, instead of the MS-DOS&lt;br /&gt;
code, is notified by the hardware. Applications do this because it allows&lt;br /&gt;
them to examine certain keyboard events, such as pressing the shift key&lt;br /&gt;
without pressing any other key, that MS-DOS does not pass on to&lt;br /&gt;
applications which ask MS-DOS to read the keyboard for them. It works fine&lt;br /&gt;
for one application to &amp;quot;hook&amp;quot; the keyboard vector; although hooking the&lt;br /&gt;
keyboard vector modifies system memory locations that don't belong to the&lt;br /&gt;
application, the application generally gets away with it successfully. In a&lt;br /&gt;
multitasking environment, however, a second application may want to do the&lt;br /&gt;
same trick, and the system probably won't function correctly. The result is&lt;br /&gt;
that a stable environment requires memory protection. An application must&lt;br /&gt;
not be allowed to modify, accidentally or deliberately, memory that isn't&lt;br /&gt;
assigned to that application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.2.2  Side-Effects Protection&lt;br /&gt;
A stable environment requires more than memory protection; it also requires&lt;br /&gt;
that the system be designed so that the execution of one application&lt;br /&gt;
doesn't cause side effects for any other application. Side effects can be&lt;br /&gt;
catastrophic or they can be unremarkable, but in all cases they violate the&lt;br /&gt;
tenet of a stable environment.&lt;br /&gt;
     For example, consider the practice of hooking the keyboard interrupt&lt;br /&gt;
vector. If one application uses this technique to intercept keystrokes, it&lt;br /&gt;
will intercept all keystrokes, even those intended for some other&lt;br /&gt;
application. The side effects in this case are catastrophic--the hooking&lt;br /&gt;
application sees keystrokes that aren't intended for it, and the other&lt;br /&gt;
applications don't get any keystrokes at all.&lt;br /&gt;
     Side effects can plague programs even when they are using official&lt;br /&gt;
system features if those features are not carefully designed. For example,&lt;br /&gt;
a mainframe operating system called TOPS-10 contains a program that&lt;br /&gt;
supports command files similar to MS-DOS .BAT files, and it also contains a&lt;br /&gt;
program that provides delayed offline execution of commands. Unfortunately,&lt;br /&gt;
both programs use the same TOPS-10 facility to do their work. If you&lt;br /&gt;
include a .BAT file in a delayed command list, the two programs will&lt;br /&gt;
conflict, and the .BAT file will not execute.&lt;br /&gt;
     OS/2 deals with side effects by virtualizing to the greatest extent&lt;br /&gt;
possible each application's operating environment. This means that OS/2&lt;br /&gt;
tries to make each application &amp;quot;see&amp;quot; a standard environment that is&lt;br /&gt;
unaffected by changes in another application's environment. The effect is&lt;br /&gt;
like that of a building of identical apartments. When each tenant moves in,&lt;br /&gt;
he or she gets a standard environment, a duplicate of all the apartments.&lt;br /&gt;
Each tenant can customize his or her environment, but doing so doesn't&lt;br /&gt;
affect the other tenants or their environments.&lt;br /&gt;
     Following are some examples of application environment issues that&lt;br /&gt;
OS/2 virtualizes.&lt;br /&gt;
&lt;br /&gt;
     þ  Working Directories. Each application has a working (or current)&lt;br /&gt;
        directory for each disk drive. Under MS-DOS version 3.x, if a child&lt;br /&gt;
        process changes the working directory for drive C and then exits,&lt;br /&gt;
        the working directory for drive C remains changed when the parent&lt;br /&gt;
        process regains control. OS/2 eliminates this side effect by&lt;br /&gt;
        maintaining a separate list of working directories for each process&lt;br /&gt;
        in the system. Thus, when an application changes its working&lt;br /&gt;
        directories, the working directories of other applications in the&lt;br /&gt;
        system remain unchanged.&lt;br /&gt;
&lt;br /&gt;
     þ  Memory Utilization. The simple act of memory consumption produces&lt;br /&gt;
        side effects. If one process consumes all available RAM, none is&lt;br /&gt;
        left for the others. The OS/2 memory management system uses memory&lt;br /&gt;
        overcommit (swapping) so that the memory needs of each application&lt;br /&gt;
        can be met.&lt;br /&gt;
&lt;br /&gt;
     þ  Priority. OS/2 uses a priority-based scheduler to assign the CPU to&lt;br /&gt;
        the processes that need it. Applications can adjust their priority&lt;br /&gt;
        and that of their child processes as they see fit. However, the&lt;br /&gt;
        very priority of a task causes side effects. Consider a process&lt;br /&gt;
        that tells OS/2 that it must run at a higher priority than any&lt;br /&gt;
        other task in the system. If a second process makes the same&lt;br /&gt;
        request, a conflict occurs: Both processes cannot be the highest&lt;br /&gt;
        priority in the system. In general, the priority that a process&lt;br /&gt;
        wants for itself depends on the priorities of the other processes&lt;br /&gt;
        in the system. The OS/2 scheduler contains a sophisticated&lt;br /&gt;
        absolute/relative mechanism to deal with these conflicts.&lt;br /&gt;
&lt;br /&gt;
     þ  File Utilization. As discussed earlier, one application may modify&lt;br /&gt;
        the files that another application is using, causing an unintended&lt;br /&gt;
        side effect. The OS/2 file-locking mechanism prevents unintended&lt;br /&gt;
        modifications, and the OS/2 record-locking mechanism coordinates&lt;br /&gt;
        intentional parallel updates to a single file.&lt;br /&gt;
&lt;br /&gt;
     þ  Environment Strings. OS/2 retains the MS-DOS concept of environment&lt;br /&gt;
        strings: Each process has its own set. A child process inherits a&lt;br /&gt;
        copy of the parent's environment strings, but changing the strings&lt;br /&gt;
        in this copy will not affect the original strings in the parent's&lt;br /&gt;
        environment.&lt;br /&gt;
&lt;br /&gt;
     þ  Keyboard Mode. OS/2 applications can place the keyboard in one of&lt;br /&gt;
        two modes--cooked or raw. These modes tell OS/2 whether the&lt;br /&gt;
        application wants to handle, for example, the backspace character&lt;br /&gt;
        (raw mode) or whether it wants OS/2 to handle the backspace&lt;br /&gt;
        character for it (cooked mode). The effect of these calls on&lt;br /&gt;
        subsequent keyboard read operations would cause side effects for&lt;br /&gt;
        other applications reading from the keyboard, so OS/2 maintains a&lt;br /&gt;
        record of the cooked/raw status of each application and silently&lt;br /&gt;
        switches the mode of the keyboard when an application issues a&lt;br /&gt;
        keyboard read request.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.3  Localization of Errors&lt;br /&gt;
&lt;br /&gt;
A key element in creating a stable environment is localizing errors. Humans&lt;br /&gt;
always make errors, and human creations such as computer programs always&lt;br /&gt;
contain errors. Before the development of computers, routine human errors&lt;br /&gt;
were usually limited in scope. Unfortunately, as the saying goes, a&lt;br /&gt;
computer can make a mistake in 60 seconds that it would take a whole office&lt;br /&gt;
force a year to make. Although OS/2 can do little to prevent such errors,&lt;br /&gt;
it needs to do its best to localize the errors.&lt;br /&gt;
     Localizing errors consists of two activities: minimizing as much as&lt;br /&gt;
possible the impact of the error on other applications in the system, and&lt;br /&gt;
maximizing the opportunity for the user to understand which of the many&lt;br /&gt;
programs running in the computer caused the error. These two activities are&lt;br /&gt;
interrelated in that the more successful the operating system is in&lt;br /&gt;
restricting the damage to the domain of a single program, the easier it is&lt;br /&gt;
for the user to know which program is at fault.&lt;br /&gt;
     The most important aspect of error localization has already been&lt;br /&gt;
discussed at length--memory management and protection. Other error&lt;br /&gt;
localization principles include the following:&lt;br /&gt;
&lt;br /&gt;
     þ  No program can crash or hang the system. A fundamental element of&lt;br /&gt;
        the OS/2 design religion is that no application program can,&lt;br /&gt;
        accidentally or even deliberately, crash or hang the system. If a&lt;br /&gt;
        failing application could crash the system, obviously the system&lt;br /&gt;
        did not localize the error! Furthermore, the user would be unable&lt;br /&gt;
        to identify the responsible application because the entire system&lt;br /&gt;
        would be dead.&lt;br /&gt;
&lt;br /&gt;
     þ  No program can make inoperable any screen group other than its own.&lt;br /&gt;
        As we'll see in later chapters of this book, sometimes design&lt;br /&gt;
        goals, design religions, or both conflict. For example, the precept&lt;br /&gt;
        of no side effects conflicts with the requirement of supporting&lt;br /&gt;
        keyboard macro expander applications. The sole purpose of such an&lt;br /&gt;
        application is to cause a side effect--specifically to translate&lt;br /&gt;
        certain keystroke sequences into other sequences. OS/2 resolves&lt;br /&gt;
        this conflict by allowing applications to examine and modify the&lt;br /&gt;
        flow of data to and from devices (see Chapter 16) but in a&lt;br /&gt;
        controlled fashion. Thus, an aberrant keyboard macro application&lt;br /&gt;
        that starts to &amp;quot;eat&amp;quot; all keys, passing none through to the&lt;br /&gt;
        application, can make its current screen group unusable, but it&lt;br /&gt;
        can't affect the user's ability to change screen groups.&lt;br /&gt;
           Note that keyboard monitors can intercept and consume any&lt;br /&gt;
        character or character sequence except for the keystrokes that OS/2&lt;br /&gt;
        uses to switch screen groups (Ctrl-Esc and Alt-Esc). This is to&lt;br /&gt;
        prevent aberrant keyboard monitor applications from accidentally&lt;br /&gt;
        locking the user into his or her screen group by consuming and&lt;br /&gt;
        discarding the keyboard sequences that are used to switch from&lt;br /&gt;
        screen groups.&lt;br /&gt;
&lt;br /&gt;
     þ  Applications cannot intercept general protection (GP) fault errors.&lt;br /&gt;
        A GP fault occurs when a program accesses invalid memory locations&lt;br /&gt;
        or accesses valid locations in an invalid way (such as writing into&lt;br /&gt;
        read-only memory areas). OS/2 always terminates the operation and&lt;br /&gt;
        displays a message for the user. A GP fault is evidence that the&lt;br /&gt;
        program's logic is incorrect, and therefore it cannot be expected&lt;br /&gt;
        to fix itself or trusted to notify the user of its ill health.&lt;br /&gt;
           The OS/2 design does allow almost any other error on the part of&lt;br /&gt;
        an application to be detected and handled by that application. For&lt;br /&gt;
        example, &amp;quot;Illegal filename&amp;quot; is an error caused by user input, not&lt;br /&gt;
        by the application. The application can deal with this error as it&lt;br /&gt;
        sees fit, perhaps correcting and retrying the operation. An error&lt;br /&gt;
        such as &amp;quot;Floppy disk drive not ready&amp;quot; is normally handled by OS/2&lt;br /&gt;
        but can be handled by the application. This is useful for&lt;br /&gt;
        applications that are designed to operate unattended; they need to&lt;br /&gt;
        handle errors themselves rather than waiting for action to be taken&lt;br /&gt;
        by a nonexistent user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3.4  Software Tools Approach&lt;br /&gt;
&lt;br /&gt;
In Chapter 2 we discussed IPC and the desirability of having separate&lt;br /&gt;
functions contained in separate programs. We discussed the flexibility of&lt;br /&gt;
such an approach over the &amp;quot;one man band&amp;quot; approach of an all-in-one&lt;br /&gt;
application. We also touched on the value of being able to upgrade the&lt;br /&gt;
functionality of the system incrementally by replacing individual programs.&lt;br /&gt;
All these issues are software tools issues.&lt;br /&gt;
     Software tools refers to a design philosophy which says that&lt;br /&gt;
individual programs and applications should be like tools: Each should do&lt;br /&gt;
one job and do it very well. A person who wants to turn screws and also&lt;br /&gt;
drive nails should get a screwdriver and a hammer rather than a single tool&lt;br /&gt;
that does neither job as well.&lt;br /&gt;
     The tools approach is used routinely in nonsoftware environments and&lt;br /&gt;
is taken for granted. For example, inside a standard PC the hardware and&lt;br /&gt;
electronics are isolated into functional components that communicate via&lt;br /&gt;
interfaces. The power supply is in a box by itself; its interface is the&lt;br /&gt;
line cord and some power connectors. The disk drives are separate from the&lt;br /&gt;
rest of the electronics; they interface via more connectors. Each component&lt;br /&gt;
is the equivalent of a software application: It does one job and does it&lt;br /&gt;
well. When the disk drive needs power, it doesn't build in a power supply;&lt;br /&gt;
it uses the standard interface to the power supply module--the power&lt;br /&gt;
&amp;quot;specialist&amp;quot; in the system.&lt;br /&gt;
     Occasionally, the software tools approach is criticized for being&lt;br /&gt;
inefficient. People may argue that space is wasted and time is lost by&lt;br /&gt;
packaging key functions separately; if they are combined, the argument&lt;br /&gt;
goes, nothing is wasted. This argument is correct in that some RAM and CPU&lt;br /&gt;
time is spent on interface issues, but it ignores the gains involved in&lt;br /&gt;
&amp;quot;sending out the work&amp;quot; to a specialist rather than doing it oneself. One&lt;br /&gt;
could argue, for example, that if I built an all-in-one PC system I'd save&lt;br /&gt;
money because I wouldn't have to buy connectors to plug everything&lt;br /&gt;
together. I might also save a little by not having to buy buffer chips to&lt;br /&gt;
drive signals over those connectors. But in doing so, I'd lose the&lt;br /&gt;
advantage of being able to buy my power supply from a very high-volume and&lt;br /&gt;
high-efficiency supplier--someone who can make a better, cheaper supply,&lt;br /&gt;
even with the cost of connectors, than my computer company can.&lt;br /&gt;
     Finally, the user gains from the modular approach. If you need more&lt;br /&gt;
disk capability, you can buy one and plug it in. You are not limited to one&lt;br /&gt;
disk maker but rather can choose the one that's right for your needs--&lt;br /&gt;
expensive and powerful or cheap and modest. You can buy third-party&lt;br /&gt;
hardware, such as plug-in cards, that the manufacturer of your computer&lt;br /&gt;
doesn't make. All in all, the modest cost of a few connectors and driver&lt;br /&gt;
chips is paid back manyfold, both in direct system costs (due to the&lt;br /&gt;
efficiency of specialization) and in the additional capability and&lt;br /&gt;
flexibility of the machine.&lt;br /&gt;
     As I said earlier, the software tools approach is the software&lt;br /&gt;
equivalent of an open system. It's an important part of the OS/2 religion:&lt;br /&gt;
Although the system doesn't require a modular tools approach from&lt;br /&gt;
applications programs, it should do everything in its power to facilitate&lt;br /&gt;
such systems, and it should itself be constructed in that fashion.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part II  The Architecture&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4  Multitasking&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
I have discussed the goals and compatibility issues that OS/2 is intended&lt;br /&gt;
to meet, and I have described the design religion that was established for&lt;br /&gt;
OS/2. The following chapters discuss individual design elements in some&lt;br /&gt;
detail, emphasizing not only how the elements work and are used but the&lt;br /&gt;
role they play in the system as a whole.&lt;br /&gt;
     In a multitasking operating system, two or more programs can execute&lt;br /&gt;
at the same time. Some benefits of such a feature are obvious: You (the&lt;br /&gt;
user) can switch between several application programs without saving work&lt;br /&gt;
and exiting one program to start another. When the telephone rings, for&lt;br /&gt;
example, you can switch from the word processor application you are using&lt;br /&gt;
to write a memo and go to the application that is managing your appointment&lt;br /&gt;
calendar or to the spreadsheet application that contains the figures that&lt;br /&gt;
are necessary to answer your caller's query.&lt;br /&gt;
     This type of multitasking is similar to what people do when they're&lt;br /&gt;
not working with a computer. You may leave a report half read on your desk&lt;br /&gt;
to address a more pressing need, such as answering the phone. Later,&lt;br /&gt;
perhaps after other tasks intervene, you return to the report. You don't&lt;br /&gt;
terminate a project and return your reference materials to the bookshelf,&lt;br /&gt;
the files, and the library to answer the telephone; you merely switch your&lt;br /&gt;
attention for a while and later pick up where you left off.&lt;br /&gt;
     This kind of multitasking is called serial multitasking because&lt;br /&gt;
actions are performed one at a time. Although you probably haven't thought&lt;br /&gt;
of it this way, you've spent much of your life serially multitasking. Every&lt;br /&gt;
day when you leave for work, you suspend your home life and resume your&lt;br /&gt;
work life. That evening, you reverse the process. You serially multitask a&lt;br /&gt;
hobby--each time picking it up where you left off last time and then&lt;br /&gt;
leaving off again. Reading the comics in a daily newspaper is a prodigious&lt;br /&gt;
feat of human serial multitasking--you switch from one to another of&lt;br /&gt;
perhaps 20 strips, remembering for each what has gone on before and then&lt;br /&gt;
waiting until tomorrow for the next installment. Although serial&lt;br /&gt;
multitasking is very useful, it is not nearly as useful as full&lt;br /&gt;
multitasking--the kind of multitasking built into OS/2.&lt;br /&gt;
     Full multitasking on the computer involves doing more than one thing--&lt;br /&gt;
running more than one application--at the same time. Humans do a little of&lt;br /&gt;
this, but not too much. People commonly talk while they drive cars, eat&lt;br /&gt;
while watching television, and walk while chewing gum. None of these&lt;br /&gt;
activities requires one's full concentration though. Humans generally can't&lt;br /&gt;
fully multitask activities that require a significant amount of&lt;br /&gt;
concentration because they have only one brain.&lt;br /&gt;
     For that matter, a personal computer has only one &amp;quot;brain&amp;quot;--one CPU.&lt;br /&gt;
1. Although multiple-CPU computers are well known, personal computers&lt;br /&gt;
with multiple CPUs are uncommon. In any case, this discussion applies,&lt;br /&gt;
with the obvious extensions, to multiple-CPU systems.&lt;br /&gt;
1&lt;br /&gt;
But OS/2 can switch this CPU from one activity to another very rapidly--&lt;br /&gt;
dozens or even hundreds of times a second. All executing programs seem to&lt;br /&gt;
be running at the same time, at least on the human scale of time. For&lt;br /&gt;
example, if five programs are running and each in turn gets 0.01 second of&lt;br /&gt;
CPU time (that is, 10 milliseconds), in 1 second each program receives 20&lt;br /&gt;
time slices. To most observers, human or other computer software, all five&lt;br /&gt;
programs appear to be running simultaneously but each at one-fifth its&lt;br /&gt;
maximum speed. We'll return to the topic of time slicing later; for now,&lt;br /&gt;
it's easiest--and, as we shall see, best--to pretend that all executing&lt;br /&gt;
programs run simultaneously.&lt;br /&gt;
     The full multitasking capabilities of OS/2 allow the personal computer&lt;br /&gt;
to act as more than a mere engine to run applications; the personal&lt;br /&gt;
computer can now be a system of services. The user can interact with a&lt;br /&gt;
spreadsheet program, for example, while a mail application is receiving&lt;br /&gt;
network messages that the user can read later. At the same time, other&lt;br /&gt;
programs may be downloading data from a mainframe computer or spooling&lt;br /&gt;
output to a printer or a plotter. The user may have explicitly initiated&lt;br /&gt;
some of these activities; a program may have initiated others. Regardless,&lt;br /&gt;
they all execute simultaneously, and they all do their work without&lt;br /&gt;
requiring the user's attention or intervention.&lt;br /&gt;
     Full multitasking is useful to programs themselves. Earlier, we&lt;br /&gt;
discussed the advantages of a tools approach--writing programs so that&lt;br /&gt;
they can offer their services to other programs. The numerous advantages&lt;br /&gt;
of this technique are possible only because of full&lt;br /&gt;
multitasking. For&lt;br /&gt;
example, if a program is to be able to invoke another program to sort a &lt;br /&gt;
data file, the sort program must execute at the same time as its client &lt;br /&gt;
program. It wouldn't be very useful if the client program had to &lt;br /&gt;
terminate in order  for the sort program to run.&lt;br /&gt;
     Finally, full multitasking is useful within a program itself. A thread&lt;br /&gt;
is an OS/2 mechanism that allows more than one path of execution through a&lt;br /&gt;
particular application. (Threads are discussed in detail later; for now it&lt;br /&gt;
will suffice to imagine that several CPUs can be made to execute the same&lt;br /&gt;
program simultaneously.) This allows individual applications to perform&lt;br /&gt;
more than one task at a time. For example, if the user tells a spreadsheet&lt;br /&gt;
program to recalculate a large budget analysis, the program can use one&lt;br /&gt;
thread to do the calculating and another to prompt for, read, and obey the&lt;br /&gt;
user's next command. In effect, multiple operations overlap during&lt;br /&gt;
execution and thereby increase the program's responsiveness to the user.&lt;br /&gt;
     OS/2 uses a time-sliced, priority-based preemptive scheduler to&lt;br /&gt;
provide full multitasking. In other words, the OS/2 scheduler preempts--&lt;br /&gt;
takes away--the CPU from one application at any time the scheduler desires&lt;br /&gt;
and assigns the CPU another application. Programs don't surrender the CPU&lt;br /&gt;
when they feel like it; OS/2 preempts it. Each program in the system (more&lt;br /&gt;
precisely, each thread in the system) has its own priority. When a thread&lt;br /&gt;
of a higher priority than the one currently running wants to run, the&lt;br /&gt;
scheduler preempts the running thread in favor of the higher priority one.&lt;br /&gt;
If two or more runnable threads have the same highest priority, OS/2 runs&lt;br /&gt;
each in turn for a fraction of a second--a time slice.&lt;br /&gt;
     The OS/2 scheduler does not periodically look around to see if the&lt;br /&gt;
highest priority thread is running. Such an approach wastes CPU time and&lt;br /&gt;
slows response time because a higher priority thread must wait to run until&lt;br /&gt;
the next scheduler scan. Instead, other parts of the system call the&lt;br /&gt;
scheduler when they think that a thread other than the one running should&lt;br /&gt;
be executed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.1  Subtask Model&lt;br /&gt;
&lt;br /&gt;
The terms task and process are used interchangeably to describe the direct&lt;br /&gt;
result of executing a binary (.EXE) file. A process is the unit of&lt;br /&gt;
ownership under OS/2, and processes own resources such as memory, open&lt;br /&gt;
files, connections to dynlink libraries, and semaphores. Casual users would&lt;br /&gt;
call a process a &amp;quot;program&amp;quot;; and, in fact, under MS-DOS all programs and&lt;br /&gt;
applications consist of a single process. OS/2 uses the terms task or&lt;br /&gt;
process because a single application program under OS/2 may consist of more&lt;br /&gt;
than one process. This section describes how this is done.&lt;br /&gt;
     First, some more terminology. When a process creates, or execs,&lt;br /&gt;
another process, the creator process is called the parent process, and the&lt;br /&gt;
created process is called the child process. The parent of the parent is&lt;br /&gt;
the child's grandparent and so on. As with people, each process in the&lt;br /&gt;
system has or had a parent.&lt;br /&gt;
2. Obviously, during boot-up OS/2 creates an initial parentless&lt;br /&gt;
process by &amp;quot;magic,&amp;quot; but this is ancient history by the time any&lt;br /&gt;
application may run, so the anomaly may be safely ignored.&lt;br /&gt;
2 Although we use genealogical terms to describe&lt;br /&gt;
task relationships, a child task, or process, is more like an agent or&lt;br /&gt;
employee of the parent task. Employees are hired to do work for an&lt;br /&gt;
employer. The employer provides a workplace and access to the information&lt;br /&gt;
employees need to do their jobs. The same is generally true for a child&lt;br /&gt;
task. When a child task is created, it inherits (or receives a copy of) a&lt;br /&gt;
great deal of the parent task's environment. For example, it inherits, or&lt;br /&gt;
takes on, the parent's base scheduling priority and its screen group. The&lt;br /&gt;
term inherit is a little inappropriate because the parent task has not&lt;br /&gt;
died. It is alive and well, going about its business.&lt;br /&gt;
     The most important items a child task inherits are its parent's open&lt;br /&gt;
file handles. OS/2 uses a handle mechanism to perform file I/O, as do MS-&lt;br /&gt;
DOS versions 2.0 and later. When a file is opened, OS/2 returns a handle--&lt;br /&gt;
an integer value--to the process. When a program wants to read from or&lt;br /&gt;
write to a file, it gives OS/2 the file handle. Handles are not identical&lt;br /&gt;
among processes. For example, the file referred to by handle 6 of one&lt;br /&gt;
process bears no relationship to the file referred to by another process's&lt;br /&gt;
handle 6, unless one of those processes is a child of the other. When a&lt;br /&gt;
parent process creates a child process, the child process, by default,&lt;br /&gt;
inherits each of the parent's open file handles. For example, a parent&lt;br /&gt;
process has the file \WORK\TEMPFILE open on handle 5; when the child&lt;br /&gt;
process starts up, handle 5 is open and references the \WORK\TEMPFILE file.&lt;br /&gt;
     This undoubtedly seems brain damaged if you are unfamiliar with this&lt;br /&gt;
model. Why is it done in this crazy way? What use does the child process&lt;br /&gt;
have for these open files? What's to keep the child from mucking up the&lt;br /&gt;
parent's files? All this becomes clearer when the other piece of the puzzle&lt;br /&gt;
is in place--the standard file handles.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.1.1  Standard File Handles&lt;br /&gt;
Many OS/2 functions use 16-bit integer values called handles for their&lt;br /&gt;
interfaces. A handle is an object that programmers call a &amp;quot;magic cookie&amp;quot;--&lt;br /&gt;
an arbitrary value that OS/2 provides the application so that the&lt;br /&gt;
application can pass the value back to OS/2 on subsequent calls. Its&lt;br /&gt;
purpose is to simplify the OS/2 interface and speed up the particular&lt;br /&gt;
service. For example, when a program creates a system semaphore, it is&lt;br /&gt;
returned a semaphore handle--a magic cookie--that it uses for subsequent&lt;br /&gt;
request and release operations. Referring to the semaphore via a 16-bit&lt;br /&gt;
value is much faster than passing around a long filename. Furthermore, the&lt;br /&gt;
magic in magic cookie is that the meaning of the 16-bit handle value is&lt;br /&gt;
indecipherable to the application. OS/2 created the value, and it has&lt;br /&gt;
meaning only to OS/2; the application need only retain the value and&lt;br /&gt;
regurgitate it when appropriate. An application can never make any&lt;br /&gt;
assumptions about the values of a magic cookie.&lt;br /&gt;
     File handles are an exceptional form of handle because they are not&lt;br /&gt;
magic cookies. The handle value, in the right circumstances, is meaningful&lt;br /&gt;
to the application and to the system as a whole. Specifically, three handle&lt;br /&gt;
values have special meaning: handle value 0, called STDIN (for standard&lt;br /&gt;
input); handle value 1, called STDOUT (standard output); and handle value&lt;br /&gt;
2, called STDERR (standard error). A simple program--let's call it NUMADD--&lt;br /&gt;
will help to explain the use of these three handles. NUMADD will read two&lt;br /&gt;
lines of ASCII text (each containing a decimal number), convert the numbers&lt;br /&gt;
to binary, add them, and then convert the results to an ASCII string and&lt;br /&gt;
write out the result. Note that we're confining our attention to a simple&lt;br /&gt;
non-screen-oriented program that might be used as a tool, either directly&lt;br /&gt;
by a programmer or by another program (see Figure 4-1 and Listing 4-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      STDIN ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
123 ÄÄÄ¿    ³          ³ STDOUT&lt;br /&gt;
14     ÀÄÄÄ�³  NUMADD  ÃÄÄÄ¿&lt;br /&gt;
            ³          ³   ÀÄÄÄÄ� 137&lt;br /&gt;
            ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-1.  Program NUMADD operation--interactive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;            /* defines stdin and stdout */&lt;br /&gt;
&lt;br /&gt;
main()&lt;br /&gt;
{&lt;br /&gt;
     int value1, value2, sum;&lt;br /&gt;
&lt;br /&gt;
     fscanf (stdin, &amp;quot;%d&amp;quot;, &amp;amp;value1);&lt;br /&gt;
     fscanf (stdin, &amp;quot;%d&amp;quot;, &amp;amp; value2);&lt;br /&gt;
     sum = value1 + value2;&lt;br /&gt;
     fprintf (stdout, &amp;quot;%d\n&amp;quot;, sum);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-1.  Program NUMADD.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     By convention, all OS/2 programs read input from STDIN and write&lt;br /&gt;
output to STDOUT. Any error messages are written to STDERR. The program&lt;br /&gt;
itself does not open these handles; it inherits them from the parent&lt;br /&gt;
process. The parent may have opened them itself or inherited them from its&lt;br /&gt;
own parent. As you can see, NUMADD would not contain DosOpen calls;&lt;br /&gt;
instead, it would start immediately issuing fscanf calls on handle 0&lt;br /&gt;
(STDIN), which in turn issues DosRead calls, and, when ready, directly&lt;br /&gt;
issue fprintf calls to handle 1 (STDOUT), which in turn issues DosWrites.&lt;br /&gt;
     Figure 4-2 and Listing 4-2 show a hypothetical application, NUMARITH.&lt;br /&gt;
NUMARITH reads three text lines. The first line contains an operation&lt;br /&gt;
character, such as a plus (+) or a minus (-); the second and third lines&lt;br /&gt;
contain the values to be operated upon. The author of this program doesn't&lt;br /&gt;
want to reinvent the wheel; so when the program NUMARITH encounters a +&lt;br /&gt;
operation, it executes NUMADD to do the work. As shown, the parent process&lt;br /&gt;
NUMARITH has its STDIN connected to the keyboard and its STDOUT connected&lt;br /&gt;
to the screen device drivers.&lt;br /&gt;
3. CMD.EXE inherited these handles from its own parent. This process&lt;br /&gt;
is discussed later.&lt;br /&gt;
3 When NUMADD executes, it reads input from&lt;br /&gt;
the keyboard via STDIN. After the user types the two numbers, NUMADD&lt;br /&gt;
displays the result on the screen via STDOUT. NUMARITH has invoked NUMADD&lt;br /&gt;
to do some work for it, and NUMADD has silently and seamlessly acted as a&lt;br /&gt;
part of NUMARITH. The employee metaphor fits well here. NUMADD acted as an&lt;br /&gt;
employee of NUMARITH, making use of NUMARITH's I/O streams, and as a result&lt;br /&gt;
the contribution of the NUMADD employee to the NUMARITH company is seamless.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
keyboard                                   screen&lt;br /&gt;
    ³     STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT     ³&lt;br /&gt;
    ÀÄÄÄÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
            ÃÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄ´&lt;br /&gt;
            �      ³          ³     �&lt;br /&gt;
            ³      ÀÄÄÄÄÄÂÄÄÄÄÙ     ³&lt;br /&gt;
            ³            ³ DosExec  ³&lt;br /&gt;
   inherits ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿     ³ inherits&lt;br /&gt;
            ³      ³          ³     ³&lt;br /&gt;
            ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄ�ÄÄÙ&lt;br /&gt;
                   ³          ³&lt;br /&gt;
                   ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-2.  Program NUMARITH operation--interactive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**     Numarith - Perform ASCII Arithmetic&lt;br /&gt;
*&lt;br /&gt;
*       Numarith reads line triplets:&lt;br /&gt;
*&lt;br /&gt;
*               operation&lt;br /&gt;
*               value1&lt;br /&gt;
*               value2&lt;br /&gt;
*&lt;br /&gt;
*       performs the specified operation (+, -, *, /) on&lt;br /&gt;
*       the two values and prints the result on stdout.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
main()&lt;br /&gt;
{&lt;br /&gt;
        char operation;&lt;br /&gt;
&lt;br /&gt;
        fscanf (stdin, &amp;quot;%c&amp;quot;, &amp;amp;operation);&lt;br /&gt;
&lt;br /&gt;
        switch (operation) {&lt;br /&gt;
&lt;br /&gt;
            case '+':  execl (&amp;quot;numadd&amp;quot;,  0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '-':  execl (&amp;quot;numsub&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '*':  execl (&amp;quot;nummul&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
&lt;br /&gt;
            case '/':  execl (&amp;quot;numdiv&amp;quot;, 0);&lt;br /&gt;
                        break;&lt;br /&gt;
            }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-2.  Program NUMARITH.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Figure 4-3 shows a similar situation. In this case, however,&lt;br /&gt;
NUMARITH's STDIN and STDOUT handles are open on two files, which we'll call&lt;br /&gt;
DATAIN and DATAOUT. Once again, NUMADD does its work seamlessly. The input&lt;br /&gt;
numbers are read from the command file on STDIN, and the output is properly&lt;br /&gt;
intermingled in the log file on STDOUT. The key here is that this NUMADD is&lt;br /&gt;
exactly the same program that ran in Listing 4-2; NUMADD contains no&lt;br /&gt;
special code to deal with this changed situation. In both examples, NUMADD&lt;br /&gt;
simply reads from STDIN and writes to STDOUT; NUMADD neither knows nor&lt;br /&gt;
cares where those handles point. Exactly the same is true for the parent.&lt;br /&gt;
NUMARITH doesn't know and doesn't care that it's working from files instead&lt;br /&gt;
of from the screen; it simply uses the STDIN and STDOUT handles that it&lt;br /&gt;
inherited from its parent.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
File data in                                     File data out&lt;br /&gt;
     ___                                              ___&lt;br /&gt;
   /     \                                          /     \&lt;br /&gt;
  ³\ ___ /³                                        ³\ ___ /³&lt;br /&gt;
  ³       ³                                        ³       ³&lt;br /&gt;
  ³       ÃÄÄ¿                                  ÚÄ�³       ³&lt;br /&gt;
  ³       ³  ³  STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT  ³  ³       ³&lt;br /&gt;
   \ ___ /   ÀÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÙ   \ ___ /&lt;br /&gt;
                  ÃÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄ´&lt;br /&gt;
                  �      ³          ³     �&lt;br /&gt;
                  ³      ÀÄÄÄÄÄÂÄÄÄÄÙ     ³&lt;br /&gt;
                  ³            ³ DosExec  ³&lt;br /&gt;
         inherits ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿     ³ inherits&lt;br /&gt;
                  ³      ³          ³     ³&lt;br /&gt;
                  ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄ�ÄÄÙ&lt;br /&gt;
                         ³          ³&lt;br /&gt;
                         ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-3.  Program NUMARITH operation--from files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This is the single most important concept in the relationship and&lt;br /&gt;
inheritance structure between processes. The reason a process inherits so&lt;br /&gt;
much from its parent is so that the parent can set up the tool's&lt;br /&gt;
environment--make it read from the parent's STDIN or from a file or from an&lt;br /&gt;
anonymous pipe (see 4.1.2 Anonymous Pipes). This gives the parent the&lt;br /&gt;
flexibility to use a tool program as it wishes, and it frees the tool's&lt;br /&gt;
author from the need to be &amp;quot;all things to all people.&amp;quot;&lt;br /&gt;
     Of equal importance, the inheritance architecture provides&lt;br /&gt;
nesting encapsulation of child processes. NUMARITH's parent process doesn't&lt;br /&gt;
know and doesn't need to know how NUMARITH does its job. NUMARITH can do&lt;br /&gt;
the additions itself, or it can invoke NUMADD as a child, but the&lt;br /&gt;
architecture encapsulates the details of NUMARITH's operation so that&lt;br /&gt;
NUMADD's involvement is hidden from NUMARITH's parent. Likewise, the&lt;br /&gt;
decision of NUMARITH's parent to work from a file or from a device or from&lt;br /&gt;
a pipe is encapsulated (that is, hidden) from NUMARITH and from any child&lt;br /&gt;
processes that NUMARITH may execute to help with its work. Obviously, this&lt;br /&gt;
architecture can be extended arbitrarily: NUMADD can itself execute a child&lt;br /&gt;
process to help NUMADD with its work, and this would silently and invisibly&lt;br /&gt;
work. Neither NUMARITH nor its parent would know or need to know anything&lt;br /&gt;
about how NUMADD was doing its work. Other versions can replace any of&lt;br /&gt;
these applications at any time. The new versions can invoke more or fewer&lt;br /&gt;
child processes or be changed in any other way, and their client (that is,&lt;br /&gt;
parent) processes are unaffected. The architecture of OS/2 is tool-based;&lt;br /&gt;
as long as the function of a tool remains constant (or is supersetted), its&lt;br /&gt;
implementation is irrelevant and can be changed arbitrarily.&lt;br /&gt;
     The STDIN, STDOUT, and STDERR architecture applies to all programs,&lt;br /&gt;
even those that only use VIO, KBD, or the presentation manager and that&lt;br /&gt;
never issue operations on these handles. See Chapter 14, Interactive&lt;br /&gt;
Programs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.1.2  Anonymous Pipes&lt;br /&gt;
NUMADD and NUMARITH are pretty silly little programs; a moment's&lt;br /&gt;
consideration will show how the inheritance architecture applies to more&lt;br /&gt;
realistic programs. An example is the TREE program that runs when the TREE&lt;br /&gt;
command is given to CMD.EXE. TREE inherits the STDIN and STDOUT handles,&lt;br /&gt;
but it does not use STDIN; it merely writes output to STDOUT. As a result,&lt;br /&gt;
when the user types TREE at a CMD.EXE prompt, the output appears on the&lt;br /&gt;
screen. When TREE appears in a batch file, the output appears in the LOG&lt;br /&gt;
file or on the screen, depending on where STDOUT is pointing.&lt;br /&gt;
     This is all very useful, but what if an application wants to further&lt;br /&gt;
process the output of the child program rather than having the child's&lt;br /&gt;
output intermingled with the application's output? OS/2 does this with&lt;br /&gt;
anonymous pipes. The adjective anonymous distinguishes these pipes from a&lt;br /&gt;
related facility, named pipes, which are not implemented in OS/2 version&lt;br /&gt;
1.0.&lt;br /&gt;
     An anonymous pipe is a data storage buffer that OS/2 maintains. When a&lt;br /&gt;
process opens an anonymous pipe, it receives two file handles--one for&lt;br /&gt;
writing and one for reading. Data can be written to the write handle via&lt;br /&gt;
the DosWrite call and then read back via the read handle and the DosRead&lt;br /&gt;
call. An anonymous pipe is similar to a file in that it is written and read&lt;br /&gt;
via file handle operations, but an anonymous pipe and a file are&lt;br /&gt;
significantly different. Pipe data is stored only in RAM buffers, not on a&lt;br /&gt;
disk, and is accessed only in FIFO (First In First Out) fashion. The&lt;br /&gt;
DosChgFilePtr operation is illegal on pipe handles.&lt;br /&gt;
     An anonymous pipe is of little value to a single process, since it&lt;br /&gt;
acts as a simple FIFO (First In First Out) storage buffer of limited size&lt;br /&gt;
and since the data has to be copied to and from OS/2's pipe buffers when&lt;br /&gt;
DosWrites and DosReads are done. What makes an anonymous pipe valuable is&lt;br /&gt;
that child processes inherit file handles. A parent process can create an&lt;br /&gt;
anonymous pipe and then create a child process, and the child process&lt;br /&gt;
inherits the anonymous pipe handles. The child process can then write to&lt;br /&gt;
the pipe's write handle, and the parent process can read the data via the&lt;br /&gt;
pipe's read handle. Once we add the DosDupHandle function, which allows&lt;br /&gt;
handles to be renumbered, and the standard file handles (STDIN, STDOUT, and&lt;br /&gt;
STDERR), we have the makings of a powerful capability.&lt;br /&gt;
     Let's go back to our NUMARITH and NUMADD programs. Suppose NUMARITH&lt;br /&gt;
wants to use NUMADD's services but that NUMARITH wants to process NUMADD's&lt;br /&gt;
results itself rather than having them appear in NUMARITH's output.&lt;br /&gt;
Furthermore, assume that NUMARITH doesn't want NUMADD to read its arguments&lt;br /&gt;
from NUMARITH's input file; NUMARITH wants to supply NUMADD's arguments&lt;br /&gt;
itself. NUMARITH can do this by following these steps:&lt;br /&gt;
&lt;br /&gt;
     1.  Create two anonymous pipes.&lt;br /&gt;
&lt;br /&gt;
     2.  Preserve the item pointed to by the current STDIN and STDOUT&lt;br /&gt;
         handles (the item can be a file, a device, or a pipe) by using&lt;br /&gt;
         DosDupHandle to provide a duplicate handle. The handle numbers of&lt;br /&gt;
         the duplicates may be any number as long as it is not the number&lt;br /&gt;
         of STDIN, STDOUT, or STDERR. We know that this is the case because&lt;br /&gt;
         DosDupHandle assigns a handle number that is not in use, and the&lt;br /&gt;
         standard handle numbers are always in use.&lt;br /&gt;
&lt;br /&gt;
     3.  Close STDIN and STDOUT via DosClose. Whatever object is &amp;quot;on the&lt;br /&gt;
         other end&amp;quot; of the handle is undisturbed because the application&lt;br /&gt;
         still has the object open on another handle.&lt;br /&gt;
&lt;br /&gt;
     4.  Use DosDupHandle to make the STDIN handle a duplicate of one of&lt;br /&gt;
         the pipe's input handles, and use DosDupHandle to make the STDOUT&lt;br /&gt;
         handle a duplicate of the other pipe's output handle.&lt;br /&gt;
&lt;br /&gt;
     5.  Create the child process via DosExecPgm.&lt;br /&gt;
&lt;br /&gt;
     6.  Close the STDIN and STDOUT handles that point to the pipes, and&lt;br /&gt;
         use DosDupHandle and DosClose to effectively rename the objects&lt;br /&gt;
         originally described by STDIN and STDOUT back to those handles.&lt;br /&gt;
&lt;br /&gt;
     The result of this operation is shown in Figure 4-4. NUMADD's STDIN&lt;br /&gt;
and STDOUT handles are pointing to two anonymous pipes, and the parent&lt;br /&gt;
process is holding the other end of those pipes. The parent process used&lt;br /&gt;
DosDupHandle and DosClose to effectively &amp;quot;rename&amp;quot; the STDIN and STDOUT&lt;br /&gt;
handles temporarily so that the child process can inherit the pipe handles&lt;br /&gt;
rather than its parent's STDIN and STDOUT. At this point the parent,&lt;br /&gt;
NUMARITH, can write input values into the pipe connected to NUMADD's STDIN&lt;br /&gt;
and read NUMADD's output from the pipe connected to NUMADD's STDOUT.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        STDIN    ÚÄÄÄÄÄÄÄÄÄÄ¿   STDOUT&lt;br /&gt;
 ÄÄÄÄÄÄÄÄÄ¿      ³          ³     ÚÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
          ÀÄÄÄÄÄ�³ NUMARITH ÃÄÄ�ÄÄÙ&lt;br /&gt;
          ÚÄÄÄÄÄÄ´          ³�ÄÄÄÄÄÄÄ¿&lt;br /&gt;
          ³      ÀÄÄÄÄÄÂÄÄÄÄÙ        ³&lt;br /&gt;
anonymous ³            ³ DosExecPgm  ³ anonymous&lt;br /&gt;
  pipe    ³      ÚÄÄÄÄÄ�ÄÄÄÄ¿        ³   pipe&lt;br /&gt;
          ³      ³          ³        ³&lt;br /&gt;
          ÀÄÄÄÄÄ�³  NUMADD  ÃÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                 ³          ³&lt;br /&gt;
                 ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-4.  Invoking NUMADD via an anonymous pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     If you compare Figure 4-4 with Listing 4-2, Figure 4-2, and Figure&lt;br /&gt;
4-3, you see another key feature of this architecture: NUMADD has no&lt;br /&gt;
special code or design to allow it to communicate directly with NUMARITH.&lt;br /&gt;
NUMADD functions correctly whether working from the keyboard and screen,&lt;br /&gt;
from data files, or as a tool for another program. The architecture is&lt;br /&gt;
fully recursive: If NUMADD invokes a child process to help it with its&lt;br /&gt;
work, everything still functions correctly. Whatever mechanism NUMADD uses&lt;br /&gt;
to interact with its child/tool program is invisible to NUMARITH. Likewise,&lt;br /&gt;
if another program uses NUMARITH as a tool, that program is not affected by&lt;br /&gt;
whatever mechanism NUMARITH uses to do its work.&lt;br /&gt;
     This example contains one more important point. Earlier I said that a&lt;br /&gt;
process uses DosRead and DosWrite to do I/O over a pipe, yet our NUMADD&lt;br /&gt;
program uses fscanf and fprintf, two C language library routines. fscanf&lt;br /&gt;
and fprintf themselves call DosRead and DosWrite, and because a pipe handle&lt;br /&gt;
is indistinguishable from a file handle or a device handle for these&lt;br /&gt;
operations, not only does NUMADD work unchanged with pipes, but the library&lt;br /&gt;
subroutines that it calls work as well. This is another example of the&lt;br /&gt;
principle of encapsulation as expressed in OS/2,&lt;br /&gt;
4. And in UNIX, from which this aspect of the architecture was&lt;br /&gt;
adapted.&lt;br /&gt;
4 in which the differences&lt;br /&gt;
among pipes, files, and devices are hidden behind, or encapsulated in, a&lt;br /&gt;
standardized handle interface.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.1.3  Details, Details&lt;br /&gt;
While presenting the &amp;quot;big picture&amp;quot; of the OS/2 tasking and tool&lt;br /&gt;
architecture, I omitted various important details. This section discusses&lt;br /&gt;
them, in no particular order.&lt;br /&gt;
     STDERR (handle value 2) is an output handle on which error messages&lt;br /&gt;
are written. STDERR is necessary because STDOUT is a program's normal&lt;br /&gt;
output stream. For example, suppose a user types:&lt;br /&gt;
&lt;br /&gt;
     DIR  filename &amp;gt;logfile&lt;br /&gt;
&lt;br /&gt;
where the file filename does not exist. If STDERR did not exist as a&lt;br /&gt;
separate entity, no error message would appear, and logfile would&lt;br /&gt;
apparently be created. Later, when the user attempted to examine the&lt;br /&gt;
contents of the file, he or she would see the following message:&lt;br /&gt;
&lt;br /&gt;
     FILE NOT FOUND&lt;br /&gt;
&lt;br /&gt;
For this reason, STDERR generally points to the display screen, and&lt;br /&gt;
applications rarely redirect it.&lt;br /&gt;
     The special meanings of STDIN, STDOUT, and STDERR are not hard coded&lt;br /&gt;
into the OS/2 kernel; they are system conventions. All programs should&lt;br /&gt;
follow these conventions at all times to preserve the flexibility and&lt;br /&gt;
utility of OS/2's tool-based architecture. Even programs that don't do&lt;br /&gt;
handle I/O on the STD handles must still follow the architectural&lt;br /&gt;
conventions (see Chapter 14, Interactive Programs). However, the OS/2&lt;br /&gt;
kernel code takes no special action nor does it contain any special cases&lt;br /&gt;
in support of this convention. Various OS/2 system utilities, such as&lt;br /&gt;
CMD.EXE and the presentation manager, do contain code in support of this&lt;br /&gt;
convention.&lt;br /&gt;
     I mentioned that a child process inherits all its parent's file&lt;br /&gt;
handles unless the parent has explicitly marked a handle &amp;quot;no inherit.&amp;quot; The&lt;br /&gt;
use of STDIN, STDOUT, and STDERR in an inherited environment has been&lt;br /&gt;
discussed, but what of the other handles?&lt;br /&gt;
     Although a child process inherits all of its parent's file handles, it&lt;br /&gt;
is usually interested only in STDIN, STDOUT, and STDERR. What happens to&lt;br /&gt;
the other handles? Generally, nothing. Only handle values 0 (STDIN), 1&lt;br /&gt;
(STDOUT), and 2 (STDERR) have explicit meaning. All other file handles are&lt;br /&gt;
merely magic cookies that OS/2 returns for use in subsequent I/O calls.&lt;br /&gt;
OS/2 doesn't guarantee any particular range or sequence of handle values,&lt;br /&gt;
and applications should never use or rely on explicit handle values other&lt;br /&gt;
than the STD ones.&lt;br /&gt;
     Thus, for example, if a parent process has a file open on handle N and&lt;br /&gt;
the child process inherits that handle, little happens. The child process&lt;br /&gt;
won't get the value N back as a result of a DosOpen because the handle is&lt;br /&gt;
already in use. The child will never issue operations to handle N because&lt;br /&gt;
it didn't open any such handle and knows nothing of its existence. Two side&lt;br /&gt;
effects can result from inheriting &amp;quot;garbage&amp;quot; handles. One is that the&lt;br /&gt;
object to which the handle points cannot be closed until both the parent&lt;br /&gt;
and the child close their handles. Because the child knows nothing of the&lt;br /&gt;
handle, it won't close it. Therefore, a handle close issued by a parent&lt;br /&gt;
won't be effective until the child and all of that child's descendant&lt;br /&gt;
processes (which in turn inherited the handle) have terminated. If another&lt;br /&gt;
application needs the file or device, it is unavailable because a child&lt;br /&gt;
process is unwittingly holding it open.&lt;br /&gt;
     The other side effect is that each garbage handle consumes an entry in&lt;br /&gt;
the child's handle space. Although you can easily increase the default&lt;br /&gt;
maximum of 20 open handles, a child process that intends to open only 10&lt;br /&gt;
files wouldn't request such an increase. If a parent process allows the&lt;br /&gt;
child to inherit 12 open files, the child will run out of available open&lt;br /&gt;
file handles. Writing programs that always raise their file handle limit is&lt;br /&gt;
not good practice because the garbage handles are extra overhead and the&lt;br /&gt;
files-held-open problem remains. Instead, parent programs should minimize&lt;br /&gt;
the number of garbage handles they allow child processes to inherit.&lt;br /&gt;
     Each time a program opens a file, it should do so with the DosOpen&lt;br /&gt;
request with the &amp;quot;don't inherit&amp;quot; bit set if the file is of no specific&lt;br /&gt;
interest to any child programs. If the bit is not set at open time, it can&lt;br /&gt;
be set later via DosSetFHandState. The bit is per-handle, not per-file; so&lt;br /&gt;
if a process has two handles open on the same file, it can allow one but&lt;br /&gt;
not the other to be inherited. Don't omit this step simply because you&lt;br /&gt;
don't plan to run any child processes; unbeknownst to you, dynlink library&lt;br /&gt;
routines may run child programs on your behalf. Likewise, in dynlink&lt;br /&gt;
programs the &amp;quot;no inherit&amp;quot; bit should be set when file opens are issued&lt;br /&gt;
because the client program may create child processes.&lt;br /&gt;
     Finally, do not follow the standard UNIX practice of blindly closing&lt;br /&gt;
file handles 3 through 20 during program initialization. Dynlink subsystems&lt;br /&gt;
are called to initialize themselves for a new client before control is&lt;br /&gt;
passed to the application itself. If subsystems have opened files during&lt;br /&gt;
that initialization, a blanket close operation will close those files and&lt;br /&gt;
cause the dynlink package to fail. All programs use dynlink subsystems,&lt;br /&gt;
whether they realize it or not, because both the OS/2 interface package and&lt;br /&gt;
the presentation manager are such subsystems. Accidentally closing a&lt;br /&gt;
subsystem's file handles can cause bizarre and inexplicable problems. For&lt;br /&gt;
example, when the VIO subsystem is initialized, it opens a handle to the&lt;br /&gt;
screen device. A program that doesn't call VIO may believe that closing&lt;br /&gt;
this handle is safe, but it's not. If a handle write is done to STDOUT when&lt;br /&gt;
STDOUT points to the screen device, OS/2 calls VIO on behalf of the&lt;br /&gt;
application--with potentially disastrous effect.&lt;br /&gt;
     I discussed how a child process, inheriting its parent's STDIN and&lt;br /&gt;
STDOUT, extracts its input from the parent's input stream and intermingles&lt;br /&gt;
its output in the parent's output stream. What keeps the parent process&lt;br /&gt;
from rereading the input consumed by the child, and what keeps the parent&lt;br /&gt;
from overwriting the output data written by the child? The answer is in the&lt;br /&gt;
distinction between duplicated or inherited handles to a file and two&lt;br /&gt;
handles to the same file that are the result of two separate opens.&lt;br /&gt;
     Each time a file is opened, OS/2 allocates a handle to that process&lt;br /&gt;
and makes an entry in the process's handle table. This entry then points to&lt;br /&gt;
the System File Table (SFT) inside OS/2. The SFT contains the seek pointer&lt;br /&gt;
to the file--the spot in the file that is currently being read from or&lt;br /&gt;
written to. When a handle is inherited or duplicated, the new handle points&lt;br /&gt;
to the same SFT entry as the original. Thus, for example, the child's STDIN&lt;br /&gt;
handle shares the same seek pointer as the parent's STDIN handle. When our&lt;br /&gt;
example child program NUMADD read two lines from STDIN, it advanced the&lt;br /&gt;
seek pointer of its own STDIN and that of its parent's STDIN (and perhaps&lt;br /&gt;
that of its grandparent's STDIN and so forth). Likewise, when the child&lt;br /&gt;
writes to STDOUT, the seek pointer advances on STDOUT so that subsequent&lt;br /&gt;
writing by the parent appends to the child's output rather than overwriting&lt;br /&gt;
it.&lt;br /&gt;
     This mechanism has two important ramifications. First, in a situation&lt;br /&gt;
such as our NUMARITH and NUMADD example, the parent process must refrain&lt;br /&gt;
from I/O to the STD handles until the child process has completed so that&lt;br /&gt;
the input or output data doesn't intermingle. Second, the processes must be&lt;br /&gt;
careful in the way they buffer data to and from the STD handles.&lt;br /&gt;
     Most programs that read data from STDIN do so until they encounter an&lt;br /&gt;
EOF (End Of File). These programs can buffer STDIN as they wish. A program&lt;br /&gt;
such as NUMARITH, in which child processes read some but not all of its&lt;br /&gt;
STDIN data, cannot use buffering because the read-ahead data in the buffer&lt;br /&gt;
might be the data that the child process was to read. NUMARITH can't &amp;quot;put&lt;br /&gt;
the data back&amp;quot; by backing up the STDIN seek pointer because STDIN might be&lt;br /&gt;
pointing to a device (such as the keyboard) or to a pipe that cannot be&lt;br /&gt;
seeked. Likewise, because NUMADD was designed to read only two lines&lt;br /&gt;
of input, it also must read STDIN a character at a time to be sure it&lt;br /&gt;
doesn't &amp;quot;overshoot&amp;quot; its two lines.&lt;br /&gt;
     Programs must also be careful about buffering STDOUT. In general, they&lt;br /&gt;
can buffer STDOUT as they wish, but they must be sure to flush out any&lt;br /&gt;
buffered data before they execute any child processes that might write to&lt;br /&gt;
STDOUT.&lt;br /&gt;
     Finally, what if a parent process doesn't want a child process to&lt;br /&gt;
inherit STDIN, STDOUT, or STDERR? The parent process should not mark those&lt;br /&gt;
handles &amp;quot;no inherit&amp;quot; because then those handles will not be open when the&lt;br /&gt;
child process starts. The OS/2 kernel has no built-in recognition of the&lt;br /&gt;
STD file handles; so if the child process does a DosOpen and handle 1 is&lt;br /&gt;
unopened (because the process's parent set &amp;quot;no inherit&amp;quot; on handle 1), OS/2&lt;br /&gt;
might open the new file on handle 1. As a result, output that the child&lt;br /&gt;
process intends for STDOUT appears in the other file that unluckily was&lt;br /&gt;
assigned handle number 1.&lt;br /&gt;
     If for some reason a child process should not inherit a STD handle,&lt;br /&gt;
the parent should use the DosDupHandle/rename technique to cause that&lt;br /&gt;
handle to point to the NULL device. You do this by opening a handle on the&lt;br /&gt;
NULL device and then moving that handle to 0, 1, or 2 with DosDupHandle.&lt;br /&gt;
This technique guarantees that the child's STD handles will all be&lt;br /&gt;
open.&lt;br /&gt;
     The subject of STDIN, STDOUT, and handle inheritance comes up again in&lt;br /&gt;
Chapter 14, Interactive Programs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.2  PIDs and Command Subtrees&lt;br /&gt;
&lt;br /&gt;
The PID (process identification) is a unique code that OS/2 assigns to each&lt;br /&gt;
process when it is created. The number is a magic cookie. Its value has no&lt;br /&gt;
significance to any process; it's simply a name for a process. The PID may&lt;br /&gt;
be any value except 0. A single PID value is never assigned to two&lt;br /&gt;
processes at the same time. PID values are reused but not &amp;quot;rapidly.&amp;quot; You&lt;br /&gt;
can safely remember a child's PID and then later attempt to affect that&lt;br /&gt;
child by using the PID in an OS/2 call. Even if the child process has died&lt;br /&gt;
unexpectedly, approximately 65,000 processes would have to be created&lt;br /&gt;
before the PID value might be reassigned; even a very active system takes&lt;br /&gt;
at least a day to create, execute, and terminate that many processes.&lt;br /&gt;
     I've discussed at some length the utility of an architecture in which&lt;br /&gt;
child programs can create children and those children can create&lt;br /&gt;
grandchildren and so on. I've also emphasized that the parent need not know&lt;br /&gt;
the architecture of a child process--whether the child process creates&lt;br /&gt;
children and grandchildren of its own. The parent need not know and indeed&lt;br /&gt;
should not know because such information may make the parent dependent on a&lt;br /&gt;
particular implementation of a child or grandchild program, an&lt;br /&gt;
implementation that might change. Given that a parent process starting up a&lt;br /&gt;
child process can't tell if that child creates its own descendants, how can&lt;br /&gt;
the parent process ask the system if the work that the child was to do has&lt;br /&gt;
been completed? The system could tell the parent whether the child process&lt;br /&gt;
is still alive, but this is insufficient. The child process may have farmed&lt;br /&gt;
out all its work to one or more grandchildren and then terminated itself&lt;br /&gt;
before the actual work was started. Furthermore, the parent process may&lt;br /&gt;
want to change the priority of the process(es) that it has created or even&lt;br /&gt;
terminate them because an error was detected or because the user typed&lt;br /&gt;
Ctrl-C.&lt;br /&gt;
     All these needs are met with a concept called the command subtree.&lt;br /&gt;
When a child process is created, its parent is told the child's PID. The&lt;br /&gt;
PID is the name of the single child process, and when taken as a command&lt;br /&gt;
subtree ID, this PID is the name of the entire tree of descendant processes&lt;br /&gt;
of which the child is the root. In other words, when used as a command&lt;br /&gt;
subtree ID, the PID refers not only to the child process but also to any of&lt;br /&gt;
its children and to any children that they may have and so on. A single&lt;br /&gt;
command subtree can conceivably contain dozens of processes (see Figure&lt;br /&gt;
4-5 and Figure 4-6).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                ³  PARENT   ³&lt;br /&gt;
                ÀÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
                ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                ³     A     ³&lt;br /&gt;
                ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
      ÚÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿                   ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
³     B     ³                   ³     C     ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ                   ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     D     ³     ÚÄÄÄÄÄÁÄÄÄÄÄ¿               ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ³     F     ³               ³     G     ³&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÀÄÄÄÄÄÄÄÄÄÄÄÙ               ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
³     E     ³                              ÚÄÄÄÄÄÙ     ÀÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÙ                        ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     H     ³     ³     I     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     J     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-5.  Command subtree. A is the root of (one of) the parent's&lt;br /&gt;
command subtrees. B and C are the root of two of A's subtrees and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                ³  PARENT   ³&lt;br /&gt;
                ÀÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
                ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                ³°°°°°A°°°°°³&lt;br /&gt;
                ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
      ÚÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿                   ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
³     B     ³                   ³     C     ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ                   ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     D     ³     ÚÄÄÄÄÄÁÄÄÄÄÄ¿               ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ³     F     ³               ³     G     ³&lt;br /&gt;
ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÀÄÄÄÄÄÄÄÄÄÄÄÙ               ÀÄÄÂÄÄÄÄÄÂÄÄÙ&lt;br /&gt;
³°°°°°E°°°°°³                              ÚÄÄÄÄÄÙ     ÀÄÄÄÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÙ                        ÚÄÄÄÄÄÁÄÄÄÄÄ¿     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³°°°°°H°°°°°³     ³     I     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÂÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                     ÚÄÄÄÄÄÁÄÄÄÄÄ¿&lt;br /&gt;
                                     ³     J     ³&lt;br /&gt;
                                     ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 4-6.   Command subtree. The shaded processes have died, but the&lt;br /&gt;
subtrees remain. PARENT can still use subtree A to affect all remaining&lt;br /&gt;
subprocesses. Likewise, an operation by C on subtree G will affect&lt;br /&gt;
process J.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Some OS/2 functions, such as DosCWait and DosKillProcess, can take&lt;br /&gt;
either PID or command subtree values, depending on the subfunction&lt;br /&gt;
requested. When the PID form is used, only the named process is affected.&lt;br /&gt;
When the command subtree form is used, the named process and all its&lt;br /&gt;
descendants are affected. This is true even if the child process no longer&lt;br /&gt;
exists or if the family tree of processes contains holes as a result of&lt;br /&gt;
process terminations.&lt;br /&gt;
     No statute of limitations applies to the use of the command subtree&lt;br /&gt;
form. That is, even if child process X died a long time ago, OS/2 still&lt;br /&gt;
allows references to the command subtree X. Consequently, OS/2 places one&lt;br /&gt;
simple restriction on the use of command subtrees so that it isn't forced&lt;br /&gt;
to keep around a complete process history forever: Only the direct parent&lt;br /&gt;
of process X can reference the command subtree X. In other words, X's&lt;br /&gt;
grandparent process can't learn X's PID from its parent and then issue&lt;br /&gt;
command subtree forms of commands; only X's direct parent can. This&lt;br /&gt;
puts an upper limit on the amount and duration of command subtree&lt;br /&gt;
information that OS/2 must retain; when a process terminates, information&lt;br /&gt;
pertaining to its command subtrees can be discarded. The command subtree&lt;br /&gt;
concept is recursive. OS/2 discards information about the terminated&lt;br /&gt;
process's own command subtrees, but if any of its descendant processes&lt;br /&gt;
still exist, the command subtree information pertaining to their child&lt;br /&gt;
processes is retained. And those surviving descendants are still part of&lt;br /&gt;
the command subtree belonging to the terminated process's parent&lt;br /&gt;
process.&lt;br /&gt;
5. Assuming that the parent process itself still exists. In any&lt;br /&gt;
case, all processes are part of a nested set of command subtrees&lt;br /&gt;
belonging to all its surviving ancestor processes.&lt;br /&gt;
5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.3  DosExecPgm&lt;br /&gt;
&lt;br /&gt;
To execute a child process, you use the DosExecPgm call. The form of the&lt;br /&gt;
call is shown in Listing 4-3.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
extern unsigned far pascal DOSEXECPGM (&lt;br /&gt;
          char far                 *OBJNAMEBUF,&lt;br /&gt;
          unsigned                 OBJNAMEBUFL,&lt;br /&gt;
          unsigned                 EXECTYPE,&lt;br /&gt;
          char far                 *ARGSTRING,&lt;br /&gt;
          char far                 *ENVSTRING,&lt;br /&gt;
          struct ResultCodes far   *CODEPID,&lt;br /&gt;
          char far                 *PGMNAME);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-3.  DosExecPgm call.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The obj namebuf arguments provide an area where OS/2 can return a&lt;br /&gt;
character string if the DosExecPgm function fails. In MS-DOS version 2.0&lt;br /&gt;
and later, the EXEC function was quite simple: It loaded a file into&lt;br /&gt;
memory. Little could go wrong: file not found, file bad format,&lt;br /&gt;
insufficient memory, to name some possibilities. A simple error code&lt;br /&gt;
sufficed to diagnose problems. OS/2's dynamic linking facility is much more&lt;br /&gt;
complicated. The earliest prototype versions of OS/2 were missing these&lt;br /&gt;
obj namebuf arguments, and engineers were quickly frustrated by the error&lt;br /&gt;
code &amp;quot;dynlink library load failed.&amp;quot; &amp;quot;Which library was it? The application&lt;br /&gt;
references seven of them! But all seven of those libraries are alive and&lt;br /&gt;
well. Perhaps it was a library that one of those libraries referenced. But&lt;br /&gt;
which libraries do they reference? Gee, I dunno...&amp;quot; For this reason, the&lt;br /&gt;
object name arguments were added. The buffer is a place where OS/2 returns&lt;br /&gt;
the name of the missing or defective library or other load object, and the&lt;br /&gt;
length argument tells OS/2 the maximum size of the buffer area. Strings&lt;br /&gt;
that will not fit in the area are truncated.&lt;br /&gt;
     The exectype word describes the form of the DosExecPgm. The values are&lt;br /&gt;
as follows.&lt;br /&gt;
&lt;br /&gt;
     0:  Execute the child program synchronously. The thread issuing the&lt;br /&gt;
         DosExecPgm will not execute further until the child process has&lt;br /&gt;
         finished executing. The thread returns from DosExecPgm when the&lt;br /&gt;
         child process itself terminates, not when the command subtree has&lt;br /&gt;
         terminated. This form of DosExecPgm is provided for ease in&lt;br /&gt;
         converting MS-DOS version 3.x programs to OS/2. It is considered&lt;br /&gt;
         obsolete and should generally be avoided. Its use may interfere&lt;br /&gt;
         with proper Ctrl-C and Ctrl-Break handling (see Chapter 14,&lt;br /&gt;
         Interactive Programs).&lt;br /&gt;
&lt;br /&gt;
     1:  Execute the program asynchronously. The child process begins&lt;br /&gt;
         executing as soon as the scheduler allows; the calling thread&lt;br /&gt;
         returns from the DosExecPgm call immediately. You cannot assume&lt;br /&gt;
         that the child process has received CPU time before the parent&lt;br /&gt;
         thread returns from the DosExecPgm call; neither can you assume&lt;br /&gt;
         that the child process has not received such CPU service. This&lt;br /&gt;
         form instructs OS/2 not to bother remembering the child's&lt;br /&gt;
         termination code for a future DosCWait call. It is used when the&lt;br /&gt;
         parent process doesn't care what the result code of the child may&lt;br /&gt;
         be or when or if it completes, and it frees the parent from the &lt;br /&gt;
         necessity of issuing DosCWait calls. Programs executing other&lt;br /&gt;
         programs as tools would rarely use this form. This form might be&lt;br /&gt;
         used by a system utility, for example, whose job is to fire off&lt;br /&gt;
         certain programs once an hour but not to take any action or notice&lt;br /&gt;
         should any of those programs fail. Note that, unlike the detach&lt;br /&gt;
         form described below, the created process is still recorded as a&lt;br /&gt;
         child of the executing parent. The parent can issue a DosCWait&lt;br /&gt;
         call to determine whether the child subtree is still executing,&lt;br /&gt;
         although naturally there is no return code when the child process&lt;br /&gt;
         does terminate.&lt;br /&gt;
&lt;br /&gt;
     2:  This form is similar to form 1 in that it executes the child&lt;br /&gt;
         process asynchronously, but it instructs OS/2 to retain the child&lt;br /&gt;
         process's exit code for future examination by DosCWait. Thus, a&lt;br /&gt;
         program can determine the success or failure of a child process.&lt;br /&gt;
         The parent process should issue an appropriate DosCWait &amp;quot;pretty&lt;br /&gt;
         soon&amp;quot; because OS/2 version 1.0 maintains about 2600 bytes of data&lt;br /&gt;
         structures for a dead process whose parent is expected to DosCWait&lt;br /&gt;
         but hasn't yet done so. To have one of these structures lying&lt;br /&gt;
         around for a few minutes is no great problem, but programs need to&lt;br /&gt;
         issue DosCWaits in a timely fashion to keep from clogging the&lt;br /&gt;
         system with the carcasses of processes that finished hours&lt;br /&gt;
         ago.&lt;br /&gt;
            OS/2 takes care of all the possible timing considerations, so&lt;br /&gt;
         it's OK to issue the DosCWait before or after the child process&lt;br /&gt;
         has completed. Although a parent process can influence the&lt;br /&gt;
         relative assignment of CPU time between the child and parent&lt;br /&gt;
         processes by setting its own and its child's relative priorities,&lt;br /&gt;
         there is no reliable way of determining which process will run&lt;br /&gt;
         when. Write your program in such a way that it doesn't matter if&lt;br /&gt;
         the child completes before or after the DosCWait, or use some form&lt;br /&gt;
         of IPC to synchronize the execution of parent and child processes.&lt;br /&gt;
         See 4.4 DosCWait for more details.&lt;br /&gt;
&lt;br /&gt;
     3:  This form is used by the system debugger, CodeView. The system&lt;br /&gt;
         architecture does not allow one process to latch onto another&lt;br /&gt;
         arbitrary process and start examining and perhaps modifying the&lt;br /&gt;
         target process's execution. Such a facility would result in a&lt;br /&gt;
         massive side effect and is contrary to the tenet of encapsulation&lt;br /&gt;
         in the design religion. Furthermore, such a facility would prevent&lt;br /&gt;
         OS/2 from ever providing an environment secure against malicious&lt;br /&gt;
         programs. OS/2 solves this problem with the DosPtrace function,&lt;br /&gt;
         which peeks, pokes, and controls a child process. This function is&lt;br /&gt;
         allowed to affect only processes that were executed with this&lt;br /&gt;
         special mode value of 3.&lt;br /&gt;
&lt;br /&gt;
     4:  This form executes the child process asynchronously but also&lt;br /&gt;
         detaches it from the process issuing the DosExecPgm call. A&lt;br /&gt;
         detached process does not inherit the parent process's screen&lt;br /&gt;
         group; instead, it executes in a special invalid screen group. Any&lt;br /&gt;
         attempt to do screen, keyboard, or mouse I/O from within this&lt;br /&gt;
         screen group returns an error code. The system does not consider a&lt;br /&gt;
         detached process a child of the parent process; the new process&lt;br /&gt;
         has no connection with the creating process. In other words, it's&lt;br /&gt;
         parentless. This form of DosExecPgm is used to create daemon&lt;br /&gt;
         programs that execute without direct interaction with the user.&lt;br /&gt;
&lt;br /&gt;
     The EnvString argument points to a list of ASCII text strings that&lt;br /&gt;
contain environment values. OS/2 supports a separate environment block for&lt;br /&gt;
each process. A process typically inherits its parent's environment&lt;br /&gt;
strings. In this case, the EnvPointer argument should be NULL, which tells&lt;br /&gt;
OS/2 to supply the child process with a copy of the parent's environment&lt;br /&gt;
strings (see Listing 4-4).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
PATH=C:\DOS;C:\EDITORS;C:\TOOLS;C:\XTOOLS;C:\BIN;C:\UBNET&lt;br /&gt;
INCLUDE=\include&lt;br /&gt;
TERM=h19&lt;br /&gt;
INIT=c:\tmp&lt;br /&gt;
HOME=c:\tmp&lt;br /&gt;
USER=c:\tmp&lt;br /&gt;
TEMP=c:\tmp&lt;br /&gt;
TERM=ibmpc&lt;br /&gt;
LIB=c:\lib&lt;br /&gt;
PROMPT=($p)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-4.  A typical environment string set.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The environment strings are normally used to customize a particular&lt;br /&gt;
execution environment. For example, suppose a user creates two screen&lt;br /&gt;
groups, each running a copy of CMD.EXE. Each CMD.EXE is a direct child of&lt;br /&gt;
the presentation manager, which manages and creates screen groups, and each&lt;br /&gt;
is also the ancestor of all processes that will run in its particular&lt;br /&gt;
screen group. If the user is running utility programs that use the&lt;br /&gt;
environment string &amp;quot;TEMP=&amp;lt;dirname&amp;gt;&amp;quot; to specify a directory to hold&lt;br /&gt;
temporary files, he or she may want to specify different TEMP= values for&lt;br /&gt;
each copy of CMD.EXE. As a result, the utilities that use TEMP= will access&lt;br /&gt;
the proper directory, depending on which screen group they are run in,&lt;br /&gt;
because they will have inherited the proper TEMP= environment string from&lt;br /&gt;
their CMD.EXE ancestor. See Chapter 10, Environment Strings, for a complete&lt;br /&gt;
discussion.&lt;br /&gt;
     Because of the inheritable nature of environment strings, parent&lt;br /&gt;
processes that edit the environment list should remove or edit only those&lt;br /&gt;
strings with which they are involved; any unrecognized strings should be&lt;br /&gt;
preserved.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.4  DosCWait&lt;br /&gt;
&lt;br /&gt;
When a process executes a child process, it usually wants to know when that&lt;br /&gt;
child process has completed and whether the process succeeded or failed.&lt;br /&gt;
DosCWait, the OS/2 companion function to DosExecPgm, returns such&lt;br /&gt;
information. Before we discuss DosCWait in detail, two observations are in&lt;br /&gt;
order. First, although each DosExecPgm call starts only a single process,&lt;br /&gt;
it's possible--and not uncommon--for that child process to create its own&lt;br /&gt;
children and perhaps they their own and so on. A program should not assume&lt;br /&gt;
that its child process won't create subchildren; instead, programs should&lt;br /&gt;
use the command-subtree forms of DosCWait. One return code from the direct&lt;br /&gt;
child process (that is, the root of the command subtree) is sufficient&lt;br /&gt;
because if that direct child process invokes other processes to do work for&lt;br /&gt;
it the direct child is responsible for monitoring their success via&lt;br /&gt;
DosCWait. In other words, if a child process farms out some of its work to&lt;br /&gt;
a grandchild process and that grandchild process terminates in error, then&lt;br /&gt;
the child process should also terminate with an error return.&lt;br /&gt;
     Second, although we discuss the process's child, in fact processes can&lt;br /&gt;
have multiple child processes and therefore multiple command subtrees at&lt;br /&gt;
any time. The parent process may have interconnected the child processes&lt;br /&gt;
via anonymous pipes, or they may be independent of one another. Issuing&lt;br /&gt;
separate DosCWaits for each process or subtree is unnecessary. The form of&lt;br /&gt;
the DosCWait call is shown in Listing 4-5.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
extern unsigned far pascal DOSCWAIT (&lt;br /&gt;
     unsigned                 ACTIONCODE,&lt;br /&gt;
     unsigned                 WAITOPTION,&lt;br /&gt;
     struct ResultCodes far   *RESULTWORD,&lt;br /&gt;
     unsigned far             *PIDRETURN,&lt;br /&gt;
     unsigned                 PID);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Listing 4-5.  DosCWait function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Three of the arguments affect the scope of the command: ActionCode,&lt;br /&gt;
WaitOption, and PID. It's easiest to show how these interact by arranging&lt;br /&gt;
their possible values into tables.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Command Subtrees&lt;br /&gt;
&lt;br /&gt;
     These forms of DosCWait operate on the entire command subtree, which&lt;br /&gt;
     may, of course, consist of only one child process. We recommend these&lt;br /&gt;
     forms because they will continue to work correctly if the child&lt;br /&gt;
     process is changed to use more or fewer child processes of its own.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId   Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     1            0            n           Wait until the command subtree&lt;br /&gt;
                                           has completed and then return&lt;br /&gt;
                                           the direct child's termination&lt;br /&gt;
                                           code.&lt;br /&gt;
&lt;br /&gt;
     1            1            n           If the command subtree has&lt;br /&gt;
                                           completed, return the direct&lt;br /&gt;
                                           child's termination code.&lt;br /&gt;
                                           Otherwise, return the&lt;br /&gt;
                                           ERROR_CHILD_NOT_COMPLETE&lt;br /&gt;
                                           error code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Individual Processes&lt;br /&gt;
&lt;br /&gt;
     These forms of DosCWait are used to monitor individual child&lt;br /&gt;
     processes. The processes must be direct children; grandchild or&lt;br /&gt;
     unrelated processes cannot be DosCWaited. Use these forms only when&lt;br /&gt;
     the child process is part of the same application or software package&lt;br /&gt;
     as the parent process; the programmer needs to be certain that she or&lt;br /&gt;
     he can safely ignore the possibility that grandchild processes might&lt;br /&gt;
     still be running after the direct child has terminated.&lt;br /&gt;
1. It is in itself not an error to collect a child process's&lt;br /&gt;
termination code via DosCWait while that child still&lt;br /&gt;
has living descendant processes. However, such a case generally&lt;br /&gt;
means that the child's work, whatever that was, is not yet complete.&lt;br /&gt;
1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId   Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     0            0            0           DosCWait returns as soon as a&lt;br /&gt;
                                           direct child process terminates.&lt;br /&gt;
                                           If a child process had already&lt;br /&gt;
                                           terminated at the time of this&lt;br /&gt;
                                           call, it will return&lt;br /&gt;
                                           immediately.&lt;br /&gt;
&lt;br /&gt;
     0            0            N           DosCWait returns as soon as the&lt;br /&gt;
                                           direct child process N&lt;br /&gt;
                                           terminates. If it had already&lt;br /&gt;
                                           terminated at the time of the&lt;br /&gt;
                                           call, DosCWait returns&lt;br /&gt;
                                           immediately.&lt;br /&gt;
&lt;br /&gt;
     0            1            0           DosCWait checks for a terminated&lt;br /&gt;
                                           direct child process. If one is&lt;br /&gt;
                                           found, its status is returned.&lt;br /&gt;
                                           If none is found, an error code&lt;br /&gt;
                                           is returned.&lt;br /&gt;
&lt;br /&gt;
     0            1            N           DosCWait checks the status of&lt;br /&gt;
                                           the direct child process N. If&lt;br /&gt;
                                           it is terminated, its status is&lt;br /&gt;
                                           returned. If it is still&lt;br /&gt;
                                           running, an error code is&lt;br /&gt;
                                           returned.&lt;br /&gt;
&lt;br /&gt;
     DosCWait forms: Not Recommended&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ActionCode   WaitOption   ProcessId    Action&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
     1            0            0            DosCWait waits until a direct&lt;br /&gt;
                                            child has terminated and then&lt;br /&gt;
                                            waits until all of that child's&lt;br /&gt;
                                            descendants have terminated. It&lt;br /&gt;
                                            then returns the direct child's&lt;br /&gt;
                                            exit code. This form does not&lt;br /&gt;
                                            wait until the first command&lt;br /&gt;
                                            subtree has terminated; it&lt;br /&gt;
                                            selects a command subtree based&lt;br /&gt;
                                            on the first direct child that&lt;br /&gt;
                                            terminates, and then it waits&lt;br /&gt;
                                            as long as necessary for the&lt;br /&gt;
                                            remainder of that command&lt;br /&gt;
                                            subtree, even if other command&lt;br /&gt;
                                            subtrees meanwhile complete.&lt;br /&gt;
&lt;br /&gt;
     1            1            0            This form returns&lt;br /&gt;
                                            ERROR_CHILD_NOT_COMPLETE if any&lt;br /&gt;
                                            process in any of the caller's&lt;br /&gt;
                                            subtrees is still executing. If&lt;br /&gt;
                                            all subtrees have terminated,&lt;br /&gt;
                                            this form returns with a direct&lt;br /&gt;
                                            child's exit code. If no direct&lt;br /&gt;
                                            child processes have unwaited&lt;br /&gt;
                                            exit codes, the code&lt;br /&gt;
                                            ERROR_WAIT_NO_CHILDREN is&lt;br /&gt;
                                            returned.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.5  Control of Child Tasks and Command Subtrees&lt;br /&gt;
&lt;br /&gt;
A parent process has only limited control over its child processes because&lt;br /&gt;
the system is designed to minimize the side effects, or cross talk, between&lt;br /&gt;
processes. Specifically, a parent process can affect its command subtrees&lt;br /&gt;
in two ways: It can change their CPU priority, and it can terminate (kill)&lt;br /&gt;
them. Once again, the command subtree is the recommended form for both&lt;br /&gt;
commands because that form is insensitive to the operational details of the&lt;br /&gt;
child process.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.5.1  DosKillProcess&lt;br /&gt;
A parent process may initiate a child process or command subtree and then&lt;br /&gt;
decide to terminate that activity before the process completes normally.&lt;br /&gt;
Often this comes about because of a direct user command or because the user&lt;br /&gt;
typed Ctrl-Break. See Chapter 14, Interactive Programs, for special&lt;br /&gt;
techniques concerning Ctrl-Break and Ctrl-C.&lt;br /&gt;
     DosKillProcess flags each process in the command subtree (or the&lt;br /&gt;
direct child process if that form is used) for termination. A process&lt;br /&gt;
flagged for termination normally terminates as soon as all its threads&lt;br /&gt;
leave the system (that is, as soon as all its threads return from all&lt;br /&gt;
system calls). The system aborts calls that might block for more than a&lt;br /&gt;
second or two, such as those that read a keyboard character, so that the&lt;br /&gt;
process can terminate quickly. A process can intercept SIGKILL to delay&lt;br /&gt;
termination longer, even indefinitely. Delaying termination inordinately&lt;br /&gt;
via SetSignalHandler/SIGKILL is very bad practice and is considered a bug&lt;br /&gt;
rather than a feature.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4.5.2  DosSetPrty&lt;br /&gt;
A child process inherits its parent's process priority when the DosExecPgm&lt;br /&gt;
call is issued. After the DosExecPgm call, the parent can still change the&lt;br /&gt;
process priority of the command subtree or of only the direct child&lt;br /&gt;
process. The command subtree form is recommended; if the child process's&lt;br /&gt;
work deserves priority N, then any child processes that it executes to help&lt;br /&gt;
in that work should also run at priority N.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5  Threads and Scheduler/Priorities&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
5.1  Threads&lt;br /&gt;
&lt;br /&gt;
Computers consist of a CPU (central processing unit) and RAM (random access&lt;br /&gt;
memory). A computer program consists of a sequence of instructions that are&lt;br /&gt;
placed, for the most part, one after the other in RAM. The CPU reads each&lt;br /&gt;
instruction in sequence and executes it. The passage of the CPU through the&lt;br /&gt;
instruction sequence is called a thread of execution. All versions of MS-&lt;br /&gt;
DOS executed programs, so they necessarily supported a thread of execution.&lt;br /&gt;
OS/2 is unique, however, in that it supports multiple threads of execution&lt;br /&gt;
within a single process. In other words, a program can execute in two or&lt;br /&gt;
more spots in its code at the same time.&lt;br /&gt;
     Obviously, a multitasking system needs to support multiple threads in&lt;br /&gt;
a systemwide sense. Each process necessarily must have a thread; so if&lt;br /&gt;
there are ten processes in the system, there must be ten threads. Such an&lt;br /&gt;
existence of multiple threads in the system is invisible to the programmer&lt;br /&gt;
because each program executes with only one thread. OS/2 is different from&lt;br /&gt;
this because it allows an individual program to execute with multiple&lt;br /&gt;
threads if it desires.&lt;br /&gt;
     Because threads are elements of processes and because the process is&lt;br /&gt;
the unit of resource ownership, all threads that belong to the same process&lt;br /&gt;
share that process's resources. Thus, if one thread opens a file on file&lt;br /&gt;
handle X, all threads in that process can issue DosReads or DosWrites to&lt;br /&gt;
that handle. If one thread allocates a memory segment, all threads in that&lt;br /&gt;
process can access that memory segment. Threads are analogous to the&lt;br /&gt;
employees of a company. A company may consist of a single employee, or it&lt;br /&gt;
may consist of two or more employees that divide the work among them. Each&lt;br /&gt;
employee has access to the company's resources--its office space and&lt;br /&gt;
equipment. The employees themselves, however, must coordinate their work so&lt;br /&gt;
that they cooperate and don't conflict. As far as the outside world is&lt;br /&gt;
concerned, each employee speaks for the company. Employees can terminate&lt;br /&gt;
and/or more can be hired without affecting how the company is seen from&lt;br /&gt;
outside. The only requirement is that the company have at least one&lt;br /&gt;
employee. When the last employee (thread) dies, the company (process) also&lt;br /&gt;
dies.&lt;br /&gt;
     Although the process is the unit of resource ownership, each thread&lt;br /&gt;
does &amp;quot;own&amp;quot; a small amount of private information. Specifically, each thread&lt;br /&gt;
has its own copy of the CPU's register contents. This is an obvious&lt;br /&gt;
requirement if each thread is to be able to execute different instruction&lt;br /&gt;
sequences. Furthermore, each thread has its own copy of the floating point&lt;br /&gt;
registers. OS/2 creates the process's first thread when the program begins&lt;br /&gt;
execution. Any additional threads are created by means of the&lt;br /&gt;
DosCreateThread call. Any thread can create another thread. All threads in&lt;br /&gt;
a process are considered siblings; there are no parent-child relationships.&lt;br /&gt;
The initial thread, thread 1, has some special characteristics and is&lt;br /&gt;
discussed below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.1  Thread Stacks&lt;br /&gt;
Each thread has its own stack area, pointed to by that thread's SS and SP&lt;br /&gt;
values. Thread 1 is the process's primal thread. OS/2 allocates it stack&lt;br /&gt;
area in response to specifications in the .EXE file. If additional threads&lt;br /&gt;
are created via the DosCreateThread call, the caller specifies a stack area&lt;br /&gt;
for the new thread. Because the memory in which each thread's stack resides&lt;br /&gt;
is owned by the process, any thread can modify this memory; the programmer&lt;br /&gt;
must make sure that this does not happen. The size of the segment in which&lt;br /&gt;
the stack resides is explicitly specified; the size of a thread's stack is&lt;br /&gt;
not. The programmer can place a thread's stack in its own segment or in a&lt;br /&gt;
segment with other data values, including other thread stacks. In any case,&lt;br /&gt;
the programmer must ensure sufficient room for each thread's needs. Each&lt;br /&gt;
thread's stack must have at least 2 KB free in addition to the thread's&lt;br /&gt;
other needs at all times. This extra space is set aside for the needs of&lt;br /&gt;
dynamic link routines, some of which consume considerable stack space. All&lt;br /&gt;
threads must maintain this stack space reserve even if they are not used to&lt;br /&gt;
call dynamic link routines. Because of a bug in many 80286 processors,&lt;br /&gt;
stack segments must be preallocated to their full size. You cannot overrun&lt;br /&gt;
a stack segment and then assume that OS/2 will grow the segment;&lt;br /&gt;
overrunning a stack segment will cause a stack fault, and the process will&lt;br /&gt;
be terminated.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2  Thread Uses&lt;br /&gt;
Threads have a great number of uses. This section describes four of them.&lt;br /&gt;
These examples are intended to be inspirations to the programmer; there are&lt;br /&gt;
many other uses for threads.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.1  Foreground and Background Work&lt;br /&gt;
Threads provide a form of multitasking within a single program; therefore,&lt;br /&gt;
one of their most obvious uses is to provide simultaneous foreground and&lt;br /&gt;
background&lt;br /&gt;
1. Here I mean foreground and background in the sense of directly&lt;br /&gt;
interacting with the user, not as in foreground and background&lt;br /&gt;
screen groups.&lt;br /&gt;
1 processing for a program. For example, a spreadsheet program&lt;br /&gt;
might use one thread to display menus and to read user input. A second&lt;br /&gt;
thread could execute user commands, update the spreadsheet, and so on.&lt;br /&gt;
     This arrangement generally increases the perceived speed of the&lt;br /&gt;
program by allowing the program to prompt for another command before the&lt;br /&gt;
previous command is complete. For example, if the user changes a cell in a&lt;br /&gt;
spreadsheet and then calls for recalculation, the &amp;quot;execute&amp;quot; thread can&lt;br /&gt;
recalculate while the &amp;quot;command&amp;quot; thread allows the user to move the cursor,&lt;br /&gt;
select new menus, and so forth. The spreadsheet should use RAM semaphores&lt;br /&gt;
to protect its structures so that one thread can't change a structure while&lt;br /&gt;
it is being manipulated by another thread. As far as the user can tell, he&lt;br /&gt;
or she is able to overlap commands without restriction. In actuality, the&lt;br /&gt;
previous command is usually complete before the user can finish entering&lt;br /&gt;
the new command. Occasionally, however, the new command is delayed until&lt;br /&gt;
the first has completed execution. This happens, for example, when the user&lt;br /&gt;
of a spreadsheet deletes a row right after saving the spreadsheet to disk.&lt;br /&gt;
Of course, the performance in this worst case situation is no worse than a&lt;br /&gt;
standard single-thread design is for all cases.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.2  Asynchronous Processing&lt;br /&gt;
Another common use of threads is to provide asynchronous elements in a&lt;br /&gt;
program's design. For example, as a protection against power failure, you&lt;br /&gt;
can design an editor so that it writes its RAM buffer to disk once every&lt;br /&gt;
minute. Threads make it unnecessary to scatter time checks throughout the&lt;br /&gt;
program or to sit in polling loops for input so that a time event isn't&lt;br /&gt;
missed while blocked on a read call. You can create a thread whose sole job&lt;br /&gt;
is periodic backup. The thread can call DosSleep to sleep for 60 seconds,&lt;br /&gt;
write the buffer, and then go back to sleep for another 60 seconds.&lt;br /&gt;
     The asynchronous event doesn't have to be time related. For example,&lt;br /&gt;
in a program that communicates over an asynchronous serial port, you can&lt;br /&gt;
dedicate a thread to wait for the modem carrier to come on or to wait for a&lt;br /&gt;
protocol time out. The main thread can continue to interact with the user.&lt;br /&gt;
     Programs that provide services to other programs via IPC can use&lt;br /&gt;
threads to simultaneously respond to multiple requests. For example, one&lt;br /&gt;
thread can watch the incoming work queue while one or more additional&lt;br /&gt;
threads perform the work.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.3  Speed Execution&lt;br /&gt;
You can use threads to speed the execution of single processes by&lt;br /&gt;
overlapping I/O and computation. A single-threaded process can perform&lt;br /&gt;
computations or call OS/2 for disk reads and writes, but not both at the&lt;br /&gt;
same time. A multithreaded process, on the other hand, can compute one&lt;br /&gt;
batch of data while reading the next batch from a device.&lt;br /&gt;
     Eventually, PCs containing multiple 80386 processors will become&lt;br /&gt;
available. An application that uses multiple threads may execute faster by&lt;br /&gt;
using more than one CPU simultaneously.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.2.4  Organizing Programs&lt;br /&gt;
Finally, you can use threads to organize and simplify the structure of a&lt;br /&gt;
program. For example, in a program for a turnkey security/alarm system, you&lt;br /&gt;
can assign a separate thread for each activity. One thread can watch the&lt;br /&gt;
status of the intrusion switches; a second can send commands to control the&lt;br /&gt;
lights; a third can run the telephone dialer; and a fourth can interface&lt;br /&gt;
with each control panel.&lt;br /&gt;
     This structure simplifies software design. The programmer needn't&lt;br /&gt;
worry that an intrusion switch is triggering unnoticed while the CPU is&lt;br /&gt;
executing the code that waits on the second key of a two-key command.&lt;br /&gt;
Likewise, the programmer doesn't have to worry about talking to two command&lt;br /&gt;
consoles at the same time; because each has its own thread and local&lt;br /&gt;
(stack) variables, multiple consoles can be used simultaneously without&lt;br /&gt;
conflict.&lt;br /&gt;
     Of course, you can write such a program without multiple threads; a&lt;br /&gt;
rat's nest of event flags and polling loops would do the job. Much better&lt;br /&gt;
would be a family of co-routines. But best, and simplest of all, is a&lt;br /&gt;
multithreaded design.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3  Interlocking&lt;br /&gt;
The good news about threads is that they share a process's data, files, and&lt;br /&gt;
resources. The bad news is that they share a process's data, files, and&lt;br /&gt;
resources--and that sometimes these items must be protected against&lt;br /&gt;
simultaneous update by multiple threads. As we discussed earlier, most OS/2&lt;br /&gt;
machines have a single CPU; the &amp;quot;random&amp;quot; preemption of the scheduler,&lt;br /&gt;
switching the CPU among threads, gives the illusion of the simultaneous&lt;br /&gt;
execution of threads. Because the scheduler is deterministic and priority&lt;br /&gt;
based, scheduling a process's threads is certainly not random; but good&lt;br /&gt;
programming practice requires that it be considered so. Each time a program&lt;br /&gt;
runs, external events will perturb the scheduling of the threads. Perhaps&lt;br /&gt;
some other, higher priority task needs the CPU for a while. Perhaps the&lt;br /&gt;
disk arm is in a different position, and a disk read by one thread takes a&lt;br /&gt;
little longer this time than it did the last. You cannot even assume that&lt;br /&gt;
only the highest priority runnable thread is executing because a multiple-&lt;br /&gt;
CPU system may execute the N highest priority threads.&lt;br /&gt;
     The only safe assumption is that all threads are executing&lt;br /&gt;
simultaneously and that--in the absence of explicit interlocking or&lt;br /&gt;
semaphore calls--each thread is always doing the &amp;quot;worst possible thing&amp;quot; in&lt;br /&gt;
terms of simultaneously updating static data or structures. Writing to&lt;br /&gt;
looser standards and then testing the program &amp;quot;to see if it's OK&amp;quot; is&lt;br /&gt;
unacceptable. The very nature of such race conditions, as they are called,&lt;br /&gt;
makes them extremely difficult to find during testing. Murphy's law says&lt;br /&gt;
that such problems are rare during testing and become a plague only after&lt;br /&gt;
the program is released.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.1  Local Variables&lt;br /&gt;
The best way to avoid a collision of threads over static data is to write&lt;br /&gt;
your program to minimize static data. Because each thread has its own&lt;br /&gt;
stack, each thread has its own stack frame in which to store local&lt;br /&gt;
variables. For example, if one thread opens and reads a file and no other&lt;br /&gt;
thread ever manipulates that file, the memory location where that file's&lt;br /&gt;
handle is stored should be in the thread's stack frame, not in static&lt;br /&gt;
memory. Likewise, buffers and work areas that are private to a thread&lt;br /&gt;
should be on that thread's stack frame. Stack variables that are local to&lt;br /&gt;
the current procedure are easily referenced in high-level languages and in&lt;br /&gt;
assembly language. Data items that are referenced by multiple procedures&lt;br /&gt;
can still be located on the stack. Pascal programs can address such items&lt;br /&gt;
directly via the data scope mechanism. C and assembly language programs&lt;br /&gt;
will need to pass pointers to the items into the procedures that use them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.2  RAM Semaphores&lt;br /&gt;
Although using local variables on stack frames greatly reduces problems&lt;br /&gt;
among threads, there will always be at least a few cases in which more than&lt;br /&gt;
one thread needs to access a static data item or a static resource such as&lt;br /&gt;
a file handle. In this situation, write the code that manipulates the&lt;br /&gt;
static item as a critical section (a body of code that manipulates a data&lt;br /&gt;
resource in a nonreentrant way) and then use RAM semaphores to reserve each&lt;br /&gt;
critical section before it is executed. This procedure guarantees that only&lt;br /&gt;
one thread at a time is in a critical section. See 16.2 Data Integrity for&lt;br /&gt;
a more detailed discussion.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.3  DosSuspendThread&lt;br /&gt;
In some situations it may be possible to enumerate all threads that might&lt;br /&gt;
enter a critical section. In these cases, a process's thread can use&lt;br /&gt;
DosSuspendThread to suspend the execution of the other thread(s) that might&lt;br /&gt;
enter the critical section. The DosSuspendThread call can only be used to&lt;br /&gt;
suspend threads that belong to the process making the call; it cannot be&lt;br /&gt;
used to suspend a thread that belongs to another process. Multiple threads&lt;br /&gt;
can be suspended by making multiple DosSuspendThread calls, one per thread.&lt;br /&gt;
If a just-suspended thread is in the middle of a system call, work on that&lt;br /&gt;
system call may or may not proceed. In either case, there will be no&lt;br /&gt;
further execution of application (ring 3) code by a suspended thread.&lt;br /&gt;
     It is usually better to protect critical sections with a RAM semaphore&lt;br /&gt;
than to use DosSuspendThread. Using a semaphore to protect a critical&lt;br /&gt;
section is analogous to using a traffic light to protect an intersection&lt;br /&gt;
(an automotive &amp;quot;critical section&amp;quot; because conflicting uses must be&lt;br /&gt;
prevented). Using DosSuspendThread, on the other hand, is analogous to your&lt;br /&gt;
stopping the other cars each time you go through an intersection; you're&lt;br /&gt;
interfering with the operation of the other cars just in case they might be&lt;br /&gt;
driving through the same intersection as you, presumably an infrequent&lt;br /&gt;
situation. Furthermore, you need a way to ensure that another vehicle isn't&lt;br /&gt;
already in the middle of the intersection when you stop it. Getting back to&lt;br /&gt;
software, you need to ensure that the thread you're suspending isn't&lt;br /&gt;
already executing the critical section at the time that you suspend it. We&lt;br /&gt;
recommend that you avoid DosSuspendThread when possible because of its&lt;br /&gt;
adverse effects on process performance and because of the difficulty in&lt;br /&gt;
guaranteeing that all the necessary threads have been suspended, especially&lt;br /&gt;
when a program undergoes future maintenance and modification.&lt;br /&gt;
     A DosResumeThread call restores the normal operation of a suspended&lt;br /&gt;
thread.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.3.4  DosEnterCritSec/DosExitCritSec&lt;br /&gt;
DosSuspendThread suspends the execution of a single thread within a&lt;br /&gt;
process. DosEnterCritSec suspends all threads in a process except the one&lt;br /&gt;
making the DosEnterCritSec call. Except for the scope of their operation,&lt;br /&gt;
DosEnterCritSec and DosExitCritSec are similar to DosSuspendThead and&lt;br /&gt;
DosResumeThread, and the same caveats and observations apply.&lt;br /&gt;
     DosExitCritSec will not undo a DosSuspendThread that was already in&lt;br /&gt;
effect. It releases only those threads that were suspended by&lt;br /&gt;
DosEnterCritSec.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.4  Thread 1&lt;br /&gt;
Each thread in a process has an associated thread ID. A thread's ID is a&lt;br /&gt;
magic cookie. Its value has no intrinsic meaning to the application; it&lt;br /&gt;
has meaning only as a name for a thread in an operating system call. The&lt;br /&gt;
one exception to this is the process's first thread, whose thread ID is&lt;br /&gt;
always 1.&lt;br /&gt;
     Thread 1 is special: It is the thread that is interrupted when a&lt;br /&gt;
process receives a signal. See Chapter 12, Signals, for further details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.5  Thread Death&lt;br /&gt;
A thread can die in two ways. First, it can terminate itself with the&lt;br /&gt;
DosExit call. Second, when any thread in a process calls DosExit with the&lt;br /&gt;
&amp;quot;exit entire process&amp;quot; argument, all threads belonging to that process are&lt;br /&gt;
terminated &amp;quot;as soon as possible.&amp;quot; If they were executing application code&lt;br /&gt;
at the time DosExit was called, they terminate immediately. If they were in&lt;br /&gt;
the middle of a system call, they terminate &amp;quot;very quickly.&amp;quot; If the system&lt;br /&gt;
call executes quickly enough, its function may complete (although the CPU&lt;br /&gt;
will not return from the system call itself); but if the system call&lt;br /&gt;
involves delays of more than 1 second, it will terminate without&lt;br /&gt;
completing. Whether a thread's last system call completes is usually moot,&lt;br /&gt;
but in a few cases, such as writes to some types of devices, it may be&lt;br /&gt;
noticed that the last write was only partially completed.&lt;br /&gt;
     When a process wants to terminate, it should use the &amp;quot;terminate entire&lt;br /&gt;
process&amp;quot; form of DosExit rather than the &amp;quot;terminate this thread&amp;quot; form.&lt;br /&gt;
Unbeknownst to the calling process, some dynlink packages, including some&lt;br /&gt;
OS/2 system calls, may create threads. These threads are called captive&lt;br /&gt;
threads because only the original calling thread returns from the dynlink&lt;br /&gt;
call; the created thread remains &amp;quot;captive&amp;quot; inside the dynlink package. If a&lt;br /&gt;
program attempts to terminate by causing all its known threads to use the&lt;br /&gt;
DosExit &amp;quot;terminate this thread&amp;quot; form, the termination may not be successful&lt;br /&gt;
because of such captive threads.&lt;br /&gt;
     Of course, if the last remaining thread of a process calls DosExit&lt;br /&gt;
&amp;quot;terminate this thread,&amp;quot; OS/2 terminates the process.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.1.6  Performance Characteristics&lt;br /&gt;
Threads are intended to be fast and cheap. In OS/2 version 1.0, each&lt;br /&gt;
additional thread that is created consumes about 1200 bytes of memory&lt;br /&gt;
inside the OS/2 kernel for its kernel mode stack. This is in addition to&lt;br /&gt;
the 2048 bytes of user mode stack space that we recommend you provide from&lt;br /&gt;
the process's data area. Terminating a thread does not release the kernel&lt;br /&gt;
stack memory, but subsequently creating another thread reuses this memory.&lt;br /&gt;
In other words, the system memory that a process's threads consume is the&lt;br /&gt;
maximum number of threads simultaneously alive times 1200 bytes. This&lt;br /&gt;
figure is exclusive of each thread's stack, which is provided by the&lt;br /&gt;
process from its own memory.&lt;br /&gt;
     The time needed to create a new thread depends on the process's&lt;br /&gt;
previous thread behavior. Creating a thread that will reuse the internal&lt;br /&gt;
memory area created for a previous thread that has terminated takes&lt;br /&gt;
approximately 3 milliseconds.&lt;br /&gt;
2. All timings in this book refer to a 6 mHz IBM AT with one&lt;br /&gt;
wait-state memory. This represents a worst case performance level.&lt;br /&gt;
2 A request to create a new thread that&lt;br /&gt;
extends the process's &amp;quot;thread count high-water mark&amp;quot; requires an internal&lt;br /&gt;
memory allocation operation. This operation may trigger a memory compaction&lt;br /&gt;
or even a segment swapout, so its time cannot be accurately predicted.&lt;br /&gt;
     It takes about 1 millisecond for the system to begin running an&lt;br /&gt;
unblocked thread. In other words, if a lower-priority thread releases a RAM&lt;br /&gt;
semaphore that is being waited on by a higher-priority thread,&lt;br /&gt;
approximately 1 millisecond passes between the lower-priority thread's call&lt;br /&gt;
to release the semaphore and the return of the higher-priority thread from&lt;br /&gt;
its DosSemRequest call.&lt;br /&gt;
     Threads are a key feature of OS/2; they will receive strong support in&lt;br /&gt;
future versions of OS/2 and will play an increasingly important&lt;br /&gt;
architectural role. You can, therefore, expect thread costs and performance&lt;br /&gt;
to be the same or to improve in future releases.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2  Scheduler/Priorities&lt;br /&gt;
&lt;br /&gt;
A typical running OS/2 system contains a lot of threads. Frequently,&lt;br /&gt;
several threads are ready to execute at any one time. The OS/2 scheduler&lt;br /&gt;
decides which thread to run next and how long to run it before assigning&lt;br /&gt;
the CPU to another thread. OS/2's scheduler is a priority-based&lt;br /&gt;
scheduler; it assigns each thread a priority and then uses that priority to&lt;br /&gt;
decide which thread to run. The OS/2 scheduler is also a preemptive&lt;br /&gt;
scheduler. If a higher-priority thread is ready to execute, OS/2 does not&lt;br /&gt;
wait for the lower-priority thread to finish with the CPU before&lt;br /&gt;
reassigning the CPU; the lower-priority thread is preempted--the CPU is&lt;br /&gt;
summarily yanked away. Naturally, the state of the preempted thread is&lt;br /&gt;
recorded so that its execution can resume later without ill effect.&lt;br /&gt;
     The scheduler's dispatch algorithm is very straightforward: It&lt;br /&gt;
executes the highest-priority runnable thread for as long as the thread&lt;br /&gt;
wants the CPU. When that thread gives up the CPU--perhaps by waiting for an&lt;br /&gt;
I/O operation--that thread is no longer runnable, and the scheduler&lt;br /&gt;
executes the thread with the highest priority that is runnable. If a&lt;br /&gt;
blocked thread becomes runnable and it has a higher priority than the&lt;br /&gt;
thread currently running, the CPU is immediately preempted and assigned to&lt;br /&gt;
the higher-priority thread. In summary, the CPU is always running the&lt;br /&gt;
highest-priority runnable thread.&lt;br /&gt;
     The scheduler's dispatcher is simplicity itself: It's blindly priority&lt;br /&gt;
based. Although the usual focus for OS/2 activities is the process--a&lt;br /&gt;
process lives, dies, opens files, and so on--the scheduler components of&lt;br /&gt;
OS/2 know little about processes. Because the thread is the dispatchable&lt;br /&gt;
entity, the scheduler is primarily thread oriented. If you're not used to&lt;br /&gt;
thinking in terms of threads, you can mentally substitute the word process&lt;br /&gt;
for the word thread in the following discussion. In practice, all of a&lt;br /&gt;
process's threads typically share the same priority, so it's not too&lt;br /&gt;
inaccurate to view the system as being made up of processes that compete&lt;br /&gt;
for CPU resources.&lt;br /&gt;
     In OS/2 threads are classified and run in three categories: general&lt;br /&gt;
priority, time-critical priority, and low priority. These categories are&lt;br /&gt;
further divided into subcategories. Figure 5-1 shows the relationship of&lt;br /&gt;
the three priority categories and their subcategories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   High&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³        ³            /ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³        ³          /  ³   Foreground   ³&lt;br /&gt;
³ Force  ³        /    ³     screen     ³&lt;br /&gt;
³  run   ³      /      ³     group      ³&lt;br /&gt;
³        ³    /        ³                ³&lt;br /&gt;
³        ³  /          ³  interactive   ³&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄ´/            ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
³        ³             ³   Foreground   ³&lt;br /&gt;
³        ³             ³     screen     ³&lt;br /&gt;
³ Normal ³             ³     group      ³&lt;br /&gt;
³        ³             ³                ³&lt;br /&gt;
³        ³             ³ noninteractive ³&lt;br /&gt;
³        ³             ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄ´\            ³                ³&lt;br /&gt;
³        ³  \          ³   Background   ³&lt;br /&gt;
³        ³    \        ³     screen     ³&lt;br /&gt;
³  Idle  ³      \      ³     group      ³&lt;br /&gt;
³  time  ³        \    ³                ³&lt;br /&gt;
³        ³          \  ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³        ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
   Low&lt;br /&gt;
&lt;br /&gt;
Figure 5-1.  Priority categories.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1  General Priority Category&lt;br /&gt;
The majority of threads in the system run in the general priority category&lt;br /&gt;
and belong to one of three subcategories: background, foreground, or&lt;br /&gt;
interactive. To a limited extent, OS/2 dynamically modifies the priorities&lt;br /&gt;
of threads in the general priority category.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.1  Background Subcategory&lt;br /&gt;
The purpose of the OS/2 priority design is to optimize response rather than&lt;br /&gt;
throughput. In other words, the system is not concerned about ensuring that&lt;br /&gt;
all runnable threads get at least some CPU time, and the system is not&lt;br /&gt;
primarily concerned about trying to keep the disks busy when the highest-&lt;br /&gt;
priority thread is compute bound. Instead, OS/2 is concerned about keeping&lt;br /&gt;
less important work from delaying or slowing more important work. This is&lt;br /&gt;
the reason for the background subcategory. The word background has been&lt;br /&gt;
used in many different ways to describe how tasks are performed in many&lt;br /&gt;
operating systems; we use the word to indicate processes that are&lt;br /&gt;
associated with a screen group not currently being displayed.&lt;br /&gt;
     For example, a user is working with a word-processing program but then&lt;br /&gt;
switches from that program to a spreadsheet program. The word-processing&lt;br /&gt;
program becomes background, and the spreadsheet program is promoted from&lt;br /&gt;
background to foreground. When the user selects different screen groups,&lt;br /&gt;
threads change from foreground to background or background to foreground.&lt;br /&gt;
Background threads have the lowest priority in the general priority&lt;br /&gt;
category. Background applications get the CPU (and, through it, the disks)&lt;br /&gt;
only when all foreground threads are idle. As soon as a foreground thread&lt;br /&gt;
is runnable, the CPU is preempted from the background thread. Background&lt;br /&gt;
threads can use leftover machine time, but they can never compete with&lt;br /&gt;
foreground threads.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.2  Foreground and Interactive Subcategories&lt;br /&gt;
All processes associated with the currently active screen group are made&lt;br /&gt;
members of the foreground subcategory. The process that is currently&lt;br /&gt;
interacting with the keyboard is promoted to the interactive subcategory.&lt;br /&gt;
This ensures that the user will get the fastest possible response to a&lt;br /&gt;
command. When the interactive process's threads release the CPU (via&lt;br /&gt;
blocking on some OS/2 call), the noninteractive foreground threads get the&lt;br /&gt;
next crack at it because those threads are usually doing work on behalf of&lt;br /&gt;
the interactive process or work that is in some way related. If no&lt;br /&gt;
foreground thread needs the CPU, background threads may run.&lt;br /&gt;
     Although the scheduler concerns itself with threads rather than&lt;br /&gt;
processes, it's processes that switch between categories--foreground,&lt;br /&gt;
background, and interactive. When a process changes category--for example,&lt;br /&gt;
when a process shows itself to be in the interactive subcategory by doing&lt;br /&gt;
keyboard I/O--the priorities of all its threads are adjusted&lt;br /&gt;
appropriately.&lt;br /&gt;
     Because background threads are the &amp;quot;low men on the totem pole&amp;quot; that is&lt;br /&gt;
composed of quite a few threads, it may seem that they'll never get to run.&lt;br /&gt;
This isn't the case, though, over a long enough period of time. Yes, a&lt;br /&gt;
background thread can be totally starved for CPU time during a 5-second&lt;br /&gt;
interval, but it would be very rare if it received no service during a 1-&lt;br /&gt;
minute interval. Interactive application commands that take more than a few&lt;br /&gt;
seconds of CPU time are rare. Commands involving disk transfer may take&lt;br /&gt;
longer, but the CPU is available for lower-priority threads while the&lt;br /&gt;
interactive process is waiting for disk operations. Finally, a user rarely&lt;br /&gt;
keeps an interactive application fully busy; the normal &amp;quot;type, look, and&lt;br /&gt;
think&amp;quot; cycle has lots of spare time in it for background threads to&lt;br /&gt;
run.&lt;br /&gt;
     But how does this apply to the presentation manager? The presentation&lt;br /&gt;
manager runs many independent interactive tasks within the same screen&lt;br /&gt;
group, so are they all foreground threads? How does OS/2 know which is the&lt;br /&gt;
interactive process? The answer is that the presentation manager advises&lt;br /&gt;
the scheduler. When the presentation manager screen group is displayed, all&lt;br /&gt;
threads within that screen group are placed in the foreground category.&lt;br /&gt;
When the user selects a particular window to receive keyboard or mouse&lt;br /&gt;
events, the presentation manager tells the scheduler that the process using&lt;br /&gt;
that window is now the interactive process. As a result, the system's&lt;br /&gt;
interactive performance is preserved in the presentation manager's screen&lt;br /&gt;
group.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.1.3  Throughput Balancing&lt;br /&gt;
We mentioned that some operating systems try to optimize system throughput&lt;br /&gt;
by trying to run CPU-bound and I/O-bound applications at the same time. The&lt;br /&gt;
theory is that the I/O-bound application ties up the disk but needs little&lt;br /&gt;
CPU time, so the disk work can be gotten out of the way while the CPU is&lt;br /&gt;
running the CPU-bound task. If the disk thread has the higher priority, the&lt;br /&gt;
tasks run in tandem. Each time the disk operation is completed, the I/O-&lt;br /&gt;
bound thread regains the CPU and issues another disk operation. Leftover&lt;br /&gt;
CPU time goes to the CPU-bound task that, in this case, has a lower&lt;br /&gt;
priority.&lt;br /&gt;
     This won't work, however, if the CPU-bound thread has a higher&lt;br /&gt;
priority than the I/O-bound thread. The CPU-bound thread will tend to hold&lt;br /&gt;
the CPU, and the I/O-bound thread won't get even the small amount of CPU&lt;br /&gt;
time that it needs to issue another I/O request. Traditionally, schedulers&lt;br /&gt;
have been designed to deal with this problem by boosting the priority of&lt;br /&gt;
I/O-bound tasks and lowering the priority of CPU-bound tasks so that,&lt;br /&gt;
eventually, the I/O-bound thread gets enough service to make its I/O&lt;br /&gt;
requests.&lt;br /&gt;
     The OS/2 scheduler incorporates this design to a limited extent. Each&lt;br /&gt;
time a thread issues a system call that blocks, the scheduler looks at the&lt;br /&gt;
period between the time the CPU was assigned to the thread and the time the&lt;br /&gt;
thread blocked itself with a system call.&lt;br /&gt;
3. We use &amp;quot;blocking&amp;quot; rather than &amp;quot;requesting an I/O operation&amp;quot; as&lt;br /&gt;
a test of I/O boundedness because nearly all blocking operations&lt;br /&gt;
wait for I/O. If a thread's data were all in the buffer cache,&lt;br /&gt;
the thread could issue many I/O requests and still be compute&lt;br /&gt;
bound. In other words, when we speak of I/O-bound threads, we&lt;br /&gt;
really mean device bound--not I/O request bound.&lt;br /&gt;
3 If that period of time is short,&lt;br /&gt;
the thread is considered I/O bound, and its priority receives a small&lt;br /&gt;
increment. If a thread is truly I/O bound, it soon receives several such&lt;br /&gt;
increments and, thus, a modest priority promotion. On the other hand, if&lt;br /&gt;
the thread held the CPU for a longer period of time, it is considered CPU&lt;br /&gt;
bound, and its priority receives a small decrement.&lt;br /&gt;
     The I/O boundedness priority adjustment is small. No background&lt;br /&gt;
thread, no matter how I/O bound, can have its priority raised to the point&lt;br /&gt;
where it has a higher priority than any foreground thread, no matter how&lt;br /&gt;
CPU bound. This throughput enhancing optimization applies only to &amp;quot;peer&amp;quot;&lt;br /&gt;
threads--threads with similar priorities. For example, the threads of a&lt;br /&gt;
single process generally have the same base priority, so this adjustment&lt;br /&gt;
helps optimize the throughput of that process.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.2  Time-Critical Priority Category&lt;br /&gt;
Foreground threads, particularly interactive foreground threads, receive&lt;br /&gt;
CPU service whenever they want it. Noninteractive foreground threads and,&lt;br /&gt;
particularly, background threads may not receive any CPU time for periods&lt;br /&gt;
of arbitrary length. This approach improves system response, but it's not&lt;br /&gt;
always a good thing. For example, you may be running a network or a&lt;br /&gt;
telecommunications application that drops its connection if it can't&lt;br /&gt;
respond to incoming packets in a timely fashion. Also, you may want to make&lt;br /&gt;
an exception to the principle of &amp;quot;response, not throughput&amp;quot; when it comes&lt;br /&gt;
to printers. Most printers are much slower than their users would like, and&lt;br /&gt;
most printer spooler programs require little in the way of CPU time; so the&lt;br /&gt;
OS/2 print spooler (the program that prints queued output on the printer)&lt;br /&gt;
would like to run at a high priority to keep the printer busy.&lt;br /&gt;
     Time-critical applications are so called because the ability to run in&lt;br /&gt;
a timely fashion is critical to their well-being. Time-critical&lt;br /&gt;
applications may or may not be interactive, and they may be in the&lt;br /&gt;
foreground or in a background screen group, but this should not affect&lt;br /&gt;
their high priority. The OS/2 scheduler contains a time-critical priority&lt;br /&gt;
category to deal with time-critical applications. A thread running in this&lt;br /&gt;
priority category has a higher priority than any non-time-critical thread&lt;br /&gt;
in the system, including interactive threads. Unlike priorities in the&lt;br /&gt;
general category, a time-critical priority is never adjusted; once given a&lt;br /&gt;
time-critical priority, a thread's priority remains fixed until a system&lt;br /&gt;
call changes it.&lt;br /&gt;
     Naturally, time-critical threads should consume only modest amounts of&lt;br /&gt;
CPU time. If an application has a time-critical thread that consumes&lt;br /&gt;
considerable CPU time--say, more than 20 percent--the foreground&lt;br /&gt;
interactive application will be noticeably slowed or even momentarily&lt;br /&gt;
stopped. System usability is severely affected when the interactive&lt;br /&gt;
application can't get service. The screen output stutters and stumbles,&lt;br /&gt;
characters are dropped when commands are typed, and, in general, the&lt;br /&gt;
computer becomes unusable.&lt;br /&gt;
     Not all threads in a process have to be of the same priority. An&lt;br /&gt;
application may need time-critical response for only some of its work; the&lt;br /&gt;
other work can run at a normal priority. For example, in a&lt;br /&gt;
telecommunications program a &amp;quot;receive incoming data&amp;quot; thread might run at a&lt;br /&gt;
time-critical priority but queue messages in memory for processing by a&lt;br /&gt;
normal-priority thread. If the time-critical thread finds that the normal-&lt;br /&gt;
priority thread has fallen behind, it can send a &amp;quot;wait for me&amp;quot; message to&lt;br /&gt;
the sending program.&lt;br /&gt;
     We strongly recommend that processes that use monitors run the monitor&lt;br /&gt;
thread, and only the monitor thread, at a time-critical priority. This&lt;br /&gt;
prevents delayed device response because of delays in processing the&lt;br /&gt;
monitor data stream. See 16.1 Device Monitors for more information.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.3  Low Priority Category&lt;br /&gt;
If you picture the general priority category as a range of priorities, with&lt;br /&gt;
the force run priority category as a higher range, there is a third range,&lt;br /&gt;
called the low priority category, that is lower in priority than the&lt;br /&gt;
general priority category. As a result, threads in this category get CPU&lt;br /&gt;
service only when no other thread in the other categories needs it. This&lt;br /&gt;
category is a mirror image of the time-critical priority category in that&lt;br /&gt;
the system call that sets the thread fixes the priority; OS/2 never changes&lt;br /&gt;
a low priority.&lt;br /&gt;
     I don't expect the low priority category to be particularly popular.&lt;br /&gt;
It's in the system primarily because it falls out for free, as a mirror&lt;br /&gt;
image of the time-critical category. Turnkey systems may want to run some&lt;br /&gt;
housekeeping processes at this priority. Some users enjoy computing PI,&lt;br /&gt;
doing cryptographic analysis, or displaying fractal images; these&lt;br /&gt;
recreations are good candidates for soaking up leftover CPU time. On a more&lt;br /&gt;
practical level, you could run a program that counts seconds of CPU time&lt;br /&gt;
and yields a histogram of CPU utilization during the course of a day.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5.2.4  Setting Process/Thread Priorities&lt;br /&gt;
We've discussed at some length the effect of the various priorities, but we&lt;br /&gt;
haven't discussed how to set these priorities. Because inheritance is an&lt;br /&gt;
important OS/2 concept, how does a parent's priority affect that of the&lt;br /&gt;
child? Finally, although we said that priority is a thread issue rather&lt;br /&gt;
than a process one, we kept bringing up processes anyway. How does all this&lt;br /&gt;
work?&lt;br /&gt;
     Currently, whenever a thread is created, it inherits the priority of&lt;br /&gt;
its creator thread. In the case of DosCreateThread, the thread making the&lt;br /&gt;
call is the creator thread. In the case of thread 1, the thread in the&lt;br /&gt;
parent process that is making the DosExecPgm call is the creator thread.&lt;br /&gt;
When a process makes a DosSetPrty call to change the priority of one of its&lt;br /&gt;
own threads, the new priority always takes effect. When a process uses&lt;br /&gt;
DosSetPrty to change the priority of another process, only the threads in&lt;br /&gt;
that other process which have not had their priorities explicitly set from&lt;br /&gt;
within their own process are changed. This prevents a parent process from&lt;br /&gt;
inadvertently lowering the priority of, say, a time-critical thread by&lt;br /&gt;
changing the base priority of a child process.&lt;br /&gt;
     In a future release, we expect to improve this algorithm so that each&lt;br /&gt;
process has a base priority. A new thread will inherit its creator's base&lt;br /&gt;
priority. A process's thread priorities that are in the general priority&lt;br /&gt;
category will all be relative to the process's base priority so that a&lt;br /&gt;
change in the base priority will raise or lower the priority of all the&lt;br /&gt;
process's general threads while retaining their relative priority&lt;br /&gt;
relationships. Threads in the time-critical and low priority categories&lt;br /&gt;
will continue to be unaffected by their process's base priority.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
6  The User Interface&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
OS/2 contains several important subsystems: the file system, the memory&lt;br /&gt;
management subsystem, the multitasking subsystem, and the user interface&lt;br /&gt;
subsystem--the presentation manager. MS-DOS does not define or support a&lt;br /&gt;
user interface subsystem; each application must provide its own. MS-DOS&lt;br /&gt;
utilities use a primitive line-oriented interface, essentially unchanged&lt;br /&gt;
from the interface provided by systems designed to interface with TTYs.&lt;br /&gt;
     OS/2 is intended to be a graphics-oriented operating system, and as&lt;br /&gt;
such it needs to provide a standard graphical user interface (GUI)&lt;br /&gt;
subsystem--for several reasons. First, because such systems are complex to&lt;br /&gt;
create, to expect that each application provide its own is unreasonable.&lt;br /&gt;
Second, a major benefit of a graphical user interface is that applications&lt;br /&gt;
can be intermingled. For example, their output windows can share the&lt;br /&gt;
screen, and the user can transfer data between applications using visual&lt;br /&gt;
metaphors. If each application had its own GUI package, such sharing would&lt;br /&gt;
be impossible. Third, a graphical user interface is supposed to make the&lt;br /&gt;
machine easier to use, but this will be so only if the user can learn one&lt;br /&gt;
interface that will work with all applications.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
6.1  VIO User Interface&lt;br /&gt;
&lt;br /&gt;
The full graphical user interface subsystem will not ship with OS/2 version&lt;br /&gt;
1.0, so the initial release will contain the character-oriented VIO/KBD/MOU&lt;br /&gt;
subsystem (see Chapter 13, The Presentation Manager and VIO, for more&lt;br /&gt;
details). Although VIO doesn't provide any graphical services, it does&lt;br /&gt;
allow applications to sidestep VIO and construct their own. The VIO&lt;br /&gt;
screen group interface is straightforward. When the machine is booted up,&lt;br /&gt;
the screen displays the screen group list. The user can select an existing&lt;br /&gt;
screen group or create a new one. From within a screen group, the user can&lt;br /&gt;
type a magic key sequence to return the screen to the screen group list.&lt;br /&gt;
Another magic key sequence allows the user to toggle through all existing&lt;br /&gt;
screen groups. One screen group is identified in the screen group list as&lt;br /&gt;
the real mode screen group.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
6.2  The Presentation Manager User Interface&lt;br /&gt;
&lt;br /&gt;
The OS/2 presentation manager is a powerful and flexible graphical user&lt;br /&gt;
interface. It supports such features as windowing, drop-down and pop-up&lt;br /&gt;
menus, and scroll bars. It works best with a graphical pointing device such&lt;br /&gt;
as a mouse, but it can be controlled exclusively from the keyboard.&lt;br /&gt;
     The presentation manager employs screen windows to allow multiple&lt;br /&gt;
applications to use the screen and keyboard simultaneously. Each&lt;br /&gt;
application uses one or more windows to display its information; the user&lt;br /&gt;
can size and position each window, overlapping some and perhaps shrinking&lt;br /&gt;
others to icons. Mouse and keyboard commands change the input focus between&lt;br /&gt;
windows; this allows the presentation manager to route keystrokes and mouse&lt;br /&gt;
events to the proper application.&lt;br /&gt;
     Because of its windowing capability, the presentation manager doesn't&lt;br /&gt;
need to use the underlying OS/2 screen group mechanism to allow the user to&lt;br /&gt;
switch between running applications. The user starts an application by&lt;br /&gt;
pointing to its name on a menu display; for most applications the&lt;br /&gt;
presentation manager creates a new window and assigns it to the new&lt;br /&gt;
process. Some applications may decline to use the presentation manager's&lt;br /&gt;
graphical user interface and prefer to take direct control of the display.&lt;br /&gt;
When such an application is initiated, the presentation manager creates a&lt;br /&gt;
private screen group for it and switches to that screen group. The user can&lt;br /&gt;
switch away by entering a special key sequence that brings up a menu which&lt;br /&gt;
allows the user to select any running program. If the selected program is&lt;br /&gt;
using the standard presentation manager GUI, the screen is switched to the&lt;br /&gt;
screen group shared by those programs. Otherwise, the screen is switched to&lt;br /&gt;
the private screen group that the specified application is using.&lt;br /&gt;
     To summarize, only the real mode application and applications that&lt;br /&gt;
take direct control of the display hardware need to run in their own screen&lt;br /&gt;
groups. The presentation manager runs all other processes in a single&lt;br /&gt;
screen group and uses its windowing facilities to share the screen among&lt;br /&gt;
them. The user can switch between applications via a special menu; if both&lt;br /&gt;
the previous and the new application are using the standard interface, the&lt;br /&gt;
user can switch the focus directly without going though the menu.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
6.3  Presentation Manager and VIO Compatibility&lt;br /&gt;
&lt;br /&gt;
In OS/2 version 1.1 and in all subsequent releases, the presentation&lt;br /&gt;
manager will replace and superset the VIO interface. Applications that use&lt;br /&gt;
the character mode VIO interface will continue to work properly as&lt;br /&gt;
windowable presentation manager applications, as will applications that use&lt;br /&gt;
the STDIN and STDOUT file handles for interactive I/O. Applications that&lt;br /&gt;
use the VIO interface to obtain direct access to the graphical display&lt;br /&gt;
hardware will also be supported; as described above, the presentation&lt;br /&gt;
manager will run such applications in their own screen group.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7  Dynamic Linking&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
A central component of OS/2 is dynamic linking. Dynamic links play several&lt;br /&gt;
critical architectural roles. Before we can discuss them at such an&lt;br /&gt;
abstract level, however, we need to understand the nuts and bolts of their&lt;br /&gt;
workings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.1  Static Linking&lt;br /&gt;
&lt;br /&gt;
A good preliminary to the study of dynamic links (called dynlinks, for&lt;br /&gt;
short) is a review of their relative, static links. Every programmer who&lt;br /&gt;
has gone beyond interpreter-based languages such as BASIC is familiar with&lt;br /&gt;
static links. You code a subroutine or a procedure call to a routine that&lt;br /&gt;
is not present in that compiland (or source file), which we'll call Foo.&lt;br /&gt;
The missing routine is declared external so that the assembler or compiler&lt;br /&gt;
doesn't flag it as an undefined symbol. At linktime, you present the linker&lt;br /&gt;
with the .OBJ file that you created from your compiland, and you also&lt;br /&gt;
provide a .OBJ file&lt;br /&gt;
1. Or a .LIB library file that contains the .OBJ file as a part of it.&lt;br /&gt;
1 that contains the missing routine Foo. The linker&lt;br /&gt;
combines the compilands into a final executable image--the .EXE file--that&lt;br /&gt;
contains the routine Foo as well as the routines that call it. During the&lt;br /&gt;
combination process, the linker adjusts the calls to Foo, which had been&lt;br /&gt;
undefined external references, to point to the place in the .EXE file where&lt;br /&gt;
the linker relocated the Foo routine. This process is diagramed in Figure&lt;br /&gt;
7-1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      .OBJ                            .LIB&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³               ³               ³  Foo  ÄÄÄÄÄÄ  ³&lt;br /&gt;
³   Call Foo    ³               ³       ÄÄÄÄÄÄ  ³&lt;br /&gt;
³               ³               ³       ÄÄÄÄÄÄ  ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ               ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ      .EXE file&lt;br /&gt;
        ³                               ³          ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ÀÄÄÄÄÄÄÄÄÄ¿           ÚÄÄÄÄÄÄÄÄÄÙ          ³               ³&lt;br /&gt;
                  ³     +     ³                    ³   Call �ÄÄÄÄÄÄÅÄ¿&lt;br /&gt;
                ÚÄ�ÄÄÄÄÄÄÄÄÄÄÄ�Ä¿                  ³               ³ ³&lt;br /&gt;
                ³               ³                  ³               ³ ³&lt;br /&gt;
                ³    Linker     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³               ³ ³&lt;br /&gt;
                ³               ³                  ³               ³ ³&lt;br /&gt;
                ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ                  ³               ³ ³&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ �ÄÄÄÄÄÄÅÄÙ&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ        ³&lt;br /&gt;
                                                   ³ ÄÄÄÄÄÄ        ³&lt;br /&gt;
                                                   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-1.  Static linking.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     In other words, with static linking you can write a program in pieces.&lt;br /&gt;
You can compile one piece at a time by having it refer to the other pieces&lt;br /&gt;
as externals. A program called a linker or a link editor combines these&lt;br /&gt;
pieces into one final .EXE image, fixing up the external references (that&lt;br /&gt;
is, references between one piece and another) that those pieces contain.&lt;br /&gt;
     Writing and compiling your program piecemeal is useful, but the&lt;br /&gt;
primary advantage of static linking is that you can use it to reference a&lt;br /&gt;
standard set of subroutines--a subroutine library--without compiling or&lt;br /&gt;
even possessing the source code for those subroutines. Nearly all high-&lt;br /&gt;
level language packages come with one or more standard runtime libraries&lt;br /&gt;
that contain various useful subroutines that the compiler can call&lt;br /&gt;
implicitly and that the programmer can call explicitly. Source for these&lt;br /&gt;
runtime libraries is rarely provided; the language supplier provides only&lt;br /&gt;
the .OBJ object files, typically in library format.&lt;br /&gt;
     To summarize, in traditional static linking the target code (that is,&lt;br /&gt;
the external subroutine) must be present at linktime and is built into the&lt;br /&gt;
final .EXE module. This makes the .EXE file larger, naturally, but more&lt;br /&gt;
important, the target code can't be changed or upgraded without relinking&lt;br /&gt;
to the main program's .OBJ files. Because the personal computer field is&lt;br /&gt;
built on commercial software whose authors don't release source or .OBJ&lt;br /&gt;
files, this relinking is out of the question for the typical end user.&lt;br /&gt;
Finally, the target code can't be shared among several (different)&lt;br /&gt;
applications that use the same library routines. This is true for two&lt;br /&gt;
reasons. First, the target code was relocated differently by the linker for&lt;br /&gt;
each client; so although the code remains logically the same for each &lt;br /&gt;
application, the address components of the binary instructions are&lt;br /&gt;
different in each .EXE file. Second, the operating system has no way of&lt;br /&gt;
knowing that these applications are using the same library, and it has no&lt;br /&gt;
way of knowing where that library is in each .EXE file. Therefore, it can't&lt;br /&gt;
avoid having duplicate copies of the library in memory.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.2  Loadtime Dynamic Linking&lt;br /&gt;
&lt;br /&gt;
The mechanical process of loadtime dynamic linking is the same as that of&lt;br /&gt;
static linking. The programmer makes an external reference to a subroutine&lt;br /&gt;
and at linktime specifies a library file (or a .OBJ file) that defines the&lt;br /&gt;
reference. The linker produces a .EXE file that OS/2 then loads and&lt;br /&gt;
executes. Behind the scenes, however, things are very much different.&lt;br /&gt;
     Step 1 is the same for both kinds of linking. The external reference&lt;br /&gt;
is compiled or assembled, resulting in a .OBJ file that contains an&lt;br /&gt;
external reference fixup record. The assembler or compiler doesn't know&lt;br /&gt;
about dynamic links; the .OBJ file that an assembler or a compiler produces&lt;br /&gt;
may be used for static links, dynamic links, or, more frequently, a&lt;br /&gt;
combination of both (some externals become dynamic links, others become&lt;br /&gt;
static links).&lt;br /&gt;
     In static linking, the linker finds the actual externally referenced&lt;br /&gt;
subroutine in the library file. In dynamic linking, the linker finds a&lt;br /&gt;
special record that defines a module name string and an entry point name&lt;br /&gt;
string. For example, in our hypothetical routine Foo, the library file&lt;br /&gt;
contains only these two name strings, not the code for Foo itself. (The&lt;br /&gt;
entry point name string doesn't have to be the name by which programs&lt;br /&gt;
called the routine.) The resultant .EXE file doesn't contain the code for&lt;br /&gt;
Foo; it contains a special dynamic link record that specifies these module&lt;br /&gt;
and entry point names for Foo. This is illustrated in Figure 7-2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿     ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     .OBJ      ³ Extern  ³     .LIB      ³     ³     .EXE      ³&lt;br /&gt;
³               ÃÄÄÄÄÄÄÄÄ�³ Foo:          ³     ³               ³&lt;br /&gt;
³   Call Foo    ³         ³ Module dlpack ³     ³      ÚÄÄÄÄ¿   ³&lt;br /&gt;
³               ³         ³ entry Foo     ³     ³ Call ³????ÃÄÄÄÅÄÄ¿&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ         ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ     ³      ÀÄÄÄÄÙ   ³  ³&lt;br /&gt;
        ³                         ³         ÚÄÄ�ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³&lt;br /&gt;
        ³                         ³         ³   ³ Reference to  ³�ÄÙ&lt;br /&gt;
        ÀÄÄÄÄÄÄÄÄÄ�  +  �ÄÄÄÄÄÄÄÄÄÙ         ³   ³ dlpack: Foo   ³&lt;br /&gt;
                ÚÄÄÄÄÄÄÄÄÄ¿                 ³   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                ³  Link   ³                 ³&lt;br /&gt;
                ÀÄÄÄÄÂÄÄÄÄÙ                 ³&lt;br /&gt;
                     ³                      ³&lt;br /&gt;
                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-2.  Dynamic linking.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When this .EXE file is run, OS/2 loads the code in the .EXE file into&lt;br /&gt;
memory and discovers the dynamic link record(s). For each dynamic link&lt;br /&gt;
module that is named, OS/2 locates the code in the system's dynamic link&lt;br /&gt;
library directory and loads it into memory (unless the module is already in&lt;br /&gt;
use; see below). The system then links the external references in the&lt;br /&gt;
application to the addresses of the called entry points. This process is&lt;br /&gt;
diagramed in Figure 7-3.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿                                    ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     .EXE      ³                                    ³     .DLL      ³&lt;br /&gt;
³               ³                                    ³  dlpack: Foo  ³&lt;br /&gt;
³     Call ÄÄÄÄÄÅÄÄ¿                                 ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³                                 ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³  dlpack: Foo  ³�ÄÙ Disk                            ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ                                    ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ&lt;br /&gt;
        ³                                                    ³&lt;br /&gt;
        ³                                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
        ³                                 ³&lt;br /&gt;
Ä Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä Ä&lt;br /&gt;
        ³                                 ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄ�ÄÄÄÄÄÄÄ¿                 ÚÄÄÄÄÄÄÄ�ÄÄÄÄÄÄÄ¿&lt;br /&gt;
³               ³      RAM        ³               ³&lt;br /&gt;
³               ³      Fixup      ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³               ³     by OS/2     ³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³     Call  ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³    ÄÄÄÄÄÄ     ³&lt;br /&gt;
³               ³                 ³               ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-3.  Loadtime dynlink fixups.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To summarize, instead of linking in the target code at linktime, the linker&lt;br /&gt;
places a module name and an entry point name into the .EXE file. When the&lt;br /&gt;
program is loaded (that is, executed), OS/2 locates the target code, loads&lt;br /&gt;
it, and does the necessary linking. Although all we're doing is postponing&lt;br /&gt;
the linkage until loadtime, this technique has several important&lt;br /&gt;
ramifications. First, the target code is not in the .EXE file but in a&lt;br /&gt;
separate dynamic link library (.DLL) file. Thus, the .EXE file is smaller&lt;br /&gt;
because it contains only the name of the target code, not the code itself.&lt;br /&gt;
You can change or upgrade the target code at any time simply by replacing&lt;br /&gt;
this .DLL file. The next time a referencing application is loaded,&lt;br /&gt;
2. With some restrictions. See 7.11.2 Dynlink Life, Death, and Sharing.&lt;br /&gt;
2 it is&lt;br /&gt;
linked to the new version of the target code. Finally, having the target&lt;br /&gt;
code in a .DLL file paves the way for automatic code sharing. OS/2 can&lt;br /&gt;
easily understand that two applications are using the same dynlink code&lt;br /&gt;
because it loaded and linked that code, and it can use this knowledge to&lt;br /&gt;
share the pure segments of that dynlink package rather than loading&lt;br /&gt;
duplicate copies.&lt;br /&gt;
     A final advantage of dynamic linking is that it's totally invisible to&lt;br /&gt;
the user, and it can even be invisible to the programmer. You need to&lt;br /&gt;
understand dynamic linking to create a dynamic link module, but you can use&lt;br /&gt;
one without even knowing that it's not an ordinary static link. The one&lt;br /&gt;
disadvantage of dynamic linking is that programs sometimes take longer to&lt;br /&gt;
load into memory than do those linked with static linking. The good news&lt;br /&gt;
about dynamic linking is that the target code(s) are separate from the main&lt;br /&gt;
.EXE file; this is also the bad news. Because the target code(s) are&lt;br /&gt;
separate from the main .EXE file, a few more disk operations may be&lt;br /&gt;
necessary to load them.&lt;br /&gt;
     The actual performance ramifications depend on the kind of dynlink&lt;br /&gt;
module that is referenced and whether this .EXE file is the first to&lt;br /&gt;
reference the module. This is discussed in more detail in 7.11&lt;br /&gt;
Implementation Details.&lt;br /&gt;
     Although this discussion has concentrated on processes calling dynlink&lt;br /&gt;
routines, dynlink routines can in fact be called by other dynlink routines.&lt;br /&gt;
When OS/2 loads a dynlink routine in response to a process's request, it&lt;br /&gt;
examines that routine to see if it has any dynlink references of its own.&lt;br /&gt;
Any such referenced dynlink routines are also loaded and so on until no&lt;br /&gt;
unsatisfied dynlink references remain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.3  Runtime Dynamic Linking&lt;br /&gt;
&lt;br /&gt;
The dynamic linking that we have been describing is called load-time&lt;br /&gt;
dynamic linking because it occurs when the .EXE file is loaded. All dynamic&lt;br /&gt;
link names need not appear in the .EXE file at loadtime; a process can link&lt;br /&gt;
itself to a dynlink package at runtime as well. Runtime dynamic linking&lt;br /&gt;
works exactly like loadtime dynamic linking except that the process creates&lt;br /&gt;
the dynlink module and entry point names at runtime and then passes them to&lt;br /&gt;
OS/2 so that OS/2 can locate and load the specified dynlink code.&lt;br /&gt;
     Runtime linking takes place in four steps.&lt;br /&gt;
&lt;br /&gt;
     1.  The process issues a DosLoadModule call to tell OS/2 to locate and&lt;br /&gt;
         load the dynlink code into memory.&lt;br /&gt;
&lt;br /&gt;
     2.  The DosGetProcAddr call is used to obtain the addresses of the&lt;br /&gt;
         routines that the process wants to call.&lt;br /&gt;
&lt;br /&gt;
     3.  The process calls the dynlink library entry points by means of an&lt;br /&gt;
         indirect call through the address returned by DosGetProcAddr.&lt;br /&gt;
&lt;br /&gt;
     4.  When the process has no more use for the dynlink code, it can call&lt;br /&gt;
         DosFreeModule to release the dynlink code. After this call, the&lt;br /&gt;
         process will still have the addresses returned by DosGetProcAddr,&lt;br /&gt;
         but they will be illegal addresses; referencing them will cause a&lt;br /&gt;
         GP fault.&lt;br /&gt;
&lt;br /&gt;
     Runtime dynamic links are useful when a program knows that it will&lt;br /&gt;
want to call some dynlink routines but doesn't know which ones. For&lt;br /&gt;
example, a charting program may support four plotters, and it may want to&lt;br /&gt;
use dynlink plotter driver packages. It doesn't make sense for the&lt;br /&gt;
application to contain loadtime dynamic links to all four plotters because&lt;br /&gt;
only one will be used and the others will take up memory and swap space.&lt;br /&gt;
Instead, the charting program can wait until it learns which plotter is&lt;br /&gt;
installed and then use the runtime dynlink facility to load the appropriate&lt;br /&gt;
package. The application need not even call DosLoadModule when it&lt;br /&gt;
initializes; it can wait until the user issues a plot command before it&lt;br /&gt;
calls DosLoadModule, thereby reducing memory demands on the system.&lt;br /&gt;
     The application need not even be able to enumerate all the modules or&lt;br /&gt;
entry points that may be called. The application can learn the names of the&lt;br /&gt;
dynlink modules from another process or by looking in a configuration file.&lt;br /&gt;
This allows the user of our charting program, for example, to install&lt;br /&gt;
additional plotter drivers that didn't even exist at the time that the&lt;br /&gt;
application was written. Of course, in this example the calling sequences&lt;br /&gt;
of the dynlink plotter driver must be standardized, or the programmer must&lt;br /&gt;
devise a way for the application to figure out the proper way to call these&lt;br /&gt;
newly found routines.&lt;br /&gt;
     Naturally, a process is not limited to one runtime dynlink module;&lt;br /&gt;
multiple calls to DosLoadModule can be used to link to several dynlink&lt;br /&gt;
modules simultaneously. Regardless of the number of modules in use,&lt;br /&gt;
DosFreeModule should be used if the dynlink module will no longer be used&lt;br /&gt;
and the process intends to continue executing. Issuing DosFreeModules is&lt;br /&gt;
unnecessary if the process is about to terminate; OS/2 releases all dynlink&lt;br /&gt;
modules at process termination time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.4  Dynlinks, Processes, and Threads&lt;br /&gt;
&lt;br /&gt;
Simply put, OS/2 views dynlinks as a fancy subroutine package. Dynlinks&lt;br /&gt;
aren't processes, and they don't own any resources. A dynlink executes only&lt;br /&gt;
because a thread belonging to a client process called the dynlink code. The&lt;br /&gt;
dynlink code is executing as the client thread and process because, in the&lt;br /&gt;
eyes of the system, the dynlink is merely a subroutine that process has&lt;br /&gt;
called. Before the client process can call a dynlink package, OS/2 ensures&lt;br /&gt;
that the dynlink's segments are in the address space of the client. No ring&lt;br /&gt;
transition or context switching overhead occurs when a client calls a&lt;br /&gt;
dynlink routine; the far call to a dynlink entry point is just that--an&lt;br /&gt;
ordinary far call to a subroutine in the process's address space.&lt;br /&gt;
     One side effect is that dynlink calls are very fast; little CPU time&lt;br /&gt;
is spent getting to the dynlink package. Another side effect is no&lt;br /&gt;
separation between a client's segments and a dynlink package's segments&lt;br /&gt;
3. Subsystem dynlink packages may be sensitive to this. For&lt;br /&gt;
detailed information, see 7.11.1 Dynlink Data Security.&lt;br /&gt;
3&lt;br /&gt;
because segments belong to processes and only one process is running both&lt;br /&gt;
the client and the dynlink code. The same goes for file handles,&lt;br /&gt;
semaphores, and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5  Data&lt;br /&gt;
&lt;br /&gt;
The careful reader will have noticed something missing in this discussion&lt;br /&gt;
of dynamic linking: We've said nothing about how to handle a dynlink&lt;br /&gt;
routine's data. Subroutines linked with static links have no problem with&lt;br /&gt;
having their own static data; when the linker binds the external code with&lt;br /&gt;
the main code, it sees how much static data the external code needs and&lt;br /&gt;
allocates the necessary space in the proper data segment(s). References&lt;br /&gt;
that the external code makes to its data are then fixed up to point&lt;br /&gt;
to the proper location. Because the linker is combining all the .OBJ&lt;br /&gt;
files into a .EXE file, it can easily divide the static data segment(s)&lt;br /&gt;
among the various compilands.&lt;br /&gt;
     This technique doesn't work for dynamic link routines because their&lt;br /&gt;
code and therefore their data requirements aren't present at linktime. It's&lt;br /&gt;
possible to extend the special dynlink .OBJ file to describe the amount of&lt;br /&gt;
static data that the dynlink package will need, but it won't work.&lt;br /&gt;
4. And even if it did work, it would be a poor design because it&lt;br /&gt;
would restrict our ability to upgrade the dynlink code in the field.&lt;br /&gt;
4&lt;br /&gt;
Because the main code in each application uses different amounts of static&lt;br /&gt;
data, the data area reserved for the dynlink package would end up at a&lt;br /&gt;
different offset in each .EXE file that was built. When these .EXE files&lt;br /&gt;
were executed, the one set of shared dynlink code segments would need to&lt;br /&gt;
reference the data that resides at different addresses for each different&lt;br /&gt;
client. Relocating the static references in all dynlink code modules at&lt;br /&gt;
each occurrence of a context switch is clearly out of the question.&lt;br /&gt;
     An alternative to letting dynamic link routines have their own static&lt;br /&gt;
data is to require that their callers allocate the necessary data areas and&lt;br /&gt;
pass pointers to them upon every call. We easily rejected this scheme: It's&lt;br /&gt;
cumbersome; call statements must be written differently if they're for a&lt;br /&gt;
dynlink routine; and, finally, this hack wouldn't support subsystems, which&lt;br /&gt;
are discussed below.&lt;br /&gt;
     Instead, OS/2 takes advantage of the segmented architecture of the&lt;br /&gt;
80286. Each dynamic link routine can use one or more data segments to hold&lt;br /&gt;
its static data. Each client process has a separate set of these segments.&lt;br /&gt;
Because these segments hold only the dynlink routine's data and none of the&lt;br /&gt;
calling process's data, the offsets of the data items within that segment&lt;br /&gt;
will be the same no matter which client process is calling the dynlink&lt;br /&gt;
code. All we need do to solve our static data addressability problem is&lt;br /&gt;
ensure that the segment selectors of the dynlink routine's static data&lt;br /&gt;
segments are the same for each client process.&lt;br /&gt;
     OS/2 ensures that the dynlink library's segment selectors are the same&lt;br /&gt;
for each client process by means of a technique called the disjoint LDT&lt;br /&gt;
space. I won't attempt a general introduction to the segmented architecture&lt;br /&gt;
of the 80286, but a brief summary is in order. Each process in 80286&lt;br /&gt;
protect mode can have a maximum of 16,383 segments. These segments are&lt;br /&gt;
described in two tables: the LDT (Local Descriptor Table) and the GDT&lt;br /&gt;
(Global Descriptor Table). An application can't read from or write to these&lt;br /&gt;
tables. OS/2 manages them, and the 80286 microprocessor uses their contents&lt;br /&gt;
when a process loads selectors into its segment registers.&lt;br /&gt;
     In practice, the GDT is not used for application segments, which&lt;br /&gt;
leaves the LDT 8192 segments--or, more precisely, 8192 segment selectors,&lt;br /&gt;
which OS/2 can set up to point to memory segments. The 80286 does not&lt;br /&gt;
support efficient position-independent code, so 80286 programs contain&lt;br /&gt;
within them, as part of the instruction stream, the particular segment&lt;br /&gt;
selector needed to access a particular memory location, as well as an&lt;br /&gt;
offset within that segment. This applies to both code and data&lt;br /&gt;
references.&lt;br /&gt;
     When OS/2 loads a program into memory, the .EXE file describes the&lt;br /&gt;
number, type, and size of the program's segments. OS/2 creates these&lt;br /&gt;
segments and allocates a selector for each from the 8192 possible LDT&lt;br /&gt;
selectors. There isn't any conflict with other processes in the system, at&lt;br /&gt;
this point, because each process has its own LDT and its own private set of&lt;br /&gt;
8192 LDT selectors. After OS/2 chooses a selector for each segment, both&lt;br /&gt;
code and data, it uses a table of addresses provided in the .EXE file to&lt;br /&gt;
relocate each segment reference in the program, changing the place holder&lt;br /&gt;
value put there by the linker into the proper segment selector value. OS/2&lt;br /&gt;
never combines or splits segments, so it never has to relocate the offset&lt;br /&gt;
part of addresses, only the segment parts. Address offsets are more common&lt;br /&gt;
than segment references. Because the segment references are relatively few,&lt;br /&gt;
this relocation process is not very time-consuming.&lt;br /&gt;
     If OS/2 discovers that the process that it's loading references a&lt;br /&gt;
dynlink routine--say, our old friend Foo--the situation is more complex.&lt;br /&gt;
For example, suppose that the process isn't the first caller of Foo; Foo is&lt;br /&gt;
already in memory and already relocated to some particular LDT slots in the&lt;br /&gt;
LDT of the earlier client of Foo. OS/2 has to fill in those same slots in&lt;br /&gt;
the new process's LDT with pointers to Foo; it can't assign different LDT&lt;br /&gt;
slots because Foo's code and data have already been relocated to the&lt;br /&gt;
earlier process's slots. If the new process is already using Foo's slot&lt;br /&gt;
numbers for something else, then we are in trouble. This is a problem with&lt;br /&gt;
all of Foo's segments, both data segments and code segments.&lt;br /&gt;
     This is where the disjoint LDT space comes in. OS/2 reserves many of&lt;br /&gt;
each process's LDT slots&lt;br /&gt;
5. In version 1.0, more than half the LDT slots are reserved for&lt;br /&gt;
this disjoint area.&lt;br /&gt;
5 for the disjoint space. The same slot numbers are&lt;br /&gt;
reserved in every process's LDT. When OS/2 allocates an LDT selector for a&lt;br /&gt;
memory segment that may be shared between processes, it allocates an entry&lt;br /&gt;
from the disjoint LDT space. After a selector is allocated, that same slot&lt;br /&gt;
in all other LDTs in the system is reserved. The slot either remains empty&lt;br /&gt;
(that is, invalid) or points to this shared segment; it can have no other&lt;br /&gt;
use. This guarantees that a process that has been running for hours and&lt;br /&gt;
that has created dozens of segments can still call DosLoadModule to get&lt;br /&gt;
access to a dynlink routine; OS/2 will find that the proper slots in this&lt;br /&gt;
process's LDT are ready and waiting. The disjoint LDT space is used for all&lt;br /&gt;
shared memory objects, not just dynlink routines. Shared memory data&lt;br /&gt;
segments are also allocated from the disjoint LDT space. A process's code&lt;br /&gt;
segments are not allocated in the disjoint LDT space, yet they can still be&lt;br /&gt;
shared.&lt;br /&gt;
6. The sharing of pure segments between multiple copies of the&lt;br /&gt;
same program is established when the duplicate copies are loaded.&lt;br /&gt;
OS/2 will use the same selector to do segment mapping as it did&lt;br /&gt;
when it loaded the first copy, so these segments can be shared&lt;br /&gt;
even though their selectors are not in the disjoint space.&lt;br /&gt;
6 Figure 7-4 illustrates the disjoint LDT concept. Bullets in the&lt;br /&gt;
shaded selectors denote reserved but invalid disjoint selectors. These are&lt;br /&gt;
reserved in case that process later requests access to the shared memory&lt;br /&gt;
segments that were assigned those disjoint slots. Only process A is using&lt;br /&gt;
the dynlink package DLX, so its assigned disjoint LDT slots are reserved&lt;br /&gt;
for it in Process B's LDT as well as in the LDT of all other processes in&lt;br /&gt;
the system. Both processes are using the dynlink package DLY.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 Process A                         Process B&lt;br /&gt;
Segment table                     Segment table&lt;br /&gt;
 (LDT) for A                       (LDT) for B&lt;br /&gt;
  ÚÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄ¿ Process    ÚÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³       ÃÄÄÄ´        ³ A's        ³       ³ ÚÄ´        ³ Process&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ segments   ÃÄÄÄÄÄÄÄ´ ³ ³        ³ B's&lt;br /&gt;
  ³°°°°°°°³   ÀÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄ¿ ³°°°°°°°³ ³ ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ÚÄÄÄÄÄÄÄÄÄÄÄÄ´        ³ ÃÄÄÄÄÄÄÄ´ ³            ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³       ³ ³            ³        ³ ³       ÃÄÙ            ³        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³ ÚÄÄÄÄÄÄÄÄ¿ ÀÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄ´    ÚÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ³°°°°°°°³ ³Ú´        ³            ³°°°°°°°³    ³         ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³³³        ³            ÃÄÄÄÄÄÄÄ´    ³&lt;br /&gt;
  ³       ÃÄÙ³ÀÄÄÄÄÄÄÄÄÙ            ³       ÃÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´  ³                      ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°³  ³                      ³°°°°°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´  ³                      ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ÃÄÄÙ                      ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°�°°°³                         ³°°°°°°°ÃÄÄÄ´        ³ Dynlink&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´   ³        ³ DLZ's&lt;br /&gt;
  ³       ³                         ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°�°°°³                         ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´              ³        ³&lt;br /&gt;
  ³       ³                         ³       ³              ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿ Dynlink    ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄ´        ³ DLX's      ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ segments   ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³   ÀÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄ¿ ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´       ÚÄÄÄÄÄÄ´        ³ ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄÄÄÄÄÙ      ³        ³ ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿ ÀÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³ ÚÄ´        ³            ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´ ³ ³        ³            ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°ÃÄÙ ÀÄÄÄÄÄÄÄÄÙ            ³°°°�°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³                         ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³°°°°°°°³                         ³°°°°°°°³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´                         ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
  ³       ³                         ³       ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿            ÃÄÄÄÄÄÄÄ´   ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄ´        ³ Dynlink    ³°°°°°°°ÃÄÄÄ´        ³ Dynlink&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´   ³        ³ DLY's      ÃÄÄÄÄÄÄÄ´   ³        ³ DLY's&lt;br /&gt;
  ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments   ³       ³   ÀÄÄÄÄÄÄÄÄÙ segments&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿ ÃÄÄÄÄÄÄÄ´              ÚÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³ ³°°°°°°°ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´        ³&lt;br /&gt;
  ÃÄÄÄÄÄÄÄ´              ³        ³ ÃÄÄÄÄÄÄÄ´              ³        ³&lt;br /&gt;
  ³       ³              ÀÄÄÄÄÄÄÄÄÙ ³       ³              ÀÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
  ÀÄÄÄÄÄÄÄÙ                         ÀÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-4.  The disjoint LDT space.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5.1  Instance Data&lt;br /&gt;
OS/2 supports two types of data segments for dynlink routines--instance&lt;br /&gt;
and global. Instance data segments hold data specific to each instance of&lt;br /&gt;
the dynlink routine. In other words, a dynlink routine has a separate set&lt;br /&gt;
of instance data segments for each process using it. The dynlink code has&lt;br /&gt;
no difficulty addressing its data; the code can reference the data segment&lt;br /&gt;
selectors as immediate values. The linker and OS/2's loader conspire so&lt;br /&gt;
that the proper selector value is in place when the code executes.&lt;br /&gt;
     The use of instance data segments is nearly invisible both to the&lt;br /&gt;
client process and to the dynlink code. The client process simply calls the&lt;br /&gt;
dynlink routine, totally unaffected by the presence or absence of the&lt;br /&gt;
routine's instance data segment(s). A dynlink routine can even return&lt;br /&gt;
addresses of items in its data segments to the client process. The client&lt;br /&gt;
cannot distinguish between a dynlink routine and a statically linked one.&lt;br /&gt;
Likewise, the code that makes up the dynlink routine doesn't need to do&lt;br /&gt;
anything special to use its instance data segments. The dynlink code was&lt;br /&gt;
assembled or compiled with its static data in one or more segments; the&lt;br /&gt;
code itself references those segments normally. The linker and OS/2 handle&lt;br /&gt;
all details of allocating the disjoint LDT selectors, loading the segments,&lt;br /&gt;
fixing up the references, and so on.&lt;br /&gt;
     A dynlink routine that uses only instance data segments (or no data&lt;br /&gt;
segments at all) can be written as a single client package, as would be a&lt;br /&gt;
statically linked subroutine. Although such a dynlink routine may have&lt;br /&gt;
multiple clients, the presence of multiple clients is invisible to the&lt;br /&gt;
routine itself. Each client has a separate copy of the instance data&lt;br /&gt;
segment(s). When a new client is created, OS/2 loads virgin copies of the&lt;br /&gt;
instance data segments from the .DLL file. The fact that OS/2 is sharing&lt;br /&gt;
the pure code segments of the routine has no effect on the operation of the&lt;br /&gt;
routine itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.5.2  Global Data&lt;br /&gt;
The second form of data segment available to a dynlink routine is a global&lt;br /&gt;
data segment. A global data segment, as the name implies, is not duplicated&lt;br /&gt;
for each client process. There is only one copy of each dynlink module's&lt;br /&gt;
global data segment(s); each client process is given shared access to that&lt;br /&gt;
segment. The segment is loaded only once--when the dynlink package is first&lt;br /&gt;
brought into memory to be linked with its first client process. Global data&lt;br /&gt;
segments allow a dynlink routine to be explicitly aware of its multiple&lt;br /&gt;
clients because changes to a global segment made by calls from one client&lt;br /&gt;
process are visible to the dynlink code when called from another client&lt;br /&gt;
process. Global data segments are provided to support subsystems, which are&lt;br /&gt;
discussed later. Figure 7-5 illustrates a dynlink routine with both&lt;br /&gt;
instance and global data segments.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³                                             ³&lt;br /&gt;
³               Code Segment(s)               ³&lt;br /&gt;
³                                             ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³              ³              ÚÁÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³&lt;br /&gt;
³    Global    ³             ÚÁÄÄÄÄÄÄÄÄÄÄÄÄÄ¿³³&lt;br /&gt;
³     data     ³             ³              ³³³&lt;br /&gt;
³  segment(s)  ³             ³   Instance   ³³³&lt;br /&gt;
³              ³             ³     data     ³³³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ             ³  segment(s)  ³ÃÙ&lt;br /&gt;
                             ³              ÃÙ&lt;br /&gt;
                             ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-5.  Dynlink segments.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.6  Dynamic Link Packages As Subroutines&lt;br /&gt;
&lt;br /&gt;
Dynamic link subroutines (or packages) generally fall into two categories--&lt;br /&gt;
subroutines and subsystems. As we discussed earlier, a dynamic link&lt;br /&gt;
subroutine is written and executes in much the same way as a statically&lt;br /&gt;
linked subroutine. The only difference is in the preparation of the dynamic&lt;br /&gt;
link library file, which contains the actual subroutines, and in the&lt;br /&gt;
preparation of the special .OBJ file, to which client programs can link.&lt;br /&gt;
During execution, both the dynlink routines and the client routines can use&lt;br /&gt;
their own static data freely, and they can pass pointers to their data&lt;br /&gt;
areas back and forth to each other. The only difference between static&lt;br /&gt;
linking and dynamic linking, in this model, is that the dynlink routine&lt;br /&gt;
cannot reference any external symbols that the client code defines, nor can&lt;br /&gt;
the client externally reference any dynlink package symbols other than the&lt;br /&gt;
module entry points. Figure 7-6 illustrates a dynamic link routine being&lt;br /&gt;
used as a subroutine. The execution environment is nearly identical to that&lt;br /&gt;
of a traditional statically linked subroutine; the client and the&lt;br /&gt;
subroutine each reference their own static data areas, all of which are&lt;br /&gt;
contained in the process's address space. Note that a dynlink package can&lt;br /&gt;
reference the application's data and the application can reference the&lt;br /&gt;
dynlink package's data, but only if the application or the dynlink package&lt;br /&gt;
passes a pointer to its data to the other.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                         Process address space&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³            far calls                           far calls            ³&lt;br /&gt;
³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ far ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³&lt;br /&gt;
³ ³            ÃÄÄ�³            ³calls³  Dynlink   ÃÄÄ�³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP code  ³   ³  APP code  ÃÄÄÄÄ�³   code     ³   ³   code     ³ ³&lt;br /&gt;
³ ³ segment #1 ³�ÄÄ´ segment #n ³     ³ segment #1 ³�ÄÄ´ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ     ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ ³&lt;br /&gt;
³    ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³           ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³    ³&lt;br /&gt;
³    ³     ³ ³     references³           ³     ³ ³     references³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³           ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³    ³&lt;br /&gt;
³ ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÚÄÄÄ�ÄÄÄÄÄ�ÄÄ¿     ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÚÄÄÄ�ÄÄÄÄÄ�ÄÄ¿ ³&lt;br /&gt;
³ ³            ³   ³            ³     ³  Dynlink   ³   ³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP data  ³   ³  APP data  ³     ³   data     ³   ³   data     ³ ³&lt;br /&gt;
³ ³ segment #1 ³   ³ segment #n ³     ³ segment #1 ³   ³ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-6.  Dynamic link routines as subroutines.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.7  Subsystems&lt;br /&gt;
&lt;br /&gt;
The term dynlink subsystems refers to the design and intended function of a&lt;br /&gt;
particular style of dynlink package and is somewhat artificial. Although&lt;br /&gt;
OS/2 provides special features to help support subsystems, OS/2 does not&lt;br /&gt;
actually classify dynlink modules as subroutines or subsystems; subsystem&lt;br /&gt;
is merely a descriptive term.&lt;br /&gt;
     The term subsystem refers to a dynlink module that provides a set of&lt;br /&gt;
services built around a resource.&lt;br /&gt;
7. In the most general sense of the word. I don't mean a&lt;br /&gt;
&amp;quot;presentation manager resource object.&amp;quot;&lt;br /&gt;
7 For example, OS/2's VIO dynlink entry&lt;br /&gt;
points are considered a dynlink subsystem because they provide a set of&lt;br /&gt;
services to manage the display screen. A subsystem usually has to manage a&lt;br /&gt;
limited resource for an effectively unlimited number of clients; VIO does&lt;br /&gt;
this, managing a single physical display controller and a small number of&lt;br /&gt;
screen groups for an indefinite number of clients.&lt;br /&gt;
     Because subsystems generally manage a limited resource, they have one&lt;br /&gt;
or more global data segments that they use to keep information about the&lt;br /&gt;
state of the resource they're controlling; they also have buffers, flags,&lt;br /&gt;
semaphores, and so on. Per-client work areas are generally kept in instance&lt;br /&gt;
data segments; it's best to reserve the global data segment(s) for global&lt;br /&gt;
information. Figure 7-7 illustrates a dynamic link routine being used as a&lt;br /&gt;
subsystem. A dynlink subsystem differs from a dynlink being used as a&lt;br /&gt;
subroutine only by the addition of a static data segment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                         Process address space&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³            far calls                           far calls            ³&lt;br /&gt;
³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ far ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿   ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³&lt;br /&gt;
³ ³            ÃÄÄ�³            ³calls³  Dynlink   ÃÄÄ�³  Dynlink   ³ ³&lt;br /&gt;
³ ³  APP code  ³   ³  APP code  ÃÄÄÄÄ�³   code     ³   ³   code     ³ ³&lt;br /&gt;
³ ³ segment #1 ³�ÄÄ´ segment #n ³     ³ segment #1 ³�ÄÄ´ segment #n ³ ³&lt;br /&gt;
³ ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ     ÀÄÄÂÄÄÄÄÄÄÄÂÄÙ   ÀÄÂÄÄÄÄÄÄÄÂÄÄÙ ³&lt;br /&gt;
³    ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³           ³     ÚÄÅÄÄÄÄÄÄÄÙ data  ³    ³&lt;br /&gt;
³    ³     ³ ³     references³           ³     ³ ³     references³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ³               ³           ³     ³ ³               ³    ³&lt;br /&gt;
³    ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³           ³     ³ ÀÄÄÄÄÄÄÄÄÄ¿     ³    ³&lt;br /&gt;
³ ÚÍÍ�ÍÍÍÍÍ�ÍÍÍ»   ÚÍÍÍ�ÍÍÍÍÍ�ÍÍ»     ÚÄÄ�ÄÄÄÄÄ�ÄÄÄ¿   ÖÄÄÄÁÄÄÄÄÄÁÄÄ¿ ³&lt;br /&gt;
³ ³            º   ³            º     ³  global    ³   º instance   ³ ³&lt;br /&gt;
³ ³  APP data  º   ³  APP data  º     ³   data     ³   º   data     ³ ³&lt;br /&gt;
³ ³ segment #1 º   ³ segment #n º     ³ segment    ³   º segment    ³ ³&lt;br /&gt;
³ ÀÄÄÄÄÄÄÄÄÄÄÄÄ½   ÀÄÄÄÄÄÄÄÄÄÄÄÄ½     ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ   ÈÍÍÍÍÍÍÍÍÍÍÍÍÙ ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-7.  Dynamic link routines as subsystems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.7.1  Special Subsystem Support&lt;br /&gt;
Two OS/2 features are particularly valuable to subsystems: global data&lt;br /&gt;
segments (which we've already discussed) and special client initialization&lt;br /&gt;
and termination support. Clearly, if a subsystem is going to manage a&lt;br /&gt;
resource, keeping track of its clients in a global data segment, it needs&lt;br /&gt;
to know when new clients arrive and when old clients terminate. The simple&lt;br /&gt;
dynlink subroutine model doesn't provide this information in a reliable&lt;br /&gt;
fashion. A subsystem undoubtedly has initialize and terminate entry points,&lt;br /&gt;
but client programs may terminate without having called a subsystem's&lt;br /&gt;
terminate entry point. Such a failure may be an error on the part of the&lt;br /&gt;
client, but the system architecture decrees that errors should be&lt;br /&gt;
localized; it's not acceptable for a bug in a client process to be able to&lt;br /&gt;
hang up a subsystem and thus all its clients as well.&lt;br /&gt;
     The two forms of subsystem initialization are global and instance. A&lt;br /&gt;
subsystem can specify either service but not both. If global initialization&lt;br /&gt;
is specified, the initialization entry point is called only once per&lt;br /&gt;
activation of the subsystem. When the subsystem dynlink package is first&lt;br /&gt;
referenced, OS/2 allocates the subsystem's global data segment(s), taking&lt;br /&gt;
their initial values from the .DLL file. OS/2 then calls the subsystem's&lt;br /&gt;
global initialization entry point so that the module can do its one-time&lt;br /&gt;
initialization. The thread that is used to call the initialization entry&lt;br /&gt;
point belongs to that first client process,&lt;br /&gt;
8. The client process doesn't explicitly call a dynlink package's&lt;br /&gt;
initialization entry points. OS/2 uses its godlike powers to&lt;br /&gt;
borrow a thread for the purpose. The mechanism is invisible to the&lt;br /&gt;
client program. It goes without saying, we hope, that it would be&lt;br /&gt;
extremely rude to the client process, not to say damaging, were&lt;br /&gt;
the dynlink package to refuse to return that initialization thread&lt;br /&gt;
or if it were to damage it in some way, such as lowering its&lt;br /&gt;
priority or calling DosExit with it!&lt;br /&gt;
8 so the first client's instance&lt;br /&gt;
data segments are also set up and may be used by the global initialization&lt;br /&gt;
process. This means that although the dynlink subsystem is free to open&lt;br /&gt;
files, read and write their contents, and close them again, it may not open&lt;br /&gt;
a handle to a file, store the handle number in a global data segment, and&lt;br /&gt;
expect to use that handle in the future.&lt;br /&gt;
     Remember, subsystems don't own resources; processes own resources.&lt;br /&gt;
When a dynlink package opens a file, that file is open only for that one&lt;br /&gt;
client process. That handle has meaning only when that particular client is&lt;br /&gt;
calling the subsystem code. If a dynlink package were to store process A's&lt;br /&gt;
handle number in a global data segment and then attempt to do a read from&lt;br /&gt;
that handle when running as process B, at best the read would fail with&lt;br /&gt;
&amp;quot;invalid handle&amp;quot;; at worst some unrelated file of B's would be molested.&lt;br /&gt;
And, of course, when client process A eventually terminates, the handle&lt;br /&gt;
becomes invalid for all clients.&lt;br /&gt;
     The second form of initialization is instance initialization. The&lt;br /&gt;
instance initialization entry point is called in the same way as the global&lt;br /&gt;
initialization entry point except that it is called for every new client&lt;br /&gt;
when that client first attaches to the dynlink package. Any instance data&lt;br /&gt;
segments that exist will already be allocated and will have been given&lt;br /&gt;
their initial values from the .DLL file. The initialization entry point for&lt;br /&gt;
a loadtime dynlink is called before the client's code begins executing. The&lt;br /&gt;
initialization entry point for a runtime dynlink is called when the client&lt;br /&gt;
calls the DosLoadModule function. A dynlink package may not specify both&lt;br /&gt;
global and instance initialization; if it desires both, it should specify&lt;br /&gt;
instance initialization and use a counter in one of its global data&lt;br /&gt;
segments to detect the first instance initialization.&lt;br /&gt;
     Even more important than initialization control is termination&lt;br /&gt;
control. In its global data area, a subsystem may have records, buffers, or&lt;br /&gt;
semaphores on behalf of a client process. It may have queued-up requests&lt;br /&gt;
from that client that it needs to purge when the client terminates. The&lt;br /&gt;
dynlink package need not release instance data segments; because these&lt;br /&gt;
belong to the client process, they are destroyed when the client&lt;br /&gt;
terminates. The global data segments themselves are released if this is the&lt;br /&gt;
dynlink module's last client, so the module may want to take this last&lt;br /&gt;
chance to update a log file, release a system semaphore, and so on.&lt;br /&gt;
     Because a dynlink routine runs as the calling client process, it could&lt;br /&gt;
use DosSetSigHandler to intercept the termination signal. This should never&lt;br /&gt;
be done, however, because the termination signal is not activated for all&lt;br /&gt;
causes of process termination. For example, if the process calls DosExit,&lt;br /&gt;
the termination signal is not sent. Furthermore, there can be only one&lt;br /&gt;
handler per signal type per process. Because client processes don't and&lt;br /&gt;
shouldn't know what goes on inside a dynlink routine, the client process&lt;br /&gt;
and a dynlink routine may conflict in the use of the signal. Such a&lt;br /&gt;
conflict may also occur between two dynlink packages.&lt;br /&gt;
     Using DosExitList service prevents such a collision. DosExitList&lt;br /&gt;
allows a process to specify one or more subroutine addresses that will be&lt;br /&gt;
called when the process terminates. Addresses can be added to and removed&lt;br /&gt;
from the list. DosExitList is ideally suited for termination control. There&lt;br /&gt;
can be many such addresses, and the addresses are called under all&lt;br /&gt;
termination conditions. Both the client process and the subsystem dynlinks&lt;br /&gt;
that it calls can have their own termination routine or routines.&lt;br /&gt;
DosExitList is discussed in more detail in 16.2 Data Integrity.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.8  Dynamic Links As Interfaces to Other Processes&lt;br /&gt;
&lt;br /&gt;
Earlier, I mentioned that dynlink subsystems have difficulty dealing with&lt;br /&gt;
resources--other than global memory--because resource ownership and access&lt;br /&gt;
are on a per-process basis. Life as a dynlink subsystem can be&lt;br /&gt;
schizophrenic. Which files are open, which semaphores are owned and so on&lt;br /&gt;
depends on which client is running your code at the moment. Global memory&lt;br /&gt;
is different; it's the one resource that all clients own jointly. The&lt;br /&gt;
memory remains as long as the client count doesn't go to zero.&lt;br /&gt;
     One way to deal with resource issues is for a dynlink package to act&lt;br /&gt;
as a front end for a server process. During module initialization, the&lt;br /&gt;
dynlink module can check a system semaphore to see whether the server&lt;br /&gt;
process is already running and, if not, start it up. It needs to do this&lt;br /&gt;
with the &amp;quot;detach&amp;quot; form of DosExecPgm so that the server process doesn't&lt;br /&gt;
appear to the system as a child of the subsystem's first client. Such a&lt;br /&gt;
mistake could mean that the client's parent thinks that the command subtree&lt;br /&gt;
it founded by running the client never terminates because the server&lt;br /&gt;
process appears to be part of the command subtree (see Figure 7-8).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
   ³   Grandparent   ³       ³   Grandparent   ³&lt;br /&gt;
   ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
            ³                         ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿      ÚÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³°°°°°°°°°°°³°°°°°°°°°°°³ ³°°°°°°°°°°°³°°°°°°°°°°°³   Ú �³  Daemon  ³&lt;br /&gt;
³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³ ³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³   |  ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³°°³     Process     ³°°³ ³°°³     Process     ³Ä Å Ä Ù   &lt;br /&gt;
³°°ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ°°³ ³°°ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ°°³&lt;br /&gt;
³°°°°°°°°°°°³°°°°°°°°°°°³ ³°°° Command subtree °°°³&lt;br /&gt;
³°°ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ¿°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°³      Daemon     ³°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°° Command subtree °°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
³°°°°°°°°°°°°°°°°°°°°°°°³ ³°°°°°°°°°°°°°°°°°°°°°°°³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-8.  Dynlink daemon initiation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When the server process is running, the dynlink subsystem can forward&lt;br /&gt;
some or all requests to it by one of the many IPC facilities. For example,&lt;br /&gt;
a database subsystem might want to use a dedicated server process to hold&lt;br /&gt;
open the database file and do reads and writes to it. It might keep buffers&lt;br /&gt;
and ISAM directories in a shared memory segment to which the dynlink&lt;br /&gt;
subsystem requests access for each of its clients; then requests that can&lt;br /&gt;
be satisfied by data from these buffers won't require the IPC to the server&lt;br /&gt;
process.&lt;br /&gt;
     The only function of some dynlink packages is to act as a procedural&lt;br /&gt;
interface to another process. For example, a spreadsheet program might&lt;br /&gt;
provide an interface through which other applications can retrieve data&lt;br /&gt;
values from a spreadsheet. The best way to do this is for the spreadsheet&lt;br /&gt;
package to contain a dynamic link library that provides clients a&lt;br /&gt;
procedural interface to the spreadsheet process. The library routine itself&lt;br /&gt;
will invoke a noninteractive copy (perhaps a special subset .EXE) of the&lt;br /&gt;
spreadsheet to recover the information, passing it back to the client via&lt;br /&gt;
IPC. Alternatively, the retrieval code that understands the spreadsheet&lt;br /&gt;
data formats could be in the dynlink package itself because that package&lt;br /&gt;
ships with the spreadsheet and will be upgraded when the spreadsheet is. In&lt;br /&gt;
this case, the spreadsheet itself could use the package instead of&lt;br /&gt;
duplicating the functionality in its own .EXE file. In any case, the&lt;br /&gt;
implementation details are hidden from the client process; the client&lt;br /&gt;
process simply makes a procedure call that returns the desired data.&lt;br /&gt;
     Viewed from the highest level, this arrangement is simple: A client&lt;br /&gt;
process uses IPC to get service from a server process via a subroutine&lt;br /&gt;
library. From the programmer's point of view, though, the entire mechanism&lt;br /&gt;
is encapsulated in the dynlink subsystem's interface. A future upgrade to&lt;br /&gt;
the dynlink package may use an improved server process and different forms&lt;br /&gt;
of IPC to talk to it but retain full binary compatibility with the existing&lt;br /&gt;
client base. Figure 7-9 illustrates a dynlink package being used as an&lt;br /&gt;
interface to a daemon process. The figure shows the dynlink package&lt;br /&gt;
interfacing with the daemon process by means of a shared memory segment and&lt;br /&gt;
some other form of IPC, perhaps a named pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                                   client process   |   daemon process&lt;br /&gt;
                                                    |&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿    Far call     ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿       |        ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³    APP     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³  Dynlink   ³�ÄÄÄÄÄÄ|ÄÄÄÄÄÄÄÄ´   Daemon   ³&lt;br /&gt;
³    code    ³                 ³    code    ³      IPC       ³    code    ³&lt;br /&gt;
³ segment(s) ³                 ³ segment(s) ÃÄÄÄÄÄÄÄ|ÄÄÄÄÄÄÄ�³ segment(s) ³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÂÄÄÄÂÄÄÙ       |        ÀÄÄÂÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
      ³                              ³   ³          |           ³   ³&lt;br /&gt;
      ³                              ³   ÀÄÄÄÄÄÄÄ¿  |   ÚÄÄÄÄÄÄÄÙ   ³&lt;br /&gt;
      ³                              ³           ³  |   ³           ³&lt;br /&gt;
ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿                 ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿ ÚÄÄ�ÄÄ|ÄÄÄ�ÄÄ¿ ÚÄÄÄÄÄÄ�ÄÄÄÄÄ¿&lt;br /&gt;
³    APP     ³                 ³  Dynlink   ³ ³     |      ³ ³   Daemon   ³&lt;br /&gt;
³    data    ³                 ³    data    ³ ³   Shared   ³ ³    data    ³&lt;br /&gt;
³ segment(s) ³                 ³ segment(s) ³ ³   memory   ³ ³ segment(s) ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄ|ÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                                                    |&lt;br /&gt;
&lt;br /&gt;
Figure 7-9.  Dynamic link routines as daemon interfaces.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.9  Dynamic Links As Interfaces to the Kernel&lt;br /&gt;
&lt;br /&gt;
We've seen how dynlink libraries can serve as simple subroutine libraries,&lt;br /&gt;
how they can serve as subsystems, and how they can serve as interfaces to&lt;br /&gt;
other processes. OS/2 has one more trick up its sleeve: Dynlink libraries&lt;br /&gt;
can also serve as interfaces to OS/2 itself.&lt;br /&gt;
     Some OS/2 calls are actually implemented as simple library routines.&lt;br /&gt;
For example, DosErrClass is implemented in OS/2 version 1.0 as a simple&lt;br /&gt;
library routine. It takes an error code and locates, in a table, an&lt;br /&gt;
explanatory text string, an error classification, and a recommended action.&lt;br /&gt;
Services such as these were traditionally part of the kernel of operating&lt;br /&gt;
systems, not because they needed to use privileged instructions, but&lt;br /&gt;
because their error tables needed to be changed each time an upgrade to the&lt;br /&gt;
operating system was released. If the service has been provided as a&lt;br /&gt;
statically linked subroutine, older applications running on newer releases&lt;br /&gt;
would receive new error codes that would not be in the library code's&lt;br /&gt;
tables.&lt;br /&gt;
     Although OS/2 implements DosErrClass as a library routine, it's a&lt;br /&gt;
dynlink library routine, and the .DLL file is bundled with the operating&lt;br /&gt;
system itself. Any later release of the system will contain an upgraded&lt;br /&gt;
version of the DosErrClass routine, one that knows about new error codes.&lt;br /&gt;
Consequently, the dynlink facility provides OS/2 with a great deal of&lt;br /&gt;
flexibility in packaging its functionality.&lt;br /&gt;
     Some functions, such as &amp;quot;open file&amp;quot; or &amp;quot;allocate memory,&amp;quot; can't be&lt;br /&gt;
implemented as ordinary subroutines. They need access to key internal data&lt;br /&gt;
structures, and these structures are of course protected so that they can't&lt;br /&gt;
be changed by unprivileged code. To get these services, the processor must&lt;br /&gt;
make a system call, entering the kernel code in a very controlled fashion&lt;br /&gt;
and there running with sufficient privilege to do its work. This privilege&lt;br /&gt;
transition is via a call gate--a feature of the 80286/80386 hardware. A&lt;br /&gt;
program calls a call gate exactly as it performs an ordinary far call;&lt;br /&gt;
special flags in the GDT and LDT tell the processor that this is a call&lt;br /&gt;
gate rather than a regular call.&lt;br /&gt;
     In OS/2, system calls are indistinguishable from ordinary dynlink&lt;br /&gt;
calls. All OS/2 system calls are defined in a dynlink module called&lt;br /&gt;
DosCalls. When OS/2 fixes up dynlink references to this module, it consults&lt;br /&gt;
a special table, built into OS/2, of resident functions. If the function is&lt;br /&gt;
not listed in this table, then an ordinary dynlink is set up. If the&lt;br /&gt;
function is in the table, OS/2 sets up a call gate call in place of the&lt;br /&gt;
ordinary dynlink call. The transparency between library and call gate&lt;br /&gt;
functions explains why passing an invalid address to an OS/2 system call&lt;br /&gt;
causes the calling process to GP fault. Because the OS/2 kernel code&lt;br /&gt;
controls and manages the GP fault mechanism, OS/2 calls that are call gates&lt;br /&gt;
could easily return an error code if an invalid address causes a GP fault.&lt;br /&gt;
If this were done, however, the behavior of OS/2 calls would differ&lt;br /&gt;
depending on their implementation: Dynlink entry points would GP fault for&lt;br /&gt;
invalid addresses;&lt;br /&gt;
9. The LAR and LSL instructions are not sufficient to prevent&lt;br /&gt;
this because another thread in that process may free a segment&lt;br /&gt;
after the LAR but before the reference.&lt;br /&gt;
9 call gate entries would return an error code. OS/2&lt;br /&gt;
prevents this dichotomy and preserves its freedom to, in future releases,&lt;br /&gt;
move function between dynlink and call gate entries by providing a uniform&lt;br /&gt;
reaction to invalid addresses. Because non-call-gate dynlink routines must&lt;br /&gt;
generate GP faults, call gate routines produce them as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.10  The Architectural Role of Dynamic Links&lt;br /&gt;
&lt;br /&gt;
Dynamic links play three major roles in OS/2: They provide the system&lt;br /&gt;
interface; they provide a high-bandwidth device interface; and they support&lt;br /&gt;
open architecture nonkernel service packages.&lt;br /&gt;
     The role of dynamic links as the system interface is clear. They&lt;br /&gt;
provide a uniform, high-efficiency interface to the system kernel as well&lt;br /&gt;
as a variety of nonkernel services. The interface is directly compatible&lt;br /&gt;
with high-level languages, and it takes advantage of special speed-&lt;br /&gt;
enhancing features of the 80286 and 80386 microprocessors.&lt;br /&gt;
10. Specifically, automatic argument passing on calls to the ring&lt;br /&gt;
0 kernel code.&lt;br /&gt;
10 It provides a&lt;br /&gt;
wide and convenient name space, and it allows the distribution of function&lt;br /&gt;
between library code and kernel code. Finally, it provides an essentially&lt;br /&gt;
unlimited expansion capability.&lt;br /&gt;
     But dynamic links do much more than act as system calls. You'll recall&lt;br /&gt;
that in the opening chapters I expressed a need for a device interface that&lt;br /&gt;
was as device independent as device drivers but without their attendant&lt;br /&gt;
overhead. Dynamic links provide this interface because they allow&lt;br /&gt;
applications to make a high-speed call to a subroutine package that can&lt;br /&gt;
directly manipulate the device (see Chapter 18, I/O Privilege Mechanism&lt;br /&gt;
and Debugging/Ptrace). The call itself is fast, and the package can specify&lt;br /&gt;
an arbitrarily wide set of parameters. No privilege or ring transition is&lt;br /&gt;
needed, and the dynlink package can directly access its client's data&lt;br /&gt;
areas. Finally, the dynlink package can use subsystem support features to&lt;br /&gt;
virtualize the device or to referee its use among multiple clients. Device&lt;br /&gt;
independence is provided because a new version of the dynlink interface can&lt;br /&gt;
be installed whenever new hardware is installed. VIO and the presentation&lt;br /&gt;
manager are examples of this kind of dynlink use. Dynlink packages have an&lt;br /&gt;
important drawback when they are being used as device driver replacements:&lt;br /&gt;
They cannot receive hardware interrupts. Some devices, such as video&lt;br /&gt;
displays, do not generate interrupts. Interrupt-driven devices, though,&lt;br /&gt;
require a true device driver. That driver can contain all of the device&lt;br /&gt;
interface function, or the work can be split between a device driver and a&lt;br /&gt;
dynlink package that acts as a front end for that device driver. See&lt;br /&gt;
Chapters 17 and 18 for further discussion of this.&lt;br /&gt;
     Dynlink routines can also act as nonkernel service packages--as an&lt;br /&gt;
open system architecture for software. Most operating systems&lt;br /&gt;
are like the early versions of the Apple Macintosh computer: They are&lt;br /&gt;
closed systems; only their creators can add features to them. Because of&lt;br /&gt;
OS/2's open system architecture, third parties and end users can add system&lt;br /&gt;
services simply by plugging in dynlink modules, just as hardware cards plug&lt;br /&gt;
into an open hardware system. The analogy extends further: Some hardware&lt;br /&gt;
cards become so popular that their interface defines a standard. Examples&lt;br /&gt;
are the Hayes modem and the Hercules Graphics Card. Third-party dynlink&lt;br /&gt;
packages will, over time, establish similar standards. Vendors will offer,&lt;br /&gt;
for example, improved database dynlink routines that are advertised as plug&lt;br /&gt;
compatible with the standard database dynlink interface, but better,&lt;br /&gt;
cheaper, and faster.&lt;br /&gt;
     Dynlinks allow third parties to add interfaces to OS/2; they also&lt;br /&gt;
allow OS/2's developers to add future interfaces. The dynlink interface&lt;br /&gt;
model allows additional functionality to be implemented as subroutines or&lt;br /&gt;
processes or even to be distributed across a network environment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11  Implementation Details&lt;br /&gt;
&lt;br /&gt;
Although dynlink routines often act very much like traditional static&lt;br /&gt;
subroutines, a programmer must be aware of some special considerations&lt;br /&gt;
involved. This section discusses some issues that must be dealt with to&lt;br /&gt;
produce a good dynlink package.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.1  Dynlink Data Security&lt;br /&gt;
We have discussed how a dynlink package runs as a subroutine of the client&lt;br /&gt;
process and that the client process has access to the dynlink package's&lt;br /&gt;
instance and global data segments.&lt;br /&gt;
11. A client process has memory access (addressability) to all of&lt;br /&gt;
the package's global segments but only to those instance data&lt;br /&gt;
segments associated with that process.&lt;br /&gt;
11 This use of the dynlink interface is&lt;br /&gt;
efficient and thus advantageous, but it's also disadvantageous because&lt;br /&gt;
aberrant client processes can damage the dynlink package's global data&lt;br /&gt;
segments.&lt;br /&gt;
     In most circumstances, accidental damage to a dynlink package's data&lt;br /&gt;
segments is rare. Unless the dynlink package returns pointers into its data&lt;br /&gt;
segments to the client process, the client doesn't &amp;quot;know&amp;quot; the dynlink&lt;br /&gt;
package's data segment selectors. The only way such a process could access&lt;br /&gt;
the dynlink's segments would be to accidentally create a random selector&lt;br /&gt;
value that matched one belonging to a dynlink package. Because the&lt;br /&gt;
majority of selector values are illegal, a process would have to be&lt;br /&gt;
very &amp;quot;lucky&amp;quot; to generate a valid dynlink package data selector before it&lt;br /&gt;
generated an unused or code segment selector.&lt;br /&gt;
12.Because if a process generates and writes with a selector that&lt;br /&gt;
is invalid or points to a code segment, the process will be&lt;br /&gt;
terminated immediately with a GP fault.&lt;br /&gt;
12 Naturally, dynlink packages&lt;br /&gt;
shouldn't use global data segments to hold sensitive data because a&lt;br /&gt;
malicious application can figure out the proper selector values.&lt;br /&gt;
     The measures a programmer takes to deal with the security issue depend&lt;br /&gt;
on the nature and sensitivity of the dynlink package. Dynlink packages that&lt;br /&gt;
don't have global data segments are at no risk; an aberrant program can&lt;br /&gt;
damage its instance data segments and thereby fail to run correctly, but&lt;br /&gt;
that's the expected outcome of a program bug. A dynlink package with global&lt;br /&gt;
data segments can minimize the risk by never giving its callers pointers&lt;br /&gt;
into its (the dynlink package's) global data segment. If the amount of&lt;br /&gt;
global data is small and merely detecting damage is sufficient, the global&lt;br /&gt;
data segments could be checksummed.&lt;br /&gt;
     Finally, if accidental damage would be grave, a dynlink package can&lt;br /&gt;
work in conjunction with a special dedicated process, as described above.&lt;br /&gt;
The dedicated process can keep the sensitive data and provide it on a per-&lt;br /&gt;
client basis to the dynlink package in response to an IPC request. Because&lt;br /&gt;
the dedicated process is a separate process, its segments are fully&lt;br /&gt;
protected from the client process as well as from all others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.2  Dynlink Life, Death, and Sharing&lt;br /&gt;
Throughout this discussion, I have referred to sharing pure segments. The&lt;br /&gt;
ability to share pure segments is an optimization that OS/2 makes for all&lt;br /&gt;
memory segments whether they are dynlink segments or an application's .EXE&lt;br /&gt;
file segments. A pure segment is one that is never modified during its&lt;br /&gt;
lifetime. All code segments (except for those created by DosCreateCSAlias)&lt;br /&gt;
are pure; read-only data segments are also pure. When OS/2 notices that&lt;br /&gt;
it's going to load two copies of the same pure segment, it performs a&lt;br /&gt;
behind-the-scenes optimization and gives the second client access to the&lt;br /&gt;
earlier copy of the segment instead of wasting memory with a duplicate&lt;br /&gt;
version.&lt;br /&gt;
     For example, if two copies of a program are run, all code segments are&lt;br /&gt;
pure; at most, only one copy of each code segment will be in memory. OS/2&lt;br /&gt;
flags these segments as &amp;quot;internally shared&amp;quot; and doesn't release them until&lt;br /&gt;
the last user has finished with the segment. This is not the same as&lt;br /&gt;
&amp;quot;shared memory&amp;quot; as it is generally defined in OS/2. Because pure segments&lt;br /&gt;
can only be read, never written, no process can tell that pure segments are&lt;br /&gt;
being shared or be affected by that sharing. Although threads from two or&lt;br /&gt;
more processes may execute the same shared code segment at the same time,&lt;br /&gt;
this is not the same as a multithreaded process. Each copy of a program has&lt;br /&gt;
its own data areas, its own stack, its own file handles, and so on. They&lt;br /&gt;
are totally independent of one another even if OS/2 is quietly sharing&lt;br /&gt;
their pure code segments among them. Unlike multiple threads within a&lt;br /&gt;
single process, threads from different processes cannot affect one another;&lt;br /&gt;
the programmer can safely ignore their possible existence in shared code&lt;br /&gt;
segments.&lt;br /&gt;
     Because the pure segments of a dynlink package are shared, the second&lt;br /&gt;
and subsequent clients of a dynlink package can load much more quickly&lt;br /&gt;
(because these pure segments don't have to be loaded from the .DLL disk&lt;br /&gt;
file). This doesn't mean that OS/2 doesn't have to &amp;quot;hit the disk&amp;quot; at all:&lt;br /&gt;
Many dynlink packages use instance data segments, and OS/2 loads a fresh&lt;br /&gt;
copy of the initial values for these segments from the .DLL file.&lt;br /&gt;
     A dynlink package's second client is its second simultaneous client.&lt;br /&gt;
Under OS/2, only processes have a life of their own. Objects such as&lt;br /&gt;
dynlink packages and shared memory segments exist only as possessions of&lt;br /&gt;
processes. When the last client process of such an object dies or otherwise&lt;br /&gt;
releases the object, OS/2 destroys it and frees up the memory. For example,&lt;br /&gt;
when the first client (since bootup) of a dynlink package references it,&lt;br /&gt;
OS/2 loads the package's code and data segments. Then OS/2 calls the&lt;br /&gt;
package's initialization routine--if the package has one. OS/2 records in&lt;br /&gt;
an internal data structure that this dynlink package has one client. If&lt;br /&gt;
additional clients come along while the first is still using the dynlink&lt;br /&gt;
package, OS/2 increments the package's user count appropriately. Each time&lt;br /&gt;
a client disconnects or dies, the user count is decremented. As long as the&lt;br /&gt;
user count remains nonzero, the package remains in existence, each client&lt;br /&gt;
sharing the original global data segments. When the client count goes to&lt;br /&gt;
zero, OS/2 discards the dynlink package's code and global data segments and&lt;br /&gt;
in effect forgets all about the package. When another client comes along,&lt;br /&gt;
OS/2 reloads the package and reloads its global data segment as if the&lt;br /&gt;
earlier use had never occurred.&lt;br /&gt;
     This mechanism affects a dynlink package only in the management of the&lt;br /&gt;
package's global data segment. The package's code segments are pure, so it&lt;br /&gt;
doesn't matter if they are reloaded from the .DLL file. The instance data&lt;br /&gt;
segments are always reinitialized for each new client, but the data in a&lt;br /&gt;
package's global data segment remains in existence only as long as the&lt;br /&gt;
package has at least one client process. When the last client releases the&lt;br /&gt;
package, the global data segment is discarded. If this is a problem for a&lt;br /&gt;
dynlink package, an associated &amp;quot;dummy&amp;quot; process (which the dynlink package&lt;br /&gt;
could start during its loadtime initialization) can reference the dynlink&lt;br /&gt;
package. As long as this process stays alive, the dynlink package and its&lt;br /&gt;
global data segments stay alive.&lt;br /&gt;
13. If you use this technique, be sure to use the detached form&lt;br /&gt;
of DosExec; see the warning in 7.8 Dynamic Links As&lt;br /&gt;
Interfaces to Other Processes.&lt;br /&gt;
13&lt;br /&gt;
     An alternative is for the dynlink package to keep track of the count&lt;br /&gt;
of its clients and save the contents of its global data segments to a disk&lt;br /&gt;
file when the last client terminates, but this is tricky. Because a process&lt;br /&gt;
may fail to call a dynlink package's &amp;quot;I'm finished&amp;quot; entry point (presumably&lt;br /&gt;
part of the dynlink package's interface) before it terminates, the dynlink&lt;br /&gt;
package must get control to write its segment via DosExitList. If the&lt;br /&gt;
client process is connected to the dynlink package via DosLoadModule (that&lt;br /&gt;
is, via runtime dynamic linking), it cannot disconnect from the package via&lt;br /&gt;
DosFreeModule as long as a DosExitList address points into the dynlink&lt;br /&gt;
package. An attempt to do so returns an error code. Typically, one would&lt;br /&gt;
expect the application to ignore this error code; but because the dynlink&lt;br /&gt;
package is still attached to the client process, it will receive&lt;br /&gt;
DosExitList service when the client eventually terminates. It's important&lt;br /&gt;
that dynlink packages which maintain client state information and therefore&lt;br /&gt;
need DosExitList also offer an &amp;quot;I'm finished&amp;quot; function. When a client calls&lt;br /&gt;
this function, the package should close it out and then remove its&lt;br /&gt;
processing address from DosExitList so that DosFreeModule can take effect&lt;br /&gt;
if the client wishes.&lt;br /&gt;
     Note that OS/2's habit of sharing in-use dynlink libraries has&lt;br /&gt;
implications for the replacement of dynlink packages. Specifically, OS/2&lt;br /&gt;
holds the dynlink .DLL file open for as long as that library has any&lt;br /&gt;
clients. To replace a dynlink library with an upgraded version,&lt;br /&gt;
you must first ensure that all clients of the old package have been&lt;br /&gt;
terminated.&lt;br /&gt;
     While we're on the subject, I'll point out that dynlink segments, like&lt;br /&gt;
.EXE file segments, can be marked (by the linker) as &amp;quot;preload&amp;quot; or &amp;quot;load on&lt;br /&gt;
demand.&amp;quot; When a dynlink module or a .EXE file is loaded, OS/2 immediately&lt;br /&gt;
loads all segments marked &amp;quot;preload&amp;quot; but usually&lt;br /&gt;
14. Segments that are loaded from removable media will be fully&lt;br /&gt;
loaded, regardless of the &amp;quot;load on demand&amp;quot; bit.&lt;br /&gt;
14 does not load any&lt;br /&gt;
segments marked &amp;quot;load on demand.&amp;quot; These segments are loaded only when (and&lt;br /&gt;
if) they are referenced. This mechanism speeds process and library loading&lt;br /&gt;
and reduces swapping by leaving infrequently used segments out of memory&lt;br /&gt;
until they are needed. Once a segment is loaded, its &amp;quot;preload&amp;quot; or &amp;quot;load on&lt;br /&gt;
demand&amp;quot; status has no further bearing; the segment will be swapped or&lt;br /&gt;
discarded without consideration for these bits.&lt;br /&gt;
     Finally, special OS/2 code keeps track of dynamic link &amp;quot;circular&lt;br /&gt;
references.&amp;quot; Because dynlink packages can call other dynlink packages,&lt;br /&gt;
package A can call package B, and package B can call package A. Even if the&lt;br /&gt;
client process C terminates, packages A and B might appear to be in use by&lt;br /&gt;
each other, and they would both stay in memory. OS/2 keeps a graph of&lt;br /&gt;
dynlink clients, both processes and other dynlink packages. When a process&lt;br /&gt;
can no longer reach a dynlink package over this graph--in other words, when&lt;br /&gt;
a package doesn't have a process for a client and when none of its client&lt;br /&gt;
packages have processes for clients and so on--the dynlink package is&lt;br /&gt;
released. Figure 7-10 illustrates a dynamic link circular reference. PA&lt;br /&gt;
and PB are two processes, and LA through LG are dynlink library routines.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿                        ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³°°°°°°°°°°°°³                        ³°°°°°°°°°°°°³&lt;br /&gt;
³°°°°°PA°°°°°³                        ³°°°°°PB°°°°°³&lt;br /&gt;
³°°°°°°°°°°°°³                        ³°°°°°°°°°°°°³&lt;br /&gt;
ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ                        ÀÄÂÄÄÄÄÄÄÄÄÂÄÙ&lt;br /&gt;
      ³                                 ³        ³&lt;br /&gt;
ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿                          ³  ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿&lt;br /&gt;
³     LA     ³�ÄÄÄÄÄÄÄ¿                 ³  ³     LF     ³&lt;br /&gt;
ÀÄÄÂÄÄÄÄÄÄÂÄÄÙ        ³                 ³  ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
   ³   ÚÄÄÁÄÄÄÄÄÄÄÄÄ¿ ³                 ³        ³&lt;br /&gt;
   ³   ³     LB     ÃÄÙ                 ³  ÚÄÄÄÄÄ�ÄÄÄÄÄÄ¿&lt;br /&gt;
   ³   ÀÄÂÄÄÄÄÄÄÄÄÄÄÙ                   ³  ³     LG     ³&lt;br /&gt;
   ³     ³  ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ  ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄ�ÄÄÄÄÄ�ÄÄ�¿           ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿          ³&lt;br /&gt;
³     LC     ÃÄÄÄÄÄÄÄÄÄÄ�³     LD     ³�ÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÄ�ÄÄÄÄÄÄÙ           ÀÄÄÄÄÄÂÄÄÄÄÄÄÙ&lt;br /&gt;
      ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 7-10.  Dynamic link circular references.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.11.3  Dynlink Side Effects&lt;br /&gt;
A well-written dynlink library needs to adhere to the OS/2 religious tenet&lt;br /&gt;
of zero side effects. A dynlink library should export to the client process&lt;br /&gt;
only its functional interface and not accidentally export side effects that&lt;br /&gt;
may interfere with the consistent execution of the client.&lt;br /&gt;
     Some possible side effects are obvious: A dynlink routine shouldn't&lt;br /&gt;
close any file handles that it didn't itself open. The same applies to&lt;br /&gt;
other system resources that the client process may be accessing, and it&lt;br /&gt;
applies in the inverse, as well: A dynlink routine that obtains resources&lt;br /&gt;
for itself, in the guise of the client process, should do so in a way that&lt;br /&gt;
doesn't affect the client code. For example, consuming many of the&lt;br /&gt;
available file handles would be a side effect because the client would then&lt;br /&gt;
unexpectedly be short of available file handles. A dynlink package with a&lt;br /&gt;
healthy file handle appetite should be sure to call OS/2 to raise the&lt;br /&gt;
maximum number of file handles so that the client process isn't&lt;br /&gt;
constrained. Finally, the amount of available stack space is a resource&lt;br /&gt;
that a dynlink package must not exhaust. A dynlink routine should try to&lt;br /&gt;
minimize its stack needs, and an upgrade to an existing dynlink package&lt;br /&gt;
must not consume much more stack space than did the earlier version, lest&lt;br /&gt;
the upgrade cause existing clients to fail in the field.&lt;br /&gt;
     Dynlink routines can also cause side effects by issuing some kinds of&lt;br /&gt;
system calls. Because a dynlink routine runs as a subroutine of the client&lt;br /&gt;
process, it must be sure that calls that it makes to OS/2 on behalf of the&lt;br /&gt;
client process don't affect the client application. For example, each&lt;br /&gt;
signal event can have only one handler address; if a dynlink routine&lt;br /&gt;
establishes a signal handler, then that signal handler preempts any handler&lt;br /&gt;
set up by the client application. Likewise, if a dynlink routine changes&lt;br /&gt;
the priority of the thread with which it was called, the dynlink routine&lt;br /&gt;
must be sure to restore that priority before it returns to its caller.&lt;br /&gt;
Several other system functions such as DosError and DosSetVerify also cause&lt;br /&gt;
side effects that can affect the client process.&lt;br /&gt;
     Enumerating all forms of side effects is not possible; it's up to the&lt;br /&gt;
programmer to take the care needed to ensure that a dynlink module is&lt;br /&gt;
properly house-trained. A dynlink module should avoid the side effects&lt;br /&gt;
mentioned as well as similar ones, and, most important, it should behave&lt;br /&gt;
consistently so that if a client application passes its acceptance tests in&lt;br /&gt;
the lab it won't mysteriously fail in the field. This applies doubly to&lt;br /&gt;
upgrades for existing dynlink routines. Upgrades must be written so that if&lt;br /&gt;
a client application works with the earlier release of the dynlink package&lt;br /&gt;
it will work with the new release; obviously the author of the application&lt;br /&gt;
will not have an opportunity to retest existing copies of the application&lt;br /&gt;
against the new release of the dynlink module.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7.12  Dynlink Names&lt;br /&gt;
&lt;br /&gt;
Each dynlink entry point has three names associated with it: an external&lt;br /&gt;
name, a module name, and an entry point name. The name the client program&lt;br /&gt;
calls as an external reference is the external name. The programmer works&lt;br /&gt;
with this name, and its syntax and form must be compatible with the&lt;br /&gt;
assembler or compiler being used. The name should be simple and explanatory&lt;br /&gt;
yet unlikely to collide with another external name in the client code or in&lt;br /&gt;
another library. A name such as READ or RESET is a poor choice because of&lt;br /&gt;
the collision possibilities; a name such as XR23P11 is obviously hard to&lt;br /&gt;
work with.&lt;br /&gt;
     The linker replaces the external name with a module name and an entry&lt;br /&gt;
point name, which are embedded in the resultant .EXE file. OS/2 uses the&lt;br /&gt;
module name to locate the dynlink .DLL file; the code for module modname is&lt;br /&gt;
in file MODNAME.DLL. The entry point name specifies the entry point in the&lt;br /&gt;
module; the entry point name need not be the same as the external name. For&lt;br /&gt;
modules with a lot of entry points, the client .EXE file size can be&lt;br /&gt;
minimized and the loading speed maximized by using entry ordinals in place&lt;br /&gt;
of entry point names. See the OS/2 technical reference literature for&lt;br /&gt;
details.&lt;br /&gt;
     Runtime dynamic links are established by using the module name and the&lt;br /&gt;
entry point name; the external name is not used.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8  File System Name Space&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
File system name space is a fancy term for how names of objects are defined&lt;br /&gt;
in OS/2. The words file system are a hint that OS/2 uses one naming scheme&lt;br /&gt;
both for files and for everything else with a name in ASCII format--system&lt;br /&gt;
semaphores, named shared memory, and so forth. First, we'll discuss the&lt;br /&gt;
syntax of names and how to manipulate them; we'll wind up with a discussion&lt;br /&gt;
of how and why we use one naming scheme for all named objects.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.1  Filenames&lt;br /&gt;
&lt;br /&gt;
Before we discuss OS/2 filenames, let's review the format of filenames&lt;br /&gt;
under MS-DOS. In MS-DOS, filenames are required to fit the 8.3 format: a&lt;br /&gt;
name field (which can contain a maximum of 8 characters) and an extension&lt;br /&gt;
field (which can contain a maximum of 3 characters).&lt;br /&gt;
1. As an aside, these sizes date from a tradition established&lt;br /&gt;
many years ago by Digital Equipment Corporation. Digital's very&lt;br /&gt;
early computers used a technique called RAD50 to store 3&lt;br /&gt;
uppercase letters in one 16-bit word, so their file systems&lt;br /&gt;
allowed a 6-character filename and a 3-character extension. CP/M&lt;br /&gt;
later picked up this filename structure. CP/M didn't use RAD50,&lt;br /&gt;
so, in a moment of generosity, it allowed 8-character filenames;&lt;br /&gt;
but the 3-character extension was kept.&lt;br /&gt;
1 The period character&lt;br /&gt;
(.) between the name and the extension is not part of the filename; it's a&lt;br /&gt;
separator character. The filename can consist of uppercase characters only.&lt;br /&gt;
If a user or an application creates a filename that contains lowercase&lt;br /&gt;
characters or a mixture of uppercase and lowercase, MS-DOS converts the&lt;br /&gt;
filename to all uppercase. If an application presents a filename whose name&lt;br /&gt;
or extension field exceeds the allotted length, MS-DOS silently truncates&lt;br /&gt;
the name to the 8.3 format before using it. MS-DOS establishes and enforces&lt;br /&gt;
these rules and maintains the file system structure on the disks. The file&lt;br /&gt;
system that MS-DOS version 3.x supports is called the FAT (File Allocation&lt;br /&gt;
Table) file system. The following are typical MS-DOS and OS/2 filenames:&lt;br /&gt;
&lt;br /&gt;
   \FOOTBALL\SRC\KERNEL\SCHED.ASM&lt;br /&gt;
&lt;br /&gt;
Football is a development project, so this name describes the source for&lt;br /&gt;
the kernel scheduler for the football project.&lt;br /&gt;
&lt;br /&gt;
   \MEMOS\286\MODESWIT.DOC&lt;br /&gt;
&lt;br /&gt;
is a memo discussing 80286 mode switching.&lt;br /&gt;
&lt;br /&gt;
   \\HAGAR\SCRATCH\GORDONL\FOR_MARK&lt;br /&gt;
&lt;br /&gt;
is a file in my scratch directory on the network server HAGAR, placed there&lt;br /&gt;
for use by Mark.&lt;br /&gt;
     The OS/2 architecture views file systems quite differently. As&lt;br /&gt;
microcomputers become more powerful and are used in more and more ways,&lt;br /&gt;
file system characteristics will be needed that might not be met by a&lt;br /&gt;
built-in OS/2 file system. Exotic peripherals, such as WORM&lt;br /&gt;
2. Write Once, Read Many disks. These are generally laser disks of&lt;br /&gt;
very high capacity, but once a track is written, it cannot be erased.&lt;br /&gt;
These disks can appear to be erasable by writing new copies of files&lt;br /&gt;
and directories each time a change is made, abandoning the old ones.&lt;br /&gt;
2 drives,&lt;br /&gt;
definitely require special file systems to meet their special&lt;br /&gt;
characteristics. For this reason, the file system is not built into OS/2&lt;br /&gt;
but is a closely allied component--an installable file system (IFS). An IFS&lt;br /&gt;
is similar to a device driver; it's a body of code that OS/2 loads at boot&lt;br /&gt;
time. The code talks to OS/2 via a standard interface and provides the&lt;br /&gt;
software to manage a file system on a storage device, including the ability&lt;br /&gt;
to create and maintain directories, to allocate disk space, and so&lt;br /&gt;
on.&lt;br /&gt;
     If you are familiar with OS/2 version 1.0, this information may be&lt;br /&gt;
surprising because you have seen no mention of an IFS in the reference&lt;br /&gt;
manuals. That's because the implementation hasn't yet caught up with the&lt;br /&gt;
architecture. We designed OS/2, from the beginning, to support installable&lt;br /&gt;
file systems, one of which would of course be the familiar FAT file system.&lt;br /&gt;
We designed the file system calls, such as DosOpen and DosClose, with this&lt;br /&gt;
in mind. Although scheduling pressures forced us to ship OS/2 version 1.0&lt;br /&gt;
with only the FAT file system--still built in--a future release will&lt;br /&gt;
include the full IFS package. Although at this writing the IFS release of&lt;br /&gt;
OS/2 has not been announced, this information is included here so that you&lt;br /&gt;
can understand the basis for the system name architecture. Also, this&lt;br /&gt;
information will help you write programs that work well under the new&lt;br /&gt;
releases of OS/2 that contain the IFS.&lt;br /&gt;
     Because the IFS will interpret filenames and pathnames and because&lt;br /&gt;
installable file systems can vary considerably, OS/2&lt;br /&gt;
3. Excluding the IFS part.&lt;br /&gt;
3 doesn't contain much&lt;br /&gt;
specific information about the format and meaning of filenames and&lt;br /&gt;
pathnames. In general, the form and meaning of filenames and pathnames are&lt;br /&gt;
private matters between the user and the IFS; both the application and OS/2&lt;br /&gt;
are simply go-betweens. Neither should attempt to parse or understand&lt;br /&gt;
filenames and pathnames. Applications shouldn't parse names because some&lt;br /&gt;
IFSs will support names in formats other than the 8.3 format. Applications&lt;br /&gt;
shouldn't even assume a specific length for a filename or a pathname. All&lt;br /&gt;
OS/2 filename and pathname interfaces, such as DosOpen, DosFindNext, and so&lt;br /&gt;
on, are designed to take name strings of arbitrary length. Applications&lt;br /&gt;
should use name buffers of at least 256 characters to ensure that a long&lt;br /&gt;
name is not truncated.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.2  Network Access&lt;br /&gt;
&lt;br /&gt;
Two hundred and fifty-six characters may seem a bit extreme for the length&lt;br /&gt;
of a filename, and perhaps it is. But OS/2 filenames are often pathnames,&lt;br /&gt;
and pathnames can be quite lengthy. To provide transparent access to files&lt;br /&gt;
on a LAN (local area network), OS/2 makes the network part of the file&lt;br /&gt;
system name space. In other words, a file's pathname can specify a machine&lt;br /&gt;
name as well as a directory path. An application can issue an open to a&lt;br /&gt;
name string such as \WORK\BOOK.DAT or \\VOGON\TEMP\RECALC.ASM. The first&lt;br /&gt;
name specifies the file BOOK.DAT in the directory WORK on the current drive&lt;br /&gt;
of the local machine; the second name specifies the file RECALC.ASM in the&lt;br /&gt;
directory TEMP on the machine VOGON.&lt;br /&gt;
4. Network naming is a bit more complex than this; the name TEMP&lt;br /&gt;
on the machine VOGON actually refers to an offered network&lt;br /&gt;
resource and might appear in any actual disk directory.&lt;br /&gt;
4 Future releases of the Microsoft LAN&lt;br /&gt;
Manager will make further use of the file system name space, so filenames,&lt;br /&gt;
especially program-generated filenames, can easily become very long.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.3  Name Generation and Compatibility&lt;br /&gt;
&lt;br /&gt;
Earlier, I said that applications should pass on filenames entered by the&lt;br /&gt;
user, ignoring their form. This is, of course, a bit unrealistic. Programs&lt;br /&gt;
often need to generate filenames--to hold scratch files, to hold derivative&lt;br /&gt;
filenames (for example, FOO.OBJ derived from FOO.ASM), and so forth. How&lt;br /&gt;
can an application generate or permute such filenames and yet ensure&lt;br /&gt;
compatibility with all installable file systems? The answer is, of course:&lt;br /&gt;
Use the least common denominator approach. In other words, you can safely&lt;br /&gt;
assume that a new IFS must accept the FAT file system's names (the 8.3&lt;br /&gt;
format) because otherwise it would be incompatible with too many programs.&lt;br /&gt;
So if an application sticks to the 8.3 rules when it creates names, it can&lt;br /&gt;
be sure that it is compatible with future file systems. Unlike MS-DOS,&lt;br /&gt;
OS/2&lt;br /&gt;
5. More properly, the FAT installable file system installed in OS/2.&lt;br /&gt;
5 will not truncate name or extension fields that are too long;&lt;br /&gt;
instead, an error will be returned. The case of a filename will continue to&lt;br /&gt;
be insignificant. Some operating systems, such as UNIX, are case sensitive;&lt;br /&gt;
for example, in UNIX the names &amp;quot;foo&amp;quot; and &amp;quot;Foo&amp;quot; refer to different files.&lt;br /&gt;
This works fine for a system used primarily by programmers, who know that a&lt;br /&gt;
lowercase f is ASCII 66 (SUB16) and that an uppercase F is ASCII&lt;br /&gt;
46 (SUB 16). Nonprogrammers, on the other hand, tend to see f and F as the&lt;br /&gt;
same character. Because most OS/2 users are nonprogrammers, OS/2&lt;br /&gt;
installable file systems will continue to be case insensitive.&lt;br /&gt;
     I said that it was safe if program-generated names adhered to the 8.3&lt;br /&gt;
rule. Program-permuted names are likewise safe if they only substitute&lt;br /&gt;
alphanumeric characters for other alphanumeric characters, for example,&lt;br /&gt;
FOO.OBJ for FOO.ASM. Lengthening filenames is also safe (for example,&lt;br /&gt;
changing FOO.C to FOO.OBJ) if your program checks for &amp;quot;invalid name&amp;quot; error&lt;br /&gt;
codes for the new name and has some way to deal with that possibility. In&lt;br /&gt;
any case, write your program so that it isn't confused by enhanced&lt;br /&gt;
pathnames; in the above substitution cases, the algorithm should work from&lt;br /&gt;
the end of the path string and ignore what comes before.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.4  Permissions&lt;br /&gt;
&lt;br /&gt;
Future releases of OS/2 will use the file system name space for more than&lt;br /&gt;
locating a file; it will also contain the permissions for the file. A&lt;br /&gt;
uniform mechanism will associate an access list with every entry in the&lt;br /&gt;
file system name space. This list will prevent unauthorized access--&lt;br /&gt;
accidental or deliberate--to the named file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8.5  Other Objects in the File System Name Space&lt;br /&gt;
&lt;br /&gt;
As we've seen, the file system name space is a valuable device in several&lt;br /&gt;
aspects. First, it allows the generation of a variety of names. You can&lt;br /&gt;
group names together (by putting them in the same directory), and you can&lt;br /&gt;
generate entire families of unique names (by creating a new subdirectory).&lt;br /&gt;
Second, the name space can encompass all files and devices on the local&lt;br /&gt;
machine as well as files and devices on remote machines. Finally, file&lt;br /&gt;
system names will eventually support a flexible access and protection&lt;br /&gt;
mechanism.&lt;br /&gt;
     Thus, it comes as no surprise that when the designers of OS/2 needed a&lt;br /&gt;
naming mechanism to deal with nonfile objects, such as shared memory,&lt;br /&gt;
system semaphores, and named pipes, we chose to use the file system name&lt;br /&gt;
space. One small disadvantage to this decision is that a shared memory&lt;br /&gt;
object cannot have a name identical to that of a system semaphore, a named&lt;br /&gt;
pipe, or a disk file. This drawback is trivial, however, compared with the&lt;br /&gt;
benefits of sharing the file system name space. And, of course, you can use&lt;br /&gt;
separate subdirectory names for each type of object, thus preventing name&lt;br /&gt;
collision.&lt;br /&gt;
     Does this mean that system semaphores, shared memory, and pipes have&lt;br /&gt;
actual file system entries on a disk somewhere? Not yet. The FAT file&lt;br /&gt;
system does not support special object names in its directories. Although&lt;br /&gt;
changing it to do so would be easy, the file system would no longer be&lt;br /&gt;
downward compatible with MS-DOS. (MS-DOS 3.x could not read such disks&lt;br /&gt;
written under OS/2.) Because only the FAT file system is available with&lt;br /&gt;
OS/2 version 1.0, that release keeps special RAM-resident pseudo&lt;br /&gt;
directories to hold the special object names. These names must start with&lt;br /&gt;
\SEM\, \SHAREMEM\, \QUEUES\, and \DEV\ to minimize the chance of name&lt;br /&gt;
collision with a real file when they do become special pseudo files in a&lt;br /&gt;
future release of OS/2.&lt;br /&gt;
     Although all file system name space features--networking and (in the&lt;br /&gt;
future) permissions--apply to all file system name space objects from an&lt;br /&gt;
architectural standpoint, not all permutations may be supported.&lt;br /&gt;
Specifically, supporting named shared memory across the network is very&lt;br /&gt;
costly&lt;br /&gt;
6. The entire shared memory segment must be transferred across&lt;br /&gt;
the network each time any byte within it is changed. Some clever&lt;br /&gt;
optimizations can reduce this cost, but none works well enough to&lt;br /&gt;
be feasible.&lt;br /&gt;
6 and won't be implemented.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9  Memory Management&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
A primary function of any multitasking operating system is to allocate&lt;br /&gt;
system resources to each process according to its need. The scheduler&lt;br /&gt;
allocates CPU time among processes (actually, among threads); the memory&lt;br /&gt;
manager allocates both physical memory and virtual memory.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.1  Protection Model&lt;br /&gt;
&lt;br /&gt;
Although MS-DOS provided a simple form of memory management, OS/2 provides&lt;br /&gt;
memory protection. Under MS-DOS 3.x, for example, a program should ask the&lt;br /&gt;
operating system to allocate a memory area before the program uses it.&lt;br /&gt;
Under OS/2, a program must ask the operating system to allocate a memory&lt;br /&gt;
area before the program uses it. As we discussed earlier, the 80286&lt;br /&gt;
microprocessor contains special memory protection hardware. Each memory&lt;br /&gt;
reference that a program makes explicitly or implicitly references a&lt;br /&gt;
segment selector. The segment selector, in turn, references an entry in the&lt;br /&gt;
GDT or the LDT, depending on the form of the selector. Before any program,&lt;br /&gt;
including OS/2 itself, can reference a memory location, that memory&lt;br /&gt;
location must be described in an LDT or a GDT entry, and the selector for&lt;br /&gt;
that entry must be loaded into one of the four segment registers.&lt;br /&gt;
     This hardware design places some restrictions on how programs can use&lt;br /&gt;
addresses.&lt;br /&gt;
&lt;br /&gt;
     þ  A program cannot address memory not set up for it in the LDT or&lt;br /&gt;
        GDT. The only way to address memory is via the LDT and GDT.&lt;br /&gt;
&lt;br /&gt;
     þ  Each segment descriptor in the LDT and GDT contains the physical&lt;br /&gt;
        address and the length of that segment. A program cannot reference&lt;br /&gt;
        an offset into a segment beyond that segment's length.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't put garbage (arbitrary values) into a segment&lt;br /&gt;
        register. Each time a segment register is loaded, the hardware&lt;br /&gt;
        examines the corresponding LDT and GDT to see if the entry is&lt;br /&gt;
        valid. If a program puts an arbitrary value--for example, the lower&lt;br /&gt;
        half of a floating point number--into a segment register, the&lt;br /&gt;
        arbitrary value will probably point to an invalid LDT or GDT entry,&lt;br /&gt;
        causing a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't execute instructions from within a data segment.&lt;br /&gt;
        Attempting to load a data segment selector into the CS register&lt;br /&gt;
        (usually via a far call or a far jump) causes a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't write into a code segment. Attempting to do so&lt;br /&gt;
        causes a GP fault.&lt;br /&gt;
&lt;br /&gt;
     þ  A program can't perform segment arithmetic. Segment arithmetic&lt;br /&gt;
        refers to activities made possible by the addressing mechanism of&lt;br /&gt;
        the 8086 and 8088 microprocessors. Although they are described as&lt;br /&gt;
        having a segment architecture, they are actually linear address&lt;br /&gt;
        space machines that use offset registers--the so-called segment&lt;br /&gt;
        registers. An 8086 can address 1 MB of memory, which requires a 20-&lt;br /&gt;
        bit address. The processor creates this address by multiplying the&lt;br /&gt;
        16-bit segment value by 16 and adding it to the 16-bit offset&lt;br /&gt;
        value. The result is an address between 0 and 1,048,575 (that is, 1&lt;br /&gt;
        MB).&lt;br /&gt;
1. Actually, it's possible to produce addresses beyond 1 MB&lt;br /&gt;
(2^20) by this method if a large enough segment and offset value&lt;br /&gt;
are chosen. The 8086 ignores the carry into the nonexistent 21st&lt;br /&gt;
address bit, effectively wrapping around such large addresses&lt;br /&gt;
into the first 65 KB-16 bytes of physical memory.&lt;br /&gt;
1 The reason these are not true segments is that they don't&lt;br /&gt;
        have any associated length and their names (that is, their&lt;br /&gt;
        selectors) aren't names at all but physical addresses divided by&lt;br /&gt;
        16. These segment values are actually scaled offsets. An address&lt;br /&gt;
        that has a segment value of 100 and an offset value of 100 (shown&lt;br /&gt;
        as 100(SUB10):100(SUB10)), and the address (99(SUB10):116(SUB10))&lt;br /&gt;
        both refer to the same memory location.&lt;br /&gt;
           Many real mode programs take advantage of this situation. Some&lt;br /&gt;
        programs that keep a great many pointers store them as 20-bit&lt;br /&gt;
        values, decomposing those values into the segment:offset form only&lt;br /&gt;
        when they need to de-reference the pointer. To ensure that certain&lt;br /&gt;
        objects have a specific offset value, other programs choose a&lt;br /&gt;
        matching segment value so that the resultant 20-bit address is&lt;br /&gt;
        correct. Neither technique works in OS/2 protect mode. Each segment&lt;br /&gt;
        selector describes its own segment, a segment with a length and an&lt;br /&gt;
        address that are independent of the numeric value of the segment&lt;br /&gt;
        selector. The memory described by segment N has nothing in common&lt;br /&gt;
        with the memory described by segment N+4 or by any other segment&lt;br /&gt;
        unless OS/2 explicitly sets it up that way.&lt;br /&gt;
&lt;br /&gt;
     The segmentation and protection hardware allows OS/2 to impose further&lt;br /&gt;
restrictions on processes.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot edit or examine the contents of the LDT or the&lt;br /&gt;
        GDT. OS/2 simply declines to build an LDT or GDT selector that a&lt;br /&gt;
        process can use to access the contents of those tables. Certain LDT&lt;br /&gt;
        and GDT selectors describe the contents of those tables themselves,&lt;br /&gt;
        but OS/2 sets them up so that they can only be used by ring 0 (that&lt;br /&gt;
        is, privileged) code.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot hook interrupt vectors. MS-DOS version 3.x&lt;br /&gt;
        programs commonly hook interrupt vectors by replacing the address&lt;br /&gt;
        of the interrupt handler with an address from their own code. Thus,&lt;br /&gt;
        these programs can monitor or intercept system calls made via INT&lt;br /&gt;
        21h, BIOS calls also made via interrupts, and hardware interrupts&lt;br /&gt;
        such as the keyboard and the system clock. OS/2 programs cannot do&lt;br /&gt;
        this. OS/2 declines to set up a segment selector that processes can&lt;br /&gt;
        use to address the interrupt vector table.&lt;br /&gt;
&lt;br /&gt;
     þ  Processes cannot call the ROM BIOS code because no selector&lt;br /&gt;
        addresses the ROM BIOS code. Even if such a selector were&lt;br /&gt;
        available, it would be of little use. The ROM BIOS is coded for&lt;br /&gt;
        real mode execution and performs segment arithmetic operations that&lt;br /&gt;
        are no longer legal. If OS/2 provided a ROM BIOS selector, calls to&lt;br /&gt;
        the ROM BIOS would usually generate GP faults.&lt;br /&gt;
&lt;br /&gt;
     þ  Finally, processes cannot run in ring 0, that is, in privileged&lt;br /&gt;
        mode. Both OS/2 and the 80286 hardware are designed to prevent an&lt;br /&gt;
        application program from ever executing in ring 0. Code running in&lt;br /&gt;
        ring 0 can manipulate the LDT and GDT tables as well as other&lt;br /&gt;
        hardware protection features. If OS/2 allowed processes to run in&lt;br /&gt;
        ring 0, the system could never be stable or secure. OS/2 obtains&lt;br /&gt;
        its privileged (literally) state by being the first code loaded at&lt;br /&gt;
        boot time. The boot process takes place in ring 0 and grants ring 0&lt;br /&gt;
        permission to OS/2 by transferring control to OS/2 while remaining&lt;br /&gt;
        in ring 0. OS/2 does not, naturally, extend this favor to the&lt;br /&gt;
        application programs it loads; it ensures that applications can&lt;br /&gt;
        only run in ring 3 user mode.&lt;br /&gt;
2. Applications can also run in ring 2 (see 18.1 I/O Privilege&lt;br /&gt;
Mechanism).&lt;br /&gt;
2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2  Memory Management API&lt;br /&gt;
&lt;br /&gt;
OS/2 provides an extensive memory management API. This book is not a&lt;br /&gt;
reference manual, so I won't cover all the calls. Instead, I'll focus on&lt;br /&gt;
areas that may not be completely self-explanatory.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.1  Shared Memory&lt;br /&gt;
OS/2 supports two kinds of shared memory--named shared memory and giveaway&lt;br /&gt;
shared memory. In both, the memory object shared is a segment. Only an&lt;br /&gt;
entire segment can be shared; sharing part of a segment is not possible.&lt;br /&gt;
Named shared memory is volatile because neither the name of the named&lt;br /&gt;
shared memory nor the memory itself can exist on the FAT file system. When&lt;br /&gt;
the number of processes using a shared memory segment goes to zero, the&lt;br /&gt;
memory is released. Shared memory can't stay around in the absence of&lt;br /&gt;
client processes; it must be reinitialized via DosAllocShrSeg after a&lt;br /&gt;
period of nonuse.&lt;br /&gt;
     Giveaway shared memory allows processes to share access to the same&lt;br /&gt;
segment. Giveaway shared memory segments don't have names because processes&lt;br /&gt;
can't ask to have access to them; a current user of the segment has to give&lt;br /&gt;
access to the segment to a new client process. The term giveaway is a bit&lt;br /&gt;
of a misnomer because the giving process retains access to the memory--the&lt;br /&gt;
access is &amp;quot;given&amp;quot; but not especially &amp;quot;away.&amp;quot; Giveaway shared memory is not&lt;br /&gt;
as convenient as named shared memory. The owner&lt;br /&gt;
3. One of the owners. Anyone with access to a giveaway shared&lt;br /&gt;
segment can give it away itself.&lt;br /&gt;
3 has to know the PID of the&lt;br /&gt;
recipient and  then communicate the recipient's segment selector (returned&lt;br /&gt;
by DosGiveSeg) to that recipient process via some form of IPC.&lt;br /&gt;
     Despite its limitations, giveaway shared memory has important virtues.&lt;br /&gt;
It's a fast and efficient way for one process to transfer data to another;&lt;br /&gt;
and because access is passed &amp;quot;hand to hand,&amp;quot; the wrong process cannot&lt;br /&gt;
accidentally or deliberately gain access to the segment. Most clients of&lt;br /&gt;
giveaway shared memory don't retain access to the segment once they've&lt;br /&gt;
passed it off; they typically call DosFreeSeg on their handle after they've&lt;br /&gt;
called DosGiveSeg. For example, consider the design of a database dynlink&lt;br /&gt;
subsystem that acts as a front end for a database serving process. As part&lt;br /&gt;
of the dynlink initialization process, the package arranged for its client&lt;br /&gt;
process to share a small named shared memory segment with the database&lt;br /&gt;
process. It might be best to use a named pipe or named shared memory--&lt;br /&gt;
created by the database process--to establish initial communication and&lt;br /&gt;
then use this interface only to set up a private piece of giveaway shared&lt;br /&gt;
memory for all further transactions between the client process (via the&lt;br /&gt;
dynlink subsystem) and the database process. Doing it this way, rather than&lt;br /&gt;
having one named shared segment hold service requests from all clients,&lt;br /&gt;
provides greater security. Because each client has its own separate shared&lt;br /&gt;
memory communications area, an amok client can't damage the communications&lt;br /&gt;
of other clients.&lt;br /&gt;
     When a client process asks the database process to read it a record,&lt;br /&gt;
the database process must use a form of IPC to transfer the data to the&lt;br /&gt;
client. Pipes are too slow for the volume of data that our example&lt;br /&gt;
anticipates; shared memory is the best technique. If we were to use named&lt;br /&gt;
shared memory, the database package would have to create a unique shared&lt;br /&gt;
memory name for each record, allocate the memory, and then communicate the&lt;br /&gt;
name to the client (actually, to the dynlink subsystem called by the&lt;br /&gt;
client) so that it can request access. This process has some drawbacks:&lt;br /&gt;
&lt;br /&gt;
     þ  A new unique shared memory name must be created for each request.&lt;br /&gt;
        We could reuse a single shared memory segment, but this would force&lt;br /&gt;
        the client to copy the data out of the segment before it could make&lt;br /&gt;
        another request--too costly a process for an application that must&lt;br /&gt;
        handle a high volume of data.&lt;br /&gt;
&lt;br /&gt;
     þ  Creating named shared memory segments is generally slower than&lt;br /&gt;
        creating giveaway shared memory segments, especially if a large&lt;br /&gt;
        number of named shared memory objects exist, as would be the case&lt;br /&gt;
        in this scenario. The client spends more time when it then requests&lt;br /&gt;
        access to the segment. Creating named shared memory segments is&lt;br /&gt;
        plenty fast enough when it's done once in a while, but in a high-&lt;br /&gt;
        frequency application such as our example, it could become a&lt;br /&gt;
        bottleneck.&lt;br /&gt;
&lt;br /&gt;
     Instead, the database process can create a giveaway shared memory&lt;br /&gt;
segment, load the data into it, and then give it to the client process. The&lt;br /&gt;
database process can easily learn the client's PID; the dynlink interface,&lt;br /&gt;
which runs as the client process, can include it as part of the data&lt;br /&gt;
request. Likewise, the database process can easily return the new client&lt;br /&gt;
selector to the client. This process is fast and efficient and doesn't bog&lt;br /&gt;
down the system by forcing it to deal with a great many name strings.&lt;br /&gt;
     Note that you must specify, at the time of the DosAllocSeg, that the&lt;br /&gt;
segment might be &amp;quot;given away.&amp;quot; Doing so allows OS/2 to allocate the&lt;br /&gt;
selector in the disjoint space, as we discussed earlier.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.2  Huge Memory&lt;br /&gt;
The design of the 80286 microprocessor specifies the maximum size of a&lt;br /&gt;
memory segment as 64 KB. For many programs, this number is far too small.&lt;br /&gt;
For example, the internal representation of a large spreadsheet commonly&lt;br /&gt;
takes up 256 KB or more. OS/2 can do nothing to set up a segment that is&lt;br /&gt;
truly larger than 64 KB, but the OS/2 facility called huge segments&lt;br /&gt;
provides a reasonable emulation of segments larger than 64 KB. The trick is&lt;br /&gt;
that a huge segment of, for example, 200 KB is not a single segment but a&lt;br /&gt;
group of four segments, three of which are 64 KB and a fourth of 8 KB. With&lt;br /&gt;
minimal programming burden, OS/2 allows an application to treat the group&lt;br /&gt;
of four segments as a single huge segment.&lt;br /&gt;
     When a process calls DosAllocHuge to allocate a huge segment, OS/2&lt;br /&gt;
allocates several physical segments, the sum of whose size equals the size&lt;br /&gt;
of the virtual huge segment. All component segments are 64 KB, except&lt;br /&gt;
possibly the last one. Unlike an arbitrary collection of segment selectors,&lt;br /&gt;
DosAllocHuge guarantees that the segment selectors it returns are spaced&lt;br /&gt;
uniformly from each other. The selector of the N+1th component segment is&lt;br /&gt;
that of the Nth segment plus i, where i is a power of two. The value of i&lt;br /&gt;
is constant for any given execution of OS/2, but it may vary between&lt;br /&gt;
releases of OS/2 or as a result of internal configuration during bootup. In&lt;br /&gt;
other words, a program must learn the factor i every time it executes; it&lt;br /&gt;
must not hard code the value. There are three ways to learn this value.&lt;br /&gt;
First, a program can call DosGetHugeShift; second, it can read this value&lt;br /&gt;
from the global infoseg; and third, it can reference this value as the&lt;br /&gt;
undefined absolute externals DOSHUGESHIFT (log(SUB2)(i)) or&lt;br /&gt;
DOSHUGEINCR (i). OS/2 will insert the proper value for these&lt;br /&gt;
externals at loadtime. This last method is the most efficient and is&lt;br /&gt;
recommended. Family API programs should call DosGetHugeShift. Figure 9-1&lt;br /&gt;
illustrates the layout of a 200 KB huge memory object. Selectors n + 4i and&lt;br /&gt;
n + 5i are currently invalid but are reserved for future growth of the&lt;br /&gt;
huge object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     ÚÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´                              ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³                              ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+0i ³       ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿       ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³               ³       ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³      ³               ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³               ³&lt;br /&gt;
n+1i ³       ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄ¿       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³       ³       ³       ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³      ³       ³       ³       ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´      ³       ³       ÀÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+2i ³       ÃÄÄÄÄÄÄÙ       ³               ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
n+3i ³       ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³&lt;br /&gt;
n+4i ³   �   ³              ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ³               ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³       ³              ³               ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´              ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³            ³&lt;br /&gt;
n+5i ³   �   ³                              ³            ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´                              ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³       ³&lt;br /&gt;
     ÀÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 9-1.  Huge memory objects.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Once an application has the first segment selector of the huge segment&lt;br /&gt;
group, called the base segment, and the value log(SUB2)(i), computing the&lt;br /&gt;
address of the Nth byte in the huge segment is easy. Take the high-order&lt;br /&gt;
word of the value of N (that is, N/64 KB), shift it left by log(SUB2)(i)&lt;br /&gt;
(that is, by the DosHugeShift value), and add the base segment selector&lt;br /&gt;
returned by DosAllocHuge. The resultant value is the segment selector for&lt;br /&gt;
the proper component segment; the low-order 16 bits of i are the offset&lt;br /&gt;
into that segment. This computation is reasonably quick to perform since it&lt;br /&gt;
involves only a shift and an addition.&lt;br /&gt;
     Huge segments can be shrunk or grown via DosReallocHuge. If the huge&lt;br /&gt;
segment is to be grown, creating more component physical segments may be&lt;br /&gt;
necessary. Because the address generation rules dictate which selector this&lt;br /&gt;
new segment may have, growing the huge segment may not be possible if that&lt;br /&gt;
selector has already been allocated for another purpose. DosAllocHuge takes&lt;br /&gt;
a maximum growth parameter; it uses this value to reserve sufficient&lt;br /&gt;
selectors to allow the huge segment to grow that big. Applications should&lt;br /&gt;
not provide an unrealistically large number for this argument because doing&lt;br /&gt;
so will waste LDT selectors.&lt;br /&gt;
     The astute reader will notice that the segment arithmetic of the 8086&lt;br /&gt;
environment is not dead; in a sense, it's been resurrected by the huge&lt;br /&gt;
segment mechanism. Applications written for the 8086 frequently use this&lt;br /&gt;
technique to address memory regions greater than 64 KB, using a shift value&lt;br /&gt;
of 12. In other words, if you add 2^12 to an 8086 segment register value,&lt;br /&gt;
the segment register will point to an address 2^12*16, or 64 KB, further in&lt;br /&gt;
physical memory. The offset value between the component segment values was&lt;br /&gt;
always 4096 because of the way the 8086 generated addresses. Although the&lt;br /&gt;
steps involved in computing the segment value are the same in protect mode,&lt;br /&gt;
what's actually happening is considerably different. When you do this&lt;br /&gt;
computation in protect mode, the segment selector value has no inherent&lt;br /&gt;
relationship to the other selectors that make up the huge object. The trick&lt;br /&gt;
only works because OS/2 has arranged for equally spaced-out selectors to&lt;br /&gt;
exist and for each to point to an area of physical memory of the&lt;br /&gt;
appropriate size. Figure 9-2 illustrates the similarities and differences&lt;br /&gt;
between huge model addressing in real and protect modes. The application&lt;br /&gt;
code sequence is identical: A segment selector is computed by adding N*i to&lt;br /&gt;
the base selector. In real mode i is always 4096; in protect mode OS/2&lt;br /&gt;
provides i.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
          REAL MODE        |             PROTECT MODE&lt;br /&gt;
                           |&lt;br /&gt;
           Physical        |&lt;br /&gt;
            Memory         |            Segment&lt;br /&gt;
           ÚÄÄÄÄÄÄ¿        |            Selector         Physical&lt;br /&gt;
           ³      ³        |             Table            Memory&lt;br /&gt;
           ³      ³        |            ÚÄÄÄÄÄÄ¿         ÚÄÄÄÄÄÄ¿&lt;br /&gt;
n+0i ÄÄÄÄÄ�³      ³±       |            ³      ³  ÚÄÄÄÄÄ�³      ³&lt;br /&gt;
           ³      ³±�64 KB | n+0i ÄÄÄÄÄ�³      ÃÄÄÅÄÄÄ¿  ÀÄÄÄÄÄÄÙ&lt;br /&gt;
n+1i ÄÄÄÄÄ�³      ³±       |            ³      ³  ³   ³&lt;br /&gt;
           ³      ³        | n+1i ÄÄÄÄÄ�³      ÃÄÄÅÄ¿ ³  ÚÄÄÄÄÄÄ¿&lt;br /&gt;
n+2i ÄÄÄÄÄ�³      ³        |            ³      ³  ³ ³ ÀÄ�³      ³&lt;br /&gt;
           ³      ³        | n+2i ÄÄÄÄÄ�³      ÃÄÄÙ ³    ÀÄÄÄÄÄÄÙ&lt;br /&gt;
n+3i ÄÄÄÄÄ�³      ³        |            ³      ³    ³    ÚÄÄÄÄÄÄ¿&lt;br /&gt;
           ³      ³        | n+3i ÄÄÄÄÄ�³      ÃÄÄÄÄÅÄÄÄ�³      ³&lt;br /&gt;
           ³      ³        |            ³      ³    ³    ÀÄÄÄÄÄÄÙ&lt;br /&gt;
           ³      ³        |            ³      ³    ³    ÚÄÄÄÄÄÄ¿&lt;br /&gt;
           ³      ³        |            ÀÄÄÄÄÄÄÙ    ÀÄÄÄ�³      ³&lt;br /&gt;
           ³      ³        |        i is set by OS/2     ÀÄÄÄÄÄÄÙ&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ³      ³        |&lt;br /&gt;
           ÀÄÄÄÄÄÄÙ        |&lt;br /&gt;
           i = 4096        |&lt;br /&gt;
&lt;br /&gt;
Figure 9-2.  Huge model addressing in real mode and in protect mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Although the similarity between 8086 segment arithmetic and OS/2 huge&lt;br /&gt;
segments is only apparent, it does make it easy to write a program as a&lt;br /&gt;
dual mode application. By using the shift value of 12 in real mode and&lt;br /&gt;
using the OS/2 supplied value in protect mode, the same code functions&lt;br /&gt;
correctly in either mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.3  Executing from Data Segments&lt;br /&gt;
We saw that OS/2 provides the huge segment mechanism to get around the&lt;br /&gt;
segment size restriction imposed by the hardware. OS/2 likewise circumvents&lt;br /&gt;
another hardware restriction--the inability to execute code from data&lt;br /&gt;
segments. Although the demand loading, the discarding, and the swapping of&lt;br /&gt;
code segments make one use of running code from data segments--code&lt;br /&gt;
overlays--obsolete, the capability is still needed. Some high-performance&lt;br /&gt;
programs--the presentation manager, for example--compile &amp;quot;on the fly&amp;quot;&lt;br /&gt;
special code to perform time-critical tasks, such as flipping bits in EGA&lt;br /&gt;
display memory. The optimal sequence may differ depending on several&lt;br /&gt;
factors, so a program may need to compile such code and execute it, gaining&lt;br /&gt;
a significant increase in efficiency over some other approach. OS/2&lt;br /&gt;
supports this need by means of the DosCreateCSAlias call.&lt;br /&gt;
     When DosCreateCSAlias is called with a selector for a data segment, it&lt;br /&gt;
creates a totally different code segment selector (in the eyes of 80286&lt;br /&gt;
hardware) that by some strange coincidence points to exactly the same&lt;br /&gt;
memory locations as does the data segment selector. As a result, code is&lt;br /&gt;
not actually executing from a data segment but from a code segment. Because&lt;br /&gt;
the code segment exactly overlaps that other data segment, the desired&lt;br /&gt;
effect is achieved. The programmer need only be careful to use the data&lt;br /&gt;
selector when writing the segment and to use the code selector when&lt;br /&gt;
executing it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.2.4  Memory Suballocation&lt;br /&gt;
All memory objects discussed so far have been segments. OS/2 provides a&lt;br /&gt;
facility called memory suballocation that allocates pieces of memory from&lt;br /&gt;
within an application's segment. Pieces of memory can be suballocated from&lt;br /&gt;
within a segment, grown, shrunk, and released. OS/2 uses a classic heap&lt;br /&gt;
algorithm to do this. The DosSubAlloc call uses space made available from&lt;br /&gt;
earlier DosSubFrees when possible, growing the segment as necessary when&lt;br /&gt;
the free heap space is insufficient. We will call the pieces of memory&lt;br /&gt;
returned by DosSubAlloc heap objects.&lt;br /&gt;
     The memory suballocation package works within the domain of a process.&lt;br /&gt;
The suballocation package doesn't allocate the memory from some &amp;quot;system&lt;br /&gt;
pool&amp;quot; outside the process's address space, as does the segment allocator.&lt;br /&gt;
The suballocation package doesn't even allocate segments; it manages only&lt;br /&gt;
segments supplied by and owned by (or at least accessible to) the caller.&lt;br /&gt;
This is a feature because memory protection is on a per-segment basis. If&lt;br /&gt;
the suballocation package were to get its space from some system global&lt;br /&gt;
segment, a process that overwrote its heap object could damage one&lt;br /&gt;
belonging to another process. Figure 9-3 illustrates memory suballocation.&lt;br /&gt;
It shows a segment j being suballocated. H is the suballocation header; the&lt;br /&gt;
shaded areas are free space.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       ÚÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
       ³  Segment  ³       ³  Segment  ³       ³  Segment  ³&lt;br /&gt;
       ³     i     ³       ³     j     ³       ³     k     ³&lt;br /&gt;
       ÀÄÄÄÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÄÄÄÙ       ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                          /            \&lt;br /&gt;
                        /                \&lt;br /&gt;
                      /                    \&lt;br /&gt;
                    /                        \&lt;br /&gt;
                  /                            \&lt;br /&gt;
                /                                \&lt;br /&gt;
              /                                    \&lt;br /&gt;
            /                                        \&lt;br /&gt;
          /                                            \&lt;br /&gt;
        /                                                \&lt;br /&gt;
      /                                                    \&lt;br /&gt;
    /                                                        \&lt;br /&gt;
  /                                                            \&lt;br /&gt;
ÚÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÂÂÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÂÄÄÄÄÄÄÄÂÄÄÄÂÄÄÄÄÂÄÄÄÂÄÄÄÄÄÄÄ¿&lt;br /&gt;
³H ³  ³     ³°°°³³°°°°³           ³ ³°°°°°°°³   ³    ³   ³°°°°°°°³&lt;br /&gt;
³  ³  ³     ³°°°³³°°°°³           ³ ³°°°°°°°³   ³    ³   ³°°°°°°°³&lt;br /&gt;
ÀÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÁÁÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÁÄÄÄÁÄÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 9-3.  Memory suballocation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     We said that the memory suballocator subdivides segments that are&lt;br /&gt;
accessible to the client process. This means that you can use it to&lt;br /&gt;
subdivide space in a shared memory segment. Such a technique can be handy&lt;br /&gt;
when two or more processes are using a shared memory segment for&lt;br /&gt;
intercommunication, but there is risk because an error in one process can&lt;br /&gt;
easily corrupt the heap objects of another.&lt;br /&gt;
     Earlier, in the discussion of dynamic link subsystems, we described&lt;br /&gt;
facilities and techniques for writing a reliable subsystem. The OS/2 memory&lt;br /&gt;
suballocation package is a good example of such a subsystem, so let's look&lt;br /&gt;
at its workings more closely. The first try at the suballocation package&lt;br /&gt;
produced a straightforward heap allocator, much like the one in a C or&lt;br /&gt;
Pascal runtime library. It maintained a free chain of heap objects and&lt;br /&gt;
allocated them at its client's request. If the closest-size free heap&lt;br /&gt;
object was still bigger than the request, it was split into an allocated&lt;br /&gt;
part and a free part. Freed heap objects were coalesced with any adjacent&lt;br /&gt;
free objects. The suballocation package took a segment pointer and some&lt;br /&gt;
other arguments and returned some values--an offset and the changed data in&lt;br /&gt;
the segment itself where the heap headers were stored. If we stretch things&lt;br /&gt;
a little and consider the changed state of the supplied data segment as a&lt;br /&gt;
returned value, then the suballocation package at this stage is much like a&lt;br /&gt;
function: It has no state of its own; it merely returns values computed&lt;br /&gt;
only from the input arguments. This simple suballocation dynlink routine&lt;br /&gt;
uses no global data segments and doesn't even need an instance data&lt;br /&gt;
segment.&lt;br /&gt;
     This simple implementation has an important drawback: More than one&lt;br /&gt;
process can't safely use it to manage a shared memory segment; likewise,&lt;br /&gt;
multiple threads within one process can't use it. The heap free list is a&lt;br /&gt;
critical section; if multiple threads call the suballocator on the same&lt;br /&gt;
segment, the heap free list can become corrupted. This problem necessitated&lt;br /&gt;
upgrading the suballocation package to use a semaphore to protect the&lt;br /&gt;
critical section. If we didn't want to support suballocation of shared&lt;br /&gt;
memory and were only worried about multiple threads within a task, we could&lt;br /&gt;
use RAM semaphores located in the managed segment itself to protect the&lt;br /&gt;
critical section. The semaphore might be left set if the process died&lt;br /&gt;
unexpectedly, but the managed segment isn't shared. It's going to be&lt;br /&gt;
destroyed in any case, so we don't care.&lt;br /&gt;
     But, even in this simple situation of managing only privately owned&lt;br /&gt;
segments, we must concern ourselves with some special situations. One&lt;br /&gt;
problem is signals: What if the suballocator is called with thread 1, and a&lt;br /&gt;
signal (such as SIGINT, meaning that the user pressed Ctrl-C) comes in?&lt;br /&gt;
Thread 1 is interrupted from the suballocation critical section to execute&lt;br /&gt;
the signal handler. Often signal handlers return to the interrupted code,&lt;br /&gt;
and all is well. But what if the signal handler does not return but jumps&lt;br /&gt;
to the application's command loop? Or what if it does return, but before it&lt;br /&gt;
does so calls the memory suballocator? In these two cases, we'd have a&lt;br /&gt;
deadlock on the critical section. We can solve these problems by using the&lt;br /&gt;
DosHoldSignal function. DosHoldSignal does for signals what the CLI&lt;br /&gt;
instruction does for hardware interrupts: It holds them off for a short&lt;br /&gt;
time. Actually, it holds them off forever unless the application releases&lt;br /&gt;
them, but holding signals for more than a second or two is poor practice.&lt;br /&gt;
If you precede the critical section's semaphore claim call with a signal&lt;br /&gt;
hold and follow the critical section's semaphore release call with a signal&lt;br /&gt;
release, you're protected from deadlocks caused by signal handling.&lt;br /&gt;
     Note that unlike the CLI instruction, DosHoldSignal calls nest. OS/2&lt;br /&gt;
counts the number of DosHoldSignal &amp;quot;hold&amp;quot; calls made and holds signals off&lt;br /&gt;
until an equal number of &amp;quot;release&amp;quot; calls are issued. This means that a&lt;br /&gt;
routine can safely execute a hold/release pair without affecting the state&lt;br /&gt;
of its calling code. If the caller had signals held at the time of the&lt;br /&gt;
call, they will remain held. If signals were free at the time of the call,&lt;br /&gt;
the callee's &amp;quot;release&amp;quot; call restores them to that state.&lt;br /&gt;
     Whenever dynlink packages make any call that changes the state of the&lt;br /&gt;
process or the thread, they must be sure to restore that state before they&lt;br /&gt;
return to their caller. Functions that nest, such as DosHoldSignal,&lt;br /&gt;
accomplish this automatically. For other functions, the dynlink package&lt;br /&gt;
should explicitly discover and remember the previous state so that it can&lt;br /&gt;
be restored.&lt;br /&gt;
     Our problems aren't over though. A second problem is brought about by&lt;br /&gt;
the DosExitList facility. If a client process's thread is in the&lt;br /&gt;
suballocation package's critical section and the client terminates&lt;br /&gt;
suddenly--it could be killed externally or have a GP fault--the process&lt;br /&gt;
might not die immediately. If any DosExitList handlers are registered, they&lt;br /&gt;
will be called. They might call the memory suballocator, and once again we&lt;br /&gt;
face deadlock. We could solve this situation with the classic approach of&lt;br /&gt;
making a bug into a feature: Document that the suballocator can't be called&lt;br /&gt;
at exitlist time. This may make sense for some dynlink subsystems, but it's&lt;br /&gt;
too restrictive for an important OS/2 facility. We've got to deal with this&lt;br /&gt;
problem too.&lt;br /&gt;
     The DosHoldSignal trick won't help us here. It would indeed prevent&lt;br /&gt;
external kills, but it would not prevent GP faults and the like. We could&lt;br /&gt;
say, &amp;quot;A program that GP faults is very sick, so all bets are off.&amp;quot; This&lt;br /&gt;
position is valid, except that if the program or one of its dynlink&lt;br /&gt;
subsystems uses DosExitList and the DosExitList handler tries to allocate&lt;br /&gt;
or release a heap object, the process will hang and never terminate&lt;br /&gt;
correctly. This is unacceptable because the user would be forced to reboot&lt;br /&gt;
to get rid of the moribund application. The answer is to use a system&lt;br /&gt;
semaphore rather than a RAM semaphore to protect the memory segment. System&lt;br /&gt;
semaphores are a bit slower than RAM semaphores, but they have some extra&lt;br /&gt;
features. One is that they can be made exclusive; only the thread that owns&lt;br /&gt;
the semaphore can release it. Coupled with this is an &amp;quot;owner death&amp;quot;&lt;br /&gt;
notification facility that allows a process's DosExitList handler an&lt;br /&gt;
opportunity to determine that one of its threads has orphaned a semaphore&lt;br /&gt;
(see 16.2 Data Integrity for details). Our suballocation package can now&lt;br /&gt;
protect itself by using exclusive system semaphores to protect its critical&lt;br /&gt;
section and by registering a DosExitList handler to release that semaphore.&lt;br /&gt;
The exitlist code can discover if a thread in its process has orphaned the&lt;br /&gt;
semaphore and, if so, can release it. Of course, releasing the semaphore&lt;br /&gt;
won't help if the heap headers are in an inconsistent state. You can write&lt;br /&gt;
the suballocation package so that the heap is never in an inconsistent&lt;br /&gt;
state, or you can write it to keep track of the modified state so that the&lt;br /&gt;
exitlist handler can repair the heap structure.&lt;br /&gt;
     In this later case, be sure the DosExitList handler you establish to&lt;br /&gt;
clean up the heap is called first (see DosExitList documentation).&lt;br /&gt;
     Finally, even if we decide that the client application won't be&lt;br /&gt;
allowed to issue suballocation requests during its own exitlist processing,&lt;br /&gt;
we want the memory suballocator to support allocating a shared segment&lt;br /&gt;
among many different processes. Because of this, the actual OS/2&lt;br /&gt;
suballocation package makes use of DosExitList so that the suballocation&lt;br /&gt;
structure and semaphores can be cleaned up should a client thread terminate&lt;br /&gt;
while in the suballocation critical section.&lt;br /&gt;
     The suballocation dynlink package does more than illustrate subsystem&lt;br /&gt;
design; it also illustrates the value of a system architecture that uses&lt;br /&gt;
dynlinks as a standard system interface, regardless of the type of code&lt;br /&gt;
that provides the service. As you have seen, the memory suballocation&lt;br /&gt;
package released with OS/2 version 1.0 doesn't reside in the kernel; it's&lt;br /&gt;
effectively a subroutine package. OS/2 in an 80286 environment will&lt;br /&gt;
undoubtedly preserve this approach in future releases, but a forthcoming&lt;br /&gt;
80386 version of OS/2 may not. The 80386 architecture supports paged&lt;br /&gt;
virtual memory, so memory swapping (actually, paging) can take place on&lt;br /&gt;
part of a segment. This future paging environment may precipitate some&lt;br /&gt;
changes in the memory suballocator. Perhaps we'll want to rearrange the&lt;br /&gt;
heap for better efficiency with paging, or perhaps the OS/2 kernel will&lt;br /&gt;
want to become involved so that it can better anticipate paging demands. In&lt;br /&gt;
any case, any future release of OS/2 has complete flexibility to upgrade&lt;br /&gt;
the memory suballocation package in any externally compatible fashion,&lt;br /&gt;
thanks to the standard interface provided by dynamic links.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.3  Segment Swapping&lt;br /&gt;
&lt;br /&gt;
One of the most important features of the 80286 memory management hardware&lt;br /&gt;
is swapping support. Swapping is a technique by which some code or data&lt;br /&gt;
segments in memory are written to a disk file, thus allowing the memory&lt;br /&gt;
they were using to be reclaimed for another purpose. Later, the swapped-out&lt;br /&gt;
code or data is reloaded into memory. This technique lets you run more&lt;br /&gt;
programs than can simultaneously fit in memory; all you need is enough&lt;br /&gt;
memory to hold the programs that are running at that particular moment.&lt;br /&gt;
Quiescent programs can be swapped to disk to make room for active ones.&lt;br /&gt;
Later, when the swapped programs become active, OS/2 reads them in and&lt;br /&gt;
resumes them. If necessary OS/2 first makes memory available by swapping&lt;br /&gt;
out another quiescent program.&lt;br /&gt;
     Although I used the word program above, swapping is actually done on a&lt;br /&gt;
segment basis. Segments are swapped out individually and completely; the&lt;br /&gt;
OS/2 swapping code doesn't pay attention to relationships between segments&lt;br /&gt;
(they aren't swapped in groups), and the 80286 hardware does not allow only&lt;br /&gt;
part of a segment to be swapped. I simplified the concept a bit in the&lt;br /&gt;
above paragraph. You need not swap out an entire process; you can swap out&lt;br /&gt;
some segments and leave others in memory. OS/2 can and commonly does run a&lt;br /&gt;
process when some of its segments are swapped out. As long as a process&lt;br /&gt;
does not try to use the swapped-out segments, it runs unhindered. If a&lt;br /&gt;
process references a swapped-out segment, the 80286 hardware generates a&lt;br /&gt;
special trap that OS/2 intercepts. The segment fault trap handler swaps in&lt;br /&gt;
the missing segment, first swapping out some other if need be, and then the&lt;br /&gt;
process resumes where it left off. Segment faulting is invisible to a&lt;br /&gt;
process; the process executes normally, except that a segment load&lt;br /&gt;
instruction takes on the order of 30 milliseconds instead of the usual 3&lt;br /&gt;
microseconds.&lt;br /&gt;
     When memory is depleted and a segment must be swapped, OS/2 has to&lt;br /&gt;
choose one to swap out. Making the right choice is important; for example,&lt;br /&gt;
consider a process that alternates references between segment A and segment&lt;br /&gt;
B. If A is swapped out, a poorly designed system might choose B to swap out&lt;br /&gt;
to make room for A. After a few instructions are executed, B has to be&lt;br /&gt;
swapped in. If A is in turn swapped out to make room for B, the system&lt;br /&gt;
would soon spend all its time swapping A and B to and from the disk. This&lt;br /&gt;
is called thrashing, and thrashing can destroy system performance. In other&lt;br /&gt;
words, the effect of swapping is to make some segment loads take 10,000&lt;br /&gt;
times longer than they would if the segment were in memory. Although the&lt;br /&gt;
number 10,000 seems very large, the actual time of about 30 milliseconds is&lt;br /&gt;
not, as long as we don't have to pay those 30 milliseconds very often.&lt;br /&gt;
     A lot hinges on choosing segments to swap out that won't be referenced&lt;br /&gt;
in the near future. OS/2 uses the LRU (Least Recently Used) scheme to&lt;br /&gt;
determine which segment it will swap out. The ideal choice is the segment--&lt;br /&gt;
among those currently in memory--that will be referenced last because this&lt;br /&gt;
postpones the swap-in of that segment as long as possible. Unfortunately,&lt;br /&gt;
it's mathematically provable that no operating system can predict the&lt;br /&gt;
behavior of arbitrary processes. Instead, operating systems try to make an&lt;br /&gt;
educated guess as to which segment in memory is least likely to be&lt;br /&gt;
referenced in the immediate future. The LRU scheme is precisely that--a&lt;br /&gt;
good guess. OS/2 figures that if a segment hasn't been used in a long time&lt;br /&gt;
then it probably won't be used for a long time yet, so it swaps out the&lt;br /&gt;
segment that was last used the longest time ago--in other words, the least&lt;br /&gt;
recently used segment.&lt;br /&gt;
     Of course, it's easy to construct an example where the LRU decision is&lt;br /&gt;
the wrong one or even the worst one. The classic example is a program that&lt;br /&gt;
references, round robin, N segments when there is room in memory for only&lt;br /&gt;
N-1. When you attempt to make room for segment I, the least recently used&lt;br /&gt;
segment will be I+1, which in fact is the segment that will next be used. A&lt;br /&gt;
discussion of reference locality and working set problems, as these are&lt;br /&gt;
called, is beyond the scope of this book. Authors of programs that will&lt;br /&gt;
make repetitious accesses to large bodies of data or code should study the&lt;br /&gt;
available literature on virtual memory systems. Remember, on an 80286, OS/2&lt;br /&gt;
swaps only on a segment basis. A future 80386 release of OS/2 will swap, or&lt;br /&gt;
page, on a 4 KB page basis.&lt;br /&gt;
     The swapping algorithm is strictly LRU among all swap-eligible&lt;br /&gt;
segments in the system. Thread/process priority is not considered; system&lt;br /&gt;
segments that are marked swappable get no special treatment. Some system&lt;br /&gt;
segments are marked nonswappable, however. For example, swapping out the&lt;br /&gt;
OS/2 code that performs swap-ins would be embarrassing. Likewise, the disk&lt;br /&gt;
driver code for the swapping disk must not be swapped out. Some kernel and&lt;br /&gt;
device driver code is called at interrupt time; this is never swapped&lt;br /&gt;
because of the swap-in delay and because of potential interference between&lt;br /&gt;
the swapped-out interrupt handling code and the interrupt handling code of&lt;br /&gt;
the disk driver that will do the swap-in. Finally, some kernel code is&lt;br /&gt;
called in real mode in response to requests from the 3x box. No real mode&lt;br /&gt;
code can be swapped because the processor does not support segment faults&lt;br /&gt;
when running in real mode.&lt;br /&gt;
     The technique of running more programs then there is RAM to hold them&lt;br /&gt;
is called memory overcommit. OS/2 has to keep careful track of the degree&lt;br /&gt;
of overcommit so that it doesn't find itself with too much of a good thing-&lt;br /&gt;
-not enough free RAM, even with swapping, to swap in a swapped-out process.&lt;br /&gt;
Such a situation is doubly painful: Not only can the user not access or&lt;br /&gt;
save the data that he or she has spent the last four hours working on, but&lt;br /&gt;
OS/2 can't even tell the program what's wrong because it can't get the&lt;br /&gt;
program into memory to run it. To prevent this, OS/2 keeps track of its&lt;br /&gt;
commitments and overcommitments in two ways. First, before it starts a&lt;br /&gt;
process, OS/2 ensures that there is enough swap space to run it. Second, it&lt;br /&gt;
ensures that there is always enough available RAM to execute a swapped-out&lt;br /&gt;
process.&lt;br /&gt;
     At first glance, knowing if RAM is sufficient to run a process seems&lt;br /&gt;
simple--either the process fits into memory or it doesn't. Life is a bit&lt;br /&gt;
more complicated than that under OS/2 because the segments of a program or&lt;br /&gt;
a dynlink library may be marked for demand loading. This means that they&lt;br /&gt;
won't come in when the program starts executing but may be called in later.&lt;br /&gt;
Obviously, once a program starts executing, it can make nearly unlimited&lt;br /&gt;
demands for memory. When a program requests a memory allocation, however,&lt;br /&gt;
OS/2 can return an error code if available memory is insufficient. The&lt;br /&gt;
program can then deal with the problem: make do with less, refuse the&lt;br /&gt;
user's command, and so forth.&lt;br /&gt;
     OS/2 isn't concerned about a program's explicit memory requests&lt;br /&gt;
because they can always be refused; the implicit memory requests are the&lt;br /&gt;
problem--faulting in a demand load segment, for example. Not only is there&lt;br /&gt;
no interface to give the program an error code,&lt;br /&gt;
4. A demand load segment is faulted in via a &amp;quot;load segment register&amp;quot;&lt;br /&gt;
instruction. These CPU instructions don't return error codes!&lt;br /&gt;
4 but the program may be&lt;br /&gt;
unable to proceed without the segment. As a result, when a program is first&lt;br /&gt;
loaded (via a DosExecPgm call), OS/2 sums the size of all its impure&lt;br /&gt;
segments even if they are marked for &amp;quot;load on demand.&amp;quot; The same computation&lt;br /&gt;
is done for all the loadtime dynlink libraries it references and for all&lt;br /&gt;
the libraries they reference and so on. This final number, plus the&lt;br /&gt;
internal system per-process overhead, is the maximum implicit memory demand&lt;br /&gt;
of the program. If that much free swap space is available, the program can&lt;br /&gt;
start execution.&lt;br /&gt;
     You have undoubtedly noticed that I said we could run the program if&lt;br /&gt;
there was enough swap space. But a program must be in RAM to execute,&lt;br /&gt;
so why don't we care about the amount of available RAM space? We&lt;br /&gt;
do care. Not about the actual amount of free RAM when we start a program,&lt;br /&gt;
but about the amount of RAM that can be made free--by swapping--if needed.&lt;br /&gt;
If some RAM contains a swappable segment, then we can swap it&lt;br /&gt;
because we set aside enough swap space for the task. Pure segments, by the&lt;br /&gt;
way, are not normally swapped. In lieu of a swap-out, OS/2 simply discards&lt;br /&gt;
them. When it's time to swap them in, OS/2 reloads them from their original&lt;br /&gt;
.EXE or .DLL files.&lt;br /&gt;
5. An exception to this is programs that were executed from removable&lt;br /&gt;
media. OS/2 preloads all pure segments from such .EXE and .DLL files&lt;br /&gt;
and swaps them as necessary. This prevents certain deadlock problems&lt;br /&gt;
involving the hard error daemon and the volume management code.&lt;br /&gt;
5&lt;br /&gt;
     Because not all segments of a process need to be in memory for the&lt;br /&gt;
process to execute, we don't have to ensure enough free RAM for the entire&lt;br /&gt;
process, just enough so that we can simultaneously load six 64 KB segments-&lt;br /&gt;
-the maximum amount of memory needed to run any process. The numbers 6 and&lt;br /&gt;
64 KB are derived from the design of the 80286. To execute even a single&lt;br /&gt;
instruction of a process, all the segments selected by the four segment&lt;br /&gt;
registers must be in memory. The other two necessary segments come from the&lt;br /&gt;
worst case scenario of a program trying to execute a far return instruction&lt;br /&gt;
from a ring 2 segment (see 18.1 I/O Privilege Mechanism). The four&lt;br /&gt;
segments named in the registers must be present for the instruction to&lt;br /&gt;
start, and the two new segments--CS and SS--that the far return instruction&lt;br /&gt;
will reference must be present for the instruction to complete. That makes&lt;br /&gt;
six; the 64 KB comes from the maximum size a segment can reach. As a&lt;br /&gt;
result, as long as OS/2 can free up those six 64 KB memory regions, by&lt;br /&gt;
swapping and discarding if necessary, any swapped-out program can execute.&lt;br /&gt;
     Naturally, if that were the only available memory and it had to be&lt;br /&gt;
shared by all running processes, system response would be very poor.&lt;br /&gt;
Normally, much more RAM space is available. The memory overcommit code is&lt;br /&gt;
concerned only that all processes can run; it won't refuse to start a&lt;br /&gt;
process because it might execute slowly. It could be that the applications&lt;br /&gt;
that a particular user runs and their usage pattern are such that the user&lt;br /&gt;
finds the performance acceptable and thus hasn't bought more memory. Or&lt;br /&gt;
perhaps the slowness is a rare occurrence, and the user is willing to&lt;br /&gt;
accept it just this once. In general, if the system thrashes--&lt;br /&gt;
spends too much time swapping--it's a soft failure: The user knows what's&lt;br /&gt;
wrong, the user knows what to do to make it get better (run fewer programs&lt;br /&gt;
or buy more memory), and the user can meanwhile continue to work.&lt;br /&gt;
     Clearly, because all segments of the applications are swappable and&lt;br /&gt;
because we've ensured that the swap space is sufficient for all of them,&lt;br /&gt;
initiating a new process doesn't consume any of our free or freeable RAM.&lt;br /&gt;
It's the device drivers and their ability to allocate nonswappable segments&lt;br /&gt;
that can drain the RAM pool. For this reason, OS/2 may refuse to load a&lt;br /&gt;
device driver or to honor a device driver's memory allocation request if to&lt;br /&gt;
do so would leave less than six 64 KB areas of RAM available.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.3.1  Swapping Miscellany&lt;br /&gt;
The system swap space consists of a special file, called SWAPPER.DAT,&lt;br /&gt;
created at boot time. The location of the file is described in the&lt;br /&gt;
CONFIG.SYS file. OS/2 may not allocate the entire maximum size of the swap&lt;br /&gt;
file initially; instead, it may allocate a smaller size and grow the swap&lt;br /&gt;
file to its maximum size if needed. The swap file may grow, but in OS/2&lt;br /&gt;
version 1.0 it never shrinks.&lt;br /&gt;
     The available swap space in the system is more than the maximum size&lt;br /&gt;
of the swap file; it also includes extra RAM. Clearly, a system with 8 MB&lt;br /&gt;
of RAM and a 200 KB swap file should be able to run programs that consume&lt;br /&gt;
more than 200 KB. After setting aside the memory consumed by nonswappable&lt;br /&gt;
segments and our six 64 KB reserved areas, the remaining RAM is considered&lt;br /&gt;
part of the swap file for memory overcommit accounting purposes.&lt;br /&gt;
     We mentioned in passing that memory used in real mode can't be&lt;br /&gt;
swapped. This means that the entire 3x box memory area is nonswappable. In&lt;br /&gt;
fact, the casual attitude of MS-DOS applications toward memory allocation&lt;br /&gt;
forces OS/2 to keep a strict boundary between real mode and protect mode&lt;br /&gt;
memory. Memory below the RMSIZE value specified in CONFIG.SYS belongs&lt;br /&gt;
exclusively to the real mode program, minus that consumed by the device&lt;br /&gt;
drivers and the parts of the OS/2 kernel that run in real mode.&lt;br /&gt;
     Early in the development of OS/2, attempts were made to put protect&lt;br /&gt;
mode segments into any unused real mode memory, but we abandoned this&lt;br /&gt;
approach. First, because the risk was great that the real mode program&lt;br /&gt;
might overwrite part of the segment. Although this is technically a bug on&lt;br /&gt;
the part of the real mode application, such bugs generally do not affect&lt;br /&gt;
program execution in an MS-DOS environment because that memory is unused at&lt;br /&gt;
the time. Thus, such bugs undoubtedly exist unnoticed in today's MS-DOS&lt;br /&gt;
applications, waiting to wreak havoc in the OS/2 environment.&lt;br /&gt;
     A second reason concerns existing real mode applications having been&lt;br /&gt;
written for a single-tasking environment. Such an application commonly asks&lt;br /&gt;
for 1 MB of memory, a request that must be refused. The refusal, however,&lt;br /&gt;
also specifies the amount of memory available at the time of the call. Real&lt;br /&gt;
mode applications then turn around and ask for that amount, but they don't&lt;br /&gt;
check to see if an &amp;quot;insufficient memory&amp;quot; error code was returned from the&lt;br /&gt;
second call. After all, how could such a code be returned? The operating&lt;br /&gt;
system has just said that the memory was available. This coding sequence&lt;br /&gt;
can cause disaster in a multitasking environment where the memory might&lt;br /&gt;
have been allocated elsewhere between the first and second call from the&lt;br /&gt;
application. This is another reason OS/2 sets aside a fixed region of&lt;br /&gt;
memory for the 3x box and never uses it for other purposes, even if it&lt;br /&gt;
appears to be idle.&lt;br /&gt;
     We mentioned that OS/2's primary concern is that programs be able to&lt;br /&gt;
execute at all; whether they execute well is the user's problem. This&lt;br /&gt;
approach is acceptable because OS/2 is a single-user system. Multiuser&lt;br /&gt;
systems need to deal with thrashing situations because the users that&lt;br /&gt;
suffer from thrashing may not be the ones who created it and may be&lt;br /&gt;
powerless to alleviate it. In a single-user environment, however, the user&lt;br /&gt;
is responsible for the load that caused the thrashing, the user is the one&lt;br /&gt;
who is suffering from it, and the user is the one who can fix the situation&lt;br /&gt;
by buying more RAM or terminating a few applications. Nevertheless,&lt;br /&gt;
applications with considerable memory needs should be written so as to&lt;br /&gt;
minimize their impact on the system swapper.&lt;br /&gt;
     Fundamentally, all swapping optimization techniques boil down to one&lt;br /&gt;
issue: locality of reference. This means keeping the memory locations that&lt;br /&gt;
are referenced near one another in time and in space. If your program&lt;br /&gt;
supports five functions, put the code of each function in a separate&lt;br /&gt;
segment, with another segment holding common code. The user can then work&lt;br /&gt;
with one function, and the other segments can be swapped. If each function&lt;br /&gt;
had some code in each of five segments, all segments would have to be in&lt;br /&gt;
memory at all times.&lt;br /&gt;
     A large body of literature deals with these issues because of the&lt;br /&gt;
prevalence of virtual memory systems in the mainframe environment. Most of&lt;br /&gt;
this work was done when RAM was very expensive. To precisely determine&lt;br /&gt;
which segments or pages should be resident and which should be swapped was&lt;br /&gt;
worth a great deal of effort. Memory was costly, and swapping devices were&lt;br /&gt;
fast, so algorithms were designed to &amp;quot;crank the screws down tight&amp;quot; and free&lt;br /&gt;
up as much memory as possible. After all, if they misjudged and swapped&lt;br /&gt;
something that was needed soon, it could be brought back in quickly. The&lt;br /&gt;
OS/2 environment is inverted: RAM is comparatively cheap, and the swapping&lt;br /&gt;
disk, being the regular system hard disk, is comparatively slow.&lt;br /&gt;
Consequently, OS/2's swapping strategy is to identify segments that are&lt;br /&gt;
clearly idle and swap them (because cheap RAM doesn't mean free RAM) but&lt;br /&gt;
not to judge things so closely that segments are frequently swapped when&lt;br /&gt;
they should not be.&lt;br /&gt;
     A key concept derived from this classic virtual memory work is that of&lt;br /&gt;
the working set. A thread's working set is the set of segments it will&lt;br /&gt;
reference &amp;quot;soon&amp;quot;--in the next several seconds or few minutes. Programmers&lt;br /&gt;
should analyze their code to determine its working sets; obviously the set&lt;br /&gt;
of segments in the working set will vary with the work the application is&lt;br /&gt;
doing. Code and data should be arranged between segments so that the size&lt;br /&gt;
of each common working set consists of a minimum amount of memory. For&lt;br /&gt;
example, if a program contains extensive code and data to deal with&lt;br /&gt;
uncommon error situations, these items should reside in separate segments&lt;br /&gt;
so that they aren't resident except when needed. You don't want to burden&lt;br /&gt;
the system with too many segments; two functions that are frequently used&lt;br /&gt;
together should occupy the same segment, but large unrelated bodies of code&lt;br /&gt;
and data should have their own segments or be grouped with other items that&lt;br /&gt;
are in their working set. Consider segment size when packing items into&lt;br /&gt;
segments. Too many small segments increase system overhead; large segments&lt;br /&gt;
decrease the efficiency of the swap mechanism. Splitting a segment in two&lt;br /&gt;
doesn't make sense if all code in the segment belongs to the same working&lt;br /&gt;
set, but it does make sense to split large bodies of unrelated code and&lt;br /&gt;
data.&lt;br /&gt;
     As we said before, an exhaustive discussion of these issues is beyond&lt;br /&gt;
the scope of this book. Programmers writing memory-intensive applications&lt;br /&gt;
should study the literature and their programs to optimize their&lt;br /&gt;
performance in an OS/2 environment. Minimizing an application's memory&lt;br /&gt;
requirements is more than being a &amp;quot;good citizen&amp;quot;; the smaller a program's&lt;br /&gt;
working set, the better it will run when the system load picks up.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
9.4  Status and Information&lt;br /&gt;
&lt;br /&gt;
OS/2 takes advantage of the 80286 LDT and GDT architecture in providing two&lt;br /&gt;
special segments, called infosegs, that contain system information. OS/2&lt;br /&gt;
updates these segments when changes take place, so their information is&lt;br /&gt;
always current. One infoseg is global, and the other is local. The global&lt;br /&gt;
infoseg contains information about the system as a whole; the local infoseg&lt;br /&gt;
contains process specific data. Naturally, the global infoseg is read only&lt;br /&gt;
and is shared among all processes. Local infosegs are also read only, but&lt;br /&gt;
each process has its own.&lt;br /&gt;
     The global infoseg contains time and date information. The &amp;quot;seconds&lt;br /&gt;
elapsed since 1970&amp;quot; field is particularly useful for time-stamping events&lt;br /&gt;
because calculating the interval between two times is easy. Simply subtract&lt;br /&gt;
and then divide by the number of seconds in the unit of time in which&lt;br /&gt;
you're interested. It's important that you remember that the date/time&lt;br /&gt;
fields are 32-bit fields but the 80286 reads data 16 bits at a time. Thus,&lt;br /&gt;
if an application reads the two time-stamp words at the same time as they&lt;br /&gt;
are being updated, it may read a bad value--not a value off by 1, but a&lt;br /&gt;
value that is off by 63335. The easiest way to deal with this is to read&lt;br /&gt;
the value and then compare the just read value with the infoseg contents.&lt;br /&gt;
If they are the same, your read value is correct. If they differ, continue&lt;br /&gt;
reading and comparing until the read and infoseg  values agree. The RAS&lt;br /&gt;
6. Reliability, Availability, and Serviceability. A buzzword that refers&lt;br /&gt;
to components intended to aid field diagnosis of system malfunctions.&lt;br /&gt;
6&lt;br /&gt;
information is used for field system diagnosis and is not of general&lt;br /&gt;
interest to programmers.&lt;br /&gt;
     The local infoseg segment contains process and thread information. The&lt;br /&gt;
information is accurate for the currently executing thread. The subscreen&lt;br /&gt;
group value is used by the presentation manager subsystem and is not of&lt;br /&gt;
value to applications. For more information on global and local infosegs,&lt;br /&gt;
see the OS/2 reference manual.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
10  Environment Strings&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
A major requirement of OS/2 is the ability to support logical device and&lt;br /&gt;
directory names. For example, a program needs to write a temporary scratch&lt;br /&gt;
file to the user's fastest disk. Which disk is that? Is it drive C, the&lt;br /&gt;
hard disk? Some machines don't have a hard disk. Is it drive B, the floppy&lt;br /&gt;
drive? Some machines don't have a drive B. And even if a hard disk on drive&lt;br /&gt;
C exists, maybe drive D also exists and has more free space. Or perhaps&lt;br /&gt;
drive E is preferred because it's a RAM disk. Perhaps it's not, though,&lt;br /&gt;
because the user wants the scratch file preserved when the machine is&lt;br /&gt;
powered down. This program needs the ability to specify a logical&lt;br /&gt;
directory--the scratch file directory--rather than a physical drive and&lt;br /&gt;
directory such as A:\ or C:\TEMP. The user could then specify the physical&lt;br /&gt;
location (drive and directory) that corresponds to the logical&lt;br /&gt;
directory.&lt;br /&gt;
     Another example is a spell-checker program that stores two&lt;br /&gt;
dictionaries on a disk. Presumably, the dictionary files were copied to a&lt;br /&gt;
hard disk when the program was installed, but on which drive and directory?&lt;br /&gt;
The checker's author could certainly hard code a directory such as&lt;br /&gt;
C:\SPELLCHK\DICT1. But what if the user doesn't have a C drive, or what if&lt;br /&gt;
drive C is full and the user wants to use drive D instead? How can this&lt;br /&gt;
program offer the user the flexibility of putting the dictionary files&lt;br /&gt;
where they best fit and yet still find them when it needs them?&lt;br /&gt;
     The answer to these problems is a logical device and directory name&lt;br /&gt;
facility. Such a facility should have three characteristics:&lt;br /&gt;
&lt;br /&gt;
     þ  It should allow the user to map the logical directories onto the&lt;br /&gt;
        actual (physical) devices and directories at will. It should be&lt;br /&gt;
        possible to change these mappings without changing the programs&lt;br /&gt;
        that use them.&lt;br /&gt;
&lt;br /&gt;
     þ  The set of possible logical devices and directories should be very&lt;br /&gt;
        large and arbitrarily expandable. Some new program, such as our&lt;br /&gt;
        spelling checker, will always need a new logical directory.&lt;br /&gt;
&lt;br /&gt;
     þ  The name set should be large and collision free. Many programs will&lt;br /&gt;
        want to use logical directory names. If all names must come from a&lt;br /&gt;
        small set of possibilities, such as X1, X2, X3, and so on, two&lt;br /&gt;
        applications, written independently, may each choose the same name&lt;br /&gt;
        for conflicting uses.&lt;br /&gt;
&lt;br /&gt;
     The original version of MS-DOS did not provide for logical devices and&lt;br /&gt;
directories. In those days a maximum PC configuration consisted of two&lt;br /&gt;
floppy disks. Operating the machine entailed playing a lot of &amp;quot;disk jockey&amp;quot;&lt;br /&gt;
as the user moved system, program, and data disks in and out of the drives.&lt;br /&gt;
The user was the only one who could judge which drive should contain which&lt;br /&gt;
floppy and its associated data, and data files moved from drive to drive&lt;br /&gt;
dynamically. A logical device mechanism would have been of little use.&lt;br /&gt;
Logical directories were not needed because MS-DOS version 1.0 didn't&lt;br /&gt;
support directories. MS-DOS versions 2.x and 3.x propagated the &amp;quot;physical&lt;br /&gt;
names only&amp;quot; architecture because of memory limitations and because of the&lt;br /&gt;
catch-22 of new operating system features: Applications won't take&lt;br /&gt;
advantage of the new feature because many machines are running older&lt;br /&gt;
versions of MS-DOS without that new feature.&lt;br /&gt;
     None of these reasons holds true for OS/2. All OS/2 protect mode&lt;br /&gt;
applications will be rewritten. OS/2 has access to plenty of memory.&lt;br /&gt;
Finally, OS/2 needs a logical drive/directory mechanism: All OS/2 machines&lt;br /&gt;
have hard disks or similar facilities, and all OS/2 machines will run a&lt;br /&gt;
variety of sophisticated applications that need access to private files and&lt;br /&gt;
work areas. As a result, the environment string mechanism in MS-DOS has&lt;br /&gt;
been expanded to serve as the logical name in OS/2.&lt;br /&gt;
     Because of the memory allocation techniques employed by MS-DOS&lt;br /&gt;
programs and because of the lack of segment motion and swapping in real&lt;br /&gt;
mode, the MS-DOS environment list was very limited in size. The size of the&lt;br /&gt;
environment segment was easily exceeded. OS/2 allows environment segments&lt;br /&gt;
to be grown arbitrarily, at any time, subject only to the hardware's 64 KB&lt;br /&gt;
length limitation. In keeping with the OS/2 architecture, each process has&lt;br /&gt;
its own environment segment. By default, the child inherits a copy of the&lt;br /&gt;
parent's segment, but the parent can substitute other environment values at&lt;br /&gt;
DosExecPgm time.&lt;br /&gt;
     Using the environment string facility to provide logical names is&lt;br /&gt;
straightforward. If a convention for the logical name that you need doesn't&lt;br /&gt;
already exist, you must choose a meaningful name. Your installation&lt;br /&gt;
instructions or software should document how to use the environment string;&lt;br /&gt;
the application should display an error message or use an appropriate&lt;br /&gt;
default if the logical names do not appear in the environment string.&lt;br /&gt;
Because each process has its own environment segment that it inherited from&lt;br /&gt;
its parent, batch files, startup scripts, and initiator programs that load&lt;br /&gt;
applications can conveniently set up the necessary strings. This also&lt;br /&gt;
allows several applications or multiple copies of the same application to&lt;br /&gt;
define the same logical name differently.&lt;br /&gt;
     The existing conventions are:&lt;br /&gt;
&lt;br /&gt;
     PATH=&lt;br /&gt;
     PATH defines a list of directories that CMD.EXE searches when it has&lt;br /&gt;
     been instructed to execute a program. The directories are searched&lt;br /&gt;
     from left to right and are separated by semicolons. For example,&lt;br /&gt;
&lt;br /&gt;
        PATH=C:\BIN;D:\TOOLS;.&lt;br /&gt;
&lt;br /&gt;
     means search C:\BIN first, D:\TOOLS second, and the current working&lt;br /&gt;
     directory third.&lt;br /&gt;
&lt;br /&gt;
     DPATH=&lt;br /&gt;
     DPATH defines a list of directories that programs may search to locate&lt;br /&gt;
     a data file. The directories are searched from left to right and are&lt;br /&gt;
     separated by semicolons. For example:&lt;br /&gt;
&lt;br /&gt;
        DPATH=C:\DBM;D:\TEMP;.&lt;br /&gt;
&lt;br /&gt;
Applications use DPATH as a convenience to the user: A user can work from&lt;br /&gt;
one directory and reference data files in another directory, named in the&lt;br /&gt;
DPATH string, without specifying the full path names of the data files.&lt;br /&gt;
Obviously, applications and users must use this technique with care.&lt;br /&gt;
Searching too widely for a filename is extremely dangerous; the wrong file&lt;br /&gt;
may be found because filenames themselves are often duplicated in different&lt;br /&gt;
directories. To use the DPATH string, an application must first use&lt;br /&gt;
DosScanEnv to locate the DPATH string, and then it must use DosSearchPath&lt;br /&gt;
to locate the data file.&lt;br /&gt;
&lt;br /&gt;
     INCLUDE=&lt;br /&gt;
     The INCLUDE name defines the drive and directory where compiler and&lt;br /&gt;
     assembler standard include files are located.&lt;br /&gt;
&lt;br /&gt;
     INIT=&lt;br /&gt;
     The INIT name defines the drive and directory that contains&lt;br /&gt;
     initialization and configuration information for the application. For&lt;br /&gt;
     example, some applications define files that contain the user's&lt;br /&gt;
     preferred defaults. These files might be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
     LIB=&lt;br /&gt;
     The LIB name defines the drive and directory where the standard&lt;br /&gt;
     language library modules are kept.&lt;br /&gt;
&lt;br /&gt;
     PROMPT=&lt;br /&gt;
     The PROMPT name defines the CMD.EXE prompt string. Special character&lt;br /&gt;
     sequences are defined so that the CMD.EXE prompt can contain the&lt;br /&gt;
     working directory, the date and time, and so on. See CMD.EXE&lt;br /&gt;
     documentation for details.&lt;br /&gt;
&lt;br /&gt;
     TEMP=&lt;br /&gt;
     The TEMP name defines the drive and directory for temporary files.&lt;br /&gt;
     This directory is on a device that is relatively fast and has&lt;br /&gt;
     sufficient room for scratch files. The TEMP directory should be&lt;br /&gt;
     considered volatile; its contents can be lost during a reboot&lt;br /&gt;
     operation.&lt;br /&gt;
&lt;br /&gt;
     The environment segment is a very flexible tool that you can use to&lt;br /&gt;
customize the environment of an application or a group of applications. For&lt;br /&gt;
example, you can use environment strings to specify default options for&lt;br /&gt;
applications. Users can use the same systemwide default or change that&lt;br /&gt;
value for a particular screen group or activation of the application.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11  Interprocess Communication&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
Interprocess Communication (IPC) is central to OS/2. As we discussed&lt;br /&gt;
earlier, effective IPC is needed to support both the tool-based&lt;br /&gt;
architecture and the dynlink interface for interprocess services. Because&lt;br /&gt;
IPC is so important, OS/2 provides several forms to fulfill a variety of&lt;br /&gt;
needs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.1  Shared Memory&lt;br /&gt;
&lt;br /&gt;
Shared memory has already been discussed in some detail. To summarize, the&lt;br /&gt;
two forms are named shared memory (access is requested by the client by&lt;br /&gt;
name) and giveaway shared memory (a current owner gives access to another&lt;br /&gt;
process). Shared memory is the most efficient form of IPC because no data&lt;br /&gt;
copying or calls to the operating system kernel are involved once the&lt;br /&gt;
shared memory has been set up. Shared memory does require more effort on&lt;br /&gt;
the part of the client processes; a protocol must be established,&lt;br /&gt;
semaphores and flags are usually needed, and exposure to amok programs and&lt;br /&gt;
premature termination must be considered. Applications that expect to deal&lt;br /&gt;
with a low volume of data may want to consider using named pipes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2  Semaphores&lt;br /&gt;
&lt;br /&gt;
A semaphore is a flag or a signal. In its basic form a semaphore has only&lt;br /&gt;
two states--on and off or stop and go. A railroad semaphore, for example,&lt;br /&gt;
is either red or green--stop or go. In computer software, a semaphore is a&lt;br /&gt;
flag or a signal used by one thread of execution to flag or signal&lt;br /&gt;
another. Often it's for purposes of mutual exclusion: &amp;quot;I'm in here, stay&lt;br /&gt;
out.&amp;quot; But sometimes it can be used to indicate other events: &amp;quot;Your data is&lt;br /&gt;
ready.&amp;quot;&lt;br /&gt;
     OS/2 supports two kinds of semaphores, each of which can be used in&lt;br /&gt;
two different ways. The two kinds of semaphores--RAM semaphores and system&lt;br /&gt;
semaphores--have a lot in common, and the same system API is used to&lt;br /&gt;
manipulate both. A RAM semaphore, as its name implies, uses a 4-byte data&lt;br /&gt;
structure kept in a RAM location that must be accessible to all threads&lt;br /&gt;
that use it. The system API that manipulates RAM semaphores is located in a&lt;br /&gt;
dynlink subsystem. This code claims semaphores with an atomic test-and-set&lt;br /&gt;
operation,&lt;br /&gt;
1. An atomic operation is one that is indivisible and therefore&lt;br /&gt;
cannot be interrupted in the middle.&lt;br /&gt;
1 so it need not enter the kernel (ring 0) to protect itself&lt;br /&gt;
against preemption. As a result, the most common tasks--claiming a free&lt;br /&gt;
semaphore and freeing a semaphore that has no waiters--are very fast, on&lt;br /&gt;
the order of 100 microseconds on a 6-MHz 1-wait-state IBM PC/AT.&lt;br /&gt;
2. This is the standard environment when quoting speeds; it's&lt;br /&gt;
both the common case and the worst case. Machines that don't run&lt;br /&gt;
at this speed run faster.&lt;br /&gt;
2 If the&lt;br /&gt;
semaphore is already claimed and the caller must block or if another thread&lt;br /&gt;
is waiting on the semaphore, the semaphore dynlink package must enter&lt;br /&gt;
kernel mode.&lt;br /&gt;
     System semaphores, on the other hand, use a data structure that is&lt;br /&gt;
kept in system memory outside the address space of any process. Therefore,&lt;br /&gt;
system semaphore operations are slower than RAM semaphore operations, on&lt;br /&gt;
the order of 350 microseconds for an uncontested semaphore claim. Some&lt;br /&gt;
important advantages offset this operating speed however. System semaphores&lt;br /&gt;
support mechanisms that prevent deadlock by crashing programs, and system&lt;br /&gt;
semaphores support exclusivity and counting features. As a general rule,&lt;br /&gt;
you should use RAM semaphores when the requirement is wholly contained&lt;br /&gt;
within one process. When multiple processes may be involved, use system&lt;br /&gt;
semaphores.&lt;br /&gt;
     The first step, regardless of the type or use of the semaphore, is to&lt;br /&gt;
create it. An application creates RAM semaphores simply by allocating a 4-&lt;br /&gt;
byte area of memory initialized to zero. The far address of this area is&lt;br /&gt;
the RAM semaphore handle. The DosCreateSem call creates system semaphores.&lt;br /&gt;
(The DosCreateSem call takes an exclusivity argument, which we'll discuss&lt;br /&gt;
later.) Although semaphores control thread execution, semaphore handles&lt;br /&gt;
are owned by the process. Once a semaphore is created and its handle&lt;br /&gt;
obtained, all threads in that process can use that handle. Other&lt;br /&gt;
processes must open the semaphore via DosOpenSem. There is no explicit&lt;br /&gt;
open for a RAM semaphore. To be useful for IPC, the RAM semaphore must&lt;br /&gt;
be in a shared memory segment so that another process can access it;&lt;br /&gt;
the other process simply learns the far address of the RAM semaphore. A RAM&lt;br /&gt;
semaphore is initialized by zeroing out its 4-byte memory area.&lt;br /&gt;
     Except for opening and closing, RAM and system semaphores use exactly&lt;br /&gt;
the same OS/2 semaphore calls. Each semaphore call takes a semaphore handle&lt;br /&gt;
as an argument. A RAM semaphore's handle is its address; a system&lt;br /&gt;
semaphore's handle was returned by the create or open call. The OS/2&lt;br /&gt;
semaphore routines can distinguish between RAM and system semaphores by&lt;br /&gt;
examining the handle they are passed. Because system semaphores and their&lt;br /&gt;
names are kept in an internal OS/2 data area, they are a finite resource;&lt;br /&gt;
the number of RAM semaphores is limited only by the amount of available RAM&lt;br /&gt;
to hold them.&lt;br /&gt;
     The most common use of semaphores is to protect critical sections. To&lt;br /&gt;
reiterate, a critical section is a body of code that manipulates a data&lt;br /&gt;
resource in a nonreentrant way. In other words, a critical section will&lt;br /&gt;
screw up if two threads call it at the same time on the same data resource.&lt;br /&gt;
A critical section can cover more than one section of code; if one&lt;br /&gt;
subroutine adds entries to a table and another subroutine removes entries,&lt;br /&gt;
both subroutines are in the table's critical section. A critical section is&lt;br /&gt;
much like an airplane washroom, and the semaphore is like the sign that&lt;br /&gt;
says &amp;quot;Occupied.&amp;quot; The first user sets the semaphore and starts manipulating&lt;br /&gt;
the resource; meanwhile others arrive, see that the semaphore is set, and&lt;br /&gt;
block (that is, wait) outside. When the critical section becomes available&lt;br /&gt;
and the semaphore is cleared, only one of the waiting threads gets to claim&lt;br /&gt;
it; the others keep on waiting.&lt;br /&gt;
     Using semaphores to protect critical sections is straightforward. At&lt;br /&gt;
the top of a section of code that will manipulate the critical resource,&lt;br /&gt;
insert a call to DosSemRequest. When this call returns, the semaphore is&lt;br /&gt;
claimed, and the code can proceed. When the code is finished and the&lt;br /&gt;
critical section is &amp;quot;clean,&amp;quot; call DosSemClear. DosSemClear releases the&lt;br /&gt;
semaphore and reactivates any thread waiting on it.&lt;br /&gt;
     System semaphores are different from RAM semaphores in this&lt;br /&gt;
application in one critical respect. If a system semaphore is created for&lt;br /&gt;
exclusive use, it can be used as a counting semaphore. Exclusive use means&lt;br /&gt;
that only the thread that set the semaphore can clear it;&lt;br /&gt;
3. This is a departure from the principle of resource ownership&lt;br /&gt;
by process, not by thread. The thread, not the process, owns the&lt;br /&gt;
privilege to clear a set &amp;quot;exclusive use&amp;quot; semaphore.&lt;br /&gt;
3 this is expected&lt;br /&gt;
when protecting critical sections. A counting semaphore can be set many&lt;br /&gt;
times but must be released an equal number of times before it becomes free.&lt;br /&gt;
For example, an application contains function A and function B, each of&lt;br /&gt;
which manipulates the same critical section. Each claims the semaphore at&lt;br /&gt;
its beginning and releases it at its end. However, under some&lt;br /&gt;
circumstances, function A may need to call function B. Function A can't&lt;br /&gt;
release the semaphore before it calls B because it's still in the critical&lt;br /&gt;
section and the data is in an inconsistent state. But when B issues&lt;br /&gt;
DosSemRequest on the semaphore, it blocks because the semaphore was already&lt;br /&gt;
set by A.&lt;br /&gt;
     A counting semaphore solves this problem. When function B makes the&lt;br /&gt;
second, redundant DosSemRequest call, OS/2 recognizes it as the same thread&lt;br /&gt;
that already owns the semaphore, and instead of blocking the thread, it&lt;br /&gt;
increments a counter to show that the semaphore has been claimed twice.&lt;br /&gt;
Later, when function B releases the semaphore, OS/2 decrements the counter.&lt;br /&gt;
Because the counter is not at zero, the semaphore is not really clear and&lt;br /&gt;
thus not released. The semaphore is truly released only after function B&lt;br /&gt;
returns to function A, and A, finishing its work, releases the semaphore a&lt;br /&gt;
second time.&lt;br /&gt;
     A second major use of semaphores is signaling (unrelated to the signal&lt;br /&gt;
facility of OS/2). Signaling is using semaphores to notify threads that&lt;br /&gt;
certain events or activities have taken place. For example, consider a&lt;br /&gt;
multithreaded application that uses one thread to communicate over a serial&lt;br /&gt;
port and another thread to compute with the results of that communication.&lt;br /&gt;
The computing thread tells the communication thread to send a message and&lt;br /&gt;
get a reply, and then it goes about its own business. Later, the computing&lt;br /&gt;
thread wants to block until the reply is received but only if the reply&lt;br /&gt;
hasn't already been received--it may have already arrived, in which case&lt;br /&gt;
the computing thread doesn't want to block.&lt;br /&gt;
     You can handle this by using a semaphore as a flag. The computing&lt;br /&gt;
thread sets the semaphore via DosSemSet before it gives the order to the&lt;br /&gt;
communications thread. When the computing thread is ready to wait for the&lt;br /&gt;
reply, it does a DosSemWait on the semaphore it set earlier. When the&lt;br /&gt;
communications thread receives the reply, it clears the semaphore. When the&lt;br /&gt;
computing thread calls DosSemWait, it will continue without&lt;br /&gt;
delay if the semaphore is already clear. Otherwise, the computing thread&lt;br /&gt;
blocks until the semaphore is cleared. In this example, we aren't&lt;br /&gt;
protecting a critical section; we're using the semaphore transition&lt;br /&gt;
from set to clear to flag an event between multiple threads. Our needs are&lt;br /&gt;
the opposite of a critical section semaphore: We don't want the semaphore&lt;br /&gt;
to be exclusively owned; if it were, the communications thread couldn't&lt;br /&gt;
release it. We also don't want the semaphore to be counting. If it counts,&lt;br /&gt;
the computing thread won't block when it does the DosSemWait; OS/2 would&lt;br /&gt;
recognize that it did the DosSemSet earlier and would increment the&lt;br /&gt;
semaphore counter.&lt;br /&gt;
     OS/2 itself uses semaphore signaling in this fashion when asynchronous&lt;br /&gt;
communication is needed. For example, asynchronous I/O uses semaphores in&lt;br /&gt;
the signaling mode to indicate that an I/O operation has completed. The&lt;br /&gt;
system timer services use semaphores in the signaling mode to indicate that&lt;br /&gt;
the specified time has elapsed. OS/2 supports a special form of semaphore&lt;br /&gt;
waiting, called DosMuxSemWait, which allows a thread to wait on more than&lt;br /&gt;
one semaphore at one time. As soon as any specified semaphore becomes&lt;br /&gt;
clear, DosMuxSemWait returns. DosMuxSemWait, like DosSemWait, only waits&lt;br /&gt;
for a semaphore to become clear; it doesn't set or claim the semaphore as&lt;br /&gt;
does DosSemRequest. DosMuxSemWait allows a thread to wait on a variety of&lt;br /&gt;
events and to wake up whenever one of those events occurs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2.1  Semaphore Recovery&lt;br /&gt;
We discussed earlier some difficulties that can arise if a semaphore is&lt;br /&gt;
left set &amp;quot;orphaned&amp;quot; when its owner terminates unexpectedly. We'll review&lt;br /&gt;
the topic because it's critical that applications handle the situation&lt;br /&gt;
correctly and because that correctness generally has to be demonstrable by&lt;br /&gt;
inspection. It's very difficult to demonstrate and fix timing-related bugs&lt;br /&gt;
by just testing a program.&lt;br /&gt;
     Semaphores can become orphaned in at least four ways:&lt;br /&gt;
&lt;br /&gt;
     1.  An incoming signal can divert the CPU, and the signal handler can&lt;br /&gt;
         fail to return to the point of interruption.&lt;br /&gt;
&lt;br /&gt;
     2.  A process can kill another process without warning.&lt;br /&gt;
&lt;br /&gt;
     3.  A process can incur a GP fault, which is fatal.&lt;br /&gt;
&lt;br /&gt;
     4.  A process can malfunction because of a coding error and fail to&lt;br /&gt;
         release a semaphore.&lt;br /&gt;
&lt;br /&gt;
     The action to take in such events depends on how semaphores are being&lt;br /&gt;
used. In some situations, no action is needed. Our example of the computing&lt;br /&gt;
and communications threads is such a situation. If the process dies, the&lt;br /&gt;
semaphore and all its users die. Special treatment is necessary only if the&lt;br /&gt;
application uses DosExitList to run code that needs to use the semaphore.&lt;br /&gt;
This should rarely be necessary because semaphores are used within a&lt;br /&gt;
process to coordinate multiple threads and only one thread remains&lt;br /&gt;
when the exitlist is activated. Likewise, a process can receive signals&lt;br /&gt;
only if it has asked for them, so an application that does not use signals&lt;br /&gt;
need not worry about their interrupting its critical sections. An&lt;br /&gt;
application that does use signals can use DosHoldSignal, always return&lt;br /&gt;
from a signal handler, or prevent thread 1 (the signal-handling thread)&lt;br /&gt;
from entering critical sections.&lt;br /&gt;
     In other situations, the semaphore can protect a recoverable resource.&lt;br /&gt;
For example, you can use a system semaphore to protect access to a printer&lt;br /&gt;
that for some reason is being dealt with directly by applications rather&lt;br /&gt;
than by the system spooler. If the owner of the &amp;quot;I'm using the printer&amp;quot;&lt;br /&gt;
system semaphore dies unexpectedly, the next thread that tries to claim the&lt;br /&gt;
semaphore will be able to do so but will receive a special error code that&lt;br /&gt;
says, &amp;quot;The owner of this semaphore died while holding it.&amp;quot; In such a case,&lt;br /&gt;
the application can simply write a form feed or two to the printer and&lt;br /&gt;
continue. Other possible actions are to clean up the protected resource or&lt;br /&gt;
to execute a process that will do so. Finally, an application can display a&lt;br /&gt;
message to the user saying, &amp;quot;Gee, this database is corrupt! You better do&lt;br /&gt;
something,&amp;quot; and then terminate. In this case, the application should&lt;br /&gt;
deliberately terminate while holding the semaphore so that any other&lt;br /&gt;
threads waiting on it will receive the &amp;quot;owner died&amp;quot; message. Once the&lt;br /&gt;
&amp;quot;owner died&amp;quot; code is received, that state is cleared; so if the recipient&lt;br /&gt;
of the code releases the semaphore without fixing the inconsistencies in&lt;br /&gt;
the critical section, problems will result.&lt;br /&gt;
     Additional matters must be considered if a process intends to clean up&lt;br /&gt;
its own semaphores by means of a DosExitList handler. First, exclusive&lt;br /&gt;
(that is, counting) semaphores must be used. Although an exitlist routine&lt;br /&gt;
can tell that a RAM or nonexclusive system semaphore is reserved, it cannot&lt;br /&gt;
tell whether it is the process that reserved it. You may be tempted simply&lt;br /&gt;
to keep a flag byte that is set each time the semaphore is claimed and&lt;br /&gt;
cleared each time the semaphore is released, but that solution contains a&lt;br /&gt;
potentially deadly window of failure. If the thread sets the &amp;quot;I own it&amp;quot;&lt;br /&gt;
flag before it calls DosSemRequest, the thread could terminate between&lt;br /&gt;
setting the flag and receiving the semaphore. In that case, the exitlist&lt;br /&gt;
routine would believe, wrongly, that it owns the semaphore and would&lt;br /&gt;
therefore release it--a very unpleasant surprise for the true owner of the&lt;br /&gt;
semaphore. Conversely, if the thread claims the semaphore and then sets the&lt;br /&gt;
flag, a window exists in which the semaphore is claimed but the flag does&lt;br /&gt;
not say so. This is also disastrous.&lt;br /&gt;
     Using exclusive system semaphores solves these problems. As I&lt;br /&gt;
mentioned earlier, when the thread that has set a system semaphore dies&lt;br /&gt;
with the semaphore set, the semaphore is placed into a special &amp;quot;owner died&amp;quot;&lt;br /&gt;
state so that the next thread to attempt to claim the semaphore is informed&lt;br /&gt;
of its orphan status. There is an extra twist to this for exclusive-use&lt;br /&gt;
system semaphores. Should the process die due to an external cause or due&lt;br /&gt;
to a DosExit call and that process has a DosExitList handler, all orphaned&lt;br /&gt;
system semaphores are placed in a special &amp;quot;owner died&amp;quot; state so that only&lt;br /&gt;
that process's remaining thread--the one executing the DosExitList&lt;br /&gt;
handlers--can claim the semaphore. When it does so, it still receives the&lt;br /&gt;
special &amp;quot;owner died&amp;quot; code. The exitlist handler can use DosSemWait with a&lt;br /&gt;
timeout value of 0 to see if the semaphore is set. If the &amp;quot;owner died&amp;quot; code&lt;br /&gt;
is returned, then the DosExitList handler cleans up the resource and then&lt;br /&gt;
issues DosSemClear to clear the semaphore. If a thread terminates by&lt;br /&gt;
explicitly calling DosExit with the &amp;quot;terminate this thread&amp;quot; subcode, any&lt;br /&gt;
exclusive-use system semaphores that it has set will not enter this special&lt;br /&gt;
&amp;quot;owner died&amp;quot; state but will instead assume the general &amp;quot;owner died&amp;quot; state&lt;br /&gt;
that allows any thread in the system to claim the semaphore and receive the&lt;br /&gt;
&amp;quot;owner died&amp;quot; code. Likewise, any semaphores in the special &amp;quot;owner died&amp;quot;&lt;br /&gt;
state that are not cleared by the DosExitList handlers become normal &amp;quot;owner&lt;br /&gt;
died&amp;quot; semaphores when the process completely terminates.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.2.2  Semaphore Scheduling&lt;br /&gt;
Although multiple threads can wait for a semaphore, only one thread gets&lt;br /&gt;
the semaphore when it becomes available. OS/2 schedules semaphore grants&lt;br /&gt;
based on CPU priority: The highest-priority waiting thread claims the&lt;br /&gt;
semaphore. If several waiting threads are at the highest priority, OS/2&lt;br /&gt;
distributes the grants among them evenly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.3  Named Pipes&lt;br /&gt;
&lt;br /&gt;
We've already discussed anonymous pipes--stream oriented IPC mechanisms&lt;br /&gt;
that work via the DosRead and DosWrite calls. Two processes can communicate&lt;br /&gt;
via anonymous pipes only if one is a descendant of the other and if the&lt;br /&gt;
descendant has inherited the parent's handle to the pipe. Anonymous pipes&lt;br /&gt;
are used almost exclusively to transfer input and output data to and from a&lt;br /&gt;
child process or to and from a subtree of child processes.&lt;br /&gt;
     OS/2 supports another form of pipes called named pipes. Named pipes&lt;br /&gt;
are not available in OS/2 version 1.0; they will be available in a later&lt;br /&gt;
release. I discuss them here because of their importance in the system&lt;br /&gt;
architecture. Also, because of the extensible nature of OS/2, it's possible&lt;br /&gt;
that named pipe functionality will be added to the system by including the&lt;br /&gt;
function in some other Microsoft system software package that, when it runs&lt;br /&gt;
under OS/2, installs the capability. In such a case, application programs&lt;br /&gt;
will be unable to distinguish the &amp;quot;add-on&amp;quot; named pipe facility from the&lt;br /&gt;
&amp;quot;built-in&amp;quot; version that will eventually be included in OS/2.&lt;br /&gt;
     Named pipes are much like anonymous pipes in that they're a serial&lt;br /&gt;
communications channel between two processes and they use the DosRead and&lt;br /&gt;
DosWrite interface. They are different, however, in several important&lt;br /&gt;
ways.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes have names in the file system name space. Users of a&lt;br /&gt;
        named pipe need not be related; they need only know the name of a&lt;br /&gt;
        pipe to access it.&lt;br /&gt;
&lt;br /&gt;
     þ  Because named pipes use the file system name space and because that&lt;br /&gt;
        name space can describe machines on a network, named pipes work&lt;br /&gt;
        both locally (within a single machine) and remotely (across a&lt;br /&gt;
        network).&lt;br /&gt;
&lt;br /&gt;
     þ  An anonymous pipe is a byte-stream mechanism. The system considers&lt;br /&gt;
        the data sent through an anonymous pipe as an undifferentiated&lt;br /&gt;
        stream of bytes. The writer can write a 100-byte block of data, and&lt;br /&gt;
        the reader can read the data with two 30-byte reads and one 40-byte&lt;br /&gt;
        read. If the byte stream contains individual messages, the&lt;br /&gt;
        recipient must determine where they start and stop. Named pipes can&lt;br /&gt;
        be used in this byte-stream mode, but named pipes also support&lt;br /&gt;
        support message mode, in which processes read and write streams&lt;br /&gt;
        of messages. When the named pipe is in message mode, OS/2&lt;br /&gt;
        (figuratively!) separates the messages from each other with pieces&lt;br /&gt;
        of waxed paper so that the reader can ask for &amp;quot;the next message&amp;quot;&lt;br /&gt;
        rather than for &amp;quot;the next 100 bytes.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes are full duplex, whereas anonymous pipes are actually a&lt;br /&gt;
        pair of pipes, each half duplex. When an anonymous pipe is created,&lt;br /&gt;
        two handles are returned--a read handle and a write handle. An open&lt;br /&gt;
        of a named pipe returns a single handle, which may (depending on&lt;br /&gt;
        the mode of the DosOpen) be both read and written. Although a full&lt;br /&gt;
        duplex named pipe is accessed via a single handle, the data moving&lt;br /&gt;
        in each direction is kept totally separate. A named pipe should be&lt;br /&gt;
        viewed as two separate pipes between the reader and the writer--one&lt;br /&gt;
        holds data going in, the other holds data coming back. For example,&lt;br /&gt;
        if a thread writes to a named pipe handle and then reads from that&lt;br /&gt;
        handle, the thread will not read back the data it just wrote. The&lt;br /&gt;
        data the thread just wrote in is in the outgoing side; the read&lt;br /&gt;
        reads from the incoming side.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes are frequently used to communicate with processes that&lt;br /&gt;
        provide a service to one or more clients, usually simultaneously.&lt;br /&gt;
        The named pipe API contains special functions to facilitate such&lt;br /&gt;
        use: pipe reusability, multiple pipes with identical names, and so&lt;br /&gt;
        on. These are discussed below.&lt;br /&gt;
&lt;br /&gt;
     þ  Named pipes support transaction I/O calls that provide an efficient&lt;br /&gt;
        way to implement local and remote procedure call dialogs between&lt;br /&gt;
        processes.&lt;br /&gt;
&lt;br /&gt;
     þ  Programs running on MS-DOS version 3.x workstations can access&lt;br /&gt;
        named pipes on an OS/2 server to conduct dialogs with server&lt;br /&gt;
        applications because, to a client, a named pipe looks exactly like&lt;br /&gt;
        a file.&lt;br /&gt;
&lt;br /&gt;
     You'll recall that the creator of an anonymous pipe uses a special&lt;br /&gt;
interface (DosMakePipe) to create the pipe but that the client process&lt;br /&gt;
can use the DosRead and DosWrite functions, remaining ignorant of the&lt;br /&gt;
nature of the handle. The same holds true for named pipes when they&lt;br /&gt;
are used in stream mode. The creator of a named pipe uses a special API&lt;br /&gt;
to set it up, but its clients can use the pipe while remaining ignorant&lt;br /&gt;
of its nature as long as that use is serial.&lt;br /&gt;
4. Random access, using DosSeek, is not supported for&lt;br /&gt;
pipes and will cause an error code to be returned.&lt;br /&gt;
4 Named pipes are created by&lt;br /&gt;
the DosMakeNmPipe call. Once the pipe is created, one of the serving&lt;br /&gt;
process's threads must wait via the DosConnectNmPipe call for the client&lt;br /&gt;
to open the pipe. The client cannot successfully open the pipe until a&lt;br /&gt;
DosConnectNmPipe has been issued to it by the server process.&lt;br /&gt;
     Although the serving process understands that it's using a named pipe&lt;br /&gt;
and can therefore call a special named pipe API, the client process need&lt;br /&gt;
not be aware that it's using a named pipe because the normal DosOpen call&lt;br /&gt;
is used to open the pipe. Because named pipes appear in the file system&lt;br /&gt;
name space, the client can, for example, open a file called \PIPE\STATUS,&lt;br /&gt;
unaware that it's a named pipe being managed by another process. The&lt;br /&gt;
DosMakeNmPipe call returns a handle to the serving end of the pipe; the&lt;br /&gt;
DosOpen call returns a handle to the client end. As soon as the client&lt;br /&gt;
opens a pipe, the DosConnectNmPipe call returns to the serving process.&lt;br /&gt;
     Communication over a named pipe is similar to that over an anonymous&lt;br /&gt;
pipe: The client and server each issue reads and writes to the handle, as&lt;br /&gt;
appropriate for the mode of the open. When a process at one end of the pipe&lt;br /&gt;
closes it, the process at the other end gets an error code in response to&lt;br /&gt;
write operations and an EOF indication in response to read operations.&lt;br /&gt;
     The scenario just described is simple enough, but that's the problem:&lt;br /&gt;
It's too simple. In real life, a serving process probably stays around so&lt;br /&gt;
that it can serve the next client. This is the purpose behind the&lt;br /&gt;
DosConnectNmPipe call. After the first client closes its end of the named&lt;br /&gt;
pipe and the server end sees the EOF on the pipe, the server end issues a&lt;br /&gt;
DosDisconnectNmPipe call to acknowledge that the client has closed the pipe&lt;br /&gt;
(either explicitly or via termination). It can then issue another&lt;br /&gt;
DosConnectNmPipe call to reenable that pipe for reopening by another client&lt;br /&gt;
or by the same client. In other words, the connect and disconnect&lt;br /&gt;
operations allow a server to let clients, one by one, connect to it via a&lt;br /&gt;
single named pipe. The DosDisconnectNmPipe call can be used to forcibly&lt;br /&gt;
disconnect a client. This action is appropriate if a client makes an&lt;br /&gt;
invalid request or otherwise shows signs of ill health.&lt;br /&gt;
     We can serve multiple clients, one at a time, but what about serving&lt;br /&gt;
them in parallel? As we've described it so far, our serving process handles&lt;br /&gt;
only one client. A client's DosOpen call fails if the named pipe already&lt;br /&gt;
has a client user or if the server process hasn't issued the&lt;br /&gt;
DosConnectNmPipe call. This is where the instancing parameter, supplied to&lt;br /&gt;
DosMakeNmPipe, comes in.&lt;br /&gt;
     When a named pipe is first opened,&lt;br /&gt;
5. Like other non-file-system resident named objects, a named pipe&lt;br /&gt;
remains known to the system only as long as a process has it open.&lt;br /&gt;
When all handles to a named pipe are closed, OS/2 forgets all&lt;br /&gt;
information concerning the named pipe. The next DosMakeNmPipe&lt;br /&gt;
call recreates the named pipe from ground zero.&lt;br /&gt;
5 the instance count parameter is&lt;br /&gt;
specified in the pipe flag's word. If this count is greater than 1, the&lt;br /&gt;
pipe can be opened by a server process more than once. Additional opens are&lt;br /&gt;
done via DosMakeNmPipe, which returns another handle to access the new&lt;br /&gt;
instance of the pipe. Obviously the pipe isn't being &amp;quot;made&amp;quot; for the second&lt;br /&gt;
and subsequent calls to DosMakeNmPipe, but the DosOpen call can't be used&lt;br /&gt;
instead because it opens the client end of the named pipe, not the server&lt;br /&gt;
end. The instance count argument is ignored for the second and subsequent&lt;br /&gt;
DosMakeNmPipe calls. Extra instances of a named pipe can be created by the&lt;br /&gt;
same process that created the first instance, or they can be created by&lt;br /&gt;
other processes. Figure 11-1 illustrates multiple instances of a named&lt;br /&gt;
pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿              \\pipe\pipename      &lt;br /&gt;
³  Client  ÃÄÄÄÄÄ¿               �                ÚÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³     A    ³     ³     °°°°°°°°°°°°°°°°°°°°°°     ³           ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ     ³               ÚÄÄÄÄÄÄÄÄÄÄ¿     ³           ³&lt;br /&gt;
                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´          ³     ³           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿                  ÚÄÄÁÄÄÄÄÄÄÄ¿  ³     ³           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´          ³  ³     ³           ³&lt;br /&gt;
³     B    ³               ÚÄÄÁÄÄÄÄÄÄÄ¿  ³  ³     ³  Server   ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ     ÚÄÄÄÄÄÄÄÄÄ´          ³  ³  ÃÄÄÄÄÄ´  Process  ³&lt;br /&gt;
                 ³      ÚÄÄÁÄÄÄÄÄÄÄ¿  ³  ÃÄÄÙ     ³           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿     ³  ÚÄÄÄ´          ³  ³  ÃÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÙ  ³   ³          ³  ÃÄÄÙ        ³           ³&lt;br /&gt;
³     C    ³        ³   ³          ³  ÃÄÄÄÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ        ³   ³          ÃÄÄÙ           ³           ³&lt;br /&gt;
                    ³   ³          ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´           ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄ¿        ³   ÀÄÄÄÄÄÄÄÄÄÄÙ              ³           ³&lt;br /&gt;
³  Client  ÃÄÄÄÄÄÄÄÄÙ                             ÀÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
³     D    ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 11-1.  Multiple instances of a named pipe.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     When a client process does a DosOpen on a named pipe that has multiple&lt;br /&gt;
instances, OS/2 connects it to any server instance of the pipe that has&lt;br /&gt;
issued a DosConnectNmPipe call. If no instances are available and enabled,&lt;br /&gt;
the client receives an error code. OS/2 makes no guarantees about&lt;br /&gt;
distributing the incoming work evenly across all server instances; it&lt;br /&gt;
assumes that all server threads that issued a DosConnectNmPipe call are&lt;br /&gt;
equal.&lt;br /&gt;
     The multiple instance capability allows a single server process or&lt;br /&gt;
perhaps multiple server processes to handle many clients simultaneously.&lt;br /&gt;
One process using four threads can serve four clients as rapidly as four&lt;br /&gt;
processes, each with one thread, can do the job. As long as threads don't&lt;br /&gt;
interfere with one another by blocking on critical sections, a multiprocess&lt;br /&gt;
server has no inherent efficiency advantage over a multithread server.&lt;br /&gt;
     The OS/2 named pipe package includes some composite operations for&lt;br /&gt;
client processes: DosTransactNmPipe and DosCallNmPipe. DosTransactNmPipe is&lt;br /&gt;
much like a DosWrite followed by a DosRead: It sends a message to the&lt;br /&gt;
server end of the named pipe and then reads a reply. DosCallNmPipe does&lt;br /&gt;
the same on an unopened named pipe: It has the combined effect of a&lt;br /&gt;
DosOpen, a DosTransactNmPipe, and a DosClose. These calls are of little&lt;br /&gt;
value if the client and server processes are on the same machine; the&lt;br /&gt;
client could easily build such subroutines itself by appropriately&lt;br /&gt;
combining DosOpen, DosClose, DosRead, and DosWrite. These calls are in the&lt;br /&gt;
named pipe package because they provide significant performance savings in&lt;br /&gt;
a networked environment. If the server process is on a different machine&lt;br /&gt;
from the client process, OS/2 and the network transport can use a&lt;br /&gt;
datagramlike mechanism to implement these calls in a network-efficient&lt;br /&gt;
fashion. Because named pipes work invisibly across the network, any client&lt;br /&gt;
process that performs these types of operations should use these composite&lt;br /&gt;
calls, even if the author of the program didn't anticipate the program&lt;br /&gt;
being used in a networked environment. Using the composite calls will&lt;br /&gt;
 ensure the performance gains if a user decides to use a server process&lt;br /&gt;
located across the network. Readers familiar with network architecture will&lt;br /&gt;
recognize the DosCallNmPipe function as a form of remote procedure call. In&lt;br /&gt;
effect, it allows a process to make a procedure call to another process,&lt;br /&gt;
even a process on another machine.&lt;br /&gt;
     The OS/2 named pipe facility contains a great many features, as befits&lt;br /&gt;
its importance in realizing the OS/2 tool-based architecture. This book is&lt;br /&gt;
not intended to provide an exhaustive coverage of features, but a few other&lt;br /&gt;
miscellaneous items merit mention.&lt;br /&gt;
     Our above discussion concentrated on stream-based communications,&lt;br /&gt;
which can be convenient because they allow a client process to use a named&lt;br /&gt;
pipe while ignorant of its nature. For example, you can write a spooler&lt;br /&gt;
package for a device not supported by the system spoolers--say, for a&lt;br /&gt;
plotter device. Input to the spooler can be via a named pipe, perhaps&lt;br /&gt;
\PIPE\PLOTOUT. An application could then be told to write its plotter&lt;br /&gt;
output to a file named \PIPE\PLOTOUT or even \\PLOTMACH\PIPE\PLOTOUT&lt;br /&gt;
(across a network). The application will then use the spooler at the&lt;br /&gt;
other end of the named pipe.&lt;br /&gt;
     Sometimes, though, the client process does understand that it's&lt;br /&gt;
talking to a named pipe, and the information exchanged is a series of&lt;br /&gt;
messages rather than a long stream of plotter data. In this case, the named&lt;br /&gt;
pipe can be configured as a message stream in which each message is&lt;br /&gt;
indivisible and atomic at the interface. In other words, when a process&lt;br /&gt;
reads from a named pipe, it gets only one message per read, and it gets the&lt;br /&gt;
entire message. Messages can queue up in the pipe, but OS/2 remembers the&lt;br /&gt;
message boundaries so that it can split them apart as they are read.&lt;br /&gt;
Message streams can be used effectively in a networking environment because&lt;br /&gt;
the network transport can better judge how to assemble packets.&lt;br /&gt;
     Although our examples have shown the client and server processes&lt;br /&gt;
issuing calls and blocking until they are done, named pipes can be&lt;br /&gt;
configured to operate in a nonblocking fashion. This allows a server or a&lt;br /&gt;
client to test a pipe to see if it's ready for a particular operation,&lt;br /&gt;
thereby guaranteeing that the process won't be held up for some period&lt;br /&gt;
waiting for a request to complete. Processes can also use DosPeekNmPipe, a&lt;br /&gt;
related facility that returns a peek at any data (without consuming the&lt;br /&gt;
data) currently waiting to be read in the pipe interface. Servers can use&lt;br /&gt;
this to scan a client's request to see if they're interested in handling it&lt;br /&gt;
at that time.&lt;br /&gt;
     Finally, we mentioned that a process that attempts a DosOpen to a&lt;br /&gt;
named pipe without any available instances is returned an error code.&lt;br /&gt;
Typically, a client in this situation wants to wait for service to become&lt;br /&gt;
available, and it doesn't want to sit in a polling loop periodically &lt;br /&gt;
testing for server availability. The DosWaitNmPipe call is provided for&lt;br /&gt;
this situation; it allows a client to block until an instance of the named&lt;br /&gt;
pipe becomes available. When DosWaitNmPipe returns, the client must still&lt;br /&gt;
do a DosOpen. The DosOpen can fail, however, if another process has taken&lt;br /&gt;
the pipe instance in the time between the &amp;quot;wait&amp;quot; and the &amp;quot;open&amp;quot; calls. But&lt;br /&gt;
because multiple waiters for a named pipe are serviced in priority order,&lt;br /&gt;
such a &amp;quot;race&amp;quot; condition is uncommon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.4  Queues&lt;br /&gt;
&lt;br /&gt;
Queues are another form of IPC. In many ways they are similar to named&lt;br /&gt;
pipes, but they are also significantly different. Like named pipes, they&lt;br /&gt;
use the file system name space, and they pass messages rather than byte&lt;br /&gt;
streams. Unlike named pipes, queues allow multiple writes to a single queue&lt;br /&gt;
because the messages bring with them information about their sending&lt;br /&gt;
process that enables the queue reader to distinguish between messages from&lt;br /&gt;
different senders. Named pipes are strictly FIFO, whereas queue messages&lt;br /&gt;
can be read in a variety of orders. Finally, queues use shared memory as a&lt;br /&gt;
transfer mechanism; so although they're faster than named pipes for higher&lt;br /&gt;
volume data transfers on a single machine, they don't work across the&lt;br /&gt;
network.&lt;br /&gt;
     The interface to the queue package is similar but not identical to&lt;br /&gt;
that of the named pipe interface. Like named pipes, each queue has a single&lt;br /&gt;
owner that creates it. Clients open and close the queue while the owner,&lt;br /&gt;
typically, lives on. Unlike named pipes, the client process must use a&lt;br /&gt;
special queue API (DosReadQueue, DosWriteQueue, and so on) and thus must be&lt;br /&gt;
written especially to use the queue package. Although each queue has a&lt;br /&gt;
single owner, each queue can have multiple clients; so the queue mechanism&lt;br /&gt;
doesn't need a facility to have multiple queues of the same name, nor does&lt;br /&gt;
it need a DosWaitNmPipe equivalent.&lt;br /&gt;
     Queue messages are somewhat different from named pipe messages. In&lt;br /&gt;
addition to carrying the body of the message, each queue message carries&lt;br /&gt;
two additional pieces of information. One is the PID of the sender; OS/2&lt;br /&gt;
provides this information, and the sender cannot affect it. The other is a&lt;br /&gt;
word value that the sender supplied and that OS/2 and the queue package do&lt;br /&gt;
not interpret. Queue servers and clients can use this information as they&lt;br /&gt;
wish to facilitate communication.&lt;br /&gt;
     The queue package also contains a peek facility, similar to that of&lt;br /&gt;
named pipes but with an interesting twist. If a process peeks the named&lt;br /&gt;
pipe and then later reads from it, it can be sure that the message it reads&lt;br /&gt;
is the same one that it peeked because named pipes are always FIFO. Queues,&lt;br /&gt;
however, allow records to be read in different orders of priority. If a&lt;br /&gt;
queue is being read in priority order, a process might well peek a message,&lt;br /&gt;
but by the time the process issues the queue read, some other message of&lt;br /&gt;
higher priority may have arrived and thus be at the front of the list. To&lt;br /&gt;
get around this problem, when the queue package peeks a message, it returns&lt;br /&gt;
a magic cookie to the caller along with the message. The caller can supply&lt;br /&gt;
this cookie to a subsequent DosReadQueue call to ensure that the peeked&lt;br /&gt;
message is the one read, overriding the normal message-ranking process.&lt;br /&gt;
This magic cookie can also be supplied to the DosPeekQueue call to peek the&lt;br /&gt;
second and subsequent records in the queue.&lt;br /&gt;
     Finally, one extremely important difference between queues and named&lt;br /&gt;
pipes is that named pipes transfer (that is, copy) the data from the client&lt;br /&gt;
to the server process. Queues transfer only the address of the data; the&lt;br /&gt;
queue package does not touch the data itself. Thus, the data body of the&lt;br /&gt;
queue message must be addressable to both the client and the serving&lt;br /&gt;
process. This is straightforward if both the client and serving threads&lt;br /&gt;
belong to the same process. If the client and serving threads are from&lt;br /&gt;
different processes, however, the data body of the queue message must be in&lt;br /&gt;
a shared memory segment that is addressable to both the client and the&lt;br /&gt;
server.&lt;br /&gt;
     A related issue is buffer reusability. An application can reuse a&lt;br /&gt;
memory area immediately after its thread returns from the named pipe call&lt;br /&gt;
that wrote the data from that area; but when using a queue, the sender must&lt;br /&gt;
not overwrite the message area until it's sure the reading process is&lt;br /&gt;
finished with the message.&lt;br /&gt;
     One way to kill both these birds--the shared memory and the memory&lt;br /&gt;
reuse problems--with one stone is to use the memory suballocation package.&lt;br /&gt;
Both the client and the queue server need to have shared access to a memory&lt;br /&gt;
segment that is then managed by the memory suballocation package. The&lt;br /&gt;
client allocates a memory object to hold the queue message and write it to&lt;br /&gt;
the queue. The queue server can address that queue message because it's in&lt;br /&gt;
the shared memory segment. When the queue manager is finished with the&lt;br /&gt;
message, it calls the memory suballocator to release the memory object. The&lt;br /&gt;
client need not worry about when the server is finished with the message&lt;br /&gt;
because the client allocates a new message buffer for each new message,&lt;br /&gt;
relying on the server to return the messages fast enough so that the memory&lt;br /&gt;
suballocator doesn't run out of available space.&lt;br /&gt;
     A similar technique on a segment level is to use giveaway shared&lt;br /&gt;
memory. The client allocates a giveaway segment for each message content,&lt;br /&gt;
creates the message, gives away a shared addressability to the segment to&lt;br /&gt;
the server process, and then writes the message (actually, the message's&lt;br /&gt;
address) to the queue. Note that the sender uses the recipient's selector&lt;br /&gt;
as the data address in this case, not its own selector. When the thread&lt;br /&gt;
returns from that DosWriteQueue call, the client releases its access to the&lt;br /&gt;
segment via DosFreeSeg. When the server process is finished with the&lt;br /&gt;
message, it also releases the memory segment. Because the queue server is&lt;br /&gt;
the last process with access to that segment, the segment is then returned&lt;br /&gt;
to the free pool.&lt;br /&gt;
     Software designers need to consider carefully the tradeoffs between&lt;br /&gt;
queues, named pipes, and other forms of IPC. Queues are potentially very&lt;br /&gt;
fast because only addresses are copied, not the data itself; but the work&lt;br /&gt;
involved in managing and reusing the shared memory may consume the time&lt;br /&gt;
savings if the messages are small. In general, small messages that are&lt;br /&gt;
always read FIFO should go by named pipes, as should applications that&lt;br /&gt;
communicate with clients and servers across a network. Very large or high&lt;br /&gt;
data rate messages may be better suited to queues.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.5  Dynamic Data Exchange (DDE)&lt;br /&gt;
&lt;br /&gt;
DDE is a form of IPC available to processes that use the presentation&lt;br /&gt;
manager API. The presentation manager's interface is message oriented; that&lt;br /&gt;
is, the primary means of communication between a process and the&lt;br /&gt;
presentation manager is the passing of messages. The presentation manager&lt;br /&gt;
message interface allows applications to define private messages that have&lt;br /&gt;
a unique meaning throughout the PC. DDE is, strictly speaking, a protocol&lt;br /&gt;
that defines new messages for communication between applications that use&lt;br /&gt;
it.&lt;br /&gt;
     DDE messages can be directed at a particular recipient or broadcast to&lt;br /&gt;
all presentation manager applications on a particular PC. Typically, a&lt;br /&gt;
client process broadcasts a message that says, &amp;quot;Does anyone out there have&lt;br /&gt;
this information?&amp;quot; or &amp;quot;Does anyone out there provide this service?&amp;quot; If no&lt;br /&gt;
response is received, the answer is taken to be no. If a response is&lt;br /&gt;
received, it contains an identifying code&lt;br /&gt;
6.A window handle.&lt;br /&gt;
6 that allows the two processes to&lt;br /&gt;
communicate privately.&lt;br /&gt;
     DDE's broadcast mechanism and message orientation gives it a lot of&lt;br /&gt;
flexibility in a multiprocessing environment. For example, a specialized&lt;br /&gt;
application might be scanning stock quotes that are arriving via a special&lt;br /&gt;
link. A spreadsheet program could use DDE to tell this scanner application&lt;br /&gt;
to notify it whenever the quotes change for certain stocks that are&lt;br /&gt;
mentioned in its spreadsheet. Another application, perhaps called Market&lt;br /&gt;
Alert, might ask the scanner to notify it of trades in a different set of&lt;br /&gt;
stocks so that the alert program can flash a banner if those stocks trade&lt;br /&gt;
outside a prescribed range. DDEs can be used only by presentation manager&lt;br /&gt;
applications to communicate with the same.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.6  Signaling&lt;br /&gt;
&lt;br /&gt;
Signals are asynchronous notification mechanisms that operate in a fashion&lt;br /&gt;
analogous to hardware interrupts. Like hardware interrupts, when a signal&lt;br /&gt;
arrives at a process, that process's thread 1 stops after the instruction&lt;br /&gt;
it is executing and begins executing at a specified handler address. The&lt;br /&gt;
many special considerations to take into account when using signals are&lt;br /&gt;
discussed in Chapter 12. This section discusses their use as a form of&lt;br /&gt;
IPC.&lt;br /&gt;
     Processes typically receive signals in response to external events&lt;br /&gt;
that must be serviced immediately. Examples of such events are the user&lt;br /&gt;
pressing Ctrl-C or a process being killed. Three signals (flag A, flag B,&lt;br /&gt;
and flag C), however, are caused by another process&lt;br /&gt;
7. This is the typical case; but like all other system calls that affect&lt;br /&gt;
processes, a thread can make such a call to affect its own process.&lt;br /&gt;
7 issuing an explicit&lt;br /&gt;
DosFlagProcess API. DosFlagProcess is a unique form of IPC because it's&lt;br /&gt;
asynchronous. The recipient doesn't have to block or poll waiting for the&lt;br /&gt;
event; it finds out about it (by discovering itself to be executing the&lt;br /&gt;
signal handler) as soon as the scheduler gives it CPU time.&lt;br /&gt;
     DosFlagProcess, however, has some unique drawbacks. First, a signal&lt;br /&gt;
carries little information with it: only the number of the signal and a&lt;br /&gt;
single argument word. Second, signals can interrupt and interfere with some&lt;br /&gt;
system calls. Third, OS/2 views signals more as events than as messages; so&lt;br /&gt;
if signals are sent faster than the recipient can process them, OS/2&lt;br /&gt;
discards some of the overrun. These disadvantages (discussed in Chapter 12)&lt;br /&gt;
restrict signals to a rather specialized role as an IPC mechanism.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
11.7  Combining IPC Forms&lt;br /&gt;
&lt;br /&gt;
We've discussed each form of IPC, listing its strengths and weaknesses. If&lt;br /&gt;
you use forms in conjunction, however, you benefit from their combined&lt;br /&gt;
strengths. For example, a process can use named pipes or DDE to establish&lt;br /&gt;
contact with another process and then agree with it to send a high volume&lt;br /&gt;
of data via shared memory. An application that provides an IPC interface&lt;br /&gt;
should also provide a dynlink package to hide the details of the IPC. This&lt;br /&gt;
gives designers the flexibility to improve the IPC component of their&lt;br /&gt;
package in future releases while still maintaining interface compatibility&lt;br /&gt;
with their clients.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
12  Signals&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
The OS/2 signal mechanism is similar, but not identical, to the UNIX signal&lt;br /&gt;
mechanism. A signal is much like a hardware interrupt except that it is&lt;br /&gt;
initiated and implemented in software. Just as a hardware interrupt causes&lt;br /&gt;
the CS, IP, and Flags registers to be saved on the stack and execution to&lt;br /&gt;
begin at a handler address, a signal causes the application's CS, IP, and&lt;br /&gt;
Flags registers to be saved on the stack and execution to begin at a&lt;br /&gt;
signal-handler address. An IRET instruction returns control to the&lt;br /&gt;
interrupted address in both cases. Signals are different from hardware&lt;br /&gt;
interrupts in that they are a software construct and don't involve&lt;br /&gt;
privilege transitions, stack switches, or ring 0 code.&lt;br /&gt;
     OS/2 supports six signals--three common signals (Ctrl-C, Ctrl-Break,&lt;br /&gt;
and program termination) and three general-purpose signals. The Ctrl-C and&lt;br /&gt;
Ctrl-Break signals occur in response to keyboard activity; the program&lt;br /&gt;
termination signal occurs when a process is killed via the DosKill call.&lt;br /&gt;
1. The process termination signal handler is not called under&lt;br /&gt;
all conditions of process termination, only in response to&lt;br /&gt;
DosKill. Normal exits, GP faults, and so on do not&lt;br /&gt;
activate the process termination signal handler.&lt;br /&gt;
1&lt;br /&gt;
The three general-purpose signals are generated by an explicit call from a&lt;br /&gt;
thread, typically a thread from another process. A signal handler is in the&lt;br /&gt;
form of a far subroutine, that is, a subroutine that returns with a far&lt;br /&gt;
return instruction. When a signal arrives, the process's thread 1 is&lt;br /&gt;
interrupted from its current location and made to call the signal-handler&lt;br /&gt;
procedure with an argument provided by the signal generator. The signal-&lt;br /&gt;
handler code can return,&lt;br /&gt;
2. OS/2 interposes a code thunk, so the signal handler need not&lt;br /&gt;
concern itself with executing an IRET instruction, which language&lt;br /&gt;
compilers usually won't generate. When the signal handler is&lt;br /&gt;
entered, its return address points to a piece of OS/2 code that&lt;br /&gt;
contains the IRET instruction.&lt;br /&gt;
2 in which case the CPU returns from where it was&lt;br /&gt;
interrupted, or the signal handler can clean its stack and jump into the&lt;br /&gt;
process's code at some other spot, as in the C language's longjmp facility.&lt;br /&gt;
     The analogy between signals and hardware interrupts holds still&lt;br /&gt;
further. As it does in a hardware interrupt, the system blocks further&lt;br /&gt;
interrupts from the same source so that the signal handler won't be&lt;br /&gt;
arbitrarily reentered. The signal handler must issue a special form of&lt;br /&gt;
DosSetSigHandler to dismiss the signal and allow further signals to occur.&lt;br /&gt;
Typically this is done at the end of the signal-handling routine unless the&lt;br /&gt;
signal handler is reentrant. Also, like hardware interrupts, the equivalent&lt;br /&gt;
of the CLI instruction--the DosHoldSignal call--is used to protect critical&lt;br /&gt;
sections from being interrupted via a signal.&lt;br /&gt;
     Unlike hardware interrupts, signals have no interrupt priority. As&lt;br /&gt;
each enabled signal occurs, the signal handler is entered, even if another&lt;br /&gt;
signal handler must be interrupted. New signal events that come in while&lt;br /&gt;
that signal is still being processed from an earlier event--before the&lt;br /&gt;
signal has been dismissed by the handler--are held until the previous&lt;br /&gt;
signal event has been dismissed. Like hardware interrupts, this is a&lt;br /&gt;
pending-signal flag, not a counter. If three signals of the same kind are&lt;br /&gt;
held off, only one signal event occurs when that signal becomes&lt;br /&gt;
reenabled.&lt;br /&gt;
     A signal event occurs in the context of a process whose thread of&lt;br /&gt;
execution is interrupted for the signal handler; a signal doesn't cause the&lt;br /&gt;
CPU to stop executing another process in order to execute the first&lt;br /&gt;
process's signal handler. When OS/2 &amp;quot;posts&amp;quot; a signal to a process, it&lt;br /&gt;
simply makes a mark that says, &amp;quot;The next time we run this guy, store his&lt;br /&gt;
CS, IP, and Flags values on the stack and start executing here instead.&amp;quot;&lt;br /&gt;
The system uses its regular priority rules to assign the CPU to threads;&lt;br /&gt;
when the scheduler next runs the signaled thread, the dispatcher code that&lt;br /&gt;
sends the CPU into the application's code reads the &amp;quot;posted signal&amp;quot; mark&lt;br /&gt;
and does the required work.&lt;br /&gt;
     Because a signal &amp;quot;pseudo interrupt&amp;quot; is merely a trick of the&lt;br /&gt;
dispatcher, signal handlers don't run in ring 0 as do hardware interrupt&lt;br /&gt;
handlers; they run in ring 3 as do all application threads. In general, as&lt;br /&gt;
far as OS/2 is concerned, the process isn't in any sort of special state&lt;br /&gt;
when it's executing a signal handler, and no special rules govern what a&lt;br /&gt;
thread can and cannot do in a signal handler.&lt;br /&gt;
     Receiving a signal when thread 1 is executing an application or&lt;br /&gt;
dynlink code is straightforward: The system saves CS, IP, and Flags, and&lt;br /&gt;
the signal handler saves the rest. The full register complement can be&lt;br /&gt;
restored after the signal has been processed, and thread 1's normal&lt;br /&gt;
execution resumes without incident. If thread 1 is executing a system call&lt;br /&gt;
that takes the CPU inside the kernel, the situation is more complex. OS/2&lt;br /&gt;
can't emulate an interrupt from the system ring 0 code to the application's&lt;br /&gt;
ring 3 code, nor can OS/2 take the chance that the signal handler never&lt;br /&gt;
returns from the signal&lt;br /&gt;
3.It's acceptable for a signal handler to clean up thread 1's&lt;br /&gt;
stack, dismiss the signal, jump to another part of the&lt;br /&gt;
application, and never return from the signal. For example, an&lt;br /&gt;
application can jump into its &amp;quot;prompt and command loop&amp;quot; in&lt;br /&gt;
response to the press of Ctrl-C.&lt;br /&gt;
3 and therefore leaves OS/2's internals in an&lt;br /&gt;
intermediate state. Instead, when a signal is posted and thread 1 is&lt;br /&gt;
executing ring 0 OS/2 code, the system either completes its operations&lt;br /&gt;
before recognizing the signal or aborts the operation and then recognizes&lt;br /&gt;
the signal. If the operation is expected to take place &amp;quot;quickly,&amp;quot; the&lt;br /&gt;
system completes the operation, and the signal is recognized at the point&lt;br /&gt;
where the CPU resumes executing the application's ring 3 code.&lt;br /&gt;
     All non-I/O operations are deemed to complete &amp;quot;quickly,&amp;quot; with the&lt;br /&gt;
exception of the explicit blocking operations such as DosSleep, DosSemWait,&lt;br /&gt;
and so on. I/O operations depend on the specific device. Disk I/O completes&lt;br /&gt;
quickly, but keyboard and serial I/O generally do not. Clearly, if we wait&lt;br /&gt;
for the user to finish typing a line before we recognize a signal, we might&lt;br /&gt;
never recognize it--especially if the signal is Ctrl-C! In the case of&lt;br /&gt;
&amp;quot;slow devices,&amp;quot; OS/2 or the device driver terminates the operation and&lt;br /&gt;
returns to the application with an error code. The signal is recognized&lt;br /&gt;
when the CPU is about to resume executing the ring 3 application code that&lt;br /&gt;
follows the system call that was interrupted.&lt;br /&gt;
     Although the application is given an error code to explain that the&lt;br /&gt;
system call was interrupted, the application may be unable to reissue the&lt;br /&gt;
system call to complete the work. In the case of device I/O, the&lt;br /&gt;
application typically can't tell how much, if any, of the requested output&lt;br /&gt;
or input took place before the signal interrupted the operation. If an&lt;br /&gt;
output operation is not reissued, some data at the end of the write may be&lt;br /&gt;
missing. If an output operation is restarted, then some data at the&lt;br /&gt;
beginning of the write may be written twice. In the case of DosSleep, the&lt;br /&gt;
application cannot tell how much of the requested sleep has elapsed. These&lt;br /&gt;
issues are not usually a problem; it's typically keyboard input that is&lt;br /&gt;
interrupted. In the case of the common signals (Ctrl-C, Ctrl-Break, and&lt;br /&gt;
process killed) the application typically flushes partial keyboard input&lt;br /&gt;
anyway. Applications that use other &amp;quot;slow&amp;quot; devices or the IPC flag signals&lt;br /&gt;
need to deal with this, however.&lt;br /&gt;
     Although a process can have multiple threads, only thread 1 is used to&lt;br /&gt;
execute the signal handler.&lt;br /&gt;
4. For this reason, a process should not terminate thread 1 and&lt;br /&gt;
continue executing with others; then it cannot receive signals.&lt;br /&gt;
4 This leads to an obvious solution to the&lt;br /&gt;
interrupted system call problem: Applications that will be inconvenienced&lt;br /&gt;
by interrupted system calls due to signals should dedicate thread 1 to work&lt;br /&gt;
that doesn't make interruptible system calls and use other thread(s) for&lt;br /&gt;
that work. In the worst case, thread 1 can be totally dedicated to waiting&lt;br /&gt;
for signals: It can block on a RAM semaphore that is never released, or it&lt;br /&gt;
can execute a DosSleep loop.&lt;br /&gt;
     A couple of practical details about signals are worth noting. First,&lt;br /&gt;
the user of a high-level language such as C need not worry about saving the&lt;br /&gt;
registers inside the signal-handler routine. The language runtimes&lt;br /&gt;
typically provide code to handle all these details; as far as the&lt;br /&gt;
application program is concerned, the signal handler is asynchronously far&lt;br /&gt;
called, and it can return from the signal by the return() statement. Also,&lt;br /&gt;
no application can receive a signal without first requesting it, so you&lt;br /&gt;
need not worry about setting up signal handlers if your application doesn't&lt;br /&gt;
explicitly ask to use them. A process can have only one signal-handling&lt;br /&gt;
address for each signal, so general-purpose dynlink routines (ones that&lt;br /&gt;
might be called by applications that aren't bundled with the dynlink&lt;br /&gt;
package) should never set a signal handler; doing so might override a&lt;br /&gt;
handler established by the client program code.&lt;br /&gt;
     Signals interact with critical sections in much the same way as&lt;br /&gt;
interrupts do. If a signal arrives while thread 1 is executing a critical&lt;br /&gt;
section that is protected by a semaphore and if that signal handler never&lt;br /&gt;
returns to the interrupted location, the critical section's semaphore will&lt;br /&gt;
be left jammed on. Even if the signal handler eventually returns, deadlock&lt;br /&gt;
occurs if it attempts to enter the critical section during processing of&lt;br /&gt;
the signal (perhaps it called a dynlink package, unaware that the package&lt;br /&gt;
contained a critical section). Dynlink packages must deal with this problem&lt;br /&gt;
by means of the DosHoldSignal call, which is analogous to the CLI/STI&lt;br /&gt;
instructions for hardware interrupts: The DosHoldSignal holds off arriving&lt;br /&gt;
signals until they are released. Held-off signals should be released within&lt;br /&gt;
a second or two so that the user won't be pounding Ctrl-C and thinking that&lt;br /&gt;
the application has crashed. Applications can use DosHoldSignal, or they&lt;br /&gt;
can simply ensure that thread 1 never enters critical sections, perhaps by&lt;br /&gt;
reserving it for signal handling, as discussed above.&lt;br /&gt;
     Ctrl-C and Ctrl-Break are special, device-specific operations. Setting&lt;br /&gt;
a signal handler for these signals is a form of I/O to the keyboard device;&lt;br /&gt;
applications must never do this until they have verified that they have&lt;br /&gt;
been assigned the keyboard device. See Chapter 14, Interactive Programs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13  The Presentation Manager and VIO&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
In the early chapters of this book, I emphasized the importance of a high-&lt;br /&gt;
powered, high-bandwidth graphical user interface. It's a lot of work for an&lt;br /&gt;
application to manage graphical rendition, windowing, menus, and so on, and&lt;br /&gt;
it's hard for the user to learn a completely different interface for each&lt;br /&gt;
application. Therefore, OS/2 contains a subsystem called the presentation&lt;br /&gt;
manager (PM) that provides these services and more. The presentation&lt;br /&gt;
manager is implemented as a dynlink subsystem and daemon process&lt;br /&gt;
combination, and it provides:&lt;br /&gt;
&lt;br /&gt;
     þ  High-performance graphical windowing.&lt;br /&gt;
&lt;br /&gt;
     þ  A powerful user interface model, including drop-down menus, scroll&lt;br /&gt;
        bars, icons, and mouse and keyboard interfaces. Most of these&lt;br /&gt;
        facilities are optional to the application; it can choose the&lt;br /&gt;
        standard services or &amp;quot;roll its own.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
     þ  Device independence. The presentation manager contains a&lt;br /&gt;
        sophisticated multilevel device interface so that as much work as&lt;br /&gt;
        possible is pushed down to &amp;quot;smart&amp;quot; graphics cards to optimize&lt;br /&gt;
        performance.&lt;br /&gt;
&lt;br /&gt;
     Interfacing an application with the presentation manager involves a&lt;br /&gt;
degree of effort that not all programmers may want to put forth. The&lt;br /&gt;
interface to the application may be so simple that the presentation&lt;br /&gt;
manager's features are of little value, or the programmer may want to port&lt;br /&gt;
an MS-DOS application to OS/2 with the minimum degree of change. For these&lt;br /&gt;
reasons, OS/2 provides a second interface package called VIO,&lt;br /&gt;
1. VIO is a convenience term that encompasses three dynlink&lt;br /&gt;
subsystems: KBD (keyboard), VIO (Video I/O; the display adapter),&lt;br /&gt;
and MOU (mouse).&lt;br /&gt;
1 which is&lt;br /&gt;
primarily character oriented and looks much like the MS-DOS ROM BIOS video&lt;br /&gt;
interface. The initial release of OS/2 contains only VIO, implemented as a&lt;br /&gt;
separate package. The next release will contain the presentation manager,&lt;br /&gt;
and VIO will then become an alternate interface to the presentation&lt;br /&gt;
manager.&lt;br /&gt;
     Fundamentally, the presentation manager and VIO are the equivalent of&lt;br /&gt;
device drivers. They are implemented as dynlink packages because they are&lt;br /&gt;
device dependent and need to be replaced if different devices are used.&lt;br /&gt;
Dynlinks are used instead of true device drivers because they can provide&lt;br /&gt;
high throughput for the screen device: A simple call is made directly to&lt;br /&gt;
the code that paints the pixels on the screen. Also, the dynlink interface&lt;br /&gt;
allows the presentation manager to be implemented partially as a dynlink&lt;br /&gt;
subsystem and partially as a daemon process accessed by that subsystem.&lt;br /&gt;
     These packages are complex; explaining them in detail is beyond the&lt;br /&gt;
scope of this book. Instead, I will discuss from a general perspective the&lt;br /&gt;
special issues for users of this package.&lt;br /&gt;
     VIO is essentially character oriented. It supports graphics-based&lt;br /&gt;
applications, but only to the extent of allowing them to manipulate the&lt;br /&gt;
display controller directly so that they can &amp;quot;go around&amp;quot; VIO and provide&lt;br /&gt;
special interfaces related to screen switching of graphics applications&lt;br /&gt;
(see below). The base VIO package plays a role similar to that of the ROM&lt;br /&gt;
BIOS INT 10/INT 16 interface used in MS-DOS. It contains some useful&lt;br /&gt;
enhancements but in general is a superset of the ROM BIOS functions, so INT&lt;br /&gt;
10-based real mode applications can be quickly adjusted to use VIO instead.&lt;br /&gt;
VIO is replaceable, in whole or in part, to allow applications being run&lt;br /&gt;
with VIO to be managed later by the presentation manager package.&lt;br /&gt;
     The presentation manager is entirely different from VIO. It offers an&lt;br /&gt;
extremely rich and powerful set of functions that support windowing, and it&lt;br /&gt;
offers a full, device-independent graphics facility. Its message-oriented&lt;br /&gt;
architecture is well suited to interactive applications. Once the&lt;br /&gt;
presentation manager programming model is learned and the key elements of&lt;br /&gt;
its complex interface are understood, a programmer can take advantage of a&lt;br /&gt;
very sexy user interface with comparatively little effort. The presentation&lt;br /&gt;
manager also replaces the existing VIO/KBD/MOU calls in order to support&lt;br /&gt;
older programs that use these interfaces.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.1  Choosing Between PM and VIO&lt;br /&gt;
&lt;br /&gt;
The roles of VIO and the presentation manager sometimes cause confusion:&lt;br /&gt;
Which should you use for your application? The default interface for a new&lt;br /&gt;
application should be the presentation manager. It's not in OS/2 version&lt;br /&gt;
1.0 because of scheduling restrictions; owners of version 1.0 will receive&lt;br /&gt;
the presentation manager as soon as it is available, and all future&lt;br /&gt;
releases will be bundled with the presentation manager. The presentation&lt;br /&gt;
manager will be present on essentially all personal computer OS/2&lt;br /&gt;
installations, so you are not restricting the potential market for an&lt;br /&gt;
application if you write it for the presentation manager.&lt;br /&gt;
     The presentation manager interface allows an application to utilize a&lt;br /&gt;
powerful, graphical user interface. In general form it's standardized for&lt;br /&gt;
ease of use, but it can be customized in a specific implementation so that&lt;br /&gt;
an application can provide important value-added features. On the other&lt;br /&gt;
hand, if you are porting an application from the real mode environment, you&lt;br /&gt;
will find it easier to use the VIO interface. Naturally, such programs run&lt;br /&gt;
well under the presentation manager, but they forgo the ability to use&lt;br /&gt;
graphics and to interact with the presentation manager. The user can still&lt;br /&gt;
&amp;quot;window&amp;quot; the VIO application's screen image, but without the application's&lt;br /&gt;
knowledge or cooperation. To summarize, you have three choices when writing&lt;br /&gt;
an application:&lt;br /&gt;
&lt;br /&gt;
     1.  Only use the VIO interface in character mode. This works well in a&lt;br /&gt;
         presentation manager environment and is a good choice for ported&lt;br /&gt;
         real mode applications. The VIO interface is also supported by the&lt;br /&gt;
         Family API mechanism. This mode is compatible with the Family API&lt;br /&gt;
         facility.&lt;br /&gt;
&lt;br /&gt;
     2.  Use the special VIO interface facilities to sidestep VIO and&lt;br /&gt;
         directly manipulate the display screen in either character or &lt;br /&gt;
         graphics mode. This also works in a presentation manager&lt;br /&gt;
         environment, but the application will not be able to run in a &lt;br /&gt;
         window. This approach can be compatible with the Family API if it &lt;br /&gt;
         is carefully implemented.&lt;br /&gt;
&lt;br /&gt;
     3.  Use the presentation manager interface--the most sophisticated&lt;br /&gt;
         interface for the least effort. The presentation manager interface&lt;br /&gt;
         provides a way to &amp;quot;operate&amp;quot; applications that will become a widely&lt;br /&gt;
         known user standard because of the capabilities of the interface,&lt;br /&gt;
         because of the support it receives from key software vendors, and&lt;br /&gt;
         because it's bundled with OS/2. The user is obviously at an&lt;br /&gt;
         advantage if he or she does not have to spend time learning a new&lt;br /&gt;
         interface and operational metaphors to use your application.&lt;br /&gt;
         Finally, Microsoft is a strong believer in the power of a&lt;br /&gt;
         graphical user interface; future releases of OS/2 will contain&lt;br /&gt;
         &amp;quot;more-faster-better&amp;quot; presentation manager features. Many of these&lt;br /&gt;
         improvements will apply to existing presentation manager&lt;br /&gt;
         applications; others will expand the interface API. The standard&lt;br /&gt;
         of performance for application interfaces, as well as for&lt;br /&gt;
         application performance, continues to evolve. The rudimentary&lt;br /&gt;
         interfaces and function of the first-generation PC software are no&lt;br /&gt;
         longer considered competitive. Although OS/2 can do nothing to&lt;br /&gt;
         alleviate the developer's burden of keeping an application's&lt;br /&gt;
         function competitive, the presentation manager is a great help in&lt;br /&gt;
         keeping the application's interface state of the art.&lt;br /&gt;
&lt;br /&gt;
     Clearly, using the presentation manager interface is the best&lt;br /&gt;
strategy for new or extensively reworked applications. The presentation&lt;br /&gt;
manager API will be expanded and improved; the INT 10-like VIO functions&lt;br /&gt;
and the VIO direct screen access capabilities will be supported for the&lt;br /&gt;
foreseeable future, but they're an evolutionary dead end. Given that, you&lt;br /&gt;
may want to use the VIO mechanism or the Family API facilities to quickly&lt;br /&gt;
port an application from a real mode version and then use the presentation&lt;br /&gt;
manager in a product upgrade release.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.2  Background I/O&lt;br /&gt;
&lt;br /&gt;
A process is in the background when it is no longer interacting directly&lt;br /&gt;
with the user. In a presentation manager environment, this means that none&lt;br /&gt;
of the process's windows are the keyboard focus. The windows themselves may&lt;br /&gt;
still be visible, or they may be obscured or iconic. In a VIO environment,&lt;br /&gt;
a process is in the background when the user has selected another screen&lt;br /&gt;
group. In this case, the application's screen display is not visible.&lt;br /&gt;
     A presentation manager application can easily continue to update its&lt;br /&gt;
window displays when it is in the background; the application can continue&lt;br /&gt;
to call the presentation manager to change its window contents in any way&lt;br /&gt;
it wishes. A presentation manager application can arrange to be informed&lt;br /&gt;
when it enters and leaves the background (actually, receives and loses the&lt;br /&gt;
keyboard focus), or it can simply carry on with its work, oblivious to the&lt;br /&gt;
issue. Background I/O can continue regardless of whether I/O form is&lt;br /&gt;
character or graphics based.&lt;br /&gt;
     VIO applications can continue to do I/O in background mode as well.&lt;br /&gt;
The VIO package maintains a logical video buffer for each screen group;&lt;br /&gt;
when VIO calls are made to update the display of a screen group that is in&lt;br /&gt;
the background, VIO makes the requested changes to the logical video&lt;br /&gt;
buffer. When the screen group is restored to the foreground, the updated&lt;br /&gt;
contents of the logical video buffer are copied to the display's physical&lt;br /&gt;
video buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
13.3  Graphics Under VIO&lt;br /&gt;
&lt;br /&gt;
VIO is a character-oriented package and provides character mode&lt;br /&gt;
applications with a variety of services. As we have just seen, when a&lt;br /&gt;
screen switch takes place, VIO automatically handles saving the old screen&lt;br /&gt;
image and restoring the new. VIO does provide a mechanism to allow an&lt;br /&gt;
application to sidestep VIO and directly manipulate the physical video&lt;br /&gt;
buffer, where it is then free to use any graphical capability of the&lt;br /&gt;
hardware. There are two major disadvantages to sidestepping VIO for&lt;br /&gt;
graphics rather than using the presentation manager services:&lt;br /&gt;
&lt;br /&gt;
     1.  The application is device dependent because it must manipulate the&lt;br /&gt;
         video display hardware directly.&lt;br /&gt;
&lt;br /&gt;
     2.  VIO can no longer save or restore the state of the physical video&lt;br /&gt;
         buffer during screen switch operations. The application must use a&lt;br /&gt;
         special VIO interface to provide these functions itself.&lt;br /&gt;
&lt;br /&gt;
     The following discussion applies only to applications that want to&lt;br /&gt;
sidestep the presentation manager and VIO interfaces and interact directly&lt;br /&gt;
with the display hardware.&lt;br /&gt;
     Gaining access to the video hardware is easy; the VIO call VioGetBuf&lt;br /&gt;
provides a selector to the video buffer and also gives the application's&lt;br /&gt;
ring 2 code segments, if any, permission to program the video controller's&lt;br /&gt;
registers. The complication arises from the screen-switching capabilities&lt;br /&gt;
of OS/2. When the user switches the application into a background screen&lt;br /&gt;
group, the contents of the video memory belong to someone else; the&lt;br /&gt;
application's video memory is stored somewhere in RAM. It is disastrous&lt;br /&gt;
when an application doesn't pay attention to this process and accidentally&lt;br /&gt;
updates the video RAM or the video controller while they are assigned to&lt;br /&gt;
another screen group.&lt;br /&gt;
     Two important issues are connected with screen switching: (1) How does&lt;br /&gt;
an application find out that it's in background mode? (2) Who saves and&lt;br /&gt;
restores its screen image and where? The VioScrLock call handles screen&lt;br /&gt;
access. Before every access to the display memory or the display&lt;br /&gt;
controller, an application must first issue the VioScrLock call. While the&lt;br /&gt;
call is in effect, OS/2 cannot perform any screen switches. Naturally, the&lt;br /&gt;
application must do its work and quickly release the screen switch lockout.&lt;br /&gt;
Failure to release the lock in a timely fashion has the effect of hanging&lt;br /&gt;
the system, not only for a user's explicit screen switch commands, but also&lt;br /&gt;
for other facilities that use the screen switch mechanism, such as the hard&lt;br /&gt;
error handler. Hard errors can't be presented to the user while the screen&lt;br /&gt;
lock is in effect. If OS/2 needs to switch screens, and an aberrant&lt;br /&gt;
application has the screen lock set, OS/2 will cancel the lock and perform&lt;br /&gt;
the screen switch after a period (currently 30 seconds). This is still a&lt;br /&gt;
disaster scenario, although a mollified one, because the application that&lt;br /&gt;
was summarily &amp;quot;delocked&amp;quot; will probably end up with a trashed screen image.&lt;br /&gt;
The screen lock and unlock calls execute relatively rapidly, so they can be&lt;br /&gt;
called frequently to protect only the actual write-to-screen operation,&lt;br /&gt;
leaving the screen unlocked during computation. Basically, an application&lt;br /&gt;
should use VioScrLock to protect a block of I/O that can be written, in its&lt;br /&gt;
entirety, without significant recomputation. Examples of such blocks are a&lt;br /&gt;
screen scroll, a screen erase, and a write to a cell in a spreadsheet&lt;br /&gt;
program.&lt;br /&gt;
     VioScrLock must be used to protect code sequences that program the&lt;br /&gt;
display hardware as well as code sequences that write to video memory. Some&lt;br /&gt;
peripheral programming sequences are noninterruptible. For example, a two-&lt;br /&gt;
step programming sequence in which the first I/O write selects a&lt;br /&gt;
multiplexed register and the second write modifies that register is&lt;br /&gt;
uninterruptible because the first write placed the peripheral device into a&lt;br /&gt;
special state. Such sequences must be protected within one lock/unlock&lt;br /&gt;
pair.&lt;br /&gt;
     Sometimes when an application calls VioScrLock, it receives a special&lt;br /&gt;
error code that says, &amp;quot;The screen is unavailable.&amp;quot; This means that the&lt;br /&gt;
screen has been switched into the background and that the application may&lt;br /&gt;
not--and must not--manipulate the display hardware. Typically, the program&lt;br /&gt;
issues a blocking form of VioScrLock that suspends the thread until the&lt;br /&gt;
screen is again in the foreground and the video display buffers contain&lt;br /&gt;
that process's image.&lt;br /&gt;
     An application that directly manipulates the video hardware must do&lt;br /&gt;
more than simply lay low when it is in the background. It must also save&lt;br /&gt;
and restore the entire video state--the contents of the display buffer and&lt;br /&gt;
the modes, palates, cursors, and so on of the display controller. VIO does&lt;br /&gt;
not provide this service to direct-screen manipulation processes for two&lt;br /&gt;
reasons. First, the process is very likely using the display in a graphics&lt;br /&gt;
mode. Some display cards contain a vast amount of video memory, and VIO&lt;br /&gt;
would be forced to save it all just in case the application was using it&lt;br /&gt;
all. Second, many popular display controllers such as the EGA and&lt;br /&gt;
compatibles contain many write-only control registers. This means that VIO&lt;br /&gt;
cannot read the controller state back from the card in order to save it for&lt;br /&gt;
a later restoration. The only entity that understands the state of the&lt;br /&gt;
card, and therefore the only entity that can restore that state, is the&lt;br /&gt;
code that programmed it--the application itself.&lt;br /&gt;
     But how does the system notify the process when it's time to save or&lt;br /&gt;
restore? Processes can call the system in many ways, but the system can't&lt;br /&gt;
call processes. OS/2 deals with this situation by inverting the usual&lt;br /&gt;
meaning of call and return. When a process first decides to refresh its own&lt;br /&gt;
screen, it creates an extra thread and uses that thread to call the&lt;br /&gt;
VioSavRedrawWait function. The thread doesn't return from this call right&lt;br /&gt;
away; instead, VIO holds the thread &amp;quot;captive&amp;quot; until it's time for a screen&lt;br /&gt;
switch. To notify the process that it must now save its screen image, VIO&lt;br /&gt;
allows the captive thread to return from the VioSavRedrawWait call. The&lt;br /&gt;
process then saves the display state and screen contents, typically using&lt;br /&gt;
the returned thread. When the save operation is complete, VioSavRedrawWait&lt;br /&gt;
is called again. This notifies VIO that the save is complete and that the&lt;br /&gt;
screen can now be switched; it also resets the cycle so that the process&lt;br /&gt;
can again be notified when it's time to restore its saved screen image. In&lt;br /&gt;
effect, this mechanism makes the return from VioSavRedrawWait analogous to&lt;br /&gt;
a system-to-process call, and it makes the later call to VioSavRedrawWait&lt;br /&gt;
analogous to a return from process to system.&lt;br /&gt;
     The design of OS/2 generally avoids features in which the system calls&lt;br /&gt;
a process to help a system activity such as screen switching. This is&lt;br /&gt;
because a tenet of the OS/2 design religion is that an aberrant process&lt;br /&gt;
should not be able to crash the system. Clearly, we're vulnerable to that&lt;br /&gt;
in this case. VIO postpones the screen switch until the process saves its&lt;br /&gt;
screen image, but what if the process somehow hangs up and doesn't complete&lt;br /&gt;
the save? The screen is in an indeterminate state, and no process can use&lt;br /&gt;
the screen and keyboard. As far as the user is concerned, the system has&lt;br /&gt;
crashed. True, other processes in the system are alive and well, but if the&lt;br /&gt;
user can't get to them, even to save his or her work, their continued&lt;br /&gt;
health is of little comfort.&lt;br /&gt;
     The designers of OS/2 were stuck here, between a rock and a hard&lt;br /&gt;
place: Applications had to be able to save their screen image if they were&lt;br /&gt;
to have direct video access, but such a facility violated the &amp;quot;no crashing&amp;quot;&lt;br /&gt;
tenet of the design religion. Because the video access had to be supported&lt;br /&gt;
and the system had to be crash resistant, we found a two-part workaround.&lt;br /&gt;
     The first part concerns the most common cause of a process hanging up&lt;br /&gt;
in its screen-save operation: hard errors. When a hard error occurs, the&lt;br /&gt;
hard error daemon uses the screen switch mechanism to take control of the&lt;br /&gt;
screen and the keyboard. The hard error daemon saves the existing screen&lt;br /&gt;
image and keeps the application that was in the foreground at the time of&lt;br /&gt;
the hard error from fighting with the daemon over control of the screen and&lt;br /&gt;
the keyboard. However, if the hard error daemon uses the screen-switching&lt;br /&gt;
mechanism and if the screen-switching mechanism allows the foreground&lt;br /&gt;
process to save its own screen image, that process might, while saving its&lt;br /&gt;
screen image, try to use the device that has the hard error and thus&lt;br /&gt;
deadlock the system. The device in error won't service more requests until&lt;br /&gt;
the hard error is cleared, but the hard error can't be cleared until the&lt;br /&gt;
daemon takes control. The daemon can't take control until the foreground&lt;br /&gt;
process is through saving, and the foreground process can't complete saving&lt;br /&gt;
until the device services its request. Note that this deadlock doesn't&lt;br /&gt;
require an explicit I/O operation on the part of the foreground process;&lt;br /&gt;
simply allocating memory or referencing a segment might cause swapping or&lt;br /&gt;
loading activity on the device that is experiencing the hard error.&lt;br /&gt;
     A two-part approach is used to solve this problem. First, deadlocks&lt;br /&gt;
involving the hard error daemon are managed by having the hard error screen&lt;br /&gt;
switch do a partial screen save. When I said earlier that VIO would not&lt;br /&gt;
save the video memory of direct access screen groups, I was lying a bit.&lt;br /&gt;
When the system is doing a hard error screen switch, VIO will save the&lt;br /&gt;
first part (typically 4 KB) of the video memory--enough to display a page&lt;br /&gt;
of text. We don't have to worry about how much video RAM the application&lt;br /&gt;
was using because the video display will be switched to character mode and&lt;br /&gt;
the hard error daemon will overwrite only a small part of video memory.&lt;br /&gt;
Naturally, this means that the hard error daemon must always restore the&lt;br /&gt;
original screen group; it can't switch to a third screen group because the&lt;br /&gt;
first one's video memory wasn't fully saved.&lt;br /&gt;
     VIO and the hard error daemon keep enough free RAM around to save this&lt;br /&gt;
piece of the video memory so that a hard error screen switch can always&lt;br /&gt;
take place without the need for memory swapping. When the hard error daemon&lt;br /&gt;
is finished with the screen, the overwritten video memory is restored from&lt;br /&gt;
the buffer. As we discussed above, however, VIO can't restore the state of&lt;br /&gt;
the video controller itself; only the application can do that. The&lt;br /&gt;
VioModeWait function is used to notify the application that it must restore&lt;br /&gt;
the screen state.&lt;br /&gt;
     In summary, any application that directly accesses the video hardware&lt;br /&gt;
must provide captive threads to VioSavRedrawWait and to VioModeWait.&lt;br /&gt;
VioSavRedrawWait will return when the application is to save or to restore&lt;br /&gt;
the video memory. VioModeWait will return when the application is to&lt;br /&gt;
restore the state of the video controller from the application's own record&lt;br /&gt;
of the controller's state.&lt;br /&gt;
     The second part of the &amp;quot;application hangs while saving screen and&lt;br /&gt;
hangs system&amp;quot; solution is unfortunately ad hoc: If the application does not&lt;br /&gt;
complete its screen save operation within approximately 30 seconds, the&lt;br /&gt;
system considers it hung and switches the screen anyway. The hung process&lt;br /&gt;
is suspended while it's in background so that it won't suddenly &amp;quot;come&lt;br /&gt;
alive&amp;quot; and manipulate the screen. When the process is again in the&lt;br /&gt;
foreground, the system unsuspends it and hopes that it will straighten&lt;br /&gt;
itself out. In such a case, the application's screen image may be trashed.&lt;br /&gt;
At best, the user can enter a &amp;quot;repaint screen&amp;quot; command to the application&lt;br /&gt;
and all will be well; at worst, the application is hung up, but the system&lt;br /&gt;
itself is alive and well. Building a system that can detect and correct&lt;br /&gt;
errors on the part of an application is impossible; the best that we can&lt;br /&gt;
hope to do is to keep an aberrant application from damaging the rest of the&lt;br /&gt;
system.&lt;br /&gt;
     I hope that this long and involved discussion of rules, regulations,&lt;br /&gt;
doom, and disaster has not frightened you into contemplating programs that&lt;br /&gt;
communicate by Morse code. You need be concerned with these issues only if&lt;br /&gt;
you write applications that manipulate the display device directly,&lt;br /&gt;
circumventing either VIO or the presentation manager interfaces. These&lt;br /&gt;
concerns are not an issue when you are writing ordinary text applications&lt;br /&gt;
that use VIO or the presentation manager or graphics applications that use&lt;br /&gt;
the presentation manager. VIO and the presentation manager handle screen&lt;br /&gt;
saving and support background display writing. Finally, I'll point out, as&lt;br /&gt;
a curiosity, that even processes that use handle operations only to write&lt;br /&gt;
to STDOUT use VIO or the presentation manager. When STDOUT points to the&lt;br /&gt;
screen device, the operating system routes STDOUT writes to the&lt;br /&gt;
VIO/presentation manager packages. This is, of course, invisible to the&lt;br /&gt;
application; it need not concern itself with foreground/background, EGA&lt;br /&gt;
screen modes, hard error screen restorations, and the like.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
14  Interactive Programs&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
A great many applications interact with the user via the screen and the&lt;br /&gt;
keyboard. Because the primary function of most desktop computers is to run&lt;br /&gt;
interactive applications, OS/2 contains a variety of services that make&lt;br /&gt;
interaction powerful and efficient.&lt;br /&gt;
     As we've seen in earlier chapters, interactive programs can use the&lt;br /&gt;
presentation manager to manage their interface, or they can do it&lt;br /&gt;
themselves, using VIO or direct video access for their I/O. The&lt;br /&gt;
presentation manager provides a great deal of function and automatically&lt;br /&gt;
solves a great many problems. For example, a presentation manager&lt;br /&gt;
application doesn't have to concern itself with the sharing of the single&lt;br /&gt;
keyboard among all processes in its screen group. The presentation manager&lt;br /&gt;
takes care of that by handling the keyboard and by simply sending keyboard&lt;br /&gt;
events to each process, as appropriate.&lt;br /&gt;
     If you're writing an application that uses the presentation manager,&lt;br /&gt;
then you can skip this chapter. If you're writing an application that does&lt;br /&gt;
not use the presentation manager but that may be used in an interactive&lt;br /&gt;
fashion, it's very important that you understand the issues discussed in&lt;br /&gt;
this chapter. They apply to all programs that use VIO or the STDIN/STDOUT&lt;br /&gt;
handles to do interactive I/O, even if such programs are being run via the&lt;br /&gt;
presentation manager.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
14.1  I/O Architecture&lt;br /&gt;
&lt;br /&gt;
Simply put, the system I/O architecture says that all programs read their&lt;br /&gt;
main input from the STDIN handle and write their main output to the STDOUT&lt;br /&gt;
handle. This applies to all non-presentation manager applications, but&lt;br /&gt;
especially to interactive applications. The reason is that OS/2 and&lt;br /&gt;
program-execution utilities such as CMD.EXE (shell programs) cooperate to&lt;br /&gt;
use the STDIN/STDOUT mechanism to control access to the screen and&lt;br /&gt;
keyboard. For example, if two processes read from the keyboard at the same&lt;br /&gt;
time, some keys go to one process, and the rest go to the other in an&lt;br /&gt;
unpredictable fashion. Likewise, it is a bad idea for more than one process&lt;br /&gt;
to write to the screen at the same time.&lt;br /&gt;
1. Within the same screen group and/or window, of course.&lt;br /&gt;
Applications that use different virtual screens can each write to&lt;br /&gt;
their own screen without regard for other virtual screens.&lt;br /&gt;
1 Clearly, you don't want too many&lt;br /&gt;
processes doing keyboard/screen I/O within a single screen group, but you&lt;br /&gt;
also don't want too few. It would be embarrassing if a user terminated one&lt;br /&gt;
interactive application in a screen group, such as a program run from&lt;br /&gt;
CMD.EXE, and CMD.EXE failed to resume use of the keyboard/screen to print a&lt;br /&gt;
prompt.&lt;br /&gt;
     So how will we handle this? We might be running a great many processes&lt;br /&gt;
in a screen group. For example, you could use CMD.EXE to execute a&lt;br /&gt;
spreadsheet program, which was told to execute a subshell--another copy of&lt;br /&gt;
CMD.EXE. The user could then execute a program to interpret a special batch&lt;br /&gt;
script, which in turn executes an editor. And this editor was told to run a&lt;br /&gt;
copy of a C compiler to scan the source being edited for errors. Oh, yes,&lt;br /&gt;
and we forgot to mention that the top level CMD.EXE was told to run a copy&lt;br /&gt;
of the assembler in parallel with all these other operations (similar to&lt;br /&gt;
the UNIX &amp;quot;&amp;amp;&amp;quot; operation).&lt;br /&gt;
     Many processes are running in this screen group; some of them are&lt;br /&gt;
interactive, and some are not, and at any time only one is using the&lt;br /&gt;
keyboard and the screen. Although it would be handy to declare that the&lt;br /&gt;
most recently executed process will use the keyboard and the screen, you&lt;br /&gt;
can't: The most recently executed program was the C compiler, and it's not&lt;br /&gt;
even interactive. OS/2 cannot decide which process should be using the&lt;br /&gt;
screen and keyboard because OS/2 lacks any knowledge of the function of&lt;br /&gt;
each process. OS/2 knows only their child-parent relationships, and the&lt;br /&gt;
situation can be far too complex for that information to be sufficient.&lt;br /&gt;
     Because OS/2 can't determine which process should be using the screen&lt;br /&gt;
and the keyboard, it doesn't try. The processes themselves make the&lt;br /&gt;
determination. The rule is simple: The process that is currently using the&lt;br /&gt;
screen and the keyboard can grant access to a child process, or it can keep&lt;br /&gt;
access for itself. If a process grants access to a child process, then it&lt;br /&gt;
must keep off the screen and the keyboard until that child terminates. Once&lt;br /&gt;
the child process is granted use of the screen and the keyboard, the child&lt;br /&gt;
process is free to do as it wishes, perhaps granting access to its own&lt;br /&gt;
children. Until that child process terminates, the parent must avoid device&lt;br /&gt;
conflict by staying quiet.&lt;br /&gt;
     Let's look at how this works in real life. For example, CMD.EXE, the&lt;br /&gt;
first process in the screen group, starts up with STDIN open on the&lt;br /&gt;
keyboard and STDOUT open on the screen. (The system did this by magic.)&lt;br /&gt;
When this copy of CMD.EXE is told to execute the spreadsheet program,&lt;br /&gt;
CMD.EXE doesn't know if the spreadsheet program is interactive or not, so&lt;br /&gt;
it lets the child process--the spreadsheet program--inherit its STDIN and&lt;br /&gt;
STDOUT handles, which point to the keyboard and to the screen. Because&lt;br /&gt;
CMD.EXE granted access to the screen and the keyboard to the child, CMD.EXE&lt;br /&gt;
can't use STDIN or STDOUT until that child process terminates. Typically,&lt;br /&gt;
at this point CMD.EXE would DosCWait on its child process.&lt;br /&gt;
     Now the spreadsheet program comes alive. It writes to STDOUT, which is&lt;br /&gt;
the screen, and it reads from STDIN, which is the keyboard. When the&lt;br /&gt;
spreadsheet program is instructed to run CMD.EXE, it does so, presuming, as&lt;br /&gt;
did its parent, that CMD.EXE is interactive and therefore letting CMD.EXE&lt;br /&gt;
inherit its STDIN and STDOUT handles. Now the spreadsheet must avoid any&lt;br /&gt;
STDIN/STDOUT I/O until its child--CMD.EXE--terminates. As long as these&lt;br /&gt;
processes continue to run interactive children, things are going to work&lt;br /&gt;
out OK. When the children start to die and execution starts popping back up&lt;br /&gt;
the tree, applications restart, using the screen and the keyboard in the&lt;br /&gt;
proper order.&lt;br /&gt;
     But what about the detached assembly that CMD.EXE started before it&lt;br /&gt;
ran the spreadsheet? In this case, the user has explicitly told CMD.EXE&lt;br /&gt;
that it wants the application run &amp;quot;detached&amp;quot; from the keyboard. If the user&lt;br /&gt;
specified a STDIN for the assembler--perhaps a file--then CMD.EXE sets that&lt;br /&gt;
up for the child's STDIN. If the user didn't specify an alternate STDIN,&lt;br /&gt;
CMD.EXE opens STDIN on the  NULL device so that an application that reads&lt;br /&gt;
it will receive EOF. In this way, CMD.EXE (which knew that the application&lt;br /&gt;
wasn't to use the keyboard because the user gave explicit instructions) did&lt;br /&gt;
not let the child process inherit STDIN, so CMD.EXE continues to use it,&lt;br /&gt;
printing a new prompt and reading a new command. Figure 14-1 shows a&lt;br /&gt;
typical process tree. The shaded processes have inherited a STDIN, which&lt;br /&gt;
points to the keyboard, and a STDOUT, which points to the screen. All such&lt;br /&gt;
processes must lie on a single path if the rules are followed because each&lt;br /&gt;
process has the option of allowing a maximum of one child to inherit its&lt;br /&gt;
STDIN and STDOUT handles unchanged.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ÀÄÄÂÄÄÄÄÄÄÄÄÂÄÄÙ                 ÚÄÄÄÄ¿    Processes&lt;br /&gt;
             ÚÄÄÄÄÄÄÙ        ÀÄÄÄÄÄÄ¿             ³°°°°³  = using the&lt;br /&gt;
             ³                      ³             ÀÄÄÄÄÙ    keyboard &lt;br /&gt;
         ÚÄÄÄÁÄÄ¿                ÚÄÄÁÄÄÄ¿&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ÀÄÂÄÄÄÄÙ                ÀÄÂÄÄÂÄÙ&lt;br /&gt;
        ÚÄÄÙ                    ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
    ÚÄÄÄÁÄÄ¿                 ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
    ³      ³                 ³°°°°³    ³    ³&lt;br /&gt;
    ³      ³                 ÀÂÄÄÂÙ    ÀÄÄÄÂÙ&lt;br /&gt;
    ÀÄÂÄÄÂÄÙ               ÚÄÄÙ  ÀÄÄ¿      ÀÄÄ¿&lt;br /&gt;
   ÚÄÄÙ  ÀÄÄ¿           ÚÄÄÁÄ¿    ÚÄÁÄÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
ÚÄÄÁÄ¿    ÚÄÁÄÄ¿        ³°°°°³    ³    ³    ³    ³&lt;br /&gt;
³    ³    ³    ³        ÀÂÄÄÂÙ    ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÙ    ÀÄÄÄÄÙ      ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
                   ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
                   ³    ³    ³°°°°³&lt;br /&gt;
                   ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 14-1.  Processes using the keyboard.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     You are undoubtedly becoming a bit concerned at this point: &amp;quot;Does this&lt;br /&gt;
mean I'm forced to use the limited, serial STDIN/STDOUT interface for my&lt;br /&gt;
high-resolution graphics output?&amp;quot; I'm glad you asked. What we've been&lt;br /&gt;
discussing is the architectural model that must be followed because it's&lt;br /&gt;
used systemwide to avoid screen and keyboard conflicts. However,&lt;br /&gt;
applications can and should use special services to optimize their&lt;br /&gt;
interactive I/O as long as they do so according to the architectural model.&lt;br /&gt;
Specifically, OS/2 provides the KBD, VIO, and MOU dynlink packages. These&lt;br /&gt;
high-performance programs interface directly with the hardware, avoiding&lt;br /&gt;
the STDIN/STDOUT limited interfaces. The key is &amp;quot;directly with the&lt;br /&gt;
hardware&amp;quot;: A process is welcome to use hardware-specific interfaces&lt;br /&gt;
to optimize performance, but only after it has ensured that the&lt;br /&gt;
architectural model grants it access to that device.&lt;br /&gt;
     In practice, this is straightforward. Any interactive program that&lt;br /&gt;
wants to use KBD first ensures (via DosQHandType) that its STDIN handle is&lt;br /&gt;
open on the keyboard. If STDIN is not open on the keyboard, the keyboard&lt;br /&gt;
belongs to another program, and the interactive program must not burst in&lt;br /&gt;
on the rightful owner by using KBD. All dynlink device interfaces are&lt;br /&gt;
trusting souls and won't check your bona fides before they do their stuff,&lt;br /&gt;
so the application must look before it leaps. The same applies to STDOUT,&lt;br /&gt;
to the keyboard device, and to the VIO package. All applications must&lt;br /&gt;
verify that STDIN and STDOUT point to the keyboard and the screen before&lt;br /&gt;
they use any device-direct interface, which includes VIO, KBD, MOU, and&lt;br /&gt;
direct device access.&lt;br /&gt;
     What's an interactive program to do if it finds that STDIN or STDOUT&lt;br /&gt;
doesn't point to the keyboard and screen devices? I don't know, but the&lt;br /&gt;
author of the application does. Some applications might not be truly&lt;br /&gt;
interactive and therefore would work fine. For example, Microsoft Macro&lt;br /&gt;
Assembler (MASM) can prompt the user for the names of source, object, and&lt;br /&gt;
listing files. Although MASM is technically interacting with the user, MASM&lt;br /&gt;
is not an interactive application because it doesn't depend on the ability&lt;br /&gt;
to interact to do its work. If STDIN points to a file, MASM is perfectly&lt;br /&gt;
happy reading the filenames from that file. MASM doesn't need to see if&lt;br /&gt;
STDIN points to the keyboard because MASM doesn't need to use the KBD&lt;br /&gt;
package. Instead, MASM reads its names from STDIN and takes what it&lt;br /&gt;
gets.&lt;br /&gt;
     Other programs may not require an interactive interface, but when they&lt;br /&gt;
are interacting, they may want to use KBD or VIO to improve performance.&lt;br /&gt;
Such applications should test STDIN and STDOUT to see if they point to&lt;br /&gt;
the appropriate devices. If they do, applications can circumvent the&lt;br /&gt;
STDIN/STDOUT limitations and use KBD and VIO. If they don't, the&lt;br /&gt;
applications are stuck with STDIN and STDOUT. Finally, many interactive&lt;br /&gt;
applications make no sense at all in a noninteractive environment. These&lt;br /&gt;
applications need to check STDIN and STDOUT, and, if they don't point to&lt;br /&gt;
the devices, the applications should write an error message to STDERR and&lt;br /&gt;
terminate. Admittedly, the user is in error if he or she attempts to run an&lt;br /&gt;
interactive application, such as a WYSIWYG editor, detached, but printing&lt;br /&gt;
an error message is far better than trashing the display screen and&lt;br /&gt;
fighting with CMD.EXE over the keyboard. The screen group would then be&lt;br /&gt;
totally unusable, and the user might not even be able to terminate the&lt;br /&gt;
editor if he or she can't get the terminate command through the keyboard&lt;br /&gt;
contention.&lt;br /&gt;
     It's technically possible, although highly unusual, for an application&lt;br /&gt;
to inherit access to the keyboard yet not have access to the screen. More&lt;br /&gt;
commonly, an application has access to the screen but not to the keyboard.&lt;br /&gt;
Although most users would find it confusing, power users can detach&lt;br /&gt;
programs such as compilers so that any output summary or error messages&lt;br /&gt;
they produce appear on the screen. Although the user may end up with&lt;br /&gt;
intermingled output, he or she may like the instant notification. Each&lt;br /&gt;
application that wants to use VIO, KBD, or the environment manager needs to&lt;br /&gt;
check STDIN and STDOUT individually for access to the appropriate device.&lt;br /&gt;
     Earlier in this section, we talked about how applications work when&lt;br /&gt;
they create children that inherit the screen and the keyboard, and it&lt;br /&gt;
probably sounded complicated. In practice, it can be simple. For example,&lt;br /&gt;
the technique used to DosExecPgm a child that will inherit the keyboard can&lt;br /&gt;
be used when the parent itself doesn't have the keyboard and thus can't&lt;br /&gt;
bequeath it. Therefore, the parent doesn't need to check its STDIN status&lt;br /&gt;
during the DosExecPgm. To summarize, here are the rules:&lt;br /&gt;
&lt;br /&gt;
     Executing Programs&lt;br /&gt;
&lt;br /&gt;
     þ  If the child process is to inherit the STDIN handle, the parent&lt;br /&gt;
        process must not access that handle any further until the child&lt;br /&gt;
        process terminates.&lt;br /&gt;
&lt;br /&gt;
     þ  If the child process is not to inherit the STDIN handle (so that&lt;br /&gt;
        your program can continue to interact), then the child process&lt;br /&gt;
        STDIN must be opened on a file or on the NULL device. Don't rely on&lt;br /&gt;
        the child not to use the handle; the child might DosExecPgm a&lt;br /&gt;
        grandchild that is not so well mannered.&lt;br /&gt;
&lt;br /&gt;
     þ  A process can let only one child at a time inherit its STDIN. If a&lt;br /&gt;
        process is going to run multiple child processes in parallel, only&lt;br /&gt;
        one can inherit STDIN; the others must use alternative STDIN&lt;br /&gt;
        sources.&lt;br /&gt;
&lt;br /&gt;
     þ  All these rules apply to STDINs open on pipes and files as well as&lt;br /&gt;
        to KBD, so your application needn't check the source of STDIN.&lt;br /&gt;
&lt;br /&gt;
     All Processes&lt;br /&gt;
&lt;br /&gt;
     þ  Verify that the STDIN handle points to the keyboard before using&lt;br /&gt;
        KBD, SIGBRK, or SIGCTLC (see below). You must not use these direct&lt;br /&gt;
        device facilities if STDIN is not open on the keyboard.&lt;br /&gt;
&lt;br /&gt;
     þ  Verify that the STDOUT handle points to the screen before using&lt;br /&gt;
        VIO. You must not use direct device facilities if STDOUT is not&lt;br /&gt;
        open on the screen.&lt;br /&gt;
&lt;br /&gt;
     þ  If the process executes any child that inherits its STDIN, it must&lt;br /&gt;
        not terminate itself until that child process terminates. This is&lt;br /&gt;
        because the parent will assume that the termination of the direct&lt;br /&gt;
        child means that the STDIN handle is now available.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
14.2  Ctrl-C and Ctrl-Break Handling&lt;br /&gt;
&lt;br /&gt;
Just when you think that it's safe to go back into the operating system,&lt;br /&gt;
one more device and process tree issue needs to be discussed: the handling&lt;br /&gt;
of Ctrl-C and Ctrl-Break. (Once again, this discussion applies only to&lt;br /&gt;
programs that don't explicitly use the presentation manager facility. Those&lt;br /&gt;
applications that do use the presentation manager have all these issues&lt;br /&gt;
handled for them automatically.) These two events are tied to the keyboard&lt;br /&gt;
hardware, so their routing has a great deal in common with the above&lt;br /&gt;
discussion. The fundamental problem is simple: When the user presses Ctrl-C&lt;br /&gt;
or Ctrl-Break, what's the operating system to do? Clearly, a process or&lt;br /&gt;
processes or perhaps an entire subtree of processes must be killed or&lt;br /&gt;
signaled. But do we kill or signal? And which one(s)?&lt;br /&gt;
     OS/2 defines a convention that allows the processes themselves to&lt;br /&gt;
decide. Consider a type of application--a &amp;quot;command application&amp;quot;--that runs&lt;br /&gt;
in command mode. In command mode, a command application reads a command,&lt;br /&gt;
executes the command, and then typically returns to command mode.&lt;br /&gt;
Furthermore, when the user presses Ctrl-Break, the command application&lt;br /&gt;
doesn't want to terminate but to stop what it's doing and return to command&lt;br /&gt;
mode. This is the style of most interactive applications but not that of&lt;br /&gt;
most noninteractive applications. For example, if the user types MASM to&lt;br /&gt;
CMD.EXE, the CMD.EXE program runs MASM as a child process. CMD.EXE is a&lt;br /&gt;
command application, but MASM is not. The distinction between &amp;quot;command&lt;br /&gt;
application&amp;quot; and &amp;quot;noncommand application&amp;quot; is not made by OS/2 but is merely&lt;br /&gt;
descriptive terminology that is useful in this discussion.&lt;br /&gt;
     The system convention is that Ctrl-C and Ctrl-Break mean &amp;quot;Stop what&lt;br /&gt;
you're doing.&amp;quot; OS/2 generates signals in response to Ctrl-C and Ctrl-Break;&lt;br /&gt;
it never directly kills a process. OS/2 can easily decide which process to&lt;br /&gt;
signal when Ctrl-C or Ctrl-Break is pressed: It signals the lowest command&lt;br /&gt;
process in the process tree in that screen group. At first glance, this may&lt;br /&gt;
not seem easy. How can OS/2 distinguish command processes, and how can it&lt;br /&gt;
determine the &amp;quot;lowest&amp;quot;? The total process tree in a screen group may be&lt;br /&gt;
very complex; some processes in it may have died, creating multiple now-&lt;br /&gt;
independent &amp;quot;treelets.&amp;quot;&lt;br /&gt;
     The process tree may be complex, but the tree of processes using the&lt;br /&gt;
keyboard is simpler because a process can't let multiple children&lt;br /&gt;
simultaneously inherit its STDIN. A process can only inherit the&lt;br /&gt;
keyboard,&lt;br /&gt;
2. Actually, a process should only inherit the&lt;br /&gt;
keyboard. The keyboard device can be opened explicitly, but doing&lt;br /&gt;
so when a process's inherited STDIN doesn't point to the keyboard&lt;br /&gt;
device would be a serious error.&lt;br /&gt;
2 not open it explicitly; so a single path down the tree must&lt;br /&gt;
intersect (or contain) all command processes. This single path can't be&lt;br /&gt;
fragmented because of missing processes due to child death because a&lt;br /&gt;
process that has let a child inherit its STDIN must not terminate until the&lt;br /&gt;
child does. So, all OS/2 needs is to find any command process in the&lt;br /&gt;
command subtree and then look at its descendants for another command&lt;br /&gt;
process and so on. The bottommost process receives the signal.&lt;br /&gt;
3. Actually, OS/2 uses a more efficient algorithm than this; I'm&lt;br /&gt;
merely illustrating that finding the lowest command process is&lt;br /&gt;
not difficult.&lt;br /&gt;
3&lt;br /&gt;
     Figure 14-2 illustrates a possible process tree. The shaded processes&lt;br /&gt;
have inherited handles to the keyboard and screen; those marked with C are&lt;br /&gt;
command processes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
                 ³°°°°°°°°°°°°°°³&lt;br /&gt;
                 ³°°°°°°c°°°°°°°³&lt;br /&gt;
                 ÀÄÄÂÄÄÄÄÄÄÄÄÂÄÄÙ                ÚÄÄÄÄ¿    Processes&lt;br /&gt;
             ÚÄÄÄÄÄÄÙ        ÀÄÄÄÄÄÄ¿            ³°°°°³  = using the&lt;br /&gt;
             ³                      ³            ÀÄÄÄÄÙ    keyboard &lt;br /&gt;
         ÚÄÄÄÁÄÄ¿                ÚÄÄÁÄÄÄ¿&lt;br /&gt;
         ³      ³                ³°°°°°°³&lt;br /&gt;
         ³      ³                ³°°c°°°³&lt;br /&gt;
         ÀÄÂÄÄÄÄÙ                ÀÄÂÄÄÂÄÙ&lt;br /&gt;
        ÚÄÄÙ                    ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
    ÚÄÄÄÁÄÄ¿                 ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
    ³      ³                 ³°c°°³    ³    ³&lt;br /&gt;
    ³      ³                 ÀÂÄÄÂÙ    ÀÄÄÄÂÙ&lt;br /&gt;
    ÀÄÂÄÄÂÄÙ               ÚÄÄÙ  ÀÄÄ¿      ÀÄÄ¿&lt;br /&gt;
   ÚÄÄÙ  ÀÄÄ¿           ÚÄÄÁÄ¿    ÚÄÁÄÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
ÚÄÄÁÄ¿    ÚÄÁÄÄ¿        ³°c°°³    ³    ³    ³    ³&lt;br /&gt;
³    ³    ³    ³        ÀÂÄÄÂÙ    ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
ÀÄÄÄÄÙ    ÀÄÄÄÄÙ      ÚÄÄÙ  ÀÄÄ¿&lt;br /&gt;
                   ÚÄÄÁÄ¿    ÚÄÁÄÄ¿&lt;br /&gt;
                   ³    ³    ³°°°°³&lt;br /&gt;
                   ÀÄÄÄÄÙ    ÀÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 14-2.  Ctrl-C routing in a process tree.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     This now begs the final question: How can OS/2 tell if an application&lt;br /&gt;
is a command process or not? It can tell because all command&lt;br /&gt;
processes/command applications do something that other processes never do.&lt;br /&gt;
By definition, a command process doesn't want to be summarily killed when&lt;br /&gt;
the user presses Ctrl-C or Ctrl-Break, so all command processes establish&lt;br /&gt;
signal handlers for Ctrl-C and Ctrl-Break. Because all command processes&lt;br /&gt;
intercept Ctrl-C and Ctrl-Break, all we need now is to establish the&lt;br /&gt;
convention that only command processes intercept Ctrl-C and Ctrl-Break.&lt;br /&gt;
This hearkens back to our earlier discussion of checking STDIN before&lt;br /&gt;
directly using the keyboard device. Telling the keyboard device that you&lt;br /&gt;
want the Ctrl-C or Ctrl-Break signals routed to your process is a form of&lt;br /&gt;
I/O with the keyboard device, and it must only be done if your program has&lt;br /&gt;
verified that STDIN points to the keyboard device. Furthermore,&lt;br /&gt;
intercepting Ctrl-C or Ctrl-Break just so that your program can clean up&lt;br /&gt;
during unexpected termination is unnecessary and insufficient. The SIGTERM&lt;br /&gt;
signal or, better, the exitlist mechanism provides this capability and&lt;br /&gt;
covers causes of death other than the keyboard. So all processes that&lt;br /&gt;
intercept Ctrl-C and Ctrl-Break have access to the keyboard, and they want&lt;br /&gt;
to do something other than die when the user presses Ctrl-C or Ctrl-Break.&lt;br /&gt;
They fit the command process definition.&lt;br /&gt;
     Now that we've exhaustively shown how OS/2 finds which process to send&lt;br /&gt;
a Ctrl-C signal, what should the process do when it gets the signal? Obey&lt;br /&gt;
the system convention and stop what it's doing as quickly as is wise. If&lt;br /&gt;
the application isn't working on a command, the application typically&lt;br /&gt;
flushes the keyboard type-ahead buffer and reprompts. If the application is&lt;br /&gt;
working on a command that is implemented in code within the application,&lt;br /&gt;
the application jumps from the signal handler to its command loop or, more&lt;br /&gt;
commonly, sets a flag to terminate the current command prematurely.&lt;br /&gt;
4. Occasionally, terminating a command halfway through could&lt;br /&gt;
leave the user's work trashed. In such a case, finishing the&lt;br /&gt;
command is prudent.&lt;br /&gt;
4&lt;br /&gt;
     Finally, if the application is running a child process, it typically&lt;br /&gt;
stops what it's doing by issuing a DosKill on that child command subtree.&lt;br /&gt;
This, then, is how Ctrl-C can kill a program such as MASM. Ctrl-C is sent&lt;br /&gt;
to MASM's closest ancestor that is a command process,&lt;br /&gt;
5. Often, CMD.EXE is MASM's direct ancestor, but other programs, such&lt;br /&gt;
as a build utility, could have come between CMD.EXE and MASM.&lt;br /&gt;
5 which in turn issues&lt;br /&gt;
a DosKill on MASM's subtree. MASM does any exitlist cleanup that it&lt;br /&gt;
wishes (probably deleting scratch files) and then terminates. When the&lt;br /&gt;
command process that ran MASM, typically CMD.EXE, sees that MASM has&lt;br /&gt;
terminated, it prints ^C on the screen, followed by a new prompt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
15  The File System&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
The file system in OS/2 version 1.0 is little changed from that of MS-DOS,&lt;br /&gt;
partially in an effort to preserve compatibility with MS-DOS programs and&lt;br /&gt;
partially due to limitations imposed by the project's schedule. When the&lt;br /&gt;
schedule for an &amp;quot;all singing, all dancing&amp;quot; OS/2 was shown to be too long,&lt;br /&gt;
planned file system improvements were moved to a future release. To explain&lt;br /&gt;
the rationale for postponing something so useful, I'll digress a little.&lt;br /&gt;
     The microcomputer industry developed around the dual concepts of mass&lt;br /&gt;
market software and standards. Because software is mass marketed, you can&lt;br /&gt;
buy some very sophisticated and useful programs for a modest sum of money--&lt;br /&gt;
at least modest in comparison to the development cost, which is often&lt;br /&gt;
measured in millions of dollars. Mass marketing encourages standards&lt;br /&gt;
because users don't want to buy machines, peripherals, and systems that&lt;br /&gt;
don't run these programs. Likewise, the acceptance of the standards&lt;br /&gt;
encourages the development of mass market software because standards make&lt;br /&gt;
it possible for a single binary program to execute correctly on a great&lt;br /&gt;
many machines and thus provide a market big enough to repay the development&lt;br /&gt;
costs of a major application.&lt;br /&gt;
     This synergy, or positive feedback, between standards and mass market&lt;br /&gt;
software affected the process of developing operating systems. At first&lt;br /&gt;
glance, adding new features to an operating system seems straightforward.&lt;br /&gt;
The developers create new features in a new release, and then applications&lt;br /&gt;
are written to use those new features. However, with mass market software,&lt;br /&gt;
it doesn't work that way. Microsoft could indeed release a new version of&lt;br /&gt;
OS/2 with new features (and, in fact, we certainly will do so), but&lt;br /&gt;
initially few new applications will use said new features. This is&lt;br /&gt;
because of the initial limited market penetration of the new release. For&lt;br /&gt;
example, let's assume that at a certain time after the availability of a&lt;br /&gt;
new release, 10 percent of OS/2 users have upgraded. An ISV (Independent&lt;br /&gt;
Software Vendor) is planning its next new product--one it hopes will be a&lt;br /&gt;
bestseller. The ISV must decide whether to use the new feature and&lt;br /&gt;
automatically lock itself out of 90 percent of the potential market or to&lt;br /&gt;
use the common subset of features contained in the earlier OS/2 release and&lt;br /&gt;
be able to run on all machines, including the 10 percent running the OS/2&lt;br /&gt;
upgrade. In general, ISVs won't use a nonvital feature until the great&lt;br /&gt;
majority of existing systems support that feature.&lt;br /&gt;
     The key to introducing new features in an operating system isn't that&lt;br /&gt;
they be available and useful; it's that the release which contains those&lt;br /&gt;
new features sees widespread use as quickly as possible. If this doesn't&lt;br /&gt;
happen, then the new feature pretty much dies stillborn. This is why each&lt;br /&gt;
MS-DOS release that contained major new functionality coincided with a&lt;br /&gt;
release that was required to use new hardware. MS-DOS version 2.0 was&lt;br /&gt;
required for the IBM XT product line; MS-DOS version 3.0 was required for&lt;br /&gt;
the IBM AT product line. And OS/2 is no exception: It wasn't required for a&lt;br /&gt;
new &amp;quot;box,&amp;quot; but it was required to bring out the protect mode machine lying&lt;br /&gt;
fallow inside 80286-based machines. If a new release of a system doesn't&lt;br /&gt;
provide a new feature that makes people want it or need it badly, then&lt;br /&gt;
market penetration will be slow. People will pay the cost and endure the&lt;br /&gt;
hassle of upgrading only if their applications require it, and those&lt;br /&gt;
applications dare require it only if most people have already upgraded.&lt;br /&gt;
     Because of this, the initial release of OS/2 is &amp;quot;magical&amp;quot; in the eyes&lt;br /&gt;
of its developers. It provides a window of opportunity in which to&lt;br /&gt;
introduce new features into the PC operating system standard, a window that&lt;br /&gt;
won't be open quite as wide again for a long time. And this postponed major&lt;br /&gt;
file system enhancements: The file system can be enhanced in a later&lt;br /&gt;
release and benefit existing applications without any change on their part,&lt;br /&gt;
whereas many other OS/2 features needed to be in the first release or they&lt;br /&gt;
might never be available.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
15.1  The OS/2 File System&lt;br /&gt;
&lt;br /&gt;
Although OS/2 version 1.0 contains little in the way of file system&lt;br /&gt;
improvements, it does contain two that are significant. The first is&lt;br /&gt;
asynchronous I/O. Asynchronous I/O consists of two functions, DosReadAsync&lt;br /&gt;
and DosWriteAsync. These functions are identical to DosRead and DosWrite&lt;br /&gt;
except that they return to the caller immediately, usually before the I/O&lt;br /&gt;
operation has completed. Each takes the handle of a semaphore that is&lt;br /&gt;
cleared when the I/O operation completes. The threads of the calling&lt;br /&gt;
process can use this semaphore to poll for operation complete, or they can&lt;br /&gt;
wait for the operation to complete. The DosMuxSemWait call is particularly&lt;br /&gt;
useful in this regard because it allows a process to wait for several&lt;br /&gt;
semaphore events, which can be asynchronous I/O events, IPC events, and&lt;br /&gt;
timer events, intermingled as the programmer wishes.&lt;br /&gt;
     The second file system feature is extended partitioning; it supports&lt;br /&gt;
dividing large physical disks into multiple sections, several of which may&lt;br /&gt;
contain FAT file systems. In effect, it causes OS/2 to treat a large hard&lt;br /&gt;
disk as two or more smaller ones, each of which meets the file system's&lt;br /&gt;
size limits. It's widely believed that MS-DOS is limited to disks less than&lt;br /&gt;
32 MB in size. This isn't strictly true. The limitation is that a disk can&lt;br /&gt;
have no more than 65,535 sectors; the standard sector size is 512 bytes,&lt;br /&gt;
which gives the 32 MB value. Furthermore, each disk is limited to 32,768&lt;br /&gt;
clusters. A sector is the unit of disk storage; disks can read and write&lt;br /&gt;
only integral sectors. A sector's size is established when the disk is&lt;br /&gt;
formatted. A cluster is the unit of disk space allocation for files and&lt;br /&gt;
directories. It may be as small as one sector, or it may be four sectors,&lt;br /&gt;
eight sectors, or some other size. Because the MS-DOS file system supports&lt;br /&gt;
a maximum of 65 KB sectors but only 32 KB clusters, a 32 MB disk must be&lt;br /&gt;
allocated in two-sector (or bigger) clusters. It's possible to write a&lt;br /&gt;
device driver that uses a sector size that is a multiple of 512 bytes,&lt;br /&gt;
which gets around the 65 KB sector restriction and allows the use of a disk&lt;br /&gt;
greater than 32 MB. This trick works for MS-DOS and for OS/2, but it's not&lt;br /&gt;
optimal because it doesn't do anything to increase the maximum number of&lt;br /&gt;
allocation clusters from the existing 32 KB value,&lt;br /&gt;
1. The FAT file system can deal with a maximum of 32 KB allocation&lt;br /&gt;
units, or clusters. No matter what the size of the disk, all files&lt;br /&gt;
must consume disk space in increments of no smaller than 1/32Kth of&lt;br /&gt;
the total disk size. This means that a 60 MB disk, using 1024 byte&lt;br /&gt;
sectors, allocates space in 2048-byte increments.&lt;br /&gt;
1 which means that&lt;br /&gt;
because many disk files are small a lot of space is wasted due to internal&lt;br /&gt;
fragmentation.&lt;br /&gt;
     The OS/2 version 1.0 extended partitioning feature provides an interim&lt;br /&gt;
solution that is not quite as convenient as large sectors but that reduces&lt;br /&gt;
the wastage from internal fragmentation: It allows more than one disk&lt;br /&gt;
partition to contain a FAT file system. Multipartitioned disks are possible&lt;br /&gt;
under MS-DOS, but only one partition can be an MS-DOS (that is, FAT) file&lt;br /&gt;
system. This restriction has been relaxed in OS/2 so that, for example, a&lt;br /&gt;
60 MB disk can be partitioned into two separate logical disks (for example,&lt;br /&gt;
C and D), each 30 MB.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
15.2  Media Volume Management&lt;br /&gt;
&lt;br /&gt;
The multitasking capability of OS/2 necessitated major file system&lt;br /&gt;
enhancements in the area of volume management. A disk volume is the name&lt;br /&gt;
given to the file system and files on a particular disk medium. A disk&lt;br /&gt;
drive that contains a fixed medium always contains the same volume, but a&lt;br /&gt;
disk drive from which the media (such as floppy disks) can be removed will&lt;br /&gt;
contain whatever disk--whatever volume--the user has in it at the time.&lt;br /&gt;
That volumes can change becomes a problem in a multitasking environment.&lt;br /&gt;
For example, suppose a user is using a word processor to edit a file on a&lt;br /&gt;
floppy disk in drive A. The editor has opened the file and is keeping it&lt;br /&gt;
open for the duration of the edit. Without closing the file or terminating&lt;br /&gt;
the editor, the user can switch to a screen group in which a spreadsheet&lt;br /&gt;
program is running. The user might then need to insert a different disk&lt;br /&gt;
into drive A--one that contains data needed by the spreadsheet. If the user&lt;br /&gt;
then switches back to the word processor without remembering to change the&lt;br /&gt;
floppy disk, disaster will strike. Pressing the Page Down key will cause&lt;br /&gt;
the editor to try to read another sector from its already open disk file.&lt;br /&gt;
The operating system knows--because of FAT information stored in RAM&lt;br /&gt;
buffers_ that the next sector in the text file is sector N, and it will&lt;br /&gt;
issue a read to sector N on the wrong medium--the spreadsheet floppy disk--&lt;br /&gt;
and return that to the word processor program as the next sector of the&lt;br /&gt;
text file. And, at that, the user is getting off lightly; he or she might&lt;br /&gt;
just as easily have given the word processor a command that caused it to&lt;br /&gt;
write a sector to the disk, which would do double damage. A file on the&lt;br /&gt;
spreadsheet floppy would be destroyed by the &amp;quot;random&amp;quot; write, and the text&lt;br /&gt;
file would be corrupted as well because it's missing a sector write that it&lt;br /&gt;
should have received.&lt;br /&gt;
     We can't solve this problem by admonishing the user to be careful;&lt;br /&gt;
many programs read from and write to disk without direct user intervention.&lt;br /&gt;
For example, the word processor might save work in progress to disk every&lt;br /&gt;
two minutes. If this time interval elapses while the user is still working&lt;br /&gt;
with the spreadsheet program on the spreadsheet floppy disk, our&lt;br /&gt;
hypothetical &amp;quot;flawless&amp;quot; user is still S.O.L.&lt;br /&gt;
2. Severely out of luck.&lt;br /&gt;
2&lt;br /&gt;
     OS/2 resolves these problems by recognizing that when an application&lt;br /&gt;
does I/O to an open file the I/O is not really aimed at drive A; it's aimed&lt;br /&gt;
at a particular floppy disk volume--the one containing the open file. Each&lt;br /&gt;
disk volume, removable or not, has a volume name stored in its root&lt;br /&gt;
directory and a unique 32-bit volume identifier stored in its boot sector.&lt;br /&gt;
Figure 15-1 illustrates the two volume names--one for computer use and one&lt;br /&gt;
for human use. Each file handle is associated with a particular 32-bit&lt;br /&gt;
volume ID. When an I/O request is made for a file handle, OS/2 checks to&lt;br /&gt;
see if the proper volume is in the drive by comparing the 32-bit value of&lt;br /&gt;
the request with that of the medium currently spinning. If they match, the&lt;br /&gt;
operation completes. If the mounted volume is different from the requested&lt;br /&gt;
volume, OS/2 uses the hard error daemon mechanism to prompt the user to&lt;br /&gt;
insert the correct volume in the drive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                            VOLUME LABELS&lt;br /&gt;
Sector                                                          Sector&lt;br /&gt;
  0                                                               N&lt;br /&gt;
  ÚÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
  ³     ³             ³      ³                                    ³&lt;br /&gt;
  ³     ³             ³      ³                                    ³&lt;br /&gt;
  ÀÄÄ�ÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄ�ÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
     ÀÄ 32-bit volume ID  ÀÄÄÄÄÄÄÄ volume name in&lt;br /&gt;
        in boot sector             home directory&lt;br /&gt;
&lt;br /&gt;
Figure 15-1.  Volume ID and volume name location.&lt;br /&gt;
&lt;br /&gt;
     Three problems must be overcome to make this scheme practical. First,&lt;br /&gt;
checking the volume ID of the medium must be fast. You can't afford to read&lt;br /&gt;
the boot sector each time you do an I/O operation if doing so halves the&lt;br /&gt;
speed of disk I/O. Second, you need assurance that volume IDs are unique.&lt;br /&gt;
Third, you need a plan to deal with a volume that doesn't have a volume ID.&lt;br /&gt;
     Keeping down the cost of volume verification is easy if you know when&lt;br /&gt;
a volume is changed; obviously, the ID is read from the boot sector only&lt;br /&gt;
when a medium has been changed. But how can OS/2 tell that a media change&lt;br /&gt;
has occurred? It can't; that's a device driver issue.&lt;br /&gt;
     For starters, if the device contains a nonremovable medium, rechecking&lt;br /&gt;
its volume ID is never necessary. The device driver understands this, and&lt;br /&gt;
when it is asked the status of the medium, it responds, &amp;quot;Unchanged.&amp;quot; Some&lt;br /&gt;
removable media drives have a flag bit that warns the driver that the door&lt;br /&gt;
has been opened. In this case, when asked, the device driver tells OS/2&lt;br /&gt;
that the medium is &amp;quot;uncertain.&amp;quot; The driver doesn't know for sure that it&lt;br /&gt;
was really changed, but it may have been; so OS/2 rechecks the volume ID.&lt;br /&gt;
Rechecking the volume ID is more difficult when a removable media device&lt;br /&gt;
has no such indicator.&lt;br /&gt;
     In this case, the author of the device driver uses device-specific&lt;br /&gt;
knowledge to decide on a minimum possible time to effect a media change. If&lt;br /&gt;
the device is ready, yet less than the minimum possible time has elapsed&lt;br /&gt;
since the last operation, the driver knows that the same medium must be in&lt;br /&gt;
the drive. If more than the minimum possible time has elapsed, the driver&lt;br /&gt;
returns &amp;quot;medium uncertain,&amp;quot; and OS/2 rechecks the volume label. This time&lt;br /&gt;
interval is typically 2 seconds for floppy disk drives, so effectively an&lt;br /&gt;
extra disk read is done after every idle period; for any given episode of&lt;br /&gt;
disk I/O, however, no extra reads are needed.&lt;br /&gt;
     Ensuring that a volume ID is unique is another problem. Simply&lt;br /&gt;
lecturing the user on the wisdom of unique IDs is inadequate; the user will&lt;br /&gt;
still label three disks &amp;quot;temp&amp;quot; or number them all as &amp;quot;10.&amp;quot; And even the&lt;br /&gt;
hypothetical perfect user might borrow from a neighbor a disk whose name is&lt;br /&gt;
the same as one the user already owns. OS/2 deals with this problem by&lt;br /&gt;
using a 32-bit randomized value for disk volume IDs. When a disk is&lt;br /&gt;
formatted, the user enters a supposedly unique name. This name is&lt;br /&gt;
checksummed, and the result, combined with the number of seconds between&lt;br /&gt;
the present and 1980, is used to seed a random number generator. This&lt;br /&gt;
generator returns a 32-bit volume ID. Although accidentally duplicating a&lt;br /&gt;
volume ID is obviously possible, the four billion possible codes make it&lt;br /&gt;
quite unlikely.&lt;br /&gt;
     The name the user enters is used only to prompt the user to insert the&lt;br /&gt;
volume when necessary, so it need not be truly unique for the volume&lt;br /&gt;
management system to work. If the user names several disks WORK, OS/2 still&lt;br /&gt;
sees them as independent volumes because their volume IDs are different. If&lt;br /&gt;
the user inserts the wrong WORK disk in response to a prompt, OS/2&lt;br /&gt;
recognizes it as the wrong disk and reissues the &amp;quot;Insert disk WORK&amp;quot; prompt.&lt;br /&gt;
After trying each WORK volume in turn, the user will probably decide to&lt;br /&gt;
relabel the disks!&lt;br /&gt;
     The thorniest problem arises from unlabeled disks--disks formatted&lt;br /&gt;
with MS-DOS. Forcing the user to label these disks is unacceptable, as is&lt;br /&gt;
having OS/2 automatically label them with volume IDs: The disk may be read-&lt;br /&gt;
only, perhaps permanently so. Even if the disk is not read-only, the&lt;br /&gt;
problem of low density and high density raises its ugly head. Low-density&lt;br /&gt;
disks can be read in a high-density drive, but writes made to a low-density&lt;br /&gt;
disk from a high-density drive can only be read on high-density drives. If&lt;br /&gt;
a low-density disk is placed in a high-density drive and then labeled by&lt;br /&gt;
OS/2, its boot sector is no longer readable when the disk is placed in a&lt;br /&gt;
low-density drive.&lt;br /&gt;
     For volumes without a proper volume ID, OS/2 attempts to create a&lt;br /&gt;
unique substitute volume ID by checksumming parts of the volume's root&lt;br /&gt;
directory and its FAT table. OS/2 uses the existing volume name if one&lt;br /&gt;
exists; if there is no volume name, OS/2 attempts to describe the disk.&lt;br /&gt;
None of these techniques is foolproof, and they require extra disk&lt;br /&gt;
operations every time the medium is identified. Therefore, software&lt;br /&gt;
distributors and users should make every effort to label disks that OS/2&lt;br /&gt;
systems are to use. OS/2 labels are backward compatible with MS-DOS version&lt;br /&gt;
3.x labels.&lt;br /&gt;
     The OS/2 DISKCOPY command makes a byte-by-byte verbatim copy of a&lt;br /&gt;
floppy disk, except that the duplicate disk has a different volume ID value&lt;br /&gt;
in the boot sector (the volume label name is not changed). OS/2 users can't&lt;br /&gt;
tell this, however, because the DISKCOMP utility lies, and if two disks are&lt;br /&gt;
identical in every byte except for the volume ID, it reports that the disks&lt;br /&gt;
are identical. However, if the user uses DISKCOPY to duplicate the disk&lt;br /&gt;
under OS/2 and then compares the two with DISKCOMP under MS-DOS 3.x, a&lt;br /&gt;
difference is reported.&lt;br /&gt;
     Our discussion so far has centered on file reads and writes to an open&lt;br /&gt;
handle. Reads and writes are volume-oriented operations because they're&lt;br /&gt;
aimed at the volume on which the file resides. DosOpens, on the other hand,&lt;br /&gt;
are drive oriented because they search the default or specified drive for&lt;br /&gt;
the file in question (or create it) regardless of the volume in the drive.&lt;br /&gt;
All handle operations are volume oriented, and all name-based calls are&lt;br /&gt;
drive oriented. Currently, you cannot specify that a given file is to be&lt;br /&gt;
opened on or created on a specific volume. To ensure that a scratch or&lt;br /&gt;
output file is created on a certain volume, arrange to have a file open on&lt;br /&gt;
that volume and issue a write to that file immediately before doing the&lt;br /&gt;
file open. The write operation followed by a DosBufReset will ensure that&lt;br /&gt;
the particular medium is in the drive at that time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
15.3  I/O Efficiency&lt;br /&gt;
&lt;br /&gt;
OS/2 provides full blocking and deblocking services for all disk I/O&lt;br /&gt;
requests. A program can read or write any number of bytes, and OS/2 will&lt;br /&gt;
read the proper sectors into internal buffers so that only the specified&lt;br /&gt;
bytes are affected. Naturally, every DosRead or DosWrite call takes time to&lt;br /&gt;
execute, so if your program makes few I/O calls, each for large amounts of&lt;br /&gt;
data, it will execute faster.&lt;br /&gt;
     I/O performance can be further improved by making sector aligned&lt;br /&gt;
calls, that is, by requesting a transfer of an integral multiple of 512&lt;br /&gt;
bytes to or from a file seek position that is itself a multiple of 512.&lt;br /&gt;
OS/2 reads and writes entire disk sectors directly from and to the device&lt;br /&gt;
hardware without an intermediate copy step through system buffers. Because&lt;br /&gt;
the file system keeps logically adjacent sectors physically adjacent on the&lt;br /&gt;
disk, disk seek times and rotational latency are such that one can read or&lt;br /&gt;
write four sectors of data (2048 bytes) in essentially the same time needed&lt;br /&gt;
to read or write one sector (512 bytes).&lt;br /&gt;
     Even if the length or the file position of the request isn't a&lt;br /&gt;
multiple of 512, OS/2 performs the initial fraction of the request via its&lt;br /&gt;
buffers, directly transfers any whole sectors out of the middle of the&lt;br /&gt;
request, and uses the buffers for the fractional remainder. Even if your&lt;br /&gt;
requests aren't sector aligned, making them as large as feasible is&lt;br /&gt;
beneficial.&lt;br /&gt;
     To summarize, I/O is most efficient when requests are large and sector&lt;br /&gt;
aligned. Even misaligned requests can be almost optimally serviced if they&lt;br /&gt;
are large. Programs that cannot naturally make aligned requests and that&lt;br /&gt;
are not I/O intensive should take advantage of the blocking and deblocking&lt;br /&gt;
services that OS/2 provides. Likewise, programs that need to make large,&lt;br /&gt;
unaligned requests should use OS/2's blocking management. Programs that&lt;br /&gt;
need to make frequent, small, nonaligned requests will perform best if they&lt;br /&gt;
read blocks of sectors into internal buffers and deblock the data&lt;br /&gt;
themselves, avoiding the overhead of frequent DosRead or DosWrite calls.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16  Device Monitors, Data Integrity, and Timer Services&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
In discussing the design goals of OS/2, I mentioned continuing to support&lt;br /&gt;
the kinds of functionality found in MS-DOS, even when that functionality&lt;br /&gt;
was obtained by going around the operating system. A good example of such&lt;br /&gt;
functionality is device data manipulation, a technique that usually&lt;br /&gt;
involves hooking interrupt vectors and that is used by many application&lt;br /&gt;
programs. For example, pop-up programs such as SideKick have become very&lt;br /&gt;
popular. These programs get into memory via the terminate and stay resident&lt;br /&gt;
mechanism and then edit the keyboard interrupt vector to point to their&lt;br /&gt;
code. These programs examine each keystroke to see if it is their special&lt;br /&gt;
activate key. If not, they transfer control to the original interrupt&lt;br /&gt;
handler. If the keystroke is their special activate key, they retain&lt;br /&gt;
control of the CPU and display, or &amp;quot;pop up,&amp;quot; a message or a menu on the&lt;br /&gt;
screen. Other programs hook the keyboard vector to provide spell checking&lt;br /&gt;
or keyboard macro expansion. Some programs also hook the BIOS entry vector&lt;br /&gt;
that commands the printer, either to substitute alternate printer driver&lt;br /&gt;
code or to manipulate the data sent to the printer. Programs that turn a&lt;br /&gt;
spreadsheet's output sideways are an example of this.&lt;br /&gt;
     In general, these programs edit, or hook, the interrupt vectors that&lt;br /&gt;
receive device interrupts and communicate with device driver routines in&lt;br /&gt;
the ROM BIOS. The functions provided by such programs and their evident&lt;br /&gt;
popularity among users demonstrate a need for programs to be able to&lt;br /&gt;
monitor and/or modify device data streams. The OS/2 mechanism that does&lt;br /&gt;
this is called a device monitor.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.1  Device Monitors&lt;br /&gt;
&lt;br /&gt;
The design of device monitors had to meet the general requirements and&lt;br /&gt;
religion of OS/2. Specifically, the MS-DOS technique of letting&lt;br /&gt;
applications receive interrupts by editing the interrupt vectors could not&lt;br /&gt;
be allowed because doing so would destroy the system's ability to provide a&lt;br /&gt;
stable environment. Furthermore, unlike MS-DOS, OS/2 doesn't use the ROM&lt;br /&gt;
BIOS as a form of device driver, so hooking the BIOS communication vectors&lt;br /&gt;
would not provide access to the device data stream. In addition, allowing&lt;br /&gt;
an application to arbitrarily interfere with a device driver's operation is&lt;br /&gt;
contrary to OS/2 design principles; the device driver is the architectural&lt;br /&gt;
embodiment of knowledge about the device, and it must be involved in and&lt;br /&gt;
&amp;quot;aware&amp;quot; of any external manipulation of the data stream. The result is an&lt;br /&gt;
OS/2 device monitor mechanism that allows processes, running in their&lt;br /&gt;
normal ring 3 state, to monitor and edit device data streams with the prior&lt;br /&gt;
permission and knowledge of the appropriate device driver.&lt;br /&gt;
     Specifically, a process registers itself as a device monitor by&lt;br /&gt;
calling the appropriate device driver via a DosMonReg call.&lt;br /&gt;
1. Which is a dynlink package that eventually calls the device&lt;br /&gt;
driver via a DosDevIOCtl call.&lt;br /&gt;
1 The process&lt;br /&gt;
also provides two data buffers, one for incoming monitor data and another&lt;br /&gt;
for outgoing monitor data. Processes can easily call OS/2, but OS/2 has no&lt;br /&gt;
way to call processes.&lt;br /&gt;
2. Signals are a partial exception to this, but signals have&lt;br /&gt;
limitations, as discussed earlier.&lt;br /&gt;
2 OS/2 gets around this by inverting the normal&lt;br /&gt;
sense of a call and return sequence. When OS/2 needs to &amp;quot;call&amp;quot; a process,&lt;br /&gt;
it requires that process to call OS/2 beforehand with one of its threads.&lt;br /&gt;
OS/2 holds this thread captive until the callback event takes place. OS/2&lt;br /&gt;
then accomplishes a call to the process by releasing the thread so that it&lt;br /&gt;
returns from the holding system call and resumes execution within the&lt;br /&gt;
process. When the process is ready to &amp;quot;return&amp;quot; to OS/2, it recalls the&lt;br /&gt;
holding entry point (see Figure 16-1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      Process calls OS/2     |        OS/2 &amp;quot;calls&amp;quot; Process&lt;br /&gt;
                             | &lt;br /&gt;
Process  call                |  Process  call           FCN    call&lt;br /&gt;
    ÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄ    |      ÄÄÄÄÄÄ¿       ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
          �       �          |            �       �             �&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
  Ä Ä Ä Ä Å Ä Ä Ä Å Ä Ä Ä    |    Ä Ä Ä Ä Å Ä Ä Ä Å Ä Ä Ä Ä Ä Ä Å Ä Ä&lt;br /&gt;
 OS/2     ³       ³          |   OS/2     ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          �       �          |            �       �             �&lt;br /&gt;
          ³       ³          |            ³       ³             ³&lt;br /&gt;
          ³  FCN  ³          |            ³       ³             ³&lt;br /&gt;
          ÀÄÄÄÄÄÄÄÙ          |            ÀÄÄ&amp;lt; &amp;lt;ÄÄÙ             ÀÄÄÄ&amp;lt;&lt;br /&gt;
                Return       |                &amp;gt; &amp;gt; Return             &amp;gt;&lt;br /&gt;
&lt;br /&gt;
Figure 16-1.  &amp;quot;Calling&amp;quot; a process from OS/2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 uses this technique for monitors as well. A monitoring process is&lt;br /&gt;
required to call the OS/2 entry point directly after registering itself as&lt;br /&gt;
a device monitor. OS/2 notifies the monitor process of the presence of data&lt;br /&gt;
in the incoming buffer by allowing this thread to return to the process.&lt;br /&gt;
Figure 16-2 illustrates a device with two monitoring processes, X and Y.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿              ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³         Monitor          ³              ³         Monitor          ³&lt;br /&gt;
³        Process X         ³              ³        Process Y         ³&lt;br /&gt;
³                          ³              ³                          ³&lt;br /&gt;
³ DosMonRead   DosMonWrite ³              ³ DosMonRead   DosMonWrite ³&lt;br /&gt;
ÀÄÄÄÄÄ�ÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ              ÀÄÄÄÄÄ�ÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÙ&lt;br /&gt;
      ÀÄ¿          ÀÄÄÄÄÄÄÄÄÄÄÄ¿      ÚÄÄÄÄÄÄÄÄÄÙ            ³&lt;br /&gt;
        ³                      ³      ³                      ³&lt;br /&gt;
ÚÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄ¿&lt;br /&gt;
³       ³                      ³      ³                      ³       ³&lt;br /&gt;
³   ÚÄÄÄÁÄÄÄÄ¿                Ú�ÄÄÄÄÄÄÁ¿                ÚÄÄÄÄ�ÄÄÄ¿   ³&lt;br /&gt;
³   ³ Buffer ³                ³ Buffer ³                ³ Buffer ³   ³&lt;br /&gt;
³   ³   1    ³                ³   2    ³                ³   3    ³   ³&lt;br /&gt;
³   ÀÄÄÄ�ÄÄÄÄÙ                ÀÄÄÄÄÄÄÄÄÙ                ÀÄÄÄÄÂÄÄÄÙ   ³&lt;br /&gt;
³  ÚÄÄÄÄÙ                        OS/2                        ÀÄÄÄÄ¿  ³&lt;br /&gt;
ÀÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÙ&lt;br /&gt;
   ³ Data in                                             Data out ³&lt;br /&gt;
   ³                                                              ³&lt;br /&gt;
ÚÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄ¿&lt;br /&gt;
³  ³                        Device driver                         �  ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 16-2.  Device monitors.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     But we need to discuss a few additional details. First, because OS/2&lt;br /&gt;
strives to make processes see a consistent environment regardless of the&lt;br /&gt;
presence of other processes, each device can have as many monitors as the&lt;br /&gt;
device driver allows. OS/2 connects multiple device monitors into a chain&lt;br /&gt;
so that the device data stream is passed through the first monitor in the&lt;br /&gt;
chain, then through the second monitor, and so on. When a process registers&lt;br /&gt;
itself as a monitor, it specifies whether it wants to be first in the chain&lt;br /&gt;
or last in the chain; some applications are sensitive to this. The first&lt;br /&gt;
monitor to register itself as first is truly first; the next monitor to ask&lt;br /&gt;
for first actually becomes second, and so forth. The same algorithm applies&lt;br /&gt;
to monitors that want to be last: The first to so request becomes the last,&lt;br /&gt;
the second to request last becomes next to last, and so forth.&lt;br /&gt;
     The actual format of the monitor data stream is device specific; the&lt;br /&gt;
device driver decrees the format. Some device drivers have special rules&lt;br /&gt;
and requirements. For example, the keyboard device driver allows monitoring&lt;br /&gt;
processes to insert the &amp;quot;screen switch&amp;quot; key sequence into the data stream,&lt;br /&gt;
whereupon it is recognized as if the user had typed it at the physical&lt;br /&gt;
keyboard. But the device driver will not pass such sequences that really&lt;br /&gt;
were typed through the monitor chain; they are directly obeyed instead.&lt;br /&gt;
     This approach prevents an amok keyboard monitor from effectively&lt;br /&gt;
crashing the system by intercepting and consuming all attempts by the user&lt;br /&gt;
to switch screen groups. The screen device driver does not allow device&lt;br /&gt;
monitors, not because the performance impact would be too big (as it, in&lt;br /&gt;
fact, would be) but because the VIO and presentation manager dynlink&lt;br /&gt;
packages totally circumvent the screen device driver so that it never sees&lt;br /&gt;
any screen data being written.&lt;br /&gt;
     The DosMonRead call holds the device's thread until incoming data is&lt;br /&gt;
available. The DosMonWrite call returns the CPU to the process as soon as&lt;br /&gt;
it is able. The same thread that calls DosMonRead need not be the one to&lt;br /&gt;
call DosMonWrite (see below).&lt;br /&gt;
     Because monitors are an important component of OS/2, an application&lt;br /&gt;
must be very careful to use them properly; therefore, some caveats are in&lt;br /&gt;
order. First, monitors are inserted into the device data chain, with&lt;br /&gt;
obvious effects on the data throughput rate of the device. Each time the&lt;br /&gt;
user presses a key, for example, a packet must pass through every monitor&lt;br /&gt;
in the keyboard chain before the application can read the key and obey or&lt;br /&gt;
echo it. Clearly, any sluggishness on the part of a monitor or the presence&lt;br /&gt;
of too many monitors in a chain will adversely affect system response. The&lt;br /&gt;
thread involved in reading, processing, and writing monitor data should be&lt;br /&gt;
set at a high priority. We recommend the lowest of the force run priority&lt;br /&gt;
categories. Furthermore, the monitor component of a monitoring application&lt;br /&gt;
must contain no critical sections or other events that could slow or&lt;br /&gt;
suspend its operation. In addition, if a monitor data stream will be&lt;br /&gt;
extensively processed, a normal-priority thread must be used to handle that&lt;br /&gt;
processing so that the high-priority thread can continue to transfer&lt;br /&gt;
monitor data in and out without impediment. For example, an auxiliary&lt;br /&gt;
thread and buffer must be used if a keyboard monitor is to write all&lt;br /&gt;
keystrokes to a disk buffer.&lt;br /&gt;
     Finally, if a monitor process terminates abnormally although OS/2&lt;br /&gt;
properly unlinks it from the monitor chain, the data in the process's&lt;br /&gt;
monitor buffers is lost. Clearly, losing an unspecified amount of data&lt;br /&gt;
without warning from the keyboard data stream or perhaps from printer&lt;br /&gt;
output will upset the user no little amount. Monitoring processes must be&lt;br /&gt;
written carefully so that they minimize this risk.&lt;br /&gt;
     The device monitor feature threatens OS/2's fundamental architectural&lt;br /&gt;
principles more than any other. Thus, its presence in the system testifies&lt;br /&gt;
to its importance. Specifically, device monitors violate the design&lt;br /&gt;
principle of minimizing interference between processes, a.k.a.&lt;br /&gt;
encapsulation. Clearly, a process that is monitoring a device's data stream&lt;br /&gt;
can affect the output of or input to a great many processes other than&lt;br /&gt;
itself. This is sometimes called a feature, not a bug. For example, the&lt;br /&gt;
printer spooler uses monitors to intercept output aimed at the printer,&lt;br /&gt;
storing it on disk, and to feed data from those disk files to the actual&lt;br /&gt;
printer device. Clearly, spooling printer output interferes with another&lt;br /&gt;
process, but the interference is valuable. Designers of monitoring&lt;br /&gt;
applications must ensure that their applications damage neither the&lt;br /&gt;
system's performance nor its stability.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2  Data Integrity&lt;br /&gt;
&lt;br /&gt;
I've discussed data integrity in a multitasking environment several times.&lt;br /&gt;
This section does not review that material in detail but brings together&lt;br /&gt;
all the elements and introduces a few related system facilities.&lt;br /&gt;
     The first problem in a multitasking system is that multiple processes,&lt;br /&gt;
or multiple threads within a process, may try to simultaneously manipulate&lt;br /&gt;
the same resource--a file, a device, a data structure in memory, or even a&lt;br /&gt;
single byte of memory. When the manipulation of a resource must be&lt;br /&gt;
serialized to work correctly, that manipulation is called a critical&lt;br /&gt;
section. This term refers to the act of manipulating the resource, but not&lt;br /&gt;
particularly to the code that does so. Clearly, if any of four subroutines&lt;br /&gt;
can manipulate a particular resource, entering any of the four is entering&lt;br /&gt;
the critical section.&lt;br /&gt;
     The problem is more pervasive than a programmer unfamiliar with the&lt;br /&gt;
issue might assume. For example, even the simple act of testing a word to&lt;br /&gt;
see if it holds the value 4 and incrementing it if it doesn't is a critical&lt;br /&gt;
section. If only one thread in one process can access this word, then the&lt;br /&gt;
critical section is serialized. But if more than one thread can access the&lt;br /&gt;
word, then more than one thread could be in the critical section at the&lt;br /&gt;
same time, with disastrous results. Specifically, consider the assembly&lt;br /&gt;
language sequence shown in Listing 16-1. It looks simple enough: Test to&lt;br /&gt;
see if COUNT holds 4; if it doesn't, increment it; if it does, jump to the&lt;br /&gt;
label COMPLETE. Listing 16-2 shows what might go wrong in a multithreaded&lt;br /&gt;
environment: Thread A checks the value to see if it's 4, but it's 3. Right&lt;br /&gt;
after the compare instruction, a context switch takes place, and thread B&lt;br /&gt;
is executed. Thread B also performs the compare, sees the value as 3, and&lt;br /&gt;
increments it. Later, thread A resumes execution, after the compare&lt;br /&gt;
instruction, at a location where it believes the COUNT value to be 3; so it&lt;br /&gt;
also increments the value of COUNT. The value is now 5 and will continue to&lt;br /&gt;
be incremented way past the value of 4 that was supposed to be its upper&lt;br /&gt;
limit. The label COMPLETE may never be reached.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
COUNT        DW      0               ; Event counter&lt;br /&gt;
&lt;br /&gt;
        .&lt;br /&gt;
        .&lt;br /&gt;
        CMP     COUNT,4        ; is this the 4th?&lt;br /&gt;
        JE      COMPLETE       ; yes, we're done&lt;br /&gt;
        INC     COUNT          ; count event&lt;br /&gt;
        .&lt;br /&gt;
        .&lt;br /&gt;
&lt;br /&gt;
Listing 16-1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        Thread A                                     Thread B&lt;br /&gt;
&lt;br /&gt;
             .&lt;br /&gt;
             .&lt;br /&gt;
             CMP     COUNT,4  [count is now 3]&lt;br /&gt;
             -------------------context switch---&amp;gt;&lt;br /&gt;
                                    CMP     COUNT,4 [count is 3]&lt;br /&gt;
                                    JE      COMPLETE&lt;br /&gt;
                                    INC     COUNT   [count is 4]&lt;br /&gt;
                                              .&lt;br /&gt;
                                              .&lt;br /&gt;
             &amp;lt;-----------context switch--------&lt;br /&gt;
             JE      COMPLETE [jmp not taken]&lt;br /&gt;
             INC     COUNT    [count is now 5]&lt;br /&gt;
&lt;br /&gt;
Listing 16-2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     I apologize for again lecturing on this topic, but such problems are&lt;br /&gt;
very nonobvious, rarely turn up in testing, are nearly impossible to find&lt;br /&gt;
in the field, and the very possibility of their existence is new with OS/2.&lt;br /&gt;
Thus, &amp;quot;too much is not enough,&amp;quot; caveat-wise. Now that we've reviewed the&lt;br /&gt;
problems, let's look at the solutions.&lt;br /&gt;
     A programmer inexperienced with a multitasking environment might&lt;br /&gt;
protest that this scenario is unlikely, and indeed it is. Maybe the chances&lt;br /&gt;
are only 1 in 1 million that it would happen. But because a microprocessor&lt;br /&gt;
executes 1 million instructions a second, it might not be all that long&lt;br /&gt;
before the 1-in-1-million unlucky chance comes true. Furthermore, an&lt;br /&gt;
incorrect program normally has multiple unprotected critical sections, many&lt;br /&gt;
of which are larger than the 2-instruction window in our simple example.&lt;br /&gt;
     The program must identify and protect all critical sections; a program&lt;br /&gt;
that fails to do so will randomly fail. You can't take solace in there&lt;br /&gt;
being only one CPU and assuming that OS/2 probably won't context switch in&lt;br /&gt;
the critical section. OS/2 can context switch at any time, and because&lt;br /&gt;
context switching can be triggered by unpredictable external events, such&lt;br /&gt;
as serial port I/O and rotational latency on a disk, no amount of testing&lt;br /&gt;
can prove that an unprotected critical section is safe. In reality, a test&lt;br /&gt;
environment is often relatively simple; context switching tends to occur at&lt;br /&gt;
consistent intervals, which means that such problems tend not to turn up&lt;br /&gt;
during program test. Instead, they turn up in the real world, and give your&lt;br /&gt;
program a reputation for instability.&lt;br /&gt;
     Naturally, testing has its place, but the only sure way to deal with&lt;br /&gt;
critical sections is to examine your code carefully while assuming that all&lt;br /&gt;
threads in the system are executing simultaneously.&lt;br /&gt;
3. This is more than a Gedankenexperiment. Multiple&lt;br /&gt;
processor machines will be built, and when they are, OS/2 will execute&lt;br /&gt;
multiple threads, even within one process, truly simultaneously.&lt;br /&gt;
3 Furthermore, when&lt;br /&gt;
examining a code sequence, always assume that the CPU will reschedule in&lt;br /&gt;
the worst way. If there is any possible window, reality will find it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.1  Semaphores&lt;br /&gt;
The traditional solution for protecting critical sections is the semaphore.&lt;br /&gt;
The two OS/2 semaphores--RAM and system--each have advantages, and the&lt;br /&gt;
operation of each is guaranteed to be completely immune to critical section&lt;br /&gt;
problems. In the jargon, their operation is guaranteed atomic. Whenever a&lt;br /&gt;
thread is going to manipulate a critical resource, it first claims the&lt;br /&gt;
semaphore that protects the resource. Only after it controls the semaphore&lt;br /&gt;
does it look at the resource because the resource's values may have changed&lt;br /&gt;
between the time the semaphore was requested and the time it was granted.&lt;br /&gt;
After the thread completes its manipulation of the resource, it releases&lt;br /&gt;
the semaphore.&lt;br /&gt;
     The semaphore mechanism protects well against all cooperating&lt;br /&gt;
4. Obviously, if some thread refuses to claim the semaphore,&lt;br /&gt;
nothing can be done.&lt;br /&gt;
4&lt;br /&gt;
threads, whether they belong to the same process or to different processes.&lt;br /&gt;
Another OS/2 mechanism, called DosEnterCritSec, can be used to protect a&lt;br /&gt;
critical section that is accessed only by threads belonging to a single&lt;br /&gt;
process. When a thread issues the DosEnterCritSec call, OS/2 suspends&lt;br /&gt;
execution of all other threads in that process until a subsequent&lt;br /&gt;
DosEnterCritSec call is issued. Naturally, only threads executing in&lt;br /&gt;
application mode are suspended; threads executing inside the OS/2 kernel&lt;br /&gt;
are not suspended until they attempt to return to application mode.&lt;br /&gt;
5. I leave as an exercise to the reader to explain why the&lt;br /&gt;
DosEnterCritSec call is not safe unless all other threads&lt;br /&gt;
in the process make use of it for that critical section as well.&lt;br /&gt;
5 The&lt;br /&gt;
use of DosEnterCritSec is dangerous because the process's other threads may&lt;br /&gt;
be suspended while they are holding a critical section. If the thread that&lt;br /&gt;
issued the DosEnterCritSec then also tries to enter that critical section,&lt;br /&gt;
the process will deadlock. If a dynlink package is involved, it may have&lt;br /&gt;
created extra threads unbeknownst to the client process so that the client&lt;br /&gt;
may not even be aware that such a critical section exists and might be in&lt;br /&gt;
use. For this reason, DosEnterCritSec is safe only when used to protect&lt;br /&gt;
short sections of code that can't block or deadlock and that don't call any&lt;br /&gt;
dynlink modules.&lt;br /&gt;
     Still another OS/2 critical section facility is file sharing and&lt;br /&gt;
record locking, which can be used to protect critical sections when they&lt;br /&gt;
consist of files or parts of files. For example, a database program&lt;br /&gt;
certainly considers its master database file a critical section, and it&lt;br /&gt;
doesn't want anyone messing with it while the database application has it&lt;br /&gt;
open. It can open the file with the file-sharing mode set to &amp;quot;allow no&lt;br /&gt;
(other) readers, allow no writers.&amp;quot; As long as the database application&lt;br /&gt;
keeps the file open, OS/2 prevents any other process from opening (or&lt;br /&gt;
deleting!) that file.&lt;br /&gt;
     The record-locking mechanism can be used to provide a smaller&lt;br /&gt;
granularity of protection. A process can lock a range of bytes within a&lt;br /&gt;
file, and while that lock is in effect, OS/2 prevents any other process&lt;br /&gt;
from reading or writing those bytes. These two specialized forms of&lt;br /&gt;
critical section protection are unique in that they protect a process&lt;br /&gt;
against all other processes, even &amp;quot;uncooperating&amp;quot; ones that don't protect&lt;br /&gt;
their own access to the critical section. Unfortunately, the file-sharing&lt;br /&gt;
and record-locking mechanisms don't contain any provision for blocking&lt;br /&gt;
until the conflict is released. Applications that want to wait for the&lt;br /&gt;
conflict to clear must use a polling loop. Use DosSleep to block for at&lt;br /&gt;
least a half second between each poll.&lt;br /&gt;
     Unfortunately, although semaphores protect critical sections well,&lt;br /&gt;
sometimes they bring problems of their own. Specifically, what happens if&lt;br /&gt;
an asynchronous event, such as program termination or a signal, pulls the&lt;br /&gt;
CPU away from inside a critical section and the CPU never returns to&lt;br /&gt;
release the semaphore? The answers range from &amp;quot;moot&amp;quot; to &amp;quot;disaster,&amp;quot;&lt;br /&gt;
depending on the circumstances. The possibilities are so manifold that&lt;br /&gt;
I'll group some of them.&lt;br /&gt;
     What can you do if the CPU is pulled away inside a critical&lt;br /&gt;
section?&lt;br /&gt;
&lt;br /&gt;
     þ  Ignore it. This is fine if the critical section is wholly accessed&lt;br /&gt;
        by a single process and that process doesn't use signals to modify&lt;br /&gt;
        the normal path of execution and if neither the process nor its&lt;br /&gt;
        dynlink routines attempt to enter the critical section during&lt;br /&gt;
        DosExitList processing.&lt;br /&gt;
&lt;br /&gt;
     þ  Clear the semaphore. This is an option if you know that the&lt;br /&gt;
        resource protected by the semaphore has no state, such as a&lt;br /&gt;
        semaphore that protects the right to be writing to the screen. The&lt;br /&gt;
        trick is to ensure that the interrupted thread set the semaphore&lt;br /&gt;
        and that you don't accidentally clear the semaphore when you don't&lt;br /&gt;
        set it. For example, if the semaphore is wholly used within a&lt;br /&gt;
        single process but that process's DosExitList handlers may use it,&lt;br /&gt;
        they can force the semaphore clear when they are entered.&lt;br /&gt;
&lt;br /&gt;
     þ  Detect the situation and repair the critical section. This&lt;br /&gt;
        detection can be made for RAM semaphores only during process&lt;br /&gt;
        termination and only if the semaphore is solely used by that&lt;br /&gt;
        process. In such a case, you know that a thread in the process set&lt;br /&gt;
        the semaphore, and you know that the thread is no longer executing&lt;br /&gt;
        the critical section because all threads are terminated. You can&lt;br /&gt;
        test the semaphore by using a nonblocking DosSemSet; if it's set,&lt;br /&gt;
        &amp;quot;recover&amp;quot; the resource.&lt;br /&gt;
&lt;br /&gt;
        System semaphores are generally better suited for this. When the&lt;br /&gt;
        owning thread of a system semaphore dies, the semaphore is given a&lt;br /&gt;
        special mark. The next attempt to set the semaphore returns with a&lt;br /&gt;
        code that tells the new owner that the previous owner died within&lt;br /&gt;
        the critical section. The new owner has the option of cleaning up&lt;br /&gt;
        the resource.&lt;br /&gt;
&lt;br /&gt;
     Another possibility is to try to prevent the CPU from being yanked out&lt;br /&gt;
of a critical section. Signals can be momentarily delayed with the&lt;br /&gt;
DosHoldSignal mechanism. Process termination that results from an external&lt;br /&gt;
kill can be postponed by setting up a signal handler for the KILL signal&lt;br /&gt;
and then using DosHoldSignal. This last technique doesn't protect you&lt;br /&gt;
against termination due to GP fault and the like however.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.2  DosBufReset&lt;br /&gt;
One remaining data integrity issue--disk data synchronization--is not&lt;br /&gt;
related to critical sections. Often, when a DosWrite call is made, OS/2&lt;br /&gt;
holds the data in a buffer rather than writing it immediately to disk.&lt;br /&gt;
Naturally, any subsequent calls made to read this data are satisfied&lt;br /&gt;
correctly, so an application cannot see that the data has not yet been&lt;br /&gt;
written unless the reading application uses direct physical access to the &lt;br /&gt;
volume (that is, raw media reads). This case explains why CHKDSK may&lt;br /&gt;
erroneously report errors that run on a volume that has open files.&lt;br /&gt;
     OS/2 eventually writes the data to the disk, so this buffering is of&lt;br /&gt;
concern only when the system crashes with unwritten buffered data.&lt;br /&gt;
Naturally, such crashes are expected to be rare, but some applications may&lt;br /&gt;
find the possibility so threatening that they want to take protective&lt;br /&gt;
steps. The two OS/2 functions for this purpose are flushing and&lt;br /&gt;
writethroughs. The flush operation--DosBufReset--writes all dirty buffers--&lt;br /&gt;
those with changed but unwritten data in them--to the disk. When the call&lt;br /&gt;
returns, the data is on the disk. Use this call sparingly; although its&lt;br /&gt;
specification promises only that it will flush buffers associated with the&lt;br /&gt;
specified file handle(s), for most file systems it writes all dirty buffers&lt;br /&gt;
in the system to disk. Moreover, if file handles are open to a server&lt;br /&gt;
machine on the network, most or all of that server's buffers get flushed,&lt;br /&gt;
even those that were used by other client machines on the network. Because&lt;br /&gt;
of these costs, applications should use this operation judiciously.&lt;br /&gt;
     Note that it's not true that a flush operation simply causes a write&lt;br /&gt;
to be done sooner rather than later. A flush operation may also cause extra&lt;br /&gt;
disk writes. For example, consider an application that is writing data 10&lt;br /&gt;
bytes at a time. In this case, OS/2 buffers the data until it has a full&lt;br /&gt;
sector's worth. A series of buffer flush operations arriving at this time&lt;br /&gt;
would cause the assembly buffer to be written to the disk many extra and&lt;br /&gt;
unnecessary times.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.2.3  Writethroughs&lt;br /&gt;
Buffer flushes are expensive, and unless they are used frequently, they&lt;br /&gt;
don't guarantee a particular write ordering. Some applications, such as&lt;br /&gt;
database managers, may want to guarantee that data be written to the disk&lt;br /&gt;
in exactly the same order in which it was given to OS/2 via DosWrite. For&lt;br /&gt;
example, an application may want to guarantee that the data is in place in&lt;br /&gt;
a database before the allocation chain is written and that the chain be&lt;br /&gt;
written before the database directory is updated. Such an ordering may make&lt;br /&gt;
it easy for the package to recover the database in case of a crash.&lt;br /&gt;
     The OS/2 mechanism for doing this is called writethrough--a status bit&lt;br /&gt;
that can be set for individual file handles. If a writethrough is in effect&lt;br /&gt;
for a handle to which the write is issued, OS/2 guarantees that the data&lt;br /&gt;
will be written to the disk before the DosWrite operation returns.&lt;br /&gt;
Obviously, applications using writethrough should write their data in large&lt;br /&gt;
chunks; writing many small chunks of data to a file marked for writethrough&lt;br /&gt;
is very inefficient.&lt;br /&gt;
&lt;br /&gt;
     Three caveats are associated with writethroughs:&lt;br /&gt;
&lt;br /&gt;
     þ  If writethrough is set on a file after it is open, all subsequent&lt;br /&gt;
        writes are written through, but data from previous writes may still&lt;br /&gt;
        be in dirty buffers.&lt;br /&gt;
&lt;br /&gt;
     þ  If a writethrough file is being shared by multiple processes or is&lt;br /&gt;
        open on multiple handles, all instances of that file should be&lt;br /&gt;
        marked writethrough. Data written to a handle not marked&lt;br /&gt;
        writethrough may go into the buffers.&lt;br /&gt;
&lt;br /&gt;
     þ  The operation of data writethroughs has some nonintuitive surprises&lt;br /&gt;
        when used with the current FAT file system. Specifically, although&lt;br /&gt;
        this feature works as advertised to place the file's data sectors&lt;br /&gt;
        on the disk, it does not update the directory entry that specifies&lt;br /&gt;
        the size of the file. Thus, if you extend a file by 10 sectors and&lt;br /&gt;
        the system crashes before you close the file, the data in those 10&lt;br /&gt;
        sectors is lost. If you had writethrough set, then those 10 sectors&lt;br /&gt;
        of data were indeed written to the disk; but because the directory&lt;br /&gt;
        entry wasn't updated, CHKDSK will return those sectors to the free&lt;br /&gt;
        list.&lt;br /&gt;
&lt;br /&gt;
        The writethrough operation protects the file's data but not the&lt;br /&gt;
        directory or allocation information. This is not a concern as long&lt;br /&gt;
        as you write over a portion of the file that has been already&lt;br /&gt;
        extended, but any writes that extend the file are not protected.&lt;br /&gt;
        The good news is that the data will be on the disk, as guaranteed,&lt;br /&gt;
        but the bad news is that the directory entry won't be updated; if&lt;br /&gt;
        the system crashes, file extensions cannot be recovered. The&lt;br /&gt;
        recommended solution is to use DosNewSize to extend the file as&lt;br /&gt;
        needed, followed by DosBufReset to update the directory information&lt;br /&gt;
        on the disk, and then to writethrough the data as needed.&lt;br /&gt;
        Overextending the file size is better than doing too many&lt;br /&gt;
        NewSize/BufReset combinations; if you overextend, you can always&lt;br /&gt;
        shrink the file before closing it with a final DosNewSize.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
16.3  Timer Services&lt;br /&gt;
&lt;br /&gt;
Frequently, applications want to keep track of the passage of real time. A&lt;br /&gt;
game program may want events to occur asynchronously with the user's input;&lt;br /&gt;
a telecommunications program may want to track how long a response takes&lt;br /&gt;
and perhaps declare a link timed-out after some interval. Other programs&lt;br /&gt;
may need to pace the display of a demonstration or assume a default action&lt;br /&gt;
if the user doesn't respond in a reasonable amount of time. OS/2 provides&lt;br /&gt;
several facilities to track the passage of real time; applications should&lt;br /&gt;
use these facilities and shun polling and timing loops because the timing&lt;br /&gt;
of such loops depends on the system's workload and the CPU's speed and&lt;br /&gt;
because they totally lock out from execution any thread of a lower&lt;br /&gt;
priority.&lt;br /&gt;
     Time intervals in OS/2 are discussed in terms of milliseconds to&lt;br /&gt;
isolate the concept of a time interval from the physical mechanism&lt;br /&gt;
(periodic clock interrupts) that measures time intervals. Although you can&lt;br /&gt;
specify a time interval down to the millisecond, the system does not&lt;br /&gt;
guarantee any such accuracy.&lt;br /&gt;
     On most hardware, OS/2 version 1.0 uses a periodic system clock&lt;br /&gt;
interrupt of 32 Hz (32 times a second). This means that OS/2 measures time&lt;br /&gt;
intervals with a quantum size of 31.25 milliseconds. As a result, any&lt;br /&gt;
timeout value is subject to quantization error of this order. For example,&lt;br /&gt;
if a process asks to sleep for 25 milliseconds, OS/2 knows that the request&lt;br /&gt;
was made at some time after the most recent clock tick, but it cannot tell&lt;br /&gt;
how long after, other than that less than 31.25 milliseconds had elapsed&lt;br /&gt;
between the previous clock tick and the sleep request. After the sleep&lt;br /&gt;
request is made, another clock tick occurs. Once again, OS/2 can't tell how&lt;br /&gt;
much time has elapsed since the sleep request and the new clock tick, other&lt;br /&gt;
than that it was less than 31.25 milliseconds. Lacking this knowledge, OS/2&lt;br /&gt;
uses a simple algorithm: At each clock tick, OS/2 decrements each timeout&lt;br /&gt;
value in the system by the clock tick interval (generally 31.25&lt;br /&gt;
milliseconds). Thus, our 25-millisecond sleep request may come back in 1&lt;br /&gt;
millisecond or less or in 31.25 milliseconds. A request to block for 33&lt;br /&gt;
milliseconds could come back in 32 milliseconds or in 62.5 milliseconds.&lt;br /&gt;
     Clearly, the OS/2 timer functions are intended for human-scale timing,&lt;br /&gt;
in which the 1/32-second quantization error is not noticeable, and not for&lt;br /&gt;
high-precision timing of fast events. Regardless of the resolution of the&lt;br /&gt;
timer, the system's preemptive scheduler prevents the implementation of&lt;br /&gt;
high-accuracy short-interval timing. Even if a timer system call were to&lt;br /&gt;
time out after a precise interval, the calling thread might not resume&lt;br /&gt;
execution immediately because a higher-priority thread might be executing&lt;br /&gt;
elsewhere.&lt;br /&gt;
     One form of OS/2 timer services is built into some system calls. For&lt;br /&gt;
example, all semaphore blocking calls support an argument that allows the&lt;br /&gt;
caller to specify a timeout value. When the specified time has elapsed, the&lt;br /&gt;
call returns with a &amp;quot;call timed out&amp;quot; error code. Some threads use this&lt;br /&gt;
facility to guard against being indefinitely locked out; if the semaphore&lt;br /&gt;
call times out, the thread can give up, display an error message, or try&lt;br /&gt;
another tactic. Other threads may use the facility expecting to be timed&lt;br /&gt;
out: They use the timeout facility to perform periodic tasks and use the&lt;br /&gt;
semaphore just as an emergency flag. Another thread in the system can&lt;br /&gt;
provide an emergency wakeup for the timer thread simply by clearing the&lt;br /&gt;
semaphore.&lt;br /&gt;
     Blocking on a semaphore merely to delay for a specific interval is&lt;br /&gt;
unnecessary; the DosSleep call allows a thread to block unconditionally for&lt;br /&gt;
an arbitrary length of time, subject, of course, to the timer's&lt;br /&gt;
quantization error.&lt;br /&gt;
6. And subject to the fact that if thread 1 is doing the DosSleeping&lt;br /&gt;
the sleep will be interrupted if a signal is taken.&lt;br /&gt;
6 DosSleep measures time intervals in a synchronous&lt;br /&gt;
fashion: The thread is held inside the operating system until the time&lt;br /&gt;
interval has elapsed. OS/2 provides an asynchronous timer service that&lt;br /&gt;
allows timing to take place in parallel with a thread's normal execution.&lt;br /&gt;
Specifically, the DosTimerAsync call is made with a timeout interval, such&lt;br /&gt;
as DosSleep, and also with the handle of a system semaphore.&lt;br /&gt;
7. Unlike most semaphore applications, the timer functions work&lt;br /&gt;
only with system semaphores. RAM semaphores may not be used&lt;br /&gt;
because of the difficulty in posting a RAM semaphore at interrupt&lt;br /&gt;
time; the RAM that contains the semaphore may be swapped out.&lt;br /&gt;
7 The&lt;br /&gt;
DosTimerAsync call returns immediately; later, when the time interval has&lt;br /&gt;
elapsed, the system semaphore is cleared. The process can poll the&lt;br /&gt;
semaphore to see if the time is up, and/or it can block on the semaphore to&lt;br /&gt;
wait for the time to elapse. Of course, if a process contains multiple&lt;br /&gt;
threads, some can poll and others can block.&lt;br /&gt;
     The DosTimerStart call is identical to the DosTimerAsync call except&lt;br /&gt;
that the semaphore is repeatedly cleared at the specified interval until a&lt;br /&gt;
corresponding DosTimerStop call is made. DosTimerStart clears the&lt;br /&gt;
semaphore; the process must set it again after it's been cleared.&lt;br /&gt;
     None of the above-mentioned facilities is completely accurate for&lt;br /&gt;
tracking the time of day or the amount of elapsed time. As we mentioned, if&lt;br /&gt;
a higher-priority thread is consuming enough CPU time, unpredictable delays&lt;br /&gt;
occur. Even DosTimerStart is susceptible to losing ticks because if the CPU&lt;br /&gt;
is unavailable for a long enough period the process won't be able to reset&lt;br /&gt;
the semaphore soon enough to prevent missing its next clearing.&lt;br /&gt;
Applications that want a precise measurement of elapsed time should use the&lt;br /&gt;
time values stored in the global infoseg. We also recommend that&lt;br /&gt;
applications with a critical need to manage timeouts, even if they are&lt;br /&gt;
executing in the lower-priority background, dedicate a thread to managing&lt;br /&gt;
the time-critical work and elevate that thread to a higher priority. This&lt;br /&gt;
will ensure that time-critical events aren't missed because a high-priority&lt;br /&gt;
foreground thread is going through a period of intensive CPU usage. Of&lt;br /&gt;
course, such an application must be designed so that the high-priority&lt;br /&gt;
timer event thread does not itself consume significant CPU time; it should&lt;br /&gt;
simply log the timer events and rely on its fellow normal-priority threads&lt;br /&gt;
to handle the major work involved.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17  Device Drivers and Hard Errors&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
The multitasking nature of OS/2 makes OS/2 device drivers considerably more&lt;br /&gt;
complex than MS-DOS device drivers. Furthermore, whenever you have devices,&lt;br /&gt;
you must deal with device failures--the infamous hard errors. The handling&lt;br /&gt;
of hard errors in a multitasking environment is likewise considerably more&lt;br /&gt;
complex than it was under MS-DOS.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1  Device Drivers&lt;br /&gt;
&lt;br /&gt;
This section gives an overview of device drivers, paying special attention&lt;br /&gt;
to their key architectural elements. Writing a device driver is a complex&lt;br /&gt;
task that must be undertaken with considerable care; a great many caveats&lt;br /&gt;
and &amp;quot;gotchas&amp;quot; lie in wait for the unsuspecting programmer. Many of these&lt;br /&gt;
&amp;quot;gotchas&amp;quot; are of that most favorite breed: ones that never show up in&lt;br /&gt;
testing, only in the field. This section is by no means an exhaustive&lt;br /&gt;
discussion of device drivers, nor is it a how-to guide. Study the OS/2&lt;br /&gt;
device driver reference documentation carefully before setting out to write&lt;br /&gt;
your own.&lt;br /&gt;
     In Chapter 2 I briefly discussed device independence and the role&lt;br /&gt;
that device drivers play in bringing it about. I said that a device driver&lt;br /&gt;
is a package of code that transforms I/O requests made in standard, device-&lt;br /&gt;
independent fashion into the operations necessary to make a specific piece&lt;br /&gt;
of hardware fulfill that request. A device driver takes data and status&lt;br /&gt;
information from the hardware, in the hardware-specific format, and&lt;br /&gt;
massages that information into the form that the operating system expects&lt;br /&gt;
to receive.&lt;br /&gt;
     The device driver architecture has two key elements. First, each&lt;br /&gt;
hardware device has its own device driver to hide the specific details of&lt;br /&gt;
the device from the operating system. Second, device drivers are not hard-&lt;br /&gt;
wired into the operating system when it is manufactured; they are&lt;br /&gt;
dynamically installed at boot time. This second point is the interesting&lt;br /&gt;
one. If all device drivers were hard-wired into OS/2, the technique of&lt;br /&gt;
encapsulating device-dependent code into specific packages would be good&lt;br /&gt;
engineering practice but of little interest to the user. OS/2 would run&lt;br /&gt;
only on a system configured with a certain magic set of peripheral devices.&lt;br /&gt;
But because device drivers are dynamically installable at boot time, OS/2&lt;br /&gt;
can work with a variety of devices, even ones that didn't exist when OS/2&lt;br /&gt;
was written, as long as a proper device driver for that device is installed&lt;br /&gt;
at boot time. Note that device drivers can be installed only at boot time;&lt;br /&gt;
they cannot be installed after the system has completed booting up. This is&lt;br /&gt;
because in a future secure environment the ability to dynamically install a&lt;br /&gt;
device driver would give any application the ability to violate system&lt;br /&gt;
security.&lt;br /&gt;
     Saying that device drivers merely translate between the operating&lt;br /&gt;
system and the device is a bit of oversimplification; in reality, they are&lt;br /&gt;
responsible for encapsulating, or owning, nearly all device-specific&lt;br /&gt;
knowledge about the device. Device drivers service the interrupts that&lt;br /&gt;
their devices generate, and they work at task time (that is, at&lt;br /&gt;
noninterrupt time). If a device monitor is necessary for a device, the&lt;br /&gt;
device driver writer decides that and provides the necessary support. If an&lt;br /&gt;
application needs direct access to a device's I/O ports or to its special&lt;br /&gt;
mapped memory,&lt;br /&gt;
1. For example, the display memory of a CGA or an EGA card.&lt;br /&gt;
1 the driver offers those services to processes. The device&lt;br /&gt;
driver also knows whether multiple processes should simultaneously use the&lt;br /&gt;
device and either allows or disallows this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.1  Device Drivers and OS/2 Communication&lt;br /&gt;
Because device drivers need to call and be called by OS/2 efficiently and&lt;br /&gt;
because device drivers must handle hardware interrupts efficiently, they&lt;br /&gt;
must run at ring 0. This means that device drivers must be trusted and must&lt;br /&gt;
be trustworthy. A flaky device driver--or worse, a malicious one--can do&lt;br /&gt;
unlimited and nearly untraceable damage to any application or data file in&lt;br /&gt;
the system.&lt;br /&gt;
     OS/2 can easily call a device driver. Because OS/2 loaded the device&lt;br /&gt;
driver into memory, it knows the address of its entry point and can call it&lt;br /&gt;
directly. For the device driver to call OS/2 is trickier because the driver&lt;br /&gt;
doesn't know the memory locations that OS/2 occupies nor does it have any&lt;br /&gt;
control over the memory descriptor tables (LDT and GDT). When the device&lt;br /&gt;
driver is initialized, OS/2 supplies the device driver with the address of&lt;br /&gt;
the OS/2 DevHlp entry point. Device drivers call this address to access a&lt;br /&gt;
variety of OS/2 services, called DevHlp services. The OS/2 DevHlp address&lt;br /&gt;
references a GDT selector so that the DevHlp address is valid at all times--&lt;br /&gt;
in protected mode, in real mode,&lt;br /&gt;
2. A GDT selector cannot literally be valid in real mode because the&lt;br /&gt;
GDT is not in use. OS/2 uses a technique called tiling so that the&lt;br /&gt;
selector, when used as a segment address in real mode, addresses&lt;br /&gt;
the same physical memory as does the protect mode segment.&lt;br /&gt;
2 at interrupt time, and during device&lt;br /&gt;
driver initialization. Some DevHlp functions are only valid in certain&lt;br /&gt;
modes, but the DevHlp facility is always available.&lt;br /&gt;
     Why don't device drivers simply use dynamic links to access OS/2&lt;br /&gt;
services, the way that applications do? The OS/2 kernel dynlink interface&lt;br /&gt;
is designed for processes running in user mode, at ring 3, to call the ring&lt;br /&gt;
0 kernel. In other words, it's designed for outsiders to call in, but&lt;br /&gt;
device drivers are already inside. They run at ring 0, in kernel mode, and&lt;br /&gt;
at interrupt time. One, of course, could kludge things so that device&lt;br /&gt;
drivers make dynlink calls, and then special code at those OS/2 entry&lt;br /&gt;
points would recognize a device driver request and do all the special&lt;br /&gt;
handling. But every system call from a normal application would be slowed&lt;br /&gt;
by this extra code, and every service call from a device driver would&lt;br /&gt;
likewise be slowed. As a result, device drivers have their own private,&lt;br /&gt;
high-efficiency &amp;quot;backdoor&amp;quot; entry into OS/2. Figure 17-1 illustrates&lt;br /&gt;
the call linkages between OS/2 and a device driver. OS/2 calls only one&lt;br /&gt;
entry point in the device driver, providing a function code that the device&lt;br /&gt;
driver uses to address a dispatch table. OS/2 learns the address of&lt;br /&gt;
thisentry point when it loads the device driver. The device driver in turn&lt;br /&gt;
calls only one OS/2 address, the DevHlp entry point. It also supplies a&lt;br /&gt;
function code that is used to address a dispatch table. The device driver&lt;br /&gt;
is told this address when it receives its initialize call from OS/2. Not&lt;br /&gt;
shown is the device driver's interrupt entry point.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
           OS/2                                   Device driver&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Far call (FCN)   ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³                        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄ¿    ³  Function              ³&lt;br /&gt;
³               DevHlp   ³             ³    ³   Table                ³&lt;br /&gt;
³         Function Table ³             ³    ³  ÚÄÄÄÄÄÄ¿  ÚÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄ¿  ÚÄÄÄÄÄÄ¿  ³             ÀÄÄÄÄÅÄ�³      ÃÄÄÙ           ³&lt;br /&gt;
³           ÀÄÄ´      ³�ÄÅÄÄÄÄÄÄÄÄÄÄ¿       ³  ³      ÃÄÄÄÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄÄÄÄ´      ³  ³ DevHlp   ³       ³  ³      ÃÄÄ¿           ³&lt;br /&gt;
³           ÚÄÄ´      ³  ³ address  ³       ³  ÀÄÄÄÄÄÄÙ  ÀÄÄ�°°°°°°  ³&lt;br /&gt;
³  °°°°°°�ÄÄÙ  ÀÄÄÄÄÄÄÙ  ³          ³       ³                        ³&lt;br /&gt;
³                        ³          ÀÄÄÄÄÄÄÄ´                        ³&lt;br /&gt;
³                        ³       Far call   ³                        ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ     (DevHlp FCN) ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 17-1.  Device driver call linkages.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.2  Device Driver Programming Model&lt;br /&gt;
The programming model for device drivers under MS-DOS is simple. Device&lt;br /&gt;
drivers are called to perform a function, and they return when that&lt;br /&gt;
function is complete or they encounter an unrecoverable error. If the&lt;br /&gt;
device is interrupt driven, the CPU hangs in a loop inside the device&lt;br /&gt;
driver while waiting for the driver's interrupt handler to be entered; when&lt;br /&gt;
the operation is complete, the interrupt handler sets a private flag to&lt;br /&gt;
break the task-time CPU out of its wait loop.&lt;br /&gt;
     The OS/2 device driver model is considerably more complicated because&lt;br /&gt;
OS/2 is a multitasking system. Even if the thread that calls the device&lt;br /&gt;
driver with a request has nothing better to do than wait for the operation&lt;br /&gt;
to complete, other threads in the system could make good use of the time.&lt;br /&gt;
Another effect of the OS/2 multitasking architecture is that two or more&lt;br /&gt;
threads can simultaneously call a device driver. To explore this last issue&lt;br /&gt;
fully, we'll digress for a moment and discuss the OS/2 internal execution&lt;br /&gt;
model.&lt;br /&gt;
     By design, OS/2 acts more like a subroutine library than like a&lt;br /&gt;
process. The only dispatchable entities in the system are threads, and all&lt;br /&gt;
threads belong to processes. When a process's thread calls OS/2, that&lt;br /&gt;
thread executes OS/2's code.&lt;br /&gt;
3. The few exceptions to this don't affect the issues discussed here.&lt;br /&gt;
3 It's like walking up to the counter at a&lt;br /&gt;
fast-food restaurant and placing your order. You then slip on an apron, run&lt;br /&gt;
around behind the counter, and prepare your own order. When the food is&lt;br /&gt;
ready, you take off the apron, run back around to the front of the counter,&lt;br /&gt;
and pick up the food. The counter represents the boundary between ring 0&lt;br /&gt;
(kernel mode) and ring 3 (application mode), and the apron represents the&lt;br /&gt;
privileged state necessary to work behind the counter.&lt;br /&gt;
     Naturally, OS/2 is reentrant; at any one time many threads are&lt;br /&gt;
executing inside OS/2, but each is doing work for only one process--the&lt;br /&gt;
process to whom that thread belongs. Behind the counter are several folks&lt;br /&gt;
wearing aprons, but each is working only on his or her own order. This&lt;br /&gt;
approach simplifies the internals of OS/2: Each instance of a section of&lt;br /&gt;
code is doing only one thing for one client. If a section of code must wait&lt;br /&gt;
for something, it simply blocks (analogous to a semaphore wait) as long as&lt;br /&gt;
it has to and resumes when it can. Threads within the kernel that are&lt;br /&gt;
competing for a single resource do so by internal semaphores, and they are&lt;br /&gt;
given access to these semaphores on a priority basis, just as they are when&lt;br /&gt;
executing in application mode.&lt;br /&gt;
     OS/2 makes little distinction between a thread running inside the&lt;br /&gt;
kernel and one running outside, in the application's code itself: The&lt;br /&gt;
process's LDT remains valid, and the thread, while inside the kernel, can&lt;br /&gt;
access any memory location that was accessible to the process in&lt;br /&gt;
application mode, in addition to being able to access restricted ring 0&lt;br /&gt;
memory. The only distinction the scheduler makes between threads inside and&lt;br /&gt;
those outside the kernel is that the scheduler never preempts a thread&lt;br /&gt;
running inside the kernel. This greatly relaxes the rigor with which kernel&lt;br /&gt;
code needs to protect its critical sections: When the CPU is executing&lt;br /&gt;
kernel code, the scheduler performs a context switch only when the CPU&lt;br /&gt;
voluntarily blocks itself. As long as kernel code doesn't block itself,&lt;br /&gt;
wait on a semaphore, or call a subroutine that waits on a semaphore, it&lt;br /&gt;
needn't worry about any other thread entering its critical section.&lt;br /&gt;
4. Although hardware interrupts still occur; any critical section&lt;br /&gt;
modified at interrupt time is still vulnerable.&lt;br /&gt;
4&lt;br /&gt;
     When OS/2 calls a device driver at task time, it does so with the&lt;br /&gt;
thread that was executing OS/2--the thread that belongs to the client&lt;br /&gt;
process and that made the original service call. Thus, the task-time part&lt;br /&gt;
of a device driver is running, at ring 0, in the client's context. The&lt;br /&gt;
client's LDT is active, all the client's addresses are active, and the&lt;br /&gt;
device driver is immune from being preempted by other task-time threads&lt;br /&gt;
(but not by interrupt service) until it blocks via a DevHlp&lt;br /&gt;
function or returns to OS/2.&lt;br /&gt;
     OS/2 device drivers are divided into two general categories: those for&lt;br /&gt;
character mode devices and those for block mode devices. This terminology&lt;br /&gt;
is traditional, but don't take it too literally because character mode&lt;br /&gt;
operations can be done to block mode devices. The actual distinction is&lt;br /&gt;
that character mode device drivers do I/O synchronously; that is, they do&lt;br /&gt;
operations in first in, first out order. Block mode device drivers can be&lt;br /&gt;
asynchronous; they can perform I/O requests in an order different from the&lt;br /&gt;
one in which they received them. A traditional serial character device,&lt;br /&gt;
such as a printer, must not change the order of its requests; doing so&lt;br /&gt;
scrambles the output. A block device, such as a disk, can reverse the order&lt;br /&gt;
of two sector reads without problems.&lt;br /&gt;
     Figure 17-2 shows an algorithm for character mode device drivers.&lt;br /&gt;
5. See the device driver reference manual for more details.&lt;br /&gt;
5&lt;br /&gt;
OS/2 calls the device driver with a request, as shown at the top of the&lt;br /&gt;
figure. If the device driver is busy with another request, the new&lt;br /&gt;
requesting thread should block on a RAM semaphore until the device is&lt;br /&gt;
available. When the device is free, the task-time thread does the requested&lt;br /&gt;
operation. Sometimes the work can be done at task time (such as an IOCTL&lt;br /&gt;
call asking about the number of characters in an input buffer), but more&lt;br /&gt;
frequently the task-time thread initiates the operation, and the work is&lt;br /&gt;
completed at interrupt time. If the task-time thread needs to wait for&lt;br /&gt;
interrupt service, it should block on a RAM semaphore&lt;br /&gt;
6. Located in the device driver data area.&lt;br /&gt;
6 that the interrupt-&lt;br /&gt;
time code will clear. When the last associated device interrupt takes place&lt;br /&gt;
and the operation is complete, the interrupt code releases the RAM&lt;br /&gt;
semaphore. The task-time thread awakens and returns to OS/2 with the status&lt;br /&gt;
bits properly set in the request block.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       OS/2 code            Device driver           Device interrupt&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Issue request to&lt;br /&gt;
device driver. ÄÄÄÄÄÄ� Block until device is&lt;br /&gt;
                       available.&lt;br /&gt;
&lt;br /&gt;
                       Peform request. Block&lt;br /&gt;
                       until interrupts&lt;br /&gt;
                       complete if necessary.  ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ Device interrupt (if any)&lt;br /&gt;
                                               ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ Perform next step in I/O&lt;br /&gt;
                                               ³ operation.&lt;br /&gt;
                                               ³&lt;br /&gt;
                                               ³ When done, use DevHlp&lt;br /&gt;
                                               ³ ProcRun to unblock task&lt;br /&gt;
                                               ³ time thread.&lt;br /&gt;
                                               ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                               ³ End of interrupt&lt;br /&gt;
                       Complete request and    ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                       return to OS/2&lt;br /&gt;
Request now complete.&lt;br /&gt;
Continue.&lt;br /&gt;
&lt;br /&gt;
Figure 17-2.  Simplified character mode device driver model.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Figure 17-3 shows an algorithm for block devices. The general outline&lt;br /&gt;
is the same as that for character devices but more complicated because of&lt;br /&gt;
the asynchronous nature of random-access devices. Because requests can be&lt;br /&gt;
processed in any order, most block device drivers maintain an internal work&lt;br /&gt;
queue to which they add each new request. They usually use a special DevHlp&lt;br /&gt;
function to sort the work queue in sector number order so that disk head&lt;br /&gt;
motion is minimized.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
       OS/2 code             Device driver           Device interrupt&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Issue request to&lt;br /&gt;
device driver. ÄÄÄÄÄÄÄ� Add request to device&lt;br /&gt;
                        request list. Fire up&lt;br /&gt;
                        device if not active.&lt;br /&gt;
&lt;br /&gt;
                        Return to OS/2 with&lt;br /&gt;
                        DONE clear. ÄÄ¿&lt;br /&gt;
           ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
           �                                    ³ Device interrupt&lt;br /&gt;
OS/2 thread(s) block on                         ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
incomplete request when                         ³ Perform next step in&lt;br /&gt;
they can proceed no                             ³ I/O operation.&lt;br /&gt;
further without the                             ³&lt;br /&gt;
data.                                           ³ If request is done&lt;br /&gt;
                                                ³    Pull request from&lt;br /&gt;
                                                ³    list&lt;br /&gt;
Any threads blocked ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
on this request are                             ³    Use DevHlp DevDone&lt;br /&gt;
awakened.                                       ³    to tell OS/2 that&lt;br /&gt;
                                                ³    the I/O is done.&lt;br /&gt;
                                                ³&lt;br /&gt;
                                                ³    If further work on&lt;br /&gt;
                                                ³    queue, start work&lt;br /&gt;
                                                ³    on next item.&lt;br /&gt;
                                                ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
                                                ³ End of interrupt&lt;br /&gt;
Request now complete.                           ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
Continue.&lt;br /&gt;
&lt;br /&gt;
Figure 17-3.  Block mode device driver model.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     The easiest way to understand this figure is to think of a block mode&lt;br /&gt;
device driver as being made up of N threads: one interrupt-time thread does&lt;br /&gt;
the actual work, and all the others are task-time threads that queue the&lt;br /&gt;
work. As each request comes into the driver via a task-time thread, that&lt;br /&gt;
thread simply puts the request on the queue and returns to OS/2. Later, the&lt;br /&gt;
device driver's interrupt service routine calls the DevHlp DevDone function&lt;br /&gt;
to tell OS/2 that the operation is complete. Returning to OS/2 with the&lt;br /&gt;
operation incomplete is permissible because the request block status bits&lt;br /&gt;
show that the operation is incomplete.&lt;br /&gt;
     Sometimes, OS/2 needs to wait for the operation (such as a read from a&lt;br /&gt;
directory); so when the device driver returns with the operation&lt;br /&gt;
incomplete, OS/2 simply waits for it to finish. In other circumstances,&lt;br /&gt;
such as flushing the cache buffers, OS/2 may not wait around for the&lt;br /&gt;
operation to complete. It may go on about its business or even issue a new&lt;br /&gt;
request to the driver, using, of course, a new request block because the&lt;br /&gt;
old one is still in use. This design gives the system a great deal of&lt;br /&gt;
parallelism and thereby improves throughput.&lt;br /&gt;
     I said that a block mode device driver consists of several task-time&lt;br /&gt;
threads and one interrupt-time thread. The term interrupt-time thread is a&lt;br /&gt;
bit misleading, however, because it's not a true thread managed by the&lt;br /&gt;
scheduler but a pseudo thread created by the hardware interrupt mechanism.&lt;br /&gt;
For example, a disk device driver has four requests queued up, and the READ&lt;br /&gt;
operation for the first request is in progress. When it completes, the&lt;br /&gt;
driver's interrupt service routine is entered by the hardware interrupt&lt;br /&gt;
generated by the disk controller. That interrupt-time thread, executing the&lt;br /&gt;
driver's interrupt service routine, checks the status, verifies that all is&lt;br /&gt;
OK, and calls various DevHlp routines to post the request as complete and&lt;br /&gt;
to remove it from the queue. It then notes that requests remain on the&lt;br /&gt;
queue and starts work on the next one, which involves a seek operation. The&lt;br /&gt;
driver's interrupt-time code issues the seek command to the hardware and&lt;br /&gt;
then returns from the interrupt. When the disk stops seeking, another&lt;br /&gt;
interrupt is generated; the interrupt-time code notes the successful seek,&lt;br /&gt;
issues the read or write operation to the controller, and exits.&lt;br /&gt;
     As you can see, the repeated activation of the device driver's&lt;br /&gt;
interrupt service routine is much like a thread, but with two major&lt;br /&gt;
differences. First, every time an interrupt service routine is entered, it&lt;br /&gt;
has a fresh stack. A task-time thread has register contents and a stack&lt;br /&gt;
that are preserved by the system; neither is preserved for an interrupt&lt;br /&gt;
service routine between interrupts. A task-time thread keeps track of what&lt;br /&gt;
it was doing by its CS:IP address, its register contents, and its stack&lt;br /&gt;
contents. An interrupt service routine must keep track of its work by means&lt;br /&gt;
of static values stored in the device driver's data segment. Typically,&lt;br /&gt;
interrupt service routines implement a state machine and maintain the&lt;br /&gt;
current state in the driver's data segment. Second, a true thread remains&lt;br /&gt;
in existence until explicitly terminated; an interrupt service thread is an&lt;br /&gt;
illusion of a thread that is maintained by repeated interrupts. If any one&lt;br /&gt;
execution of the interrupt service routine fails to give the hardware a&lt;br /&gt;
command that will generate another interrupt, the interrupt pseudo thread&lt;br /&gt;
will no longer exist after the interrupt service routine returns.&lt;br /&gt;
     The block mode driver algorithm description left out a detail. If the&lt;br /&gt;
disk is idle when a new request comes in, the request is put on the queue,&lt;br /&gt;
but there is no interrupt-time pseudo thread to service the request. Thus,&lt;br /&gt;
both the task-time and interrupt-time parts of a device driver must be able&lt;br /&gt;
to initiate an operation. The recommended approach is to use a software&lt;br /&gt;
state machine to control the hardware and to ensure that the state machine,&lt;br /&gt;
at least the start operation part of it, is callable at both task and&lt;br /&gt;
interrupt time. The algorithm above is then modified so that after the&lt;br /&gt;
task-time part of a block device driver puts its request on the driver's&lt;br /&gt;
internal queue it verifies that the device (or state machine or interrupt&lt;br /&gt;
pseudo thread) is active. If the device has been idle, the task-time thread&lt;br /&gt;
in the device driver initiates the operation by calling the initial state&lt;br /&gt;
of the state machine; it then returns to OS/2. This primes the pump;&lt;br /&gt;
the interrupt pseudo thread now continues to run until the request queue is&lt;br /&gt;
empty.&lt;br /&gt;
     Figure 17-4 shows an overview of the OS/2 device driver architecture.&lt;br /&gt;
Each device driver consists of a task-time part, an interrupt-time part (if&lt;br /&gt;
the device generates interrupts), and the start-operation code that is&lt;br /&gt;
executed in either mode. The driver's data area typically contains state&lt;br /&gt;
information, flags, and semaphores to handle communication between the&lt;br /&gt;
task-time part and the interrupt. Figure 17-4 also shows that the task-time&lt;br /&gt;
part of a device driver can have multiple instances. It can be called by&lt;br /&gt;
several threads at the same time, just as a shared dynlink library routine&lt;br /&gt;
might be. Unlike a dynlink library, a device driver has no instance data&lt;br /&gt;
segment; the device driver's data segment is a global data segment,&lt;br /&gt;
accessible to all execution instances of the device driver's task-time&lt;br /&gt;
component. Just as a dynlink package uses semaphores to protect critical&lt;br /&gt;
data areas in its global data segment, a device driver uses semaphores to&lt;br /&gt;
protect the critical data values in its data segment. Unlike dynlink&lt;br /&gt;
routines, the device driver has an additional special thread--the interrupt&lt;br /&gt;
service thread. A device driver can't protect critical sections that are&lt;br /&gt;
accessed at interrupt time by using semaphores because an interrupt service&lt;br /&gt;
thread cannot block. It must complete the interrupt service and exit--&lt;br /&gt;
quickly, at that. When you write device drivers, you must minimize the&lt;br /&gt;
critical sections that are entered by the interrupt service thread and&lt;br /&gt;
protect them via the CLI/STI instruction sequence.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
         ³              &amp;quot;D&amp;quot;³&lt;br /&gt;
      ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ³                ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
      ³              &amp;quot;C&amp;quot;³  ³                ³   Device driver   ³&lt;br /&gt;
   ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ÃÄÄÙ                ³ interrupt service ³&lt;br /&gt;
   ³              &amp;quot;B&amp;quot;³  ³                   ÀÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÙ&lt;br /&gt;
ÚÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ÃÄÄÙ                     ³           ³&lt;br /&gt;
³  Instance &amp;quot;A&amp;quot;   ³  ³                        ³           ³&lt;br /&gt;
³  device driver  ÃÄÄÙ                ÚÄÄÄÄÄÄÄ�ÄÄÄ¿       ³&lt;br /&gt;
³  task-time code ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³ Start I/O ³       ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ                   ³   code    ³       ³&lt;br /&gt;
         ³                            ÀÄÄÄÄÄÂÄÄÄÄÄÙ       ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                                  ³             ³&lt;br /&gt;
         ³                         ÚÄÄÄÄÄÄÄÄ�ÄÄÄÄÄÄ¿      ³&lt;br /&gt;
         ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ�³ Device driver ³�ÄÄÄÄÄÙ&lt;br /&gt;
                                   ³     data      ³&lt;br /&gt;
                                   ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 17-4.  Device driver code structure.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.3  Device Management&lt;br /&gt;
Device drivers do more than talk to the device; they also manage it for the&lt;br /&gt;
system. Device drivers are called each time a process opens or closes a&lt;br /&gt;
device; device drivers determine whether a device can be used by more than&lt;br /&gt;
one process simultaneously. Likewise, device drivers receive device monitor&lt;br /&gt;
requests from applications via the IOCTL interface and, when appropriate,&lt;br /&gt;
call OS/2 via the DevHlp interface to perform the bulk of the monitor work.&lt;br /&gt;
Finally, device drivers can grant processes access to the device's I/O&lt;br /&gt;
ports, to the device's mapped memory, and/or to special control areas in&lt;br /&gt;
the device driver's data area itself. Once again, processes ask for these&lt;br /&gt;
features via IOCTL; the device driver grants the requests via a DevHlp&lt;br /&gt;
dialog with OS/2. Some device drivers are degenerate; they don't actually&lt;br /&gt;
transfer data but exist solely to manage these other tasks. The screen&lt;br /&gt;
device driver is an example. Screen data is always written directly to the&lt;br /&gt;
display buffer by VIO, the application, or the presentation manager. The&lt;br /&gt;
screen device driver exists to grant direct access, manage screen groups,&lt;br /&gt;
and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.1.4  Dual Mode&lt;br /&gt;
The last key architectural feature of device drivers is that they are&lt;br /&gt;
written in dual mode: The driver code, both task time and interrupt time,&lt;br /&gt;
must be able to execute in protected mode and real mode. The process of&lt;br /&gt;
mode switching between protected mode and real mode is quite slow--about&lt;br /&gt;
800 microseconds. If we decreed that all device drivers run only in&lt;br /&gt;
protected mode and that service interrupts run only in protected mode, a&lt;br /&gt;
disk request from a real mode program might require six or more mode&lt;br /&gt;
switches--one for the request, and five for the interrupts--for a penalty&lt;br /&gt;
of almost 5 milliseconds. Consequently, device drivers must run in whatever&lt;br /&gt;
mode the CPU is in when the request comes along or the interrupt arrives.&lt;br /&gt;
     At first glance, this seems easy enough: As long as the device driver&lt;br /&gt;
refrains from computing its own segment selectors, it can execute in either&lt;br /&gt;
mode. The catch is that OS/2 may switch between modes at every call and/or&lt;br /&gt;
interrupt, and the addresses of code and data items are different in each&lt;br /&gt;
mode. A device driver might be called in protected mode with an address in&lt;br /&gt;
the client process's address space. When the &amp;quot;data ready&amp;quot; interrupt&lt;br /&gt;
arrives, however, the CPU may be running in real mode, and that client's&lt;br /&gt;
address is no longer valid--for two reasons. One, the segment selector part&lt;br /&gt;
of a memory address has a different meaning in real mode than it does in&lt;br /&gt;
protected mode; and, two, the client's selector was in the LDT, and the LDT&lt;br /&gt;
is invalid at interrupt time.&lt;br /&gt;
7. See the device driver reference manual for more details.&lt;br /&gt;
7 OS/2 helps device drivers deal with&lt;br /&gt;
addressing in a dual mode environment in three ways:&lt;br /&gt;
&lt;br /&gt;
     1.  Some addresses are the same in both modes and in either protected&lt;br /&gt;
         mode or real mode. The DevHlp entry point, the global infoseg&lt;br /&gt;
         address, the request packet address, and any addresses returned&lt;br /&gt;
         via the DevHlp GetDosVar function are valid at all times and in&lt;br /&gt;
         both modes.&lt;br /&gt;
&lt;br /&gt;
     2.  Although the segment selector value for the device driver's code&lt;br /&gt;
         and data segments is different in each mode, OS/2 loads the proper&lt;br /&gt;
         values into CS and DS before it calls the device driver's task-&lt;br /&gt;
         time or interrupt-time entry points. As long as a device driver is&lt;br /&gt;
         careful not to &amp;quot;remember&amp;quot; and reuse these values, it won't notice&lt;br /&gt;
         that they (possibly) change at every call.&lt;br /&gt;
&lt;br /&gt;
     3.  OS/2 provides a variety of DevHlp functions that allow a device&lt;br /&gt;
         driver to convert a selector:offset pair into a physical address&lt;br /&gt;
         and then later convert this physical address back into a&lt;br /&gt;
         selector:offset pair that is valid at that particular time. This&lt;br /&gt;
         allows device drivers to convert addresses that are outside their&lt;br /&gt;
         own segments into physical addresses and then, upon each task-time&lt;br /&gt;
         or interrupt-time call to the driver, convert that physical&lt;br /&gt;
         address back into one that is usable in the current mode. This&lt;br /&gt;
         avoids the problem of recording a selector:offset pair in protect&lt;br /&gt;
         mode and then trying to use it as a segment:offset pair in real&lt;br /&gt;
         mode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2  Hard Errors&lt;br /&gt;
&lt;br /&gt;
Sometimes the system encounters an error that it can neither ignore nor&lt;br /&gt;
correct but which the user can correct. A classic example is the user&lt;br /&gt;
leaving ajar the door to the floppy drive; the system can do nothing to&lt;br /&gt;
access that floppy disk until someone closes the door. Such an error is&lt;br /&gt;
called a hard error. The term originated to describe an error that won't go&lt;br /&gt;
away when the operation is retried, but it also aptly describes the effort&lt;br /&gt;
involved in the design of OS/2 to deal with such errors.&lt;br /&gt;
     The manner in which MS-DOS handles hard errors is straightforward. In&lt;br /&gt;
our drive door example, MS-DOS discovers the problem when it is deep inside&lt;br /&gt;
the bowels of the system, communicating with the disk driver. The driver&lt;br /&gt;
reports the problem, and MS-DOS displays some text on the screen--the&lt;br /&gt;
infamous &amp;quot;Abort, Retry, Ignore?&amp;quot; message. Typically, the user fixes the&lt;br /&gt;
problem and replies; MS-DOS then takes the action specified by the user,&lt;br /&gt;
finishes its work, and returns to the application. Often, applications&lt;br /&gt;
didn't want the system to handle hard errors automatically. Perhaps they&lt;br /&gt;
were concerned about data integrity and wanted to be aware of a disk&lt;br /&gt;
writing problem, or they wanted to prevent the user from specifying&lt;br /&gt;
&amp;quot;Ignore,&amp;quot;&lt;br /&gt;
8. This response is classic. Sophisticated users understand the likely&lt;br /&gt;
consequences of such a reply, but most users would interpret &amp;quot;Ignore&amp;quot;&lt;br /&gt;
as &amp;quot;Make the problem go away&amp;quot;--an apparently ideal solution!&lt;br /&gt;
8 or they didn't want MS-DOS to write over their screen display&lt;br /&gt;
without their knowing. To handle these situations, MS-DOS lets applications&lt;br /&gt;
store the address of a hard error handler in the INT 24 vector; if a&lt;br /&gt;
handler is present, MS-DOS calls it instead of its own handler.&lt;br /&gt;
     The system is in an unusual state while processing an MS-DOS hard&lt;br /&gt;
error. The application originally calls MS-DOS via the INT 21 vector. MS-&lt;br /&gt;
DOS then calls several levels deep within itself, whereupon an internal MS-&lt;br /&gt;
DOS routine calls the hard error handler back in the application. Because&lt;br /&gt;
MS-DOS is not generally reentrant, the application cannot recall MS-DOS via&lt;br /&gt;
INT 21 at this point; doing so would mean that it has called MS-DOS twice&lt;br /&gt;
at the same time. The application probably needs to do screen and keyboard&lt;br /&gt;
I/O when handling the hard error, so MS-DOS was made partially reentrant.&lt;br /&gt;
The original call involves disk I/O, so MS-DOS can be reentered via a&lt;br /&gt;
screen/keyboard I/O call without problem.&lt;br /&gt;
     Several problems prevented us from adopting a similar scheme for OS/2.&lt;br /&gt;
First, unlike the single-tasking MS-DOS, OS/2 cannot suspend operations&lt;br /&gt;
while the operating system calls an application--a call that might not&lt;br /&gt;
return for a long time. Second, major technical and security problems are&lt;br /&gt;
involved with calling from ring 0 (the privileged kernel mode) to ring 3&lt;br /&gt;
(the application mode). Also, in the MS-DOS environment, deciding which&lt;br /&gt;
process was responsible for the operation that triggered the hard error is&lt;br /&gt;
easy: Only one application is running. OS/2 may have a hard time&lt;br /&gt;
determining which process to alert because more than one process may have&lt;br /&gt;
caused a disk FAT sector or a disk directory to be edited. The improved&lt;br /&gt;
buffering techniques employed by OS/2 may cause a hard error to occur at a&lt;br /&gt;
time when no process is doing any I/O. Finally, even if we solve all these&lt;br /&gt;
problems, the application that triggers the hard error may be running in a&lt;br /&gt;
background screen group and be unable to display a message or use the&lt;br /&gt;
keyboard. Even if the application is in the foreground screen group, it&lt;br /&gt;
can't use the screen and keyboard if it's not the process currently&lt;br /&gt;
controlling them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2.1  The Hard Error Daemon&lt;br /&gt;
This last problem yields a clue to the solution. OS/2 supports multiple&lt;br /&gt;
screen groups, and its screen group mechanism manages multiple simultaneous&lt;br /&gt;
use of the screen and the keyboard, keeping the current users--one in each&lt;br /&gt;
screen group--isolated from one another. Clearly, we need to use screen&lt;br /&gt;
groups to allow a hard error dialog to be completed with the user without&lt;br /&gt;
interfering with the current foreground application. Doing so solves the&lt;br /&gt;
problem of writing on another application's screen image and therefore&lt;br /&gt;
removes most of the need for notifying an application that a hard error has&lt;br /&gt;
occurred.&lt;br /&gt;
     Specifically, OS/2 always has running a process called the hard error&lt;br /&gt;
daemon. When a hard error occurs, OS/2 doesn't attempt to figure out which&lt;br /&gt;
process caused it; instead, it notifies the hard error daemon. The hard&lt;br /&gt;
error daemon performs a special form of screen group switch to the reserved&lt;br /&gt;
hard error screen group and then displays its message and reads its input.&lt;br /&gt;
Because the hard error daemon is the only process in this screen group,&lt;br /&gt;
screen and keyboard usage do not conflict. The previous foreground process&lt;br /&gt;
is now temporarily in the background; the screen group mechanism keeps it&lt;br /&gt;
at bay.&lt;br /&gt;
     Meanwhile, the process thread that encountered the hard error in the&lt;br /&gt;
kernel is blocked there, waiting for the hard error daemon to get a&lt;br /&gt;
response from the user. The thread that handles the hard error is never the&lt;br /&gt;
thread that caused the hard error, and the kernel is already fully&lt;br /&gt;
reentrant for different threads; so the hard error daemon thread is free to&lt;br /&gt;
call OS/2 at will. When the user corrects the problem and responds to the&lt;br /&gt;
hard error daemon, the hard error daemon sends the response back to the&lt;br /&gt;
kernel, which allows the thread that encountered the error to take the&lt;br /&gt;
specified action. That thread either retries the operation or produces an&lt;br /&gt;
error code; the hard error daemon returns the system to the original&lt;br /&gt;
screen group. The screen group code then does its usual trick of&lt;br /&gt;
restoring the screen image to its previous state. Figure 17-5 illustrates&lt;br /&gt;
the hard error handling sequence. A process thread encounters a hard error&lt;br /&gt;
while in the OS/2 kernel. The thread blocks at that point while the hard&lt;br /&gt;
error daemon's previously captured thread is released. The hard error&lt;br /&gt;
daemon performs a special modified screen switch at (1), displays its&lt;br /&gt;
message, gets the user's response, restores the application screen group at&lt;br /&gt;
(2), and reenters the OS/2 kernel. The response code is then passed to the&lt;br /&gt;
blocked application thread, which then resumes execution.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
             DosCall&lt;br /&gt;
   Process  ÄÄÄÄ¿                                        ÚÄÄÄÄÄÄÄÄ&lt;br /&gt;
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
³  OS/2 kernel  ³                                        ³  Return   ³&lt;br /&gt;
³               ³ Hard                                   ³  to       ³&lt;br /&gt;
³               ³ error                                  ³  process  ³&lt;br /&gt;
³               ÀÄÄÄÄÄÄ´                      ÃÄÄÄ&amp;gt;Ä&amp;gt;ÄÄÄÄÙ           ³&lt;br /&gt;
³                                                &amp;lt; &amp;lt;                 ³&lt;br /&gt;
³                      ÃÄ¿                  ÚÄ´                      ³&lt;br /&gt;
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                         �                  �&lt;br /&gt;
                 returns ³                  ³ calls&lt;br /&gt;
                         ³                  ³&lt;br /&gt;
   Hard error daemon     ³ Display message  ³&lt;br /&gt;
                         ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
                      (1)    Get response    (2)&lt;br /&gt;
 &lt;br /&gt;
                                    TimeÄÄÄÄÄÄ�&lt;br /&gt;
&lt;br /&gt;
Figure 17-5.  Hard error handling.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     Although the most common cause of hard errors is a disk problem for&lt;br /&gt;
example, an open drive door or a medium error--other events that require&lt;br /&gt;
user intervention or user notification use the hard error mechanism. For&lt;br /&gt;
example, the volume management package (see 15.2 Media Volume Management)&lt;br /&gt;
uses the hard error mechanism to display its &amp;quot;Insert volume &amp;lt;name&amp;gt;&amp;quot;&lt;br /&gt;
messages. As I mentioned earlier, MS-DOS applications running in the&lt;br /&gt;
compatibility box can encounter problems, such as locked files, that they&lt;br /&gt;
can't understand. Rather than have these applications fail mysteriously,&lt;br /&gt;
OS/2 uses the hard error daemon mechanism to inform the user of the cause&lt;br /&gt;
of the real mode application's difficulties. Although the application&lt;br /&gt;
running in the compatibility box sees an operating system that acts like&lt;br /&gt;
MS-DOS, the operating system is actually OS/2. Because of this, hard errors&lt;br /&gt;
encountered by a real mode process are handled by an amalgam of the MS-DOS&lt;br /&gt;
INT 24 mechanism and the OS/2 hard error daemon. See Chapter 19, The 3X&lt;br /&gt;
Box.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
17.2.2  Application Hard Error Handling&lt;br /&gt;
In some cases an application doesn't want the system to handle its hard&lt;br /&gt;
errors. For example, an application designed for unattended or remote&lt;br /&gt;
operation, such as a network server, may want to pass notification of hard&lt;br /&gt;
errors to a remote correspondent rather than hanging up forever with a&lt;br /&gt;
message on a screen that might not be read for hours. Another example is a&lt;br /&gt;
database program concerned about the integrity of its master file; it may&lt;br /&gt;
want to know about hard errors so that it can take some special action or&lt;br /&gt;
perhaps use an alternative master file on another device. OS/2 allows a&lt;br /&gt;
process to disable automatic hard error handling on a per file basis. Our&lt;br /&gt;
network example will want to disable hard error pop-ups for anything the&lt;br /&gt;
process does; our database example may want to disable hard error pop-ups&lt;br /&gt;
only for its master file, keeping their convenience for any other files&lt;br /&gt;
that it might access. When a hard error occurs on behalf of a process or a&lt;br /&gt;
handle that has hard error pop-ups disabled, OS/2 assumes that a FAIL&lt;br /&gt;
response was entered to a hypothetical hard error pop-up and returns to the&lt;br /&gt;
application with a special error code. The application must analyze the&lt;br /&gt;
code and take the necessary actions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
18  I/O Privilege Mechanism and Debugging/Ptrace&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
The earlier chapters of this book focused on the &amp;quot;captains and kings&amp;quot; of&lt;br /&gt;
the operating system world, the major architectural features. But like any&lt;br /&gt;
real world operating system, OS/2 contains a variety of miscellaneous&lt;br /&gt;
facilities that have to be there to get the work done. Although these&lt;br /&gt;
facilities may not be major elements in some architectural grand scheme,&lt;br /&gt;
they still have to obey the principles of the design religion. Two of them&lt;br /&gt;
are the I/O privilege mechanism and the debugging facility.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
18.1  I/O Privilege Mechanism&lt;br /&gt;
&lt;br /&gt;
Earlier I discussed the need for a mechanism that allows applications high-&lt;br /&gt;
speed direct access to devices. But the mechanism must control access in&lt;br /&gt;
such a way that the system's stability isn't jeopardized and in such a way&lt;br /&gt;
that applications don't fight over device control. OS/2 meets this&lt;br /&gt;
requirement with its I/O privilege mechanism. This facility allows a&lt;br /&gt;
process to ask a device driver for direct access to the device's I/O&lt;br /&gt;
ports and any dedicated or mapped memory locations it has. The I/O&lt;br /&gt;
privilege mechanism can be used directly by an application, which&lt;br /&gt;
necessarily makes it device dependent, or indirectly by a dynlink package.&lt;br /&gt;
The dynlink package can act as a kind of device driver; a new version can&lt;br /&gt;
be shipped with new hardware to maintain application compatibility. This&lt;br /&gt;
pseudo device driver is normally much faster than a true device driver&lt;br /&gt;
because of the customized procedural interface; not entering ring 0 and the&lt;br /&gt;
OS/2 kernel code saves much time.&lt;br /&gt;
     Unfortunately, this isn't a free lunch. Dynlink pseudo device drivers&lt;br /&gt;
can do everything that true device drivers can except handle interrupts.&lt;br /&gt;
Because hardware interrupts must be handled at ring 0, the handler must be&lt;br /&gt;
part of a true device driver. Frequently, a compromise is in order: Both a&lt;br /&gt;
dynlink package and a true device driver are provided. The true device&lt;br /&gt;
driver handles the interrupts, and the dynlink package does the rest of the&lt;br /&gt;
work. The two typically communicate via shared memory and/or private&lt;br /&gt;
IOCTLs. An example of such a compromise is the system KBD dynlink package.&lt;br /&gt;
The system VIO package doesn't need a device driver to handle interrupts&lt;br /&gt;
because the display device doesn't generate any.&lt;br /&gt;
     The two components in the OS/2 I/O access model are access to the&lt;br /&gt;
device's memory and access to its I/O ports. Granting and controlling&lt;br /&gt;
access to a device's mapped memory is easy because the 80286 protect mode&lt;br /&gt;
supports powerful memory management facilities. First, a process asks the&lt;br /&gt;
device driver for access to the device's memory, for example, to the memory&lt;br /&gt;
buffer of a CGA board. Typically, a dynlink package, rather than an&lt;br /&gt;
application, does this via the DosDevIOCtl call. If the device driver&lt;br /&gt;
approves the request, it asks OS/2 via the DevHlp interface to set up an&lt;br /&gt;
LDT memory descriptor to the proper physical memory locations. OS/2 returns&lt;br /&gt;
the resultant selector to the device driver, which returns it to the&lt;br /&gt;
calling process. This technique isn't limited to memory-mapped device&lt;br /&gt;
memory; device drivers can use it to allow their companion dynlink packages&lt;br /&gt;
direct access to a piece of the device driver's data segment. In this way,&lt;br /&gt;
a combination dynlink/device driver device interface can optimize&lt;br /&gt;
communication between the dynlink package and the device driver.&lt;br /&gt;
     Providing I/O port access to a process is more difficult because it is&lt;br /&gt;
supported more modestly by the 80286 processor. The 80286 uses its ring&lt;br /&gt;
protection mechanism to control I/O access; the system can grant code&lt;br /&gt;
running at a certain ring privilege access to all I/O ports, but it can't&lt;br /&gt;
grant access to only some I/O ports. It's too dangerous to grant an&lt;br /&gt;
application access to all I/O ports simply because it uses VIO and VIO&lt;br /&gt;
needs direct port access for the display adapter. This solution would mean&lt;br /&gt;
that OS/2's I/O space is effectively unprotected because almost all&lt;br /&gt;
programs use VIO or the presentation manager directly or indirectly.&lt;br /&gt;
     Instead, OS/2 was designed to allow, upon request from the device&lt;br /&gt;
driver, any code segments marked&lt;br /&gt;
1. This is done via a special command to the linker.&lt;br /&gt;
1 to execute at ring 2 to have I/O access.&lt;br /&gt;
The bad news is that access to all I/O ports must be granted&lt;br /&gt;
indiscriminately, but the good news is that the system is vulnerable to&lt;br /&gt;
program bugs only when those ring 2 segments are being executed. The&lt;br /&gt;
capabilities of ring 2 code, as it's called, are restricted: Ring 2 code&lt;br /&gt;
cannot issue dynlink calls to the system. This is partly a result of ring&lt;br /&gt;
architecture (supporting ring 2 system calls would require significant&lt;br /&gt;
additional overhead) and partly to discourage lazy programmers from&lt;br /&gt;
flagging their entire process as ring 2 to avoid sequestering their I/O&lt;br /&gt;
routines.&lt;br /&gt;
     As I said, in OS/2 version 1.0 the ring mechanism can restrict I/O&lt;br /&gt;
access only to a limited degree. Any malicious program and some buggy&lt;br /&gt;
programs can still damage system stability by manipulating the system's&lt;br /&gt;
peripherals. Furthermore, a real mode application can issue any I/O&lt;br /&gt;
instruction at any time. A future release of OS/2 that runs only on the&lt;br /&gt;
80386 processor will solve these problems. The 80386 hardware is&lt;br /&gt;
specifically designed to allow processes access to some I/O ports but not&lt;br /&gt;
to others through a bit map the system maintains. This map, which of course&lt;br /&gt;
the application cannot directly change, tells the 80386 which port&lt;br /&gt;
addresses may be accessed and which must be refused. This map applies&lt;br /&gt;
equally to protect mode and real mode applications.&lt;br /&gt;
2. Actually, to &amp;quot;virtual real mode&amp;quot; applications. This is&lt;br /&gt;
functionally the same as real mode on earlier processors.&lt;br /&gt;
2 OS/2 will use the&lt;br /&gt;
port addresses supplied by the device driver to allow access only to the&lt;br /&gt;
I/O ports associated with the device(s) to which the process has been&lt;br /&gt;
granted access. This release will not support application code segments&lt;br /&gt;
running at ring 2; any segments so marked will be loaded and run at ring 3.&lt;br /&gt;
The change will be invisible to all applications that use only the&lt;br /&gt;
proper I/O ports. Applications that request access to one device and then&lt;br /&gt;
use their I/O permissions to program another device will fail.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
18.2  Debugging/Ptrace&lt;br /&gt;
&lt;br /&gt;
Because OS/2 goes to a great deal of effort to keep one application from&lt;br /&gt;
interfering with another, special facilities were built to allow debugging&lt;br /&gt;
programs to manipulate and examine a debuggee (the process being debugged).&lt;br /&gt;
Because a debugger is available for OS/2 and writing your own is laborious,&lt;br /&gt;
we expect few programmers to write debuggers. This discussion is included,&lt;br /&gt;
nevertheless, because it further illuminates the OS/2 architectural&lt;br /&gt;
approach.&lt;br /&gt;
     The first concern of a debugger is that it be able to read and write&lt;br /&gt;
the debuggee's code and data segments as well as intercept traps, signals,&lt;br /&gt;
breakpoints, and the like. All these capabilities are strictly in the&lt;br /&gt;
domain of OS/2, so OS/2 must &amp;quot;export&amp;quot; them to the debugger program. A&lt;br /&gt;
second concern is system security: Obviously, the debug interface provides&lt;br /&gt;
a golden opportunity for &amp;quot;cracker&amp;quot; programs to manipulate any other&lt;br /&gt;
program, thereby circumventing passwords, encryption, or any other&lt;br /&gt;
protection scheme. OS/2 prevents this by requiring that the debuggee&lt;br /&gt;
process be flagged as a debug target when it is initially executed; a&lt;br /&gt;
debugger can't latch onto an already-running process. Furthermore, when&lt;br /&gt;
secure versions of OS/2 are available, processes executed under control of&lt;br /&gt;
a debugger will be shorn of any permissions they might have that are in&lt;br /&gt;
excess of those owned by the debugger.&lt;br /&gt;
     Before we examine the debugging interface, we should digress for a&lt;br /&gt;
moment and discuss the OS/2 approach to forcing actions upon threads and&lt;br /&gt;
processes. Earlier I described the process of kernel execution. I mentioned&lt;br /&gt;
that when a process thread makes a kernel request that thread itself enters&lt;br /&gt;
kernel mode and services its own request. This arrangement simplified the&lt;br /&gt;
design of the kernel because a function is coded to perform one action for&lt;br /&gt;
one client in a serial, synchronous fashion. Furthermore, nothing is ever&lt;br /&gt;
forced on a thread that is in kernel mode; any action taken on a thread in&lt;br /&gt;
kernel mode is taken by that thread itself. For example, if a process is to&lt;br /&gt;
be killed and one of its threads is in kernel mode, OS/2 doesn't terminate&lt;br /&gt;
that thread; it sets a flag that says, &amp;quot;Please kill yourself at your&lt;br /&gt;
earliest convenience.&amp;quot; Consequently, OS/2 doesn't need special code to&lt;br /&gt;
enumerate and release any internal flags or resources that a killed kernel&lt;br /&gt;
mode thread might leave orphaned, and in general no thread need&lt;br /&gt;
&amp;quot;understand&amp;quot; the state of any other. The thread to be killed cleans itself&lt;br /&gt;
up, releasing resources, flags, and whatever before it obligingly commits&lt;br /&gt;
suicide.&lt;br /&gt;
     But when is the thread's &amp;quot;earliest convenience&amp;quot;? Thread termination is&lt;br /&gt;
a forced event, and all threads check for any pending forced events&lt;br /&gt;
immediately before they leave kernel mode and reenter application mode.&lt;br /&gt;
This transition takes place frequently: not only when a system call returns&lt;br /&gt;
to the calling application, but also each time a context switch takes&lt;br /&gt;
place.&lt;br /&gt;
     Although it may appear that forced events might languish unprocessed,&lt;br /&gt;
they are serviced rapidly. For example, when a process issues a DosKill&lt;br /&gt;
function on its child process, each thread in the child process is marked&lt;br /&gt;
&amp;quot;kill yourself.&amp;quot; Because the parent process had the CPU, obviously, when it&lt;br /&gt;
issued the DosKill, each of the child's threads is in kernel mode, either&lt;br /&gt;
because the thread is working on a system call or because it was&lt;br /&gt;
artificially placed in kernel mode when the scheduler preempted it. Before&lt;br /&gt;
any of those now-marked threads can execute even a single instruction of&lt;br /&gt;
the child application's code, they must go through OS/2's dispatch routine.&lt;br /&gt;
The &amp;quot;kill yourself&amp;quot; flag is noted, and the thread terminates itself instead&lt;br /&gt;
of returning to application mode. As you can see, the final effect of this&lt;br /&gt;
approach is far from slow: The DosKill takes effect immediately--not one&lt;br /&gt;
more instruction of the child process is executed.&lt;br /&gt;
3. Excepting any SIGKILL handlers and&lt;br /&gt;
DosExitList handlers, of course.&lt;br /&gt;
3 The only significant&lt;br /&gt;
delay in recognizing a forced event occurs when a system call takes a long&lt;br /&gt;
time to process. OS/2 is not very CPU bound, so any call that takes a &amp;quot;long&lt;br /&gt;
time&amp;quot; (1 second or more) must be blocked for most of that time.&lt;br /&gt;
     When a kernel thread issues a block call for an event that might take&lt;br /&gt;
a long time--such as waiting for a keystroke or waiting for a semaphore to&lt;br /&gt;
clear--it uses a special form of block called an interruptible block. When&lt;br /&gt;
OS/2 posts a force flag against a thread, it checks to see if that thread&lt;br /&gt;
is blocking interruptibly. If it is, that thread is released from its block&lt;br /&gt;
with a special code that says, &amp;quot;You were awakened not because the event has&lt;br /&gt;
come to pass but because a forced event was posted.&amp;quot; That thread must then&lt;br /&gt;
finish the system call quickly (generally by declaring an error) so that&lt;br /&gt;
the thread can go through the dispatch routine and recognize the force&lt;br /&gt;
flag. I described this mechanism in Chapter 12 when I talked about another&lt;br /&gt;
kind of forced event--the OS/2 signal mechanism. An incoming signal is a&lt;br /&gt;
forced event for a process's thread 1; it therefore receives the same&lt;br /&gt;
timely response and has the same effect of aborting a slow system call.&lt;br /&gt;
     I've gone through this long discussion of forced events and how&lt;br /&gt;
they're processed because the internal debugging facility is based on one&lt;br /&gt;
giant special forced event. When a process is placed in debug state, a&lt;br /&gt;
trace force flag is permanently set for the initial thread of that process&lt;br /&gt;
and for any other threads it creates. When any of those threads are in&lt;br /&gt;
kernel mode--and they enter kernel mode whenever anything of interest takes&lt;br /&gt;
place--they execute the debuggee half of the OS/2 trace code. The debugger&lt;br /&gt;
half is executed by a debugger thread that issues special DosPtrace calls;&lt;br /&gt;
the two halves of the package communicate through a shared memory area&lt;br /&gt;
built into OS/2.&lt;br /&gt;
     When the debuggee encounters a special event (for example, a Ctrl-C&lt;br /&gt;
signal or a GP fault), the trace force event takes precedence over any&lt;br /&gt;
other, and the debuggee's thread executes the debuggee half of the&lt;br /&gt;
DosPtrace code. This code writes a record describing the event into a&lt;br /&gt;
communications buffer, wakes up the debugger thread, which is typically&lt;br /&gt;
blocked in the debugger's part of the DosPtrace code, and blocks, awaiting&lt;br /&gt;
a reply. The debugger's thread wakes up and returns to the debugger with&lt;br /&gt;
the event information. When the debugger recalls DosPtrace with a command,&lt;br /&gt;
the command is written into the communications area, and the debuggee is&lt;br /&gt;
awakened to read and obey. The command might be &amp;quot;Resume normal execution,&amp;quot;&lt;br /&gt;
&amp;quot;Process the event as you normally would,&amp;quot; or &amp;quot;Give me the contents of&lt;br /&gt;
these locations in your address space,&amp;quot; whereupon the debuggee thread&lt;br /&gt;
replies and remains in the DosPtrace handler.&lt;br /&gt;
     This approach is simple to implement, does the job well, and takes&lt;br /&gt;
advantage of existing OS/2 features. For example, no special code is needed&lt;br /&gt;
to allow the debugger access to the debuggee's address space because the&lt;br /&gt;
debuggee itself, unwittingly in the DosPtrace code, reads and writes its&lt;br /&gt;
own address space. Credit goes to the UNIX ptrace facility, upon which this&lt;br /&gt;
facility was closely modeled.&lt;br /&gt;
     Finally, here are a few incidental facts that the readers of this&lt;br /&gt;
book, being likely users of debugging facilities, should know. OS/2&lt;br /&gt;
maintains a linkage between the debugger process and the debuggee process.&lt;br /&gt;
When the debugger process terminates, the debuggee process also terminates&lt;br /&gt;
if it has not already done so. The debuggee program need not be a direct&lt;br /&gt;
child of the debugger; when the debugger process makes its initial&lt;br /&gt;
DosPtrace call, OS/2 connects it to the last process that was executed with&lt;br /&gt;
the special tracing option. If a process is executed with the tracing&lt;br /&gt;
option but no debugger process subsequently issues a DosPtrace function,&lt;br /&gt;
the jilted debuggee process is terminated in about two minutes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
19  The 3x Box&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
It's of critical importance that OS/2 do a good job of running existing MS-&lt;br /&gt;
DOS applications, but as we've discussed, this is a difficult task. To&lt;br /&gt;
offer the official MS-DOS interfaces under OS/2 and therefore claim upward&lt;br /&gt;
compatibility would be easy; unfortunately, few popular applications would&lt;br /&gt;
run successfully in such an environment. Most sophisticated applications&lt;br /&gt;
take direct control of the machine environment and use MS-DOS for tasks the&lt;br /&gt;
application doesn't want to bother with, such as file I/O, keyboard&lt;br /&gt;
buffering, and so forth. If we're to run existing applications&lt;br /&gt;
successfully, we must provide a close facsimile to a real mode PC running&lt;br /&gt;
MS-DOS in all respects, not just the INT 21 program interface.&lt;br /&gt;
     OS/2 provides such a highly compatible environment, called the real&lt;br /&gt;
mode screen group, the compatibility box, or simply the 3x box. The 3x box&lt;br /&gt;
is an environment that emulates an 8086-based PC running MS-DOS version&lt;br /&gt;
3.3.&lt;br /&gt;
1. For OS/2 version 1.0, the 3x box is compatible with MS-DOS&lt;br /&gt;
version 3.3.&lt;br /&gt;
1 MS-DOS programs execute in real mode, and because emulating real&lt;br /&gt;
mode from within protected mode is prohibitively slow, OS/2 physically&lt;br /&gt;
switches into real mode to execute MS-DOS applications. Because MS-DOS&lt;br /&gt;
programs are well aware of the MS-DOS memory layout, this layout is&lt;br /&gt;
replicated for the OS/2 3x box. The first N bytes (typically 640 KB) are&lt;br /&gt;
reserved for the exclusive use of the low-memory parts of OS/2 and the 3x&lt;br /&gt;
box; protected mode applications never use any of this memory. Thus,&lt;br /&gt;
programs that are careless about memory allocation or that make single-&lt;br /&gt;
tasking assumptions about the availability of memory can run in a&lt;br /&gt;
multitasking environment. Figure 19-1 illustrates the OS/2 memory layout.&lt;br /&gt;
The low bytes of memory are reserved for the device drivers and portions of&lt;br /&gt;
OS/2 that must run in real mode. The remainder of the space, up to the&lt;br /&gt;
RMSIZE value, is dedicated to the 3x box. Memory from 640 KB to 1 MB is&lt;br /&gt;
reserved for ROMs and video display buffers. Memory above 1 MB holds the&lt;br /&gt;
remainder of OS/2 and all protect mode applications. Nonswappable, fixed&lt;br /&gt;
segments are kept at one end of this memory to reduce fragmentation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
      N ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ³    Protect     ³&lt;br /&gt;
        ³      mode      ³&lt;br /&gt;
        ³  applications  ³&lt;br /&gt;
        ³   (movable)    ³&lt;br /&gt;
        &amp;lt;                &amp;lt;&lt;br /&gt;
         &amp;gt;                &amp;gt;&lt;br /&gt;
        &amp;lt;                &amp;lt;&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³     Fixed      ³&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³      OS/2      ³&lt;br /&gt;
   1 MB ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
 640 KB ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
        ³      Real      ³&lt;br /&gt;
        ³      mode      ³&lt;br /&gt;
        ³  application   ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³   Additional   ³&lt;br /&gt;
        ³ device drivers ³&lt;br /&gt;
  ~100k ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³                ³&lt;br /&gt;
        ³    Low OS/2    ³&lt;br /&gt;
        ³                ³&lt;br /&gt;
   90:0 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
        ³    Bios ROM    ³&lt;br /&gt;
      0 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 19-1.  System memory layout.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 uses the screen group mechanism to provide a user interface to&lt;br /&gt;
the 3x box. One screen group is designated the real mode screen group;&lt;br /&gt;
automatically, OS/2 executes COMMAND.COM in that screen group when it is&lt;br /&gt;
first selected. The user accesses the real mode environment by selecting&lt;br /&gt;
that screen group and returns to the protected mode environment by&lt;br /&gt;
selecting another screen group. OS/2 version 1.0 supports a single real&lt;br /&gt;
mode screen group because the real mode compatibility is provided by&lt;br /&gt;
actually running the application in real mode. Thus, only one 640 KB area&lt;br /&gt;
is reserved for all real mode applications, and adjudicating between the&lt;br /&gt;
conflicting hardware manipulations of multiple real mode applications&lt;br /&gt;
without any assistance from the 80286 microprocessor hardware would be&lt;br /&gt;
prohibitively difficult. The 80386 microprocessor, however, provides a&lt;br /&gt;
special hardware facility called virtual 8086 mode that will allow a future&lt;br /&gt;
release of OS/2 to support multiple real mode screen groups, but only on an&lt;br /&gt;
80386-based machine.&lt;br /&gt;
     The operating system that services the 3x application's INT 21&lt;br /&gt;
requests is not an exact copy of MS-DOS; it's actually a low-memory&lt;br /&gt;
extension of OS/2 itself. Because OS/2 is derived from MS-DOS, OS/2&lt;br /&gt;
executes MS-DOS functions in a manner identical to that of the real MS-DOS.&lt;br /&gt;
OS/2 supports the non-MS-DOS functions mentioned above by staying out of&lt;br /&gt;
the way as much as possible and letting the 3x application &amp;quot;party hearty&amp;quot;&lt;br /&gt;
with the hardware. For example, hooking most interrupt vectors is&lt;br /&gt;
supported, as is hooking INT 21 and the ROM BIOS INT vectors. The ROM BIOS&lt;br /&gt;
calls themselves are fully supported. Frequently, staying out of the way is&lt;br /&gt;
not as easy as it may sound. For example, OS/2 must intercept and monitor&lt;br /&gt;
real mode calls made to the disk driver part of the ROM BIOS so that it can&lt;br /&gt;
prevent conflict with ongoing, asynchronous protect-mode disk I/O. OS/2 may&lt;br /&gt;
find it necessary to momentarily block a real mode application's BIOS call&lt;br /&gt;
until the protect mode device driver can release the hardware. Once the&lt;br /&gt;
real mode application is in the BIOS, the same interlock mechanism prevents&lt;br /&gt;
the protect mode device driver from entering the disk I/O critical&lt;br /&gt;
section.&lt;br /&gt;
     Hard errors encountered by the real mode application are handled by a&lt;br /&gt;
hybrid of the OS/2 hard error daemon and the 3x box INT 24 mechanism in a&lt;br /&gt;
three-step process, as follows:&lt;br /&gt;
&lt;br /&gt;
     1:  Hard error codes caused by events unique to the OS/2 environment--&lt;br /&gt;
         such as a volume manager media change request--activate the hard&lt;br /&gt;
         error daemon so that the user can get an accurate explanation&lt;br /&gt;
         of the problem. The user's response to the hard error is saved&lt;br /&gt;
         but is not yet acted upon. Hard error codes, which are also&lt;br /&gt;
         present in MS-DOS version 3.3, skip this step and start at&lt;br /&gt;
         step 2.&lt;br /&gt;
&lt;br /&gt;
     2:  If the real mode application has installed its own hard error&lt;br /&gt;
         handler via the INT 24 vector, it is called. If step 1 was&lt;br /&gt;
         skipped, the code should be known to the application, and it is&lt;br /&gt;
         presented unchanged. If step 1 was taken, the error code is&lt;br /&gt;
         transformed to ERROR_I24_GEN_FAILURE for this step. The response&lt;br /&gt;
         returned by the program, if valid for this class of hard error, is&lt;br /&gt;
         acted upon. This means that hard errors new to OS/2 can actually&lt;br /&gt;
         generate two pop-ups--one from the hard error daemon with an&lt;br /&gt;
         accurate message and one from the application itself with a&lt;br /&gt;
         General Failure message. This allows the user to understand the&lt;br /&gt;
         true cause of the hard error and yet notifies the application that&lt;br /&gt;
         a hard error has occurred. In such a case, the action specified by&lt;br /&gt;
         the application when it returned from its own hard error handler&lt;br /&gt;
         is the one taken, not the action specified by the user to the&lt;br /&gt;
         initial hard error daemon pop-up.&lt;br /&gt;
&lt;br /&gt;
3:       If the real mode application has not registered its hard error&lt;br /&gt;
         handler via the INT 24 mechanism, OS/2 provides a default handler&lt;br /&gt;
         that uses the hard error daemon. If step 1 was taken and the hard&lt;br /&gt;
         error daemon has already run, it is not run again; OS/2 takes the&lt;br /&gt;
         action specified in response to the hard error pop-up that was&lt;br /&gt;
         displayed. If step 1 was not taken because the hard error code is&lt;br /&gt;
         MS-DOS 3.x compatible and if step 2 was not taken because the&lt;br /&gt;
         application did not provide its own handler, then OS/2 activates&lt;br /&gt;
         the hard error daemon in step 3 to present the message and receive&lt;br /&gt;
         a reply.&lt;br /&gt;
&lt;br /&gt;
     The 3x box supports only MS-DOS functionality; no new OS/2 features&lt;br /&gt;
are available to 3x box applications--no new API, no multiple threads, no&lt;br /&gt;
IPC, no semaphores, and so on.&lt;br /&gt;
2. There are two exceptions. The OPEN function was&lt;br /&gt;
extended, and an INT 2F multiplex function was added to notify&lt;br /&gt;
real mode applications of screen switches.&lt;br /&gt;
2 This decision was made for two reasons.&lt;br /&gt;
First, although any real mode application can damage the system's&lt;br /&gt;
stability, allowing real mode applications to access some protect mode&lt;br /&gt;
features may aggravate the problem. For example, terminate and stay&lt;br /&gt;
resident programs may manipulate the CPU in such a way as to make it&lt;br /&gt;
impossible for a real mode application to protect a critical section with&lt;br /&gt;
semaphores and yet guarantee that it won't leave the semaphore orphaned.&lt;br /&gt;
Second, because OS/2 has only one real mode box and it labors under a 640&lt;br /&gt;
KB memory ceiling, it doesn't make sense to develop new real mode&lt;br /&gt;
applications that use new OS/2 functions and thus require OS/2.&lt;br /&gt;
     The 3x box emulation extends to interrupts. OS/2 continues to context&lt;br /&gt;
switch the CPU when the 3x box is active; that is, the 3x box application&lt;br /&gt;
is the foreground application. Because the foreground process receives a&lt;br /&gt;
favorable priority, its CPU is preempted only when a time-critical protect&lt;br /&gt;
mode application needs to run or when the real mode application blocks. If&lt;br /&gt;
the CPU is running a protect mode application when a device interrupt comes&lt;br /&gt;
in, OS/2 switches to real mode so that a real mode application that is&lt;br /&gt;
hooking the interrupt vectors can receive the interrupt in real mode. When&lt;br /&gt;
the interrupt is complete, OS/2 switches back to protected mode and resumes&lt;br /&gt;
the protected application.&lt;br /&gt;
     Although protected mode applications can continue to run when the 3x&lt;br /&gt;
box is in the foreground, the reverse is not true. When the 3x box screen&lt;br /&gt;
group is in the background, all 3x box execution is suspended, including&lt;br /&gt;
interrupts. Unlike protected mode applications, real mode applications&lt;br /&gt;
cannot be trusted to refrain from manipulating the screen hardware when&lt;br /&gt;
they are in a background screen group. Normally, a real mode application&lt;br /&gt;
doesn't notice its suspension when it's in background mode; the only thing&lt;br /&gt;
it might notice is that the system time-of-day has apparently &amp;quot;jumped&lt;br /&gt;
forward.&amp;quot; Because mode switching is a slow process and leaves interrupts&lt;br /&gt;
disabled for almost 1 millisecond, mode switching can cause interrupt&lt;br /&gt;
overruns on fast devices such as serial ports. The best way to deal with&lt;br /&gt;
this is to switch the real mode application into a background screen group;&lt;br /&gt;
with no more real mode programs to execute, OS/2 does no further mode&lt;br /&gt;
switching.&lt;br /&gt;
     Some OS/2 utility programs such as FIND are packaged as Family API&lt;br /&gt;
applications. A single binary can run in both protected mode and real mode,&lt;br /&gt;
and the user is saved the inconvenience of switching from real mode to&lt;br /&gt;
protected mode to do simple utility functions. This works well for simple&lt;br /&gt;
utility programs without full screen or graphical interfaces and for&lt;br /&gt;
programs that have modest memory demands and that in other ways have little&lt;br /&gt;
need of OS/2's extended capabilities. Obviously, if an application can make&lt;br /&gt;
good use of OS/2's protect mode features, it should be written to be&lt;br /&gt;
protect mode only so that it can take advantage of those features.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
20  Family API&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
When a new release of a PC operating system is announced, application&lt;br /&gt;
writers face a decision: Should they write a new application to use some of&lt;br /&gt;
the new features or should they use only the features in earlier releases?&lt;br /&gt;
If they go for the sexy new features, their product might do more, be&lt;br /&gt;
easier to write, or be more efficient; but when the program hits the&lt;br /&gt;
market, only 10 percent of existing PCs may be running the new release. Not&lt;br /&gt;
all of the existing machines have the proper processor to be able to run&lt;br /&gt;
the new system, and, of those, many of their users haven't seen the need to&lt;br /&gt;
go to the expense and endure the hassle of upgrading their operating&lt;br /&gt;
system. If it's viable to write the new application so that it requires&lt;br /&gt;
only the old operating system (and therefore runs in compatibility mode&lt;br /&gt;
under the new operating system), then it's tempting to do so. Even though&lt;br /&gt;
the product is not as good as it might be, it can sell to 100 percent of&lt;br /&gt;
the installed base of machines--10 times as many as it would if it required&lt;br /&gt;
the new operating system.&lt;br /&gt;
     And here you have the classic &amp;quot;catch-22&amp;quot; of software standards: If&lt;br /&gt;
users don't see a need, they won't use the new system. If they don't use&lt;br /&gt;
the new system, applications will not be written explicitly for it; so the&lt;br /&gt;
users never see a need. Without some way to prime the pump, it will be a&lt;br /&gt;
long time before a comprehensive set of applications are available that use&lt;br /&gt;
the new system's features.&lt;br /&gt;
     OS/2 tackles this problem in several ways. The software bundled with&lt;br /&gt;
OS/2 runs in protected mode, and OS/2 attempts to include as much&lt;br /&gt;
additional user function as possible to increase its value to a user who&lt;br /&gt;
initially owns no protected mode applications. The most important user&lt;br /&gt;
acceptance feature of OS/2, however, is called Family API. Family API is a&lt;br /&gt;
special subset of the OS/2 protected mode API. Using special tools included&lt;br /&gt;
in the OS/2 developer's kit, you can build applications that use only the&lt;br /&gt;
Family API. The resultant .EXE file(s) run unchanged in OS/2 protect mode&lt;br /&gt;
or on an 8086 running MS-DOS 2.x or 3.x.&lt;br /&gt;
1. Of course, they also run in the MS-DOS 3.x compatible screen group&lt;br /&gt;
under OS/2; but except for convenience utilities, it's generally a&lt;br /&gt;
waste to dedicate the one real mode screen group to running an appl-&lt;br /&gt;
cation that could run in any of the many protect mode screen groups.&lt;br /&gt;
1 Thus, developers don't have to&lt;br /&gt;
choose between writing applications that are OS/2 protect mode and writing&lt;br /&gt;
applications that are MS-DOS compatible; they can use the Family API&lt;br /&gt;
mechanism and do both. Your applications will run as protected mode&lt;br /&gt;
applications under OS/2 and as MS-DOS applications under a true MS-DOS&lt;br /&gt;
system.&lt;br /&gt;
     Clearly, the Family API is a noteworthy feature. It offers some OS/2&lt;br /&gt;
functions, together with the dynamic link system interface, to programs&lt;br /&gt;
that run under MS-DOS without a copy of OS/2 anywhere in sight. It does&lt;br /&gt;
this by providing an OS/2 compatibility library that accepts the OS/2&lt;br /&gt;
system interface calls and implements them itself, calling the underlying&lt;br /&gt;
MS-DOS system via INT 21 as necessary. This information should give you a&lt;br /&gt;
big head start in figuring out which OS/2 functions are included in the&lt;br /&gt;
Family API: Clearly all functions that have similar INT 21 functions--such&lt;br /&gt;
as DosOpen, DosRead, and DosAllocSeg--are supported. Also present are&lt;br /&gt;
functions, such as DosSubAlloc, that can be supported directly by the&lt;br /&gt;
special Family API library. Features that are extremely difficult to&lt;br /&gt;
support in a true MS-DOS environment, such as multiple threads and&lt;br /&gt;
asynchronous I/O, are not present in the Family API.&lt;br /&gt;
     Where does this library come from? And how does it get loaded by MS-&lt;br /&gt;
DOS to satisfy the OS/2 executable's dynlink requests? It's all done with&lt;br /&gt;
mirrors, as the expression goes, and the &amp;quot;mirrors&amp;quot; must be built into the&lt;br /&gt;
application's .EXE file because that file is all that's present when a&lt;br /&gt;
Family API application is executed under MS-DOS. Figure 20-1 shows the&lt;br /&gt;
layout of a Family API .EXE file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
         Family API&lt;br /&gt;
            .EXE&lt;br /&gt;
     ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³&lt;br /&gt;
     ³°°°MS-DOS 3.x°°°°³&lt;br /&gt;
     ³°°°.EXE header°ÄÄÅÄÄ¿&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³  ³&lt;br /&gt;
     ³°°°Family API°°°°³  ³ Shaded area is&lt;br /&gt;
     ³°°°°°loader°°°°°°³  ³ read by MS-DOS as&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ³ a real mode&lt;br /&gt;
     ³°°°°°°°°°°°°°°°°°³  ³ application&lt;br /&gt;
     ³°°°Family API°°°°³  ³&lt;br /&gt;
     ³°°°°°library°°°°°³  ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´�ÄÙ&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³      OS/2       ³&lt;br /&gt;
     ³   .EXE header   ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³      OS/2       ³&lt;br /&gt;
     ³   application   ³&lt;br /&gt;
     ³    segments     ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     Ã Ä Ä Ä Ä Ä Ä Ä Ä ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´&lt;br /&gt;
     ³                 ³&lt;br /&gt;
     ³     Dynlink     ³&lt;br /&gt;
     ³      names      ³&lt;br /&gt;
     ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ&lt;br /&gt;
&lt;br /&gt;
Figure 20-1.  Family API executable (.EXE) format.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     OS/2 needed to define a new .EXE file because the existing MS-DOS .EXE&lt;br /&gt;
file format contained too little information for the OS/2 protect mode&lt;br /&gt;
segmented environment. Because, as we've discussed, the 8086 memory&lt;br /&gt;
architecture is--despite the terminology normally used--a linear memory&lt;br /&gt;
architecture, the MS-DOS .EXE format described only a single hunk of memory&lt;br /&gt;
that was to be loaded contiguously. OS/2 needs each segment described&lt;br /&gt;
separately, with information on its status: read only, code or data, demand&lt;br /&gt;
load or preload, and so on. Naturally, OS/2 also needs a .EXE format with&lt;br /&gt;
special records to describe loadtime dynamic links. This new .EXE format&lt;br /&gt;
was defined so that its initial bytes look exactly like those of the old&lt;br /&gt;
MS-DOS .EXE file header. A special flag bit is set in this fake .EXE&lt;br /&gt;
 header that is ignored by all releases of MS-DOS but that OS/2 recognizes&lt;br /&gt;
to mean &amp;quot;It's not true. I'm really an OS/2 .EXE file. Seek to this location&lt;br /&gt;
to find the true, new-style .EXE header.&amp;quot;&lt;br /&gt;
     When an MS-DOS system is told to load this .EXE file, it sees and&lt;br /&gt;
believes the old .EXE file header. This header does not describe the&lt;br /&gt;
application itself but a body of special code built into the .EXE file&lt;br /&gt;
before the actual application's code: the Family API loader and library. In&lt;br /&gt;
other words, to MS-DOS this .EXE file looks like a valid, executable&lt;br /&gt;
program, and that program is the Family API loader and library. The Family&lt;br /&gt;
API loader and library are loaded into memory, and execution begins. MS-DOS&lt;br /&gt;
doesn't load in the body of the application itself because it wasn't&lt;br /&gt;
described as part of the load image in the special MS-DOS .EXE file header.&lt;br /&gt;
As soon as it starts to execute, the Family API loader begins reading in&lt;br /&gt;
the application's segments, performs a loader's general relocation chores,&lt;br /&gt;
and fixes up dynlink references to the proper entry points in the Family&lt;br /&gt;
API library package. When the application is loaded, the Family API loader&lt;br /&gt;
block moves the application to its final execution address, which overlays&lt;br /&gt;
most of the Family API loader to reclaim that space, and execution&lt;br /&gt;
begins.&lt;br /&gt;
     All OS/2 .EXE files have this fake MS-DOS .EXE format header. In non-&lt;br /&gt;
Family API executables, the Family API loader and library are missing, and&lt;br /&gt;
by default the header describes an impossibly big MS-DOS executable. Should&lt;br /&gt;
the application be accidentally run under a non-OS/2 system or in the OS/2&lt;br /&gt;
compatibility screen group, MS-DOS will refuse to load the program.&lt;br /&gt;
Optionally, the programmer can link in a small stub program that goes where&lt;br /&gt;
the Family API loader would and that prints a more meaningful error&lt;br /&gt;
message. As we said earlier, the old-style .EXE headers on the front of the&lt;br /&gt;
file contain a flag bit to alert OS/2 to the presence of a new-style .EXE&lt;br /&gt;
header further into the file. Because this header doesn't describe the&lt;br /&gt;
Family API loader and library parts of the file, OS/2 ignores their&lt;br /&gt;
presence when it loads a Family API application in protected mode; the&lt;br /&gt;
application's dynlink references are fixed up to the normal dynlink&lt;br /&gt;
libraries, and the Family API versions of those libraries are ignored.&lt;br /&gt;
     There Ain't No Such Thing As A Free Lunch, and the same unfortunately&lt;br /&gt;
applies to the Family API mechanism. First, although the Family API allows&lt;br /&gt;
dynlink calls to be used in an MS-DOS environment, this is not true&lt;br /&gt;
dynlinking; it's quasi dynlinking. Obviously, runtime dynlinking is not&lt;br /&gt;
supported, but even loadtime dynlinking is special because the dynlink&lt;br /&gt;
target library is bound into the .EXE file. One of the advantages of&lt;br /&gt;
dynlinks is that the target code is not part of the .EXE file and can&lt;br /&gt;
therefore be changed and upgraded without changing the .EXE file. This is&lt;br /&gt;
not true of the dynlink emulation library used by the Family API because&lt;br /&gt;
it is built into the .EXE file. Fortunately, this disadvantage isn't&lt;br /&gt;
normally a problem. Dynlink libraries are updated either to improve&lt;br /&gt;
their implementation or to add new features. The Family API library can't&lt;br /&gt;
be improved very much because its environment--MS-DOS--is limited and&lt;br /&gt;
unchanging. If new Family API features were added, loading that new library&lt;br /&gt;
with preexisting Family API .EXE files would make no sense; those programs&lt;br /&gt;
wouldn't be calling the new features.&lt;br /&gt;
     A more significant drawback is the size and speed hit that the Family&lt;br /&gt;
API introduces. Clearly, the size of a Family API .EXE file is extended by&lt;br /&gt;
the size of the Family API loader and the support library. The tools used&lt;br /&gt;
to build Family API executables include only those library routines used by&lt;br /&gt;
the program, but even so the library and the loader add up to a nontrivial&lt;br /&gt;
amount of memory--typically 10 KB to 14 KB in the .EXE file and perhaps&lt;br /&gt;
9KB (the loader is not included) in RAM. Finally, loading a Family API&lt;br /&gt;
application under MS-DOS is slower than loading a true MS-DOS .EXE file.&lt;br /&gt;
Comparing loadtime against the loadtime of MS-DOS is tough for any&lt;br /&gt;
operating system because loading faster than MS-DOS is difficult. The .EXE&lt;br /&gt;
file consists of a single lump of contiguous data that can be read into&lt;br /&gt;
memory in a single disk read operation. A relocation table must also be&lt;br /&gt;
read, but it's typically very small. It's hard for any system to be faster&lt;br /&gt;
than this. Clearly, loading a Family API application is slower because the&lt;br /&gt;
loader and library must be loaded, and then they must, a segment at a time,&lt;br /&gt;
bring in the body of the application.&lt;br /&gt;
     Although the Family API makes dual environment applications possible,&lt;br /&gt;
it can't totally hide from an application the difference between the MS-DOS&lt;br /&gt;
3.x and the OS/2 execution environment. For example, the Family API&lt;br /&gt;
supports only the DosFindFirst function for a single search handle at a&lt;br /&gt;
time. An application that wants to perform multiple directory searches&lt;br /&gt;
simultaneously should use DosGetMachineMode to determine its environment&lt;br /&gt;
and then use the unrestricted DosFindFirst function if running in protect&lt;br /&gt;
mode or use the INT 21 functions if running in real mode. Likewise, an&lt;br /&gt;
application that wants to manipulate printer data needs to contain version-&lt;br /&gt;
specific code to hook INT 17 or to use device monitors, depending on the&lt;br /&gt;
environment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Part III  The Future&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21  The Future&lt;br /&gt;
&lt;br /&gt;
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ&lt;br /&gt;
&lt;br /&gt;
This chapter is difficult to write because describing the second version of&lt;br /&gt;
OS/2 becomes uninteresting when that version is released. Furthermore,&lt;br /&gt;
preannouncing products is bad practice; the trade-offs between schedule and&lt;br /&gt;
customer demand can accelerate the inclusion of some features and postpone&lt;br /&gt;
others, often late in the development cycle. If we talk explicitly about&lt;br /&gt;
future features, developers may plan their work around the availability of&lt;br /&gt;
those features and be left high and dry if said features are postponed. As&lt;br /&gt;
a result, this chapter is necessarily vague about both the functional&lt;br /&gt;
details and the release schedule, discussing future goals for features&lt;br /&gt;
rather than the features themselves. Design your application, not so that&lt;br /&gt;
it depends on the features described here, but so that it is compatible&lt;br /&gt;
with them.&lt;br /&gt;
     OS/2 version 1.0 is the first standard MS-DOS-compatible operating&lt;br /&gt;
system that unlocks the memory-addressing potential of the 80286--a &amp;quot;train&amp;quot;&lt;br /&gt;
that will &amp;quot;pull&amp;quot; a great many APIs into the standard. On the other hand,&lt;br /&gt;
foreseeable future releases cannot expect such penetration, so the&lt;br /&gt;
designers of OS/2 version 1.0 focused primarily on including a full set of&lt;br /&gt;
APIs. Major performance improvements were postponed for future releases&lt;br /&gt;
simply because such improvements can be easily added later, whereas new&lt;br /&gt;
APIs cannot. Most of the planned work is to take further advantage of&lt;br /&gt;
existing interfaces, not to create new ones.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.1  File System&lt;br /&gt;
&lt;br /&gt;
Clearly, the heart of the office automation environment is data--lots of&lt;br /&gt;
data--searching for it, reading it, and, less frequently, writing it. A&lt;br /&gt;
machine's raw number-crunching capacity is relatively uninteresting in this&lt;br /&gt;
milieu; the important issue is how fast the machine can get the data and&lt;br /&gt;
manipulate it. Certainly, raw CPU power is advantageous; it allows the use&lt;br /&gt;
of a relatively compute bound graphical user interface, for example. But&lt;br /&gt;
I/O performance is becoming the limiting factor, especially in a&lt;br /&gt;
multitasking environment. Where does this data come from? If it's from the&lt;br /&gt;
keyboard, no problem; human typing speeds are glacially slow to a computer.&lt;br /&gt;
If the data is from a non-mass-storage device, OS/2's direct device access&lt;br /&gt;
facilities should provide sufficient throughput. That leaves the file&lt;br /&gt;
system for local disks and the network for remote data. The file system is&lt;br /&gt;
a natural for a future release upgrade. Its interface is generic so that&lt;br /&gt;
applications written for the first release will work compatibly with new&lt;br /&gt;
file systems in subsequent releases.&lt;br /&gt;
     Talking about pending file system improvements is relatively easy&lt;br /&gt;
because the weaknesses in the current FAT file system are obvious.&lt;br /&gt;
&lt;br /&gt;
     þ  Large Disk Support&lt;br /&gt;
        Clearly, a new file system will support arbitrarily large&lt;br /&gt;
        disks without introducing prohibitive allocation fragmentation.&lt;br /&gt;
        Allocation fragmentation refers to the minimum amount of disk space&lt;br /&gt;
        that a file system can allocate to a small file--the allocation&lt;br /&gt;
        unit. If the allocation unit is size N, the average file on the&lt;br /&gt;
        disk is expected to waste N/2 bytes of disk space because each file&lt;br /&gt;
        has a last allocation unit and on the average that unit will be&lt;br /&gt;
        only half filled. Actually, if the allocation unit is large, say&lt;br /&gt;
        more than 2 KB, the average fragmentation loss is greater than this&lt;br /&gt;
        estimate because a disproportionate number of files are small.&lt;br /&gt;
           The existing MS-DOS FAT file system can handle large disks,&lt;br /&gt;
        but at the cost of using very large allocation units. Depending on&lt;br /&gt;
        the number and the size of the files, a 100 MB disk might be as&lt;br /&gt;
        much as 50 percent wasted by this fragmentation. The new Microsoft&lt;br /&gt;
        file system will support a very small allocation unit--probably 512&lt;br /&gt;
        bytes--to reduce this fragmentation, and this small allocation unit&lt;br /&gt;
        size will not adversely affect the performance of the file system.&lt;br /&gt;
&lt;br /&gt;
     þ  File Protection&lt;br /&gt;
        A new file system also must support file access protection as part&lt;br /&gt;
        of the move toward a fully secure environment. File protection is&lt;br /&gt;
        typically a feature of multiuser operating systems; the MS-DOS FAT&lt;br /&gt;
        file system was designed for a single-user environment and contains&lt;br /&gt;
        no protection facilities. So why do we need them now? One reason is&lt;br /&gt;
        that a networked PC is physically a single-user machine, but&lt;br /&gt;
        logically it's a multiuser machine because multiple users can&lt;br /&gt;
        access the same files over the network. Also, as we shall see, it&lt;br /&gt;
        is sometimes useful to be able to protect your own files from&lt;br /&gt;
        access by yourself.&lt;br /&gt;
           Today, most network installations consist of server machines&lt;br /&gt;
        and client machines, with client machines able to access only files&lt;br /&gt;
        on server machines. MSNET and PCNET servers have a rudimentary form&lt;br /&gt;
        of file protection, but it needs improvement (see below). In the&lt;br /&gt;
        future, as machines become bigger and as products improve, files on&lt;br /&gt;
        client machines will also be available across the network. Clearly,&lt;br /&gt;
        a strong protection mechanism is needed to eliminate risks to a&lt;br /&gt;
        client machine's files. Finally, a file protection mechanism can be&lt;br /&gt;
        useful even on a single-user machine that is not accessible from a&lt;br /&gt;
        network. Today a variety of &amp;quot;Trojan&amp;quot; programs claim to be one thing&lt;br /&gt;
        but actually are another. In a nonnetworked environment, these&lt;br /&gt;
        programs are generally examples of mindless vandalism; typically,&lt;br /&gt;
        they purge the contents of the victim's hard disk. In a future&lt;br /&gt;
        office environment, they might edit payroll files or send sensitive&lt;br /&gt;
        data to someone waiting across the network. If you, as a user, can&lt;br /&gt;
        put sensitive files under password protection, they are safe even&lt;br /&gt;
        from yourself when you unwittingly run a Trojan program. That&lt;br /&gt;
        program doesn't know the password, and you certainly will decline&lt;br /&gt;
        to supply it. Self-protection also prevents someone from sitting&lt;br /&gt;
        down at your PC while you are at lunch or on vacation and wreaking&lt;br /&gt;
        havoc with your files.&lt;br /&gt;
           Protection mechanisms take two general forms:&lt;br /&gt;
        capability tokens and access lists. capability token gives access&lt;br /&gt;
        to an object if the requestor can supply the proper token,&lt;br /&gt;
        which itself can take a variety of forms. A per-file or per-&lt;br /&gt;
        directory password, such as is available on existing MSNET&lt;br /&gt;
        and PCNET products, is a kind of  capability token: If you&lt;br /&gt;
        can present the password, you can access the file. Note that&lt;br /&gt;
        the password is associated with the item, not the user. The&lt;br /&gt;
        front door key to your house is a good example of a&lt;br /&gt;
        capability token, and it shows the features and limitations&lt;br /&gt;
        of the approach very well. Access to your house depends on&lt;br /&gt;
        owning the capability token--the key--and not on who you&lt;br /&gt;
        are. If you don't have your key, you can't get in, even if&lt;br /&gt;
        it's your own house. Anybody that does have the key can get&lt;br /&gt;
        in, no matter who they are. A key can sometimes be handy:&lt;br /&gt;
        You can loan it to someone for a day and then get it back.&lt;br /&gt;
        You can give it to the plumber's office, for example, and&lt;br /&gt;
        the office can give it to the plumber, who can in turn give&lt;br /&gt;
        it to an assistant. Capability tokens are flexible because&lt;br /&gt;
        you can pass them around without notifying the owner of the&lt;br /&gt;
        protected object.&lt;br /&gt;
           This benefit is also the major drawback of capability token&lt;br /&gt;
        systems: The capabilities can be passed around willy-nilly&lt;br /&gt;
        and, like a key, can be duplicated. Once you give your key&lt;br /&gt;
        out, you never know if you've gotten &amp;quot;them&amp;quot; back again. You&lt;br /&gt;
        can't enumerate who has access to your house, and if they&lt;br /&gt;
        refuse to return a key or if they've duplicated it, you&lt;br /&gt;
        can't withdraw access to your house. The only way to regain&lt;br /&gt;
        control over your house is to change the lock, which means&lt;br /&gt;
        that you have to reissue keys to everybody who should get&lt;br /&gt;
        access. In the world of houses and keys, this isn't much of&lt;br /&gt;
        a problem because keys aren't given out that much and it's&lt;br /&gt;
        easy to contact the few people who should have them.&lt;br /&gt;
        Changing the capability &amp;quot;lock&amp;quot; on a computer file is much&lt;br /&gt;
        more difficult, however, because it may mean updating a&lt;br /&gt;
        great many programs that are allowed access, and they all&lt;br /&gt;
        have to be updated simultaneously so that none is&lt;br /&gt;
        accidentally locked out. And, of course, the distribution of&lt;br /&gt;
        the new capability token must be carried out securely; you&lt;br /&gt;
        must ensure that no &amp;quot;bad guy&amp;quot; gets a chance to see and copy&lt;br /&gt;
        the token.&lt;br /&gt;
           And, finally, because a separate capability token, or&lt;br /&gt;
        password, needs to be kept for each file or directory, you can't&lt;br /&gt;
        possibly memorize them all. Instead, they get built into programs,&lt;br /&gt;
        stored in files, entered into batch scripts, and so on. All these&lt;br /&gt;
        passwords--the ones that are difficult to change because of the&lt;br /&gt;
        hassle of updating everybody--are being kept around in &amp;quot;plain text&amp;quot;&lt;br /&gt;
        in standardized locations, an invitation for pilferage. And just as&lt;br /&gt;
        the lock on your door won't tell you how many keys exist, a&lt;br /&gt;
        capability token system won't be able to warn you that someone has&lt;br /&gt;
        stolen a copy of the capability token.&lt;br /&gt;
           An alternative approach is the access list mechanism. It is&lt;br /&gt;
        equivalent to the guard at the movie studio gate who has a&lt;br /&gt;
        list of people on his clipboard. Each protected object is&lt;br /&gt;
        associated with a list of who is allowed what kind of access. It's&lt;br /&gt;
        easy to see who has access--simply look at the list. It's easy to&lt;br /&gt;
        give or take away access--simply edit the list. Maintaining the&lt;br /&gt;
        list is easy because no change is made unless someone is to be&lt;br /&gt;
        added or removed, and the list can contain group names, such as&lt;br /&gt;
        &amp;quot;anyone from the production department&amp;quot; or &amp;quot;all vice presidents.&amp;quot;&lt;br /&gt;
           The fly in this particular ointment--and the reason that&lt;br /&gt;
        MSNET didn't use this approach--is in authenticating the&lt;br /&gt;
        identification of the person who wants access. In our movie studio,&lt;br /&gt;
        a picture badge is probably sufficient.&lt;br /&gt;
1. Note that the photo on the badge, together with a hard-to duplicate&lt;br /&gt;
design, keeps the badge from being just another capability token.&lt;br /&gt;
1 With the computer, we use&lt;br /&gt;
        a personal password. This password doesn't show that you have&lt;br /&gt;
        access to a particular file; it shows that you are who you claim to&lt;br /&gt;
        be. Also, because you have only one password, you can memorize it;&lt;br /&gt;
        it needn't be written on any list. Finally, you can change the&lt;br /&gt;
        password frequently because only one person--the one changing it,&lt;br /&gt;
        you--needs to be notified. Once the computer system knows that&lt;br /&gt;
        you're truly Hiram G. Hornswoggle, it grants or refuses access&lt;br /&gt;
        based on whether you're on an access list or belong to a group that&lt;br /&gt;
        is on an access list. MS-DOS can't use this approach because it's&lt;br /&gt;
        an unprotected system; whatever flag it sets in memory to say that&lt;br /&gt;
        you have properly authenticated yourself can be set by a cheater&lt;br /&gt;
        program. OS/2 is a protect mode operating system and is secure from&lt;br /&gt;
        such manipulation provided that no untrusted real mode applications&lt;br /&gt;
        are executed.&lt;br /&gt;
2. A future OS/2 release will take advantage of the 80386&lt;br /&gt;
processor's virtual real mode facility to make it safe to run&lt;br /&gt;
untrusted real mode programs on an 80386.&lt;br /&gt;
2 A networking environment provides an extra&lt;br /&gt;
        challenge because you can write a program--perhaps running&lt;br /&gt;
        on an MS-DOS machine to avoid protection mechanisms--that &amp;quot;sniffs&amp;quot;&lt;br /&gt;
        the network, examining every communication. A client machine can't&lt;br /&gt;
        send a plain-text password over the network to authenticate its&lt;br /&gt;
        user because a sniffer could see it. And it certainly can't send a&lt;br /&gt;
        message saying, &amp;quot;I'm satisfied that this is really Hiram.&amp;quot; The&lt;br /&gt;
        client machine may be running bogus software that will lie and say&lt;br /&gt;
        that when it isn't true. In other words, a network authentication&lt;br /&gt;
        protocol must assume that &amp;quot;bad guys&amp;quot; can read all net transmissions&lt;br /&gt;
        and can generate any transmission they wish.&lt;br /&gt;
           As should be clear by now, a future OS/2 file system will&lt;br /&gt;
        support per-object permission lists. OS/2 will be enhanced&lt;br /&gt;
        to support users' identifying themselves by means of personal&lt;br /&gt;
        passwords. Future network software will support a secure network&lt;br /&gt;
        authentication protocol.&lt;br /&gt;
&lt;br /&gt;
     A new file system will do more than support access lists; it will also&lt;br /&gt;
support filenames longer than the FAT 8.3 convention, and it will support&lt;br /&gt;
extended file attributes. The FAT file system supports a very limited set&lt;br /&gt;
of attributes, each of which are binary flags--system, hidden, read-only,&lt;br /&gt;
and so on. Extended attributes allow an arbitrary set of attributes,&lt;br /&gt;
represented as text strings, to be associated with each file. Individual&lt;br /&gt;
applications will be able to define specific attributes, set them on files,&lt;br /&gt;
and later query their values. Extended attributes can be used, for example,&lt;br /&gt;
to name the application that created the file. This would allow a user to&lt;br /&gt;
click the mouse over the filename on a directory display and have the&lt;br /&gt;
presentation manager bring up the proper application on that file.&lt;br /&gt;
     Finally, although this file system wish list looks pretty good, how do&lt;br /&gt;
we know that we've covered all the bases? And will our new file system work&lt;br /&gt;
well with CD-ROM&lt;br /&gt;
3. Special versions of compact discs that contain digital data instead&lt;br /&gt;
of digitized music. When accessed via a modified CD player, they&lt;br /&gt;
provide approximately 600 MB of read-only storage.&lt;br /&gt;
3 disks and WORM drives? The answers are &amp;quot;We don't&amp;quot; and&lt;br /&gt;
&amp;quot;It doesn't,&amp;quot; so a future OS/2 release will support installable file&lt;br /&gt;
systems. An installable file system is similar to an installable device&lt;br /&gt;
driver. When the system is initialized, not only device drivers but new&lt;br /&gt;
file system management packages can be installed into OS/2. This will allow&lt;br /&gt;
specialized file systems to handle specialized devices such as CD-ROMs and&lt;br /&gt;
WORM, as well as providing an easy interface to media written on foreign&lt;br /&gt;
file systems that are on non-MS-DOS or non-OS/2 systems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2  The 80386&lt;br /&gt;
&lt;br /&gt;
Throughout this book, the name 80386 keeps cropping up, almost as a kind of&lt;br /&gt;
magical incantation. To a system designer, it is a magical device. It&lt;br /&gt;
provides the protection facilities of the 80286, but it also provides three&lt;br /&gt;
other key features.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.1  Large Segments&lt;br /&gt;
The 80386 has a segmented architecture very much like that of the 80286,&lt;br /&gt;
but 80286 segments are limited to 64 KB. On the 80386, segments can be as&lt;br /&gt;
large as 4 million KB; segments can be so large that an entire program can&lt;br /&gt;
run in 2 segments (one code and one data) and essentially ignore the&lt;br /&gt;
segmentation facilities of the processor. This is called flat model.&lt;br /&gt;
Writing programs that deal with large structures is easier using flat&lt;br /&gt;
model, and because compilers have a hard time generating optimal segmented&lt;br /&gt;
code, converting 8086/80286 large model programs to 80386 flat model can&lt;br /&gt;
produce dramatic increases in execution speed.&lt;br /&gt;
     Although a future release of OS/2 will certainly support large&lt;br /&gt;
segments and applications that use flat model internally, OS/2 will not&lt;br /&gt;
necessarily provide a flat model API. The system API for 32-bit&lt;br /&gt;
applications may continue to use segmented (that is, 48-bit) addresses.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.2  Multiple Real Mode Boxes&lt;br /&gt;
The 80386 provides a mode of execution called virtual real mode. Processes&lt;br /&gt;
that run in this mode execute instructions exactly as they would in real&lt;br /&gt;
mode, but they are not truly in real mode; they are in a special 8086-&lt;br /&gt;
compatible protected mode. The additional memory management and protection&lt;br /&gt;
facilities that this mode provides allow a future version of OS/2 to&lt;br /&gt;
support more than one real mode box at the same time; multiple real mode&lt;br /&gt;
applications will be able to execute simultaneously and to continue&lt;br /&gt;
executing while in background mode. The virtual real mode eliminates the&lt;br /&gt;
need for mode switching; thus, the user can execute real mode applications&lt;br /&gt;
while running communications applications that have a high interrupt&lt;br /&gt;
rate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.3  Full Protection Capability&lt;br /&gt;
The virtual real mode capability, coupled with the 80386's ability to&lt;br /&gt;
allow/disallow I/O access on a port-by-port basis, provides the hardware&lt;br /&gt;
foundation for a future OS/2 that is fully secure. In a fully secure OS/2,&lt;br /&gt;
the modules loaded in during bootup--the operating system itself, device&lt;br /&gt;
drivers, installable file systems, and so on--must be trusted, but no other&lt;br /&gt;
program can accidentally or deliberately damage others, read protected&lt;br /&gt;
files, or otherwise access or damage restricted data. The only damage an&lt;br /&gt;
aberrant or malicious program will be able to do is to slow down the&lt;br /&gt;
machine by hogging the resources, such as consuming most of the RAM or CPU&lt;br /&gt;
time. This is relatively harmless; the user can simply kill the offending&lt;br /&gt;
program and not run it anymore.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.2.4  Other Features&lt;br /&gt;
The 80386 contains other significant features besides speed, such as paged&lt;br /&gt;
virtual memory, that don't appear in an API or in a specific user benefit.&lt;br /&gt;
For this reason, we won't discuss them here other than to state the&lt;br /&gt;
obvious: An 80386 machine is generally considerably faster than an 80286-&lt;br /&gt;
based one.&lt;br /&gt;
     So what do these 80386 features mean for the 80286? What role will it&lt;br /&gt;
play in the near and far future? Should a developer write for the 80286 or&lt;br /&gt;
the 80386? First, OS/2 for the 80386&lt;br /&gt;
4. A product still under development at the time of this writing.&lt;br /&gt;
All releases of OS/2 will run on the 80386, but the initial OS/2&lt;br /&gt;
release treats the 80386 as a &amp;quot;fast 80286.&amp;quot; The only 80386&lt;br /&gt;
feature it uses is the faster mode-switching capability.&lt;br /&gt;
4 is the same operating system,&lt;br /&gt;
essentially, as OS/2 for the 80286. The only new API in 80386 OS/2 will be&lt;br /&gt;
the 32-bit wide one for 32-bit mode 80386-only binaries. The other&lt;br /&gt;
features--such as virtual memory, I/O permission mapping, and multiple real&lt;br /&gt;
mode boxes--are of value to the user but don't present any new APIs and&lt;br /&gt;
therefore are compatible with all applications. Certainly, taking advantage&lt;br /&gt;
of the 80386's new instruction order codes and 2^32-byte-length segments&lt;br /&gt;
will require a new API; in fact, a program must be specially written and&lt;br /&gt;
compiled for that environment. Only applications that can't function at all&lt;br /&gt;
using the smaller 80286-compatible segments need to become 80386 dependent;&lt;br /&gt;
80286 protect mode programs will run without change and without any&lt;br /&gt;
disadvantage on the 80386, taking advantage of its improved speed.&lt;br /&gt;
     To summarize, there is only one operating system, OS/2. OS/2 supports&lt;br /&gt;
16-bit protected mode applications that run on all machines, and OS/2 will&lt;br /&gt;
support 32-bit protected mode applications that will run only on 80386&lt;br /&gt;
machines. A developer should consider writing an application for the&lt;br /&gt;
32-bit model&lt;br /&gt;
5. When it is announced and documented.&lt;br /&gt;
5 only if the application performs so poorly in the 16-bit&lt;br /&gt;
model that a 16-bit version is worthless. Otherwise, one should develop&lt;br /&gt;
applications for the 16-bit model; such applications will run well on all&lt;br /&gt;
existing OS/2-compatible machines and on all OS/2 releases. Later, when the&lt;br /&gt;
80386 and OS/2-386 have sufficient market penetration, you may want to&lt;br /&gt;
release higher-performance upgrades to products that require the 80386.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
21.3  The Next Ten Years&lt;br /&gt;
&lt;br /&gt;
Microsoft believes that OS/2 will be a major influence in the personal&lt;br /&gt;
computer industry for roughly the next ten years. The standardization of&lt;br /&gt;
computing environments that mass market software brings about gives such&lt;br /&gt;
standards abnormal longevity, while the incredible rate of hardware&lt;br /&gt;
improvements brings on great pressure to change. As a result, we expect&lt;br /&gt;
OS/2 to live long and prosper, where long is a relative term in an industry&lt;br /&gt;
in which nothing can survive more than a decade. What might OS/2's&lt;br /&gt;
successor system look like? If we could answer that today, a successor&lt;br /&gt;
system would be unnecessary. Clearly, the increases in CPU performance will&lt;br /&gt;
continue. Personal computers will undoubtedly follow in the footsteps of&lt;br /&gt;
their supercomputer brethren and become used for more than calculation, but&lt;br /&gt;
also for simulation, modeling, and expert systems, not only in the&lt;br /&gt;
workplace but also in the home. The future will become clearer, over time,&lt;br /&gt;
as this most wonderful of tools continues to change its users.&lt;br /&gt;
     The development of OS/2 is, to date, the largest project that&lt;br /&gt;
Microsoft has ever taken on. From an initially very small group of&lt;br /&gt;
Microsoft engineers, to a still small joint Microsoft-IBM design team, to&lt;br /&gt;
finally a great many developers, builders, testers, and documenters from&lt;br /&gt;
both Microsoft and IBM, the project became known affectionately as the&lt;br /&gt;
&amp;quot;Black Hole.&amp;quot;&lt;br /&gt;
     As I write this, OS/2 is just weeks away from retail sale. It's been a&lt;br /&gt;
great pleasure for me and for the people who worked with me to see our&lt;br /&gt;
black hole begin to give back to our customers the fruits of the labors&lt;br /&gt;
that were poured into it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Glossary&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
anonymous pipe&lt;br /&gt;
a data storage buffer that OS/2 maintains in RAM; used for interprocess&lt;br /&gt;
communications.&lt;br /&gt;
&lt;br /&gt;
Applications Program Interface (API)&lt;br /&gt;
the set of calls a program uses to obtain services from the operating&lt;br /&gt;
system. The term API denotes a service interface, whatever its form.&lt;br /&gt;
&lt;br /&gt;
background category&lt;br /&gt;
a classification of processes that consists of those associated with a&lt;br /&gt;
screen group not currently being displayed.&lt;br /&gt;
&lt;br /&gt;
call gate&lt;br /&gt;
a special LDT or GDT entry that describes a subroutine entry point rather&lt;br /&gt;
than a memory segment. A far call to a call gate selector will cause a&lt;br /&gt;
transfer to the entry point specified in the call gate. This is a feature&lt;br /&gt;
of the 80286/80386 hardware and is normally used to provide a transition&lt;br /&gt;
from a lower privilege state to a higher one.&lt;br /&gt;
&lt;br /&gt;
captive thread&lt;br /&gt;
a thread that has been created by a dynlink package and that stays within&lt;br /&gt;
the dynlink code, never transferring back to the client process's code;&lt;br /&gt;
also a thread that is used to call a service entry point and that will&lt;br /&gt;
never return or that will return only if some specific event occurs.&lt;br /&gt;
&lt;br /&gt;
child process&lt;br /&gt;
a process created by another process (its parent process).&lt;br /&gt;
&lt;br /&gt;
closed system&lt;br /&gt;
hardware or software design that cannot be enhanced in the field by third-&lt;br /&gt;
party suppliers.&lt;br /&gt;
&lt;br /&gt;
command subtree&lt;br /&gt;
a process and all its descendants.&lt;br /&gt;
&lt;br /&gt;
context switch&lt;br /&gt;
the act of switching the CPU from the execution of one thread to another,&lt;br /&gt;
which may belong to the same process or to a different one.&lt;br /&gt;
&lt;br /&gt;
cooked mode&lt;br /&gt;
a mode established by programs for keyboard input. In cooked mode, OS/2&lt;br /&gt;
handles the line-editing characters such as the back space.&lt;br /&gt;
&lt;br /&gt;
critical section&lt;br /&gt;
a body of code that manipulates a data resource in a non-reentrant way.&lt;br /&gt;
&lt;br /&gt;
daemon program&lt;br /&gt;
a process that performs a utility function without interaction with the&lt;br /&gt;
user. For example, the swapper process is a daemon program.&lt;br /&gt;
&lt;br /&gt;
debuggee&lt;br /&gt;
the program being debugged.&lt;br /&gt;
&lt;br /&gt;
debugger&lt;br /&gt;
a program that helps the programmer locate the source of problems found&lt;br /&gt;
during runtime testing of a program.&lt;br /&gt;
&lt;br /&gt;
device driver&lt;br /&gt;
a program that transforms I/O requests made in a standard, device-&lt;br /&gt;
independent fashion into the operations necessary to make a specific piece&lt;br /&gt;
of hardware fulfill that request.&lt;br /&gt;
&lt;br /&gt;
device monitor&lt;br /&gt;
a mechanism that allows processes to track and/or modify device data&lt;br /&gt;
streams.&lt;br /&gt;
&lt;br /&gt;
disjoint LDT space&lt;br /&gt;
the LDT selectors reserved for memory objects that are shared or that may&lt;br /&gt;
be shared among processes.&lt;br /&gt;
&lt;br /&gt;
dynamic link&lt;br /&gt;
a method of postponing the resolution of external references until loadtime&lt;br /&gt;
or runtime. A dynamic link allows the called subroutines to be packaged,&lt;br /&gt;
dist ributed, and maintained independently of their callers. OS/2 extends&lt;br /&gt;
the dynamic link (or dynlink) mechanism to serve as the primary method by&lt;br /&gt;
which all system and nonsystem services are obtained.&lt;br /&gt;
&lt;br /&gt;
dynlink&lt;br /&gt;
see dynamic link.&lt;br /&gt;
&lt;br /&gt;
dynlink library&lt;br /&gt;
a file, in a special format, that contains the binary code for a group of&lt;br /&gt;
dynamically linked subroutines.&lt;br /&gt;
&lt;br /&gt;
dynlink routine&lt;br /&gt;
see dynamic link.&lt;br /&gt;
&lt;br /&gt;
dynlink subsystem&lt;br /&gt;
a dynlink module that provides a set of services built around a resource.&lt;br /&gt;
&lt;br /&gt;
encapsulation&lt;br /&gt;
the principle of hiding the internal implementation of a program, function,&lt;br /&gt;
or service so that its clients can tell what it does but not how it does&lt;br /&gt;
it.&lt;br /&gt;
&lt;br /&gt;
environment strings&lt;br /&gt;
a series of user-definable and program-definable strings that are&lt;br /&gt;
associated with each process. The initial values of environment strings are&lt;br /&gt;
established by a process's parent.&lt;br /&gt;
&lt;br /&gt;
exitlist&lt;br /&gt;
a list of subroutines that OS/2 calls when a process has terminated. The&lt;br /&gt;
exitlist is executed after process termination but before the process is&lt;br /&gt;
actually destroyed.&lt;br /&gt;
&lt;br /&gt;
Family Applications Program Interface (Family API)&lt;br /&gt;
a standard execution environment under MS-DOS versions 2.x and 3.x and&lt;br /&gt;
OS/2. The programmer can use the Family API to create an application that&lt;br /&gt;
uses a subset of OS/2 functions (but a superset of MS-DOS 3.x functions)&lt;br /&gt;
and that runs in a binary-compatible fashion under MS-DOS versions 2.x and&lt;br /&gt;
3.x and OS/2.&lt;br /&gt;
&lt;br /&gt;
file handle&lt;br /&gt;
a binary value that represents an open file; used in all file I/O calls.&lt;br /&gt;
&lt;br /&gt;
file locking&lt;br /&gt;
an OS/2 facility that allows one program to temporarily prevent other&lt;br /&gt;
programs from reading and/or writing a particular file.&lt;br /&gt;
&lt;br /&gt;
file system name space&lt;br /&gt;
names that have the format of filenames. All such names will eventually&lt;br /&gt;
represent disk &amp;quot;files&amp;quot;--data or special. Initially, some of these names are&lt;br /&gt;
kept in internal OS/2 RAM tables and are not present on any disk volume.&lt;br /&gt;
&lt;br /&gt;
forced event&lt;br /&gt;
an event or action that is forced upon a thread or a process from an&lt;br /&gt;
external source; for example, a Ctrl-C or a DosKill command.&lt;br /&gt;
&lt;br /&gt;
foreground category&lt;br /&gt;
a classification of processes that consists of those associated with the&lt;br /&gt;
currently active screen group.&lt;br /&gt;
&lt;br /&gt;
GDT&lt;br /&gt;
see global descriptor table.&lt;br /&gt;
&lt;br /&gt;
general priority category&lt;br /&gt;
the OS/2 classification of threads that consists of three subcategories:&lt;br /&gt;
background, foreground, and interactive.&lt;br /&gt;
&lt;br /&gt;
general protection (GP) fault&lt;br /&gt;
an error that occurs when a program accesses invalid memory locations or&lt;br /&gt;
accesses valid locations in an invalid way (such as writing into read-only&lt;br /&gt;
memory areas).&lt;br /&gt;
&lt;br /&gt;
giveaway shared memory&lt;br /&gt;
a shared memory mechanism in which a process that already has access to the&lt;br /&gt;
segment can grant access to another process. Processes cannot obtain access&lt;br /&gt;
for themselves; access must be granted by another process that already has&lt;br /&gt;
access.&lt;br /&gt;
&lt;br /&gt;
global data segment&lt;br /&gt;
a data segment that is shared among all instances of a dynlink routine; in&lt;br /&gt;
other words, a single segment that is accessible to all processes that call&lt;br /&gt;
a particular dynlink routine.&lt;br /&gt;
&lt;br /&gt;
global descriptor table (GDT)&lt;br /&gt;
an element of the 80286/80386 memory management hardware. The GDT holds the&lt;br /&gt;
descriptions of as many as 4095 global segments. A global segment is&lt;br /&gt;
accessible to all processes.&lt;br /&gt;
&lt;br /&gt;
global subsystem initialization&lt;br /&gt;
a facility that allows a dynlink routine to specify that its initialize&lt;br /&gt;
entry point should be called when the dynlink package is loaded on behalf&lt;br /&gt;
of its first client.&lt;br /&gt;
&lt;br /&gt;
grandparent process&lt;br /&gt;
the parent process of a process that created a process.&lt;br /&gt;
&lt;br /&gt;
handle&lt;br /&gt;
an arbitrary integer value that OS/2 returns to a process so that the&lt;br /&gt;
process can return it to OS/2 on subsequent calls; known to programmers as&lt;br /&gt;
a magic cookie.&lt;br /&gt;
&lt;br /&gt;
hard error&lt;br /&gt;
an error that the system detects but which it cannot correct without user&lt;br /&gt;
intervention.&lt;br /&gt;
&lt;br /&gt;
hard error daemon&lt;br /&gt;
a daemon process that services hard errors. The hard error daemon may be an&lt;br /&gt;
independent process, or it may be a thread that belongs to the session&lt;br /&gt;
manager or to the presentation manager.&lt;br /&gt;
&lt;br /&gt;
huge segments&lt;br /&gt;
a software technique that allows the creation and use of pseudo segments&lt;br /&gt;
larger than 65 KB.&lt;br /&gt;
&lt;br /&gt;
installable file system (IFS)&lt;br /&gt;
a body of code that OS/2 loads at boot time and that provides the software&lt;br /&gt;
to manage a file system on a storage device, including the ability to&lt;br /&gt;
create and maintain directories, allocate disk space, and so on.&lt;br /&gt;
&lt;br /&gt;
instance data segment&lt;br /&gt;
a memory segment that holds data specific to each instance of the dynlink&lt;br /&gt;
routine.&lt;br /&gt;
&lt;br /&gt;
instance subsystem initialization&lt;br /&gt;
a service that dynlink routines can request. A dynlink routine's initialize&lt;br /&gt;
entry point is called each time a new client is linked to the routine.&lt;br /&gt;
&lt;br /&gt;
interactive category&lt;br /&gt;
a classification of processes that consists of the process currently&lt;br /&gt;
interacting with the keyboard.&lt;br /&gt;
&lt;br /&gt;
interactive program&lt;br /&gt;
a program whose function is to obey commands from a user, such as an editor&lt;br /&gt;
or a spreadsheet program. Programs such as compilers may literally interact&lt;br /&gt;
by asking for filenames and compilation options, but they are considered&lt;br /&gt;
noninteractive because their function is to compile a source program, not&lt;br /&gt;
to provide answers to user-entered commands.&lt;br /&gt;
&lt;br /&gt;
interprocess communications (IPC)&lt;br /&gt;
the ability of processes and threads to transfer data and messages among&lt;br /&gt;
themselves; used to offer services to and receive services from other&lt;br /&gt;
programs.&lt;br /&gt;
&lt;br /&gt;
interruptible block&lt;br /&gt;
a special form of a blocking operation used inside the OS/2  kernel so that&lt;br /&gt;
events such as process kill and Ctrl-C can interrupt a thread that is&lt;br /&gt;
waiting, inside OS/2, for an event.&lt;br /&gt;
&lt;br /&gt;
I/O privilege mechanism&lt;br /&gt;
a facility that allows a process to ask a device driver for direct access&lt;br /&gt;
to the device's I/O ports and any dedicated or mapped memory locations it&lt;br /&gt;
has. The I/O privilege mechanism can be used directly by an application or&lt;br /&gt;
indirectly by a dynlink package.&lt;br /&gt;
&lt;br /&gt;
IPC&lt;br /&gt;
see interprocess communications.&lt;br /&gt;
&lt;br /&gt;
KBD&lt;br /&gt;
an abbreviated name for the dynlink package that manages the keyboard&lt;br /&gt;
device. All its entry points start with Kbd.&lt;br /&gt;
&lt;br /&gt;
kernel&lt;br /&gt;
the central part of OS/2. It resides permanently in fixed memory locations&lt;br /&gt;
and executes in the privileged ring 0 state.&lt;br /&gt;
&lt;br /&gt;
LDT&lt;br /&gt;
see local descriptor table.&lt;br /&gt;
&lt;br /&gt;
loadtime dynamic linking&lt;br /&gt;
the act of connecting a client process to dynamic link libraries when the&lt;br /&gt;
process is first loaded into memory.&lt;br /&gt;
&lt;br /&gt;
local descriptor table (LDT)&lt;br /&gt;
an element of the 80286/80386 memory management hardware. The LDT holds the&lt;br /&gt;
descriptions of as many as 4095 local segments. Each process has its own&lt;br /&gt;
LDT and cannot access the LDTs of other processes.&lt;br /&gt;
&lt;br /&gt;
logical device&lt;br /&gt;
a symbolic name for a device that the user can cause to be mapped to any&lt;br /&gt;
physical (actual) device.&lt;br /&gt;
&lt;br /&gt;
logical directory&lt;br /&gt;
a symbolic name for a directory that the user can cause to be mapped to any&lt;br /&gt;
actual drive and directory.&lt;br /&gt;
&lt;br /&gt;
low priority category&lt;br /&gt;
a classification of processes that consists of processes that get CPU time&lt;br /&gt;
only when no other thread in the other categories needs it; this category&lt;br /&gt;
is lower in priority than the general priority category.&lt;br /&gt;
&lt;br /&gt;
magic cookie&lt;br /&gt;
see handle.&lt;br /&gt;
&lt;br /&gt;
memory manager&lt;br /&gt;
the section of OS/2 that allocates both physical memory and virtual memory.&lt;br /&gt;
&lt;br /&gt;
memory overcommit&lt;br /&gt;
allocating more memory to the running program than physically exists.&lt;br /&gt;
&lt;br /&gt;
memory suballocation&lt;br /&gt;
the OS/2 facility that allocates pieces of memory from within an&lt;br /&gt;
application's segment.&lt;br /&gt;
&lt;br /&gt;
MOU&lt;br /&gt;
an abbreviated name for the dynlink package that manages the mouse device.&lt;br /&gt;
All its entry points start with Mou.&lt;br /&gt;
&lt;br /&gt;
multitasking operating system&lt;br /&gt;
an operating system in which two or more programs/threads can execute&lt;br /&gt;
simultaneously.&lt;br /&gt;
&lt;br /&gt;
named pipe&lt;br /&gt;
a data storage buffer that OS/2 maintains in RAM; used for interprocess&lt;br /&gt;
communication.&lt;br /&gt;
&lt;br /&gt;
named shared memory&lt;br /&gt;
a memory segment that can be accessed simultaneously by more than one&lt;br /&gt;
process. Its name allows processes to request access to it.&lt;br /&gt;
&lt;br /&gt;
open system&lt;br /&gt;
hardware or software design that allows third-party additions and upgrades&lt;br /&gt;
in the field.&lt;br /&gt;
&lt;br /&gt;
object name buffer&lt;br /&gt;
the area in which OS/2 returns a character string if the DosExecPgm&lt;br /&gt;
function fails.&lt;br /&gt;
&lt;br /&gt;
parallel multitasking&lt;br /&gt;
the process whereby programs execute simultaneously.&lt;br /&gt;
&lt;br /&gt;
parent process&lt;br /&gt;
a process that creates another process, which is called the child process.&lt;br /&gt;
&lt;br /&gt;
physical memory&lt;br /&gt;
the RAM (Random Access Memory) physically present inside the machine.&lt;br /&gt;
&lt;br /&gt;
PID (Process Identification Number)&lt;br /&gt;
a unique code that OS/2 assigns to a process when the process is created.&lt;br /&gt;
The PID may be any value except 0.&lt;br /&gt;
&lt;br /&gt;
pipe&lt;br /&gt;
see anonymous pipe; named pipe.&lt;br /&gt;
&lt;br /&gt;
presentation manager&lt;br /&gt;
the graphical user interface for OS/2.&lt;br /&gt;
&lt;br /&gt;
priority&lt;br /&gt;
(also known as CPU priority) the numeric value assigned to each runnable&lt;br /&gt;
thread in the system. Threads with a higher priority are assigned the CPU&lt;br /&gt;
in preference to those with a lower priority.&lt;br /&gt;
&lt;br /&gt;
privilege mode&lt;br /&gt;
a special execution mode (also known as ring 0) supported by the&lt;br /&gt;
80286/80386 hardware. Code executing in this mode can execute restricted&lt;br /&gt;
instructions that are used to manipulate key system structures and tables.&lt;br /&gt;
Only the OS/2 kernel and device drivers run in this mode.&lt;br /&gt;
&lt;br /&gt;
process&lt;br /&gt;
the executing instance of a binary file. In OS/2, the terms task and&lt;br /&gt;
process are used interchangeably. A process is the unit of ownership, and&lt;br /&gt;
processes own resources such as memory, open files, dynlink libraries, and&lt;br /&gt;
semaphores.&lt;br /&gt;
&lt;br /&gt;
protect mode&lt;br /&gt;
the operating mode of the 80286 microprocessor that allows the operating&lt;br /&gt;
system to use features that protect one application from another; also&lt;br /&gt;
called protected mode.&lt;br /&gt;
&lt;br /&gt;
queue&lt;br /&gt;
an orderly list of elements waiting for processing.&lt;br /&gt;
&lt;br /&gt;
RAM semaphore&lt;br /&gt;
a kind of semaphore that is based in memory accessible to a thread; fast,&lt;br /&gt;
but with limited functionality. See system semaphore.&lt;br /&gt;
&lt;br /&gt;
raw mode&lt;br /&gt;
a mode established by programs for keyboard input. In raw mode OS/2 passes&lt;br /&gt;
to the caller each character typed immediately as it is typed. The caller&lt;br /&gt;
is responsible for handling line-editing characters such as the back space.&lt;br /&gt;
&lt;br /&gt;
real mode&lt;br /&gt;
the operating mode of the 80286 microprocessor that runs programs designed&lt;br /&gt;
for the 8086/8088 microprocessor.&lt;br /&gt;
&lt;br /&gt;
record locking&lt;br /&gt;
the mechanism that allows a process to lock a range of bytes within a file.&lt;br /&gt;
While the lock is in effect, no other process can read or write those&lt;br /&gt;
bytes.&lt;br /&gt;
&lt;br /&gt;
ring 3&lt;br /&gt;
the privilege level that is used to run applications. Code executing at&lt;br /&gt;
this level cannot modify critical system structures.&lt;br /&gt;
&lt;br /&gt;
runtime dynamic linking&lt;br /&gt;
the act of establishing a dynamic link after a process has begun execution.&lt;br /&gt;
This is done by providing OS/2 with the module and entry point names; OS/2&lt;br /&gt;
returns the address of the routine.&lt;br /&gt;
&lt;br /&gt;
scheduler&lt;br /&gt;
the part of OS/2 that decides which thread to run and how long to run it&lt;br /&gt;
before assigning the CPU to another thread; also, the part of OS/2 that&lt;br /&gt;
determines the priority value for each thread.&lt;br /&gt;
&lt;br /&gt;
screen group&lt;br /&gt;
a group of one or more processes that share (generally in a serial fashion)&lt;br /&gt;
a single logical screen and keyboard.&lt;br /&gt;
&lt;br /&gt;
semaphore&lt;br /&gt;
a software flag or signal used to coordinate the activities of two or more&lt;br /&gt;
threads; commonly used to protect a critical section.&lt;br /&gt;
&lt;br /&gt;
serial multitasking&lt;br /&gt;
the process whereby multiple programs execute, but only one at a time.&lt;br /&gt;
&lt;br /&gt;
session manager&lt;br /&gt;
a system utility that manages screen group switching. The session manager&lt;br /&gt;
is used only in the absence of the presentation manager; the presentation&lt;br /&gt;
manager replaces the session manager.&lt;br /&gt;
&lt;br /&gt;
shared memory&lt;br /&gt;
a memory segment that can be accessed simultaneously by more than one&lt;br /&gt;
process.&lt;br /&gt;
&lt;br /&gt;
signaling&lt;br /&gt;
using semaphores to notify threads that certain events or activities have&lt;br /&gt;
taken place.&lt;br /&gt;
&lt;br /&gt;
signals&lt;br /&gt;
notification mechanisms implemented in software that operate in a fashion&lt;br /&gt;
analogous to hardware interrupts.&lt;br /&gt;
&lt;br /&gt;
software tools approach&lt;br /&gt;
a design philosophy in which each program and application in a package is&lt;br /&gt;
dedicated to performing a specific task and doing that task very well. See&lt;br /&gt;
also encapsulation.&lt;br /&gt;
&lt;br /&gt;
stack frame&lt;br /&gt;
a portion of a thread's stack that contains a procedure's local variables&lt;br /&gt;
and parameters.&lt;br /&gt;
&lt;br /&gt;
static linking&lt;br /&gt;
the combining of multiple compilands into a single executable file, thereby&lt;br /&gt;
resolving undefined external references.&lt;br /&gt;
&lt;br /&gt;
single-tasking&lt;br /&gt;
a computer environment in which only one program runs at a time.&lt;br /&gt;
&lt;br /&gt;
swapping&lt;br /&gt;
the technique by which some code or data in memory is written to a disk&lt;br /&gt;
file, thus allowing the memory it was using to be reused for another&lt;br /&gt;
purpose.&lt;br /&gt;
&lt;br /&gt;
system semaphore&lt;br /&gt;
a semaphore that is implemented in OS/2's internal memory area; somewhat&lt;br /&gt;
slower than RAM semaphores, but providing more features.&lt;br /&gt;
&lt;br /&gt;
System File Table (SFT)&lt;br /&gt;
an internal OS/2 table that contains an entry for every file currently&lt;br /&gt;
open.&lt;br /&gt;
&lt;br /&gt;
task&lt;br /&gt;
see process.&lt;br /&gt;
&lt;br /&gt;
thread&lt;br /&gt;
the OS/2 mechanism that allows more than one path of execution through the&lt;br /&gt;
same instance of an application program.&lt;br /&gt;
&lt;br /&gt;
thread ID&lt;br /&gt;
the handle of a particular thread within a process.&lt;br /&gt;
&lt;br /&gt;
thread of execution&lt;br /&gt;
the passage of the CPU through the instruction sequence.&lt;br /&gt;
&lt;br /&gt;
time-critical priority&lt;br /&gt;
a classification of processes that may be interactive or noninteractive, in&lt;br /&gt;
the foreground or background screen group, which have a higher priority&lt;br /&gt;
than any non-time-critical thread in the system.&lt;br /&gt;
&lt;br /&gt;
time slice&lt;br /&gt;
the amount of execution time that the scheduler will give a thread before&lt;br /&gt;
reassigning the CPU to another thread of equal priority.&lt;br /&gt;
&lt;br /&gt;
VIO&lt;br /&gt;
an abbreviated name of the dynlink package that manages the display device.&lt;br /&gt;
All its entry points start with Vio.&lt;br /&gt;
&lt;br /&gt;
virtual memory&lt;br /&gt;
the memory space allocated to and used by a process. At the time it is&lt;br /&gt;
being referenced, the virtual memory must be present in physical memory,&lt;br /&gt;
but otherwise it may be swapped to a disk file.&lt;br /&gt;
&lt;br /&gt;
virtualization&lt;br /&gt;
the general technique of hiding a complicated actual situation behind a&lt;br /&gt;
simple, standard interface.&lt;br /&gt;
&lt;br /&gt;
writethrough&lt;br /&gt;
an option available when a file write operation is performed which&lt;br /&gt;
specifies that the normal caching mechanism is to be sidestepped and the&lt;br /&gt;
data is to be written through to the disk surface immediately.&lt;br /&gt;
&lt;br /&gt;
3x box&lt;br /&gt;
the OS/2 environment that emulates an 8086-based PC running MS-DOS versions&lt;br /&gt;
2.x or 3.x.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Index&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Symbols&lt;br /&gt;
----&lt;br /&gt;
3x box&lt;br /&gt;
80286 processor&lt;br /&gt;
   bugs in&lt;br /&gt;
   I/O access control in&lt;br /&gt;
   segmented architecture of&lt;br /&gt;
   size of segments&lt;br /&gt;
80386 processor&lt;br /&gt;
   I/O access control in&lt;br /&gt;
   key features of&lt;br /&gt;
8080 processor&lt;br /&gt;
8086/8088 processor&lt;br /&gt;
   memory limitations of&lt;br /&gt;
   real mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A&lt;br /&gt;
----&lt;br /&gt;
Abort, Retry, Ignore message (MS-DOS)&lt;br /&gt;
access lists&lt;br /&gt;
addresses&lt;br /&gt;
   invalid&lt;br /&gt;
   subroutine&lt;br /&gt;
addressing, huge model&lt;br /&gt;
address offsets&lt;br /&gt;
address space, linear&lt;br /&gt;
allocation. See also memory&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
anonymous pipes&lt;br /&gt;
API&lt;br /&gt;
   80386 processor&lt;br /&gt;
   Family&lt;br /&gt;
   memory management&lt;br /&gt;
Apple Macintosh&lt;br /&gt;
application environment. See environment&lt;br /&gt;
application mode&lt;br /&gt;
applications&lt;br /&gt;
   ``combo''&lt;br /&gt;
   command&lt;br /&gt;
   communicating between&lt;br /&gt;
   compatibility with MS-DOS&lt;br /&gt;
   designing for both OS/2 and MS-DOS&lt;br /&gt;
   device monitors and&lt;br /&gt;
   dual mode&lt;br /&gt;
   I/O-bound&lt;br /&gt;
   protecting&lt;br /&gt;
   real mode&lt;br /&gt;
   running MS-DOS&lt;br /&gt;
   time-critical&lt;br /&gt;
Applications Program Interface. See API&lt;br /&gt;
   Family&lt;br /&gt;
architecture&lt;br /&gt;
   80386 processor&lt;br /&gt;
   design concepts of OS/2&lt;br /&gt;
   device driver&lt;br /&gt;
   I/O&lt;br /&gt;
   segmented&lt;br /&gt;
arguments&lt;br /&gt;
   DosCWait&lt;br /&gt;
   DosExecPgm&lt;br /&gt;
ASCII text strings&lt;br /&gt;
ASCIIZ strings&lt;br /&gt;
asynchronous I/O&lt;br /&gt;
asynchronous processing&lt;br /&gt;
atomic operation&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
B&lt;br /&gt;
----&lt;br /&gt;
background&lt;br /&gt;
   category&lt;br /&gt;
   I/O&lt;br /&gt;
   processing&lt;br /&gt;
   threads and applications&lt;br /&gt;
base segment&lt;br /&gt;
BAT files, MS-DOS&lt;br /&gt;
BIOS entry vector, hooking the&lt;br /&gt;
blocking services&lt;br /&gt;
block mode&lt;br /&gt;
   device drivers&lt;br /&gt;
   driver algorithm&lt;br /&gt;
blocks, interruptible&lt;br /&gt;
boot process&lt;br /&gt;
boot time, installing device drivers at&lt;br /&gt;
breakthroughs, technological&lt;br /&gt;
buffer reusability&lt;br /&gt;
buffers&lt;br /&gt;
   flushing&lt;br /&gt;
   monitor&lt;br /&gt;
bugs&lt;br /&gt;
   80286 processor&lt;br /&gt;
   program&lt;br /&gt;
byte-stream mechanism&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
C&lt;br /&gt;
----&lt;br /&gt;
call gate&lt;br /&gt;
call and return sequence, OS/2&lt;br /&gt;
call statements, writing for dynamic link routines&lt;br /&gt;
call timed out error&lt;br /&gt;
capability tokens&lt;br /&gt;
captive threads&lt;br /&gt;
categories, priority&lt;br /&gt;
Central Processing Unit. See CPU&lt;br /&gt;
character mode&lt;br /&gt;
   device driver model for&lt;br /&gt;
child processes&lt;br /&gt;
   controlling&lt;br /&gt;
CHKDSK&lt;br /&gt;
circular references&lt;br /&gt;
CLI instruction&lt;br /&gt;
client processes&lt;br /&gt;
closed system&lt;br /&gt;
clusters&lt;br /&gt;
CMD.EXE&lt;br /&gt;
   I/O architecture and&lt;br /&gt;
   logical device and directory names&lt;br /&gt;
code, swapping&lt;br /&gt;
CodeView&lt;br /&gt;
command application&lt;br /&gt;
COMMAND.COM&lt;br /&gt;
command mode&lt;br /&gt;
command processes&lt;br /&gt;
command subtrees&lt;br /&gt;
   controlling&lt;br /&gt;
command threads&lt;br /&gt;
communication, interprocess&lt;br /&gt;
compatibility&lt;br /&gt;
   downward&lt;br /&gt;
   functional&lt;br /&gt;
   levels of&lt;br /&gt;
   MS-DOS&lt;br /&gt;
   name generation and&lt;br /&gt;
   VIO and presentation manager&lt;br /&gt;
compatibility box&lt;br /&gt;
compatibility issues, OS/2&lt;br /&gt;
compatibility mode&lt;br /&gt;
computers&lt;br /&gt;
   mental work and&lt;br /&gt;
   multiple CPU&lt;br /&gt;
   networked (see also networks)&lt;br /&gt;
   Von Neumann&lt;br /&gt;
CONFIG.SYS file&lt;br /&gt;
consistency, design&lt;br /&gt;
context switching&lt;br /&gt;
conventions, system&lt;br /&gt;
cooked mode&lt;br /&gt;
CP/M, compatibility with&lt;br /&gt;
CP/M-80&lt;br /&gt;
CPU See also 8086/8088 processor, 80286 processor, 80386 processor&lt;br /&gt;
   priority&lt;br /&gt;
crashes, system&lt;br /&gt;
critical sections&lt;br /&gt;
   protecting&lt;br /&gt;
   signals and&lt;br /&gt;
CS register&lt;br /&gt;
Ctrl-Break and Ctrl-C&lt;br /&gt;
customized environment&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
D&lt;br /&gt;
----&lt;br /&gt;
daemon, hard error&lt;br /&gt;
daemon interfaces, dynamic links as&lt;br /&gt;
daemon program&lt;br /&gt;
data&lt;br /&gt;
   global&lt;br /&gt;
   handling in dynamic linking&lt;br /&gt;
   instance&lt;br /&gt;
   instructions and&lt;br /&gt;
   swapped-out&lt;br /&gt;
data integrity&lt;br /&gt;
data segments, executing from&lt;br /&gt;
data streams, monitoring&lt;br /&gt;
debuggee&lt;br /&gt;
debugger&lt;br /&gt;
   system&lt;br /&gt;
debugging&lt;br /&gt;
demand loading&lt;br /&gt;
demand load segment&lt;br /&gt;
densities, disk&lt;br /&gt;
design, concepts of OS/2&lt;br /&gt;
design goals&lt;br /&gt;
DevHlp&lt;br /&gt;
device data streams&lt;br /&gt;
device drivers&lt;br /&gt;
   architecture of&lt;br /&gt;
   block mode&lt;br /&gt;
   character mode&lt;br /&gt;
   code structure&lt;br /&gt;
   definition of&lt;br /&gt;
   dynamic link pseudo&lt;br /&gt;
   OS/2 communication and&lt;br /&gt;
   programming model for&lt;br /&gt;
device independence&lt;br /&gt;
   definition of&lt;br /&gt;
device management&lt;br /&gt;
device monitors&lt;br /&gt;
device names&lt;br /&gt;
devices&lt;br /&gt;
   direct access of&lt;br /&gt;
   logical&lt;br /&gt;
device-specific code, encapsulating&lt;br /&gt;
Digital Research&lt;br /&gt;
direct device access&lt;br /&gt;
directories&lt;br /&gt;
   ISAM&lt;br /&gt;
   logical&lt;br /&gt;
   working&lt;br /&gt;
directory names&lt;br /&gt;
directory tree hierarchy&lt;br /&gt;
disjoint LDT space&lt;br /&gt;
disk data synchronization&lt;br /&gt;
disk I/O requests&lt;br /&gt;
disk seek times&lt;br /&gt;
disk space, allocation of&lt;br /&gt;
DISKCOMP&lt;br /&gt;
DISKCOPY&lt;br /&gt;
disks&lt;br /&gt;
   laser&lt;br /&gt;
   unlabeled&lt;br /&gt;
dispatcher&lt;br /&gt;
display device, manipulating the&lt;br /&gt;
display memory&lt;br /&gt;
.DLL files&lt;br /&gt;
DosAllocHuge&lt;br /&gt;
DosAllocSeg&lt;br /&gt;
DosAllocShrSeg&lt;br /&gt;
DosBufReset&lt;br /&gt;
DosCallNmPipe&lt;br /&gt;
DosCalls&lt;br /&gt;
DosClose&lt;br /&gt;
DosConnectNmPipe&lt;br /&gt;
DosCreateCSAlias&lt;br /&gt;
DosCreateSem&lt;br /&gt;
DosCreateThread&lt;br /&gt;
DosCWait&lt;br /&gt;
DosDevIOCtl&lt;br /&gt;
DosDupHandle&lt;br /&gt;
DosEnterCritSec&lt;br /&gt;
DosErrClass&lt;br /&gt;
DosError&lt;br /&gt;
DosExecPgm&lt;br /&gt;
DosExit&lt;br /&gt;
DosExitCritSec&lt;br /&gt;
DosExitList&lt;br /&gt;
DosFindFirst&lt;br /&gt;
DosFindNext&lt;br /&gt;
DosFlagProcess&lt;br /&gt;
DosFreeModule&lt;br /&gt;
DosFreeSeg&lt;br /&gt;
DosGetHugeShift&lt;br /&gt;
DosGetMachineMode&lt;br /&gt;
DosGetProcAddr&lt;br /&gt;
DosGiveSeg&lt;br /&gt;
DosHoldSignal&lt;br /&gt;
DosKill&lt;br /&gt;
DosKillProcess&lt;br /&gt;
DosLoadModule&lt;br /&gt;
DosMakeNmPipe&lt;br /&gt;
DosMakePipe&lt;br /&gt;
DosMonRead&lt;br /&gt;
DosMonReq&lt;br /&gt;
DosMonWrite&lt;br /&gt;
DosMuxSemWait&lt;br /&gt;
DosNewSize&lt;br /&gt;
DosOpen&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosPeekNmPipe&lt;br /&gt;
DosPtrace&lt;br /&gt;
DosRead&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosReadQueue&lt;br /&gt;
DosReallocHuge&lt;br /&gt;
DosResumeThread&lt;br /&gt;
DosScanEnv&lt;br /&gt;
DosSearchPath&lt;br /&gt;
DosSemClear&lt;br /&gt;
DosSemRequest&lt;br /&gt;
DosSemSet&lt;br /&gt;
DosSemWait&lt;br /&gt;
DosSetFHandState&lt;br /&gt;
DosSetPrty&lt;br /&gt;
DosSetSigHandler&lt;br /&gt;
DosSetVerify&lt;br /&gt;
DosSleep&lt;br /&gt;
DosSubAlloc&lt;br /&gt;
DosSubFrees&lt;br /&gt;
DosSuspendThread&lt;br /&gt;
DosTimerAsync&lt;br /&gt;
DosTimerStart&lt;br /&gt;
DosTimerStop&lt;br /&gt;
DosTransactNmPipe&lt;br /&gt;
DosWrite&lt;br /&gt;
   named pipes and&lt;br /&gt;
DosWriteQueue&lt;br /&gt;
downward compatibility&lt;br /&gt;
DPATH&lt;br /&gt;
drive-oriented operations&lt;br /&gt;
drives, high- and low-density&lt;br /&gt;
dual mode&lt;br /&gt;
Dynamic Data Exchange (DDE)&lt;br /&gt;
dynamic linking&lt;br /&gt;
   Family API use of&lt;br /&gt;
   loadtime&lt;br /&gt;
   runtime&lt;br /&gt;
dynamic link libraries&lt;br /&gt;
dynamic link routines, calling&lt;br /&gt;
dynamic links&lt;br /&gt;
   architectural role of&lt;br /&gt;
   circular references in&lt;br /&gt;
   details on implementing&lt;br /&gt;
   device drivers and&lt;br /&gt;
   interfaces to other processes&lt;br /&gt;
   naming&lt;br /&gt;
   side effects of&lt;br /&gt;
   using for pseudo device drivers&lt;br /&gt;
dynlink See also dynamic links&lt;br /&gt;
   routines&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
E&lt;br /&gt;
----&lt;br /&gt;
encapsulation&lt;br /&gt;
   device driver&lt;br /&gt;
entry ordinals&lt;br /&gt;
entry point name&lt;br /&gt;
environment&lt;br /&gt;
   customized&lt;br /&gt;
   dual mode&lt;br /&gt;
   protected&lt;br /&gt;
   single-tasking&lt;br /&gt;
   single-tasking versus multitasking&lt;br /&gt;
   stable&lt;br /&gt;
   stand-alone&lt;br /&gt;
   virtualizing the&lt;br /&gt;
environment block&lt;br /&gt;
environment strings&lt;br /&gt;
EnvPointer&lt;br /&gt;
EnvString&lt;br /&gt;
error codes, compatibility mode&lt;br /&gt;
errors&lt;br /&gt;
   general protection fault&lt;br /&gt;
   hard&lt;br /&gt;
   localization of&lt;br /&gt;
   program&lt;br /&gt;
   timed out&lt;br /&gt;
.EXE files&lt;br /&gt;
   executing&lt;br /&gt;
   Family API&lt;br /&gt;
EXEC function (MS-DOS)&lt;br /&gt;
execute threads&lt;br /&gt;
executing programs&lt;br /&gt;
execution speed&lt;br /&gt;
exitlist&lt;br /&gt;
expandability&lt;br /&gt;
extended file attributes&lt;br /&gt;
extended partitioning&lt;br /&gt;
extension, filename&lt;br /&gt;
external name, dynamic link&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
F&lt;br /&gt;
----&lt;br /&gt;
Family API&lt;br /&gt;
   drawbacks of&lt;br /&gt;
Family Applications Program Interface (Family API)&lt;br /&gt;
far return instruction&lt;br /&gt;
fault, memory not present&lt;br /&gt;
fault errors, general protection&lt;br /&gt;
faults, GP&lt;br /&gt;
features&lt;br /&gt;
   additional&lt;br /&gt;
   introducing new operating system&lt;br /&gt;
file&lt;br /&gt;
   allocation, improving&lt;br /&gt;
   attributes, extended&lt;br /&gt;
   handles&lt;br /&gt;
   I/O&lt;br /&gt;
   limits, floppy disk&lt;br /&gt;
   locking&lt;br /&gt;
   protection&lt;br /&gt;
   seek position&lt;br /&gt;
   sharing&lt;br /&gt;
   system&lt;br /&gt;
File Allocation Table&lt;br /&gt;
filenames, OS/2 and MS-DOS&lt;br /&gt;
files&lt;br /&gt;
   .DLL&lt;br /&gt;
   .EXE&lt;br /&gt;
   linking&lt;br /&gt;
   naming&lt;br /&gt;
   .OBJ&lt;br /&gt;
file system&lt;br /&gt;
   future of&lt;br /&gt;
   hierarchical&lt;br /&gt;
   installable&lt;br /&gt;
file system name space&lt;br /&gt;
   named pipes and&lt;br /&gt;
file utilization&lt;br /&gt;
FIND utility program&lt;br /&gt;
flags register&lt;br /&gt;
flat model, 80386 processor&lt;br /&gt;
flexibility, maximum&lt;br /&gt;
flush operation, buffer&lt;br /&gt;
forced events&lt;br /&gt;
foreground&lt;br /&gt;
   category&lt;br /&gt;
   processing&lt;br /&gt;
   threads and applications&lt;br /&gt;
fprintf&lt;br /&gt;
fragmentation&lt;br /&gt;
   disk&lt;br /&gt;
   internal&lt;br /&gt;
functions&lt;br /&gt;
   addition of&lt;br /&gt;
   resident&lt;br /&gt;
future versions of OS/2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
G&lt;br /&gt;
----&lt;br /&gt;
garbage collection&lt;br /&gt;
garbage handles&lt;br /&gt;
general failure error&lt;br /&gt;
general priority category&lt;br /&gt;
general protection fault (GP fault)&lt;br /&gt;
   errors&lt;br /&gt;
GetDosVar&lt;br /&gt;
giveaway shared memory&lt;br /&gt;
global data&lt;br /&gt;
global data segments&lt;br /&gt;
Global Descriptor Table (GDT)&lt;br /&gt;
global subsystem initialization&lt;br /&gt;
glossary&lt;br /&gt;
goals&lt;br /&gt;
   design&lt;br /&gt;
   OS/2&lt;br /&gt;
GP faults&lt;br /&gt;
grandparent processes&lt;br /&gt;
graphical user interface&lt;br /&gt;
   standard&lt;br /&gt;
graphics, VIO and&lt;br /&gt;
graphics devices&lt;br /&gt;
graphics drivers, device-independent&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
H&lt;br /&gt;
----&lt;br /&gt;
handles&lt;br /&gt;
   closing&lt;br /&gt;
   closing with dynamic links&lt;br /&gt;
   duplicated and inherited&lt;br /&gt;
   garbage&lt;br /&gt;
   semaphore&lt;br /&gt;
hard error daemon&lt;br /&gt;
hard errors&lt;br /&gt;
   handling in real mode&lt;br /&gt;
hardware, nonstandard&lt;br /&gt;
hardware devices. See devices&lt;br /&gt;
hardware interrupts&lt;br /&gt;
   device drivers and&lt;br /&gt;
hardware-specific interfaces&lt;br /&gt;
Hayes modems&lt;br /&gt;
heap algorithm&lt;br /&gt;
heap objects&lt;br /&gt;
Hercules Graphics Card&lt;br /&gt;
hierarchical file system&lt;br /&gt;
hooking the keyboard vector&lt;br /&gt;
huge memory&lt;br /&gt;
huge model addressing&lt;br /&gt;
huge segments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I&lt;br /&gt;
----&lt;br /&gt;
IBM PC/AT&lt;br /&gt;
INCLUDE&lt;br /&gt;
Independent Software Vendors (ISVs)&lt;br /&gt;
industrial revolution, second&lt;br /&gt;
infosegs&lt;br /&gt;
inheritance&lt;br /&gt;
INIT&lt;br /&gt;
initialization&lt;br /&gt;
   device driver&lt;br /&gt;
   global subsystem&lt;br /&gt;
   instance subsystem&lt;br /&gt;
initialization entry points, dynamic link&lt;br /&gt;
input/output. See I/O&lt;br /&gt;
installable file system (IFS)&lt;br /&gt;
instance data&lt;br /&gt;
   segment&lt;br /&gt;
instance subsystem initialization&lt;br /&gt;
instructions&lt;br /&gt;
   sequence of&lt;br /&gt;
insufficient memory&lt;br /&gt;
INT 21&lt;br /&gt;
INT 2F multiplex function&lt;br /&gt;
integrated applications&lt;br /&gt;
integrity, data&lt;br /&gt;
Intel. See also 8086/8088 processor, 80286 processor, 80386 processor&lt;br /&gt;
   80286 processor&lt;br /&gt;
   80386 processor&lt;br /&gt;
   8080 processor&lt;br /&gt;
   8086/8088 processor&lt;br /&gt;
interactive&lt;br /&gt;
   category&lt;br /&gt;
   processes&lt;br /&gt;
   programs&lt;br /&gt;
interface, user&lt;br /&gt;
interlocking&lt;br /&gt;
internal fragmentation&lt;br /&gt;
interprocess communication (IPC)&lt;br /&gt;
interrupt handling code&lt;br /&gt;
interruptible block&lt;br /&gt;
interrupts&lt;br /&gt;
   3x box emulation&lt;br /&gt;
   hardware&lt;br /&gt;
   signals and&lt;br /&gt;
interrupt service routine, device driver&lt;br /&gt;
interrupt-time thread&lt;br /&gt;
interrupt vectors&lt;br /&gt;
   hooking&lt;br /&gt;
I/O&lt;br /&gt;
   architecture&lt;br /&gt;
   asynchronous&lt;br /&gt;
   background&lt;br /&gt;
   disk requests&lt;br /&gt;
   efficiency&lt;br /&gt;
   file&lt;br /&gt;
   port access&lt;br /&gt;
   privilege mechanism&lt;br /&gt;
   requesting an operation&lt;br /&gt;
   signals and&lt;br /&gt;
   video&lt;br /&gt;
I/O-bound applications&lt;br /&gt;
IOCTL call&lt;br /&gt;
IP register&lt;br /&gt;
IPC See also interprocess communication&lt;br /&gt;
   combining forms of&lt;br /&gt;
   using with dynamic links&lt;br /&gt;
IRET instruction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
K&lt;br /&gt;
----&lt;br /&gt;
KBD&lt;br /&gt;
kernel&lt;br /&gt;
   interfacing with dynamic links&lt;br /&gt;
   mode&lt;br /&gt;
keyboard, processes using the&lt;br /&gt;
keyboard mode, cooked and raw&lt;br /&gt;
keyboard vector, hooking the&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&lt;br /&gt;
----&lt;br /&gt;
label names, volume&lt;br /&gt;
large disk support&lt;br /&gt;
LAR instruction&lt;br /&gt;
latency, rotational&lt;br /&gt;
Least Recently Used (LRU) scheme&lt;br /&gt;
levels, compatibility&lt;br /&gt;
LIB&lt;br /&gt;
libraries&lt;br /&gt;
   dynamic link&lt;br /&gt;
   sharing dynamic link&lt;br /&gt;
   subroutine and runtime&lt;br /&gt;
linear address space&lt;br /&gt;
linking&lt;br /&gt;
   dynamic&lt;br /&gt;
   static&lt;br /&gt;
loadtime dynamic linking&lt;br /&gt;
local area networks. See networks&lt;br /&gt;
Local Descriptor Table (LDT)&lt;br /&gt;
locality of reference&lt;br /&gt;
localization of errors&lt;br /&gt;
local variables&lt;br /&gt;
locking, file. See file and record locking&lt;br /&gt;
logical&lt;br /&gt;
   device and directory name facility&lt;br /&gt;
   devices&lt;br /&gt;
   directories&lt;br /&gt;
   disks, partitioning&lt;br /&gt;
low priority category&lt;br /&gt;
LSL instruction&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
M&lt;br /&gt;
----&lt;br /&gt;
magic cookie&lt;br /&gt;
mass marketing, software and&lt;br /&gt;
media volume management&lt;br /&gt;
memory&lt;br /&gt;
   display&lt;br /&gt;
   huge&lt;br /&gt;
   insufficient&lt;br /&gt;
   layout of system&lt;br /&gt;
   limitations of in 8088 processor&lt;br /&gt;
   named shared&lt;br /&gt;
   physical&lt;br /&gt;
   protection&lt;br /&gt;
   shared&lt;br /&gt;
   shared giveaway&lt;br /&gt;
   swapping&lt;br /&gt;
   swapping segments in&lt;br /&gt;
   swapped-out&lt;br /&gt;
   utilization&lt;br /&gt;
   video&lt;br /&gt;
   virtual&lt;br /&gt;
memory management&lt;br /&gt;
   API&lt;br /&gt;
   hardware&lt;br /&gt;
memory manager&lt;br /&gt;
   definition of&lt;br /&gt;
memory not present fault&lt;br /&gt;
memory objects, tracking&lt;br /&gt;
memory overcommit&lt;br /&gt;
memory segments, allocating&lt;br /&gt;
memory suballocation&lt;br /&gt;
memory unit&lt;br /&gt;
mental work, mechanizing&lt;br /&gt;
message mode&lt;br /&gt;
Microsoft, vision of&lt;br /&gt;
Microsoft Macro Assembler (MASM)&lt;br /&gt;
model, architectural&lt;br /&gt;
modes&lt;br /&gt;
   incompatible&lt;br /&gt;
   keyboard&lt;br /&gt;
modular tools&lt;br /&gt;
module name&lt;br /&gt;
monitors, device&lt;br /&gt;
MOU&lt;br /&gt;
MS-DOS&lt;br /&gt;
   compatibility with&lt;br /&gt;
   environment list&lt;br /&gt;
   expandability of&lt;br /&gt;
   filenames in&lt;br /&gt;
   history of&lt;br /&gt;
   memory allocation in&lt;br /&gt;
   running applications in OS/2&lt;br /&gt;
   version 1.0&lt;br /&gt;
   version 2.0&lt;br /&gt;
   version 3.0&lt;br /&gt;
   version 4.0&lt;br /&gt;
   version 5.0&lt;br /&gt;
   versions of&lt;br /&gt;
multitasking&lt;br /&gt;
   data integrity and&lt;br /&gt;
   definition of&lt;br /&gt;
   device drivers and&lt;br /&gt;
   full&lt;br /&gt;
   MS-DOS version&lt;br /&gt;
   parallel&lt;br /&gt;
   serial&lt;br /&gt;
multiuser systems, thrashing and&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
N&lt;br /&gt;
----&lt;br /&gt;
named pipes&lt;br /&gt;
   multiple instances of&lt;br /&gt;
named shared memory&lt;br /&gt;
name generation, compatibility and&lt;br /&gt;
names, dynamic link&lt;br /&gt;
name set&lt;br /&gt;
network piggybacking&lt;br /&gt;
networks&lt;br /&gt;
   access to&lt;br /&gt;
   compatibility on&lt;br /&gt;
   file protection on&lt;br /&gt;
   importance of security on&lt;br /&gt;
network virtual circuit&lt;br /&gt;
NUMADD program&lt;br /&gt;
NUMARITH application&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
O&lt;br /&gt;
----&lt;br /&gt;
.OBJ files&lt;br /&gt;
obj namebuf&lt;br /&gt;
object name buffer&lt;br /&gt;
objects&lt;br /&gt;
   defining&lt;br /&gt;
   file system name space and&lt;br /&gt;
office automation&lt;br /&gt;
   objective of&lt;br /&gt;
   operating system for&lt;br /&gt;
offset registers&lt;br /&gt;
OPEN function&lt;br /&gt;
open system&lt;br /&gt;
operating systems&lt;br /&gt;
   multitasking&lt;br /&gt;
   other&lt;br /&gt;
ordinals, entry&lt;br /&gt;
orphaned semaphores&lt;br /&gt;
overlays, code&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
P&lt;br /&gt;
----&lt;br /&gt;
packet, request&lt;br /&gt;
paged virtual memory&lt;br /&gt;
paperless office&lt;br /&gt;
parallel multitasking&lt;br /&gt;
parent processes&lt;br /&gt;
partitioning, extended&lt;br /&gt;
passwords, file&lt;br /&gt;
Paterson, Tim&lt;br /&gt;
PATH&lt;br /&gt;
pathnames&lt;br /&gt;
peripherals, high-bandwidth&lt;br /&gt;
permissions&lt;br /&gt;
physical memory&lt;br /&gt;
PID&lt;br /&gt;
piggybacking&lt;br /&gt;
pipes&lt;br /&gt;
   anonymous&lt;br /&gt;
   named&lt;br /&gt;
pointer, seek&lt;br /&gt;
preemptive scheduler&lt;br /&gt;
presentation manager&lt;br /&gt;
   choosing between VIO and&lt;br /&gt;
   DDE and&lt;br /&gt;
priority&lt;br /&gt;
   categories of&lt;br /&gt;
   time-critical&lt;br /&gt;
priority scheduler&lt;br /&gt;
   semaphore&lt;br /&gt;
privilege mechanism, I/O&lt;br /&gt;
privilege mode&lt;br /&gt;
privilege transition&lt;br /&gt;
process termination signal handler&lt;br /&gt;
processes&lt;br /&gt;
   calling from OS/2&lt;br /&gt;
   child&lt;br /&gt;
   client&lt;br /&gt;
   command&lt;br /&gt;
   daemon&lt;br /&gt;
   dynamic linking and&lt;br /&gt;
   foreground and background&lt;br /&gt;
   grandparent&lt;br /&gt;
   interactive&lt;br /&gt;
   interfacing with dynamic links&lt;br /&gt;
   parent&lt;br /&gt;
   problems with multiple&lt;br /&gt;
   threads and&lt;br /&gt;
processing&lt;br /&gt;
   asynchronous&lt;br /&gt;
   foreground and background&lt;br /&gt;
processing unit&lt;br /&gt;
process tree&lt;br /&gt;
programming model, device drivers&lt;br /&gt;
programs&lt;br /&gt;
   debugger&lt;br /&gt;
   executing&lt;br /&gt;
   interactive&lt;br /&gt;
   running on OS/2&lt;br /&gt;
program termination signal&lt;br /&gt;
PROMPT&lt;br /&gt;
protected environment&lt;br /&gt;
protection&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
   model&lt;br /&gt;
   side-effects&lt;br /&gt;
protect mode&lt;br /&gt;
   huge model addressing in&lt;br /&gt;
   real mode versus&lt;br /&gt;
protocol, DDE&lt;br /&gt;
pseudo interrupts&lt;br /&gt;
Ptrace&lt;br /&gt;
pure segment&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Q, R&lt;br /&gt;
----&lt;br /&gt;
queues&lt;br /&gt;
RAM See also memory&lt;br /&gt;
   available&lt;br /&gt;
   semaphores&lt;br /&gt;
Random Access Memory. See RAM&lt;br /&gt;
random selector values&lt;br /&gt;
RAS information&lt;br /&gt;
raw mode&lt;br /&gt;
real mode&lt;br /&gt;
   80386 processor&lt;br /&gt;
   8086 processor&lt;br /&gt;
   applications&lt;br /&gt;
   compatibility box&lt;br /&gt;
   device drivers in&lt;br /&gt;
   hard errors in&lt;br /&gt;
   huge model addressing in&lt;br /&gt;
   protect mode versus&lt;br /&gt;
   screen group&lt;br /&gt;
   swapping in&lt;br /&gt;
real time, tracking passage of&lt;br /&gt;
record locking&lt;br /&gt;
redirector code (MS-DOS)&lt;br /&gt;
reference locality&lt;br /&gt;
registers&lt;br /&gt;
   offset&lt;br /&gt;
   segment&lt;br /&gt;
   signals and&lt;br /&gt;
Reliability, Availability, and Serviceability (RAS)&lt;br /&gt;
religion, OS/2&lt;br /&gt;
remote procedure call&lt;br /&gt;
request packet&lt;br /&gt;
resident functions&lt;br /&gt;
resources, manipulating&lt;br /&gt;
return()&lt;br /&gt;
ring 3&lt;br /&gt;
ring transition&lt;br /&gt;
ROM BIOS&lt;br /&gt;
root directory&lt;br /&gt;
rotational latency&lt;br /&gt;
runtime dynamic linking&lt;br /&gt;
runtime library&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
S&lt;br /&gt;
----&lt;br /&gt;
scheduler&lt;br /&gt;
   preemptive&lt;br /&gt;
   priority&lt;br /&gt;
   semaphore&lt;br /&gt;
SCP-DOS&lt;br /&gt;
screen device, handle for&lt;br /&gt;
screen groups&lt;br /&gt;
   multiple&lt;br /&gt;
   using in 3x box&lt;br /&gt;
screen images, manipulating&lt;br /&gt;
screen-save operation&lt;br /&gt;
screen switch&lt;br /&gt;
screen updates, requirements for&lt;br /&gt;
Seattle Computer Products&lt;br /&gt;
sector aligned calls&lt;br /&gt;
sectors&lt;br /&gt;
   blocks of&lt;br /&gt;
security, dynamic link&lt;br /&gt;
seek pointer&lt;br /&gt;
segment arithmetic&lt;br /&gt;
segment fault&lt;br /&gt;
segments&lt;br /&gt;
   80286 architecture&lt;br /&gt;
   80386 architecture&lt;br /&gt;
   data&lt;br /&gt;
   dynamic link&lt;br /&gt;
   executing from data&lt;br /&gt;
   global data&lt;br /&gt;
   huge&lt;br /&gt;
   internally shared&lt;br /&gt;
   nonswappable&lt;br /&gt;
   pure&lt;br /&gt;
   sharing&lt;br /&gt;
   stack&lt;br /&gt;
   status and information&lt;br /&gt;
segment selectors&lt;br /&gt;
segment swapping&lt;br /&gt;
semaphore handles&lt;br /&gt;
semaphores&lt;br /&gt;
   data integrity and&lt;br /&gt;
   file system name space and&lt;br /&gt;
   orphaned&lt;br /&gt;
   RAM&lt;br /&gt;
   recovering&lt;br /&gt;
   scheduling&lt;br /&gt;
   system&lt;br /&gt;
serial multitasking&lt;br /&gt;
session manager&lt;br /&gt;
SetSignalHandler&lt;br /&gt;
shared memory&lt;br /&gt;
   giveaway&lt;br /&gt;
   named&lt;br /&gt;
sharing segments&lt;br /&gt;
side effects&lt;br /&gt;
   controlling&lt;br /&gt;
   dynamic link&lt;br /&gt;
   protection&lt;br /&gt;
signal handler address&lt;br /&gt;
signal handlers&lt;br /&gt;
signaling&lt;br /&gt;
signals&lt;br /&gt;
   holding&lt;br /&gt;
SIGTERM signal&lt;br /&gt;
single-tasking&lt;br /&gt;
single-tasking environment, containing errors in&lt;br /&gt;
software design, OS/2 concepts of&lt;br /&gt;
software tools approach&lt;br /&gt;
speed execution&lt;br /&gt;
stable environment&lt;br /&gt;
stack frames&lt;br /&gt;
stacks, thread&lt;br /&gt;
stack segments&lt;br /&gt;
standard error. See STDERR&lt;br /&gt;
standard file handles&lt;br /&gt;
standard input. See STDIN&lt;br /&gt;
standard output. See STDOUT&lt;br /&gt;
standards, software&lt;br /&gt;
static data segments, adding&lt;br /&gt;
static links&lt;br /&gt;
status and information, segment&lt;br /&gt;
STDERR&lt;br /&gt;
STDIN&lt;br /&gt;
   compatibility with presentation manager&lt;br /&gt;
STDIN/STDOUT mechanism, I/O&lt;br /&gt;
STDOUT&lt;br /&gt;
   compatibility with presentation manager&lt;br /&gt;
strings, environment&lt;br /&gt;
suballocation, memory&lt;br /&gt;
subroutine library&lt;br /&gt;
subroutines, dynamic link packages as&lt;br /&gt;
subsystems&lt;br /&gt;
   dynamic link&lt;br /&gt;
   special support&lt;br /&gt;
subtask model&lt;br /&gt;
subtrees, command&lt;br /&gt;
   controlling&lt;br /&gt;
swapped-out memory&lt;br /&gt;
SWAPPER.DAT&lt;br /&gt;
swapping&lt;br /&gt;
   segment&lt;br /&gt;
system&lt;br /&gt;
   conventions&lt;br /&gt;
   crashes&lt;br /&gt;
   diagnosis&lt;br /&gt;
   File Table (SFT)&lt;br /&gt;
   memory layout&lt;br /&gt;
   security, debuggers and&lt;br /&gt;
   semaphores&lt;br /&gt;
system, hanging the&lt;br /&gt;
systems&lt;br /&gt;
   closed and open&lt;br /&gt;
   file&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
T&lt;br /&gt;
----&lt;br /&gt;
task See also processes&lt;br /&gt;
task-time thread&lt;br /&gt;
technological breakthroughs&lt;br /&gt;
TEMP&lt;br /&gt;
terminate and stay resident mechanism&lt;br /&gt;
thrashing&lt;br /&gt;
thread 1&lt;br /&gt;
thread death&lt;br /&gt;
thread of execution&lt;br /&gt;
thread ID&lt;br /&gt;
threads&lt;br /&gt;
   background&lt;br /&gt;
   captive&lt;br /&gt;
   collisions of&lt;br /&gt;
   dynamic linking and&lt;br /&gt;
   foreground and background processing with&lt;br /&gt;
   foreground and interactive&lt;br /&gt;
   from different processes&lt;br /&gt;
   I/O-bound&lt;br /&gt;
   interrupt-time&lt;br /&gt;
   low priority&lt;br /&gt;
   multiple&lt;br /&gt;
   organizing programs using&lt;br /&gt;
   performance characteristics of&lt;br /&gt;
   priority categories of&lt;br /&gt;
   problems with multiple&lt;br /&gt;
   switching the CPU among&lt;br /&gt;
   task-time&lt;br /&gt;
   working sets and&lt;br /&gt;
thread stacks&lt;br /&gt;
throughput balancing&lt;br /&gt;
time-critical priority category&lt;br /&gt;
time intervals&lt;br /&gt;
timer services&lt;br /&gt;
time slice&lt;br /&gt;
timing, thread&lt;br /&gt;
tools, software&lt;br /&gt;
TOPS-10&lt;br /&gt;
TREE&lt;br /&gt;
tree, process&lt;br /&gt;
tree-structured file system&lt;br /&gt;
Trojan programs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
U&lt;br /&gt;
----&lt;br /&gt;
UNIX&lt;br /&gt;
   naming conventions in&lt;br /&gt;
   ptrace facility&lt;br /&gt;
upper- and lowercase&lt;br /&gt;
upward compatibility&lt;br /&gt;
user interface&lt;br /&gt;
   graphical&lt;br /&gt;
   presentation manager&lt;br /&gt;
   VIO&lt;br /&gt;
utilization&lt;br /&gt;
   file&lt;br /&gt;
   memory&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
V&lt;br /&gt;
----&lt;br /&gt;
variables, local&lt;br /&gt;
versions, future OS/2&lt;br /&gt;
video hardware, accessing&lt;br /&gt;
VIO,&lt;br /&gt;
   choosing between presentation manager and&lt;br /&gt;
   dynamic link entry points&lt;br /&gt;
   graphics under&lt;br /&gt;
   subsystem&lt;br /&gt;
   user interface&lt;br /&gt;
VioGetBuf&lt;br /&gt;
VioModeWait&lt;br /&gt;
VioSavRedrawWait&lt;br /&gt;
VioScrLock&lt;br /&gt;
virtual circuit, network&lt;br /&gt;
virtual display device&lt;br /&gt;
virtualization&lt;br /&gt;
   device&lt;br /&gt;
virtualizing the environment&lt;br /&gt;
virtual memory&lt;br /&gt;
   concepts of&lt;br /&gt;
   paged&lt;br /&gt;
virtual real mode (80386)&lt;br /&gt;
volume, disk&lt;br /&gt;
volume ID&lt;br /&gt;
volume-oriented operations&lt;br /&gt;
Von Neumann, John&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
W&lt;br /&gt;
----&lt;br /&gt;
windowing interface&lt;br /&gt;
working directories&lt;br /&gt;
working set&lt;br /&gt;
WORM drives&lt;br /&gt;
writethrough&lt;br /&gt;
WYSIWYG&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
X&lt;br /&gt;
----&lt;br /&gt;
XENIX&lt;br /&gt;
&lt;br /&gt;
Z&lt;br /&gt;
----&lt;br /&gt;
zero-terminated strings&lt;br /&gt;
&lt;br /&gt;
[[Category: OS/2]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=IBM_OS/2_SE_1.0_announcement&amp;diff=36396</id>
		<title>IBM OS/2 SE 1.0 announcement</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=IBM_OS/2_SE_1.0_announcement&amp;diff=36396"/>
				<updated>2025-06-13T12:03:21Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: Ak120 moved page IBM OS2 1.0 announcement to IBM OS/2 SE 1.0 announcement&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;IBM Operating System/2 Standard Edition&amp;quot;,&amp;quot;IBM Announce&amp;quot;,04-2-1987&lt;br /&gt;
&lt;br /&gt;
The IBM Operating System/2(TM) Standard Edition&lt;br /&gt;
&lt;br /&gt;
The IBM Operating System/2 Standard Edition complements new hardware,&lt;br /&gt;
provides significant new function, offers enhanced ease-of-use, and&lt;br /&gt;
provides a platform for future application growth.  IBM Operating&lt;br /&gt;
System/2 supports large memory, multiple applications, graphics and&lt;br /&gt;
windowing capability, and IBM Disk Operating System Version 3.30&lt;br /&gt;
compatibility.  IBM Operating System/2 Standard Edition will be&lt;br /&gt;
released in two stages:  Version 1.0 contains all the described&lt;br /&gt;
function except graphics and windowing, which is provided in Version&lt;br /&gt;
1.1.  IBM Operating System/2 supports the following advanced IBM&lt;br /&gt;
Personal Computer systems:  IBM Personal System/2, Models 50, 60 and&lt;br /&gt;
80, Personal Computer AT(R) 5170 Models 099, 239, 319 and 339, and PC&lt;br /&gt;
XT(TM) 5162 Model 286.&lt;br /&gt;
&lt;br /&gt;
(TM)  Trademark of the International Business Machines Corporation.&lt;br /&gt;
&lt;br /&gt;
(R)  Registered trademark of the International Business Machines&lt;br /&gt;
Corporation.&lt;br /&gt;
&lt;br /&gt;
General Availability:&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 Standard Edition -- First quarter 1988&lt;br /&gt;
      Version 1.0&lt;br /&gt;
&lt;br /&gt;
The availability of the IBM Operating System/2 Standard Edition&lt;br /&gt;
Version 1.1 will be announced fourth quarter 1987.&lt;br /&gt;
&lt;br /&gt;
Program Number:&lt;br /&gt;
&lt;br /&gt;
  6280196  IBM Operating System/2&lt;br /&gt;
            Standard Edition Version 1.0&lt;br /&gt;
            for 3.5-inch (1.44MB) Media&lt;br /&gt;
&lt;br /&gt;
  6280198  IBM Operating System/2&lt;br /&gt;
            Standard Edition Version 1.0&lt;br /&gt;
            for 5.25-inch (1.2MB) Media&lt;br /&gt;
&lt;br /&gt;
  6280194  IBM Operating System/2&lt;br /&gt;
            Standard Edition Version 1.1&lt;br /&gt;
            for 3.5-inch (1.44MB) Media&lt;br /&gt;
&lt;br /&gt;
  6280195  IBM Operating System/2&lt;br /&gt;
            Standard Edition Version 1.1&lt;br /&gt;
            for 5.25-inch (1.2MB) Media&lt;br /&gt;
&lt;br /&gt;
  6280197  IBM Operating System/2&lt;br /&gt;
            Standard Edition Transitional&lt;br /&gt;
            Offering to Version 1.0&lt;br /&gt;
            for 3.5-inch (1.44MB) Media&lt;br /&gt;
&lt;br /&gt;
  6280199  IBM Operating System/2&lt;br /&gt;
            Standard Edition Transitional&lt;br /&gt;
            Offering to Version 1.0&lt;br /&gt;
           for 5.25-inch (1.2MB) Media&lt;br /&gt;
&lt;br /&gt;
HIGHLIGHTS&lt;br /&gt;
&lt;br /&gt;
o  16MB Addressable Random Access Memory Support&lt;br /&gt;
&lt;br /&gt;
o  Concurrent Processing of Multiple Applications&lt;br /&gt;
&lt;br /&gt;
o  High Level Programming Interface&lt;br /&gt;
&lt;br /&gt;
o  Presentation Manager&lt;br /&gt;
&lt;br /&gt;
o  Enhanced Ease of Use Facilities&lt;br /&gt;
&lt;br /&gt;
o  Compatibility with IBM DOS, Version 3.30&lt;br /&gt;
&lt;br /&gt;
o  Systems Application Architecture&lt;br /&gt;
&lt;br /&gt;
o  New Service and Warranty&lt;br /&gt;
&lt;br /&gt;
DESCRIPTION&lt;br /&gt;
&lt;br /&gt;
Note:  The IBM Operating System/2 Standard Edition is usually&lt;br /&gt;
referred to as the IBM Operating System/2.&lt;br /&gt;
&lt;br /&gt;
16MB Addressable Random Access Memory Support&lt;br /&gt;
&lt;br /&gt;
Operating System/2 supports up to 16MB of addressable random access&lt;br /&gt;
memory.  This enables application developers to take full advantage&lt;br /&gt;
of memory beyond 640KB for applications and data.  End users will&lt;br /&gt;
have the benefit of larger and functionally richer applications that&lt;br /&gt;
can process larger amounts of data such as spreadsheets and large&lt;br /&gt;
documents.&lt;br /&gt;
&lt;br /&gt;
Operating System/2 implements virtual memory through segment&lt;br /&gt;
swapping.  An Operating System/2 application program can be larger&lt;br /&gt;
than available real memory.  Actual size is dependent on program&lt;br /&gt;
characteristics and the physical capacity of the system.  Operating&lt;br /&gt;
System/2 manages physical memory by swapping memory segments to a&lt;br /&gt;
disk file as required.&lt;br /&gt;
&lt;br /&gt;
Concurrent Processing of Multiple Applications&lt;br /&gt;
&lt;br /&gt;
New applications written to take advantage of the function provided&lt;br /&gt;
in Operating System/2 may be run and displayed concurrently for the&lt;br /&gt;
convenience and increased productivity of the end user.  Switching&lt;br /&gt;
between applications is fast and simple.  Productivity increases&lt;br /&gt;
because time previously spent starting and stopping, entering and&lt;br /&gt;
exiting applications in a single application environment, can now be&lt;br /&gt;
spent more productively actually processing data.  Applications can&lt;br /&gt;
also be written without knowledge of what other applications will&lt;br /&gt;
coexist.&lt;br /&gt;
&lt;br /&gt;
High Level Programming Interface&lt;br /&gt;
&lt;br /&gt;
IBM is addressing application development productivity and future&lt;br /&gt;
compatibility by providing application developers with a high level&lt;br /&gt;
CALL interface to Operating System/2.  Applications written to this&lt;br /&gt;
interface will be compatible with successive versions of Operating&lt;br /&gt;
System/2, and also with successive Personal System/2 systems.&lt;br /&gt;
Applications can also take advantage of a high level of device&lt;br /&gt;
independence.  This assists in upgrading to new IBM Personal System/2&lt;br /&gt;
systems, or to new versions of Operating System/2.&lt;br /&gt;
&lt;br /&gt;
Application developers may choose to use a subset of the Operating&lt;br /&gt;
System/2 CALL interface which allows the same program to run under&lt;br /&gt;
DOS 3.30 and under Operating System/2.&lt;br /&gt;
&lt;br /&gt;
Presentation Manager&lt;br /&gt;
&lt;br /&gt;
The Presentation Manager provides windowing and graphics functions&lt;br /&gt;
along with an application enabling interface to improve productivity&lt;br /&gt;
when developing applications that support the Common User Access&lt;br /&gt;
definitions of the Systems Application Architecture.&lt;br /&gt;
&lt;br /&gt;
Windowing allows multiple applications to be viewed by the end user&lt;br /&gt;
at the same time.  Each application can support multiple windows.&lt;br /&gt;
The user can control window size and position, and an application can&lt;br /&gt;
create or delete windows.  A clipboard function is provided to enable&lt;br /&gt;
the user or application to extract data from one window and move it&lt;br /&gt;
to another window, or from one application to another.&lt;br /&gt;
&lt;br /&gt;
Graphics support enables the development of a broad range of&lt;br /&gt;
applications which take advantage of the supported all points&lt;br /&gt;
addressable (APA) devices.  There is a wide range of vector graphics,&lt;br /&gt;
raster operations, and extensive font support, including multiple&lt;br /&gt;
font styles and sizes.  Graphics orders can be stored which enables&lt;br /&gt;
the application to manage the picture data, and facilitates fast&lt;br /&gt;
redrawing of pictures.&lt;br /&gt;
&lt;br /&gt;
Enhanced Ease of Use Facilities&lt;br /&gt;
&lt;br /&gt;
Enhanced ease of use facilities in Operating System/2 make it easier&lt;br /&gt;
for the novice to learn and simpler for both the novice and the&lt;br /&gt;
experienced user to operate.  Operating System/2 provides&lt;br /&gt;
comprehensive Help information and descriptive written system&lt;br /&gt;
messages.  Much of this Help information is contextual in nature,&lt;br /&gt;
giving the end user the information needed to complete the task at&lt;br /&gt;
hand.  For beginners, there is a tutorial to assist in getting&lt;br /&gt;
started by learning how to perform basic operating system tasks.&lt;br /&gt;
&lt;br /&gt;
Operating System/2 also provides an interface to the system commands&lt;br /&gt;
that support the IBM Systems Application Architecture conventions for&lt;br /&gt;
Common User Access.  Through this interface, applications can be&lt;br /&gt;
started and stopped, or added and deleted from the system as&lt;br /&gt;
required.  The size and position of the application windows can also&lt;br /&gt;
be controlled.  The user can control local print out on a printer or&lt;br /&gt;
a plotter.  Also, information and data contained in the Operating&lt;br /&gt;
System/2 file system can be accessed.&lt;br /&gt;
&lt;br /&gt;
Compatibility with IBM DOS, Version 3.30&lt;br /&gt;
&lt;br /&gt;
Operating System/2 provides a DOS environment that allows many&lt;br /&gt;
existing DOS applications to run unchanged.  This assists in the&lt;br /&gt;
transition from DOS to Operating System/2.&lt;br /&gt;
&lt;br /&gt;
Applications which may not run in the DOS compatibility environment&lt;br /&gt;
include time dependent programs, such as communications and real time&lt;br /&gt;
applications, hardware specific routines, such as device drivers and&lt;br /&gt;
network dependent applications.&lt;br /&gt;
&lt;br /&gt;
Operating System/2 uses many commands from the basic set of DOS&lt;br /&gt;
commands.  This also assists in the transition from DOS to Operating&lt;br /&gt;
System/2.  The user familiar with DOS commands need only learn the&lt;br /&gt;
Operating System/2 commands and concepts that support Operating&lt;br /&gt;
System/2's extended capabilities.&lt;br /&gt;
&lt;br /&gt;
With the new Programmer Toolkit, it will be possible to create&lt;br /&gt;
applications that run on both DOS and Operating System/2, using a&lt;br /&gt;
subset of the full capability of Operating System/2.&lt;br /&gt;
&lt;br /&gt;
Files created by the user under either DOS 3.30 or Operating System/2&lt;br /&gt;
are interchangeable.  This also assists in the transition from DOS to&lt;br /&gt;
Operating System/2.&lt;br /&gt;
&lt;br /&gt;
Systems Application Architecture&lt;br /&gt;
&lt;br /&gt;
Operating System/2 is a participant in IBM Systems Application&lt;br /&gt;
Architecture, a collection of selected software interfaces,&lt;br /&gt;
conventions, and protocols whose initial set of specifications is&lt;br /&gt;
planned to be published in 1987.  IBM Systems Application&lt;br /&gt;
Architecture is to be the framework for development of consistent&lt;br /&gt;
applications across the future offerings of the major IBM computing&lt;br /&gt;
environments:  System/370, System/3X, and the IBM Personal Computers.&lt;br /&gt;
&lt;br /&gt;
IBM Systems Application Architecture consists of four related&lt;br /&gt;
elements, two of which are new (Common User Access and Common&lt;br /&gt;
Programming Interface), plus extensions to today's existing&lt;br /&gt;
communication architectures (Common Communications Support).  IBM&lt;br /&gt;
Systems Application Architecture establishes the basis for Common&lt;br /&gt;
Applications, developed by IBM to be consistent across IBM systems.&lt;br /&gt;
&lt;br /&gt;
Operating System/2 participates in the following elements of IBM&lt;br /&gt;
Systems Application Architecture:&lt;br /&gt;
&lt;br /&gt;
o  Common User Access&lt;br /&gt;
o  Common Programming Interface&lt;br /&gt;
   -  Presentation Interface&lt;br /&gt;
   -  Dialog Interface&lt;br /&gt;
&lt;br /&gt;
New Service and Warranty&lt;br /&gt;
&lt;br /&gt;
For the first time on an IBM Personal Computer operating system, IBM&lt;br /&gt;
is introducing service and limited warranty for Operating System/2.&lt;br /&gt;
In addition to the media warranty, IBM is providing a three month&lt;br /&gt;
program warranty that includes replacement, correction, or refund.&lt;br /&gt;
Program Services will be available for Operating System/2 until the&lt;br /&gt;
service expiration date provided at general availability.&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 Planned Enhancements&lt;br /&gt;
&lt;br /&gt;
To assist end users in their planning, IBM plans to provide the&lt;br /&gt;
following enhancements to Operating System/2 Standard Edition:&lt;br /&gt;
&lt;br /&gt;
o  Enhanced support for fixed disks to support partitions greater&lt;br /&gt;
   than 32MB&lt;br /&gt;
&lt;br /&gt;
o  Application development facilities for the Dialog Manager&lt;br /&gt;
   Interface&lt;br /&gt;
&lt;br /&gt;
Technical Information&lt;br /&gt;
&lt;br /&gt;
This section gives additional technical information on the IBM&lt;br /&gt;
Operating System/2.  It is directed toward application developers and&lt;br /&gt;
technical planners.&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 Version 1.1 contains all the IBM Operating&lt;br /&gt;
System/2 functions.  Version 1.0 contains all the described function&lt;br /&gt;
except graphics and windowing, which are provided in Version 1.1.&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 and DOS Applications:  The IBM Operating&lt;br /&gt;
System/2 can run both IBM Operating System/2 and DOS applications.&lt;br /&gt;
IBM Operating System/2 applications can use all the capabilities of&lt;br /&gt;
the IBM Personal Computers which contain an Intel 80286 processor, or&lt;br /&gt;
an Intel 80386 processor running as a 80286 processor.  These&lt;br /&gt;
capabilities include memory addressability of up to 16MB,&lt;br /&gt;
multi-programming, and memory management.&lt;br /&gt;
&lt;br /&gt;
The IBM Operating System/2 migration path for DOS users is a DOS&lt;br /&gt;
environment similar to IBM Disk Operating System (IBM DOS Version&lt;br /&gt;
3.30).  The end user can run many timing-independent IBM DOS Version&lt;br /&gt;
3.30 applications or program development tools in the IBM Operating&lt;br /&gt;
System/2 DOS environment.  Communication, network dependent, hardware&lt;br /&gt;
specific, and interrupt driven applications are timing dependent and&lt;br /&gt;
cannot be used in the IBM Operating System/2 DOS environment.&lt;br /&gt;
&lt;br /&gt;
The end user, via a CONFIG.SYS parameter, specifies the DOS&lt;br /&gt;
environment size for the IBM Operating System/2.  The maximum value&lt;br /&gt;
for the DOS environment size is 640KB.  Minimum size is 100KB, or the&lt;br /&gt;
operating system can be specified to run only IBM Operating System/2&lt;br /&gt;
applications.  If the environment size parameter is changed, the new&lt;br /&gt;
environment size is obtained at the next IPL (Initial Program Load).&lt;br /&gt;
The IBM Operating System/2 applications, data files, and most&lt;br /&gt;
operating system modules reside in the physical memory above the&lt;br /&gt;
memory reserved for the DOS environment.&lt;br /&gt;
&lt;br /&gt;
Concurrent running of both IBM Operating System/2 and DOS&lt;br /&gt;
applications is as follows:  When the end user runs a IBM DOS Version&lt;br /&gt;
3.30 application in the IBM Operating System/2 DOS environment, IBM&lt;br /&gt;
Operating System/2 applications may continue to run concurrently.&lt;br /&gt;
When a IBM Operating System/2 application is selected to be displayed&lt;br /&gt;
(that is, when it is brought into the foreground), the DOS&lt;br /&gt;
application is suspended.  Other IBM Operating System/2 applications&lt;br /&gt;
continue to run in the background.&lt;br /&gt;
&lt;br /&gt;
A typical IBM Operating System/2 application cannot inadvertently&lt;br /&gt;
modify another application or the operating system.  On systems where&lt;br /&gt;
the DOS environment is defined, there is no memory protection for any&lt;br /&gt;
program (DOS application, IBM Operating System/2 application or&lt;br /&gt;
operating system) below the 640KB physical memory boundary.  All IBM&lt;br /&gt;
Operating System/2 programs (operating system and applications) and&lt;br /&gt;
data areas above the 1MB physical memory boundary are isolated from&lt;br /&gt;
inadvertent modification by IBM Operating System/2 application.&lt;br /&gt;
&lt;br /&gt;
Installing a VDISK in the DOS environment does not interfere with&lt;br /&gt;
application and operating system protection above the 1MB physical&lt;br /&gt;
memory boundary.  VDISK is an IBM Operating System/2 device driver&lt;br /&gt;
which uses the operating system to allocate a block of physical RAM&lt;br /&gt;
memory for use as a virtual disk.&lt;br /&gt;
&lt;br /&gt;
The DOS environment in IBM Operating System/2 is a migration&lt;br /&gt;
mechanism for running a DOS application.  It is anticipated that&lt;br /&gt;
usage of the DOS environment will diminish as many applications are&lt;br /&gt;
converted to IBM Operating System/2 applications to obtain the&lt;br /&gt;
advantages of larger memory and multi-programming.  The IBM Operating&lt;br /&gt;
&lt;br /&gt;
System/2 DOS environment preserves the end user's existing software&lt;br /&gt;
investment during the migration to running only IBM Operating&lt;br /&gt;
System/2 applications.&lt;br /&gt;
&lt;br /&gt;
Memory Management:  IBM Operating System/2 provides memory management&lt;br /&gt;
services to support the full physical addressability of the 80286&lt;br /&gt;
processor (up to 16 megabyte).  An application is no longer confined&lt;br /&gt;
to the DOS 640 kilobyte memory limitation; this limitation is&lt;br /&gt;
associated with the Intel 8088 processor and the real addressing mode&lt;br /&gt;
of the Intel 80286 processor.&lt;br /&gt;
&lt;br /&gt;
Segment swapping routines in IBM Operating System/2 permit one or&lt;br /&gt;
more applications to run while exceeding total physical memory.  If&lt;br /&gt;
the system has more memory to run applications, there is less segment&lt;br /&gt;
swapping, and system performance is improved.  Segment swapping helps&lt;br /&gt;
protect the end user's current hardware investment until more memory&lt;br /&gt;
is obtained.&lt;br /&gt;
&lt;br /&gt;
Memory can also be shared between applications, so applications can&lt;br /&gt;
be more tightly integrated.  Memory management dynamically allocates&lt;br /&gt;
and releases memory as needed, and collects fragmented memory as&lt;br /&gt;
appropriate.&lt;br /&gt;
&lt;br /&gt;
Dynamic Linking:  Dynamic linking is a significant IBM Operating&lt;br /&gt;
System/2 feature which allows routines to be linked with far call&lt;br /&gt;
references to external libraries.  The application developer&lt;br /&gt;
determines during program development whether the far calls are bound&lt;br /&gt;
during either the program load or program execution time.  Dynamic&lt;br /&gt;
linking improves storage utilization, since the same common library&lt;br /&gt;
routines are not link-edited into many different load modules.&lt;br /&gt;
Performance may also be improved, since inactive segments (such as&lt;br /&gt;
exception processing modules) would not be loaded, unless needed.&lt;br /&gt;
The application developer can use dynamic linking techniques to&lt;br /&gt;
construct complex applications as libraries or subsystems.  Dynamic&lt;br /&gt;
linking removes the need to re-link an application (i.e., create a&lt;br /&gt;
new version for each application executable module) when other&lt;br /&gt;
external routines are changed or updated.&lt;br /&gt;
&lt;br /&gt;
Multi-programming and Multi-tasking:  The multi-programming features&lt;br /&gt;
of IBM Operating System/2 allow a user to operate several&lt;br /&gt;
applications concurrently.  For most purposes, each application will&lt;br /&gt;
appear to have the entire system unit to itself, and may be designed&lt;br /&gt;
and coded in much the same manner as is done using DOS.&lt;br /&gt;
&lt;br /&gt;
Multi-tasking is the sharing of the computer's resources (e.g., the&lt;br /&gt;
processor, display or keyboard) among tasks.  An application program&lt;br /&gt;
can create or control one or more tasks.  The foreground application&lt;br /&gt;
is the current user of the display, keyboard, or pointing device.  A&lt;br /&gt;
background application can be running or be suspended.  Multi-tasking&lt;br /&gt;
permits development of an application, which starts one or more tasks&lt;br /&gt;
and allows the operating system to manage the execution of these&lt;br /&gt;
tasks.&lt;br /&gt;
&lt;br /&gt;
Multi-programming and multi-tasking use a priority based, time&lt;br /&gt;
slicing scheduler.&lt;br /&gt;
&lt;br /&gt;
Interprocess Communication:  These functions allow processes to&lt;br /&gt;
communicate effectively with one another via pipes, semaphores,&lt;br /&gt;
queues, signals, and shared memory.  A programmer can use&lt;br /&gt;
interprocess communications to integrate the separate tasks of an&lt;br /&gt;
application into one &amp;quot;logical&amp;quot; application, as viewed by the end&lt;br /&gt;
user.  These functions significantly enhance programmer productivity&lt;br /&gt;
when creating complex applications.&lt;br /&gt;
&lt;br /&gt;
System Interfaces:  The IBM Operating System/2 CALL interface is&lt;br /&gt;
designed for implementation of system extensions, device drivers, and&lt;br /&gt;
other programmer-defined functions.  The documented operating system&lt;br /&gt;
interface and application enabling interface allow application&lt;br /&gt;
developers to write programs which are not dependent on the internal&lt;br /&gt;
operating system and/or hardware interfaces.  The application&lt;br /&gt;
developer becomes more nearly independent of hardware and software&lt;br /&gt;
changes, and the end user's hardware and software investment is&lt;br /&gt;
protected.&lt;br /&gt;
&lt;br /&gt;
Significant Impact on Application Programs:  The above functions will&lt;br /&gt;
significantly affect program development and programmer productivity.&lt;br /&gt;
Many DOS applications will be simpler and much smaller when converted&lt;br /&gt;
to IBM Operating System/2 applications.  Applications can use the&lt;br /&gt;
operating system's memory management and multi-tasking services,&lt;br /&gt;
instead of performing these functions in application code.&lt;br /&gt;
Furthermore, the application developer can use larger memory and&lt;br /&gt;
multi-tasking to define new types of applications.&lt;br /&gt;
&lt;br /&gt;
Presentation Manager:  The Presentation Manager contains the&lt;br /&gt;
Presentation Interface.  The application developer can use this&lt;br /&gt;
interface to write IBM Operating System/2 applications which support&lt;br /&gt;
the Common User Access element of the Systems Application&lt;br /&gt;
Architecture.  The programmer can develop alphameric and graphic&lt;br /&gt;
applications which use IBM Operating System/2 function calls.  The&lt;br /&gt;
IBM Operating System/2 Programmer Toolkit and the IBM Operating&lt;br /&gt;
System/2 Technical Reference describe the functions, capabilities,&lt;br /&gt;
and application enabling interfaces for the Presentation Manager.&lt;br /&gt;
The IBM Operating System/2 Programmer Toolkit contains the necessary&lt;br /&gt;
development tools to write applications which use Presentation&lt;br /&gt;
Manager functions.&lt;br /&gt;
&lt;br /&gt;
The Presentation Manager encompasses the following topics:&lt;br /&gt;
&lt;br /&gt;
o  Windowing&lt;br /&gt;
o  Graphics&lt;br /&gt;
o  Program Selector&lt;br /&gt;
o  Enhanced Ease-of-use Facilities&lt;br /&gt;
o  Presentation Interface&lt;br /&gt;
&lt;br /&gt;
Windowing:  Multiple IBM Operating System/2 applications written to&lt;br /&gt;
the Presentation Interface can be displayed via overlapping screen&lt;br /&gt;
windows.  Each application can support one or more windows, which are&lt;br /&gt;
organized in an hierarchical parent to child basis.  A child window&lt;br /&gt;
is contained within its parent, and lies on top of it.  Each pop-up&lt;br /&gt;
window has a higher priority than its parent window, and is used in a&lt;br /&gt;
dialog manner to display information to the end user.  An application&lt;br /&gt;
can use dialog boxes which can be dynamically updated.  Windows can&lt;br /&gt;
be scrolled, and window functions can be chosen using icon selection&lt;br /&gt;
or a menu bar.  A menu bar allows the end user to send commands&lt;br /&gt;
directly to the application or to select pull down menus.  An IBM&lt;br /&gt;
Operating System/2 application can specify the form of the window&lt;br /&gt;
frame, control the data which appears in each window, and select&lt;br /&gt;
which window is for input.&lt;br /&gt;
&lt;br /&gt;
Graphics:  The IBM Operating System/2 has extensive graphics&lt;br /&gt;
functions.  IBM Operating System/2 supports All Points Addressable&lt;br /&gt;
devices:  displays, printers, and plotters.  An IBM Operating&lt;br /&gt;
System/2 application can draw graphics data on screen windows or&lt;br /&gt;
display the equivalent data using bitmaps.  A bitmap, which is used&lt;br /&gt;
to produce rapid changes on a screen, is similar to the screen image&lt;br /&gt;
but is created in memory.  IBM Operating System/2 supports both&lt;br /&gt;
retained and non-retained graphics.  An application can draw and fill&lt;br /&gt;
graphics objects, such as lines, arcs, font characters, and images.&lt;br /&gt;
An application can specify various attributes, such as color, line&lt;br /&gt;
style and area fill pattern.  IBM Operating System/2 contains&lt;br /&gt;
graphics transformations to change the object's size, position and&lt;br /&gt;
orientation.  IBM Operating System/2 contains many text functions and&lt;br /&gt;
standard fonts.  Alphameric extensions allow for loadable fonts and&lt;br /&gt;
features, such as underscoring of individual characters.&lt;br /&gt;
&lt;br /&gt;
Program Selector:  A user of IBM Operating System/2 Version 1.0 can&lt;br /&gt;
start and switch between applications with this easy-to-use, full&lt;br /&gt;
screen interface.  The Program Selector displays the names of IBM&lt;br /&gt;
Operating System/2 and Family applications.  (Family applications are&lt;br /&gt;
discussed in the Family Application Program Interface section.) The&lt;br /&gt;
end user can also select the DOS environment, if it exists, and then&lt;br /&gt;
enter the appropriate command(s) to start a DOS application.  The&lt;br /&gt;
Program Selector can be used to add, delete or rename a program on&lt;br /&gt;
the menu.  Input is from either a keyboard or pointing device.&lt;br /&gt;
&lt;br /&gt;
There are two methods to switch between applications:&lt;br /&gt;
&lt;br /&gt;
o  The end user uses a &amp;quot;hot-key&amp;quot; to display the Program Selector.&lt;br /&gt;
   The end user then positions the selection cursor on the desired&lt;br /&gt;
   menu item and presses the Enter key (or uses the mouse); this&lt;br /&gt;
   action invokes a different IBM Operating System/2 application or&lt;br /&gt;
   the DOS environment.&lt;br /&gt;
&lt;br /&gt;
o  The end user uses another &amp;quot;hot-key&amp;quot; to change to another IBM&lt;br /&gt;
   Operating System/2 application, or to the DOS environment without&lt;br /&gt;
   displaying the Program Selector panel.&lt;br /&gt;
&lt;br /&gt;
An IBM Operating System/2 application can be started from the Program&lt;br /&gt;
Selector menu or from the IBM Operating System/2 command line.  The&lt;br /&gt;
function of the Program Selector is incorporated into the Enhanced&lt;br /&gt;
Ease-of-Use Facilities provided with IBM Operating System/2 Version&lt;br /&gt;
1.1.&lt;br /&gt;
&lt;br /&gt;
Enhanced Ease-of-Use Facilities:  Program selection in IBM Operating&lt;br /&gt;
System/2, Version 1.1, combines the function of the Program Selector&lt;br /&gt;
with additional easy-to-use capabilities.  A screen panel is&lt;br /&gt;
&lt;br /&gt;
displayed when the end user starts IBM Operating System/2.  This&lt;br /&gt;
panel provides the function of the Program Selector in Version 1.0&lt;br /&gt;
consistent with other available panel selections.  Each IBM Operating&lt;br /&gt;
System/2 application which uses the graphics interface functions can&lt;br /&gt;
display one or more windows, and the end user can control the size&lt;br /&gt;
and position of the visible windows.  The end user can choose panel&lt;br /&gt;
options to print and plot combined alphameric and graphics data.&lt;br /&gt;
&lt;br /&gt;
The end user can choose another panel option which performs IBM&lt;br /&gt;
Operating System/2 file commands such as copying and renaming.  The&lt;br /&gt;
end user can easily change default system parameters, such as the&lt;br /&gt;
background color on the screen.  The online help facility is always&lt;br /&gt;
available from the system panels.&lt;br /&gt;
&lt;br /&gt;
Presentation Interface:  An IBM Operating System/2 application can&lt;br /&gt;
use this interface to display alphameric and graphics data on a range&lt;br /&gt;
of output devices:  displays, printers and plotters.  An application&lt;br /&gt;
can use the IBM Operating System/2 input handling functions to&lt;br /&gt;
process keyboard and pointing device information, and messages from&lt;br /&gt;
either the operating system or other applications.  When a pointing&lt;br /&gt;
device is attached, the mouse pointer is displayed on top of all&lt;br /&gt;
other data, and is always visible.  Window characteristics can change&lt;br /&gt;
the pointer shape and size.&lt;br /&gt;
&lt;br /&gt;
Dialog Manager:  The Dialog Manager contains the Dialog Interface.&lt;br /&gt;
The text-oriented Dialog Manager is used by several operating system&lt;br /&gt;
functions, such as program selector, installation aid, and system&lt;br /&gt;
tutorial to present a consistent user interface.&lt;br /&gt;
&lt;br /&gt;
Installation Aid:  The Installation Aid functions run in the&lt;br /&gt;
Operating System/2 environment, and include full screen, interactive,&lt;br /&gt;
installation menus with online field help.  The Installation Aid is&lt;br /&gt;
used to install system extensions.&lt;br /&gt;
&lt;br /&gt;
Operating System/2 installation procedures are as follows:  One of&lt;br /&gt;
the Operating System/2 distribution diskettes is the Operating&lt;br /&gt;
System/2 installation diskette.  IPL can be performed on the diskette&lt;br /&gt;
which runs in the Operating System/2 environment, and contains the&lt;br /&gt;
installation program, installation support files, and supporting&lt;br /&gt;
Operating System/2 programs.  The end user places the diskette in&lt;br /&gt;
drive &amp;quot;A&amp;quot;, and turns on the system.  Panels guide the user through&lt;br /&gt;
the installation process.  The user selects items, such as the&lt;br /&gt;
country and keyboard, printer, pointing device, preferred code page,&lt;br /&gt;
and tuning parameters (with defaults given) for the Operating&lt;br /&gt;
System/2 and DOS environments.  The install process warns the end&lt;br /&gt;
user that some existing DOS files will be replaced by Operating&lt;br /&gt;
System/2 files with the same names.  The installation process&lt;br /&gt;
displays a panel which allows the user to designate new names for&lt;br /&gt;
those existing DOS files.&lt;br /&gt;
&lt;br /&gt;
System Installation uses high-capacity, diskette drive &amp;quot;A&amp;quot; and fixed&lt;br /&gt;
disk &amp;quot;C&amp;quot;.  Additional information on operating system installation&lt;br /&gt;
can be obtained in the IBM Operating System/2 user guide.&lt;br /&gt;
&lt;br /&gt;
Online Messages:  IBM Operating System/2 displays messages informing&lt;br /&gt;
the end user of system or program status and problems and prompting&lt;br /&gt;
the end user to perform necessary actions.   For many functions, the&lt;br /&gt;
end user may request &amp;quot;Help&amp;quot;, which displays information about the&lt;br /&gt;
desired function and its use.&lt;br /&gt;
&lt;br /&gt;
Tutorial:  An end user can invoke a IBM Operating System/2 online&lt;br /&gt;
tutorial.  Topics include:  tutorial use, IBM Operating System/2&lt;br /&gt;
menus, running an application, online message help, special IBM&lt;br /&gt;
Operating System/2 keys, system installation, and frequently used&lt;br /&gt;
commands.&lt;br /&gt;
&lt;br /&gt;
Code Page Switching:  A code page is a set of characters and symbols&lt;br /&gt;
that is appropriate to a given country.  IBM Operating System/2&lt;br /&gt;
provides concurrent support for two code pages, which are determined&lt;br /&gt;
by the specified country during the IBM Operating System/2&lt;br /&gt;
installation process.&lt;br /&gt;
&lt;br /&gt;
Large Fixed Disk Support:  A physical fixed disk greater than 32MB is&lt;br /&gt;
partitioned into multiple logical drives, each having a maximum size&lt;br /&gt;
of 32MB.&lt;br /&gt;
&lt;br /&gt;
BASIC Interpreter:  The IBM BASIC Language Interpreter runs in the&lt;br /&gt;
DOS environment, and is included in the IBM Operating System/2.  The&lt;br /&gt;
interpreter is functionally equivalent to the IBM BASIC Language&lt;br /&gt;
Interpreter in IBM DOS Version 3.30.  The BASIC Interpreter is&lt;br /&gt;
described in the separately available BASIC Reference Version 3.30&lt;br /&gt;
(part number 6280189).&lt;br /&gt;
&lt;br /&gt;
Family Application Program Interface:  The application developer can&lt;br /&gt;
use the Family Application Program Interface to obtain program&lt;br /&gt;
portability from IBM Operating System/2 to DOS.  A Family application&lt;br /&gt;
is an executable module that can run in all three environments:  IBM&lt;br /&gt;
Operating System/2, IBM Operating System/2 DOS, or IBM DOS Version&lt;br /&gt;
3.30.  A Family application has the same or similar capabilities of a&lt;br /&gt;
IBM DOS Version 3.30 application; a Family application cannot use the&lt;br /&gt;
new IBM Operating System/2 capabilities, such as larger memory&lt;br /&gt;
addressability, multi-tasking application program interface, or the&lt;br /&gt;
Presentation Interface.&lt;br /&gt;
&lt;br /&gt;
The procedure for developing a Family application is as follows:  The&lt;br /&gt;
application developer writes an IBM Operating System/2 application&lt;br /&gt;
program following certain conventions, which include using a IBM&lt;br /&gt;
System/2 compiler or assembler.  The developer then uses the BIND&lt;br /&gt;
function.  The output from BIND is a single module which can be run&lt;br /&gt;
in all three environments:  IBM Operating System/2, IBM Operating&lt;br /&gt;
System/2 DOS, or IBM DOS Version 3.30.  The DOS application modules&lt;br /&gt;
created by the BIND function can be run on IBM PC hardware supported&lt;br /&gt;
by IBM Operating System/2 and IBM Personal Computer DOS Operating&lt;br /&gt;
System Version 3.30.  A Family application has the same capabilities&lt;br /&gt;
in both IBM DOS Version 3.30 and IBM Operating System/2 environments.&lt;br /&gt;
&lt;br /&gt;
Device Drivers:  A device driver is a program which can pass&lt;br /&gt;
information between the operating system and an input or output&lt;br /&gt;
device.  The IBM Operating System/2 contains many device drivers,&lt;br /&gt;
&lt;br /&gt;
although the application developer can write device drivers to&lt;br /&gt;
support additional hardware devices.  IBM Operating System/2 device&lt;br /&gt;
drivers can service requests in both IBM Operating System/2  and DOS&lt;br /&gt;
environments.  A new device driver can be easily installed; this is&lt;br /&gt;
especially important for new hardware support.&lt;br /&gt;
&lt;br /&gt;
Operating system characteristics affect device driver design and&lt;br /&gt;
implementation.  Since IBM Operating System/2 is a multi-tasking&lt;br /&gt;
operating system, an IBM Operating System/2 device driver is&lt;br /&gt;
interrupt-driven and surrenders the processor while waiting for&lt;br /&gt;
input/output completion.  A DOS device drive is usually synchronous&lt;br /&gt;
and non-interrupt driven; an application cannot continue until the&lt;br /&gt;
input/output operation is completed.  A synchronous device driver&lt;br /&gt;
cannot be used in the multi-programming IBM Operating System/2&lt;br /&gt;
environment.  When developing a device driver, the application&lt;br /&gt;
programmer can use special interfaces in the operating system.  The&lt;br /&gt;
application developer can use information in the IBM Operating&lt;br /&gt;
System/2 Standard Edition Technical Reference Manual to write a&lt;br /&gt;
device driver.&lt;br /&gt;
&lt;br /&gt;
Input/Output Services:  IBM Operating System Version 1.0 Input/Output&lt;br /&gt;
Services provide application program access to the output (display,&lt;br /&gt;
printer, and plotter) and input (keyboard and pointing device)&lt;br /&gt;
devices.  The developer can use this support asynchronously, and thus&lt;br /&gt;
overlap input/output operations with other application processing.&lt;br /&gt;
All support is in text mode.&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 Version 1.1 Input/Output Services contains&lt;br /&gt;
graphics support (All Points Addressable) in addition to the&lt;br /&gt;
capabilities of IBM Operating System/2 Version 1.0.&lt;br /&gt;
&lt;br /&gt;
Link Related Functions:  The IBM Operating System/2 Linker (LINK) and&lt;br /&gt;
Import Librarian (IMPLIB) utilities are used to create program&lt;br /&gt;
modules, dynamic link modules and libraries.  LINK runs in both the&lt;br /&gt;
IBM Operating System/2 and DOS environments.  LINK creates execution&lt;br /&gt;
modules which can run in the following environments:  IBM Operating&lt;br /&gt;
System/2, IBM Operating System/2 DOS, or IBM DOS Version 3.30.  The&lt;br /&gt;
IMPLIB utility runs only in the IBM Operating System/2 environment.&lt;br /&gt;
&lt;br /&gt;
Timer Services:  IBM Operating System/2 has date and time&lt;br /&gt;
specification functions and control mechanisms for specifying the&lt;br /&gt;
following time intervals:  regularly occurring, asynchronous, and&lt;br /&gt;
sleep.  Timer Services help provide application independence from a&lt;br /&gt;
hardware clock speed.&lt;br /&gt;
&lt;br /&gt;
Operating System Commands:  Most IBM DOS Version 3.30 commands and&lt;br /&gt;
utilities are also IBM Operating System/2 commands; this protects end&lt;br /&gt;
user investment in previous learning and in prior application design&lt;br /&gt;
and development.  IBM Operating System/2 also contains new commands&lt;br /&gt;
for user interaction with the multi-tasking, large memory&lt;br /&gt;
environment.&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 has command processors for the IBM Operating&lt;br /&gt;
System/2 environment and the DOS environment.  Both processors have&lt;br /&gt;
similar capabilities to the IBM DOS Version 3.30 command processor.&lt;br /&gt;
&lt;br /&gt;
Migration:  Source code changes to an existing program may be&lt;br /&gt;
required before running that application in the IBM Operating&lt;br /&gt;
System/2 environment.  Programs written in a high level language must&lt;br /&gt;
at a minimum be recompiled and relinked.  All assembler programs must&lt;br /&gt;
be modified, reassembled, and relinked.  All timing-dependent&lt;br /&gt;
programs, such as communication, network dependent, hardware&lt;br /&gt;
specific, and interrupt driven applications, must be rewritten to run&lt;br /&gt;
in the IBM Operating System/2 environment.&lt;br /&gt;
&lt;br /&gt;
Additional Information&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2, Version 1.1, contains all the IBM Operating&lt;br /&gt;
System/2 functions.  Version 1.0 contains all the described function,&lt;br /&gt;
except graphics and windowing which are provided in Version 1.1.&lt;br /&gt;
&lt;br /&gt;
PUBLICATIONS&lt;br /&gt;
&lt;br /&gt;
The IBM Operating System/2 Version 1.0 is distributed with the&lt;br /&gt;
following items which are not available separately:&lt;br /&gt;
&lt;br /&gt;
o  Program License Agreement&lt;br /&gt;
&lt;br /&gt;
o  Diskettes containing the IBM Operating System/2 Version 1.0&lt;br /&gt;
   machine readable information.  Part number 6280196 contains&lt;br /&gt;
   1.44MB, 3.5-inch diskettes.  Part number 6280198 contains 1.2MB,&lt;br /&gt;
   5.25-inch diskettes.&lt;br /&gt;
&lt;br /&gt;
o  User guide for Version 1.0.&lt;br /&gt;
&lt;br /&gt;
   Provides introductory information for all users.  Topics include&lt;br /&gt;
   operating system installation and the Installation Aid, program&lt;br /&gt;
   selector, basic IBM Operating System/2 tasks, file management,&lt;br /&gt;
   operating environments for IBM Operating System/2 and DOS&lt;br /&gt;
   applications, and input/output redirection.&lt;br /&gt;
&lt;br /&gt;
o  User reference for Version 1.0&lt;br /&gt;
&lt;br /&gt;
   Provides additional information for the experienced user and&lt;br /&gt;
   programmer.  Topics include  multi-tasking, memory management,&lt;br /&gt;
   configuration parameters, IBM Operating System/2 commands, and&lt;br /&gt;
   batch file creation.&lt;br /&gt;
&lt;br /&gt;
The BASIC Reference Version 3.30 (part number 6280189) is separately&lt;br /&gt;
available for a charge.&lt;br /&gt;
&lt;br /&gt;
Packaging information for IBM Operating System/2, Version 1.1, will&lt;br /&gt;
be described at general availability of IBM Operating System/2,&lt;br /&gt;
Version 1.1.  Part number 6280194 will contain 1.44MB, 3.5-inch&lt;br /&gt;
diskettes.  Part number 6280195 will contain 1.2MB, 5.25-inch&lt;br /&gt;
diskettes.&lt;br /&gt;
&lt;br /&gt;
Items may also be available to end users under the terms of the&lt;br /&gt;
revised Quantity Discount Agreement (QDA) Exhibits for IBM Personal&lt;br /&gt;
Computer Licensed Programs announced today.&lt;br /&gt;
&lt;br /&gt;
SCHEDULE&lt;br /&gt;
&lt;br /&gt;
General availability of IBM Operating System/2, Version 1.0, is&lt;br /&gt;
planned for first quarter 1988.&lt;br /&gt;
&lt;br /&gt;
The general availability date for IBM Operating System/2, Version&lt;br /&gt;
1.1, will be announced fourth quarter 1987.&lt;br /&gt;
&lt;br /&gt;
Note:  The IBM Operating System/2 Standard Edition is usually&lt;br /&gt;
referred to as the IBM Operating System/2.&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 is positioned as the new enhanced operating&lt;br /&gt;
system for IBM Personal System/2 Models 50, 60, and 80, the IBM PC XT&lt;br /&gt;
Model 286, and the IBM Personal Computer AT for those end users who&lt;br /&gt;
do not require the Communications and Data Base Managers of Operating&lt;br /&gt;
System/2 Extended Edition.  It supports the extended architecture&lt;br /&gt;
common to these products, and provides a foundation for eventual&lt;br /&gt;
migration to the expanded addressing modes of the IBM 80386 systems.&lt;br /&gt;
&lt;br /&gt;
The extended memory addressability in IBM Operating System/2 removes&lt;br /&gt;
the previous 640KB memory restriction by supporting up to 16MB of&lt;br /&gt;
addressability.  Extended memory supports a new generation of&lt;br /&gt;
applications, and enables the coresidency of multiple applications.&lt;br /&gt;
IBM Operating System/2 supports concurrent operation of multiple&lt;br /&gt;
applications, which provides a more productive environment for the&lt;br /&gt;
user who must move among many applications to be productive.&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 provides a new level of consistency and ease&lt;br /&gt;
of use for multi-tasking operating systems with windowing, graphics,&lt;br /&gt;
online documentation, contextual help, and automated installation.&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 provides a DOS application environment which&lt;br /&gt;
allows operation of a single DOS application concurrent with&lt;br /&gt;
applications written or modified for IBM Operating System/2.&lt;br /&gt;
Applications which may not run in the DOS compatibility environment&lt;br /&gt;
include time dependent programs; such as communications and real time&lt;br /&gt;
applications; hardware specific routines such as device drivers, and&lt;br /&gt;
network dependent applications.&lt;br /&gt;
&lt;br /&gt;
A transitional offering to IBM Operating System/2 Version 1.0 will be&lt;br /&gt;
available to licensees of IBM DOS Version 3.X (versions DOS 3.0, 3.1,&lt;br /&gt;
3.2, and 3.30) for six months following general availability in the&lt;br /&gt;
United States.  This offering will allow licensees of IBM DOS Version&lt;br /&gt;
3.X to acquire IBM Operating System/2 at a reduced price, and permits&lt;br /&gt;
the end user to continue using IBM DOS Version 3.X, while using IBM&lt;br /&gt;
Operating System/2.  This offering:&lt;br /&gt;
&lt;br /&gt;
o  Lowers cost to migrate to IBM Operating System/2 Version 1.0&lt;br /&gt;
&lt;br /&gt;
o  Allows continued support for programs that do not run in IBM&lt;br /&gt;
   Operating System/2 Version 1.0&lt;br /&gt;
&lt;br /&gt;
o  Encourages the end user to purchase newly announced hardware now,&lt;br /&gt;
   rather than delay the purchase until availability of IBM Operating&lt;br /&gt;
   System/2 Version 1.0&lt;br /&gt;
&lt;br /&gt;
This offering applies only to licensees of the IBM DOS Version 3.X&lt;br /&gt;
residing in the United States or Puerto Rico.  Ordering information&lt;br /&gt;
will be provided at general availability of IBM Operating System/2&lt;br /&gt;
Version 1.0.&lt;br /&gt;
&lt;br /&gt;
In addition, licensees of IBM Operating System/2 Version 1.0, in the&lt;br /&gt;
United States or Puerto Rico, are entitled to a free IBM Operating&lt;br /&gt;
System/2 replacement at availability of IBM Operating System Version&lt;br /&gt;
1.1.  Details will be provided at general availability of the&lt;br /&gt;
product.&lt;br /&gt;
A transitional offering to IBM Operating System/2, Version 1.0, will&lt;br /&gt;
be available to licensees of IBM DOS Version 3.X (Versions DOS 3.0,&lt;br /&gt;
3.1, 3.2 and 3.30).  Details on the transitional offering will be&lt;br /&gt;
published at general availability of IBM Operating System/2, Version&lt;br /&gt;
1.0.&lt;br /&gt;
&lt;br /&gt;
TECHNICAL INFORMATION&lt;br /&gt;
&lt;br /&gt;
Hardware Requirements:&lt;br /&gt;
&lt;br /&gt;
Machine Requirements: The recommended minimum hardware configuration&lt;br /&gt;
for IBM Operating System/2 is:&lt;br /&gt;
&lt;br /&gt;
o  IBM Personal Computer system unit with 1.5MB of memory when&lt;br /&gt;
   configured to run only IBM Operating System/2 applications, and&lt;br /&gt;
   2MB of memory when configured to run both IBM Operating System/2&lt;br /&gt;
   and DOS applications.&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal System/2 (Models 50, 60 or 80)&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal Computer AT (5170): Models 099, 239, 319, or 339&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal Computer AT (5170): Model 068 with fixed disk to&lt;br /&gt;
      make the system unit equivalent to a Model 099.&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal Computer XT (5162): Model 286&lt;br /&gt;
&lt;br /&gt;
o  One diskette drive (high capacity)&lt;br /&gt;
&lt;br /&gt;
o  One fixed disk drive&lt;br /&gt;
&lt;br /&gt;
o  Keyboard&lt;br /&gt;
&lt;br /&gt;
o  Display adapter and associated display&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2 supports the following devices:&lt;br /&gt;
&lt;br /&gt;
o  Diskette Drives&lt;br /&gt;
&lt;br /&gt;
   -  IBM 3.5-inch high-capacity diskette drive (1.44MB)&lt;br /&gt;
   -  IBM 3.5-inch diskette drive (720KB)&lt;br /&gt;
   -  IBM 4865, Model 2.  IBM Personal Computer 3.5-inch External&lt;br /&gt;
      Diskette Drive (720KB)&lt;br /&gt;
   -  IBM 5.25-inch high-capacity diskette drive (1.2MB)&lt;br /&gt;
   -  IBM 5.25-inch diskette drive (360KB)&lt;br /&gt;
   -  Personal Computer 5.25-inch External Diskette Drive (360KB)&lt;br /&gt;
&lt;br /&gt;
o  Fixed Disks&lt;br /&gt;
&lt;br /&gt;
   -  IBM 20MB Fixed Disk Drive&lt;br /&gt;
   -  IBM 30MB Fixed Disk Drive&lt;br /&gt;
   -  IBM 44MB Fixed Disk Drive&lt;br /&gt;
   -  IBM 70MB Fixed Disk Drive&lt;br /&gt;
   -  IBM 115MB Fixed Disk Drive&lt;br /&gt;
&lt;br /&gt;
      Note: The 44MB, 70MB and 115MB fixed disks are supported as&lt;br /&gt;
      multiple logical drives, each having a maximum size of 32MB.&lt;br /&gt;
&lt;br /&gt;
o  One of the adapter and display combinations listed below:&lt;br /&gt;
&lt;br /&gt;
   -  IBM Color/Graphics Monitor Adapter with IBM Color Display&lt;br /&gt;
      (5153).&lt;br /&gt;
&lt;br /&gt;
   -  IBM Enhanced Graphics Adapter with one of the following&lt;br /&gt;
      displays:  IBM Enhanced Color Display (5154) or IBM Color&lt;br /&gt;
      Display (5153).&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal System/2  Display Adapter for IBM Personal&lt;br /&gt;
      Computer AT or XT-286 with one of the following displays:  IBM&lt;br /&gt;
      Personal System/2 Color Display (8513), IBM Personal System/2&lt;br /&gt;
      Monochrome Display (8503), IBM Personal System/2  Color Display&lt;br /&gt;
      (8512), or IBM Personal System/2  Color Display (8514).&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal System/2 system unit (Models 50, 60 or 80) with&lt;br /&gt;
      one of the following displays: IBM Personal System/2 Color&lt;br /&gt;
      Display (8513), IBM Personal System/2 Monochrome Display&lt;br /&gt;
      (8503), IBM Personal System/2  Color Display (8512), or IBM&lt;br /&gt;
      Personal System/2 Color Display (8514).&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal System/2  Display Adapter 8514/A with one of the&lt;br /&gt;
      following displays:  IBM Personal System/2 Color Display&lt;br /&gt;
      (8513), IBM Personal System/2 Monochrome Display (8503), IBM&lt;br /&gt;
      Personal System/2 Color Display (8512), or IBM Personal&lt;br /&gt;
      System/2 Color Display (8514).&lt;br /&gt;
&lt;br /&gt;
   IBM Operating System/2, Version 1.0, support for specific adapters&lt;br /&gt;
   is as follows:&lt;br /&gt;
&lt;br /&gt;
   -  IBM Color/Graphics Monitor Adapter:  IBM Operating System/2&lt;br /&gt;
      only supports text mode (25 lines) in the IBM Operating&lt;br /&gt;
      System/2 environment and CGA compatibility modes in the DOS&lt;br /&gt;
      environment.&lt;br /&gt;
&lt;br /&gt;
   -  IBM Enhanced Graphics Adapter:  IBM Operating System/2 only&lt;br /&gt;
      supports text mode (25 or 43 lines) in the IBM Operating&lt;br /&gt;
      System/2 environment, and CGA compatibility modes in the DOS&lt;br /&gt;
      environment.&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal System/2 Display Adapter for IBM Personal Computer&lt;br /&gt;
      AT or XT-286:  IBM Operating System/2 only supports text mode&lt;br /&gt;
      (25 or 50 lines) in the IBM Operating System/2 environment, and&lt;br /&gt;
      CGA compatibility modes in the DOS environment.&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal System/2 system unit (Models 50, 60 or 80):  Same&lt;br /&gt;
      support as for IBM Personal System/2 Display Adapter.&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal System/2  Display Adapter 8514/A:  Same support as&lt;br /&gt;
      for IBM Personal System/2 Display Adapter.&lt;br /&gt;
&lt;br /&gt;
   The IBM Operating System/2, Version 1.1, support for each adapter&lt;br /&gt;
   includes all Version 1.0 support for that adapter, plus All Points&lt;br /&gt;
   Addressable support in the IBM Operating System/2 environment.&lt;br /&gt;
&lt;br /&gt;
o  Printers&lt;br /&gt;
&lt;br /&gt;
   Note:  Parallel attached printers are supported in both the IBM&lt;br /&gt;
   Operating System/2 and DOS environments.  Serially (asynchronous)&lt;br /&gt;
   attached printers are supported in only the IBM Operating System/2&lt;br /&gt;
   environment.&lt;br /&gt;
&lt;br /&gt;
   -  IBM 4201 Proprinter, Model 1&lt;br /&gt;
&lt;br /&gt;
   -  IBM 4201 Proprinter II&lt;br /&gt;
&lt;br /&gt;
   -  IBM 4202 Proprinter XL, Model 1&lt;br /&gt;
&lt;br /&gt;
   -  IBM 4207 Proprinter X 24&lt;br /&gt;
&lt;br /&gt;
   -  IBM 4208 Proprinter XL 24&lt;br /&gt;
&lt;br /&gt;
   -  IBM 5152 Graphics Printer, Model 2&lt;br /&gt;
&lt;br /&gt;
      Note:  The Graphics Printer is no longer marketed.&lt;br /&gt;
&lt;br /&gt;
   -  IBM 5182 Color Printer, Model 1&lt;br /&gt;
&lt;br /&gt;
      Note:  The Color Printer is no longer marketed.&lt;br /&gt;
&lt;br /&gt;
   -  IBM 5201 Quietwriter(R), Models 1 &amp;amp; 2&lt;br /&gt;
&lt;br /&gt;
   -  IBM 5202 Quietwriter III&lt;br /&gt;
&lt;br /&gt;
   -  IBM 5216 Wheelprinter (parallel)&lt;br /&gt;
&lt;br /&gt;
   -  IBM 5223 Wheelprinter E (parallel)&lt;br /&gt;
&lt;br /&gt;
      Note:  IBM Operating System/2, Version 1.0, supports each of&lt;br /&gt;
      the above printers as an IBM Graphics Printer (5152, Model 2).&lt;br /&gt;
      IBM Operating System/2, Version 1.1, provides All Points&lt;br /&gt;
      Addressable support to the printers, where appropriate.&lt;br /&gt;
&lt;br /&gt;
      (R)  Registered trademark of the International Business&lt;br /&gt;
      Machines Corporation.&lt;br /&gt;
&lt;br /&gt;
o  Keyboard&lt;br /&gt;
&lt;br /&gt;
o  Pointing Devices&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal System/2 Mouse attached to the system pointing&lt;br /&gt;
      device port&lt;br /&gt;
&lt;br /&gt;
   -  Serial pointing device&lt;br /&gt;
&lt;br /&gt;
      -- Microsoft(1) Mouse for IBM Personal Computers, Part Number&lt;br /&gt;
         039-099, 100ppi&lt;br /&gt;
&lt;br /&gt;
      -- Microsoft Mouse for IBM Personal Computers, Part Number&lt;br /&gt;
         039-199, 200ppi&lt;br /&gt;
&lt;br /&gt;
      -- PC Mouse(2), Part Number 900120-214, 100 ppi&lt;br /&gt;
&lt;br /&gt;
      -- Visi On(3) Mouse, Part Number 69910-1011, 100 ppi&lt;br /&gt;
&lt;br /&gt;
         Note:  Serially (asynchronous) pointing devices are&lt;br /&gt;
         supported in only the Operating System/2 environment.&lt;br /&gt;
&lt;br /&gt;
   -  Parallel pointing device for IBM Personal Computer AT and&lt;br /&gt;
      XT-286&lt;br /&gt;
&lt;br /&gt;
      -- Microsoft Mouse for IBM Personal Computers, Part Number&lt;br /&gt;
         037-099, 100ppi Part Number 037-199, 200ppi&lt;br /&gt;
&lt;br /&gt;
   -  InPort Microsoft Mouse for IBM Personal Computers AT and&lt;br /&gt;
      XT-286, Part Number 037-299, 200ppi&lt;br /&gt;
&lt;br /&gt;
(1)  Registered trademark of Microsoft Corporation.&lt;br /&gt;
&lt;br /&gt;
(2)  Trademark of Metagraphics/Mouse Systems.&lt;br /&gt;
&lt;br /&gt;
(3)  Trademark of Visi-On Corporation.&lt;br /&gt;
&lt;br /&gt;
o  Plotters&lt;br /&gt;
&lt;br /&gt;
   Note:  Plotters are asynchronously attached, and can be supported&lt;br /&gt;
   only in the IBM Operating System/2 environment.  The IBM Operating&lt;br /&gt;
   System/2, Version 1.1 provides plotter support.  Plotter support&lt;br /&gt;
   for IBM Operating System/2, Version 1.0, must be provided by the&lt;br /&gt;
   application.  For example, the IBM Operating System/2 Graphics&lt;br /&gt;
   Development Toolkit supports the IBM 6180, IBM 7371, and IBM 7372&lt;br /&gt;
   plotters.&lt;br /&gt;
&lt;br /&gt;
   -  IBM 6180 Plotter&lt;br /&gt;
   -  IBM 6184 Plotter&lt;br /&gt;
   -  IBM 6186 Plotter&lt;br /&gt;
   -  IBM 7371 Plotter&lt;br /&gt;
   -  IBM 7372 Plotter&lt;br /&gt;
   -  IBM 7374 Plotter&lt;br /&gt;
   -  IBM 7375 Plotter&lt;br /&gt;
&lt;br /&gt;
   Note:  The IBM 7371, 7374 and 7375 Plotters are no longer&lt;br /&gt;
   marketed.&lt;br /&gt;
&lt;br /&gt;
o  Other&lt;br /&gt;
&lt;br /&gt;
   -  IBM Personal Computer AT Serial/Parallel Adapter Card&lt;br /&gt;
   -  IBM Personal System/2 Dual Asynchronous Adapter/A&lt;br /&gt;
   -  IBM Personal System/2 Multiprotocol Adapter/A  in asynchronous&lt;br /&gt;
      mode&lt;br /&gt;
&lt;br /&gt;
   Note:  A maximum of three serial ports is supported on the IBM&lt;br /&gt;
   Personal System/2 (Models 50, 60, or 80) system unit.  One port is&lt;br /&gt;
   already on the system board.&lt;br /&gt;
&lt;br /&gt;
   -  Math Co-processor (Intel 80287)&lt;br /&gt;
   -  Math Co-processor (Intel 80387)&lt;br /&gt;
&lt;br /&gt;
   Note:  The availability date for support of the 80387 math&lt;br /&gt;
   co-processor on 80386 systems (supported as an 80287) will be&lt;br /&gt;
   announced in fourth quarter 1987.&lt;br /&gt;
&lt;br /&gt;
   For additional memory for the IBM Personal Computer AT or XT-286,&lt;br /&gt;
   the end user can order one or two of the following combinations:&lt;br /&gt;
&lt;br /&gt;
   -  Memory Expansion Adapter with 512K Memory installed (55X3560)&lt;br /&gt;
&lt;br /&gt;
   -  Memory Expansion Adapter with 1MB Memory installed (55X3679)&lt;br /&gt;
&lt;br /&gt;
   -  Memory Module Kit - 512K (55X3547)&lt;br /&gt;
&lt;br /&gt;
   -  Memory Module Kit - 1MB (55X3681)&lt;br /&gt;
&lt;br /&gt;
   For additional memory for the IBM Personal System/2 (Models 50 or&lt;br /&gt;
   60), the end user can order:&lt;br /&gt;
&lt;br /&gt;
   -  512KB to 2MB Memory Expansion&lt;br /&gt;
&lt;br /&gt;
   -  2MB Memory Expansion&lt;br /&gt;
&lt;br /&gt;
   Note:  The IBM Operating System/2 supports these features as&lt;br /&gt;
   extended memory.&lt;br /&gt;
&lt;br /&gt;
   For additional memory for the IBM Personal System/2 (Model 80),&lt;br /&gt;
   the end user can order:&lt;br /&gt;
&lt;br /&gt;
   -  1MB System Board Memory Expansion&lt;br /&gt;
&lt;br /&gt;
   -  2MB to 6MB Memory Expansion&lt;br /&gt;
&lt;br /&gt;
Supported Hardware for Family Applications:  The application&lt;br /&gt;
developer can write a Family application which is portable from IBM&lt;br /&gt;
Operating System/2 to DOS.  A Family application is an executable&lt;br /&gt;
module that can run in all three environments:  an IBM Operating&lt;br /&gt;
System/2 environment that runs IBM Operating System/2 applications,&lt;br /&gt;
an IBM Operating System/2 environment that runs a DOS application, or&lt;br /&gt;
the IBM DOS Version 3.30 environment.  A Family application has the&lt;br /&gt;
same or similar capabilities as an IBM DOS Version 3.30 application;&lt;br /&gt;
a Family application cannot use the new IBM Operating System/2&lt;br /&gt;
capabilities, such as larger memory addressability, multi-tasking&lt;br /&gt;
application program interface, or the graphics and windowing&lt;br /&gt;
capabilities of the Presentation Interface.  Assuming there is&lt;br /&gt;
sufficient memory, display, and other appropriate hardware, a Family&lt;br /&gt;
application can run on all the systems units supported by the IBM&lt;br /&gt;
Operating System/2 and IBM Personal Computer Disk Operating System&lt;br /&gt;
Version 3.30.&lt;br /&gt;
&lt;br /&gt;
Software Requirements:&lt;br /&gt;
&lt;br /&gt;
Programming Requirements:  An application developer should purchase&lt;br /&gt;
the IBM Operating System/2 Programmer Toolkit and IBM Operating&lt;br /&gt;
System/2 Technical Reference.  The Toolkit is required to create an&lt;br /&gt;
IBM Operating System/2 application using many Presentation Manager&lt;br /&gt;
functions.  It is also required to create Family applications.&lt;br /&gt;
&lt;br /&gt;
Compatibility:  Both IBM Operating System/2 and IBM DOS Version 3.30&lt;br /&gt;
can be run on the same machine, but not concurrently.  The same data&lt;br /&gt;
files can be accessed by programs or commands running under either&lt;br /&gt;
operating system.&lt;br /&gt;
&lt;br /&gt;
Prerequisites:  The recommended minimum system unit memory size is&lt;br /&gt;
1.5MB when configured to run only IBM Operating System/2&lt;br /&gt;
applications, and 2MB when configured to run both IBM Operating&lt;br /&gt;
System/2 and DOS Version 3.30 applications.&lt;br /&gt;
&lt;br /&gt;
The appropriate Personal Computer AT and PC XT Models must have&lt;br /&gt;
either 512KB or 640KB in the lower address space, and 1MB above the&lt;br /&gt;
one megabyte address space boundary.&lt;br /&gt;
&lt;br /&gt;
End User Responsibilities:  The end user should:&lt;br /&gt;
&lt;br /&gt;
o  Confirm that the Diskette Drive &amp;quot;A&amp;quot; is the same size (3.5-inch or&lt;br /&gt;
   5.25-inch) and capacity (1.2MB or 1.44MB) as the IBM Operating&lt;br /&gt;
   System/2 diskettes.&lt;br /&gt;
&lt;br /&gt;
o  Ensure that fixed disk &amp;quot;C&amp;quot; has enough free space.  Space&lt;br /&gt;
   requirements are given in the next section.&lt;br /&gt;
&lt;br /&gt;
o  Create a backup copy of the IBM Operating System/2 distribution&lt;br /&gt;
   diskettes.&lt;br /&gt;
&lt;br /&gt;
o  Install IBM Operating System/2 on fixed disk using the automated&lt;br /&gt;
   installation procedure.&lt;br /&gt;
&lt;br /&gt;
Installability:  The IBM Operating System/2 diskettes contain&lt;br /&gt;
procedures for installing the IBM Operating System/2, and the&lt;br /&gt;
Installation Aid for installing system extensions.  The operating&lt;br /&gt;
system installation uses diskette drive &amp;quot;A&amp;quot; and fixed disk &amp;quot;C&amp;quot;.  The&lt;br /&gt;
IBM Operating System/2 installation and the IBM Operating System/2&lt;br /&gt;
Installation Aid are described in the attachment.  The IBM Operating&lt;br /&gt;
System/2 user guide contains instructions for system installation and&lt;br /&gt;
using the Aid.&lt;br /&gt;
&lt;br /&gt;
IBM Operating System/2, Version 1.0, distribution diskettes contain&lt;br /&gt;
approximately 5 megabytes of information.  Additional disk space is&lt;br /&gt;
needed for system files, such as swap and history files.  The amount&lt;br /&gt;
of required disk space is dependent on the number of installed&lt;br /&gt;
applications and the amount of physical memory (which affects swap&lt;br /&gt;
file space).  The end user must ensure that fixed disk &amp;quot;C&amp;quot; has&lt;br /&gt;
sufficient space.&lt;br /&gt;
&lt;br /&gt;
End user management is responsible for evaluation, selection, and&lt;br /&gt;
implementation of security features, administrative procedures, and&lt;br /&gt;
appropriate controls in application systems and communication&lt;br /&gt;
facilities.&lt;br /&gt;
&lt;br /&gt;
Warranty Period:  All media is warranted for three months.&lt;br /&gt;
&lt;br /&gt;
The following items have enhanced services and some are warranted as&lt;br /&gt;
shown below:&lt;br /&gt;
&lt;br /&gt;
Part                     3 Month     Program     Extended    Last&lt;br /&gt;
Number   Description     Warranty(1) Services(2) Support(3)  Date(4)&lt;br /&gt;
&lt;br /&gt;
6280196  Oper. S/2       Yes         Yes         No&lt;br /&gt;
          Stand. Ed.&lt;br /&gt;
          Version 1.0&lt;br /&gt;
          3.5-inch Media&lt;br /&gt;
&lt;br /&gt;
6280198  Oper. S/2       Yes         Yes         No&lt;br /&gt;
          Stand. Ed.&lt;br /&gt;
          Version 1.0&lt;br /&gt;
          5.25-inch Media&lt;br /&gt;
&lt;br /&gt;
6280194  Oper. S/2       Yes         Yes         No&lt;br /&gt;
          Stand. Ed.&lt;br /&gt;
          Version 1.1&lt;br /&gt;
          3.5-inch Media&lt;br /&gt;
&lt;br /&gt;
6280195  Oper. S/2       Yes         Yes         No&lt;br /&gt;
          Stand. Ed.&lt;br /&gt;
          Version 1.1&lt;br /&gt;
          5.25-inch Media&lt;br /&gt;
&lt;br /&gt;
(1) 3 Month Warranty:  Available for three months following the date&lt;br /&gt;
    of delivery to the end user by the Dealer, but not later than the&lt;br /&gt;
    date shown under &amp;quot;Last Date.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
(2) Program Services:  Defect-related service, including the IBM&lt;br /&gt;
    Support Center, will be available until 12 months after general&lt;br /&gt;
    availability, but not later than the date shown under &amp;quot;Last&lt;br /&gt;
    Date.&amp;quot;  IBM does not guarantee service results or represent or&lt;br /&gt;
    warrant that all errors or program defects will be corrected.&lt;br /&gt;
    Instructions for reporting problems are included in the product&lt;br /&gt;
    publications.&lt;br /&gt;
&lt;br /&gt;
    The Dealer will act as the interface between its end users and&lt;br /&gt;
    IBM.&lt;br /&gt;
&lt;br /&gt;
    IBM will respond to a reported defect in the unaltered portion of&lt;br /&gt;
    the program by issuing defect correction information, such as&lt;br /&gt;
    corrected code.  One copy of a correction will be provided to the&lt;br /&gt;
    Dealer reporting the defect.  Dealers are responsible for making&lt;br /&gt;
    and distributing copies of the correction to their end users.&lt;br /&gt;
&lt;br /&gt;
(3) Extended Support:  IBM Extended Support is available from IBM at&lt;br /&gt;
    a charge.  Licensed end users must subscribe to IBM Extended&lt;br /&gt;
    Support through a 12-month subscription.  For additional&lt;br /&gt;
    information, call the IBM Program Support Center, toll-free, at 1&lt;br /&gt;
    800 426-2266.&lt;br /&gt;
&lt;br /&gt;
(4) This information will be provided when each product is available&lt;br /&gt;
    for shipment.&lt;br /&gt;
&lt;br /&gt;
Transitional Offering Ordering Information:  The following two items&lt;br /&gt;
will be available to licensees of IBM DOS Version 3.X (Versions 3.0,&lt;br /&gt;
3.1, 3.2, and 3.30).  Payment will be $200.00 for each copy plus&lt;br /&gt;
applicable state and local sales taxes.  Details on the transitional&lt;br /&gt;
offering, and Dealer participation, will be published at general&lt;br /&gt;
availability of IBM Operating System/2 Version 1.0.&lt;br /&gt;
&lt;br /&gt;
CHARGES&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                                  Single&lt;br /&gt;
                                  Unit&lt;br /&gt;
Part                              License&lt;br /&gt;
Number    Description             Charge*&lt;br /&gt;
&lt;br /&gt;
6280196   Operating System/2      $325&lt;br /&gt;
            Standard Edition&lt;br /&gt;
            Version 1.0&lt;br /&gt;
&lt;br /&gt;
6280198   Operating System/2       325&lt;br /&gt;
            Standard Edition&lt;br /&gt;
            Version 1.0&lt;br /&gt;
&lt;br /&gt;
6280194   Operating System/2       325&lt;br /&gt;
            Standard Edition&lt;br /&gt;
            Version 1.1&lt;br /&gt;
&lt;br /&gt;
6280195   Operating System/2       325&lt;br /&gt;
            Standard Edition&lt;br /&gt;
            Version 1.1&lt;br /&gt;
&lt;br /&gt;
* Single Unit License Charge&lt;br /&gt;
This reflects the license charges for a single unit acquired from IBM&lt;br /&gt;
and is subject to change without notice.  These charges are for&lt;br /&gt;
information purposes only and shall not limit in any way the Dealers'&lt;br /&gt;
ability to set their own charges for IBM products.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category: OS/2]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=36395</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=36395"/>
				<updated>2025-06-13T12:01:06Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* 16 bit versions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
&lt;br /&gt;
'''OS/2''' started as a collaborative effort between [[International Business Machines|IBM]] and [[Microsoft]] to put together the next generation [[operating system]] for the [[IBM AT]] and [[IBM PS/2|PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Microsoft Windows|Windows]] project around the same time, as a low cost entry interface with rudimentary (cooperative) [[multi-tasking]].&lt;br /&gt;
&lt;br /&gt;
Needless to say, Microsoft wanted to target the [[Intel 80386|i386]] processor, and work on 32-bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and the upcoming [[PS/2 model 60]], hence the demand for the [[Intel 80286|i286]] 16-bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was '''NOT''' a big seller.  Instead, the industry worked around OS/2, and developed [[DOS extender]] technology, and Microsoft practically gave away the Windows SDK, allowed for [[OEM]] customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft leapt at the chance to formalize DOS extenders into [[DOS Protected Mode Interface|DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on Google's [http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e usenet archive], or locally here [[Gordon Letwin OS/2 usenet post]].  There is also a perspective from an Autodesk programmer available [http://www.sibbald.com/windows/windows01.html here].&lt;br /&gt;
&lt;br /&gt;
With a bit more research and having things in hand, OS/2 as mentioned in the book ''Inside OS/2'', is clearly an evolution from the [[MS-DOS]] 4.00M, the multi-tasking version of DOS.  There was clearly a need for an 'advanced' operating system on the desktop, unfortunately in retrospect Microsoft had to get involved with IBM and derail the industry for nearly a decade.&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== Pre-Release version ===&lt;br /&gt;
In the beginning there are several known pre-release versions, then followed up with the infamous $3,000 SDK package.  These include:&lt;br /&gt;
&lt;br /&gt;
* A/DOS&lt;br /&gt;
* SIZZLE&lt;br /&gt;
* FOOTBALL&lt;br /&gt;
&lt;br /&gt;
The SDK versions have recently surfaced on archive.org and are currently known to be:&lt;br /&gt;
* [[Microsoft OS/2 beta 1.00|1.00]]&lt;br /&gt;
* 1.01&lt;br /&gt;
* 1.02&lt;br /&gt;
* 1.03&lt;br /&gt;
* 1.05&lt;br /&gt;
* 1.06&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an i286 [[Central Processing Unit|CPU]], and an IBM AT, or PS/2 compatible computer.  All of the 16-bit versions were limited to a SINGLE MS-DOS compatibility box, greatly reducing the overall usefulness of OS/2 with the ever-increasing prevalence of MS-DOS based applications.  At the same time, the 16-bit version supported swapping, DLL's, threads and preemptive multi-tasking.  There was an excellent overview of the original OS/2's in the book [http://ebooks.znu.edu.ua/files/comp_books1/cd0/isos2.txt Inside OS/2].&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
*:This version was all textmode, and had an interface that was inspired from [[TopView]].  Although it could multi-task, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in [[real mode|real]] &amp;amp; [[protected mode]]. Until 1.3 all versions were released by OEM hardware vendors ([[Compaq]], [[Zenith]] etc, along with IBM), this was normal practice for Microsoft at the time.&lt;br /&gt;
*:The IBM OS/2 1.0 announcement can be read [[IBM OS2 1.0 announcement|here]].&lt;br /&gt;
*1.1&lt;br /&gt;
*:OS/2 1.1 was released in 1988, and was the first version to include the [[Presentation Manager]].  It 'looked' identical to that of [[Windows 2.0]].  IBM OS/2 1.1 included the PM version of [[Borland Sidekick]] to fill in the gap of accessories for OS/2.  While there was some initial excitement over this version of OS/2, it quickly faded as you had to either buy a new computer with it installed, or jump through OEM channels to get OS/2.  Microsoft didn't sell OS'es to end users in the 1980s (This didn't change until OS/2 1.3, MS-DOS 5 &amp;amp; Windows NT 3.1). Version 1.1c was 386 aware, in that it could use the 80386's ability to quickly &amp;amp; easily transition from real &amp;amp; protected modes, bypassing the [[triple fault]] method of the 286. &lt;br /&gt;
*1.2&lt;br /&gt;
*:This version released in September 1988 was significant with the inclusion of the [[HPFS]] [[file system]].  HPFS was significantly faster than the aging [[FAT]] file system as it placed its tables in the middle of the disk, and it allowed for larger file systems, long filenames and extended attributes.  A later [[service pack]] allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
*:OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
*:[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false|here].&lt;br /&gt;
*1.21&lt;br /&gt;
*:I think this version was a Microsoft exclusive, and the final version that they were directly involved in.&lt;br /&gt;
*1.3&lt;br /&gt;
*:This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32-bit HPFS driver in their [[Lan Manager]] package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applets &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16-bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32-bit versions ===&lt;br /&gt;
All of these versions require an i386 SX or better CPU running on either an IBM AT compatible [[motherboard]], or the IBM PS/2 32-bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (LA Internal revision 6.167 91-10-08)&lt;br /&gt;
[[Image:IBM OS2 2.0 LA cover.jpg|150px|thumb|right|OS/2 2.0 LA]]&lt;br /&gt;
This was the first 32-bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.  There was no seamless Windows in this release, and Win-OS/2 only featured Windows 3.0a in standard mode.&lt;br /&gt;
&lt;br /&gt;
The LA version does not include 'seamless' WIN-OS/2 sessions, and much like OS/2 2.0 GA it does not support Windows's 386 enhanced mode.  While it is possible to launch Windows in a Window the display corrupts and it is exceptionally unstable.&lt;br /&gt;
&lt;br /&gt;
Attempting to use any production GA drivers will result in a [[kernel]] crash.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (GA Internal revision 6.307 92-03-01)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32-bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16-bit device drivers as the kernel was still a hybrid 16-bit/32-bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
The default syslevel for OS/2 2.0 is XR02000.  The last known service pack for OS/2 2.0 brings it up to XR06100.  The XR06100 update also installs the OS/2 32-bit Graphics Engine, XR02010.&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0 .  OS/2 2.1 also included the [[multi-media]] update which would allow for sound effects for almost every conceivable motion.  It was very annoying. &lt;br /&gt;
&lt;br /&gt;
OS/2 2.1 also supported more video cards, more printers, and included support for [[PCMCIA]] and [[APM]], making it acceptable for laptop use.&lt;br /&gt;
&lt;br /&gt;
The update XR06200 brings OS/2 2.1 up to 2.11 functionality.&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
OS/2 2.11 supported multiple processors, and from a user standpoint it was halfway between 2.11 and Warp.  I remember this version being insanely expensive, as it was targeted to the '[[server]]' crowd, IBM had shortsightedly decided end users wouldn't want SMP. While [[Windows NT]] Workstation always supported two physical processors.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
[[Image:IBM OS2 Warp 3.0 blue spine cover.jpg|150px|thumb|right|OS/2 Warp 3.0 BlueSpine]]&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of [[Random Access Memory|RAM]] on a 386SX CPU.  Warp also included the 'bonus pack' which included SLIP/[[PPP]] [[TCP/IP]], a dialer application and a [[word processor]] &amp;amp; spreadsheet.  A simple [[Gopher]] [[client]] &amp;amp; [[Network News Transfer Protocol|NNTP]] client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including [[local area network|LAN]] drivers (that was WARP CONNECT), while [[Windows 95]] &amp;amp; [[Windows_NT#Windows_NT_3.5|NT 3.5]] both included SLIP/PPP *AND* LAN drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
[[Image:logo-warp.gif|thumb|150px|right|OS/2 Warp Logo]]&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revolutionized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run [[Intel x86|x86]] code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[Reduced Instruction Set Computer|RISC]] CPU could in fact run x86 instructions much quicker then a real Intel CPU...  This opened the way to the Pentium CPUs and effectively killed the RISC revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://www.os2museum.com/wp/?page_id=30 here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
The latest version of VirtualBOX from SUN is capable of installing &amp;amp; Running the 1.x version of OS/2 provided that they have had a [[Patching OS/2 for fast machines|timing patch]] in place.  I have found that the 'best' profile is to use the &amp;quot;Oracle Solaris 10 5/09 and earlier&amp;quot; profile.  Be sure to limit the VM to 16MB of RAM, add both a floppy drive, and a serial port, otherwise you'll have difficulty booting.&lt;br /&gt;
&lt;br /&gt;
Right now the best solution is either [https://pcem-emulator.co.uk/ PCem] or [http://ci.86box.net/ 86Box].  With the appropriate [http://tinyurl.com/rs20170821 ROMS] it's possible to install onto an emulated 286/386 class based machine.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.0 ====&lt;br /&gt;
[[Image:OS2 1.0.png|thumb|200px|right|OS/2 1.0 under VirtualBOX]]&lt;br /&gt;
*First version from November 1987 - no success.&lt;br /&gt;
*There is a later 1987 version that does run, it may be an OS/2 1.1 beta?&lt;br /&gt;
It seems the 1.0 IBM kernels rely on 286 triple faults and Virtual Box does not emulate it.  However there is an early Microsoft OS/2 1.1 beta that uses the 386 method of switching to protected mode, and will run on modern machines (as long as the speed fix in place.).&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]]. There are some [[Patching OS/2 for fast machines|binary patches]] you can do to allow old 1.x OS/2 on fast machines.  Even without an emulator you'd need to do this on anything in Pentium II speeds..  With the speed stuff in hand, you can now run 1.1 on VirtualBOX as its floppy driver now works with OS/2 to detect speed/density correctly.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.1c is the first version that supports the 386's method of switching from protected mode to real mode.  Prior to level c of OS/2 1.1 the method was a 286 triple fault, which almost all emulators do not fully support, or it may simply be in the IBM versions.  I'm still not sure as early versions of OS/2 are hard to track down.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.2 ====&lt;br /&gt;
[[Image:OS2 1.2.png|thumb|200px|right|OS/2 1.2 on VirtualBOX]]&lt;br /&gt;
With the above hex edits in place, OS/2 1.2 installs somewhat ok.  The big 'issue' I found was that the IBM version of OS/2 1.2 does not support PS/2 [[mouse|mice]] on the IBM AT computers.  However you can take the PS/2 driver from OS/2 1.3 and use it.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.21 ====&lt;br /&gt;
[[Image:OS2 1.21.png|thumb|200px|right|OS/2 1.21 on VirtualBOX]]&lt;br /&gt;
Again there is an issue of no included working mice drivers, and poaching the driver from 1.3 works fine.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.&lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
* [http://support.microsoft.com/kb/102628/en-us Microsoft OS/2 1.3 HCL]&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.0 ====&lt;br /&gt;
OS/2 2.00 ended up being a [[death march]] of a project, taking far too long, straining the joint agreement between Microsoft and IBM to the breaking point and leading to the infamous divorce.  As it'd come to light decades later Microsoft really had been trying to bring an 80386 version of OS/2 to market since inception, and version 2.00 was the chance to bring this to fruition in codename 'Cruiser'.  Sadly many of these betas did not survive.  There are 3 main branches of these early versions, the Microsoft versions where they were directly involved, the IBM transition to where IBM was now responsible for Cruiser, and finally the Workplace Shell versions, where IBM decided to add a new shell to OS/2 to make 2.00 more 'substantial' but also ended up delaying OS/2 2.00 further.&lt;br /&gt;
&lt;br /&gt;
===== Betas =====&lt;br /&gt;
These are the ones I currently have access to, or directly know about:&lt;br /&gt;
&lt;br /&gt;
====== Microsoft Developer's Relase 1 ======&lt;br /&gt;
====== Microsoft Developer's Relase 2 ======&lt;br /&gt;
&lt;br /&gt;
====== Build 123 ======&lt;br /&gt;
[[Image:OS2 2.0 6.123.png|thumb|200px|right|OS/2 2.0 Build 123 on 86Box.]]&lt;br /&gt;
The best way to install this is on 86Box/PCem using the AMI 486 clone with a Pentium Overdrive processor and use the [[Microsoft InPort|InPort]] [[Bus mouse]] adapter, along with the generic VGA adapter.&lt;br /&gt;
&lt;br /&gt;
This build is the 'divorce' edition when IBM started to ship the betas itself, this version uses the OS/2 1.2 Presentation Manager.  The MS-DOS is built around MS-DOS 4.0 and includes no DPMI support at all.&lt;br /&gt;
You can read more about it [http://www.os2museum.com/wp/os-2-2-0-spring-91-edition/ on OS/2 Museum].  The disks can be downloaded from archive.org [[https://archive.org/details/os2-2.0-6.123 here]].&lt;br /&gt;
&lt;br /&gt;
To install I used [[86box]] i486 Socket 2, IBM PS/ValuePoint 433DX/Si, with a Pentium OverDrive 63 MHz processor, and a 'type 9' disk setup on IDE.  The mouse is set to PS/2 mode.&lt;br /&gt;
&lt;br /&gt;
The kernel has the following version string:&lt;br /&gt;
&lt;br /&gt;
	Internal revision 6.123, 91/02/04 $&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Copyright dialog has this dated for 1990.&lt;br /&gt;
&lt;br /&gt;
====== Build 605 ======&lt;br /&gt;
Not sure why it's build 605 as it is way out of sequence. Perhaps they thought they were close to the end.  This is one of the last Presentation Manager based releases.  It can be downloaded from [[https://archive.org/details/os2-2.0-6.605 archive.org here]].&lt;br /&gt;
&lt;br /&gt;
The kernel string reports as:&lt;br /&gt;
&lt;br /&gt;
	Internal revision 6.605, 91/09/11&lt;br /&gt;
&lt;br /&gt;
The accessories are more fleshed out than 6.123, and to me despite it not supporting DPMI it feels like something that could have shipped in '89.&lt;br /&gt;
&lt;br /&gt;
605 also introduced the minimal text setup spanning install disk 1-4, with the rest being setup under the Presentation Manager.&lt;br /&gt;
&lt;br /&gt;
====== Build 177 ======&lt;br /&gt;
[[Image:OS2 2.0LA in VirtualBOX.png|thumb|200px|right|OS/2 2.0 LA running under VirtualBOX.]]&lt;br /&gt;
&lt;br /&gt;
At the moment the only way I've been able to install OS/2 2.0 LA on Virtual PC/VirtualBOX is to 'cheat' and use the install &amp;amp; disk 1 from OS/2 2.0 GA, and replace the following files on disk1, from LA's disk 1:&lt;br /&gt;
&lt;br /&gt;
*CMD.EXE&lt;br /&gt;
*HARDERR.EXE&lt;br /&gt;
*SYSINST1.EXE&lt;br /&gt;
*SYSINST2.EXE&lt;br /&gt;
*FDISK.COM&lt;br /&gt;
*SYSLEVEL.OS2&lt;br /&gt;
*DISK.NUM&lt;br /&gt;
&lt;br /&gt;
Then boot from GA's install, then use its disk 1.  Then use LA's disk from that point onward, and it'll install.&lt;br /&gt;
&lt;br /&gt;
====== Build 304 ======&lt;br /&gt;
&lt;br /&gt;
===== GA =====&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
[[Image:Vpc5x os2v20 wps.png|thumb|200px|right|OS/2 2.0 running under Virtual PC 5 for OS/2]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and [[Qemu]]... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host PC.  I've heard that VMWare has given up the compatibility mode fixes.&lt;br /&gt;
&lt;br /&gt;
I'm now running OS/2 2.0 under VMWare Player, and it seems to be running OK.  I've found one issue with networking, the Set PermaNet Server feature must be set to TRUE, under the LAPS configuration tool.&lt;br /&gt;
&lt;br /&gt;
*Qemu&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.1 ====&lt;br /&gt;
&lt;br /&gt;
Adds 32-bit Graphics Subsystem. S3 display drivers are usable under Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://drivers.s3graphics.com/en/download/drivers/legacy/Trio64V_765/eng30316.zip eng30316.zip] S3 drivers&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
*For installation with XDF diskettes, Virtual PC works, however you'll need to use the floppy drivers &amp;amp; xdf driver from OS/2 4.0&lt;br /&gt;
*You should first apply the latest fixpak (XRGW040) to use Guest Additions from Virtual PC.&lt;br /&gt;
*After this also GRADD device drivers (from Additions, IBM or Scitech SNAP) can be installed.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
Fixpak 5 or better 9 should be applied for GRADD.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp Server for e-business (4.5) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 1 (4.51) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 2 (4.52) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC and VirtualBox.&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
&lt;br /&gt;
=== Autodesk ===&lt;br /&gt;
* AutoCad &lt;br /&gt;
&lt;br /&gt;
=== IBM ===&lt;br /&gt;
* IBM DisplayWrite5/2&lt;br /&gt;
&lt;br /&gt;
=== Informix ===&lt;br /&gt;
* Wingz&lt;br /&gt;
&lt;br /&gt;
=== Lotus ===&lt;br /&gt;
* Lotus 1-2-3/G&lt;br /&gt;
* Lotus Freelance Graphics&lt;br /&gt;
&lt;br /&gt;
=== Micrografx ===&lt;br /&gt;
* Micrografx Designer 3.0&lt;br /&gt;
* Micrografx Draw for OS/2&lt;br /&gt;
&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
* Languages&lt;br /&gt;
** Microsoft MASM 6.0&lt;br /&gt;
** Microsoft C 5.1, 6.0&lt;br /&gt;
** Microsoft COBOL PDS&lt;br /&gt;
** Microsoft Basic 6.0&lt;br /&gt;
** Microsoft Basic PDS 7.0, 7.1&lt;br /&gt;
** [[Microsoft Fortran]]&lt;br /&gt;
* Productivity&lt;br /&gt;
** Microsoft Word 5.0, 5.5&lt;br /&gt;
** Microsoft Word (for Presentation Manager) 1.1&lt;br /&gt;
** Microsoft Excel 2.2, 3.0&lt;br /&gt;
&lt;br /&gt;
All of these products are 16 bit only.  While there were two pre-releases of MS OS/2 2.0 that included CL386 &amp;amp; MASM386 none of these are fully out in the wild so I'm not sure if they could produce programs that would run on the IBM OS/2 2.0 GA and beyond.&lt;br /&gt;
&lt;br /&gt;
=== StarDivision ===&lt;br /&gt;
* StarWriter 2.0 for OS/2&lt;br /&gt;
* StarOffice 3.0&lt;br /&gt;
&lt;br /&gt;
=== Watcom ===&lt;br /&gt;
* Watcom C / C++&lt;br /&gt;
* Watcom Fortran 77&lt;br /&gt;
* Watcom SQL&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Inside OS/2]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Microsoft Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=Ken_Thompson&amp;diff=36394</id>
		<title>Ken Thompson</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=Ken_Thompson&amp;diff=36394"/>
				<updated>2025-06-13T11:47:44Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:KenDMR.jpg|400px|thumb|right|Ken Thompson (sitting) and Dennis Ritchie (standing]]&lt;br /&gt;
&lt;br /&gt;
'''Ken Thompson''' (often referred to as '''ken''') was an influential American computer scientist, best known for his work on [[UNIX]] (produced in collaboration with [[Dennis Ritchie]]). He also initiated the [[C programming language]], although most of the work on that was done by Ritchie.&lt;br /&gt;
&lt;br /&gt;
He attended the University of California, Berkeley, where he received an undergraduate degree in 1965, and a master's degree in 1966. He went to work at [[Bell Labs]] in 1966, joining the Computing Sciences Research Center. He spent the rest of his career at the Labs, and its descendants., retiring in 2000. He was part of the Bell team which joined the [[Multics]] project; after Bell pulled out of Multics, he and Ritchie eventually fell into UNIX and C.&lt;br /&gt;
&lt;br /&gt;
{{semi-stub}}&lt;br /&gt;
&lt;br /&gt;
==Further reading==&lt;br /&gt;
	&lt;br /&gt;
* Brian Kernighan, [https://www.amazon.com/UNIX-History-Memoir-Brian-Kernighan/dp/1695978552 UNIX: A History and a Memoir] - has a brief bio of Thompson at the end of Chapter 2                            &lt;br /&gt;
&lt;br /&gt;
==External links==&lt;br /&gt;
&lt;br /&gt;
* [http://cs.bell-labs.co/who/ken/ Ken Thompson's Home Page]&lt;br /&gt;
* [https://history.computer.org/pioneers/pdfs/T/Thompson.pdf Kenneth Thompson] - brief biography at the IEEE Computer Society &lt;br /&gt;
* D. M. Ritchie and K. Thompson, [https://www.nokia.com/bell-labs/about/dennis-m-ritchie/cacm.html ''The Unix Time-sharing System''] ([https://www.nokia.com/bell-labs/about/dennis-m-ritchie/cacm.pdf PDF]) - 1978 [[Bell System Technical Journal]] version&lt;br /&gt;
* [https://www.nokia.com/bell-labs/about/dennis-m-ritchie/picture.html An amusing photo] - &amp;quot;Ken and [DMR] in front of a PDP-11&amp;quot;&lt;br /&gt;
* [https://computerhistory.org/wp-content/uploads/2019/10/102685442.03.01.jpg Ken and DMR] - from another angle&lt;br /&gt;
&lt;br /&gt;
{{DEFAULTSORT: Thompson, Ken}}&lt;br /&gt;
[[Category: People]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=BCPL&amp;diff=36393</id>
		<title>BCPL</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=BCPL&amp;diff=36393"/>
				<updated>2025-06-13T11:47:02Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''BCPL''' is a [[programming language]] that was first developed in the late 1960s. Although little-used nowadays, it is historically important as the [[C programming language]] is derived from it. (C can be crisply, and aptly, described as 'BCPL with [[type]]s and terser [[syntax]]'.)&lt;br /&gt;
&lt;br /&gt;
Like [[ALGOL]], from which it is descended, it includes modern [[control flow]], including 'block structure'. Unlike Algol, it did not have types; the only type supported was '[[word]]'.&lt;br /&gt;
&lt;br /&gt;
It was intended by Martin Richards, its designer, mostly for systems programming (such as [[operating system]]s, [[compiler]]s, etc), for which the type limitation was not severe. (The lack of any support for [[floating point]] made it a poor choice for classic computational [[application]]s, the main use of computers at the time.)&lt;br /&gt;
&lt;br /&gt;
BCPL was also used in a number of other significant places, including much of the early work on the [[Xerox Alto]] and the first [[Multi-User Dungeon]].&lt;br /&gt;
&lt;br /&gt;
==History==&lt;br /&gt;
&lt;br /&gt;
BCPL is based on Combined Programming Language (CPL), an ambitious collaboration between Cambridge University and the University of London, by a team including Christopher Strachey. BCPL was defined in part as a interim (originally the name apparently stood for 'Bootstrap CPL'; it later became 'Basic CPL') while waiting for CPL to appear (which it never did).&lt;br /&gt;
&lt;br /&gt;
BCPL retains much of the syntactic richness of CPL, but did so while considerably limiting its complexity - thereby producing a very elegant language.&lt;br /&gt;
&lt;br /&gt;
It was first implemented on the [[CTSS]] operating system, while Richards was visiting MIT. A number of the early [[UNIX]] personnel from Bell Labs became familiar with it there, while working on [[Multics]].                  &lt;br /&gt;
&lt;br /&gt;
==Portability==&lt;br /&gt;
&lt;br /&gt;
[[Portable|Portability]] was a significant goal of BCPL, both in the language itself, and also in the openly available original BCPL compiler, itself written in BCPL; this compiler was ported to a large number of machines.&lt;br /&gt;
&lt;br /&gt;
The compiler was structured as three phases, the third of which converted a machine-independent intermediate language called OCODE, generated by the second phase, into the target machine's [[object code]].&lt;br /&gt;
&lt;br /&gt;
Porting the compiler involved writing a new third phase; compiling that with the existing compiler on the host machine, producing a [[cross-compiler]]; and then running the compiler itself through the cross-compiler, producing object code for a native compiler for the target machine.&lt;br /&gt;
&lt;br /&gt;
==External links==&lt;br /&gt;
&lt;br /&gt;
* [https://www.nokia.com/bell-labs/about/dennis-m-ritchie/bcpl.html Martin Richards's BCPL Reference Manual, 1967]&lt;br /&gt;
** [https://www.nokia.com/bell-labs/about/dennis-m-ritchie/bcpl.pdf BCPL Reference Manual]&lt;br /&gt;
* [https://dl.acm.org/doi/10.1145/1476793.1476880 BCPL: a tool for compiler writing and system programming], Richards 1969&lt;br /&gt;
* [http://www.softwarepreservation.net/projects/BCPL History of BCPL] - a wealth of material, including many compilers&lt;br /&gt;
* [https://www.ancientgeek.org.uk/CPL/ CPL] - a collection of interesting CPL material&lt;br /&gt;
** [https://www.ancientgeek.org.uk/CPL/The_Main_Features_of_CPL.pdf The main features of CPL]&lt;br /&gt;
** [https://www.ancientgeek.org.uk/CPL/CPL_Elementary_Programming_Manual.pdf CPL Elementary Programming Manual]&lt;br /&gt;
* [https://www.cl.cam.ac.uk/~mr10/cpl2bcpl.pdf How BCPL evolved from CPL]&lt;br /&gt;
* [https://arstechnica.com/features/2020/12/a-damn-stupid-thing-to-do-the-origins-of-c/ “A damn stupid thing to do” — the origins of C] - has good coverage of the CPL story&lt;br /&gt;
&lt;br /&gt;
===Various implementations===&lt;br /&gt;
&lt;br /&gt;
* [https://www.cl.cam.ac.uk/~mr10/BCPLCTSS.html CTSS BCPL] (from MIT)&lt;br /&gt;
* [https://people.csail.mit.edu/saltzer/Multics/Multics-Documents/MSPM/bz-6-00.680618.bcpl-overview.pdf Multics BCPL]&lt;br /&gt;
* [http://www.bitsavers.org/pdf/mit/tx-2/TX-2_BCPL_Reference_Manual_May69.pdf TX-2 BCPL manual, 1969]&lt;br /&gt;
* [https://github.com/PDP-10/essex-bcpl Essex BCPL] for TOPS-10&lt;br /&gt;
* [https://github.com/PDP-10/tenex-bcpl TENEX BCPL] from BBN - source code only&lt;br /&gt;
* [http://bitsavers.trailing-edge.com/pdf/xerox/alto/bcpl/ Alto BCPL]&lt;br /&gt;
* [http://bitsavers.org/pdf/aarhusUniversity/md/MD-38_The_RIKKE_BCPL_System_Sep80.pdf The RIKKE BCPL System]&lt;br /&gt;
* [https://www.cpcwiki.eu/index.php/BCPL Arnor BCPL] for Amstrad CPC and PCW computers&lt;br /&gt;
* [https://github.com/LardoBoffin/BBC-BCPL_Overview Acornsoft BCPL] for the BBC Micro&lt;br /&gt;
&lt;br /&gt;
[[Category: Languages]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=Hello,_world&amp;diff=36392</id>
		<title>Hello, world</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=Hello,_world&amp;diff=36392"/>
				<updated>2025-06-13T11:45:41Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Hello World''' is the canonical 'shortest example [[program]]' in any given [[programming language]]; a program which simply prints 'hello, world', and then terminates.&lt;br /&gt;
&lt;br /&gt;
The first version appeared in &amp;quot;''A Tutorial Introduction to the Programming Language [[B programming language|B]]''&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main( ) {&lt;br /&gt;
 extrn a, b, c;&lt;br /&gt;
 putchar(a); putchar(b); putchar(c); putchar('!*n');&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
a 'hell';&lt;br /&gt;
b 'o, w';&lt;br /&gt;
c 'orld';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It was popularized with one in the '[[C programming language|C]] Tutorial' in [[UNIX Sixth Edition|UNIX V6]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main() {&lt;br /&gt;
	printf(&amp;quot;hello, world&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{semi-stub}}&lt;br /&gt;
&lt;br /&gt;
==External links==&lt;br /&gt;
&lt;br /&gt;
* S. C. Johnson, B. W. Kernighan, [https://www.nokia.com/bell-labs/about/dennis-m-ritchie/bintro.html ''The Programming Language B'']&lt;br /&gt;
** B. W. Kernighan, [https://www.nokia.com/bell-labs/about/dennis-m-ritchie/btut.html  ''A Tutorial Introduction to the Programming Language B'']&lt;br /&gt;
*  Brian W. Kernighan, [https://www.nokia.com/bell-labs/about/dennis-m-ritchie/ctut.pdf ''Programming in C - A Tutorial'']&lt;br /&gt;
&lt;br /&gt;
[[Category: Demo Software]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=Phar_Lap&amp;diff=36391</id>
		<title>Phar Lap</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=Phar_Lap&amp;diff=36391"/>
				<updated>2025-06-13T11:43:08Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Phar Lap Software Inc.''' was more so known for inventing the concept of a [[DOS extender]].&lt;br /&gt;
&lt;br /&gt;
Their known extenders include:&lt;br /&gt;
&lt;br /&gt;
* [[286 DOS-Extender]]&lt;br /&gt;
* [[386 DOS-Extender]]&lt;br /&gt;
* [[TNT DOS extender]]&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
&lt;br /&gt;
[[Category: Software Manufacturers]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=File:Qnx4n000.png&amp;diff=10180</id>
		<title>File:Qnx4n000.png</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=File:Qnx4n000.png&amp;diff=10180"/>
				<updated>2011-03-30T20:08:55Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: QNX 4 Boot&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;QNX 4 Boot&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=QNX&amp;diff=10179</id>
		<title>QNX</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=QNX&amp;diff=10179"/>
				<updated>2011-03-30T20:08:07Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: Created page with &amp;quot;== History ==  * 1982: Quantum Software - QUNIX for Intel 8088  == QNX 4 ==  Latest Version:  == QNX 6 ==  Latest Version: 6.5.0&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== History ==&lt;br /&gt;
&lt;br /&gt;
* 1982: Quantum Software - QUNIX for Intel 8088&lt;br /&gt;
&lt;br /&gt;
== QNX 4 ==&lt;br /&gt;
&lt;br /&gt;
Latest Version:&lt;br /&gt;
&lt;br /&gt;
== QNX 6 ==&lt;br /&gt;
&lt;br /&gt;
Latest Version: 6.5.0&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=10173</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=10173"/>
				<updated>2011-03-30T01:59:01Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* OS/2 1.3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
OS/2 started as a collabrative effort between [[IBM]] and [[Microsoft]] to put together the next generation Operating System for the [[IBM AT]] and [[PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Windows]] project around the same time, as a low cost entry interface with rudementary (cooperative) multitasking&lt;br /&gt;
&lt;br /&gt;
Needless to say Microsoft wanted to target the [[i386]] processor, and work on 32bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and demanded the [[i286]] 16bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was *NOT* a big seller.  Instead the industry worked around OS/2, and developed [[DOS Extenders]] technology, and Microsoft practically gave away the Windows SDK, allowed for OEM customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft lept at the chance to formalize DOS extenders into [[DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on google's usenet archive:&lt;br /&gt;
&lt;br /&gt;
http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e&lt;br /&gt;
Or locally here [[Gordon Letwin OS/2 usenet post]].&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an [[i286]] cpu, and an [[IBM AT]], or [[PS/2]] compatible computer.&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
This version was all textmode, and had an interface that was inspired from TopView.  Although it could multitask, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in real &amp;amp; protected mode.&lt;br /&gt;
&lt;br /&gt;
*1.1&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
This version introduced Presentation Manager.  It 'looked' identical to that of [[Windows 2.0]].&lt;br /&gt;
&lt;br /&gt;
*1.2&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
I think this version was released in September of 1988. This release was significant with the inclusion of the HPFS filesystem.  HPFS was significantly faster then the aging FAT filesystem as it placed its tables in the middle of the disk, and it allowed for larger filesystems, long filenames and extended attributes.  A later service pack allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
&lt;br /&gt;
[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false here].&lt;br /&gt;
&lt;br /&gt;
*1.3&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32bit HPFS driver in their Lan Manager package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applettes &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32bit versions ===&lt;br /&gt;
All of these versions require an [[i386]] SX or better CPU running on either an [[IBM AT]] compatible motherboard, or the [[IBM PS/2]] 32bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (12/1991)&lt;br /&gt;
This was the first 32bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (04/1992)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16bit device drivers as the kernel was still a hybrid 16bit/32bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of ram on a 386sx cpu.  Warp also included the 'bonus pack' which included SLIP/PPP TCP/IP, a dialer application and a word processor &amp;amp; spreadsheet.  A simple gopher client &amp;amp; NNTP client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including LAN drivers (that was WARP CONNECT), while Windows 95 &amp;amp; NT 3.5 both included SLIP/PPP *AND* lan drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
[[Image:logo-warp.gif|thumb|150px|right|OS/2 Warp Logo]]&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revelutionalized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run x86 code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[RISC]] cpu could in fact run x86 instructions much quicker then a real Intel cpu...  This opened the way to the Pentium CPUs and effectivly killed the [[RISC]] revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://pages.prodigy.net/michaln/history/os2ppc/index.html here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
==== OS/2 1.0 ====&lt;br /&gt;
First version from November 1987 - no success.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]].  I'll try to get the hex 'diffs' so these can be saved &amp;amp; identified...&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.  The last major hurdle is the method of switching from [[protected mode]] to [[real mode]].  Prior to the last fixpack for OS/2 1.3 the method was a [[tripple fault]].  &lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
* [http://support.microsoft.com/kb/102628/en-us Microsoft OS/2 1.3 HCL]&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.0 ====&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
[[Image:Vpc5x os2v20 wps.png|thumb|200px|right|OS/2 2.0 running under Virtual PC 5 for OS/2]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and Qemu... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host pc.  I've heard that VMWare has given up the compatability mode fixes.&lt;br /&gt;
&lt;br /&gt;
*[[Qemu]]&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.1 ====&lt;br /&gt;
&lt;br /&gt;
Adds 32-bit Graphics Subsystem. S3 display drivers are usable under Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://drivers.s3graphics.com/en/download/drivers/legacy/Trio64V_765/eng30316.zip eng30316.zip] S3 drivers&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
You should first apply the latest fixpak (XRGW040) to use Guest Additions from Virtual PC.&lt;br /&gt;
After this also GRADD device drivers (from Additions, IBM or Scitech SNAP) can be installed.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
Fixpak 5 or better 9 shoul be applied for GRADD.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp Server for e-business (4.5) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 1 (4.51) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 2 (4.52) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC and VirtualBox.&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
=== IBM ===&lt;br /&gt;
* IBM DisplayWrite5/2&lt;br /&gt;
&lt;br /&gt;
=== Informix ===&lt;br /&gt;
* Wingz&lt;br /&gt;
&lt;br /&gt;
=== Lotus ===&lt;br /&gt;
* Lotus 1-2-3/G&lt;br /&gt;
* Lotus Freelance Graphics&lt;br /&gt;
&lt;br /&gt;
=== Micrografx ===&lt;br /&gt;
* Micrografx Designer 3.0&lt;br /&gt;
* Micrografx Draw for OS/2&lt;br /&gt;
&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
* Languages&lt;br /&gt;
** Microsoft MASM 6.0&lt;br /&gt;
** Microsoft C 5.1, 6.0&lt;br /&gt;
** Microsoft COBOL PDS&lt;br /&gt;
** Microsoft Basic PDS 7.0, 7.1&lt;br /&gt;
** Microsoft Fortran&lt;br /&gt;
* Productivity&lt;br /&gt;
** Microsoft Word 5.0, 5.5&lt;br /&gt;
** Microsoft Word (for Presentation Manager) 1.1&lt;br /&gt;
** Microsoft Excel 2.2, 3.0&lt;br /&gt;
&lt;br /&gt;
=== StarDivision ===&lt;br /&gt;
* StarWriter 2.0 für OS/2&lt;br /&gt;
* StarOffice 3.0&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
[[Category:Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10172</id>
		<title>UnixWare</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10172"/>
				<updated>2011-03-30T01:39:55Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox OS &lt;br /&gt;
| image = UnixWare 7.1.1 login.png&lt;br /&gt;
| caption = Logging into a UnixWare 7.1.3 system&lt;br /&gt;
| name = UnixWare&lt;br /&gt;
| creator = Univel, Novell &amp;amp; SCO&lt;br /&gt;
| current version = 7.1.4 &lt;br /&gt;
| year introduced = 1992&lt;br /&gt;
| type = Multitasking, multiuser&lt;br /&gt;
| architecture = [[IBM-PC]] theoretically portable&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
I've seen UnixWare used in all kinds of call centre applications with the AT&amp;amp;T g3 definity switch... I remember this thing running the voice mail appliances, and a few other things.  UnixWare ran on x86 computers. Unixware is a [[SYSV]] r4 derived OS.&lt;br /&gt;
&lt;br /&gt;
== Version 1 ==&lt;br /&gt;
Unixware started out as a Univel/USL product, 1.0 was primarily setup to integrate in [[Netware]] environments, I recall TCP/IP being optional!  &lt;br /&gt;
== Version 2 ==&lt;br /&gt;
Versions 2 included multiprocessor support, and then was purchased by By SCO who released 3 updates for version 2.&lt;br /&gt;
&lt;br /&gt;
=== Novell UnixWare 2.0 ===&lt;br /&gt;
* Virtual PC: 86C764 Trio64 chipset supports 64K colours and maximum resolution 1280x1024.&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 2.1.2 ===&lt;br /&gt;
&lt;br /&gt;
* Update to 2.1.3&lt;br /&gt;
* Update to 2.1.4&lt;br /&gt;
&lt;br /&gt;
== Version 7 ==&lt;br /&gt;
This version removed support for legacy Xenix binaries to cut out the Microsoft royalty.&lt;br /&gt;
&lt;br /&gt;
* Virtual PC: 86C764 Trio64 chipset supports 64K colours and maximum resolution 1280x1024.&lt;br /&gt;
&lt;br /&gt;
=== UnixWare 7.0 ===&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/rs700/ Release Supplement] Mar.1998 (FTP)&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/update701/ Update to 7.0.1] Sep.1998 (FTP)&lt;br /&gt;
&lt;br /&gt;
=== UnixWare 7.0.1 ===&lt;br /&gt;
&lt;br /&gt;
Needs boot floppy.&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.0 ===&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.1 ===&lt;br /&gt;
&lt;br /&gt;
[[Image:UnixWare 7.1.1 box.jpg|200px|right|UnixWare 7.1.1 box]]&lt;br /&gt;
Latest Maintenance Pack: MP5&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/uw711pk/ Unixware 7.1.1 MP5] Dec. 2004 (FTP)&lt;br /&gt;
&lt;br /&gt;
=== Caldera OpenUnix 8 (7.1.2) ===&lt;br /&gt;
&lt;br /&gt;
Integrates Linux Kernel Personality (LKP).&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.3 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware713/ SCO UnixWare 7.1.3] Product&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.4 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware714/ SCO UnixWare 7.1.4] Product&lt;br /&gt;
* [http://sco.com/support/update/download/product.php?pfid=1&amp;amp;prid=6 UnixWare 7.1.4 Supplements]&lt;br /&gt;
* [http://sco.com/support/update/download/release.php?rid=346 ISO Image CD1] Updated 2008&lt;br /&gt;
&lt;br /&gt;
[[Category:Operating Systems]]&lt;br /&gt;
{{Nav Unix}}&lt;br /&gt;
{{stub}}&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10171</id>
		<title>UnixWare</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10171"/>
				<updated>2011-03-30T01:38:32Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* Version 2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox OS &lt;br /&gt;
| image = UnixWare 7.1.1 login.png&lt;br /&gt;
| caption = Logging into a UnixWare 7.1.3 system&lt;br /&gt;
| name = UnixWare&lt;br /&gt;
| creator = Univel, Novell &amp;amp; SCO&lt;br /&gt;
| current version = 7.1.4 &lt;br /&gt;
| year introduced = 1992&lt;br /&gt;
| type = Multitasking, multiuser&lt;br /&gt;
| architecture = [[IBM-PC]] theoretically portable&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Image:UnixWare 7.1.1 box.jpg|200px|right|UnixWare 7.1.1 box]]&lt;br /&gt;
&lt;br /&gt;
I've seen UnixWare used in all kinds of call centre applications with the AT&amp;amp;T g3 definity switch... I remember this thing running the voice mail appliances, and a few other things.  UnixWare ran on x86 computers. Unixware is a [[SYSV]] r4 derived OS.&lt;br /&gt;
&lt;br /&gt;
== Version 1 ==&lt;br /&gt;
Unixware started out as a Univel/USL product, 1.0 was primarily setup to integrate in [[Netware]] environments, I recall TCP/IP being optional!  &lt;br /&gt;
== Version 2 ==&lt;br /&gt;
Versions 2 included multiprocessor support, and then was purchased by By SCO who released 3 updates for version 2.&lt;br /&gt;
&lt;br /&gt;
=== Novell UnixWare 2.0 ===&lt;br /&gt;
* Virtual PC: 86C764 Trio64 chipset supports 64K colours and maximum resolution 1280x1024.&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 2.1.2 ===&lt;br /&gt;
&lt;br /&gt;
* Update to 2.1.3&lt;br /&gt;
* Update to 2.1.4&lt;br /&gt;
&lt;br /&gt;
== Version 7 ==&lt;br /&gt;
This version removed support for legacy Xenix binaries to cut out the Microsoft royalty.&lt;br /&gt;
&lt;br /&gt;
* Virtual PC: 86C764 Trio64 chipset supports 64K colours and maximum resolution 1280x1024.&lt;br /&gt;
&lt;br /&gt;
=== UnixWare 7.0 ===&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/rs700/ Release Supplement] Mar.1998 (FTP)&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/update701/ Update to 7.0.1] Sep.1998 (FTP)&lt;br /&gt;
&lt;br /&gt;
=== UnixWare 7.0.1 ===&lt;br /&gt;
&lt;br /&gt;
Needs boot floppy.&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.0 ===&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.1 ===&lt;br /&gt;
&lt;br /&gt;
Latest Maintenance Pack: MP5&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/uw711pk/ Unixware 7.1.1 MP5] Dec. 2004 (FTP)&lt;br /&gt;
&lt;br /&gt;
=== Caldera OpenUnix 8 (7.1.2) ===&lt;br /&gt;
&lt;br /&gt;
Integrates Linux Kernel Personality (LKP).&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.3 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware713/ SCO UnixWare 7.1.3] Product&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.4 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware714/ SCO UnixWare 7.1.4] Product&lt;br /&gt;
* [http://sco.com/support/update/download/product.php?pfid=1&amp;amp;prid=6 UnixWare 7.1.4 Supplements]&lt;br /&gt;
* [http://sco.com/support/update/download/release.php?rid=346 ISO Image CD1] Updated 2008&lt;br /&gt;
&lt;br /&gt;
[[Category:Operating Systems]]&lt;br /&gt;
{{Nav Unix}}&lt;br /&gt;
{{stub}}&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10170</id>
		<title>UnixWare</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10170"/>
				<updated>2011-03-30T01:37:02Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* Version 7 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox OS &lt;br /&gt;
| image = UnixWare 7.1.1 login.png&lt;br /&gt;
| caption = Logging into a UnixWare 7.1.3 system&lt;br /&gt;
| name = UnixWare&lt;br /&gt;
| creator = Univel, Novell &amp;amp; SCO&lt;br /&gt;
| current version = 7.1.4 &lt;br /&gt;
| year introduced = 1992&lt;br /&gt;
| type = Multitasking, multiuser&lt;br /&gt;
| architecture = [[IBM-PC]] theoretically portable&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Image:UnixWare 7.1.1 box.jpg|200px|right|UnixWare 7.1.1 box]]&lt;br /&gt;
&lt;br /&gt;
I've seen UnixWare used in all kinds of call centre applications with the AT&amp;amp;T g3 definity switch... I remember this thing running the voice mail appliances, and a few other things.  UnixWare ran on x86 computers. Unixware is a [[SYSV]] r4 derived OS.&lt;br /&gt;
&lt;br /&gt;
== Version 1 ==&lt;br /&gt;
Unixware started out as a Univel/USL product, 1.0 was primarily setup to integrate in [[Netware]] environments, I recall TCP/IP being optional!  &lt;br /&gt;
== Version 2 ==&lt;br /&gt;
Versions 2 included multiprocessor support, and then was purchased by By SCO who released 3 updates for version 2.&lt;br /&gt;
== Version 7 ==&lt;br /&gt;
This version removed support for legacy Xenix binaries to cut out the Microsoft royalty.&lt;br /&gt;
&lt;br /&gt;
* Virtual PC: 86C764 Trio64 chipset supports 64K colours and maximum resolution 1280x1024.&lt;br /&gt;
&lt;br /&gt;
=== UnixWare 7.0 ===&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/rs700/ Release Supplement] Mar.1998 (FTP)&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/update701/ Update to 7.0.1] Sep.1998 (FTP)&lt;br /&gt;
&lt;br /&gt;
=== UnixWare 7.0.1 ===&lt;br /&gt;
&lt;br /&gt;
Needs boot floppy.&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.0 ===&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.1 ===&lt;br /&gt;
&lt;br /&gt;
Latest Maintenance Pack: MP5&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/uw711pk/ Unixware 7.1.1 MP5] Dec. 2004 (FTP)&lt;br /&gt;
&lt;br /&gt;
=== Caldera OpenUnix 8 (7.1.2) ===&lt;br /&gt;
&lt;br /&gt;
Integrates Linux Kernel Personality (LKP).&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.3 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware713/ SCO UnixWare 7.1.3] Product&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.4 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware714/ SCO UnixWare 7.1.4] Product&lt;br /&gt;
* [http://sco.com/support/update/download/product.php?pfid=1&amp;amp;prid=6 UnixWare 7.1.4 Supplements]&lt;br /&gt;
* [http://sco.com/support/update/download/release.php?rid=346 ISO Image CD1] Updated 2008&lt;br /&gt;
&lt;br /&gt;
[[Category:Operating Systems]]&lt;br /&gt;
{{Nav Unix}}&lt;br /&gt;
{{stub}}&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=File:Vpc_os2_w3.png&amp;diff=10169</id>
		<title>File:Vpc os2 w3.png</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=File:Vpc_os2_w3.png&amp;diff=10169"/>
				<updated>2011-03-30T01:27:32Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: Virtual PC: OS/2 Warp 3 guest&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Virtual PC: OS/2 Warp 3 guest&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=File:SC_os2_20pr_i2.png&amp;diff=10168</id>
		<title>File:SC os2 20pr i2.png</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=File:SC_os2_20pr_i2.png&amp;diff=10168"/>
				<updated>2011-03-30T01:25:42Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: LA Rev&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;LA Rev&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=File:SC_os2_20pr_i1.png&amp;diff=10167</id>
		<title>File:SC os2 20pr i1.png</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=File:SC_os2_20pr_i1.png&amp;diff=10167"/>
				<updated>2011-03-30T01:25:05Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: LA&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;LA&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=10166</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=10166"/>
				<updated>2011-03-30T01:01:39Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* Popular Applications */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
OS/2 started as a collabrative effort between [[IBM]] and [[Microsoft]] to put together the next generation Operating System for the [[IBM AT]] and [[PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Windows]] project around the same time, as a low cost entry interface with rudementary (cooperative) multitasking&lt;br /&gt;
&lt;br /&gt;
Needless to say Microsoft wanted to target the [[i386]] processor, and work on 32bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and demanded the [[i286]] 16bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was *NOT* a big seller.  Instead the industry worked around OS/2, and developed [[DOS Extenders]] technology, and Microsoft practically gave away the Windows SDK, allowed for OEM customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft lept at the chance to formalize DOS extenders into [[DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on google's usenet archive:&lt;br /&gt;
&lt;br /&gt;
http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e&lt;br /&gt;
Or locally here [[Gordon Letwin OS/2 usenet post]].&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an [[i286]] cpu, and an [[IBM AT]], or [[PS/2]] compatible computer.&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
This version was all textmode, and had an interface that was inspired from TopView.  Although it could multitask, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in real &amp;amp; protected mode.&lt;br /&gt;
&lt;br /&gt;
*1.1&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
This version introduced Presentation Manager.  It 'looked' identical to that of [[Windows 2.0]].&lt;br /&gt;
&lt;br /&gt;
*1.2&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
I think this version was released in September of 1988. This release was significant with the inclusion of the HPFS filesystem.  HPFS was significantly faster then the aging FAT filesystem as it placed its tables in the middle of the disk, and it allowed for larger filesystems, long filenames and extended attributes.  A later service pack allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
&lt;br /&gt;
[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false here].&lt;br /&gt;
&lt;br /&gt;
*1.3&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32bit HPFS driver in their Lan Manager package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applettes &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32bit versions ===&lt;br /&gt;
All of these versions require an [[i386]] SX or better CPU running on either an [[IBM AT]] compatible motherboard, or the [[IBM PS/2]] 32bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (12/1991)&lt;br /&gt;
This was the first 32bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (04/1992)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16bit device drivers as the kernel was still a hybrid 16bit/32bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of ram on a 386sx cpu.  Warp also included the 'bonus pack' which included SLIP/PPP TCP/IP, a dialer application and a word processor &amp;amp; spreadsheet.  A simple gopher client &amp;amp; NNTP client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including LAN drivers (that was WARP CONNECT), while Windows 95 &amp;amp; NT 3.5 both included SLIP/PPP *AND* lan drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
[[Image:logo-warp.gif|thumb|150px|right|OS/2 Warp Logo]]&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revelutionalized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run x86 code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[RISC]] cpu could in fact run x86 instructions much quicker then a real Intel cpu...  This opened the way to the Pentium CPUs and effectivly killed the [[RISC]] revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://pages.prodigy.net/michaln/history/os2ppc/index.html here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
==== OS/2 1.0 ====&lt;br /&gt;
First version from November 1987 - no success.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]].  I'll try to get the hex 'diffs' so these can be saved &amp;amp; identified...&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.  The last major hurdle is the method of switching from [[protected mode]] to [[real mode]].  Prior to the last fixpack for OS/2 1.3 the method was a [[tripple fault]].  &lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.0 ====&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
[[Image:Vpc5x os2v20 wps.png|thumb|200px|right|OS/2 2.0 running under Virtual PC 5 for OS/2]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and Qemu... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host pc.  I've heard that VMWare has given up the compatability mode fixes.&lt;br /&gt;
&lt;br /&gt;
*[[Qemu]]&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.1 ====&lt;br /&gt;
&lt;br /&gt;
Adds 32-bit Graphics Subsystem. S3 display drivers are usable under Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://drivers.s3graphics.com/en/download/drivers/legacy/Trio64V_765/eng30316.zip eng30316.zip] S3 drivers&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
You should first apply the latest fixpak (XRGW040) to use Guest Additions from Virtual PC.&lt;br /&gt;
After this also GRADD device drivers (from Additions, IBM or Scitech SNAP) can be installed.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
Fixpak 5 or better 9 shoul be applied for GRADD.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp Server for e-business (4.5) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 1 (4.51) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 2 (4.52) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC and VirtualBox.&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
=== IBM ===&lt;br /&gt;
* IBM DisplayWrite5/2&lt;br /&gt;
&lt;br /&gt;
=== Informix ===&lt;br /&gt;
* Wingz&lt;br /&gt;
&lt;br /&gt;
=== Lotus ===&lt;br /&gt;
* Lotus 1-2-3/G&lt;br /&gt;
* Lotus Freelance Graphics&lt;br /&gt;
&lt;br /&gt;
=== Micrografx ===&lt;br /&gt;
* Micrografx Designer 3.0&lt;br /&gt;
* Micrografx Draw for OS/2&lt;br /&gt;
&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
* Languages&lt;br /&gt;
** Microsoft MASM 6.0&lt;br /&gt;
** Microsoft C 5.1, 6.0&lt;br /&gt;
** Microsoft COBOL PDS&lt;br /&gt;
** Microsoft Basic PDS 7.0, 7.1&lt;br /&gt;
** Microsoft Fortran&lt;br /&gt;
* Productivity&lt;br /&gt;
** Microsoft Word 5.0, 5.5&lt;br /&gt;
** Microsoft Word (for Presentation Manager) 1.1&lt;br /&gt;
** Microsoft Excel 2.2, 3.0&lt;br /&gt;
&lt;br /&gt;
=== StarDivision ===&lt;br /&gt;
* StarWriter 2.0 für OS/2&lt;br /&gt;
* StarOffice 3.0&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
[[Category:Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=10165</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=10165"/>
				<updated>2011-03-30T00:58:50Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* OS/2 2.0 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
OS/2 started as a collabrative effort between [[IBM]] and [[Microsoft]] to put together the next generation Operating System for the [[IBM AT]] and [[PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Windows]] project around the same time, as a low cost entry interface with rudementary (cooperative) multitasking&lt;br /&gt;
&lt;br /&gt;
Needless to say Microsoft wanted to target the [[i386]] processor, and work on 32bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and demanded the [[i286]] 16bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was *NOT* a big seller.  Instead the industry worked around OS/2, and developed [[DOS Extenders]] technology, and Microsoft practically gave away the Windows SDK, allowed for OEM customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft lept at the chance to formalize DOS extenders into [[DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on google's usenet archive:&lt;br /&gt;
&lt;br /&gt;
http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e&lt;br /&gt;
Or locally here [[Gordon Letwin OS/2 usenet post]].&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an [[i286]] cpu, and an [[IBM AT]], or [[PS/2]] compatible computer.&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
This version was all textmode, and had an interface that was inspired from TopView.  Although it could multitask, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in real &amp;amp; protected mode.&lt;br /&gt;
&lt;br /&gt;
*1.1&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
This version introduced Presentation Manager.  It 'looked' identical to that of [[Windows 2.0]].&lt;br /&gt;
&lt;br /&gt;
*1.2&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
I think this version was released in September of 1988. This release was significant with the inclusion of the HPFS filesystem.  HPFS was significantly faster then the aging FAT filesystem as it placed its tables in the middle of the disk, and it allowed for larger filesystems, long filenames and extended attributes.  A later service pack allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
&lt;br /&gt;
[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false here].&lt;br /&gt;
&lt;br /&gt;
*1.3&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32bit HPFS driver in their Lan Manager package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applettes &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32bit versions ===&lt;br /&gt;
All of these versions require an [[i386]] SX or better CPU running on either an [[IBM AT]] compatible motherboard, or the [[IBM PS/2]] 32bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (12/1991)&lt;br /&gt;
This was the first 32bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (04/1992)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16bit device drivers as the kernel was still a hybrid 16bit/32bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of ram on a 386sx cpu.  Warp also included the 'bonus pack' which included SLIP/PPP TCP/IP, a dialer application and a word processor &amp;amp; spreadsheet.  A simple gopher client &amp;amp; NNTP client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including LAN drivers (that was WARP CONNECT), while Windows 95 &amp;amp; NT 3.5 both included SLIP/PPP *AND* lan drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
[[Image:logo-warp.gif|thumb|150px|right|OS/2 Warp Logo]]&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revelutionalized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run x86 code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[RISC]] cpu could in fact run x86 instructions much quicker then a real Intel cpu...  This opened the way to the Pentium CPUs and effectivly killed the [[RISC]] revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://pages.prodigy.net/michaln/history/os2ppc/index.html here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
==== OS/2 1.0 ====&lt;br /&gt;
First version from November 1987 - no success.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]].  I'll try to get the hex 'diffs' so these can be saved &amp;amp; identified...&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.  The last major hurdle is the method of switching from [[protected mode]] to [[real mode]].  Prior to the last fixpack for OS/2 1.3 the method was a [[tripple fault]].  &lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.0 ====&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
[[Image:Vpc5x os2v20 wps.png|thumb|200px|right|OS/2 2.0 running under Virtual PC 5 for OS/2]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and Qemu... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host pc.  I've heard that VMWare has given up the compatability mode fixes.&lt;br /&gt;
&lt;br /&gt;
*[[Qemu]]&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.1 ====&lt;br /&gt;
&lt;br /&gt;
Adds 32-bit Graphics Subsystem. S3 display drivers are usable under Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://drivers.s3graphics.com/en/download/drivers/legacy/Trio64V_765/eng30316.zip eng30316.zip] S3 drivers&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
You should first apply the latest fixpak (XRGW040) to use Guest Additions from Virtual PC.&lt;br /&gt;
After this also GRADD device drivers (from Additions, IBM or Scitech SNAP) can be installed.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
Fixpak 5 or better 9 shoul be applied for GRADD.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp Server for e-business (4.5) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 1 (4.51) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 2 (4.52) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC and VirtualBox.&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
=== IBM ===&lt;br /&gt;
* IBM DisplayWrite5/2&lt;br /&gt;
&lt;br /&gt;
=== Lotus ===&lt;br /&gt;
* Lotus 1-2-3/G&lt;br /&gt;
* Lotus Freelance Graphics&lt;br /&gt;
&lt;br /&gt;
=== Micrografx ===&lt;br /&gt;
* Micrografx Designer 3.0&lt;br /&gt;
* Micrografx Draw for OS/2&lt;br /&gt;
&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
* Languages&lt;br /&gt;
** Microsoft MASM 6.0&lt;br /&gt;
** Microsoft C 5.1, 6.0&lt;br /&gt;
** Microsoft COBOL PDS&lt;br /&gt;
** Microsoft Basic PDS 7.0, 7.1&lt;br /&gt;
** Microsoft Fortran&lt;br /&gt;
* Productivity&lt;br /&gt;
** Microsoft Word 5.0, 5.5&lt;br /&gt;
** Microsoft Word (for Presentation Manager) 1.1&lt;br /&gt;
** Microsoft Excel 2.2, 3.0&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
[[Category:Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=File:Vpc5x_os2v20_wps.png&amp;diff=10164</id>
		<title>File:Vpc5x os2v20 wps.png</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=File:Vpc5x_os2v20_wps.png&amp;diff=10164"/>
				<updated>2011-03-30T00:56:12Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: IBM OS/2 2.0 in Virtual PC dor OS/2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IBM OS/2 2.0 in Virtual PC dor OS/2&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=AIX&amp;diff=10163</id>
		<title>AIX</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=AIX&amp;diff=10163"/>
				<updated>2011-03-30T00:50:57Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox OS &lt;br /&gt;
| image = logo-aix.gif&lt;br /&gt;
| caption = Logging into an AIX system&lt;br /&gt;
| name = AIX&lt;br /&gt;
| creator = [[IBM]]&lt;br /&gt;
| current version = 5.1L for RS/6000&lt;br /&gt;
| year introduced = 1987&lt;br /&gt;
| type = Multitasking, multiuser UNIX&lt;br /&gt;
| architecture = [[IBM 386]], [[RS/6000]], [[System/370]]&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
I just remember that IBM had someone do a port of SYSV to the PS/2, then it was later ported to the new POWER based RS/6000 platform, and even to the 370 mainframe.&lt;br /&gt;
&lt;br /&gt;
== Version 1.x ==&lt;br /&gt;
This was focused on the [[PS/2]] family of computers with an [[i386]] or greater CPU.  There was also boot disks and drivers to enable any [[IBM 386]] compatible to boot AIX as well.  This version is not exactly stable..&lt;br /&gt;
&lt;br /&gt;
== Version 2.x ==&lt;br /&gt;
&lt;br /&gt;
== Version 3.x ==&lt;br /&gt;
Starting with version 3.x, AIX was built for the [[POWER]] CPU, and namely the [[RS/6000]] line of computers.&lt;br /&gt;
&lt;br /&gt;
== Version 4.x ==&lt;br /&gt;
Version 4.x introduced CPU support for the [[PowerPC]] family of processors, and included minor [[CHIRP]]/[[PReP]] compatibility, and at least one [[Apple]] server that ran AIX.&lt;br /&gt;
&lt;br /&gt;
== Version 5.x ==&lt;br /&gt;
As it remains now, it's a SYSVr3 based OS with many enhancements from BSD and Linux.&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
{{Nav Unix}}&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=10162</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=10162"/>
				<updated>2011-03-30T00:49:35Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
OS/2 started as a collabrative effort between [[IBM]] and [[Microsoft]] to put together the next generation Operating System for the [[IBM AT]] and [[PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Windows]] project around the same time, as a low cost entry interface with rudementary (cooperative) multitasking&lt;br /&gt;
&lt;br /&gt;
Needless to say Microsoft wanted to target the [[i386]] processor, and work on 32bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and demanded the [[i286]] 16bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was *NOT* a big seller.  Instead the industry worked around OS/2, and developed [[DOS Extenders]] technology, and Microsoft practically gave away the Windows SDK, allowed for OEM customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft lept at the chance to formalize DOS extenders into [[DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on google's usenet archive:&lt;br /&gt;
&lt;br /&gt;
http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e&lt;br /&gt;
Or locally here [[Gordon Letwin OS/2 usenet post]].&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an [[i286]] cpu, and an [[IBM AT]], or [[PS/2]] compatible computer.&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
This version was all textmode, and had an interface that was inspired from TopView.  Although it could multitask, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in real &amp;amp; protected mode.&lt;br /&gt;
&lt;br /&gt;
*1.1&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
This version introduced Presentation Manager.  It 'looked' identical to that of [[Windows 2.0]].&lt;br /&gt;
&lt;br /&gt;
*1.2&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
I think this version was released in September of 1988. This release was significant with the inclusion of the HPFS filesystem.  HPFS was significantly faster then the aging FAT filesystem as it placed its tables in the middle of the disk, and it allowed for larger filesystems, long filenames and extended attributes.  A later service pack allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
&lt;br /&gt;
[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false here].&lt;br /&gt;
&lt;br /&gt;
*1.3&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32bit HPFS driver in their Lan Manager package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applettes &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32bit versions ===&lt;br /&gt;
All of these versions require an [[i386]] SX or better CPU running on either an [[IBM AT]] compatible motherboard, or the [[IBM PS/2]] 32bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (12/1991)&lt;br /&gt;
This was the first 32bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (04/1992)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16bit device drivers as the kernel was still a hybrid 16bit/32bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of ram on a 386sx cpu.  Warp also included the 'bonus pack' which included SLIP/PPP TCP/IP, a dialer application and a word processor &amp;amp; spreadsheet.  A simple gopher client &amp;amp; NNTP client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including LAN drivers (that was WARP CONNECT), while Windows 95 &amp;amp; NT 3.5 both included SLIP/PPP *AND* lan drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
[[Image:logo-warp.gif|thumb|150px|right|OS/2 Warp Logo]]&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revelutionalized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run x86 code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[RISC]] cpu could in fact run x86 instructions much quicker then a real Intel cpu...  This opened the way to the Pentium CPUs and effectivly killed the [[RISC]] revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://pages.prodigy.net/michaln/history/os2ppc/index.html here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
==== OS/2 1.0 ====&lt;br /&gt;
First version from November 1987 - no success.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]].  I'll try to get the hex 'diffs' so these can be saved &amp;amp; identified...&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.  The last major hurdle is the method of switching from [[protected mode]] to [[real mode]].  Prior to the last fixpack for OS/2 1.3 the method was a [[tripple fault]].  &lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.0 ====&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and Qemu... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host pc.  I've heard that VMWare has given up the compatability mode fixes.&lt;br /&gt;
&lt;br /&gt;
*[[Qemu]]&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.1 ====&lt;br /&gt;
&lt;br /&gt;
Adds 32-bit Graphics Subsystem. S3 display drivers are usable under Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://drivers.s3graphics.com/en/download/drivers/legacy/Trio64V_765/eng30316.zip eng30316.zip] S3 drivers&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
You should first apply the latest fixpak (XRGW040) to use Guest Additions from Virtual PC.&lt;br /&gt;
After this also GRADD device drivers (from Additions, IBM or Scitech SNAP) can be installed.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
Fixpak 5 or better 9 shoul be applied for GRADD.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp Server for e-business (4.5) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 1 (4.51) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 2 (4.52) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC and VirtualBox.&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
=== IBM ===&lt;br /&gt;
* IBM DisplayWrite5/2&lt;br /&gt;
&lt;br /&gt;
=== Lotus ===&lt;br /&gt;
* Lotus 1-2-3/G&lt;br /&gt;
* Lotus Freelance Graphics&lt;br /&gt;
&lt;br /&gt;
=== Micrografx ===&lt;br /&gt;
* Micrografx Designer 3.0&lt;br /&gt;
* Micrografx Draw for OS/2&lt;br /&gt;
&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
* Languages&lt;br /&gt;
** Microsoft MASM 6.0&lt;br /&gt;
** Microsoft C 5.1, 6.0&lt;br /&gt;
** Microsoft COBOL PDS&lt;br /&gt;
** Microsoft Basic PDS 7.0, 7.1&lt;br /&gt;
** Microsoft Fortran&lt;br /&gt;
* Productivity&lt;br /&gt;
** Microsoft Word 5.0, 5.5&lt;br /&gt;
** Microsoft Word (for Presentation Manager) 1.1&lt;br /&gt;
** Microsoft Excel 2.2, 3.0&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
[[Category:Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=File:Logo-warp.gif&amp;diff=10161</id>
		<title>File:Logo-warp.gif</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=File:Logo-warp.gif&amp;diff=10161"/>
				<updated>2011-03-30T00:47:20Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=File:Logo-aix.gif&amp;diff=10160</id>
		<title>File:Logo-aix.gif</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=File:Logo-aix.gif&amp;diff=10160"/>
				<updated>2011-03-30T00:46:54Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=10158</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=10158"/>
				<updated>2011-03-29T23:42:53Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* OS/2 2.1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
OS/2 started as a collabrative effort between [[IBM]] and [[Microsoft]] to put together the next generation Operating System for the [[IBM AT]] and [[PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Windows]] project around the same time, as a low cost entry interface with rudementary (cooperative) multitasking&lt;br /&gt;
&lt;br /&gt;
Needless to say Microsoft wanted to target the [[i386]] processor, and work on 32bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and demanded the [[i286]] 16bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was *NOT* a big seller.  Instead the industry worked around OS/2, and developed [[DOS Extenders]] technology, and Microsoft practically gave away the Windows SDK, allowed for OEM customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft lept at the chance to formalize DOS extenders into [[DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on google's usenet archive:&lt;br /&gt;
&lt;br /&gt;
http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e&lt;br /&gt;
Or locally here [[Gordon Letwin OS/2 usenet post]].&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an [[i286]] cpu, and an [[IBM AT]], or [[PS/2]] compatible computer.&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
This version was all textmode, and had an interface that was inspired from TopView.  Although it could multitask, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in real &amp;amp; protected mode.&lt;br /&gt;
&lt;br /&gt;
*1.1&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
This version introduced Presentation Manager.  It 'looked' identical to that of [[Windows 2.0]].&lt;br /&gt;
&lt;br /&gt;
*1.2&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
I think this version was released in September of 1988. This release was significant with the inclusion of the HPFS filesystem.  HPFS was significantly faster then the aging FAT filesystem as it placed its tables in the middle of the disk, and it allowed for larger filesystems, long filenames and extended attributes.  A later service pack allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
&lt;br /&gt;
[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false here].&lt;br /&gt;
&lt;br /&gt;
*1.3&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32bit HPFS driver in their Lan Manager package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applettes &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32bit versions ===&lt;br /&gt;
All of these versions require an [[i386]] SX or better CPU running on either an [[IBM AT]] compatible motherboard, or the [[IBM PS/2]] 32bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (12/1991)&lt;br /&gt;
This was the first 32bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (04/1992)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16bit device drivers as the kernel was still a hybrid 16bit/32bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of ram on a 386sx cpu.  Warp also included the 'bonus pack' which included SLIP/PPP TCP/IP, a dialer application and a word processor &amp;amp; spreadsheet.  A simple gopher client &amp;amp; NNTP client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including LAN drivers (that was WARP CONNECT), while Windows 95 &amp;amp; NT 3.5 both included SLIP/PPP *AND* lan drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revelutionalized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run x86 code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[RISC]] cpu could in fact run x86 instructions much quicker then a real Intel cpu...  This opened the way to the Pentium CPUs and effectivly killed the [[RISC]] revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://pages.prodigy.net/michaln/history/os2ppc/index.html here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
==== OS/2 1.0 ====&lt;br /&gt;
First version from November 1987 - no success.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]].  I'll try to get the hex 'diffs' so these can be saved &amp;amp; identified...&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.  The last major hurdle is the method of switching from [[protected mode]] to [[real mode]].  Prior to the last fixpack for OS/2 1.3 the method was a [[tripple fault]].  &lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.0 ====&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and Qemu... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host pc.  I've heard that VMWare has given up the compatability mode fixes.&lt;br /&gt;
&lt;br /&gt;
*[[Qemu]]&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.1 ====&lt;br /&gt;
&lt;br /&gt;
Adds 32-bit Graphics Subsystem. S3 display drivers are usable under Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://drivers.s3graphics.com/en/download/drivers/legacy/Trio64V_765/eng30316.zip eng30316.zip] S3 drivers&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
You should first apply the latest fixpak (XRGW040) to use Guest Additions from Virtual PC.&lt;br /&gt;
After this also GRADD device drivers (from Additions, IBM or Scitech SNAP) can be installed.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
Fixpak 5 or better 9 shoul be applied for GRADD.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp Server for e-business (4.5) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 1 (4.51) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 2 (4.52) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC and VirtualBox.&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
=== IBM ===&lt;br /&gt;
* IBM DisplayWrite5/2&lt;br /&gt;
&lt;br /&gt;
=== Lotus ===&lt;br /&gt;
* Lotus 1-2-3/G&lt;br /&gt;
* Lotus Freelance Graphics&lt;br /&gt;
&lt;br /&gt;
=== Micrografx ===&lt;br /&gt;
* Micrografx Designer 3.0&lt;br /&gt;
* Micrografx Draw for OS/2&lt;br /&gt;
&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
* Languages&lt;br /&gt;
** Microsoft MASM 6.0&lt;br /&gt;
** Microsoft C 5.1, 6.0&lt;br /&gt;
** Microsoft COBOL PDS&lt;br /&gt;
** Microsoft Basic PDS 7.0, 7.1&lt;br /&gt;
** Microsoft Fortran&lt;br /&gt;
* Productivity&lt;br /&gt;
** Microsoft Word 5.0, 5.5&lt;br /&gt;
** Microsoft Word (for Presentation Manager) 1.1&lt;br /&gt;
** Microsoft Excel 2.2, 3.0&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
[[Category:Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10157</id>
		<title>UnixWare</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10157"/>
				<updated>2011-03-29T23:31:00Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* Version 7 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox OS &lt;br /&gt;
| image = UnixWare 7.1.1 login.png&lt;br /&gt;
| caption = Logging into a UnixWare 7.1.3 system&lt;br /&gt;
| name = UnixWare&lt;br /&gt;
| creator = Univel, Novell &amp;amp; SCO&lt;br /&gt;
| current version = 7.1.4 &lt;br /&gt;
| year introduced = 1992&lt;br /&gt;
| type = Multitasking, multiuser&lt;br /&gt;
| architecture = [[IBM-PC]] theoretically portable&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Image:UnixWare 7.1.1 box.jpg|200px|right|UnixWare 7.1.1 box]]&lt;br /&gt;
&lt;br /&gt;
I've seen UnixWare used in all kinds of call centre applications with the AT&amp;amp;T g3 definity switch... I remember this thing running the voice mail appliances, and a few other things.  UnixWare ran on x86 computers. Unixware is a [[SYSV]] r4 derived OS.&lt;br /&gt;
&lt;br /&gt;
== Version 1 ==&lt;br /&gt;
Unixware started out as a Univel/USL product, 1.0 was primarily setup to integrate in [[Netware]] environments, I recall TCP/IP being optional!  &lt;br /&gt;
== Version 2 ==&lt;br /&gt;
Versions 2 included multiprocessor support, and then was purchased by By SCO who released 3 updates for version 2.&lt;br /&gt;
== Version 7 ==&lt;br /&gt;
This version removed support for legacy Xenix binaries to cut out the Microsoft royalty.&lt;br /&gt;
=== UnixWare 7.0 ===&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/rs700/ Release Supplement] Mar.1998 (FTP)&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/update701/ Update to 7.0.1] Sep.1998 (FTP)&lt;br /&gt;
&lt;br /&gt;
=== UnixWare 7.0.1 ===&lt;br /&gt;
&lt;br /&gt;
Needs boot floppy.&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.0 ===&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.1 ===&lt;br /&gt;
&lt;br /&gt;
Latest Maintenance Pack: MP5&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/uw711pk/ Unixware 7.1.1 MP5] Dec. 2004 (FTP)&lt;br /&gt;
&lt;br /&gt;
=== Caldera OpenUnix 8 (7.1.2) ===&lt;br /&gt;
&lt;br /&gt;
Integrates Linux Kernel Personality (LKP).&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.3 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware713/ SCO UnixWare 7.1.3] Product&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.4 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware714/ SCO UnixWare 7.1.4] Product&lt;br /&gt;
* [http://sco.com/support/update/download/product.php?pfid=1&amp;amp;prid=6 UnixWare 7.1.4 Supplements]&lt;br /&gt;
* [http://sco.com/support/update/download/release.php?rid=346 ISO Image CD1] Updated 2008&lt;br /&gt;
&lt;br /&gt;
[[Category:Operating Systems]]&lt;br /&gt;
{{Nav Unix}}&lt;br /&gt;
{{stub}}&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10156</id>
		<title>UnixWare</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10156"/>
				<updated>2011-03-29T23:25:50Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* SCO UnixWare 7.1.1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox OS &lt;br /&gt;
| image = UnixWare 7.1.1 login.png&lt;br /&gt;
| caption = Logging into a UnixWare 7.1.3 system&lt;br /&gt;
| name = UnixWare&lt;br /&gt;
| creator = Univel, Novell &amp;amp; SCO&lt;br /&gt;
| current version = 7.1.4 &lt;br /&gt;
| year introduced = 1992&lt;br /&gt;
| type = Multitasking, multiuser&lt;br /&gt;
| architecture = [[IBM-PC]] theoretically portable&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Image:UnixWare 7.1.1 box.jpg|200px|right|UnixWare 7.1.1 box]]&lt;br /&gt;
&lt;br /&gt;
I've seen UnixWare used in all kinds of call centre applications with the AT&amp;amp;T g3 definity switch... I remember this thing running the voice mail appliances, and a few other things.  UnixWare ran on x86 computers. Unixware is a [[SYSV]] r4 derived OS.&lt;br /&gt;
&lt;br /&gt;
== Version 1 ==&lt;br /&gt;
Unixware started out as a Univel/USL product, 1.0 was primarily setup to integrate in [[Netware]] environments, I recall TCP/IP being optional!  &lt;br /&gt;
== Version 2 ==&lt;br /&gt;
Versions 2 included multiprocessor support, and then was purchased by By SCO who released 3 updates for version 2.&lt;br /&gt;
== Version 7 ==&lt;br /&gt;
This version removed support for legacy Xenix binaries to cut out the Microsoft royalty.&lt;br /&gt;
&lt;br /&gt;
=== UnixWare 7.0.1 ===&lt;br /&gt;
&lt;br /&gt;
Needs boot floppy.&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.0 ===&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.1 ===&lt;br /&gt;
&lt;br /&gt;
Latest Maintenance Pack: MP5&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.sco.com/pub/unixware7/uw711pk/ Unixware 7.1.1 MP5] Dec. 2004 (FTP)&lt;br /&gt;
&lt;br /&gt;
=== Caldera OpenUnix 8 (7.1.2) ===&lt;br /&gt;
&lt;br /&gt;
Integrates Linux Kernel Personality (LKP).&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.3 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware713/ SCO UnixWare 7.1.3] Product&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.4 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware714/ SCO UnixWare 7.1.4] Product&lt;br /&gt;
* [http://sco.com/support/update/download/product.php?pfid=1&amp;amp;prid=6 UnixWare 7.1.4 Supplements]&lt;br /&gt;
* [http://sco.com/support/update/download/release.php?rid=346 ISO Image CD1] Updated 2008&lt;br /&gt;
&lt;br /&gt;
[[Category:Operating Systems]]&lt;br /&gt;
{{Nav Unix}}&lt;br /&gt;
{{stub}}&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10155</id>
		<title>UnixWare</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=UnixWare&amp;diff=10155"/>
				<updated>2011-03-29T23:22:06Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox OS &lt;br /&gt;
| image = UnixWare 7.1.1 login.png&lt;br /&gt;
| caption = Logging into a UnixWare 7.1.3 system&lt;br /&gt;
| name = UnixWare&lt;br /&gt;
| creator = Univel, Novell &amp;amp; SCO&lt;br /&gt;
| current version = 7.1.4 &lt;br /&gt;
| year introduced = 1992&lt;br /&gt;
| type = Multitasking, multiuser&lt;br /&gt;
| architecture = [[IBM-PC]] theoretically portable&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Image:UnixWare 7.1.1 box.jpg|200px|right|UnixWare 7.1.1 box]]&lt;br /&gt;
&lt;br /&gt;
I've seen UnixWare used in all kinds of call centre applications with the AT&amp;amp;T g3 definity switch... I remember this thing running the voice mail appliances, and a few other things.  UnixWare ran on x86 computers. Unixware is a [[SYSV]] r4 derived OS.&lt;br /&gt;
&lt;br /&gt;
== Version 1 ==&lt;br /&gt;
Unixware started out as a Univel/USL product, 1.0 was primarily setup to integrate in [[Netware]] environments, I recall TCP/IP being optional!  &lt;br /&gt;
== Version 2 ==&lt;br /&gt;
Versions 2 included multiprocessor support, and then was purchased by By SCO who released 3 updates for version 2.&lt;br /&gt;
== Version 7 ==&lt;br /&gt;
This version removed support for legacy Xenix binaries to cut out the Microsoft royalty.&lt;br /&gt;
&lt;br /&gt;
=== UnixWare 7.0.1 ===&lt;br /&gt;
&lt;br /&gt;
Needs boot floppy.&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.0 ===&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.1 ===&lt;br /&gt;
&lt;br /&gt;
Latest Maintenance Pack: MP5&lt;br /&gt;
&lt;br /&gt;
=== Caldera OpenUnix 8 (7.1.2) ===&lt;br /&gt;
&lt;br /&gt;
Integrates Linux Kernel Personality (LKP).&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.3 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware713/ SCO UnixWare 7.1.3] Product&lt;br /&gt;
&lt;br /&gt;
=== SCO UnixWare 7.1.4 ===&lt;br /&gt;
&lt;br /&gt;
Runs with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
* [http://sco.com/products/unixware714/ SCO UnixWare 7.1.4] Product&lt;br /&gt;
* [http://sco.com/support/update/download/product.php?pfid=1&amp;amp;prid=6 UnixWare 7.1.4 Supplements]&lt;br /&gt;
* [http://sco.com/support/update/download/release.php?rid=346 ISO Image CD1] Updated 2008&lt;br /&gt;
&lt;br /&gt;
[[Category:Operating Systems]]&lt;br /&gt;
{{Nav Unix}}&lt;br /&gt;
{{stub}}&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=User:Ak120&amp;diff=10154</id>
		<title>User:Ak120</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=User:Ak120&amp;diff=10154"/>
				<updated>2011-03-29T23:12:06Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Cheerz'''&lt;br /&gt;
&lt;br /&gt;
Some stuff:&lt;br /&gt;
*[[OS/2]]&lt;br /&gt;
*[[Xenix]]&lt;br /&gt;
&lt;br /&gt;
Some links:&lt;br /&gt;
* http://www.os2.com/os2news/ - Best OS/2 News site&lt;br /&gt;
* http://hobbes.nmsu.edu/ - huge OS/2 files archive&lt;br /&gt;
* http://www.os2site.com - more files...&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=User:Ak120&amp;diff=10153</id>
		<title>User:Ak120</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=User:Ak120&amp;diff=10153"/>
				<updated>2011-03-29T23:08:54Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: Created page with &amp;quot;'''Cheerz'''  Some stuff: *OS/2 *Xenix&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Cheerz'''&lt;br /&gt;
&lt;br /&gt;
Some stuff:&lt;br /&gt;
*[[OS/2]]&lt;br /&gt;
*[[Xenix]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=10152</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=10152"/>
				<updated>2011-03-29T23:06:15Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* Microsoft */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
OS/2 started as a collabrative effort between [[IBM]] and [[Microsoft]] to put together the next generation Operating System for the [[IBM AT]] and [[PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Windows]] project around the same time, as a low cost entry interface with rudementary (cooperative) multitasking&lt;br /&gt;
&lt;br /&gt;
Needless to say Microsoft wanted to target the [[i386]] processor, and work on 32bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and demanded the [[i286]] 16bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was *NOT* a big seller.  Instead the industry worked around OS/2, and developed [[DOS Extenders]] technology, and Microsoft practically gave away the Windows SDK, allowed for OEM customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft lept at the chance to formalize DOS extenders into [[DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on google's usenet archive:&lt;br /&gt;
&lt;br /&gt;
http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e&lt;br /&gt;
Or locally here [[Gordon Letwin OS/2 usenet post]].&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an [[i286]] cpu, and an [[IBM AT]], or [[PS/2]] compatible computer.&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
This version was all textmode, and had an interface that was inspired from TopView.  Although it could multitask, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in real &amp;amp; protected mode.&lt;br /&gt;
&lt;br /&gt;
*1.1&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
This version introduced Presentation Manager.  It 'looked' identical to that of [[Windows 2.0]].&lt;br /&gt;
&lt;br /&gt;
*1.2&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
I think this version was released in September of 1988. This release was significant with the inclusion of the HPFS filesystem.  HPFS was significantly faster then the aging FAT filesystem as it placed its tables in the middle of the disk, and it allowed for larger filesystems, long filenames and extended attributes.  A later service pack allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
&lt;br /&gt;
[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false here].&lt;br /&gt;
&lt;br /&gt;
*1.3&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32bit HPFS driver in their Lan Manager package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applettes &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32bit versions ===&lt;br /&gt;
All of these versions require an [[i386]] SX or better CPU running on either an [[IBM AT]] compatible motherboard, or the [[IBM PS/2]] 32bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (12/1991)&lt;br /&gt;
This was the first 32bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (04/1992)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16bit device drivers as the kernel was still a hybrid 16bit/32bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of ram on a 386sx cpu.  Warp also included the 'bonus pack' which included SLIP/PPP TCP/IP, a dialer application and a word processor &amp;amp; spreadsheet.  A simple gopher client &amp;amp; NNTP client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including LAN drivers (that was WARP CONNECT), while Windows 95 &amp;amp; NT 3.5 both included SLIP/PPP *AND* lan drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revelutionalized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run x86 code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[RISC]] cpu could in fact run x86 instructions much quicker then a real Intel cpu...  This opened the way to the Pentium CPUs and effectivly killed the [[RISC]] revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://pages.prodigy.net/michaln/history/os2ppc/index.html here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
==== OS/2 1.0 ====&lt;br /&gt;
First version from November 1987 - no success.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]].  I'll try to get the hex 'diffs' so these can be saved &amp;amp; identified...&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.  The last major hurdle is the method of switching from [[protected mode]] to [[real mode]].  Prior to the last fixpack for OS/2 1.3 the method was a [[tripple fault]].  &lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.0 ====&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and Qemu... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host pc.  I've heard that VMWare has given up the compatability mode fixes.&lt;br /&gt;
&lt;br /&gt;
*[[Qemu]]&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.1 ====&lt;br /&gt;
&lt;br /&gt;
Adds 32-bit Graphics Subsystem. S3 display drivers are usable under Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
You should first apply the latest fixpak (XRGW040) to use Guest Additions from Virtual PC.&lt;br /&gt;
After this also GRADD device drivers (from Additions, IBM or Scitech SNAP) can be installed.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
Fixpak 5 or better 9 shoul be applied for GRADD.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp Server for e-business (4.5) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 1 (4.51) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 2 (4.52) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC and VirtualBox.&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
=== IBM ===&lt;br /&gt;
* IBM DisplayWrite5/2&lt;br /&gt;
&lt;br /&gt;
=== Lotus ===&lt;br /&gt;
* Lotus 1-2-3/G&lt;br /&gt;
* Lotus Freelance Graphics&lt;br /&gt;
&lt;br /&gt;
=== Micrografx ===&lt;br /&gt;
* Micrografx Designer 3.0&lt;br /&gt;
* Micrografx Draw for OS/2&lt;br /&gt;
&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
* Languages&lt;br /&gt;
** Microsoft MASM 6.0&lt;br /&gt;
** Microsoft C 5.1, 6.0&lt;br /&gt;
** Microsoft COBOL PDS&lt;br /&gt;
** Microsoft Basic PDS 7.0, 7.1&lt;br /&gt;
** Microsoft Fortran&lt;br /&gt;
* Productivity&lt;br /&gt;
** Microsoft Word 5.0, 5.5&lt;br /&gt;
** Microsoft Word (for Presentation Manager) 1.1&lt;br /&gt;
** Microsoft Excel 2.2, 3.0&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
[[Category:Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=10151</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=10151"/>
				<updated>2011-03-29T22:53:37Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* 16 bit versions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
OS/2 started as a collabrative effort between [[IBM]] and [[Microsoft]] to put together the next generation Operating System for the [[IBM AT]] and [[PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Windows]] project around the same time, as a low cost entry interface with rudementary (cooperative) multitasking&lt;br /&gt;
&lt;br /&gt;
Needless to say Microsoft wanted to target the [[i386]] processor, and work on 32bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and demanded the [[i286]] 16bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was *NOT* a big seller.  Instead the industry worked around OS/2, and developed [[DOS Extenders]] technology, and Microsoft practically gave away the Windows SDK, allowed for OEM customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft lept at the chance to formalize DOS extenders into [[DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on google's usenet archive:&lt;br /&gt;
&lt;br /&gt;
http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e&lt;br /&gt;
Or locally here [[Gordon Letwin OS/2 usenet post]].&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an [[i286]] cpu, and an [[IBM AT]], or [[PS/2]] compatible computer.&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
This version was all textmode, and had an interface that was inspired from TopView.  Although it could multitask, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in real &amp;amp; protected mode.&lt;br /&gt;
&lt;br /&gt;
*1.1&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
This version introduced Presentation Manager.  It 'looked' identical to that of [[Windows 2.0]].&lt;br /&gt;
&lt;br /&gt;
*1.2&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
I think this version was released in September of 1988. This release was significant with the inclusion of the HPFS filesystem.  HPFS was significantly faster then the aging FAT filesystem as it placed its tables in the middle of the disk, and it allowed for larger filesystems, long filenames and extended attributes.  A later service pack allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
&lt;br /&gt;
[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false here].&lt;br /&gt;
&lt;br /&gt;
*1.3&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32bit HPFS driver in their Lan Manager package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applettes &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32bit versions ===&lt;br /&gt;
All of these versions require an [[i386]] SX or better CPU running on either an [[IBM AT]] compatible motherboard, or the [[IBM PS/2]] 32bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (12/1991)&lt;br /&gt;
This was the first 32bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (04/1992)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16bit device drivers as the kernel was still a hybrid 16bit/32bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of ram on a 386sx cpu.  Warp also included the 'bonus pack' which included SLIP/PPP TCP/IP, a dialer application and a word processor &amp;amp; spreadsheet.  A simple gopher client &amp;amp; NNTP client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including LAN drivers (that was WARP CONNECT), while Windows 95 &amp;amp; NT 3.5 both included SLIP/PPP *AND* lan drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revelutionalized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run x86 code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[RISC]] cpu could in fact run x86 instructions much quicker then a real Intel cpu...  This opened the way to the Pentium CPUs and effectivly killed the [[RISC]] revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://pages.prodigy.net/michaln/history/os2ppc/index.html here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
==== OS/2 1.0 ====&lt;br /&gt;
First version from November 1987 - no success.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]].  I'll try to get the hex 'diffs' so these can be saved &amp;amp; identified...&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.  The last major hurdle is the method of switching from [[protected mode]] to [[real mode]].  Prior to the last fixpack for OS/2 1.3 the method was a [[tripple fault]].  &lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.0 ====&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and Qemu... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host pc.  I've heard that VMWare has given up the compatability mode fixes.&lt;br /&gt;
&lt;br /&gt;
*[[Qemu]]&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.1 ====&lt;br /&gt;
&lt;br /&gt;
Adds 32-bit Graphics Subsystem. S3 display drivers are usable under Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
You should first apply the latest fixpak (XRGW040) to use Guest Additions from Virtual PC.&lt;br /&gt;
After this also GRADD device drivers (from Additions, IBM or Scitech SNAP) can be installed.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
Fixpak 5 or better 9 shoul be applied for GRADD.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp Server for e-business (4.5) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 1 (4.51) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 2 (4.52) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC and VirtualBox.&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
*Microsoft C&lt;br /&gt;
*Microsoft PDS (Basic)&lt;br /&gt;
*Microsoft Fortran&lt;br /&gt;
*Microsoft Word (for Presentation Manager)&lt;br /&gt;
*Microsoft Excel&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
[[Category:Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=10150</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=10150"/>
				<updated>2011-03-29T22:52:33Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* 32 bit versions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
OS/2 started as a collabrative effort between [[IBM]] and [[Microsoft]] to put together the next generation Operating System for the [[IBM AT]] and [[PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Windows]] project around the same time, as a low cost entry interface with rudementary (cooperative) multitasking&lt;br /&gt;
&lt;br /&gt;
Needless to say Microsoft wanted to target the [[i386]] processor, and work on 32bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and demanded the [[i286]] 16bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was *NOT* a big seller.  Instead the industry worked around OS/2, and developed [[DOS Extenders]] technology, and Microsoft practically gave away the Windows SDK, allowed for OEM customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft lept at the chance to formalize DOS extenders into [[DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on google's usenet archive:&lt;br /&gt;
&lt;br /&gt;
http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e&lt;br /&gt;
Or locally here [[Gordon Letwin OS/2 usenet post]].&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an [[i286]] cpu, and an [[IBM AT]], or [[PS/2]] compatible computer.&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
This version was all textmode, and had an interface that was inspired from TopView.  Although it could multitask, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in real &amp;amp; protected mode.&lt;br /&gt;
&lt;br /&gt;
*1.1&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
This version introduced Presentation Manager.  It 'looked' identical to that of [[Windows 2.0]].&lt;br /&gt;
&lt;br /&gt;
*1.2&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
I think this version was released in September of 1988. This release was significant with the inclusion of the HPFS filesystem.  HPFS was significantly faster then the aging FAT filesystem as it placed its tables in the middle of the disk, and it allowed for larger filesystems, long filenames and extended attributes.  A later service pack allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
&lt;br /&gt;
[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false here].&lt;br /&gt;
&lt;br /&gt;
*1.3&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32bit HPFS driver in their Lan Manager package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applettes &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32bit versions ===&lt;br /&gt;
All of these versions require an [[i386]] SX or better CPU running on either an [[IBM AT]] compatible motherboard, or the [[IBM PS/2]] 32bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (12/1991)&lt;br /&gt;
This was the first 32bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (04/1992)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16bit device drivers as the kernel was still a hybrid 16bit/32bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of ram on a 386sx cpu.  Warp also included the 'bonus pack' which included SLIP/PPP TCP/IP, a dialer application and a word processor &amp;amp; spreadsheet.  A simple gopher client &amp;amp; NNTP client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including LAN drivers (that was WARP CONNECT), while Windows 95 &amp;amp; NT 3.5 both included SLIP/PPP *AND* lan drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revelutionalized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run x86 code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[RISC]] cpu could in fact run x86 instructions much quicker then a real Intel cpu...  This opened the way to the Pentium CPUs and effectivly killed the [[RISC]] revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://pages.prodigy.net/michaln/history/os2ppc/index.html here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]].  I'll try to get the hex 'diffs' so these can be saved &amp;amp; identified...&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.  The last major hurdle is the method of switching from [[protected mode]] to [[real mode]].  Prior to the last fixpack for OS/2 1.3 the method was a [[tripple fault]].  &lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.0 ====&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and Qemu... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host pc.  I've heard that VMWare has given up the compatability mode fixes.&lt;br /&gt;
&lt;br /&gt;
*[[Qemu]]&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.1 ====&lt;br /&gt;
&lt;br /&gt;
Adds 32-bit Graphics Subsystem. S3 display drivers are usable under Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
You should first apply the latest fixpak (XRGW040) to use Guest Additions from Virtual PC.&lt;br /&gt;
After this also GRADD device drivers (from Additions, IBM or Scitech SNAP) can be installed.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
Fixpak 5 or better 9 shoul be applied for GRADD.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp Server for e-business (4.5) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 1 (4.51) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC.&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Convenience Package 2 (4.52) ====&lt;br /&gt;
&lt;br /&gt;
Works with Virtual PC and VirtualBox.&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
*Microsoft C&lt;br /&gt;
*Microsoft PDS (Basic)&lt;br /&gt;
*Microsoft Fortran&lt;br /&gt;
*Microsoft Word (for Presentation Manager)&lt;br /&gt;
*Microsoft Excel&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
[[Category:Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	<entry>
		<id>https://gunkies.org/w/index.php?title=OS/2&amp;diff=8409</id>
		<title>OS/2</title>
		<link rel="alternate" type="text/html" href="https://gunkies.org/w/index.php?title=OS/2&amp;diff=8409"/>
				<updated>2010-12-06T15:58:50Z</updated>
		
		<summary type="html">&lt;p&gt;Ak120: /* 32bit versions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:OS2 1.x neonlogo.jpg|thumb|150px|right|OS/2's early logo]]&lt;br /&gt;
OS/2 started as a collabrative effort between [[IBM]] and [[Microsoft]] to put together the next generation Operating System for the [[IBM AT]] and [[PS/2]] machines.  &lt;br /&gt;
&lt;br /&gt;
Microsoft, famous for hedging bets, started the [[Windows]] project around the same time, as a low cost entry interface with rudementary (cooperative) multitasking&lt;br /&gt;
&lt;br /&gt;
Needless to say Microsoft wanted to target the [[i386]] processor, and work on 32bit software, while IBM wanted to deliver to the IBM AT customers it had sold to, and demanded the [[i286]] 16bit version.  Someone at IBM even got the idea that the development tools should be a revenue stream, and needless to say, the $3,000 SDK was *NOT* a big seller.  Instead the industry worked around OS/2, and developed [[DOS Extenders]] technology, and Microsoft practically gave away the Windows SDK, allowed for OEM customizations, and famously released the [[QuickC for Windows]] product.&lt;br /&gt;
&lt;br /&gt;
Microsoft lept at the chance to formalize DOS extenders into [[DPMI]], and use it in Windows, cementing OS/2's 1.x inability to run DPMI programs.  Microsoft was also upset that IBM locked them out of the graphical components of the OS, and that OS/2 worked BACKWARDS compared to Windows... the 0/0 in the screen coordinates is the bottom right, while everywhere else it's the top left..&lt;br /&gt;
&lt;br /&gt;
There is a great writeup on the divorce on google's usenet archive:&lt;br /&gt;
&lt;br /&gt;
http://groups.google.com/group/comp.os.ms-windows.misc/msg/d710490b745d5e5e&lt;br /&gt;
Or locally here [[Gordon Letwin OS/2 usenet post]].&lt;br /&gt;
&lt;br /&gt;
== Versions ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
All of these versions require an [[i286]] cpu, and an [[IBM AT]], or [[PS/2]] compatible computer.&lt;br /&gt;
&lt;br /&gt;
*1.0&lt;br /&gt;
[[Image:Microsoft OS2 1.0 - Heathkit Zenith OEM.jpg|thumb|right|150px|OS/2 1.0]]&lt;br /&gt;
This version was all textmode, and had an interface that was inspired from TopView.  Although it could multitask, most people didn't realize it, as all programs ran full screen.  It ran in 286 protected mode, except for the single &amp;quot;DOS&amp;quot; mode session.  As a result all device drivers for OS/2 had to be able to run in real &amp;amp; protected mode.&lt;br /&gt;
&lt;br /&gt;
*1.1&lt;br /&gt;
[[Image:IBM OS2 1.1 full package.jpg|thumb|right|150px|OS/2 1.1 full package]]&lt;br /&gt;
This version introduced Presentation Manager.  It 'looked' identical to that of [[Windows 2.0]].&lt;br /&gt;
&lt;br /&gt;
*1.2&lt;br /&gt;
[[Image:IBM OS2 1.2 box cover.jpg|thumb|right|150px|OS/2 1.2 box]]&lt;br /&gt;
I think this version was released in September of 1988. This release was significant with the inclusion of the HPFS filesystem.  HPFS was significantly faster then the aging FAT filesystem as it placed its tables in the middle of the disk, and it allowed for larger filesystems, long filenames and extended attributes.  A later service pack allowed for 386 and above CPUs to use the 386 method of switching between real &amp;amp; protected mode, allowing it to operate significantly faster (1.2c).  From what I understand this was the last version of OS/2 that included direct involvement from Microsoft.&lt;br /&gt;
&lt;br /&gt;
OS/2 1.2 from IBM included the 'standard' edition, along with the EE or extended edition.  The EE edition included basic communications capability (x.25, rs232 terminal), and a SQL database.&lt;br /&gt;
&lt;br /&gt;
[[InfoWorld]] included an excellent review of OS/2 1.2 [http://books.google.com/books?id=1DsEAAAAMBAJ&amp;amp;lpg=PT79&amp;amp;dq=%22OS%2F2%201.2%22&amp;amp;pg=PT66#v=onepage&amp;amp;q=%22OS/2%201.2%22&amp;amp;f=false here].&lt;br /&gt;
&lt;br /&gt;
*1.3&lt;br /&gt;
[[Image:Microsoft OS2 front.JPG|thumb|right|150px|Microsoft OS/2 1.3]]&lt;br /&gt;
This was the last version of the 16 bit OS/2 family. The 1.3 user interface resembled that of [[Windows 3.0]]. Microsoft did include a 32bit HPFS driver in their Lan Manager package which allowed for the fastest HPFS implementation prior to OS/2 2.0 &amp;amp; Windows NT 3.1  &lt;br /&gt;
&lt;br /&gt;
Around this time, Microsoft had released a beta of the WLO or Windows library for OS/2.  The beta included a copy of all of the applettes &amp;amp; games from Windows 3.0 that could run in the Presentation Manager of OS/2.  These libraries were also used to deliver the last versions of Microsoft Word &amp;amp; Excel for OS/2.  Microsoft had planned on releasing these libraries to allow people to easily port their Windows applications to OS/2, but the rift had happened right before that date, so the beta (which is easy to find) was the only thing released.  You can read more about it [http://pages.prodigy.net/michaln/history/pr/wlo.html here].&lt;br /&gt;
&lt;br /&gt;
Additionally, market penetration and OEM interest in OS/2 had dwindled so quickly by this point that Microsoft had decided to do a retail version of OS/2 (pictured to the right) to support its new [[Microsoft SQL Server]] product.  Windows NT on the i386 platform included support for 16bit OS/2 applications, namely for the Microsoft Languages (Fortran/Assembler &amp;amp; C) and SQL Server.  Since they all were text mode, they would run unmodified up through Windows 2000.&lt;br /&gt;
&lt;br /&gt;
=== 32bit versions ===&lt;br /&gt;
All of these versions require an [[i386]] SX or better CPU running on either an [[IBM AT]] compatible motherboard, or the [[IBM PS/2]] 32bit machines. &lt;br /&gt;
&lt;br /&gt;
==== OS/2 2.x ====&lt;br /&gt;
&lt;br /&gt;
*2.0 LA (12/1991)&lt;br /&gt;
This was the first 32bit version.  It was released after the IBM/Microsoft divorce, and was strictly an IBM release.&lt;br /&gt;
&lt;br /&gt;
*2.0 GA (04/1992)&lt;br /&gt;
[[Image:IBM OS2 2.0 cover.jpg|150px|thumb|right|OS/2 2.0]]&lt;br /&gt;
This release included [[Windows 3.0]] for use in Win OS/2.  At the time of the release the Presentation Managers graphic drivers were still 16 bit, although a later service pack was released which included 32bit drivers.  It's interesting to note that OS/2's market share was so low at this time, that OS/2 2.0 included the ability to load older 16bit device drivers as the kernel was still a hybrid 16bit/32bit kernel.&lt;br /&gt;
&lt;br /&gt;
The GUI had radically changed from 1.3 to 2.0 as it now included the Workplace Shell, a full OO GUI.  Many people considered WPS to be 'the' killer application at the time, as Windows still had the program manager.&lt;br /&gt;
&lt;br /&gt;
The new Presentation Manager replacement, Workplace Shell, included a deal with Commodore for the &amp;quot;look and feel&amp;quot; of [[AmigaDOS]], and as part of the deal, Commodore picked up a license for [[REXX]] into its products as first seen by AmigaDOS 2.0 .&lt;br /&gt;
&lt;br /&gt;
*2.1 (06/1993)&lt;br /&gt;
This release brought the Win OS/2 functionality up to [[Windows 3.1]].  From the user standpoint it still looked like 2.0&lt;br /&gt;
&lt;br /&gt;
*2.11 (02/1994)&lt;br /&gt;
*2.11 SMP (08/1994)&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 3 ====&lt;br /&gt;
&lt;br /&gt;
*3.0 (09/1994)&lt;br /&gt;
This was the WARP release.  At the time this release preempted the [[Windows 95]] release.  IBM had done their best to tune OS/2 to run in 4MB of ram on a 386sx cpu.  Warp also included the 'bonus pack' which included SLIP/PPP TCP/IP, a dialer application and a word processor &amp;amp; spreadsheet.  A simple gopher client &amp;amp; NNTP client were also included.&lt;br /&gt;
&lt;br /&gt;
IMHO this is where IBM missed the boat, by making TCP/IP difficult to configure, and by not including LAN drivers (that was WARP CONNECT), while Windows 95 &amp;amp; NT 3.5 both included SLIP/PPP *AND* lan drivers.&lt;br /&gt;
&lt;br /&gt;
I *THINK* it was this release that included the ability to run [[Win32s]], which was a boon for Netscape &amp;amp; Mosaic.&lt;br /&gt;
&lt;br /&gt;
*3.01 (1995)&lt;br /&gt;
OS/2 Warp with Win-OS/2&lt;br /&gt;
&lt;br /&gt;
*3.02 (1995)&lt;br /&gt;
OS/2 Warp Connect&lt;br /&gt;
OS/2 Warp Server &lt;br /&gt;
OS/2 Warp Server Advanced&lt;br /&gt;
&lt;br /&gt;
*3.05 (01/1996)&lt;br /&gt;
OS/2 Warp Server Advanced for SMP&lt;br /&gt;
&lt;br /&gt;
==== OS/2 Warp 4 ====&lt;br /&gt;
&lt;br /&gt;
*4.0&lt;br /&gt;
OS/2 4.0 included both Java and Netscape in this release.  Sadly IBM had still not 'gotten it' with regards to TCP/IP and insisted on a 'connect' version of 4.0 that included the LAN drivers.  4.0 also included the ability to install servicepacks online.&lt;br /&gt;
&lt;br /&gt;
*4.01&lt;br /&gt;
Workspace on-Demand 1.0 (WSOD)&lt;br /&gt;
Workspace on-Demand 2.0&lt;br /&gt;
&lt;br /&gt;
*4.5&lt;br /&gt;
IBM OS/2 Warp Server for e-business&lt;br /&gt;
Fixpak &amp;gt;=13 applied to OS/2 Warp 4 or WSOD&lt;br /&gt;
&lt;br /&gt;
*4.51&lt;br /&gt;
Aurora Convenience Package 1 (ACP1), Merlin Convenience Package 1 (MCP1)&lt;br /&gt;
&lt;br /&gt;
*4.52&lt;br /&gt;
Aurora Convenience Package 2 (ACP2), Merlin Convenience Package 2 (MCP2)&lt;br /&gt;
&lt;br /&gt;
This was the last IBM release of OS/2.&lt;br /&gt;
&lt;br /&gt;
== PowerPC port ==&lt;br /&gt;
&lt;br /&gt;
It's a deep secret that the PowerPC version ended up sucking up so much time, effort and money from IBM's development of OS/2, that it ended up bleeding the group dry, and without a product to ship.  IMHO it's a shame, as partnered with the [[PowerPC 615]] CPU it could have revelutionalized the industry.. But then back then everyone expected Intel to hit a wall, IBM had the 615 in their pocket which was a PowerPC CPU which was pin compatible with a 486, and could run x86 code (albeit slow..) and then switch to PPC mode.  The company [[NexGen]] opened up everyone's eyes that a specialized [[RISC]] cpu could in fact run x86 instructions much quicker then a real Intel cpu...  This opened the way to the Pentium CPUs and effectivly killed the [[RISC]] revolution.&lt;br /&gt;
&lt;br /&gt;
There is a most excellent review to be found [http://pages.prodigy.net/michaln/history/os2ppc/index.html here] that also includes screenshots.&lt;br /&gt;
&lt;br /&gt;
== Running OS/2 under an Emulator ==&lt;br /&gt;
&lt;br /&gt;
=== 16 bit versions ===&lt;br /&gt;
==== OS/2 1.1 ====&lt;br /&gt;
[[Image:OS2 1.1.jpg|thumb|200px|right|OS/2 1.1 under Bochs]]&lt;br /&gt;
There are some hacks available to run OS/2 1.1 under [[VMWare]], and [[Bochs]].  I'll try to get the hex 'diffs' so these can be saved &amp;amp; identified...&lt;br /&gt;
&lt;br /&gt;
==== OS/2 1.3 ====&lt;br /&gt;
[[Image:Os213.png|thumb|200px|right|OS/2 1.3 under Virtual PC.]]&lt;br /&gt;
The only version of OS/2 1.x that can run under an emulator without any hacks applied.  The three problems that you will run into is emulated floppy disks are too quick, and other various timing anomalies that will lead to a COUNTRY.SYS failure.  The last major hurdle is the method of switching from [[protected mode]] to [[real mode]].  Prior to the last fixpack for OS/2 1.3 the method was a [[tripple fault]].  &lt;br /&gt;
&lt;br /&gt;
The method for install requires you to install OS/2 1.3 on a physical machine, update it, then make a whole disk image of it.  I can confirm that OS/2 1.3 runs under [[Virtual PC 2007]] just fine.  While it does have some issues with the floppy (it'll throw an error reading the floppy every time you put in a new disk) it will allow you to use the floppy.  This makes OS/2 1.3 the easiest to install programs into.&lt;br /&gt;
&lt;br /&gt;
At the moment, none of the 1.x versions will install under emulation, they all must be imaged from a physical machine.&lt;br /&gt;
&lt;br /&gt;
=== 32 bit versions ===&lt;br /&gt;
[[Image:OS2 2.0 in Qemu.png|thumb|200px|right|OS/2 2.0 running under Qemu.]]&lt;br /&gt;
&lt;br /&gt;
I've run OS/2 2.0 &amp;amp; 4.0 under Virtual PC, and Qemu... I guess it really comes down to if you move disk images around between various hardware platforms.  Anything prior to version 3.0 should be run in an ISA emulation mode (-M isa) to let the peripherals work in a more compatible manner...  Virtual PC 2007 works fine as well, and includes extensions that allow the guest VM to use drives that are installed on the host pc.  I've heard that VMWare has given up the compatability mode fixes.&lt;br /&gt;
&lt;br /&gt;
*[[Qemu]]&lt;br /&gt;
*[[Virtual PC 2007]]&lt;br /&gt;
&lt;br /&gt;
== Popular Applications ==&lt;br /&gt;
=== Microsoft ===&lt;br /&gt;
Microsoft did port over a bunch of their languages, along with a few applications, namely:&lt;br /&gt;
&lt;br /&gt;
*Microsoft C&lt;br /&gt;
*Microsoft PDS (Basic)&lt;br /&gt;
*Microsoft Fortran&lt;br /&gt;
*Microsoft Word (for Presentation Manager)&lt;br /&gt;
*Microsoft Excel&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
[[Category:Operating Systems]]&lt;/div&gt;</summary>
		<author><name>Ak120</name></author>	</entry>

	</feed>