home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft_Programmers_Library.7z / MPL / os2 / os2insd.txt < prev    next >
Encoding:
Text File  |  2013-11-08  |  618.7 KB  |  11,210 lines

  1.  INSIDE OS/2
  2.  
  3.  
  4.  
  5.  ───────────────────────────────────────────────────────────────────────────
  6.  
  7.  INSIDE OS/2
  8.  
  9.  
  10.  Gordon Letwin
  11.  Chief Architect, Systems Software, Microsoft(R)
  12.  
  13.  Foreword by Bill Gates
  14.  
  15.  
  16.  ───────────────────────────────────────────────────────────────────────────
  17.  
  18.  
  19.  PUBLISHED BY
  20.  Microsoft Press
  21.  A Division of Microsoft Corporation
  22.  16011 NE 36th Way, Box 97017, Redmond, Washington 98073-9717
  23.  
  24.  Copyright (C) 1988 by Microsoft Press
  25.  All rights reserved. No part of the contents of this book may be
  26.  reproduced or transmitted in any form or by any means without the written
  27.  permission of the publisher.
  28.  
  29.  Library of Congress Cataloging in Publication Data
  30.  Letwin, Gordon.
  31.  Inside OS/2.
  32.  
  33.  Includes index.
  34.  1. MS OS/2 (Computer operating system) I. Title.
  35.  II. Title: Inside OS/Two.
  36.  QA76.76.063L48    1988    005.4'46    87-31579
  37.  ISBN 1-55615-117-9
  38.  
  39.  Printed and bound in the United States of America
  40.  
  41.  1 2 3 4 5 6 7 8 9 MLML 8 9 0 9 8
  42.  
  43.  Distributed to the book trade in the United States by Harper & Row.
  44.  
  45.  Distributed to the book trade in Canada by General Publishing Company, Ltd.
  46.  
  47.  Distributed to the book trade outside the United States and Canada by
  48.  Penguin Books Ltd.
  49.  
  50.  Penguin Books Ltd., Harmondsworth, Middlesex, England
  51.  Penguin Books Australia Ltd., Ringwood, Victoria, Australia
  52.  Penguin Books N.Z. Ltd., 182-190 Wairau Road, Auckland 10, New Zealand
  53.  
  54.  British Cataloging in publication Data available
  55.  
  56.  Editor: Patricia Pratt
  57.  
  58.  
  59.  
  60.  ───────────────────────────────────────────────────────────────────────────
  61.  
  62.  
  63.  
  64.                                Dedication
  65.  
  66.                                 To R.P.W.
  67.  
  68.  
  69.  
  70.  ───────────────────────────────────────────────────────────────────────────
  71.  
  72.  
  73.  
  74.  Contents
  75.  
  76.  
  77.  
  78.  Foreword by Bill Gates
  79.  
  80.  Introduction
  81.  
  82.  
  83.  Part I: The Project
  84.  
  85.  Chapter 1.  History of the Project
  86.               1.1  MS-DOS version 1.0
  87.               1.2  MS-DOS version 2.0
  88.               1.3  MS-DOS version 3.0
  89.               1.4  MS-DOS version 4.0
  90.  
  91.  Chapter 2.  Goals and Compatibility Issues
  92.               2.1  Goals
  93.                     2.1.1  Graphical User Interface
  94.                     2.1.2  Multitasking
  95.                     2.1.3  Memory Management
  96.                     2.1.4  Protection
  97.                     2.1.5  Encapsulation
  98.                     2.1.6  Interprocess Communication (IPC)
  99.                     2.1.7  Direct Device Access
  100.               2.2  Compatibility Issues
  101.                     2.2.1  Real Mode vs Protect Mode
  102.                     2.2.2  Running Applications in Real (Compatibility) Mode
  103.                             2.2.2.1  Memory Utilization
  104.                             2.2.2.2  File Locking
  105.                             2.2.2.3  Network Piggybacking
  106.                     2.2.3  Popular Function Compatibility
  107.                     2.2.4  Downward Compatibility
  108.                             2.2.4.1  Family API
  109.                             2.2.4.2  Network Server-Client Compatibility
  110.  
  111.  Chapter 3.  The OS/2 Religion
  112.               3.1  Maximum Flexibility
  113.               3.2  Stable Environment
  114.                     3.2.1  Memory Protection
  115.                     3.2.2  Side-Effects Protection
  116.               3.3  Localization of Errors
  117.               3.4  Software Tools Approach
  118.  
  119.  
  120.  Part II: The Architecture
  121.  
  122.  Chapter 4.  Multitasking
  123.               4.1  Subtask Model
  124.                     4.1.1  Standard File Handles
  125.                     4.1.2  Anonymous Pipes
  126.                     4.1.3  Details, Details
  127.               4.2  PIDs and Command Subtrees
  128.               4.3  DosExecPgm
  129.               4.4  DosCWait
  130.               4.5  Control of Child Tasks and Command Subtrees
  131.                     4.5.1  DosKillProcess
  132.                     4.5.2  DosSetPriority
  133.  
  134.  Chapter 5.  Threads and Scheduler/Priorities
  135.               5.1  Threads
  136.                     5.1.1  Thread Stacks
  137.                     5.1.2  Thread Uses
  138.                             5.1.2.1  Foreground and Background Work
  139.                             5.1.2.2  Asynchronous Processing
  140.                             5.1.2.3  Speed Execution
  141.                             5.1.2.4  Organizing Programs
  142.                     5.1.3  Interlocking
  143.                             5.1.3.1  Local Variables
  144.                             5.1.3.2  RAM Semaphores
  145.                             5.1.3.3  DosSuspendThread
  146.                             5.1.3.4  DosEnterCritSec/DosExitCritSec
  147.                     5.1.4  Thread 1
  148.                     5.1.5  Thread Death
  149.                     5.1.6  Performance Characteristics
  150.               5.2  Scheduler/Priorities
  151.                     5.2.1  General Priority Category
  152.                             5.2.1.1  Background Subcategory
  153.                             5.2.1.2  Foreground and Interactive
  154.                                       Subcategories
  155.                             5.2.1.3  Throughput Balancing
  156.                     5.2.2  Time-Critical Priority Category
  157.                     5.2.3  Force Background Priority Category
  158.                     5.2.4  Setting Process/Thread Priorities
  159.  
  160.  Chapter 6.  The User Interface
  161.               6.1  VIO User Interface
  162.               6.2  The Presentation Manager User Interface
  163.               6.3  Presentation Manager and VIO Compatibility
  164.  
  165.  Chapter 7.  Dynamic Linking
  166.               7.1  Static Linking
  167.               7.2  Loadtime Dynamic Linking
  168.               7.3  Runtime Dynamic Linking
  169.               7.4  Dynlinks, Processes, and Threads
  170.               7.5  Data
  171.                     7.5.1  Instance Data
  172.                     7.5.2  Global Data
  173.               7.6  Dynamic Link Packages As Subroutines
  174.               7.7  Subsystems
  175.                     7.7.1  Special Subsystem Support
  176.               7.8  Dynamic Links As Interfaces to Other Processes
  177.               7.9  Dynamic Links As Interfaces to the Kernel
  178.               7.10 The Architectural Role of Dynamic Links
  179.               7.11 Implementation Details
  180.                     7.11.1  Dynlink Data Security
  181.                     7.11.2  Dynlink Life, Death, and Sharing
  182.                     7.11.3  Dynlink Side Effects
  183.               7.12 Dynlink Names
  184.  
  185.  Chapter 8.  File System Name Space
  186.               8.1  Filenames
  187.               8.2  Network Access
  188.               8.3  Name Generation and Compatibility
  189.               8.4  Permissions
  190.               8.5  Other Objects in the File System Name Space
  191.  
  192.  Chapter 9.  Memory Management
  193.               9.1  Protection Model
  194.               9.2  Memory Management API
  195.                     9.2.1  Shared Memory
  196.                     9.2.2  Huge Memory
  197.                     9.2.3  Executing from Data Segments
  198.                     9.2.4  Memory Suballocation
  199.               9.3  Segment Swapping
  200.                     9.3.1  Swapping Miscellany
  201.               9.4  Status and Information
  202.  
  203.  Chapter 10. Environment Strings
  204.  
  205.  Chapter 11. Interprocess Communication (IPC)
  206.               11.1  Shared Memory
  207.               11.2  Semaphores
  208.                      11.2.1  Semaphore Recovery
  209.                      11.2.2  Semaphore Scheduling
  210.               11.3  Named Pipes
  211.               11.4  Queues
  212.               11.5  Dynamic Data Exchange (DDE)
  213.               11.6  Signals
  214.               11.7  Combining IPC Forms
  215.  
  216.  Chapter 12. Signals
  217.  
  218.  Chapter 13. The Presentation Manager and VIO
  219.               13.1  Choosing Between PM and VIO
  220.               13.2  Background I/O
  221.               13.3  Graphics Under VIO
  222.  
  223.  Chapter 14. Interactive Programs
  224.               14.1  I/O Architecture
  225.               14.2  Ctrl-C and Ctrl-Break Handling
  226.  
  227.  Chapter 15. The File System
  228.               15.1  The OS/2 File System
  229.               15.2  Media Volume Management
  230.               15.3  I/O Efficiency
  231.  
  232.  Chapter 16. Device Monitors, Data Integrity, and Timer Services
  233.               16.1  Device Monitors
  234.               16.2  Data Integrity
  235.                      16.2.1  Semaphores
  236.                      16.2.2  DosBufReset
  237.                      16.2.3  Writethroughs
  238.               16.3  Timer Services
  239.  
  240.  Chapter 17. Device Drivers and Hard Errors
  241.               17.1  Device Drivers
  242.                      17.1.1  Device Drivers and OS/2 Communication
  243.                      17.1.2  Device Driver Programming Model
  244.                      17.1.3  Device Management
  245.                      17.1.4  Dual Mode
  246.               17.2  Hard Errors
  247.                      17.2.1  The Hard Error Daemon
  248.                      17.2.2  Application Hard Error Handling
  249.  
  250.  Chapter 18. I/O Privilege Mechanism and Debugging/Ptrace
  251.               18.1  I/O Privilege Mechanism
  252.               18.2  Debugging/Ptrace
  253.  
  254.  Chapter 19. The 3x Box
  255.  
  256.  Chapter 20. Family API
  257.  
  258.  
  259.  Part III: The Future
  260.  
  261.  Chapter 21. The Future
  262.               21.1  File System
  263.               21.2  The 80386
  264.                      21.2.1  Large Segments
  265.                      21.2.2  Multiple Real Mode Boxes
  266.                      21.2.3  Full Protection Capability
  267.                      21.2.4  Other Features
  268.               21.3  The Next Ten Years
  269.  
  270.  Glossary
  271.  
  272.  Index
  273.  
  274.  
  275.  
  276.  Acknowledgments
  277.  
  278.  
  279.  Although a book can have a single author, a work such as OS/2 necessarily
  280.  owes its existence to the efforts of a great many people. The architecture
  281.  described herein was hammered out by a joint Microsoft/ IBM design team:
  282.  Ann, Anthony, Carolyn, Ed, Gordon, Jerry, Mark, Mike, Ray, and Ross. This
  283.  team accomplished a great deal of work in a short period of time.
  284.       The bulk of the credit, and my thanks, go to the engineers who
  285.  designed and implemented the code and made it work. The size of the teams
  286.  involved throughout the project prevents me from listing all the names
  287.  here. It's hard for someone who has not been involved in a software project
  288.  of this scope to imagine the problems, pressure, chaos, and "reality
  289.  shifts" that arise in a never-ending stream. These people deserve great
  290.  credit for their skill and determination in making OS/2 come to pass.
  291.       Thanks go to the OS/2 development staffers who found time, in the heat
  292.  of the furnace, to review and critique this book: Ian Birrell, Ross Cook,
  293.  Rick Dewitt, Dave Gilman, Vic Heller, Mike McLaughlin, Jeff Parsons, Ray
  294.  Pedrizetti, Robert Reichel, Rajen Shah, Anthony Short, Ben Slivka, Pete
  295.  Stewart, Indira Subramanian, Bryan Willman, and Mark Zbikowski.
  296.       I'd like to give special thanks to Mark Zbikowski and Aaron Reynolds,
  297.  the "gurus of DOS." Without their successes there would never have been an
  298.  opportunity for a product such as OS/2.
  299.       And finally I'd like to thank Bill Gates for creating and captaining
  300.  one hell of a company, thereby making all this possible.
  301.  
  302.  
  303.  
  304.  Foreword
  305.  
  306.  
  307.       OS/2 is destined to be a very important piece of software. During the
  308.  next 10 years, millions of programmers and users will utilize this system.
  309.  From time to time they will come across a feature or a limitation and
  310.  wonder why it's there. The best way for them to understand the overall
  311.  philosophy of the system will be to read this book. Gordon Letwin is
  312.  Microsoft's architect for OS/2. In his very clear and sometimes humorous
  313.  way, Gordon has laid out in this book why he included what he did and why
  314.  he didn't include other things.
  315.       The very first generation of microcomputers were 8-bit machines, such
  316.  as the Commodore Pet, the TRS-80, the Apple II, and the CPM 80 based
  317.  machines. Built into almost all of them was Microsoft's BASIC Interpreter.
  318.  I met Gordon Letwin when I went to visit Heath's personal computer group
  319.  (now part of Zenith). Gordon had written his own BASIC as well as an
  320.  operating system for the Heath system, and he wasn't too happy that his
  321.  management was considering buying someone else's. In a group of about 15
  322.  people, he bluntly pointed out the limitations of my BASIC versus his.
  323.  After Heath licensed my BASIC, I convinced Gordon that Microsoft was the
  324.  place to be if you wanted your great software to be popular, and so he
  325.  became one of Microsoft's first 10 programmers. His first project was to
  326.  single-handedly write a compiler for Microsoft BASIC. He put a sign on his
  327.  door that read
  328.  
  329.          Do not disturb, feed, poke, tease...the animal
  330.  
  331.  and in 5 months wrote a superb compiler that is still the basis for all our
  332.  BASIC compilers. Unlike the code that a lot of superstar programmers write,
  333.  Gordon's source code is a model of readability and includes precise
  334.  explanations of algorithms and why they were chosen.
  335.       When the Intel 80286 came along, with its protected mode completely
  336.  separate from its compatible real mode, we had no idea how we were going to
  337.  get at its new capabilities. In fact, we had given up until Gordon came up
  338.  with the patented idea described in this book that has been referred to as
  339.  "turning the car off and on at 60 MPH." When we first explained the idea to
  340.  Intel and many of its customers, they were sure it wouldn't work. Even
  341.  Gordon wasn't positive it would work until he wrote some test programs that
  342.  proved it did.
  343.       Gordon's role as an operating systems architect is to overview our
  344.  designs and approaches and make sure they are as simple and as elegant as
  345.  possible. Part of this job includes reviewing people's code. Most
  346.  programmers enjoy having Gordon look over their code and point out how it
  347.  could be improved and simplified. A lot of programs end up about half as
  348.  big after Gordon has explained a better way to write them. Gordon doesn't
  349.  mince words, however, so in at least one case a particularly sensitive
  350.  programmer burst into tears after reading his commentary. Gordon isn't
  351.  content to just look over other people's code. When a particular project
  352.  looks very difficult, he dives in. Currently, Gordon has decided to
  353.  personally write most of our new file system, which will be dramatically
  354.  faster than our present one. On a recent "vacation" he wrote more than 50
  355.  pages of source code.
  356.       This is Gordon's debut as a book author, and like any good designer he
  357.  has already imagined what bad reviews might say. I think this book is both
  358.  fun and important. I hope you enjoy it as much as I have.
  359.  
  360.  Bill Gates
  361.  
  362.  
  363.  
  364.  Introduction
  365.  
  366.  
  367.  Technological breakthroughs develop in patterns that are distinct from
  368.  patterns of incremental advancements. An incremental advancement--an
  369.  improvement to an existing item--is straightforward and unsurprising. An
  370.  improvement is created; people see the improvement, know what it will do
  371.  for them, and start using it.
  372.       A major advance without closely related antecedents--a technological
  373.  breakthrough--follows a different pattern. The field of communication is a
  374.  good example. Early in this century, a large infrastructure existed to
  375.  facilitate interpersonal communication. Mail was delivered twice a day, and
  376.  a variety of efficient services relayed messages. A businessman dictated a
  377.  message to his secretary, who gave it to a messenger service. The service
  378.  carried the message to its nearby destination, where a secretary delivered
  379.  it to the recipient.
  380.       Into this environment came a technological breakthrough--the
  381.  telephone. The invention of the telephone was a breakthrough, not an
  382.  incremental advance, because it provided an entirely new way to communicate.
  383.  It wasn't an improvement over an existing method. That it was
  384.  a breakthrough development impeded its acceptance. Most business people
  385.  considered it a newfangled toy, of little practical use. "What good does it
  386.  do me? By the time I dictate the message, and my secretary writes it down
  387.  and gives it to the mailroom, and they phone the addressee's mailroom, and
  388.  the message is copied--perhaps incorrectly--and delivered to the
  389.  addressee's secretary, it would have been as fast to have it delivered by
  390.  messenger! All my correspondents are close by, and, besides, with
  391.  messengers I don't have to pay someone to sit by the telephone all day in
  392.  case a message comes in."
  393.       This is a classic example of the earliest stages of breakthrough
  394.  technology--potential users evaluate it by trying to fit it into present
  395.  work patterns. Our example businessman has not yet realized that he needn't
  396.  write the message down anymore and that it needn't be copied down at the
  397.  destination. He also doesn't realize that the reason his recipients are
  398.  close by is that they have to be for decent messenger delivery. The
  399.  telephone relaxed this requirement, allowing more efficient locations near
  400.  factories and raw materials or where office space was cheaper. But it
  401.  was necessary for the telephone to be accepted before these advantages
  402.  could be realized.
  403.       Another impedance to the acceptance of a breakthrough technology is
  404.  that the necessary new infrastructure is not in place. A telephone did
  405.  little good if your intended correspondent didn't have one. The nature of
  406.  telephones required a standard; until that standard was set, your
  407.  correspondent might own a phone, but it could be connected to a network
  408.  unreachable by you. Furthermore, because the technology was in its infancy,
  409.  the facilities were crude.
  410.       These obstacles were not insurmountable. The communications
  411.  requirements of some people were so critical that they were willing to
  412.  invent new procedures and to put up with the problems of the early stages.
  413.  Some people, because of their daring or ambition, used the new system to
  414.  augment their existing system. And finally, because the new technology was
  415.  so powerful, some used it to enhance the existing technology. For example,
  416.  a messenger service might establish several offices with telephone linkage
  417.  between them and use the telephones to speed delivery of short messages by
  418.  phoning them to the office nearest the destination, where they were copied
  419.  down and delivered normally. Using the telephone in this fashion was
  420.  wasteful, but where demand for the old service was high enough, any
  421.  improvement, however "wasteful," was welcome.
  422.       After it has a foot in the door, a breakthrough technology is
  423.  unstoppable. After a time, standards are established, the bugs are worked
  424.  out, and, most important, the tool changes its users. Once the telephone
  425.  became available, business and personal practices developed in new
  426.  patterns, patterns that were not considered before because they were not
  427.  possible. Messenger services used to be fast enough, but only because,
  428.  before the telephone, the messenger service was the fastest technology
  429.  available. The telephone changed the life-style of its users.
  430.       This change in the structure of human activity explains why an
  431.  intelligent person could say, "Telephones are silly gadgets," and a few
  432.  years later say, "Telephones are indispensable." This change in the tool
  433.  user--caused by the tool itself--also makes predicting the ultimate effect
  434.  of the new technology difficult. Extrapolating from existing trends is
  435.  wildly inaccurate because the new tool destroys many practices and creates
  436.  wholly unforeseen ones. It's great fun to read early, seemingly silly
  437.  predictions of life in the future and to laugh at the predictors, but the
  438.  predictors were frequently intelligent and educated. Their only mistake was
  439.  in treating the new development as an incremental advance rather than as a
  440.  breakthrough technology. They saw how the new development would improve
  441.  their current practices, but they couldn't see how it would replace those
  442.  practices.
  443.       Digital computers are an obvious breakthrough technology, and they've
  444.  shared the classic three-stage pattern: "exotic toys," "limited use," and
  445.  "indispensable." Mainframe computers have gone the full route, in the
  446.  milieu of business and scientific computing. IBM's initial estimate of the
  447.  computer market was a few dozen machines. But, as the technology and the
  448.  support infrastructure grew, and as people's ways of working adapted to
  449.  computers, the use of computers grew--from the census bureau, to life
  450.  insurance companies, to payroll systems, and finally to wholly new
  451.  functions such as MIS (Management Information Sciences) systems and airline
  452.  reservation networks.
  453.       Microcomputers are in the process of a similar development. The
  454.  "exotic toy" stage has already given way to the "limited use" stage. We're
  455.  just starting to develop standards and infrastructure and are only a few
  456.  years from the "indispensable" stage. In anticipation of this stage,
  457.  Microsoft undertook the design and the development of OS/2.
  458.       Although studying the mainframe computer revolution helps in trying to
  459.  predict the path of the microcomputer revolution, microcomputers are more
  460.  than just "cheap mainframes." The microcomputer revolution will follow the
  461.  tradition of breakthroughs, creating new needs and new uses that cannot be
  462.  anticipated solely by studying what happened with mainframe systems.
  463.       This book was written because of the breakthrough nature of the
  464.  microcomputer and the impact of the coming second industrial revolution.
  465.  The designers of OS/2 tried to anticipate, to the greatest extent possible,
  466.  the demands that would be placed on the system when the tool--the personal
  467.  computer--and the tool user reached their new equilibrium. A knowledge of
  468.  MS-DOS and a thorough reading of the OS/2 reference manuals will not, in
  469.  themselves, clarify the key issues of the programming environment that OS/2
  470.  was written to support. This is true not only because of the complexity of
  471.  the product but because many design elements were chosen to provide
  472.  services that from a prebreakthrough perspective--don't seem needed and
  473.  solve problems that haven't yet arisen.
  474.       Other books provide reference information and detailed how-to
  475.  instructions for writing OS/2 programs. This book describes the underlying
  476.  architectural models that make up OS/2 and discusses how those models are
  477.  expected to meet the foreseen and unforeseen requirements of the oncoming
  478.  office automation revolution. It focuses on the general issues, problems,
  479.  and solutions that all OS/2 programs encounter regardless of the
  480.  programming and interface models that a programmer may employ.
  481.       As is often the case in a technical discussion, everything in OS/2 is
  482.  interconnected in some fashion to everything else. A discussion on the
  483.  shinbone naturally leads to a discussion of the thighbone and so on. The
  484.  author and the editor of this book have tried hard to group the material
  485.  into a logical progression without redundancy, but the very nature of the
  486.  material makes complete success at this impossible. It's often desirable,
  487.  in fact, to repeat material, perhaps from a different viewpoint or with a
  488.  different emphasis. For these reasons, the index references every mention
  489.  of an item or a topic, however peripheral. Having too many references
  490.  (including a few worthless ones) is far better than having too few
  491.  references. When you're looking for information about a particular subject,
  492.  I recommend that you first consult the contents page to locate the major
  493.  discussion and then peruse the index to pick up references that may appear
  494.  in unexpected places.
  495.  
  496.  
  497.  
  498.  ───────────────────────────────────────────────────────────────────────────
  499.  
  500.  
  501.  
  502.  Part I  The Project
  503.  
  504.  
  505.  
  506.  ───────────────────────────────────────────────────────────────────────────
  507.  
  508.  
  509.  
  510.  1  History of the Project
  511.  
  512.  ───────────────────────────────────────────────────────────────────────────
  513.  
  514.  Microsoft was founded to realize a vision of a microcomputer on every
  515.  desktop--a vision of the second industrial revolution. The first industrial
  516.  revolution mechanized physical work. Before the eighteenth century, nearly
  517.  all objects were created and constructed by human hands, one at a time.
  518.  With few exceptions, such as animal-powered plowing and cartage, all power
  519.  was human muscle power. The second industrial revolution will mechanize
  520.  routine mental work. Today, on the verge of the revolution, people are
  521.  still doing "thought work," one piece at a time.
  522.       Certain tasks--those massive in scope and capable of being rigidly
  523.  described, such as payroll calculations--have been automated, but the
  524.  majority of "thought work" is still done by people, not by computers. We
  525.  have the computer equivalent of the plow horse, but we don't have the
  526.  computer equivalent of the electric drill or the washing machine.
  527.       Of course, computers cannot replace original thought and creativity
  528.  (at least, not in the near future) any more than machines have replaced
  529.  design and creativity in the physical realm. But the bulk of the work in a
  530.  white-collar office involves routine manipulation of information. The
  531.  second industrial revolution will relieve us of the "grunt work"--routine
  532.  data manipulation, analysis, and decisions--freeing us to deal only with
  533.  those situations that require human judgment.
  534.       Most people do not recognize the inevitability of the second
  535.  industrial revolution. They can't see how a computer could do 75 percent of
  536.  their work because their work was structured in the absence of computers.
  537.  But, true to the pattern for technological breakthroughs, the tremendous
  538.  utility of the microcomputer will transform its users and the way they do
  539.  their work.
  540.       For example, a great deal of work is hard to computerize because the
  541.  input information arrives on paper and it would take too long to type it
  542.  all in. Ten years ago, computer proponents envisioned the "paperless
  543.  office" as a solution for this problem: All material would be generated by
  544.  computer and then transferred electronically or via disk to other
  545.  computers. Offices are certainly becoming more paperless, and the arrival
  546.  of powerful networking systems will accelerate this, but paper continues to
  547.  be a very useful medium. As a result, in recent years growth has occurred
  548.  in another direction--incorporating paper as a computer input and output
  549.  device. Powerful laser printers, desktop publishing systems, and optical
  550.  scanners and optical character recognition will make it more practical to
  551.  input from and output to paper.
  552.       Although the founders of Microsoft fully appreciate the impact of the
  553.  second industrial revolution, nobody can predict in detail how the
  554.  revolution will unfold. Instead, Microsoft bases its day-to-day decisions
  555.  on dual sets of goals: short-term goals, which are well known, and a long-
  556.  term goal--our vision of the automated office. Each decision has to meet
  557.  our short-term goals, and it must be consonant with our long-term vision, a
  558.  vision that becomes more precise as the revolution progresses.
  559.       When 16-bit microprocessors were first announced, Microsoft knew that
  560.  the "iron" was now sufficiently powerful to begin to realize this vision.
  561.  But a powerful computer environment requires both strong iron and a
  562.  sophisticated operating system. The iron was becoming available, but the
  563.  operating system that had been standard for 8-bit microprocessors was
  564.  inadequate. This is when and why Microsoft entered the operating system
  565.  business: We knew that we needed a powerful operating system to realize our
  566.  vision and that the only way to guarantee its existence and suitability was
  567.  to write it ourselves.
  568.  
  569.  
  570.  1.1  MS-DOS version 1.0
  571.  
  572.  MS-DOS got its start when IBM asked Microsoft to develop a disk operating
  573.  system for a new product that IBM was developing, the IBM Personal Computer
  574.  (PC). Microsoft's only operating system product at that time was XENIX, a
  575.  licensed version of AT&T's UNIX  operating system. XENIX/UNIX requires a
  576.  processor with memory management and protection facilities. Because the
  577.  8086/8088 processors had neither and because XENIX/UNIX memory
  578.  requirements--modest by minicomputer standards of the day--were nonetheless
  579.  large by microcomputer standards, a different operating system had to be
  580.  developed.
  581.       CP/M-80, developed by Digital Research, Incorporated (DRI), had been
  582.  the standard 8-bit operating system, and the majority of existing
  583.  microcomputer software had been written to run on CP/M-80. For this reason,
  584.  Microsoft decided to make MS-DOS version 1.0 as compatible as possible with
  585.  CP/M-80. The 8088 processor would not run the existing CP/M-80 programs,
  586.  which were written for the 8080 processor, but because 8080 programs could
  587.  be easily and semiautomatically converted to run on the 8088, Microsoft
  588.  felt that minimizing adaptation hassles by minimizing operating system
  589.  incompatibility would hasten the acceptance of MS-DOS on the IBM PC.
  590.       A major software product requires a great deal of development time,
  591.  and IBM was in a hurry to introduce its PC. Microsoft, therefore, looked
  592.  around for a software product to buy that could be built onto to create MS-
  593.  DOS version 1.0. Such a product was found at Seattle Computer Products. Tim
  594.  Paterson, an engineer there, had produced a CP/M-80 "clone," called SCP-
  595.  DOS, that ran on the 8088 processor. Microsoft purchased full rights to
  596.  this product and to its source code and used the product as a starting
  597.  point in the development of MS-DOS version 1.0.
  598.       MS-DOS version 1.0 was released in August 1981. Available only for the
  599.  IBM PC, it consisted of 4000 lines of assembly-language source code and ran
  600.  in 8 KB of memory. MS-DOS version 1.1 was released in 1982 and worked with
  601.  double-sided 320 KB floppy disks.
  602.       Microsoft's goal was that MS-DOS version 1.0 be highly CP/M
  603.  compatible, and it was. Ironically, it was considerably more compatible
  604.  than DRI's own 8088 product, CP/M-86. As we shall see later, this CP/M
  605.  compatibility, necessary at the time, eventually came to cause Microsoft
  606.  engineers a great deal of difficulty.
  607.  
  608.  
  609.  1.2  MS-DOS version 2.0
  610.  
  611.  In early 1982, IBM disclosed to Microsoft that it was developing a hard
  612.  disk-based personal computer, the IBM XT. Microsoft began work on MS-DOS
  613.  version 2.0 to provide support for the new disk hardware. Changes were
  614.  necessary because MS-DOS, in keeping with its CP/M-80 compatible heritage,
  615.  had been designed for a floppy disk environment. A disk could contain only
  616.  one directory, and that directory could contain a maximum of 64 files. This
  617.  decision was reasonable when first made because floppy disks held only
  618.  about 180 KB of data.
  619.       For the hard disk, however, the 64-file limit was much too small, and
  620.  using a single directory to manage perhaps hundreds of files was
  621.  clumsy. Therefore, the MS-DOS version 2.0 developers--Mark Zbikowski, Aaron
  622.  Reynolds, Chris Peters, and Nancy Panners--added a hierarchical file
  623.  system. In a hierarchical file system a directory can contain other
  624.  directories and files. In turn, those directories can con- tain a mixture
  625.  of files and directories and so on. A hierarchically designed system starts
  626.  with the main, or "root," directory, which itself can contain (as seen in
  627.  Figure 1-1) a tree-structured collection of files and directories.
  628.  
  629.  
  630.                  directory WORK
  631.                             ADMIN
  632.                 ┌────────── BUDGET
  633.                 │           CALENDAR
  634.                 │           LUNCH.DOC
  635.                 │           PAYROLL ─────────┐
  636.                 │           PHONE.LST        │
  637.                 │           SCHED.DOC        │
  638.                 │                            │
  639.             directory BUDGET              directory PAYROLL
  640.                        MONTH                         ADDRESSES
  641.                        QUARTER                       MONTHLY
  642.                        YEAR                          NAMES
  643.                        1986 ────────┐                RETIRED ─────┐
  644.              ┌──────── 1985         │                VACATION     │
  645.              │                      │                WEEKLY       │
  646.              │                      │                             │
  647.       directory 1985         directory 1986             directory RETIRED
  648.              │                      │                             │
  649.  
  650.  Figure 1-1.  A directory tree hierarchy. Within the WORK directory
  651.  are five files (ADMIN, CALENDAR, LUNCH.DOC, PHONE.LST, SCHED.DOC) and two
  652.  subdirectories (BUDGET, PAYROLL). Each subdirectory has its own
  653.  subdirectories.
  654.  
  655.  
  656.  1.3  MS-DOS version 3.0
  657.  
  658.  MS-DOS version 3.0 was introduced in August 1984, when IBM announced the
  659.  IBM PC/AT. The AT contains an 80286 processor, but, when running DOS, it
  660.  uses the 8086 emulation mode built into the chip and runs as a "fast 8086."
  661.  The chip's extended addressing range and its protected mode architecture
  662.  sit unused.1
  663.       MS-DOS version 3.1 was released in November 1984 and contained
  664.  networking support. In January 1986, MS-DOS version 3.2--a minor revision--
  665.  was released. This version supported 3-1/2-inch floppy disks and contained
  666.  the formatting function for a device in the device driver. In 1987, MS-DOS
  667.  version 3.3 followed; the primary enhancement of this release was support
  668.  for the IBM PS/2 and compatible hardware.
  669.  
  670.  
  671.  1.4  MS-DOS version 4.0
  672.  
  673.  Microsoft started work on a multitasking version of MS-DOS in January 1983.
  674.  At the time, it was internally called MS-DOS version 3.0. When a new
  675.  version of the single-tasking MS-DOS was shipped under the name MS-DOS
  676.  version 3.0, the multitasking version was renamed, internally, to MS-DOS
  677.  version 4.0. A version of this product--a multitasking, real-mode only MS-
  678.  DOS--was shipped as MS-DOS version 4.0. Because MS-DOS version 4.0 runs
  679.  only in real mode, it can run on 8088 and 8086 machines as well as on 80286
  680.  machines. The limitations of the real mode environment make MS-DOS version
  681.  4.0 a specialized product. Although MS-DOS version 4.0 supports full
  682.  preemptive multitasking, system memory is limited to the 640 KB available
  683.  in real mode, with no swapping.2 This means that all processes have to fit
  684.  into the single 640 KB memory area. Only one MS-DOS version 3.x compatible
  685.  real mode application can be run; the other processes must be special MS-
  686.  DOS version 4.0 processes that understand their environment and cooperate
  687.  with the operating system to coexist peacefully with the single MS-DOS
  688.  version 3.x real mode application.
  689.       Because of these restrictions, MS-DOS version 4.0 was not intended for
  690.  general release, but as a platform for specific OEMs to support extended PC
  691.  architectures. For example, a powerful telephone management system could be
  692.  built into a PC by using special MS-DOS version 4.0 background processes to
  693.  control the telephone equipment. The resulting machine could then be
  694.  marketed as a "compatible MS-DOS 3 PC with a built-in superphone."
  695.       Although MS-DOS version 4.0 was released as a special OEM product, the
  696.  project--now called MS-DOS version 5.0--continued. The goal was to take
  697.  advantage of the protected mode of the 80286 to provide full general
  698.  purpose multitasking without the limitations--as seen in MS-DOS version
  699.  4.0--of a real-mode only environment. Soon, Microsoft and IBM signed a
  700.  Joint Development Agreement that provided for the design and development of
  701.  MS-DOS version 5.0 (now called CP/DOS). The agreement is complex, but it
  702.  basically provides for joint development and then subsequent joint
  703.  ownership, with both companies holding full rights to the resulting
  704.  product.
  705.       As the project neared completion, the marketing staffs looked at
  706.  CP/DOS, nee DOS 5, nee DOS 4, nee DOS 3, and decided that it needed...you
  707.  guessed it...a name change. As a result, the remainder of this book will
  708.  discuss the design and function of an operating system called OS/2.
  709.  
  710.  
  711.  
  712.  2  Goals and Compatibility Issues
  713.  
  714.  ───────────────────────────────────────────────────────────────────────────
  715.  
  716.  OS/2 is similar to traditional multitasking operating systems in many ways:
  717.  It provides multitasking, scheduling, disk management, memory management,
  718.  and so on. But it is also different in many ways, because a personal
  719.  computer is very different from a multiuser minicomputer. The designers of
  720.  OS/2 worked from two lists: a set of goals and a set of compatibility
  721.  issues. This chapter describes those goals and compatibility issues and
  722.  provides the context for a later discussion of the design itself.
  723.  
  724.  
  725.  2.1  Goals
  726.  
  727.  The primary goal of OS/2 is to be the ideal office automation operating
  728.  system. The designers worked toward this goal by defining the following
  729.  intermediate and, seemingly, contradictory goals:
  730.  
  731.       ■  To provide device-independent graphics drivers without introducing
  732.          any significant overhead.
  733.  
  734.       ■  To allow applications direct access to high-bandwidth peripherals
  735.          but maintain the ability to virtualize or apportion the usage of
  736.          those peripherals.
  737.  
  738.       ■  To provide multitasking without reducing the performance and
  739.          response available from a single-tasking system.
  740.  
  741.       ■  To provide a fully customized environment for each program and its
  742.          descendants yet also provide a standard environment that is
  743.          unaffected by other programs in the system.
  744.  
  745.       ■  To provide a protected environment to ensure system stability yet
  746.          one that will not constrain applications from the capabilities they
  747.          have under nonprotected systems.
  748.  
  749.  
  750.  2.1.1  Graphical User Interface
  751.  By far the fastest and easiest way people receive information is through
  752.  the eye. We are inherently visual creatures. Our eyes receive information
  753.  rapidly; they can "seek" to the desired information and "zoom" their
  754.  attention in and out with small, rapid movements of the eye muscles. A
  755.  large part of the human brain is dedicated to processing visual
  756.  information. People abstract data and meaning from visual material--from
  757.  text to graphics to motion pictures--hundreds of times faster than from any
  758.  other material.
  759.       As a result, if an office automation system is to provide quantities
  760.  of information quickly and in a form in which it can be easily absorbed, a
  761.  powerful graphics capability is essential. Such capabilities were rare in
  762.  earlier minicomputer operating systems because of the huge memory and
  763.  compute power costs of high-resolution displays. Today's microcomputers
  764.  have the memory to contain the display information, they have the CPU power
  765.  to create and manipulate that information, and they have no better use for
  766.  those capabilities than to support powerful, easy-to-use graphical
  767.  applications.
  768.       Graphics can take many forms--pictures, tables, drawings, charts--
  769.  perhaps incorporating color and even animation. All are powerful adjuncts
  770.  to the presentation of alphanumeric text. Graphical applications don't
  771.  necessarily employ charts and pictures. A WYSIWYG (What You See Is What You
  772.  Get) typesetting program may display only text, but if that text is drawn
  773.  in graphics mode, the screen can show any font, in any type size, with
  774.  proportional spacing, kerning, and so on.
  775.       The screen graphics components of OS/2 need to be device independent;
  776.  that is, an application must display the proper graphical "picture" without
  777.  relying on the specific characteristics of any particular graphical display
  778.  interface board. Each year the state of the art in displays gets better; it
  779.  would be extremely shortsighted to tie applications to a particular display
  780.  board, for no matter how good it is, within a couple of years it will be
  781.  obsolete.
  782.       The idea is to encapsulate device-specific code by requiring that each
  783.  device come with a software package called a device driver. The application
  784.  program issues commands for a generic device, and the device driver then
  785.  translates those commands to fit the characteristics of the actual device.
  786.  The result is that the manufacturer of a new graphics display board needs
  787.  to write an appropriate device driver and supply it with the board. The
  788.  application program doesn't need to know anything about the device, and the
  789.  device driver doesn't need to know anything about the application, other
  790.  than the specification of the common interface they share. This common
  791.  interface describes a virtual display device; the general technique of
  792.  hiding a complicated actual situation behind a simple, standard interface
  793.  is called "virtualization."
  794.       Figure 2-1 shows the traditional operating system device driver
  795.  architecture. Applications don't directly call device drivers because
  796.  device drivers need to execute in the processor's privilege mode to
  797.  manipulate their device; the calling application must run in normal mode.
  798.  In the language of the 80286/80386 family of processors, privilege mode is
  799.  called ring 0, and normal mode is called ring 3. The operating system
  800.  usually acts as a middleman: It receives the request, validates it, deals
  801.  with issues that arise when there is only one device but multiple
  802.  applications are using it, and then passes the request to the device
  803.  driver. The device driver's response or return of data takes the reverse
  804.  path, winding its way through the operating system and back to the
  805.  application program.
  806.  
  807.  
  808.           Application                      Kernel             Device driver
  809.            (ring 3)                       (ring 0)               (ring 0)
  810.  ─────────────────────────────┬──────────────────────────────┬──────────────
  811.                                     Request
  812.                               │     packet:                  │ ┌──────────┐
  813.                                  ┌──────────┐    Device        │  Device  │
  814.                               │  │          │    descriptor ─┼─┤  driver  │
  815.  Call deviceio (arg 1...argn)    │ function │    #1            └──────────┘
  816.                               │  │   arg1   │          ·     │
  817.          ───────────────────────│    ∙     ├─┐        ·
  818.                    Ring       │  │   argn   │ │        ·     │
  819.                    transition    │          │ │        ·       ┌──────────┐
  820.                               │  └──────────┘ └ Device      │ │  Device  │
  821.                                                  descriptor ───┤  driver  │
  822.                               │                  #N          │ └──────────┘
  823.  
  824.  Figure 2-1.  Traditional device driver architecture. When an application
  825.  wants to do device I/0, it calls the operating system, which builds a
  826.  device request packet, determines the target device, and delivers the
  827.  packet. The device driver's response follows the opposite route through the
  828.  kernel back to the application.
  829.  
  830.  
  831.       This approach solves the device virtualization problem, but at a cost
  832.  in performance. The interface between the application and the device driver
  833.  is narrow; that is, the form messages can take is usually restricted.
  834.  Commonly, the application program is expected to build a request block that
  835.  contains all the information and data that the device driver needs to
  836.  service the request; the actual call to the operating system is simply
  837.  "pass this request block to the device driver." Setting up this block takes
  838.  time, and breaking it down in the device driver again takes time. More time
  839.  is spent on the reply; the device driver builds, the operating system
  840.  copies, and the application breaks down. Further time is spent calling down
  841.  through the internal layers of the operating system, examining and copying
  842.  the request block, routing to the proper device driver, and so forth.
  843.  Finally, the transition between rings (privilege and normal mode) is also
  844.  time-consuming, and two such transitions occur--to privilege mode and back
  845.  again.
  846.       Such a cost in performance was acceptable in nongraphics-based systems
  847.  because, typically, completely updating a screen required only 1920 (or
  848.  fewer) bytes of data. Today's graphics devices can require 256,000 bytes or
  849.  more per screen update, and future devices will be even more demanding.
  850.  Furthermore, applications may expect to update these high-resolution
  851.  screens several times a second.1
  852.       OS/2 needed powerful, device-independent graphical display support
  853.  that had a wide, efficient user interface--one that did not involve ring
  854.  transitions, the operating system, or other unnecessary overhead. As we'll
  855.  see later, OS/2 meets this requirement by means of a mechanism called
  856.  dynamic linking.
  857.  
  858.  
  859.  2.1.2  Multitasking
  860.  To be really useful, a personal computer must be able to do more than one
  861.  chore at a time--an ability called multitasking. We humans multitask all
  862.  the time. For example, you may be involved in three projects at work, be
  863.  halfway through a novel, and be taking Spanish lessons. You pick up each
  864.  task in turn, work on it for a while, and then put it down and work on
  865.  something else. This is called serial multitasking. Humans can also do some
  866.  tasks simultaneously, such as driving a car and talking. This is called
  867.  parallel multitasking.
  868.       In a serial multitasking computer environment, a user can switch
  869.  activities at will, working for a while at each. For example, a user can
  870.  leave a word-processing program without terminating it, consult a
  871.  spreadsheet, and then return to the waiting word-processing program. Or, if
  872.  someone telephones and requests an appointment, the user can switch from a
  873.  spreadsheet to a scheduling program, consult the calendar, and then return
  874.  to the spreadsheet.
  875.       The obvious value of multitasking makes it another key requirement for
  876.  OS/2: Many programs or applications can run at the same time. But
  877.  multitasking is useful for more than just switching between applications:
  878.  Parallel multitasking allows an application to do work by itself--perhaps
  879.  print a large file or recalculate a large spreadsheet--while the user
  880.  works with another application. Because OS/2 supports full multitasking,
  881.  it can execute programs in addition to the application(s) the user is
  882.  running, providing advanced services such as network mail without
  883.  interrupting or interfering with the user's work.2
  884.  
  885.  
  886.  2.1.3  Memory Management
  887.  Multitasking is fairly easy to achieve. All that's necessary is a source of
  888.  periodic hardware interrupts, such as a clock circuit, to enable the
  889.  operating system to effect a "context switch," or to reschedule. To be
  890.  useful, however, a multitasking system needs an effective memory management
  891.  system. For example, a user wants to run two applications on a system. Each
  892.  starts at as low a memory location as possible to maximize the amount of
  893.  memory it can use. Unfortunately, if the system supports multitasking and
  894.  the user tries to run both applications simultaneously, each attempts to
  895.  use the same memory cells, and the applications destroy each other.
  896.       A memory management system solves this problem by using special
  897.  hardware facilities built into 80286/80386 processors (for example, IBM
  898.  PC/AT machines and compatibles and 80386-based machines).3 The memory
  899.  management system uses the hardware to virtualize the memory of the machine
  900.  so that each program appears to have all memory to itself.
  901.       Memory management is more than keeping programs out of each other's
  902.  way. The system must track the owner or user(s) of each piece of memory so
  903.  that the memory space can be reclaimed when it is no longer needed, even if
  904.  the owner of the memory neglects to explicitly release it. Some operating
  905.  systems avoid this work by assuming that no application will ever fail to
  906.  return its memory when done or by examining the contents of memory and
  907.  ascertaining from those contents whether the memory is still being used.
  908.  (This is called "garbage collection.") Neither alternative was acceptable
  909.  for OS/2. Because OS/2 will run a variety of programs written by many
  910.  vendors, identifying free memory by inspection is impossible, and assuming
  911.  perfection from the applications themselves is unwise. Tracking the
  912.  ownership and usage of memory objects can be complex, as we shall see in
  913.  our discussion on dynamic link libraries.
  914.       Finally, the memory management system must manage memory overcommit.
  915.  The multitasking capability of OS/2 allows many applications to be run
  916.  simultaneously; thus, RAM must hold all these programs and their data.
  917.  Although RAM becomes cheaper every year, buying enough to hold all of one's
  918.  applications at one time is still prohibitive. Furthermore, although RAM
  919.  prices continue to drop, the memory requirements of applications will
  920.  continue to rise. Consequently, OS/2 must contain an effective mechanism to
  921.  allocate more memory to the running programs than in fact physically
  922.  exists. This is called memory overcommit.
  923.       OS/2 accomplishes this magic with the classic technique of swapping.
  924.  OS/2 periodically examines each segment of memory to see if it has been
  925.  used recently. When a request is made for RAM and none is available, the
  926.  least recently used segment of memory (the piece that has been unused for
  927.  the longest time) is written to a disk file, and the RAM it occupied is
  928.  made available. Later, if a program attempts to use the swapped-out memory,
  929.  a "memory not present" fault occurs. OS/2 intercepts the fault and reloads
  930.  the memory information from the disk into memory, swapping out some other
  931.  piece of memory, if necessary, to make room. This whole process is
  932.  invisible to the application that uses the swapped memory area; the only
  933.  impact is a small delay while the needed memory is read back from the
  934.  disk.
  935.       The fundamental concepts of memory overcommit and swapping are simple,
  936.  but a good implementation is not. OS/2 must choose the right piece of
  937.  memory to swap out, and it must swap it out efficiently. Not only must care
  938.  be taken that the swap file doesn't grow too big and consume all the free
  939.  disk space but also that deadlocks don't occur. For example, if all the
  940.  disk swap space is filled, it may be impossible to swap into RAM a piece of
  941.  memory because no free RAM is available, and OS/2 can't free up RAM because
  942.  no swap space exists to write it out to. Naturally, the greater the load on
  943.  the system, the slower the system will be, but the speed degradation must
  944.  be gradual and acceptable, and the system must never deadlock.
  945.       The issues involved in memory management and the memory management
  946.  facilities that OS/2 provides are considerably more complex than this
  947.  overview. We'll return to the subject of memory management in detail in
  948.  Chapter 9.
  949.  
  950.  
  951.  2.1.4  Protection
  952.  I mentioned earlier that OS/2 cannot trust applications to behave
  953.  correctly. I was talking about memory management, but this concern
  954.  generalizes into the next key requirement: OS/2 must protect applications
  955.  from the proper or improper actions of other applications that may be
  956.  running on the system.
  957.       Because OS/2 will run applications and programs from a variety of
  958.  vendors, every user's machine will execute a different set of applications,
  959.  running in different ways on different data. No software vendor can fully
  960.  test a product in all possible environments. This makes it critical that an
  961.  error on the part of one program does not crash the system or some other
  962.  program or, worse, corrupt data and not bring down the system. Even if no
  963.  data is damaged, system crashes are unacceptable. Few users have the
  964.  background or equipment even to diagnose which application caused the
  965.  problem.
  966.       Furthermore, malice, as well as accident, is a concern. Microsoft's
  967.  vision of the automated office cannot be realized without a system that is
  968.  secure from deliberate attack. No corporation will be willing to base its
  969.  operations on a computer network when any person in that company--with the
  970.  help of some "cracker" programs bought from the back of a computer
  971.  magazine--can see and change personnel or payroll files, billing notices,
  972.  or strategic planning memos.
  973.       Today, personal computers are being used as a kind of super-
  974.  sophisticated desk calculator. As such, data is secured by traditional
  975.  means--physical locks on office doors, computers, or file cabinets that
  976.  store disks. Users don't see a need for a protected environment because
  977.  their machine is physically protected. This lack of interest in protection
  978.  is another example of the development of a breakthrough technology.
  979.  Protection is not needed because the machine is secure and operates on data
  980.  brought to it by traditional office channels. In the future, however,
  981.  networked personal computers will become universal and will act both as the
  982.  processors and as the source (via the network) of the data. Thus, in this
  983.  role, protection is a key requirement and is indeed a prerequisite for
  984.  personal computers to assume that central role.
  985.  
  986.  
  987.  2.1.5  Encapsulation
  988.  When a program runs in a single-tasking system such as MS-DOS version 3.x,
  989.  its environment is always constant--consisting of the machine and MS-DOS.
  990.  The program can expect to get the same treatment from the system and to
  991.  provide exactly the same interaction with the user each time it runs. In a
  992.  multitasking environment, however, many programs can be running. Each
  993.  program can be using files and devices in different ways; each program can
  994.  be using the mouse, each program can have the screen display in a different
  995.  mode, and so on. OS/2 must encapsulate, or isolate, each program so that it
  996.  "sees" a uniform environment each time it runs, even though the computer
  997.  environment itself may be different each time.
  998.  
  999.  
  1000.  2.1.6  Interprocess Communication (IPC)
  1001.  In a single-tasking environment such as MS-DOS version 3.x, each program
  1002.  stands alone. If it needs a particular service not provided by the
  1003.  operating system, it must provide that service itself. For example, every
  1004.  application that needs a sort facility must contain its own.
  1005.       Likewise, if a spreadsheet needs to access values from a database, it
  1006.  must contain the code to do so. This extra code complicates the spreadsheet
  1007.  program, and it ties the program to a particular database product or
  1008.  format. A user might be unable to switch to a better product because the
  1009.  spreadsheet is unable to understand the new database's file formats.
  1010.       A direct result of such a stand-alone environment is the creation of
  1011.  very large and complex "combo" packages such as Lotus Symphony. Because
  1012.  every function that the user may want must be contained within one program,
  1013.  vendors supply packages that attempt to contain everything.
  1014.       In practice, such chimeric programs tend to be large and cumbersome,
  1015.  and their individual functional components (spreadsheets, word processors,
  1016.  and databases, for example) are generally more difficult to use and less
  1017.  sophisticated than individual applications that specialize in a single
  1018.  function.
  1019.       The stand-alone environment forces the creation of larger and more
  1020.  complex programs, each of which typically understands only its own file
  1021.  formats and works poorly, if at all, with data produced by other programs.
  1022.  This vision of personal computer software growing monstrous until
  1023.  collapsing from its own weight brings about another OS/2 requirement:
  1024.  Applications must be able to communicate, easily and efficiently, with
  1025.  other applications.
  1026.       More specifically, an application must be able to find (or name) the
  1027.  application that provides the information or service that the client needs,
  1028.  and it must be able to establish efficient communication with the provider
  1029.  program without requiring that either application have specific knowledge
  1030.  of the internal workings of the other. Thus, a spreadsheet program must be
  1031.  able to communicate with a database program and access the values it needs.
  1032.  The spreadsheet program is therefore not tied to any particular database
  1033.  program but can work with any database system that recognizes OS/2 IPC
  1034.  requests.
  1035.       Applications running under OS/2 not only retain their full power as
  1036.  individual applications but also benefit from cross-application
  1037.  communication. Furthermore, the total system can be enhanced by upgrading
  1038.  an application that provides services to others. When a new, faster, or
  1039.  more fully featured database package is installed, not only is the user's
  1040.  database application improved but the database functions of the spreadsheet
  1041.  program are improved as well.
  1042.       The OS/2 philosophy is that no program should reinvent the wheel.
  1043.  Programs should be written to offer their services to other programs and to
  1044.  take advantage of the offered services of other programs. The result is a
  1045.  maximally effective and efficient system.
  1046.  
  1047.  
  1048.  2.1.7  Direct Device Access
  1049.  Earlier, we discussed the need for a high-performance graphical interface
  1050.  and the limitations of the traditional device driver architecture. OS/2
  1051.  contains a built-in solution for the screen graphical interface, but what
  1052.  about other, specialized devices that may require a higher bandwidth
  1053.  interface than device drivers provide? The one sure prediction about the
  1054.  future of a technological breakthrough is that you can't fully predict it.
  1055.  For this reason, the final key requirement for OS/2 is that it contain an
  1056.  "escape hatch" in anticipation of devices that have performance needs too
  1057.  great for a device driver model.
  1058.       OS/2 provides this expandability by allowing applications direct
  1059.  access to hardware devices--both the I/O ports and any device memory. This
  1060.  must be done, of course, in such a way that only devices which are intended
  1061.  to be used in this fashion can be so accessed. Applications are prevented
  1062.  from using this access technique on devices that are being managed by the
  1063.  operating system or by a device driver. This facility gives applications
  1064.  the ability to take advantage of special nonstandard hardware such as OCRs
  1065.  (Optical Character Readers), digitizer tablets, Fax equipment, special
  1066.  purpose graphics cards, and the like.
  1067.  
  1068.  
  1069.  2.2  Compatibility Issues
  1070.  
  1071.  But OS/2 has to do more than meet the goals we've discussed: It must be
  1072.  compatible with 8086/8088 and 80286 architecture, and it must be compatible
  1073.  with MS-DOS. By far the easiest solution would have been to create a new
  1074.  multitasking operating system that would not be compatible with MS-DOS, but
  1075.  such a system is unacceptable. Potential users may be excited about the new
  1076.  system, but they won't buy it until applications are available. Application
  1077.  writers may likewise be excited, but they won't adapt their products for it
  1078.  until the system has sold enough copies to gain significant market share.
  1079.  This "catch 22" means that the only people who will buy the new operating
  1080.  system are the developers' mothers, and they probably get it at a discount
  1081.  anyway.
  1082.  
  1083.  
  1084.  2.2.1  Real Mode vs Protect Mode
  1085.  The first real mode compatibility issue relates to the design of the 80286
  1086.  microprocessor--the "brain" of an MS-DOS computer. This chip has two
  1087.  incompatible modes--real (compatibility) mode and protect mode. Real mode
  1088.  is designed to run programs in exactly the same manner as they run on the
  1089.  8086/8088 processor. In other words, when the 80286 is in real mode, it
  1090.  "looks" to the operating system and programs exactly like a fast
  1091.  8088.
  1092.       But the designers of the 80286 wanted it to be more than a fast 8088.
  1093.  They wanted to add such features as memory management, memory protection,
  1094.  and the ring protection mechanism, which allows the operating system to
  1095.  protect one application from another. They weren't able to do this while
  1096.  remaining fully compatible with the earlier 8088 chip, so they added a
  1097.  second mode to the 80286--protect mode. When the processor is running in
  1098.  protect mode, it provides these important new features, but it will not run
  1099.  most programs written for the 8086/8088.
  1100.       In effect, an 80286 is two separate microprocessors in one package. It
  1101.  can act like a very fast 8088--compatible, but with no new capabilities--or
  1102.  it can act like an 80286--incompatible, but providing new features.
  1103.  Unfortunately, the designers of the chip didn't appreciate the importance
  1104.  of compatibility in the MS-DOS marketplace, and they designed the 80286 so
  1105.  that it can run in either mode but can't switch back and forth at will.4
  1106.  In other words, an 80286 was designed to run only old 8086/8088 programs,
  1107.  or it can run only new 80286 style programs, but never both at the same
  1108.  time.
  1109.       In summary, OS/2 was required to do something that the 80286 was not
  1110.  designed for--execute both 8086/8088 style (real mode) and 80286 style
  1111.  (protect) mode programs at the same time. The existence of this book should
  1112.  lead you to believe that this problem was solved, and indeed it was.
  1113.  
  1114.  
  1115.  2.2.2  Running Applications in Real (Compatibility) Mode
  1116.  Solving the real mode vs protect mode problem, however, presented other
  1117.  problems. In general, the problems came about because the real mode
  1118.  programs were written for MS-DOS versions 2.x or 3.x, both of which are
  1119.  single-tasking environments.
  1120.       Although MS-DOS is normally spoken of as an operating system, it could
  1121.  just as accurately be called a "system executive." Because it runs in an
  1122.  unprotected environment, applications are free to edit interrupt vectors,
  1123.  manipulate peripherals, and in general take over from MS-DOS wherever they
  1124.  wish. This flexibility is one reason for the success of MS-DOS; if MS-DOS
  1125.  doesn't offer the service your program needs, you can always help yourself.
  1126.  Developers were free to explore new possibilities, often with great
  1127.  success. Most applications view MS-DOS as a program loader and as a set of
  1128.  file system subroutines, interfacing directly with the hardware for all
  1129.  their other needs, such as intercepting interrupt vectors, editing disk
  1130.  controller parameter tables, and so on.
  1131.       It may seem that if a popular application "pokes" the operating system
  1132.  and otherwise engages in unsavory practices that the authors or users of
  1133.  the application will suffer because a future release, such as OS/2, may not
  1134.  run the application correctly. To the contrary, the market dynamics state
  1135.  that the application has now set a standard, and it's the operating system
  1136.  developers who suffer because they must support that standard. Usually,
  1137.  that "standard" operating system interface is not even known; a great deal
  1138.  of experimentation is necessary to discover exactly which undocumented side
  1139.  effects, system internals, and timing relationships the application is
  1140.  dependent on.
  1141.       Offering an MS-DOS-compatible Applications Program Interface (API)
  1142.  provides what we call level 1 compatibility. Allowing applications to
  1143.  continue to manipulate system hardware provides level 2 compatibility.
  1144.  Level 3 issues deal with providing an execution environment that supports
  1145.  the hidden assumptions that programs written for a single-tasking
  1146.  environment may make. Three are discussed below by way of illustration.
  1147.  
  1148.  
  1149.  2.2.2.1  Memory Utilization
  1150.  The existing real mode applications that OS/2 must support were written for
  1151.  an environment in which no other programs are running. As a result,
  1152.  programs typically consume all available memory in the system in the belief
  1153.  that, since no other program is around to use any leftover memory, they
  1154.  might as well use it all. If a program doesn't ask for all available memory
  1155.  at first, it may ask for the remainder at some later time. Such a
  1156.  subsequent request could never be refused under MS-DOS versions 2.x and
  1157.  3.x, and applications were written to depend on this. Therefore, such a
  1158.  request must be satisfied under OS/2 to maintain full compatibility.
  1159.       Even the manner of a memory request depends on single-tasking
  1160.  assumptions. Programs typically ask for all memory in two steps. First,
  1161.  they ask for the maximum amount of memory that an 8088 can provide--1 MB.
  1162.  The application's programmer knew that the request would be refused because
  1163.  1 MB is greater than the 640 KB maximum supported by MS-DOS; but when MS-
  1164.  DOS refuses the request, it tells the application exactly how much memory
  1165.  is available. Programs then ask for that amount of memory. The programmer
  1166.  knew that MS-DOS would not refuse the second memory request for
  1167.  insufficient memory because when MS-DOS responded to the first request it
  1168.  told the application exactly how much memory was available. Consequently,
  1169.  programmers rarely included a check for an "insufficient memory" error from
  1170.  the second call.
  1171.       This shortcut introduces problems in the OS/2 multitasking
  1172.  environment. When OS/2 responded to the first too-large request, it would
  1173.  return the amount of memory available at that exact moment. Other programs
  1174.  are simultaneously executing; by the time our real mode program makes its
  1175.  second request, some more memory may have been given out, and the second
  1176.  request may also be too large. It won't do any good for OS/2 to respond
  1177.  with an error code, however, because the real mode application does not
  1178.  check for one (it was written in the belief that it is impossible to get
  1179.  such a code on the second call). The upshot is that even if OS/2 refused
  1180.  the second call the real mode application would assume that it had been
  1181.  given the memory, would use it, and in the process would destroy the other
  1182.  program(s) that were the true owners of that memory.
  1183.       Obviously, OS/2 must resolve this and similar issues to support the
  1184.  existing base of real mode applications.
  1185.  
  1186.  
  1187.  2.2.2.2  File Locking
  1188.  Because multitasking systems run more than one program at the same time,
  1189.  two programs may try to write or to modify the same file at the same time.
  1190.  Or one may try to read a file while another is changing that file's
  1191.  contents. Multitasking systems usually solve this problem by means of a
  1192.  file-locking mechanism, which allows one program to temporarily prevent
  1193.  other programs from reading and/or writing a particular file.
  1194.       An application may find that a file it is accessing has been locked by
  1195.  some other application in the system. In such a situation, OS/2 normally
  1196.  returns a "file locked" error code, and the application typically gives up
  1197.  or waits and retries the operation later. OS/2 cannot return a "file
  1198.  locked" error to an old-style real mode application, though, because when
  1199.  the application was written (for MS-DOS versions 2.x or 3.x) no such error
  1200.  code existed because no such error was possible. Few real mode applications
  1201.  even bother to check their read and write operations for error codes, and
  1202.  those that do wouldn't "understand" the error code and wouldn't handle it
  1203.  correctly.
  1204.       OS/2 cannot compromise the integrity of the file-locking mechanism by
  1205.  allowing the real mode application to ignore locks, but it cannot report
  1206.  that the file is locked to the application either. OS/2 must determine the
  1207.  proper course of action and then take that action on behalf of the real
  1208.  mode application.
  1209.  
  1210.  
  1211.  2.2.2.3  Network Piggybacking
  1212.  Running under MS-DOS version 3.1, an application can use an existing
  1213.  network virtual circuit to communicate with an application running on the
  1214.  server machine to which the virtual circuit is connected. This is called
  1215.  "piggybacking" the virtual circuit because the applications on each end are
  1216.  borrowing a circuit that the network redirector established for other
  1217.  purposes. The two sets of programs can use a single circuit for two
  1218.  different purposes without confusion under MS-DOS version 3.1 because of
  1219.  its single-tasking nature. The redirector only uses the circuit when the
  1220.  application calls MS-DOS to perform a network function. Because the CPU is
  1221.  inside MS-DOS, it can't be executing the application software that sends
  1222.  private messages, which leaves the circuit free for use by the
  1223.  redirector.
  1224.       Conversely, if the application is sending its own private messages--
  1225.  piggybacking--then it can't be executing MS-DOS, and therefore the
  1226.  redirector code (which is built into MS-DOS) can't be using the virtual
  1227.  circuit.
  1228.       This is no longer the case in OS/2. OS/2 is a multitasking system, and
  1229.  one application can use the redirector at the same time that the real mode
  1230.  application is piggybacking the circuit. OS/2 must somehow interlock access
  1231.  to network virtual circuits so that multiple users of a network virtual
  1232.  circuit do not conflict.
  1233.  
  1234.  
  1235.  2.2.3  Popular Function Compatibility
  1236.  We've discussed some issues of binary compatibility, providing applications
  1237.  the internal software interfaces they had in MS-DOS. This is because it is
  1238.  vitally important that existing applications run correctly, unchanged,
  1239.  under the new operating system.5 OS/2 also needs to provide functional
  1240.  compatibility; it has to allow the creation of protect mode applications
  1241.  that provide the functions that users grew to know and love in real mode
  1242.  applications.
  1243.       This can be difficult because many popular applications (for example,
  1244.  "terminate and stay resident loadable helper" routines such as SideKick)
  1245.  were written for a single-tasking, unprotected environment without regard
  1246.  to the ease with which their function could be provided in a protected
  1247.  environment. For example, a popular application may implement some of its
  1248.  features by patching (that is, editing) MS-DOS itself. This cannot be
  1249.  allowed in OS/2 (the reason is discussed in Chapter 4), so OS/2 must
  1250.  provide alternative mechanisms for protect mode applications to provide
  1251.  services that users have grown to expect.
  1252.  
  1253.  
  1254.  2.2.4  Downward Compatibility
  1255.  So far, our discussion on compatibility has focused exclusively on upward
  1256.  compatibility--old programs must run in the new system but not vice versa.
  1257.  Downward compatibility--running new programs under MS-DOS--is also
  1258.  important. Developers are reluctant to write OS/2-only applications until
  1259.  OS/2 has achieved major penetration of the market, yet this very
  1260.  unavailability of software slows such penetration. If it's possible to
  1261.  write applications that take advantage of OS/2's protect mode yet also run
  1262.  unchanged under MS-DOS version 3.x, ISVs (Independent Software Vendors) can
  1263.  write their products for OS/2 without locking themselves out of the
  1264.  existing MS-DOS market.
  1265.  
  1266.  
  1267.  2.2.4.1  Family API
  1268.  To provide downward compatibility for applications, OS/2 designers
  1269.  integrated a Family6 Applications Program Interface (Family API) into the
  1270.  OS/2 project. The Family API provides a standard execution environment
  1271.  under MS-DOS version 3.x and OS/2. Using the Family API, a programmer can
  1272.  create an application that uses a subset of OS/2 functions (but a superset
  1273.  of MS-DOS version 3.x functions) and that runs in a binary compatible
  1274.  fashion under MS-DOS version 3.x and OS/2. In effect, some OS/2 functions
  1275.  can be retrofitted into an MS-DOS version 3.x environment by means of the
  1276.  Family API.
  1277.  
  1278.  
  1279.  2.2.4.2  Network Server-Client Compatibility
  1280.  Another important form of upward and downward compatibility is the network
  1281.  system. You can expect any OS/2 system to be on a network, communicating
  1282.  not only with MS-DOS 3.x systems but, one day, with a new version of OS/2
  1283.  as well. The network interface must be simultaneously upwardly and
  1284.  downwardly compatible with all past and future versions of networking MS-
  1285.  DOS.
  1286.  
  1287.  
  1288.  
  1289.  3  The OS/2 Religion
  1290.  
  1291.  ───────────────────────────────────────────────────────────────────────────
  1292.  
  1293.  Religion, in the context of software design, is a body of beliefs about
  1294.  design rights and design wrongs. A particular design is praised or
  1295.  criticized on the basis of fact--it is small or large, fast or slow--and
  1296.  also on the basis of religion--it is good or bad, depending on how well it
  1297.  obeys the religious precepts. Purpose and consistency underlie the design
  1298.  religion as a whole; its influence is felt in every individual judgment.
  1299.       The purpose of software design religion is to specify precepts that
  1300.  designers can follow when selecting an approach from among the many
  1301.  possibilities before them. A project of the size and scope of OS/2 needed a
  1302.  carefully thought out religion because OS/2 will dramatically affect this
  1303.  and future generations of operating systems. It needed a strong religion
  1304.  for another reason: to ensure consistency among wide-ranging features
  1305.  implemented by a large team of programmers. Such consistency is very
  1306.  important; if one programmer optimizes design to do A well, at the expense
  1307.  of doing B less well, and another programmer--in the absence of religious
  1308.  guidance--does the opposite, the end result is a product that does neither
  1309.  A nor B well.
  1310.       This chapter discusses the major architectural dogmas of the OS/2
  1311.  religion: maximum flexibility, a stable environment, localization of
  1312.  errors, and the software tools approach.
  1313.  
  1314.  
  1315.  3.1  Maximum Flexibility
  1316.  
  1317.  The introduction to this book discusses the process of technological
  1318.  breakthroughs. I have pointed out that one of the easiest predictions about
  1319.  breakthroughs is that fully predicting their course is impossible. For
  1320.  example, the 8088 microprocessor is designed to address 1 MB of memory, but
  1321.  the IBM PC and compatible machines are designed so that addressable memory
  1322.  is limited to 640 KB. When this decision was made, 640 KB was ten times the
  1323.  memory that the then state-of-the-art 8080 machines could use; the initial
  1324.  PCs were going to ship with 16 KB in them, and it seemed to all concerned
  1325.  that 640 KB was overly generous. Yet it took only a few years before 640 KB
  1326.  became the typical memory complement of a machine, and within another year
  1327.  that amount of memory was viewed as pitifully small.
  1328.       OS/2's design religion addresses the uncertain future by decreeing
  1329.  that--to the extent compatible with other elements in the design religion--
  1330.  OS/2 shall be as flexible as possible. The tenet of flexibility is that
  1331.  each component of OS/2 should be designed as if massive changes will occur
  1332.  in that area in a future release. In other words, the current component
  1333.  should be designed in a way that does not restrict new features and in a
  1334.  way that can be easily supported by a new version of OS/2, one that might
  1335.  differ dramatically in internal design.
  1336.       Several general principles result from a design goal of flexibility.
  1337.  All are intended to facilitate change, which is inevitable in the general
  1338.  yet unpredictable in the specific:
  1339.  
  1340.       1.  All OS/2 features should be sufficiently elemental (simple) that
  1341.           they can be easily supported in any future system, including
  1342.           systems fundamentally different in design from OS/2. Either the
  1343.           features themselves are this simple, or the features are built
  1344.           using base features that are this simple. The adjective simple
  1345.           doesn't particularly refer to externals--a small number of
  1346.           functions and options--but to internals. The internal operating
  1347.           system infrastructure necessary to provide a function should be
  1348.           either very simple or so fundamental to the nature of operating
  1349.           systems that it is inevitable in future releases.
  1350.              By way of analogy, as time travelers we may be able to guess
  1351.           very little about the twenty-first century, but we do know that
  1352.           people will still need to eat. The cuisine of the twenty-first
  1353.           century may be unguessable, but certainly future kitchens will
  1354.           contain facilities to cut and heat food. If we bring food that
  1355.           needs only those two operations, we'll find that even if there's
  1356.           nothing to our liking on the twenty-first-century standard menu
  1357.           the kitchen can still meet our needs.
  1358.              We've seen how important upward compatibility is for computer
  1359.           operating systems, so we can rest assured that the future MS-DOS
  1360.           "kitchen" will be happy to make the necessary effort to support
  1361.           old programs. All we have to do today is to ensure that such
  1362.           support is possible. Producing a compatible line of operating
  1363.           system releases means more than looking backward; it also means
  1364.           looking forward.
  1365.  
  1366.       2.  All system interfaces need to support expansion in a future
  1367.           release. For example, if a call queries the status of a disk file,
  1368.           then in addition to passing the operating system a pointer to a
  1369.           structure to fill in with the information, the application must
  1370.           also pass in the length of that structure. Although the current
  1371.           release of the operating system returns N bytes of information, a
  1372.           future release may support new kinds of disk files and may return
  1373.           M bytes of information. Because the application tells the
  1374.           operating system, via the buffer length parameter, which version
  1375.           of the information structure that the application understands (the
  1376.           old short version or the new longer version), the operating system
  1377.           can support both old programs and new programs simultaneously.
  1378.              In general, all system interfaces should be designed to support
  1379.           the current feature set without restraining the addition of
  1380.           features to the interfaces in future releases. Extra room should
  1381.           be left in count and flag arguments for future expansion, and all
  1382.           passed and returned structures need either to be self-sizing or to
  1383.           include a size argument.
  1384.              One more interface deserves special mention--the file system
  1385.           interface. Expanding the capabilities of the file system, such as
  1386.           allowing filenames longer than eight characters, is difficult
  1387.           because many old applications don't know how to process filenames
  1388.           that are longer than eight characters or they regard the longer
  1389.           names as illegal and reject them. OS/2 solves this and similar
  1390.           problems by specifying that all filenames supplied to or returned
  1391.           from the operating system be zero-terminated strings (ASCIIZ
  1392.           strings) of arbitrary length.
  1393.              Programmers are specifically cautioned against parsing or
  1394.           otherwise "understanding" filenames. Programs should consider file
  1395.           system pathnames as "magic cookies" to be passed to and from the
  1396.           operating system, but not to be parsed by the program. The details
  1397.           of this interface and other expandable interfaces are discussed in
  1398.           later chapters.
  1399.  
  1400.       3.  OS/2 needs to support the addition of functions at any time. The
  1401.           implementation details of these functions need to be hidden from
  1402.           the client applications so that those details can be changed at
  1403.           any time. Indeed, OS/2 should disguise even the source of a
  1404.           feature. Some APIs are serviced by kernel code, others are
  1405.           serviced by subroutine libraries, and still others may be serviced
  1406.           by other processes running in the system. Because a client
  1407.           application can't tell the difference, the system designers are
  1408.           free to change the implementation of an API as necessary. For
  1409.           example, an OS/2 kernel API might be considerably changed in a
  1410.           future release. The old API can continue to be supported by the
  1411.           creation of a subroutine library routine. This routine would take
  1412.           the old form of the API, convert it to the new form, call the OS/2
  1413.           kernel, and then backconvert the result. Such a technique allows
  1414.           future versions of OS/2 to support new features while continuing
  1415.           to provide the old features to existing programs. These techniques
  1416.           are discussed in detail in Chapter 7, Dynamic Linking.
  1417.  
  1418.       4.  Finally, to provide maximum flexibility, the operating system
  1419.           should be extensible and expandable in a piecemeal fashion out in
  1420.           the field. In other words, a user should be able to add functions
  1421.           to the system--for example, a database engine--or to upgrade or
  1422.           replace system components--such as a new graphics display driver--
  1423.           without a new release from Microsoft. A microcomputer design that
  1424.           allows third-party hardware additions and upgrades in the field is
  1425.           called an open system. The IBM PC line is a classic example of an
  1426.           open system. A design that contains no provisions for such
  1427.           enhancements is called a closed system. The earliest version of
  1428.           the Apple Macintosh is an example. At first glance, MS-DOS appears
  1429.           to be a closed software system because it contains no provisions
  1430.           for expansion. In practice, its unprotected environment makes MS-
  1431.           DOS the king of the open software systems because every
  1432.           application is free to patch the system and access the hardware as
  1433.           it sees fit. Keeping a software system open is as important as
  1434.           keeping a hardware system open. Because OS/2 is a protected
  1435.           operating system, explicit features, such as dynamic linking, are
  1436.           provided to allow system expansion by Microsoft, other software
  1437.           vendors, and users themselves. The topic of open systems is
  1438.           discussed more fully in Chapter 7.
  1439.  
  1440.  
  1441.  3.2  A Stable Environment
  1442.  
  1443.  An office automation operating system has to provide its users--the
  1444.  application programs and the human operator--with a stable environment.
  1445.  Every application should work the same way each time it's run; and each
  1446.  time an application is given the same data, it should produce the same
  1447.  result. The normal operation of one application should not affect any other
  1448.  application. Even a program error (bug) should not affect other programs in
  1449.  the system. Finally, if a program has bugs, the operating system should
  1450.  detect those bugs whenever possible and report them to the user. These
  1451.  certainly are obvious goals, but the nature of present-day computers makes
  1452.  them surprisingly difficult to achieve.
  1453.  
  1454.  
  1455.  3.2.1  Memory Protection
  1456.  Modern computers are based on the Von Neumann design--named after John Von
  1457.  Neumann, the pioneering Hungarian-born American mathematician and computer
  1458.  scientist. A Von Neumann computer consists of only two parts: a memory unit
  1459.  and a processing unit. The memory unit contains both the data to be
  1460.  operated on and the instructions (or program) that command the processing
  1461.  unit. The processing unit reads instructions from memory; these
  1462.  instructions may tell it to issue further reads to memory to retrieve data,
  1463.  to operate on data retrieved earlier, or to store data back into
  1464.  memory.
  1465.       A Von Neumann computer does not distinguish between instructions and
  1466.  data; both are stored in binary code in the computer's memory. Individual
  1467.  programs are responsible for keeping track of which memory locations hold
  1468.  instructions and which hold data, and each program uses the memory in a
  1469.  different way. Because the computer does not distinguish between
  1470.  instructions and data, a program may operate on its own instructions
  1471.  exactly as it operates on data. A program can read, modify, and write
  1472.  computer instructions at will.1
  1473.       This is exactly what OS/2 does when it is commanded to run a program:
  1474.  It reads the program into memory by treating it as data, and then it causes
  1475.  the data in those locations to be executed. It is even possible for a
  1476.  program to dynamically "reprogram" itself by manipulating its own
  1477.  instructions.
  1478.       Computer programs are extremely complex, and errors in their logic can
  1479.  cause the program to unintentionally modify data or instructions in memory.
  1480.  For example, a carelessly written program might contain a command buffer 80
  1481.  bytes in size because it expects no commands longer than 80 bytes. If a
  1482.  user types a longer command, perhaps in error, and the program does not
  1483.  contain a special check for this circumstance, the program will overwrite
  1484.  the memory beyond the 80-byte command buffer, destroying the data or
  1485.  instructions placed there.2
  1486.       In a single-tasking environment such as MS-DOS, only one application
  1487.  runs at a time. An error such as our example could damage memory belonging
  1488.  to MS-DOS, the application, or memory that is not in use. In practice (due
  1489.  to memory layout conventions) MS-DOS is rarely damaged. An aberrant program
  1490.  typically damages itself or modifies memory not in use. In any case, the
  1491.  error goes undetected, the program produces an incorrect result, or the
  1492.  system crashes. In the last two cases, the user loses work, but it is clear
  1493.  which application is in error--the one executing at the time of the crash.
  1494.  (For completeness, I'll point out that it is possible for an aberrant
  1495.  application to damage MS-DOS subtly enough so that the application itself
  1496.  completes correctly, but the next time an application runs, it fails. This
  1497.  is rare, and the new application generally fails immediately upon startup;
  1498.  so after a few such episodes with different applications, the user
  1499.  generally identifies the true culprit.)
  1500.       As we have seen, errors in programs are relatively well contained in a
  1501.  single-tasking system. MS-DOS cannot, unfortunately, correct the error, nor
  1502.  can it very often detect the error (these tasks can be shown to be
  1503.  mathematically impossible, in the general case). But at least the errors
  1504.  are contained within the aberrant application; and should errors in data or
  1505.  logic become apparent, the user can identify the erring application. When
  1506.  we execute a second program in memory alongside the first, the situation
  1507.  becomes more complicated.
  1508.       The first difficulty arises because the commonest error for a program
  1509.  to make is to use memory that MS-DOS has not allocated to it. In a single-
  1510.  tasking environment these memory locations are typically unused, but in a
  1511.  multitasking environment the damaged location(s) probably belong to some
  1512.  other program. That program will then either give incorrect results, damage
  1513.  still other memory locations, crash, or some combination of these. In
  1514.  summary, a memory addressing error is more dangerous because there is more
  1515.  in memory to damage and that damage will have a more severe effect.
  1516.       The second difficulty arises, not from explicit programming errors,
  1517.  but from conflicts in the normal operation of two or more co-resident
  1518.  programs that are in some fashion incompatible. A simple example is called
  1519.  "hooking the keyboard vector" (see Figure 3-1).
  1520.  
  1521.  
  1522.  BEFORE
  1523.                        Vector table                    Keyboard device
  1524.                       ┌────────────┐                   driver routine
  1525.    Device interrupt   │            │        ┌──────── x x x x: ──────
  1526.  ─────────────┐       ├────────────┤        │                   ──────
  1527.               └───── │  x x x x   ├────────┘                   ──────
  1528.                       ├────────────┤                            ──────
  1529.                       │            │                            ──────
  1530.                       │            │
  1531.                       │            │
  1532.                       └────────────┘
  1533.  
  1534.  AFTER                                 Application      Keyboard device
  1535.                        Vector table    edits table      driver
  1536.                       ┌────────────┐    │            ┌ x x x x: ──────
  1537.    Device interrupt   │            │    │            │           ──────
  1538.  ─────────────┐       ├────────────┤ ──┘            └─────────┐ ──────
  1539.               └───── │  y y y y   ├─┐   Application           │ ──────
  1540.                       ├────────────┤ │   routine               │ ──────
  1541.                       │            │ └─ y y y y: ──────       │
  1542.                       │            │              ──────       │
  1543.                       │            │              ──────       │
  1544.                       └────────────┘              ──────       │
  1545.                                                   ──────       │
  1546.                                                   jmp x x x x ─┘
  1547.  
  1548.  Figure 3-1.  Hooking the keyboard vector.
  1549.  
  1550.  
  1551.       In this case, an application modifies certain MS-DOS memory locations
  1552.  so that when a key is pressed the application code, instead of the MS-DOS
  1553.  code, is notified by the hardware. Applications do this because it allows
  1554.  them to examine certain keyboard events, such as pressing the shift key
  1555.  without pressing any other key, that MS-DOS does not pass on to
  1556.  applications which ask MS-DOS to read the keyboard for them. It works fine
  1557.  for one application to "hook" the keyboard vector; although hooking the
  1558.  keyboard vector modifies system memory locations that don't belong to the
  1559.  application, the application generally gets away with it successfully. In a
  1560.  multitasking environment, however, a second application may want to do the
  1561.  same trick, and the system probably won't function correctly. The result is
  1562.  that a stable environment requires memory protection. An application must
  1563.  not be allowed to modify, accidentally or deliberately, memory that isn't
  1564.  assigned to that application.
  1565.  
  1566.  
  1567.  3.2.2  Side-Effects Protection
  1568.  A stable environment requires more than memory protection; it also requires
  1569.  that the system be designed so that the execution of one application
  1570.  doesn't cause side effects for any other application. Side effects can be
  1571.  catastrophic or they can be unremarkable, but in all cases they violate the
  1572.  tenet of a stable environment.
  1573.       For example, consider the practice of hooking the keyboard interrupt
  1574.  vector. If one application uses this technique to intercept keystrokes, it
  1575.  will intercept all keystrokes, even those intended for some other
  1576.  application. The side effects in this case are catastrophic--the hooking
  1577.  application sees keystrokes that aren't intended for it, and the other
  1578.  applications don't get any keystrokes at all.
  1579.       Side effects can plague programs even when they are using official
  1580.  system features if those features are not carefully designed. For example,
  1581.  a mainframe operating system called TOPS-10 contains a program that
  1582.  supports command files similar to MS-DOS .BAT files, and it also contains a
  1583.  program that provides delayed offline execution of commands. Unfortunately,
  1584.  both programs use the same TOPS-10 facility to do their work. If you
  1585.  include a .BAT file in a delayed command list, the two programs will
  1586.  conflict, and the .BAT file will not execute.
  1587.       OS/2 deals with side effects by virtualizing to the greatest extent
  1588.  possible each application's operating environment. This means that OS/2
  1589.  tries to make each application "see" a standard environment that is
  1590.  unaffected by changes in another application's environment. The effect is
  1591.  like that of a building of identical apartments. When each tenant moves in,
  1592.  he or she gets a standard environment, a duplicate of all the apartments.
  1593.  Each tenant can customize his or her environment, but doing so doesn't
  1594.  affect the other tenants or their environments.
  1595.       Following are some examples of application environment issues that
  1596.  OS/2 virtualizes.
  1597.  
  1598.       ■  Working Directories. Each application has a working (or current)
  1599.          directory for each disk drive. Under MS-DOS version 3.x, if a child
  1600.          process changes the working directory for drive C and then exits,
  1601.          the working directory for drive C remains changed when the parent
  1602.          process regains control. OS/2 eliminates this side effect by
  1603.          maintaining a separate list of working directories for each process
  1604.          in the system. Thus, when an application changes its working
  1605.          directories, the working directories of other applications in the
  1606.          system remain unchanged.
  1607.  
  1608.       ■  Memory Utilization. The simple act of memory consumption produces
  1609.          side effects. If one process consumes all available RAM, none is
  1610.          left for the others. The OS/2 memory management system uses memory
  1611.          overcommit (swapping) so that the memory needs of each application
  1612.          can be met.
  1613.  
  1614.       ■  Priority. OS/2 uses a priority-based scheduler to assign the CPU to
  1615.          the processes that need it. Applications can adjust their priority
  1616.          and that of their child processes as they see fit. However, the
  1617.          very priority of a task causes side effects. Consider a process
  1618.          that tells OS/2 that it must run at a higher priority than any
  1619.          other task in the system. If a second process makes the same
  1620.          request, a conflict occurs: Both processes cannot be the highest
  1621.          priority in the system. In general, the priority that a process
  1622.          wants for itself depends on the priorities of the other processes
  1623.          in the system. The OS/2 scheduler contains a sophisticated
  1624.          absolute/relative mechanism to deal with these conflicts.
  1625.  
  1626.       ■  File Utilization. As discussed earlier, one application may modify
  1627.          the files that another application is using, causing an unintended
  1628.          side effect. The OS/2 file-locking mechanism prevents unintended
  1629.          modifications, and the OS/2 record-locking mechanism coordinates
  1630.          intentional parallel updates to a single file.
  1631.  
  1632.       ■  Environment Strings. OS/2 retains the MS-DOS concept of environment
  1633.          strings: Each process has its own set. A child process inherits a
  1634.          copy of the parent's environment strings, but changing the strings
  1635.          in this copy will not affect the original strings in the parent's
  1636.          environment.
  1637.  
  1638.       ■  Keyboard Mode. OS/2 applications can place the keyboard in one of
  1639.          two modes--cooked or raw. These modes tell OS/2 whether the
  1640.          application wants to handle, for example, the backspace character
  1641.          (raw mode) or whether it wants OS/2 to handle the backspace
  1642.          character for it (cooked mode). The effect of these calls on
  1643.          subsequent keyboard read operations would cause side effects for
  1644.          other applications reading from the keyboard, so OS/2 maintains a
  1645.          record of the cooked/raw status of each application and silently
  1646.          switches the mode of the keyboard when an application issues a
  1647.          keyboard read request.
  1648.  
  1649.  
  1650.  3.3  Localization of Errors
  1651.  
  1652.  A key element in creating a stable environment is localizing errors. Humans
  1653.  always make errors, and human creations such as computer programs always
  1654.  contain errors. Before the development of computers, routine human errors
  1655.  were usually limited in scope. Unfortunately, as the saying goes, a
  1656.  computer can make a mistake in 60 seconds that it would take a whole office
  1657.  force a year to make. Although OS/2 can do little to prevent such errors,
  1658.  it needs to do its best to localize the errors.
  1659.       Localizing errors consists of two activities: minimizing as much as
  1660.  possible the impact of the error on other applications in the system, and
  1661.  maximizing the opportunity for the user to understand which of the many
  1662.  programs running in the computer caused the error. These two activities are
  1663.  interrelated in that the more successful the operating system is in
  1664.  restricting the damage to the domain of a single program, the easier it is
  1665.  for the user to know which program is at fault.
  1666.       The most important aspect of error localization has already been
  1667.  discussed at length--memory management and protection. Other error
  1668.  localization principles include the following:
  1669.  
  1670.       ■  No program can crash or hang the system. A fundamental element of
  1671.          the OS/2 design religion is that no application program can,
  1672.          accidentally or even deliberately, crash or hang the system. If a
  1673.          failing application could crash the system, obviously the system
  1674.          did not localize the error! Furthermore, the user would be unable
  1675.          to identify the responsible application because the entire system
  1676.          would be dead.
  1677.  
  1678.       ■  No program can make inoperable any screen group other than its own.
  1679.          As we'll see in later chapters of this book, sometimes design
  1680.          goals, design religions, or both conflict. For example, the precept
  1681.          of no side effects conflicts with the requirement of supporting
  1682.          keyboard macro expander applications. The sole purpose of such an
  1683.          application is to cause a side effect--specifically to translate
  1684.          certain keystroke sequences into other sequences. OS/2 resolves
  1685.          this conflict by allowing applications to examine and modify the
  1686.          flow of data to and from devices (see Chapter 16) but in a
  1687.          controlled fashion. Thus, an aberrant keyboard macro application
  1688.          that starts to "eat" all keys, passing none through to the
  1689.          application, can make its current screen group unusable, but it
  1690.          can't affect the user's ability to change screen groups.
  1691.             Note that keyboard monitors can intercept and consume any
  1692.          character or character sequence except for the keystrokes that OS/2
  1693.          uses to switch screen groups (Ctrl-Esc and Alt-Esc). This is to
  1694.          prevent aberrant keyboard monitor applications from accidentally
  1695.          locking the user into his or her screen group by consuming and
  1696.          discarding the keyboard sequences that are used to switch from
  1697.          screen groups.
  1698.  
  1699.       ■  Applications cannot intercept general protection (GP) fault errors.
  1700.          A GP fault occurs when a program accesses invalid memory locations
  1701.          or accesses valid locations in an invalid way (such as writing into
  1702.          read-only memory areas). OS/2 always terminates the operation and
  1703.          displays a message for the user. A GP fault is evidence that the
  1704.          program's logic is incorrect, and therefore it cannot be expected
  1705.          to fix itself or trusted to notify the user of its ill health.
  1706.             The OS/2 design does allow almost any other error on the part of
  1707.          an application to be detected and handled by that application. For
  1708.          example, "Illegal filename" is an error caused by user input, not
  1709.          by the application. The application can deal with this error as it
  1710.          sees fit, perhaps correcting and retrying the operation. An error
  1711.          such as "Floppy disk drive not ready" is normally handled by OS/2
  1712.          but can be handled by the application. This is useful for
  1713.          applications that are designed to operate unattended; they need to
  1714.          handle errors themselves rather than waiting for action to be taken
  1715.          by a nonexistent user.
  1716.  
  1717.  
  1718.  3.4  Software Tools Approach
  1719.  
  1720.  In Chapter 2 we discussed IPC and the desirability of having separate
  1721.  functions contained in separate programs. We discussed the flexibility of
  1722.  such an approach over the "one man band" approach of an all-in-one
  1723.  application. We also touched on the value of being able to upgrade the
  1724.  functionality of the system incrementally by replacing individual programs.
  1725.  All these issues are software tools issues.
  1726.       Software tools refers to a design philosophy which says that
  1727.  individual programs and applications should be like tools: Each should do
  1728.  one job and do it very well. A person who wants to turn screws and also
  1729.  drive nails should get a screwdriver and a hammer rather than a single tool
  1730.  that does neither job as well.
  1731.       The tools approach is used routinely in nonsoftware environments and
  1732.  is taken for granted. For example, inside a standard PC the hardware and
  1733.  electronics are isolated into functional components that communicate via
  1734.  interfaces. The power supply is in a box by itself; its interface is the
  1735.  line cord and some power connectors. The disk drives are separate from the
  1736.  rest of the electronics; they interface via more connectors. Each component
  1737.  is the equivalent of a software application: It does one job and does it
  1738.  well. When the disk drive needs power, it doesn't build in a power supply;
  1739.  it uses the standard interface to the power supply module--the power
  1740.  "specialist" in the system.
  1741.       Occasionally, the software tools approach is criticized for being
  1742.  inefficient. People may argue that space is wasted and time is lost by
  1743.  packaging key functions separately; if they are combined, the argument
  1744.  goes, nothing is wasted. This argument is correct in that some RAM and CPU
  1745.  time is spent on interface issues, but it ignores the gains involved in
  1746.  "sending out the work" to a specialist rather than doing it oneself. One
  1747.  could argue, for example, that if I built an all-in-one PC system I'd save
  1748.  money because I wouldn't have to buy connectors to plug everything
  1749.  together. I might also save a little by not having to buy buffer chips to
  1750.  drive signals over those connectors. But in doing so, I'd lose the
  1751.  advantage of being able to buy my power supply from a very high-volume and
  1752.  high-efficiency supplier--someone who can make a better, cheaper supply,
  1753.  even with the cost of connectors, than my computer company can.
  1754.       Finally, the user gains from the modular approach. If you need more
  1755.  disk capability, you can buy one and plug it in. You are not limited to one
  1756.  disk maker but rather can choose the one that's right for your needs--
  1757.  expensive and powerful or cheap and modest. You can buy third-party
  1758.  hardware, such as plug-in cards, that the manufacturer of your computer
  1759.  doesn't make. All in all, the modest cost of a few connectors and driver
  1760.  chips is paid back manyfold, both in direct system costs (due to the
  1761.  efficiency of specialization) and in the additional capability and
  1762.  flexibility of the machine.
  1763.       As I said earlier, the software tools approach is the software
  1764.  equivalent of an open system. It's an important part of the OS/2 religion:
  1765.  Although the system doesn't require a modular tools approach from
  1766.  applications programs, it should do everything in its power to facilitate
  1767.  such systems, and it should itself be constructed in that fashion.
  1768.  
  1769.  
  1770.  
  1771.  ───────────────────────────────────────────────────────────────────────────
  1772.  
  1773.  
  1774.  
  1775.  Part II  The Architecture
  1776.  
  1777.  
  1778.  
  1779.  ───────────────────────────────────────────────────────────────────────────
  1780.  
  1781.  
  1782.  
  1783.  4  Multitasking
  1784.  
  1785.  ───────────────────────────────────────────────────────────────────────────
  1786.  
  1787.  I have discussed the goals and compatibility issues that OS/2 is intended
  1788.  to meet, and I have described the design religion that was established for
  1789.  OS/2. The following chapters discuss individual design elements in some
  1790.  detail, emphasizing not only how the elements work and are used but the
  1791.  role they play in the system as a whole.
  1792.       In a multitasking operating system, two or more programs can execute
  1793.  at the same time. Some benefits of such a feature are obvious: You (the
  1794.  user) can switch between several application programs without saving work
  1795.  and exiting one program to start another. When the telephone rings, for
  1796.  example, you can switch from the word processor application you are using
  1797.  to write a memo and go to the application that is managing your appointment
  1798.  calendar or to the spreadsheet application that contains the figures that
  1799.  are necessary to answer your caller's query.
  1800.       This type of multitasking is similar to what people do when they're
  1801.  not working with a computer. You may leave a report half read on your desk
  1802.  to address a more pressing need, such as answering the phone. Later,
  1803.  perhaps after other tasks intervene, you return to the report. You don't
  1804.  terminate a project and return your reference materials to the bookshelf,
  1805.  the files, and the library to answer the telephone; you merely switch your
  1806.  attention for a while and later pick up where you left off.
  1807.       This kind of multitasking is called serial multitasking because
  1808.  actions are performed one at a time. Although you probably haven't thought
  1809.  of it this way, you've spent much of your life serially multitasking. Every
  1810.  day when you leave for work, you suspend your home life and resume your
  1811.  work life. That evening, you reverse the process. You serially multitask a
  1812.  hobby--each time picking it up where you left off last time and then
  1813.  leaving off again. Reading the comics in a daily newspaper is a prodigious
  1814.  feat of human serial multitasking--you switch from one to another of
  1815.  perhaps 20 strips, remembering for each what has gone on before and then
  1816.  waiting until tomorrow for the next installment. Although serial
  1817.  multitasking is very useful, it is not nearly as useful as full
  1818.  multitasking--the kind of multitasking built into OS/2.
  1819.       Full multitasking on the computer involves doing more than one thing--
  1820.  running more than one application--at the same time. Humans do a little of
  1821.  this, but not too much. People commonly talk while they drive cars, eat
  1822.  while watching television, and walk while chewing gum. None of these
  1823.  activities requires one's full concentration though. Humans generally can't
  1824.  fully multitask activities that require a significant amount of
  1825.  concentration because they have only one brain.
  1826.       For that matter, a personal computer has only one "brain"--one CPU.1
  1827.  But OS/2 can switch this CPU from one activity to another very rapidly--
  1828.  dozens or even hundreds of times a second. All executing programs seem to
  1829.  be running at the same time, at least on the human scale of time. For
  1830.  example, if five programs are running and each in turn gets 0.01 second of
  1831.  CPU time (that is, 10 milliseconds), in 1 second each program receives 20
  1832.  time slices. To most observers, human or other computer software, all five
  1833.  programs appear to be running simultaneously but each at one-fifth its
  1834.  maximum speed. We'll return to the topic of time slicing later; for now,
  1835.  it's easiest--and, as we shall see, best--to pretend that all executing
  1836.  programs run simultaneously.
  1837.       The full multitasking capabilities of OS/2 allow the personal computer
  1838.  to act as more than a mere engine to run applications; the personal
  1839.  computer can now be a system of services. The user can interact with a
  1840.  spreadsheet program, for example, while a mail application is receiving
  1841.  network messages that the user can read later. At the same time, other
  1842.  programs may be downloading data from a mainframe computer or spooling
  1843.  output to a printer or a plotter. The user may have explicitly initiated
  1844.  some of these activities; a program may have initiated others. Regardless,
  1845.  they all execute simultaneously, and they all do their work without
  1846.  requiring the user's attention or intervention.
  1847.       Full multitasking is useful to programs themselves. Earlier, we
  1848.  discussed the advantages of a tools approach--writing programs so that
  1849.  they can offer their services to other programs. The numerous advantages
  1850.  of this technique are possible only because of full
  1851.  multitasking. For
  1852.  example, if a program is to be able to invoke another program to sort a
  1853.  data file, the sort program must execute at the same time as its client
  1854.  program. It wouldn't be very useful if the client program had to
  1855.  terminate in order  for the sort program to run.
  1856.       Finally, full multitasking is useful within a program itself. A thread
  1857.  is an OS/2 mechanism that allows more than one path of execution through a
  1858.  particular application. (Threads are discussed in detail later; for now it
  1859.  will suffice to imagine that several CPUs can be made to execute the same
  1860.  program simultaneously.) This allows individual applications to perform
  1861.  more than one task at a time. For example, if the user tells a spreadsheet
  1862.  program to recalculate a large budget analysis, the program can use one
  1863.  thread to do the calculating and another to prompt for, read, and obey the
  1864.  user's next command. In effect, multiple operations overlap during
  1865.  execution and thereby increase the program's responsiveness to the user.
  1866.       OS/2 uses a time-sliced, priority-based preemptive scheduler to
  1867.  provide full multitasking. In other words, the OS/2 scheduler preempts--
  1868.  takes away--the CPU from one application at any time the scheduler desires
  1869.  and assigns the CPU another application. Programs don't surrender the CPU
  1870.  when they feel like it; OS/2 preempts it. Each program in the system (more
  1871.  precisely, each thread in the system) has its own priority. When a thread
  1872.  of a higher priority than the one currently running wants to run, the
  1873.  scheduler preempts the running thread in favor of the higher priority one.
  1874.  If two or more runnable threads have the same highest priority, OS/2 runs
  1875.  each in turn for a fraction of a second--a time slice.
  1876.       The OS/2 scheduler does not periodically look around to see if the
  1877.  highest priority thread is running. Such an approach wastes CPU time and
  1878.  slows response time because a higher priority thread must wait to run until
  1879.  the next scheduler scan. Instead, other parts of the system call the
  1880.  scheduler when they think that a thread other than the one running should
  1881.  be executed.
  1882.  
  1883.  
  1884.  4.1  Subtask Model
  1885.  
  1886.  The terms task and process are used interchangeably to describe the direct
  1887.  result of executing a binary (.EXE) file. A process is the unit of
  1888.  ownership under OS/2, and processes own resources such as memory, open
  1889.  files, connections to dynlink libraries, and semaphores. Casual users would
  1890.  call a process a "program"; and, in fact, under MS-DOS all programs and
  1891.  applications consist of a single process. OS/2 uses the terms task or
  1892.  process because a single application program under OS/2 may consist of more
  1893.  than one process. This section describes how this is done.
  1894.       First, some more terminology. When a process creates, or execs,
  1895.  another process, the creator process is called the parent process, and the
  1896.  created process is called the child process. The parent of the parent is
  1897.  the child's grandparent and so on. As with people, each process in the
  1898.  system has or had a parent.2 Although we use genealogical terms to describe
  1899.  task relationships, a child task, or process, is more like an agent or
  1900.  employee of the parent task. Employees are hired to do work for an
  1901.  employer. The employer provides a workplace and access to the information
  1902.  employees need to do their jobs. The same is generally true for a child
  1903.  task. When a child task is created, it inherits (or receives a copy of) a
  1904.  great deal of the parent task's environment. For example, it inherits, or
  1905.  takes on, the parent's base scheduling priority and its screen group. The
  1906.  term inherit is a little inappropriate because the parent task has not
  1907.  died. It is alive and well, going about its business.
  1908.       The most important items a child task inherits are its parent's open
  1909.  file handles. OS/2 uses a handle mechanism to perform file I/O, as do MS-
  1910.  DOS versions 2.0 and later. When a file is opened, OS/2 returns a handle--
  1911.  an integer value--to the process. When a program wants to read from or
  1912.  write to a file, it gives OS/2 the file handle. Handles are not identical
  1913.  among processes. For example, the file referred to by handle 6 of one
  1914.  process bears no relationship to the file referred to by another process's
  1915.  handle 6, unless one of those processes is a child of the other. When a
  1916.  parent process creates a child process, the child process, by default,
  1917.  inherits each of the parent's open file handles. For example, a parent
  1918.  process has the file \WORK\TEMPFILE open on handle 5; when the child
  1919.  process starts up, handle 5 is open and references the \WORK\TEMPFILE file.
  1920.       This undoubtedly seems brain damaged if you are unfamiliar with this
  1921.  model. Why is it done in this crazy way? What use does the child process
  1922.  have for these open files? What's to keep the child from mucking up the
  1923.  parent's files? All this becomes clearer when the other piece of the puzzle
  1924.  is in place--the standard file handles.
  1925.  
  1926.  
  1927.  4.1.1  Standard File Handles
  1928.  Many OS/2 functions use 16-bit integer values called handles for their
  1929.  interfaces. A handle is an object that programmers call a "magic cookie"--
  1930.  an arbitrary value that OS/2 provides the application so that the
  1931.  application can pass the value back to OS/2 on subsequent calls. Its
  1932.  purpose is to simplify the OS/2 interface and speed up the particular
  1933.  service. For example, when a program creates a system semaphore, it is
  1934.  returned a semaphore handle--a magic cookie--that it uses for subsequent
  1935.  request and release operations. Referring to the semaphore via a 16-bit
  1936.  value is much faster than passing around a long filename. Furthermore, the
  1937.  magic in magic cookie is that the meaning of the 16-bit handle value is
  1938.  indecipherable to the application. OS/2 created the value, and it has
  1939.  meaning only to OS/2; the application need only retain the value and
  1940.  regurgitate it when appropriate. An application can never make any
  1941.  assumptions about the values of a magic cookie.
  1942.       File handles are an exceptional form of handle because they are not
  1943.  magic cookies. The handle value, in the right circumstances, is meaningful
  1944.  to the application and to the system as a whole. Specifically, three handle
  1945.  values have special meaning: handle value 0, called STDIN (for standard
  1946.  input); handle value 1, called STDOUT (standard output); and handle value
  1947.  2, called STDERR (standard error). A simple program--let's call it NUMADD--
  1948.  will help to explain the use of these three handles. NUMADD will read two
  1949.  lines of ASCII text (each containing a decimal number), convert the numbers
  1950.  to binary, add them, and then convert the results to an ASCII string and
  1951.  write out the result. Note that we're confining our attention to a simple
  1952.  non-screen-oriented program that might be used as a tool, either directly
  1953.  by a programmer or by another program (see Figure 4-1 and Listing 4-1).
  1954.  
  1955.  
  1956.        STDIN ┌──────────┐
  1957.  123 ───┐    │          │ STDOUT
  1958.  14     └───│  NUMADD  ├───┐
  1959.              │          │   └──── 137
  1960.              └──────────┘
  1961.  
  1962.  Figure 4-1.  Program NUMADD operation--interactive.
  1963.  
  1964.  
  1965.  #include <stdio.h>            /* defines stdin and stdout */
  1966.  
  1967.  main()
  1968.  {
  1969.       int value1, value2, sum;
  1970.  
  1971.       fscanf (stdin, "%d", &value1);
  1972.       fscanf (stdin, "%d", & value2);
  1973.       sum = value1 + value2;
  1974.       fprintf (stdout, "%d\n", sum);
  1975.  }
  1976.  
  1977.  
  1978.  Listing 4-1.  Program NUMADD.
  1979.  
  1980.  
  1981.       By convention, all OS/2 programs read input from STDIN and write
  1982.  output to STDOUT. Any error messages are written to STDERR. The program
  1983.  itself does not open these handles; it inherits them from the parent
  1984.  process. The parent may have opened them itself or inherited them from its
  1985.  own parent. As you can see, NUMADD would not contain DosOpen calls;
  1986.  instead, it would start immediately issuing fscanf calls on handle 0
  1987.  (STDIN), which in turn issues DosRead calls, and, when ready, directly
  1988.  issue fprintf calls to handle 1 (STDOUT), which in turn issues DosWrites.
  1989.       Figure 4-2 and Listing 4-2 show a hypothetical application, NUMARITH.
  1990.  NUMARITH reads three text lines. The first line contains an operation
  1991.  character, such as a plus (+) or a minus (-); the second and third lines
  1992.  contain the values to be operated upon. The author of this program doesn't
  1993.  want to reinvent the wheel; so when the program NUMARITH encounters a +
  1994.  operation, it executes NUMADD to do the work. As shown, the parent process
  1995.  NUMARITH has its STDIN connected to the keyboard and its STDOUT connected
  1996.  to the screen device drivers.3 When NUMADD executes, it reads input from
  1997.  the keyboard via STDIN. After the user types the two numbers, NUMADD
  1998.  displays the result on the screen via STDOUT. NUMARITH has invoked NUMADD
  1999.  to do some work for it, and NUMADD has silently and seamlessly acted as a
  2000.  part of NUMARITH. The employee metaphor fits well here. NUMADD acted as an
  2001.  employee of NUMARITH, making use of NUMARITH's I/O streams, and as a result
  2002.  the contribution of the NUMADD employee to the NUMARITH company is seamless.
  2003.  
  2004.  
  2005.  keyboard                                   screen
  2006.      │     STDIN    ┌──────────┐   STDOUT     │
  2007.      └───────┐      │          │     ┌────────┘
  2008.              ├─────│ NUMARITH ├────┤
  2009.                    │          │     
  2010.              │      └─────┬────┘     │
  2011.              │            │ DosExec  │
  2012.     inherits │      ┌─────────┐     │ inherits
  2013.              │      │          │     │
  2014.              └─────│  NUMADD  ├────┘
  2015.                     │          │
  2016.                     └──────────┘
  2017.  
  2018.  Figure 4-2.  Program NUMARITH operation--interactive.
  2019.  
  2020.  
  2021.  #include <stdio.h>
  2022.  
  2023.  /**     Numarith - Perform ASCII Arithmetic
  2024.  *
  2025.  *       Numarith reads line triplets:
  2026.  *
  2027.  *               operation
  2028.  *               value1
  2029.  *               value2
  2030.  *
  2031.  *       performs the specified operation (+, -, *, /) on
  2032.  *       the two values and prints the result on stdout.
  2033.  */
  2034.  
  2035.  main()
  2036.  {
  2037.          char operation;
  2038.  
  2039.          fscanf (stdin, "%c", &operation);
  2040.  
  2041.          switch (operation) {
  2042.  
  2043.              case '+':  execl ("numadd",  0);
  2044.                          break;
  2045.  
  2046.              case '-':  execl ("numsub", 0);
  2047.                          break;
  2048.  
  2049.              case '*':  execl ("nummul", 0);
  2050.                          break;
  2051.  
  2052.              case '/':  execl ("numdiv", 0);
  2053.                          break;
  2054.              }
  2055.  }
  2056.  
  2057.  
  2058.  Listing 4-2.  Program NUMARITH.
  2059.  
  2060.  
  2061.       Figure 4-3 shows a similar situation. In this case, however,
  2062.  NUMARITH's STDIN and STDOUT handles are open on two files, which we'll call
  2063.  DATAIN and DATAOUT. Once again, NUMADD does its work seamlessly. The input
  2064.  numbers are read from the command file on STDIN, and the output is properly
  2065.  intermingled in the log file on STDOUT. The key here is that this NUMADD is
  2066.  exactly the same program that ran in Listing 4-2; NUMADD contains no
  2067.  special code to deal with this changed situation. In both examples, NUMADD
  2068.  simply reads from STDIN and writes to STDOUT; NUMADD neither knows nor
  2069.  cares where those handles point. Exactly the same is true for the parent.
  2070.  NUMARITH doesn't know and doesn't care that it's working from files instead
  2071.  of from the screen; it simply uses the STDIN and STDOUT handles that it
  2072.  inherited from its parent.
  2073.  
  2074.  
  2075.  File data in                                     File data out
  2076.       ___                                              ___
  2077.     /     \                                          /     \
  2078.    │\ ___ /│                                        │\ ___ /│
  2079.    │       │                                        │       │
  2080.    │       ├──┐                                  ┌─│       │
  2081.    │       │  │  STDIN    ┌──────────┐   STDOUT  │  │       │
  2082.     \ ___ /   └────┐      │          │     ┌─────┘   \ ___ /
  2083.                    ├─────│ NUMARITH ├────┤
  2084.                          │          │     
  2085.                    │      └─────┬────┘     │
  2086.                    │            │ DosExec  │
  2087.           inherits │      ┌─────────┐     │ inherits
  2088.                    │      │          │     │
  2089.                    └─────│  NUMADD  ├────┘
  2090.                           │          │
  2091.                           └──────────┘
  2092.  
  2093.  Figure 4-3.  Program NUMARITH operation--from files.
  2094.  
  2095.  
  2096.       This is the single most important concept in the relationship and
  2097.  inheritance structure between processes. The reason a process inherits so
  2098.  much from its parent is so that the parent can set up the tool's
  2099.  environment--make it read from the parent's STDIN or from a file or from an
  2100.  anonymous pipe (see 4.1.2 Anonymous Pipes). This gives the parent the
  2101.  flexibility to use a tool program as it wishes, and it frees the tool's
  2102.  author from the need to be "all things to all people."
  2103.       Of equal importance, the inheritance architecture provides
  2104.  nesting encapsulation of child processes. NUMARITH's parent process doesn't
  2105.  know and doesn't need to know how NUMARITH does its job. NUMARITH can do
  2106.  the additions itself, or it can invoke NUMADD as a child, but the
  2107.  architecture encapsulates the details of NUMARITH's operation so that
  2108.  NUMADD's involvement is hidden from NUMARITH's parent. Likewise, the
  2109.  decision of NUMARITH's parent to work from a file or from a device or from
  2110.  a pipe is encapsulated (that is, hidden) from NUMARITH and from any child
  2111.  processes that NUMARITH may execute to help with its work. Obviously, this
  2112.  architecture can be extended arbitrarily: NUMADD can itself execute a child
  2113.  process to help NUMADD with its work, and this would silently and invisibly
  2114.  work. Neither NUMARITH nor its parent would know or need to know anything
  2115.  about how NUMADD was doing its work. Other versions can replace any of
  2116.  these applications at any time. The new versions can invoke more or fewer
  2117.  child processes or be changed in any other way, and their client (that is,
  2118.  parent) processes are unaffected. The architecture of OS/2 is tool-based;
  2119.  as long as the function of a tool remains constant (or is supersetted), its
  2120.  implementation is irrelevant and can be changed arbitrarily.
  2121.       The STDIN, STDOUT, and STDERR architecture applies to all programs,
  2122.  even those that only use VIO, KBD, or the presentation manager and that
  2123.  never issue operations on these handles. See Chapter 14, Interactive
  2124.  Programs.
  2125.  
  2126.  
  2127.  4.1.2  Anonymous Pipes
  2128.  NUMADD and NUMARITH are pretty silly little programs; a moment's
  2129.  consideration will show how the inheritance architecture applies to more
  2130.  realistic programs. An example is the TREE program that runs when the TREE
  2131.  command is given to CMD.EXE. TREE inherits the STDIN and STDOUT handles,
  2132.  but it does not use STDIN; it merely writes output to STDOUT. As a result,
  2133.  when the user types TREE at a CMD.EXE prompt, the output appears on the
  2134.  screen. When TREE appears in a batch file, the output appears in the LOG
  2135.  file or on the screen, depending on where STDOUT is pointing.
  2136.       This is all very useful, but what if an application wants to further
  2137.  process the output of the child program rather than having the child's
  2138.  output intermingled with the application's output? OS/2 does this with
  2139.  anonymous pipes. The adjective anonymous distinguishes these pipes from a
  2140.  related facility, named pipes, which are not implemented in OS/2 version
  2141.  1.0.
  2142.       An anonymous pipe is a data storage buffer that OS/2 maintains. When a
  2143.  process opens an anonymous pipe, it receives two file handles--one for
  2144.  writing and one for reading. Data can be written to the write handle via
  2145.  the DosWrite call and then read back via the read handle and the DosRead
  2146.  call. An anonymous pipe is similar to a file in that it is written and read
  2147.  via file handle operations, but an anonymous pipe and a file are
  2148.  significantly different. Pipe data is stored only in RAM buffers, not on a
  2149.  disk, and is accessed only in FIFO (First In First Out) fashion. The
  2150.  DosChgFilePtr operation is illegal on pipe handles.
  2151.       An anonymous pipe is of little value to a single process, since it
  2152.  acts as a simple FIFO (First In First Out) storage buffer of limited size
  2153.  and since the data has to be copied to and from OS/2's pipe buffers when
  2154.  DosWrites and DosReads are done. What makes an anonymous pipe valuable is
  2155.  that child processes inherit file handles. A parent process can create an
  2156.  anonymous pipe and then create a child process, and the child process
  2157.  inherits the anonymous pipe handles. The child process can then write to
  2158.  the pipe's write handle, and the parent process can read the data via the
  2159.  pipe's read handle. Once we add the DosDupHandle function, which allows
  2160.  handles to be renumbered, and the standard file handles (STDIN, STDOUT, and
  2161.  STDERR), we have the makings of a powerful capability.
  2162.       Let's go back to our NUMARITH and NUMADD programs. Suppose NUMARITH
  2163.  wants to use NUMADD's services but that NUMARITH wants to process NUMADD's
  2164.  results itself rather than having them appear in NUMARITH's output.
  2165.  Furthermore, assume that NUMARITH doesn't want NUMADD to read its arguments
  2166.  from NUMARITH's input file; NUMARITH wants to supply NUMADD's arguments
  2167.  itself. NUMARITH can do this by following these steps:
  2168.  
  2169.       1.  Create two anonymous pipes.
  2170.  
  2171.       2.  Preserve the item pointed to by the current STDIN and STDOUT
  2172.           handles (the item can be a file, a device, or a pipe) by using
  2173.           DosDupHandle to provide a duplicate handle. The handle numbers of
  2174.           the duplicates may be any number as long as it is not the number
  2175.           of STDIN, STDOUT, or STDERR. We know that this is the case because
  2176.           DosDupHandle assigns a handle number that is not in use, and the
  2177.           standard handle numbers are always in use.
  2178.  
  2179.       3.  Close STDIN and STDOUT via DosClose. Whatever object is "on the
  2180.           other end" of the handle is undisturbed because the application
  2181.           still has the object open on another handle.
  2182.  
  2183.       4.  Use DosDupHandle to make the STDIN handle a duplicate of one of
  2184.           the pipe's input handles, and use DosDupHandle to make the STDOUT
  2185.           handle a duplicate of the other pipe's output handle.
  2186.  
  2187.       5.  Create the child process via DosExecPgm.
  2188.  
  2189.       6.  Close the STDIN and STDOUT handles that point to the pipes, and
  2190.           use DosDupHandle and DosClose to effectively rename the objects
  2191.           originally described by STDIN and STDOUT back to those handles.
  2192.  
  2193.       The result of this operation is shown in Figure 4-4. NUMADD's STDIN
  2194.  and STDOUT handles are pointing to two anonymous pipes, and the parent
  2195.  process is holding the other end of those pipes. The parent process used
  2196.  DosDupHandle and DosClose to effectively "rename" the STDIN and STDOUT
  2197.  handles temporarily so that the child process can inherit the pipe handles
  2198.  rather than its parent's STDIN and STDOUT. At this point the parent,
  2199.  NUMARITH, can write input values into the pipe connected to NUMADD's STDIN
  2200.  and read NUMADD's output from the pipe connected to NUMADD's STDOUT.
  2201.  
  2202.  
  2203.          STDIN    ┌──────────┐   STDOUT
  2204.   ─────────┐      │          │     ┌─────────
  2205.            └─────│ NUMARITH ├────┘
  2206.            ┌──────┤          │───────┐
  2207.            │      └─────┬────┘        │
  2208.  anonymous │            │ DosExecPgm  │ anonymous
  2209.    pipe    │      ┌─────────┐        │   pipe
  2210.            │      │          │        │
  2211.            └─────│  NUMADD  ├────────┘
  2212.                   │          │
  2213.                   └──────────┘
  2214.  
  2215.  Figure 4-4.  Invoking NUMADD via an anonymous pipe.
  2216.  
  2217.  
  2218.       If you compare Figure 4-4 with Listing 4-2, Figure 4-2, and Figure
  2219.  4-3, you see another key feature of this architecture: NUMADD has no
  2220.  special code or design to allow it to communicate directly with NUMARITH.
  2221.  NUMADD functions correctly whether working from the keyboard and screen,
  2222.  from data files, or as a tool for another program. The architecture is
  2223.  fully recursive: If NUMADD invokes a child process to help it with its
  2224.  work, everything still functions correctly. Whatever mechanism NUMADD uses
  2225.  to interact with its child/tool program is invisible to NUMARITH. Likewise,
  2226.  if another program uses NUMARITH as a tool, that program is not affected by
  2227.  whatever mechanism NUMARITH uses to do its work.
  2228.       This example contains one more important point. Earlier I said that a
  2229.  process uses DosRead and DosWrite to do I/O over a pipe, yet our NUMADD
  2230.  program uses fscanf and fprintf, two C language library routines. fscanf
  2231.  and fprintf themselves call DosRead and DosWrite, and because a pipe handle
  2232.  is indistinguishable from a file handle or a device handle for these
  2233.  operations, not only does NUMADD work unchanged with pipes, but the library
  2234.  subroutines that it calls work as well. This is another example of the
  2235.  principle of encapsulation as expressed in OS/2,4 in which the differences
  2236.  among pipes, files, and devices are hidden behind, or encapsulated in, a
  2237.  standardized handle interface.
  2238.  
  2239.  
  2240.  4.1.3  Details, Details
  2241.  While presenting the "big picture" of the OS/2 tasking and tool
  2242.  architecture, I omitted various important details. This section discusses
  2243.  them, in no particular order.
  2244.       STDERR (handle value 2) is an output handle on which error messages
  2245.  are written. STDERR is necessary because STDOUT is a program's normal
  2246.  output stream. For example, suppose a user types:
  2247.  
  2248.       DIR  filename >logfile
  2249.  
  2250.  where the file filename does not exist. If STDERR did not exist as a
  2251.  separate entity, no error message would appear, and logfile would
  2252.  apparently be created. Later, when the user attempted to examine the
  2253.  contents of the file, he or she would see the following message:
  2254.  
  2255.       FILE NOT FOUND
  2256.  
  2257.  For this reason, STDERR generally points to the display screen, and
  2258.  applications rarely redirect it.
  2259.       The special meanings of STDIN, STDOUT, and STDERR are not hard coded
  2260.  into the OS/2 kernel; they are system conventions. All programs should
  2261.  follow these conventions at all times to preserve the flexibility and
  2262.  utility of OS/2's tool-based architecture. Even programs that don't do
  2263.  handle I/O on the STD handles must still follow the architectural
  2264.  conventions (see Chapter 14, Interactive Programs). However, the OS/2
  2265.  kernel code takes no special action nor does it contain any special cases
  2266.  in support of this convention. Various OS/2 system utilities, such as
  2267.  CMD.EXE and the presentation manager, do contain code in support of this
  2268.  convention.
  2269.       I mentioned that a child process inherits all its parent's file
  2270.  handles unless the parent has explicitly marked a handle "no inherit." The
  2271.  use of STDIN, STDOUT, and STDERR in an inherited environment has been
  2272.  discussed, but what of the other handles?
  2273.       Although a child process inherits all of its parent's file handles, it
  2274.  is usually interested only in STDIN, STDOUT, and STDERR. What happens to
  2275.  the other handles? Generally, nothing. Only handle values 0 (STDIN), 1
  2276.  (STDOUT), and 2 (STDERR) have explicit meaning. All other file handles are
  2277.  merely magic cookies that OS/2 returns for use in subsequent I/O calls.
  2278.  OS/2 doesn't guarantee any particular range or sequence of handle values,
  2279.  and applications should never use or rely on explicit handle values other
  2280.  than the STD ones.
  2281.       Thus, for example, if a parent process has a file open on handle N and
  2282.  the child process inherits that handle, little happens. The child process
  2283.  won't get the value N back as a result of a DosOpen because the handle is
  2284.  already in use. The child will never issue operations to handle N because
  2285.  it didn't open any such handle and knows nothing of its existence. Two side
  2286.  effects can result from inheriting "garbage" handles. One is that the
  2287.  object to which the handle points cannot be closed until both the parent
  2288.  and the child close their handles. Because the child knows nothing of the
  2289.  handle, it won't close it. Therefore, a handle close issued by a parent
  2290.  won't be effective until the child and all of that child's descendant
  2291.  processes (which in turn inherited the handle) have terminated. If another
  2292.  application needs the file or device, it is unavailable because a child
  2293.  process is unwittingly holding it open.
  2294.       The other side effect is that each garbage handle consumes an entry in
  2295.  the child's handle space. Although you can easily increase the default
  2296.  maximum of 20 open handles, a child process that intends to open only 10
  2297.  files wouldn't request such an increase. If a parent process allows the
  2298.  child to inherit 12 open files, the child will run out of available open
  2299.  file handles. Writing programs that always raise their file handle limit is
  2300.  not good practice because the garbage handles are extra overhead and the
  2301.  files-held-open problem remains. Instead, parent programs should minimize
  2302.  the number of garbage handles they allow child processes to inherit.
  2303.       Each time a program opens a file, it should do so with the DosOpen
  2304.  request with the "don't inherit" bit set if the file is of no specific
  2305.  interest to any child programs. If the bit is not set at open time, it can
  2306.  be set later via DosSetFHandState. The bit is per-handle, not per-file; so
  2307.  if a process has two handles open on the same file, it can allow one but
  2308.  not the other to be inherited. Don't omit this step simply because you
  2309.  don't plan to run any child processes; unbeknownst to you, dynlink library
  2310.  routines may run child programs on your behalf. Likewise, in dynlink
  2311.  programs the "no inherit" bit should be set when file opens are issued
  2312.  because the client program may create child processes.
  2313.       Finally, do not follow the standard UNIX practice of blindly closing
  2314.  file handles 3 through 20 during program initialization. Dynlink subsystems
  2315.  are called to initialize themselves for a new client before control is
  2316.  passed to the application itself. If subsystems have opened files during
  2317.  that initialization, a blanket close operation will close those files and
  2318.  cause the dynlink package to fail. All programs use dynlink subsystems,
  2319.  whether they realize it or not, because both the OS/2 interface package and
  2320.  the presentation manager are such subsystems. Accidentally closing a
  2321.  subsystem's file handles can cause bizarre and inexplicable problems. For
  2322.  example, when the VIO subsystem is initialized, it opens a handle to the
  2323.  screen device. A program that doesn't call VIO may believe that closing
  2324.  this handle is safe, but it's not. If a handle write is done to STDOUT when
  2325.  STDOUT points to the screen device, OS/2 calls VIO on behalf of the
  2326.  application--with potentially disastrous effect.
  2327.       I discussed how a child process, inheriting its parent's STDIN and
  2328.  STDOUT, extracts its input from the parent's input stream and intermingles
  2329.  its output in the parent's output stream. What keeps the parent process
  2330.  from rereading the input consumed by the child, and what keeps the parent
  2331.  from overwriting the output data written by the child? The answer is in the
  2332.  distinction between duplicated or inherited handles to a file and two
  2333.  handles to the same file that are the result of two separate opens.
  2334.       Each time a file is opened, OS/2 allocates a handle to that process
  2335.  and makes an entry in the process's handle table. This entry then points to
  2336.  the System File Table (SFT) inside OS/2. The SFT contains the seek pointer
  2337.  to the file--the spot in the file that is currently being read from or
  2338.  written to. When a handle is inherited or duplicated, the new handle points
  2339.  to the same SFT entry as the original. Thus, for example, the child's STDIN
  2340.  handle shares the same seek pointer as the parent's STDIN handle. When our
  2341.  example child program NUMADD read two lines from STDIN, it advanced the
  2342.  seek pointer of its own STDIN and that of its parent's STDIN (and perhaps
  2343.  that of its grandparent's STDIN and so forth). Likewise, when the child
  2344.  writes to STDOUT, the seek pointer advances on STDOUT so that subsequent
  2345.  writing by the parent appends to the child's output rather than overwriting
  2346.  it.
  2347.       This mechanism has two important ramifications. First, in a situation
  2348.  such as our NUMARITH and NUMADD example, the parent process must refrain
  2349.  from I/O to the STD handles until the child process has completed so that
  2350.  the input or output data doesn't intermingle. Second, the processes must be
  2351.  careful in the way they buffer data to and from the STD handles.
  2352.       Most programs that read data from STDIN do so until they encounter an
  2353.  EOF (End Of File). These programs can buffer STDIN as they wish. A program
  2354.  such as NUMARITH, in which child processes read some but not all of its
  2355.  STDIN data, cannot use buffering because the read-ahead data in the buffer
  2356.  might be the data that the child process was to read. NUMARITH can't "put
  2357.  the data back" by backing up the STDIN seek pointer because STDIN might be
  2358.  pointing to a device (such as the keyboard) or to a pipe that cannot be
  2359.  seeked. Likewise, because NUMADD was designed to read only two lines
  2360.  of input, it also must read STDIN a character at a time to be sure it
  2361.  doesn't "overshoot" its two lines.
  2362.       Programs must also be careful about buffering STDOUT. In general, they
  2363.  can buffer STDOUT as they wish, but they must be sure to flush out any
  2364.  buffered data before they execute any child processes that might write to
  2365.  STDOUT.
  2366.       Finally, what if a parent process doesn't want a child process to
  2367.  inherit STDIN, STDOUT, or STDERR? The parent process should not mark those
  2368.  handles "no inherit" because then those handles will not be open when the
  2369.  child process starts. The OS/2 kernel has no built-in recognition of the
  2370.  STD file handles; so if the child process does a DosOpen and handle 1 is
  2371.  unopened (because the process's parent set "no inherit" on handle 1), OS/2
  2372.  might open the new file on handle 1. As a result, output that the child
  2373.  process intends for STDOUT appears in the other file that unluckily was
  2374.  assigned handle number 1.
  2375.       If for some reason a child process should not inherit a STD handle,
  2376.  the parent should use the DosDupHandle/rename technique to cause that
  2377.  handle to point to the NULL device. You do this by opening a handle on the
  2378.  NULL device and then moving that handle to 0, 1, or 2 with DosDupHandle.
  2379.  This technique guarantees that the child's STD handles will all be
  2380.  open.
  2381.       The subject of STDIN, STDOUT, and handle inheritance comes up again in
  2382.  Chapter 14, Interactive Programs.
  2383.  
  2384.  
  2385.  4.2  PIDs and Command Subtrees
  2386.  
  2387.  The PID (process identification) is a unique code that OS/2 assigns to each
  2388.  process when it is created. The number is a magic cookie. Its value has no
  2389.  significance to any process; it's simply a name for a process. The PID may
  2390.  be any value except 0. A single PID value is never assigned to two
  2391.  processes at the same time. PID values are reused but not "rapidly." You
  2392.  can safely remember a child's PID and then later attempt to affect that
  2393.  child by using the PID in an OS/2 call. Even if the child process has died
  2394.  unexpectedly, approximately 65,000 processes would have to be created
  2395.  before the PID value might be reassigned; even a very active system takes
  2396.  at least a day to create, execute, and terminate that many processes.
  2397.       I've discussed at some length the utility of an architecture in which
  2398.  child programs can create children and those children can create
  2399.  grandchildren and so on. I've also emphasized that the parent need not know
  2400.  the architecture of a child process--whether the child process creates
  2401.  children and grandchildren of its own. The parent need not know and indeed
  2402.  should not know because such information may make the parent dependent on a
  2403.  particular implementation of a child or grandchild program, an
  2404.  implementation that might change. Given that a parent process starting up a
  2405.  child process can't tell if that child creates its own descendants, how can
  2406.  the parent process ask the system if the work that the child was to do has
  2407.  been completed? The system could tell the parent whether the child process
  2408.  is still alive, but this is insufficient. The child process may have farmed
  2409.  out all its work to one or more grandchildren and then terminated itself
  2410.  before the actual work was started. Furthermore, the parent process may
  2411.  want to change the priority of the process(es) that it has created or even
  2412.  terminate them because an error was detected or because the user typed
  2413.  Ctrl-C.
  2414.       All these needs are met with a concept called the command subtree.
  2415.  When a child process is created, its parent is told the child's PID. The
  2416.  PID is the name of the single child process, and when taken as a command
  2417.  subtree ID, this PID is the name of the entire tree of descendant processes
  2418.  of which the child is the root. In other words, when used as a command
  2419.  subtree ID, the PID refers not only to the child process but also to any of
  2420.  its children and to any children that they may have and so on. A single
  2421.  command subtree can conceivably contain dozens of processes (see Figure
  2422.  4-5 and Figure 4-6).
  2423.  
  2424.  
  2425.                  ┌───────────┐
  2426.                  │  PARENT   │
  2427.                  └─────┬─────┘
  2428.                  ┌─────┴─────┐
  2429.                  │     A     │
  2430.                  └──┬─────┬──┘
  2431.        ┌────────────┘     └────────────┐
  2432.  ┌─────┴─────┐                   ┌─────┴─────┐
  2433.  │     B     │                   │     C     │
  2434.  └─────┬─────┘                   └──┬─────┬──┘
  2435.  ┌─────┴─────┐           ┌──────────┘     └──────────┐
  2436.  │     D     │     ┌─────┴─────┐               ┌─────┴─────┐
  2437.  └─────┬─────┘     │     F     │               │     G     │
  2438.  ┌─────┴─────┐     └───────────┘               └──┬─────┬──┘
  2439.  │     E     │                              ┌─────┘     └─────┐
  2440.  └───────────┘                        ┌─────┴─────┐     ┌─────┴─────┐
  2441.                                       │     H     │     │     I     │
  2442.                                       └─────┬─────┘     └───────────┘
  2443.                                       ┌─────┴─────┐
  2444.                                       │     J     │
  2445.                                       └───────────┘
  2446.  
  2447.  Figure 4-5.  Command subtree. A is the root of (one of) the parent's
  2448.  command subtrees. B and C are the root of two of A's subtrees and so on.
  2449.  
  2450.  
  2451.                  ┌───────────┐
  2452.                  │  PARENT   │
  2453.                  └─────┬─────┘
  2454.                  ┌─────┴─────┐
  2455.                  │░░░░░A░░░░░│
  2456.                  └──┬─────┬──┘
  2457.        ┌────────────┘     └────────────┐
  2458.  ┌─────┴─────┐                   ┌─────┴─────┐
  2459.  │     B     │                   │     C     │
  2460.  └─────┬─────┘                   └──┬─────┬──┘
  2461.  ┌─────┴─────┐           ┌──────────┘     └──────────┐
  2462.  │     D     │     ┌─────┴─────┐               ┌─────┴─────┐
  2463.  └─────┬─────┘     │     F     │               │     G     │
  2464.  ┌─────┴─────┐     └───────────┘               └──┬─────┬──┘
  2465.  │░░░░░E░░░░░│                              ┌─────┘     └─────┐
  2466.  └───────────┘                        ┌─────┴─────┐     ┌─────┴─────┐
  2467.                                       │░░░░░H░░░░░│     │     I     │
  2468.                                       └─────┬─────┘     └───────────┘
  2469.                                       ┌─────┴─────┐
  2470.                                       │     J     │
  2471.                                       └───────────┘
  2472.  
  2473.  Figure 4-6.   Command subtree. The shaded processes have died, but the
  2474.  subtrees remain. PARENT can still use subtree A to affect all remaining
  2475.  subprocesses. Likewise, an operation by C on subtree G will affect
  2476.  process J.
  2477.  
  2478.  
  2479.       Some OS/2 functions, such as DosCWait and DosKillProcess, can take
  2480.  either PID or command subtree values, depending on the subfunction
  2481.  requested. When the PID form is used, only the named process is affected.
  2482.  When the command subtree form is used, the named process and all its
  2483.  descendants are affected. This is true even if the child process no longer
  2484.  exists or if the family tree of processes contains holes as a result of
  2485.  process terminations.
  2486.       No statute of limitations applies to the use of the command subtree
  2487.  form. That is, even if child process X died a long time ago, OS/2 still
  2488.  allows references to the command subtree X. Consequently, OS/2 places one
  2489.  simple restriction on the use of command subtrees so that it isn't forced
  2490.  to keep around a complete process history forever: Only the direct parent
  2491.  of process X can reference the command subtree X. In other words, X's
  2492.  grandparent process can't learn X's PID from its parent and then issue
  2493.  command subtree forms of commands; only X's direct parent can. This
  2494.  puts an upper limit on the amount and duration of command subtree
  2495.  information that OS/2 must retain; when a process terminates, information
  2496.  pertaining to its command subtrees can be discarded. The command subtree
  2497.  concept is recursive. OS/2 discards information about the terminated
  2498.  process's own command subtrees, but if any of its descendant processes
  2499.  still exist, the command subtree information pertaining to their child
  2500.  processes is retained. And those surviving descendants are still part of
  2501.  the command subtree belonging to the terminated process's parent
  2502.  process.5
  2503.  
  2504.  
  2505.  4.3  DosExecPgm
  2506.  
  2507.  To execute a child process, you use the DosExecPgm call. The form of the
  2508.  call is shown in Listing 4-3.
  2509.  
  2510.  
  2511.  extern unsigned far pascal DOSEXECPGM (
  2512.            char far                 *OBJNAMEBUF,
  2513.            unsigned                 OBJNAMEBUFL,
  2514.            unsigned                 EXECTYPE,
  2515.            char far                 *ARGSTRING,
  2516.            char far                 *ENVSTRING,
  2517.            struct ResultCodes far   *CODEPID,
  2518.            char far                 *PGMNAME);
  2519.  
  2520.  
  2521.  Listing 4-3.  DosExecPgm call.
  2522.  
  2523.  
  2524.       The obj namebuf arguments provide an area where OS/2 can return a
  2525.  character string if the DosExecPgm function fails. In MS-DOS version 2.0
  2526.  and later, the EXEC function was quite simple: It loaded a file into
  2527.  memory. Little could go wrong: file not found, file bad format,
  2528.  insufficient memory, to name some possibilities. A simple error code
  2529.  sufficed to diagnose problems. OS/2's dynamic linking facility is much more
  2530.  complicated. The earliest prototype versions of OS/2 were missing these
  2531.  obj namebuf arguments, and engineers were quickly frustrated by the error
  2532.  code "dynlink library load failed." "Which library was it? The application
  2533.  references seven of them! But all seven of those libraries are alive and
  2534.  well. Perhaps it was a library that one of those libraries referenced. But
  2535.  which libraries do they reference? Gee, I dunno..." For this reason, the
  2536.  object name arguments were added. The buffer is a place where OS/2 returns
  2537.  the name of the missing or defective library or other load object, and the
  2538.  length argument tells OS/2 the maximum size of the buffer area. Strings
  2539.  that will not fit in the area are truncated.
  2540.       The exectype word describes the form of the DosExecPgm. The values are
  2541.  as follows.
  2542.  
  2543.       0:  Execute the child program synchronously. The thread issuing the
  2544.           DosExecPgm will not execute further until the child process has
  2545.           finished executing. The thread returns from DosExecPgm when the
  2546.           child process itself terminates, not when the command subtree has
  2547.           terminated. This form of DosExecPgm is provided for ease in
  2548.           converting MS-DOS version 3.x programs to OS/2. It is considered
  2549.           obsolete and should generally be avoided. Its use may interfere
  2550.           with proper Ctrl-C and Ctrl-Break handling (see Chapter 14,
  2551.           Interactive Programs).
  2552.  
  2553.       1:  Execute the program asynchronously. The child process begins
  2554.           executing as soon as the scheduler allows; the calling thread
  2555.           returns from the DosExecPgm call immediately. You cannot assume
  2556.           that the child process has received CPU time before the parent
  2557.           thread returns from the DosExecPgm call; neither can you assume
  2558.           that the child process has not received such CPU service. This
  2559.           form instructs OS/2 not to bother remembering the child's
  2560.           termination code for a future DosCWait call. It is used when the
  2561.           parent process doesn't care what the result code of the child may
  2562.           be or when or if it completes, and it frees the parent from the
  2563.           necessity of issuing DosCWait calls. Programs executing other
  2564.           programs as tools would rarely use this form. This form might be
  2565.           used by a system utility, for example, whose job is to fire off
  2566.           certain programs once an hour but not to take any action or notice
  2567.           should any of those programs fail. Note that, unlike the detach
  2568.           form described below, the created process is still recorded as a
  2569.           child of the executing parent. The parent can issue a DosCWait
  2570.           call to determine whether the child subtree is still executing,
  2571.           although naturally there is no return code when the child process
  2572.           does terminate.
  2573.  
  2574.       2:  This form is similar to form 1 in that it executes the child
  2575.           process asynchronously, but it instructs OS/2 to retain the child
  2576.           process's exit code for future examination by DosCWait. Thus, a
  2577.           program can determine the success or failure of a child process.
  2578.           The parent process should issue an appropriate DosCWait "pretty
  2579.           soon" because OS/2 version 1.0 maintains about 2600 bytes of data
  2580.           structures for a dead process whose parent is expected to DosCWait
  2581.           but hasn't yet done so. To have one of these structures lying
  2582.           around for a few minutes is no great problem, but programs need to
  2583.           issue DosCWaits in a timely fashion to keep from clogging the
  2584.           system with the carcasses of processes that finished hours
  2585.           ago.
  2586.              OS/2 takes care of all the possible timing considerations, so
  2587.           it's OK to issue the DosCWait before or after the child process
  2588.           has completed. Although a parent process can influence the
  2589.           relative assignment of CPU time between the child and parent
  2590.           processes by setting its own and its child's relative priorities,
  2591.           there is no reliable way of determining which process will run
  2592.           when. Write your program in such a way that it doesn't matter if
  2593.           the child completes before or after the DosCWait, or use some form
  2594.           of IPC to synchronize the execution of parent and child processes.
  2595.           See 4.4 DosCWait for more details.
  2596.  
  2597.       3:  This form is used by the system debugger, CodeView. The system
  2598.           architecture does not allow one process to latch onto another
  2599.           arbitrary process and start examining and perhaps modifying the
  2600.           target process's execution. Such a facility would result in a
  2601.           massive side effect and is contrary to the tenet of encapsulation
  2602.           in the design religion. Furthermore, such a facility would prevent
  2603.           OS/2 from ever providing an environment secure against malicious
  2604.           programs. OS/2 solves this problem with the DosPtrace function,
  2605.           which peeks, pokes, and controls a child process. This function is
  2606.           allowed to affect only processes that were executed with this
  2607.           special mode value of 3.
  2608.  
  2609.       4:  This form executes the child process asynchronously but also
  2610.           detaches it from the process issuing the DosExecPgm call. A
  2611.           detached process does not inherit the parent process's screen
  2612.           group; instead, it executes in a special invalid screen group. Any
  2613.           attempt to do screen, keyboard, or mouse I/O from within this
  2614.           screen group returns an error code. The system does not consider a
  2615.           detached process a child of the parent process; the new process
  2616.           has no connection with the creating process. In other words, it's
  2617.           parentless. This form of DosExecPgm is used to create daemon
  2618.           programs that execute without direct interaction with the user.
  2619.  
  2620.       The EnvString argument points to a list of ASCII text strings that
  2621.  contain environment values. OS/2 supports a separate environment block for
  2622.  each process. A process typically inherits its parent's environment
  2623.  strings. In this case, the EnvPointer argument should be NULL, which tells
  2624.  OS/2 to supply the child process with a copy of the parent's environment
  2625.  strings (see Listing 4-4).
  2626.  
  2627.  
  2628.  PATH=C:\DOS;C:\EDITORS;C:\TOOLS;C:\XTOOLS;C:\BIN;C:\UBNET
  2629.  INCLUDE=\include
  2630.  TERM=h19
  2631.  INIT=c:\tmp
  2632.  HOME=c:\tmp
  2633.  USER=c:\tmp
  2634.  TEMP=c:\tmp
  2635.  TERM=ibmpc
  2636.  LIB=c:\lib
  2637.  PROMPT=($p)
  2638.  
  2639.  
  2640.  Listing 4-4.  A typical environment string set.
  2641.  
  2642.  
  2643.       The environment strings are normally used to customize a particular
  2644.  execution environment. For example, suppose a user creates two screen
  2645.  groups, each running a copy of CMD.EXE. Each CMD.EXE is a direct child of
  2646.  the presentation manager, which manages and creates screen groups, and each
  2647.  is also the ancestor of all processes that will run in its particular
  2648.  screen group. If the user is running utility programs that use the
  2649.  environment string "TEMP=<dirname>" to specify a directory to hold
  2650.  temporary files, he or she may want to specify different TEMP= values for
  2651.  each copy of CMD.EXE. As a result, the utilities that use TEMP= will access
  2652.  the proper directory, depending on which screen group they are run in,
  2653.  because they will have inherited the proper TEMP= environment string from
  2654.  their CMD.EXE ancestor. See Chapter 10, Environment Strings, for a complete
  2655.  discussion.
  2656.       Because of the inheritable nature of environment strings, parent
  2657.  processes that edit the environment list should remove or edit only those
  2658.  strings with which they are involved; any unrecognized strings should be
  2659.  preserved.
  2660.  
  2661.  
  2662.  4.4  DosCWait
  2663.  
  2664.  When a process executes a child process, it usually wants to know when that
  2665.  child process has completed and whether the process succeeded or failed.
  2666.  DosCWait, the OS/2 companion function to DosExecPgm, returns such
  2667.  information. Before we discuss DosCWait in detail, two observations are in
  2668.  order. First, although each DosExecPgm call starts only a single process,
  2669.  it's possible--and not uncommon--for that child process to create its own
  2670.  children and perhaps they their own and so on. A program should not assume
  2671.  that its child process won't create subchildren; instead, programs should
  2672.  use the command-subtree forms of DosCWait. One return code from the direct
  2673.  child process (that is, the root of the command subtree) is sufficient
  2674.  because if that direct child process invokes other processes to do work for
  2675.  it the direct child is responsible for monitoring their success via
  2676.  DosCWait. In other words, if a child process farms out some of its work to
  2677.  a grandchild process and that grandchild process terminates in error, then
  2678.  the child process should also terminate with an error return.
  2679.       Second, although we discuss the process's child, in fact processes can
  2680.  have multiple child processes and therefore multiple command subtrees at
  2681.  any time. The parent process may have interconnected the child processes
  2682.  via anonymous pipes, or they may be independent of one another. Issuing
  2683.  separate DosCWaits for each process or subtree is unnecessary. The form of
  2684.  the DosCWait call is shown in Listing 4-5.
  2685.  
  2686.  
  2687.  extern unsigned far pascal DOSCWAIT (
  2688.       unsigned                 ACTIONCODE,
  2689.       unsigned                 WAITOPTION,
  2690.       struct ResultCodes far   *RESULTWORD,
  2691.       unsigned far             *PIDRETURN,
  2692.       unsigned                 PID);
  2693.  
  2694.  
  2695.  Listing 4-5.  DosCWait function.
  2696.  
  2697.  
  2698.       Three of the arguments affect the scope of the command: ActionCode,
  2699.  WaitOption, and PID. It's easiest to show how these interact by arranging
  2700.  their possible values into tables.
  2701.  
  2702.  
  2703.       DosCWait forms: Command Subtrees
  2704.  
  2705.       These forms of DosCWait operate on the entire command subtree, which
  2706.       may, of course, consist of only one child process. We recommend these
  2707.       forms because they will continue to work correctly if the child
  2708.       process is changed to use more or fewer child processes of its own.
  2709.  
  2710. ╓┌─────────────────┌────────────┌───────────┌────────────────────────────────╖
  2711.       ActionCode   WaitOption   ProcessId   Action
  2712.  ───────────────────────────────────────────────────────────────────────────
  2713.       1            0            n           Wait until the command subtree
  2714.                                             has completed and then return
  2715.                                             the direct child's termination
  2716.                                             code.
  2717.  
  2718.       1            1            n           If the command subtree has
  2719.                                             completed, return the direct
  2720.                                             child's termination code.
  2721.                                             Otherwise, return the
  2722.                                             ERROR_CHILD_NOT_COMPLETE
  2723.                                             error code.
  2724.  
  2725.  
  2726.       DosCWait forms: Individual Processes
  2727.  
  2728.       These forms of DosCWait are used to monitor individual child
  2729.       processes. The processes must be direct children; grandchild or
  2730.       unrelated processes cannot be DosCWaited. Use these forms only when
  2731.       the child process is part of the same application or software package
  2732.       as the parent process; the programmer needs to be certain that she or
  2733.       he can safely ignore the possibility that grandchild processes might
  2734.       still be running after the direct child has terminated.1
  2735.  
  2736. ╓┌─────────────────┌────────────┌───────────┌────────────────────────────────╖
  2737.       ActionCode   WaitOption   ProcessId   Action
  2738.  ───────────────────────────────────────────────────────────────────────────
  2739.       0            0            0           DosCWait returns as soon as a
  2740.                                             direct child process terminates.
  2741.                                             If a child process had already
  2742.                                             terminated at the time of this
  2743.                                             call, it will return
  2744.                                             immediately.
  2745.  
  2746.       0            0            N           DosCWait returns as soon as the
  2747.                                             direct child process N
  2748.                                             terminates. If it had already
  2749.                                             terminated at the time of the
  2750.                                             call, DosCWait returns
  2751.                                             immediately.
  2752.  
  2753.       ActionCode   WaitOption   ProcessId   Action
  2754. 
  2755.       0            1            0           DosCWait checks for a terminated
  2756.                                             direct child process. If one is
  2757.                                             found, its status is returned.
  2758.                                             If none is found, an error code
  2759.                                             is returned.
  2760.  
  2761.       0            1            N           DosCWait checks the status of
  2762.                                             the direct child process N. If
  2763.                                             it is terminated, its status is
  2764.                                             returned. If it is still
  2765.                                             running, an error code is
  2766.                                             returned.
  2767.  
  2768.       DosCWait forms: Not Recommended
  2769.  
  2770. ╓┌─────────────────┌────────────┌───────────┌────────────────────────────────╖
  2771.       ActionCode   WaitOption   ProcessId    Action
  2772.  ───────────────────────────────────────────────────────────────────────────
  2773.       1            0            0            DosCWait waits until a direct
  2774.       ActionCode   WaitOption   ProcessId    Action
  2775.      1            0            0            DosCWait waits until a direct
  2776.                                              child has terminated and then
  2777.                                              waits until all of that child's
  2778.                                              descendants have terminated. It
  2779.                                              then returns the direct child's
  2780.                                              exit code. This form does not
  2781.                                              wait until the first command
  2782.                                              subtree has terminated; it
  2783.                                              selects a command subtree based
  2784.                                              on the first direct child that
  2785.                                              terminates, and then it waits
  2786.                                              as long as necessary for the
  2787.                                              remainder of that command
  2788.                                              subtree, even if other command
  2789.                                              subtrees meanwhile complete.
  2790.  
  2791.       1            1            0            This form returns
  2792.                                              ERROR_CHILD_NOT_COMPLETE if any
  2793.                                              process in any of the caller's
  2794.                                              subtrees is still executing. If
  2795.       ActionCode   WaitOption   ProcessId    Action
  2796.                                             subtrees is still executing. If
  2797.                                              all subtrees have terminated,
  2798.                                              this form returns with a direct
  2799.                                              child's exit code. If no direct
  2800.                                              child processes have unwaited
  2801.                                              exit codes, the code
  2802.                                              ERROR_WAIT_NO_CHILDREN is
  2803.                                              returned.
  2804.  
  2805.  
  2806.  4.5  Control of Child Tasks and Command Subtrees
  2807.  
  2808.  A parent process has only limited control over its child processes because
  2809.  the system is designed to minimize the side effects, or cross talk, between
  2810.  processes. Specifically, a parent process can affect its command subtrees
  2811.  in two ways: It can change their CPU priority, and it can terminate (kill)
  2812.  them. Once again, the command subtree is the recommended form for both
  2813.  commands because that form is insensitive to the operational details of the
  2814.  child process.
  2815.  
  2816.  
  2817.  4.5.1  DosKillProcess
  2818.  A parent process may initiate a child process or command subtree and then
  2819.  decide to terminate that activity before the process completes normally.
  2820.  Often this comes about because of a direct user command or because the user
  2821.  typed Ctrl-Break. See Chapter 14, Interactive Programs, for special
  2822.  techniques concerning Ctrl-Break and Ctrl-C.
  2823.       DosKillProcess flags each process in the command subtree (or the
  2824.  direct child process if that form is used) for termination. A process
  2825.  flagged for termination normally terminates as soon as all its threads
  2826.  leave the system (that is, as soon as all its threads return from all
  2827.  system calls). The system aborts calls that might block for more than a
  2828.  second or two, such as those that read a keyboard character, so that the
  2829.  process can terminate quickly. A process can intercept SIGKILL to delay
  2830.  termination longer, even indefinitely. Delaying termination inordinately
  2831.  via SetSignalHandler/SIGKILL is very bad practice and is considered a bug
  2832.  rather than a feature.
  2833.  
  2834.  
  2835.  4.5.2  DosSetPrty
  2836.  A child process inherits its parent's process priority when the DosExecPgm
  2837.  call is issued. After the DosExecPgm call, the parent can still change the
  2838.  process priority of the command subtree or of only the direct child
  2839.  process. The command subtree form is recommended; if the child process's
  2840.  work deserves priority N, then any child processes that it executes to help
  2841.  in that work should also run at priority N.
  2842.  
  2843.  
  2844.  
  2845.  5  Threads and Scheduler/Priorities
  2846.  
  2847.  ───────────────────────────────────────────────────────────────────────────
  2848.  
  2849.  5.1  Threads
  2850.  
  2851.  Computers consist of a CPU (central processing unit) and RAM (random access
  2852.  memory). A computer program consists of a sequence of instructions that are
  2853.  placed, for the most part, one after the other in RAM. The CPU reads each
  2854.  instruction in sequence and executes it. The passage of the CPU through the
  2855.  instruction sequence is called a thread of execution. All versions of MS-
  2856.  DOS executed programs, so they necessarily supported a thread of execution.
  2857.  OS/2 is unique, however, in that it supports multiple threads of execution
  2858.  within a single process. In other words, a program can execute in two or
  2859.  more spots in its code at the same time.
  2860.       Obviously, a multitasking system needs to support multiple threads in
  2861.  a systemwide sense. Each process necessarily must have a thread; so if
  2862.  there are ten processes in the system, there must be ten threads. Such an
  2863.  existence of multiple threads in the system is invisible to the programmer
  2864.  because each program executes with only one thread. OS/2 is different from
  2865.  this because it allows an individual program to execute with multiple
  2866.  threads if it desires.
  2867.       Because threads are elements of processes and because the process is
  2868.  the unit of resource ownership, all threads that belong to the same process
  2869.  share that process's resources. Thus, if one thread opens a file on file
  2870.  handle X, all threads in that process can issue DosReads or DosWrites to
  2871.  that handle. If one thread allocates a memory segment, all threads in that
  2872.  process can access that memory segment. Threads are analogous to the
  2873.  employees of a company. A company may consist of a single employee, or it
  2874.  may consist of two or more employees that divide the work among them. Each
  2875.  employee has access to the company's resources--its office space and
  2876.  equipment. The employees themselves, however, must coordinate their work so
  2877.  that they cooperate and don't conflict. As far as the outside world is
  2878.  concerned, each employee speaks for the company. Employees can terminate
  2879.  and/or more can be hired without affecting how the company is seen from
  2880.  outside. The only requirement is that the company have at least one
  2881.  employee. When the last employee (thread) dies, the company (process) also
  2882.  dies.
  2883.       Although the process is the unit of resource ownership, each thread
  2884.  does "own" a small amount of private information. Specifically, each thread
  2885.  has its own copy of the CPU's register contents. This is an obvious
  2886.  requirement if each thread is to be able to execute different instruction
  2887.  sequences. Furthermore, each thread has its own copy of the floating point
  2888.  registers. OS/2 creates the process's first thread when the program begins
  2889.  execution. Any additional threads are created by means of the
  2890.  DosCreateThread call. Any thread can create another thread. All threads in
  2891.  a process are considered siblings; there are no parent-child relationships.
  2892.  The initial thread, thread 1, has some special characteristics and is
  2893.  discussed below.
  2894.  
  2895.  
  2896.  5.1.1  Thread Stacks
  2897.  Each thread has its own stack area, pointed to by that thread's SS and SP
  2898.  values. Thread 1 is the process's primal thread. OS/2 allocates it stack
  2899.  area in response to specifications in the .EXE file. If additional threads
  2900.  are created via the DosCreateThread call, the caller specifies a stack area
  2901.  for the new thread. Because the memory in which each thread's stack resides
  2902.  is owned by the process, any thread can modify this memory; the programmer
  2903.  must make sure that this does not happen. The size of the segment in which
  2904.  the stack resides is explicitly specified; the size of a thread's stack is
  2905.  not. The programmer can place a thread's stack in its own segment or in a
  2906.  segment with other data values, including other thread stacks. In any case,
  2907.  the programmer must ensure sufficient room for each thread's needs. Each
  2908.  thread's stack must have at least 2 KB free in addition to the thread's
  2909.  other needs at all times. This extra space is set aside for the needs of
  2910.  dynamic link routines, some of which consume considerable stack space. All
  2911.  threads must maintain this stack space reserve even if they are not used to
  2912.  call dynamic link routines. Because of a bug in many 80286 processors,
  2913.  stack segments must be preallocated to their full size. You cannot overrun
  2914.  a stack segment and then assume that OS/2 will grow the segment;
  2915.  overrunning a stack segment will cause a stack fault, and the process will
  2916.  be terminated.
  2917.  
  2918.  
  2919.  5.1.2  Thread Uses
  2920.  Threads have a great number of uses. This section describes four of them.
  2921.  These examples are intended to be inspirations to the programmer; there are
  2922.  many other uses for threads.
  2923.  
  2924.  
  2925.  5.1.2.1  Foreground and Background Work
  2926.  Threads provide a form of multitasking within a single program; therefore,
  2927.  one of their most obvious uses is to provide simultaneous foreground and
  2928.  background1 processing for a program. For example, a spreadsheet program
  2929.  might use one thread to display menus and to read user input. A second
  2930.  thread could execute user commands, update the spreadsheet, and so on.
  2931.       This arrangement generally increases the perceived speed of the
  2932.  program by allowing the program to prompt for another command before the
  2933.  previous command is complete. For example, if the user changes a cell in a
  2934.  spreadsheet and then calls for recalculation, the "execute" thread can
  2935.  recalculate while the "command" thread allows the user to move the cursor,
  2936.  select new menus, and so forth. The spreadsheet should use RAM semaphores
  2937.  to protect its structures so that one thread can't change a structure while
  2938.  it is being manipulated by another thread. As far as the user can tell, he
  2939.  or she is able to overlap commands without restriction. In actuality, the
  2940.  previous command is usually complete before the user can finish entering
  2941.  the new command. Occasionally, however, the new command is delayed until
  2942.  the first has completed execution. This happens, for example, when the user
  2943.  of a spreadsheet deletes a row right after saving the spreadsheet to disk.
  2944.  Of course, the performance in this worst case situation is no worse than a
  2945.  standard single-thread design is for all cases.
  2946.  
  2947.  
  2948.  5.1.2.2  Asynchronous Processing
  2949.  Another common use of threads is to provide asynchronous elements in a
  2950.  program's design. For example, as a protection against power failure, you
  2951.  can design an editor so that it writes its RAM buffer to disk once every
  2952.  minute. Threads make it unnecessary to scatter time checks throughout the
  2953.  program or to sit in polling loops for input so that a time event isn't
  2954.  missed while blocked on a read call. You can create a thread whose sole job
  2955.  is periodic backup. The thread can call DosSleep to sleep for 60 seconds,
  2956.  write the buffer, and then go back to sleep for another 60 seconds.
  2957.       The asynchronous event doesn't have to be time related. For example,
  2958.  in a program that communicates over an asynchronous serial port, you can
  2959.  dedicate a thread to wait for the modem carrier to come on or to wait for a
  2960.  protocol time out. The main thread can continue to interact with the user.
  2961.       Programs that provide services to other programs via IPC can use
  2962.  threads to simultaneously respond to multiple requests. For example, one
  2963.  thread can watch the incoming work queue while one or more additional
  2964.  threads perform the work.
  2965.  
  2966.  
  2967.  5.1.2.3  Speed Execution
  2968.  You can use threads to speed the execution of single processes by
  2969.  overlapping I/O and computation. A single-threaded process can perform
  2970.  computations or call OS/2 for disk reads and writes, but not both at the
  2971.  same time. A multithreaded process, on the other hand, can compute one
  2972.  batch of data while reading the next batch from a device.
  2973.       Eventually, PCs containing multiple 80386 processors will become
  2974.  available. An application that uses multiple threads may execute faster by
  2975.  using more than one CPU simultaneously.
  2976.  
  2977.  
  2978.  5.1.2.4  Organizing Programs
  2979.  Finally, you can use threads to organize and simplify the structure of a
  2980.  program. For example, in a program for a turnkey security/alarm system, you
  2981.  can assign a separate thread for each activity. One thread can watch the
  2982.  status of the intrusion switches; a second can send commands to control the
  2983.  lights; a third can run the telephone dialer; and a fourth can interface
  2984.  with each control panel.
  2985.       This structure simplifies software design. The programmer needn't
  2986.  worry that an intrusion switch is triggering unnoticed while the CPU is
  2987.  executing the code that waits on the second key of a two-key command.
  2988.  Likewise, the programmer doesn't have to worry about talking to two command
  2989.  consoles at the same time; because each has its own thread and local
  2990.  (stack) variables, multiple consoles can be used simultaneously without
  2991.  conflict.
  2992.       Of course, you can write such a program without multiple threads; a
  2993.  rat's nest of event flags and polling loops would do the job. Much better
  2994.  would be a family of co-routines. But best, and simplest of all, is a
  2995.  multithreaded design.
  2996.  
  2997.  
  2998.  5.1.3  Interlocking
  2999.  The good news about threads is that they share a process's data, files, and
  3000.  resources. The bad news is that they share a process's data, files, and
  3001.  resources--and that sometimes these items must be protected against
  3002.  simultaneous update by multiple threads. As we discussed earlier, most OS/2
  3003.  machines have a single CPU; the "random" preemption of the scheduler,
  3004.  switching the CPU among threads, gives the illusion of the simultaneous
  3005.  execution of threads. Because the scheduler is deterministic and priority
  3006.  based, scheduling a process's threads is certainly not random; but good
  3007.  programming practice requires that it be considered so. Each time a program
  3008.  runs, external events will perturb the scheduling of the threads. Perhaps
  3009.  some other, higher priority task needs the CPU for a while. Perhaps the
  3010.  disk arm is in a different position, and a disk read by one thread takes a
  3011.  little longer this time than it did the last. You cannot even assume that
  3012.  only the highest priority runnable thread is executing because a multiple-
  3013.  CPU system may execute the N highest priority threads.
  3014.       The only safe assumption is that all threads are executing
  3015.  simultaneously and that--in the absence of explicit interlocking or
  3016.  semaphore calls--each thread is always doing the "worst possible thing" in
  3017.  terms of simultaneously updating static data or structures. Writing to
  3018.  looser standards and then testing the program "to see if it's OK" is
  3019.  unacceptable. The very nature of such race conditions, as they are called,
  3020.  makes them extremely difficult to find during testing. Murphy's law says
  3021.  that such problems are rare during testing and become a plague only after
  3022.  the program is released.
  3023.  
  3024.  
  3025.  5.1.3.1  Local Variables
  3026.  The best way to avoid a collision of threads over static data is to write
  3027.  your program to minimize static data. Because each thread has its own
  3028.  stack, each thread has its own stack frame in which to store local
  3029.  variables. For example, if one thread opens and reads a file and no other
  3030.  thread ever manipulates that file, the memory location where that file's
  3031.  handle is stored should be in the thread's stack frame, not in static
  3032.  memory. Likewise, buffers and work areas that are private to a thread
  3033.  should be on that thread's stack frame. Stack variables that are local to
  3034.  the current procedure are easily referenced in high-level languages and in
  3035.  assembly language. Data items that are referenced by multiple procedures
  3036.  can still be located on the stack. Pascal programs can address such items
  3037.  directly via the data scope mechanism. C and assembly language programs
  3038.  will need to pass pointers to the items into the procedures that use them.
  3039.  
  3040.  
  3041.  5.1.3.2  RAM Semaphores
  3042.  Although using local variables on stack frames greatly reduces problems
  3043.  among threads, there will always be at least a few cases in which more than
  3044.  one thread needs to access a static data item or a static resource such as
  3045.  a file handle. In this situation, write the code that manipulates the
  3046.  static item as a critical section (a body of code that manipulates a data
  3047.  resource in a nonreentrant way) and then use RAM semaphores to reserve each
  3048.  critical section before it is executed. This procedure guarantees that only
  3049.  one thread at a time is in a critical section. See 16.2 Data Integrity for
  3050.  a more detailed discussion.
  3051.  
  3052.  
  3053.  5.1.3.3  DosSuspendThread
  3054.  In some situations it may be possible to enumerate all threads that might
  3055.  enter a critical section. In these cases, a process's thread can use
  3056.  DosSuspendThread to suspend the execution of the other thread(s) that might
  3057.  enter the critical section. The DosSuspendThread call can only be used to
  3058.  suspend threads that belong to the process making the call; it cannot be
  3059.  used to suspend a thread that belongs to another process. Multiple threads
  3060.  can be suspended by making multiple DosSuspendThread calls, one per thread.
  3061.  If a just-suspended thread is in the middle of a system call, work on that
  3062.  system call may or may not proceed. In either case, there will be no
  3063.  further execution of application (ring 3) code by a suspended thread.
  3064.       It is usually better to protect critical sections with a RAM semaphore
  3065.  than to use DosSuspendThread. Using a semaphore to protect a critical
  3066.  section is analogous to using a traffic light to protect an intersection
  3067.  (an automotive "critical section" because conflicting uses must be
  3068.  prevented). Using DosSuspendThread, on the other hand, is analogous to your
  3069.  stopping the other cars each time you go through an intersection; you're
  3070.  interfering with the operation of the other cars just in case they might be
  3071.  driving through the same intersection as you, presumably an infrequent
  3072.  situation. Furthermore, you need a way to ensure that another vehicle isn't
  3073.  already in the middle of the intersection when you stop it. Getting back to
  3074.  software, you need to ensure that the thread you're suspending isn't
  3075.  already executing the critical section at the time that you suspend it. We
  3076.  recommend that you avoid DosSuspendThread when possible because of its
  3077.  adverse effects on process performance and because of the difficulty in
  3078.  guaranteeing that all the necessary threads have been suspended, especially
  3079.  when a program undergoes future maintenance and modification.
  3080.       A DosResumeThread call restores the normal operation of a suspended
  3081.  thread.
  3082.  
  3083.  
  3084.  5.1.3.4  DosEnterCritSec/DosExitCritSec
  3085.  DosSuspendThread suspends the execution of a single thread within a
  3086.  process. DosEnterCritSec suspends all threads in a process except the one
  3087.  making the DosEnterCritSec call. Except for the scope of their operation,
  3088.  DosEnterCritSec and DosExitCritSec are similar to DosSuspendThead and
  3089.  DosResumeThread, and the same caveats and observations apply.
  3090.       DosExitCritSec will not undo a DosSuspendThread that was already in
  3091.  effect. It releases only those threads that were suspended by
  3092.  DosEnterCritSec.
  3093.  
  3094.  
  3095.  5.1.4  Thread 1
  3096.  Each thread in a process has an associated thread ID. A thread's ID is a
  3097.  magic cookie. Its value has no intrinsic meaning to the application; it
  3098.  has meaning only as a name for a thread in an operating system call. The
  3099.  one exception to this is the process's first thread, whose thread ID is
  3100.  always 1.
  3101.       Thread 1 is special: It is the thread that is interrupted when a
  3102.  process receives a signal. See Chapter 12, Signals, for further details.
  3103.  
  3104.  
  3105.  5.1.5  Thread Death
  3106.  A thread can die in two ways. First, it can terminate itself with the
  3107.  DosExit call. Second, when any thread in a process calls DosExit with the
  3108.  "exit entire process" argument, all threads belonging to that process are
  3109.  terminated "as soon as possible." If they were executing application code
  3110.  at the time DosExit was called, they terminate immediately. If they were in
  3111.  the middle of a system call, they terminate "very quickly." If the system
  3112.  call executes quickly enough, its function may complete (although the CPU
  3113.  will not return from the system call itself); but if the system call
  3114.  involves delays of more than 1 second, it will terminate without
  3115.  completing. Whether a thread's last system call completes is usually moot,
  3116.  but in a few cases, such as writes to some types of devices, it may be
  3117.  noticed that the last write was only partially completed.
  3118.       When a process wants to terminate, it should use the "terminate entire
  3119.  process" form of DosExit rather than the "terminate this thread" form.
  3120.  Unbeknownst to the calling process, some dynlink packages, including some
  3121.  OS/2 system calls, may create threads. These threads are called captive
  3122.  threads because only the original calling thread returns from the dynlink
  3123.  call; the created thread remains "captive" inside the dynlink package. If a
  3124.  program attempts to terminate by causing all its known threads to use the
  3125.  DosExit "terminate this thread" form, the termination may not be successful
  3126.  because of such captive threads.
  3127.       Of course, if the last remaining thread of a process calls DosExit
  3128.  "terminate this thread," OS/2 terminates the process.
  3129.  
  3130.  
  3131.  5.1.6  Performance Characteristics
  3132.  Threads are intended to be fast and cheap. In OS/2 version 1.0, each
  3133.  additional thread that is created consumes about 1200 bytes of memory
  3134.  inside the OS/2 kernel for its kernel mode stack. This is in addition to
  3135.  the 2048 bytes of user mode stack space that we recommend you provide from
  3136.  the process's data area. Terminating a thread does not release the kernel
  3137.  stack memory, but subsequently creating another thread reuses this memory.
  3138.  In other words, the system memory that a process's threads consume is the
  3139.  maximum number of threads simultaneously alive times 1200 bytes. This
  3140.  figure is exclusive of each thread's stack, which is provided by the
  3141.  process from its own memory.
  3142.       The time needed to create a new thread depends on the process's
  3143.  previous thread behavior. Creating a thread that will reuse the internal
  3144.  memory area created for a previous thread that has terminated takes
  3145.  approximately 3 milliseconds.2 A request to create a new thread that
  3146.  extends the process's "thread count high-water mark" requires an internal
  3147.  memory allocation operation. This operation may trigger a memory compaction
  3148.  or even a segment swapout, so its time cannot be accurately predicted.
  3149.       It takes about 1 millisecond for the system to begin running an
  3150.  unblocked thread. In other words, if a lower-priority thread releases a RAM
  3151.  semaphore that is being waited on by a higher-priority thread,
  3152.  approximately 1 millisecond passes between the lower-priority thread's call
  3153.  to release the semaphore and the return of the higher-priority thread from
  3154.  its DosSemRequest call.
  3155.       Threads are a key feature of OS/2; they will receive strong support in
  3156.  future versions of OS/2 and will play an increasingly important
  3157.  architectural role. You can, therefore, expect thread costs and performance
  3158.  to be the same or to improve in future releases.
  3159.  
  3160.  
  3161.  5.2  Scheduler/Priorities
  3162.  
  3163.  A typical running OS/2 system contains a lot of threads. Frequently,
  3164.  several threads are ready to execute at any one time. The OS/2 scheduler
  3165.  decides which thread to run next and how long to run it before assigning
  3166.  the CPU to another thread. OS/2's scheduler is a priority-based
  3167.  scheduler; it assigns each thread a priority and then uses that priority to
  3168.  decide which thread to run. The OS/2 scheduler is also a preemptive
  3169.  scheduler. If a higher-priority thread is ready to execute, OS/2 does not
  3170.  wait for the lower-priority thread to finish with the CPU before
  3171.  reassigning the CPU; the lower-priority thread is preempted--the CPU is
  3172.  summarily yanked away. Naturally, the state of the preempted thread is
  3173.  recorded so that its execution can resume later without ill effect.
  3174.       The scheduler's dispatch algorithm is very straightforward: It
  3175.  executes the highest-priority runnable thread for as long as the thread
  3176.  wants the CPU. When that thread gives up the CPU--perhaps by waiting for an
  3177.  I/O operation--that thread is no longer runnable, and the scheduler
  3178.  executes the thread with the highest priority that is runnable. If a
  3179.  blocked thread becomes runnable and it has a higher priority than the
  3180.  thread currently running, the CPU is immediately preempted and assigned to
  3181.  the higher-priority thread. In summary, the CPU is always running the
  3182.  highest-priority runnable thread.
  3183.       The scheduler's dispatcher is simplicity itself: It's blindly priority
  3184.  based. Although the usual focus for OS/2 activities is the process--a
  3185.  process lives, dies, opens files, and so on--the scheduler components of
  3186.  OS/2 know little about processes. Because the thread is the dispatchable
  3187.  entity, the scheduler is primarily thread oriented. If you're not used to
  3188.  thinking in terms of threads, you can mentally substitute the word process
  3189.  for the word thread in the following discussion. In practice, all of a
  3190.  process's threads typically share the same priority, so it's not too
  3191.  inaccurate to view the system as being made up of processes that compete
  3192.  for CPU resources.
  3193.       In OS/2 threads are classified and run in three categories: general
  3194.  priority, time-critical priority, and low priority. These categories are
  3195.  further divided into subcategories. Figure 5-1 shows the relationship of
  3196.  the three priority categories and their subcategories.
  3197.  
  3198.  
  3199.     High
  3200.  ┌────────┐
  3201.  │        │            /┌────────────────┐
  3202.  │        │          /  │   Foreground   │
  3203.  │ Force  │        /    │     screen     │
  3204.  │  run   │      /      │     group      │
  3205.  │        │    /        │                │
  3206.  │        │  /          │  interactive   │
  3207.  ├────────┤/            ├────────────────┤
  3208.  │        │             │   Foreground   │
  3209.  │        │             │     screen     │
  3210.  │ Normal │             │     group      │
  3211.  │        │             │                │
  3212.  │        │             │ noninteractive │
  3213.  │        │             ├────────────────┤
  3214.  ├────────┤\            │                │
  3215.  │        │  \          │   Background   │
  3216.  │        │    \        │     screen     │
  3217.  │  Idle  │      \      │     group      │
  3218.  │  time  │        \    │                │
  3219.  │        │          \  └────────────────┘
  3220.  │        │
  3221.  └────────┘
  3222.     Low
  3223.  
  3224.  Figure 5-1.  Priority categories.
  3225.  
  3226.  
  3227.  5.2.1  General Priority Category
  3228.  The majority of threads in the system run in the general priority category
  3229.  and belong to one of three subcategories: background, foreground, or
  3230.  interactive. To a limited extent, OS/2 dynamically modifies the priorities
  3231.  of threads in the general priority category.
  3232.  
  3233.  
  3234.  5.2.1.1  Background Subcategory
  3235.  The purpose of the OS/2 priority design is to optimize response rather than
  3236.  throughput. In other words, the system is not concerned about ensuring that
  3237.  all runnable threads get at least some CPU time, and the system is not
  3238.  primarily concerned about trying to keep the disks busy when the highest-
  3239.  priority thread is compute bound. Instead, OS/2 is concerned about keeping
  3240.  less important work from delaying or slowing more important work. This is
  3241.  the reason for the background subcategory. The word background has been
  3242.  used in many different ways to describe how tasks are performed in many
  3243.  operating systems; we use the word to indicate processes that are
  3244.  associated with a screen group not currently being displayed.
  3245.       For example, a user is working with a word-processing program but then
  3246.  switches from that program to a spreadsheet program. The word-processing
  3247.  program becomes background, and the spreadsheet program is promoted from
  3248.  background to foreground. When the user selects different screen groups,
  3249.  threads change from foreground to background or background to foreground.
  3250.  Background threads have the lowest priority in the general priority
  3251.  category. Background applications get the CPU (and, through it, the disks)
  3252.  only when all foreground threads are idle. As soon as a foreground thread
  3253.  is runnable, the CPU is preempted from the background thread. Background
  3254.  threads can use leftover machine time, but they can never compete with
  3255.  foreground threads.
  3256.  
  3257.  
  3258.  5.2.1.2  Foreground and Interactive Subcategories
  3259.  All processes associated with the currently active screen group are made
  3260.  members of the foreground subcategory. The process that is currently
  3261.  interacting with the keyboard is promoted to the interactive subcategory.
  3262.  This ensures that the user will get the fastest possible response to a
  3263.  command. When the interactive process's threads release the CPU (via
  3264.  blocking on some OS/2 call), the noninteractive foreground threads get the
  3265.  next crack at it because those threads are usually doing work on behalf of
  3266.  the interactive process or work that is in some way related. If no
  3267.  foreground thread needs the CPU, background threads may run.
  3268.       Although the scheduler concerns itself with threads rather than
  3269.  processes, it's processes that switch between categories--foreground,
  3270.  background, and interactive. When a process changes category--for example,
  3271.  when a process shows itself to be in the interactive subcategory by doing
  3272.  keyboard I/O--the priorities of all its threads are adjusted
  3273.  appropriately.
  3274.       Because background threads are the "low men on the totem pole" that is
  3275.  composed of quite a few threads, it may seem that they'll never get to run.
  3276.  This isn't the case, though, over a long enough period of time. Yes, a
  3277.  background thread can be totally starved for CPU time during a 5-second
  3278.  interval, but it would be very rare if it received no service during a 1-
  3279.  minute interval. Interactive application commands that take more than a few
  3280.  seconds of CPU time are rare. Commands involving disk transfer may take
  3281.  longer, but the CPU is available for lower-priority threads while the
  3282.  interactive process is waiting for disk operations. Finally, a user rarely
  3283.  keeps an interactive application fully busy; the normal "type, look, and
  3284.  think" cycle has lots of spare time in it for background threads to
  3285.  run.
  3286.       But how does this apply to the presentation manager? The presentation
  3287.  manager runs many independent interactive tasks within the same screen
  3288.  group, so are they all foreground threads? How does OS/2 know which is the
  3289.  interactive process? The answer is that the presentation manager advises
  3290.  the scheduler. When the presentation manager screen group is displayed, all
  3291.  threads within that screen group are placed in the foreground category.
  3292.  When the user selects a particular window to receive keyboard or mouse
  3293.  events, the presentation manager tells the scheduler that the process using
  3294.  that window is now the interactive process. As a result, the system's
  3295.  interactive performance is preserved in the presentation manager's screen
  3296.  group.
  3297.  
  3298.  
  3299.  5.2.1.3  Throughput Balancing
  3300.  We mentioned that some operating systems try to optimize system throughput
  3301.  by trying to run CPU-bound and I/O-bound applications at the same time. The
  3302.  theory is that the I/O-bound application ties up the disk but needs little
  3303.  CPU time, so the disk work can be gotten out of the way while the CPU is
  3304.  running the CPU-bound task. If the disk thread has the higher priority, the
  3305.  tasks run in tandem. Each time the disk operation is completed, the I/O-
  3306.  bound thread regains the CPU and issues another disk operation. Leftover
  3307.  CPU time goes to the CPU-bound task that, in this case, has a lower
  3308.  priority.
  3309.       This won't work, however, if the CPU-bound thread has a higher
  3310.  priority than the I/O-bound thread. The CPU-bound thread will tend to hold
  3311.  the CPU, and the I/O-bound thread won't get even the small amount of CPU
  3312.  time that it needs to issue another I/O request. Traditionally, schedulers
  3313.  have been designed to deal with this problem by boosting the priority of
  3314.  I/O-bound tasks and lowering the priority of CPU-bound tasks so that,
  3315.  eventually, the I/O-bound thread gets enough service to make its I/O
  3316.  requests.
  3317.       The OS/2 scheduler incorporates this design to a limited extent. Each
  3318.  time a thread issues a system call that blocks, the scheduler looks at the
  3319.  period between the time the CPU was assigned to the thread and the time the
  3320.  thread blocked itself with a system call.3 If that period of time is short,
  3321.  the thread is considered I/O bound, and its priority receives a small
  3322.  increment. If a thread is truly I/O bound, it soon receives several such
  3323.  increments and, thus, a modest priority promotion. On the other hand, if
  3324.  the thread held the CPU for a longer period of time, it is considered CPU
  3325.  bound, and its priority receives a small decrement.
  3326.       The I/O boundedness priority adjustment is small. No background
  3327.  thread, no matter how I/O bound, can have its priority raised to the point
  3328.  where it has a higher priority than any foreground thread, no matter how
  3329.  CPU bound. This throughput enhancing optimization applies only to "peer"
  3330.  threads--threads with similar priorities. For example, the threads of a
  3331.  single process generally have the same base priority, so this adjustment
  3332.  helps optimize the throughput of that process.
  3333.  
  3334.  
  3335.  5.2.2  Time-Critical Priority Category
  3336.  Foreground threads, particularly interactive foreground threads, receive
  3337.  CPU service whenever they want it. Noninteractive foreground threads and,
  3338.  particularly, background threads may not receive any CPU time for periods
  3339.  of arbitrary length. This approach improves system response, but it's not
  3340.  always a good thing. For example, you may be running a network or a
  3341.  telecommunications application that drops its connection if it can't
  3342.  respond to incoming packets in a timely fashion. Also, you may want to make
  3343.  an exception to the principle of "response, not throughput" when it comes
  3344.  to printers. Most printers are much slower than their users would like, and
  3345.  most printer spooler programs require little in the way of CPU time; so the
  3346.  OS/2 print spooler (the program that prints queued output on the printer)
  3347.  would like to run at a high priority to keep the printer busy.
  3348.       Time-critical applications are so called because the ability to run in
  3349.  a timely fashion is critical to their well-being. Time-critical
  3350.  applications may or may not be interactive, and they may be in the
  3351.  foreground or in a background screen group, but this should not affect
  3352.  their high priority. The OS/2 scheduler contains a time-critical priority
  3353.  category to deal with time-critical applications. A thread running in this
  3354.  priority category has a higher priority than any non-time-critical thread
  3355.  in the system, including interactive threads. Unlike priorities in the
  3356.  general category, a time-critical priority is never adjusted; once given a
  3357.  time-critical priority, a thread's priority remains fixed until a system
  3358.  call changes it.
  3359.       Naturally, time-critical threads should consume only modest amounts of
  3360.  CPU time. If an application has a time-critical thread that consumes
  3361.  considerable CPU time--say, more than 20 percent--the foreground
  3362.  interactive application will be noticeably slowed or even momentarily
  3363.  stopped. System usability is severely affected when the interactive
  3364.  application can't get service. The screen output stutters and stumbles,
  3365.  characters are dropped when commands are typed, and, in general, the
  3366.  computer becomes unusable.
  3367.       Not all threads in a process have to be of the same priority. An
  3368.  application may need time-critical response for only some of its work; the
  3369.  other work can run at a normal priority. For example, in a
  3370.  telecommunications program a "receive incoming data" thread might run at a
  3371.  time-critical priority but queue messages in memory for processing by a
  3372.  normal-priority thread. If the time-critical thread finds that the normal-
  3373.  priority thread has fallen behind, it can send a "wait for me" message to
  3374.  the sending program.
  3375.       We strongly recommend that processes that use monitors run the monitor
  3376.  thread, and only the monitor thread, at a time-critical priority. This
  3377.  prevents delayed device response because of delays in processing the
  3378.  monitor data stream. See 16.1 Device Monitors for more information.
  3379.  
  3380.  
  3381.  5.2.3  Low Priority Category
  3382.  If you picture the general priority category as a range of priorities, with
  3383.  the force run priority category as a higher range, there is a third range,
  3384.  called the low priority category, that is lower in priority than the
  3385.  general priority category. As a result, threads in this category get CPU
  3386.  service only when no other thread in the other categories needs it. This
  3387.  category is a mirror image of the time-critical priority category in that
  3388.  the system call that sets the thread fixes the priority; OS/2 never changes
  3389.  a low priority.
  3390.       I don't expect the low priority category to be particularly popular.
  3391.  It's in the system primarily because it falls out for free, as a mirror
  3392.  image of the time-critical category. Turnkey systems may want to run some
  3393.  housekeeping processes at this priority. Some users enjoy computing PI,
  3394.  doing cryptographic analysis, or displaying fractal images; these
  3395.  recreations are good candidates for soaking up leftover CPU time. On a more
  3396.  practical level, you could run a program that counts seconds of CPU time
  3397.  and yields a histogram of CPU utilization during the course of a day.
  3398.  
  3399.  
  3400.  5.2.4  Setting Process/Thread Priorities
  3401.  We've discussed at some length the effect of the various priorities, but we
  3402.  haven't discussed how to set these priorities. Because inheritance is an
  3403.  important OS/2 concept, how does a parent's priority affect that of the
  3404.  child? Finally, although we said that priority is a thread issue rather
  3405.  than a process one, we kept bringing up processes anyway. How does all this
  3406.  work?
  3407.       Currently, whenever a thread is created, it inherits the priority of
  3408.  its creator thread. In the case of DosCreateThread, the thread making the
  3409.  call is the creator thread. In the case of thread 1, the thread in the
  3410.  parent process that is making the DosExecPgm call is the creator thread.
  3411.  When a process makes a DosSetPrty call to change the priority of one of its
  3412.  own threads, the new priority always takes effect. When a process uses
  3413.  DosSetPrty to change the priority of another process, only the threads in
  3414.  that other process which have not had their priorities explicitly set from
  3415.  within their own process are changed. This prevents a parent process from
  3416.  inadvertently lowering the priority of, say, a time-critical thread by
  3417.  changing the base priority of a child process.
  3418.       In a future release, we expect to improve this algorithm so that each
  3419.  process has a base priority. A new thread will inherit its creator's base
  3420.  priority. A process's thread priorities that are in the general priority
  3421.  category will all be relative to the process's base priority so that a
  3422.  change in the base priority will raise or lower the priority of all the
  3423.  process's general threads while retaining their relative priority
  3424.  relationships. Threads in the time-critical and low priority categories
  3425.  will continue to be unaffected by their process's base priority.
  3426.  
  3427.  
  3428.  
  3429.  6  The User Interface
  3430.  
  3431.  ───────────────────────────────────────────────────────────────────────────
  3432.  
  3433.  OS/2 contains several important subsystems: the file system, the memory
  3434.  management subsystem, the multitasking subsystem, and the user interface
  3435.  subsystem--the presentation manager. MS-DOS does not define or support a
  3436.  user interface subsystem; each application must provide its own. MS-DOS
  3437.  utilities use a primitive line-oriented interface, essentially unchanged
  3438.  from the interface provided by systems designed to interface with TTYs.
  3439.       OS/2 is intended to be a graphics-oriented operating system, and as
  3440.  such it needs to provide a standard graphical user interface (GUI)
  3441.  subsystem--for several reasons. First, because such systems are complex to
  3442.  create, to expect that each application provide its own is unreasonable.
  3443.  Second, a major benefit of a graphical user interface is that applications
  3444.  can be intermingled. For example, their output windows can share the
  3445.  screen, and the user can transfer data between applications using visual
  3446.  metaphors. If each application had its own GUI package, such sharing would
  3447.  be impossible. Third, a graphical user interface is supposed to make the
  3448.  machine easier to use, but this will be so only if the user can learn one
  3449.  interface that will work with all applications.
  3450.  
  3451.  
  3452.  6.1  VIO User Interface
  3453.  
  3454.  The full graphical user interface subsystem will not ship with OS/2 version
  3455.  1.0, so the initial release will contain the character-oriented VIO/KBD/MOU
  3456.  subsystem (see Chapter 13, The Presentation Manager and VIO, for more
  3457.  details). Although VIO doesn't provide any graphical services, it does
  3458.  allow applications to sidestep VIO and construct their own. The VIO
  3459.  screen group interface is straightforward. When the machine is booted up,
  3460.  the screen displays the screen group list. The user can select an existing
  3461.  screen group or create a new one. From within a screen group, the user can
  3462.  type a magic key sequence to return the screen to the screen group list.
  3463.  Another magic key sequence allows the user to toggle through all existing
  3464.  screen groups. One screen group is identified in the screen group list as
  3465.  the real mode screen group.
  3466.  
  3467.  
  3468.  6.2  The Presentation Manager User Interface
  3469.  
  3470.  The OS/2 presentation manager is a powerful and flexible graphical user
  3471.  interface. It supports such features as windowing, drop-down and pop-up
  3472.  menus, and scroll bars. It works best with a graphical pointing device such
  3473.  as a mouse, but it can be controlled exclusively from the keyboard.
  3474.       The presentation manager employs screen windows to allow multiple
  3475.  applications to use the screen and keyboard simultaneously. Each
  3476.  application uses one or more windows to display its information; the user
  3477.  can size and position each window, overlapping some and perhaps shrinking
  3478.  others to icons. Mouse and keyboard commands change the input focus between
  3479.  windows; this allows the presentation manager to route keystrokes and mouse
  3480.  events to the proper application.
  3481.       Because of its windowing capability, the presentation manager doesn't
  3482.  need to use the underlying OS/2 screen group mechanism to allow the user to
  3483.  switch between running applications. The user starts an application by
  3484.  pointing to its name on a menu display; for most applications the
  3485.  presentation manager creates a new window and assigns it to the new
  3486.  process. Some applications may decline to use the presentation manager's
  3487.  graphical user interface and prefer to take direct control of the display.
  3488.  When such an application is initiated, the presentation manager creates a
  3489.  private screen group for it and switches to that screen group. The user can
  3490.  switch away by entering a special key sequence that brings up a menu which
  3491.  allows the user to select any running program. If the selected program is
  3492.  using the standard presentation manager GUI, the screen is switched to the
  3493.  screen group shared by those programs. Otherwise, the screen is switched to
  3494.  the private screen group that the specified application is using.
  3495.       To summarize, only the real mode application and applications that
  3496.  take direct control of the display hardware need to run in their own screen
  3497.  groups. The presentation manager runs all other processes in a single
  3498.  screen group and uses its windowing facilities to share the screen among
  3499.  them. The user can switch between applications via a special menu; if both
  3500.  the previous and the new application are using the standard interface, the
  3501.  user can switch the focus directly without going though the menu.
  3502.  
  3503.  
  3504.  6.3  Presentation Manager and VIO Compatibility
  3505.  
  3506.  In OS/2 version 1.1 and in all subsequent releases, the presentation
  3507.  manager will replace and superset the VIO interface. Applications that use
  3508.  the character mode VIO interface will continue to work properly as
  3509.  windowable presentation manager applications, as will applications that use
  3510.  the STDIN and STDOUT file handles for interactive I/O. Applications that
  3511.  use the VIO interface to obtain direct access to the graphical display
  3512.  hardware will also be supported; as described above, the presentation
  3513.  manager will run such applications in their own screen group.
  3514.  
  3515.  
  3516.  
  3517.  7  Dynamic Linking
  3518.  
  3519.  ───────────────────────────────────────────────────────────────────────────
  3520.  
  3521.  A central component of OS/2 is dynamic linking. Dynamic links play several
  3522.  critical architectural roles. Before we can discuss them at such an
  3523.  abstract level, however, we need to understand the nuts and bolts of their
  3524.  workings.
  3525.  
  3526.  
  3527.  7.1  Static Linking
  3528.  
  3529.  A good preliminary to the study of dynamic links (called dynlinks, for
  3530.  short) is a review of their relative, static links. Every programmer who
  3531.  has gone beyond interpreter-based languages such as BASIC is familiar with
  3532.  static links. You code a subroutine or a procedure call to a routine that
  3533.  is not present in that compiland (or source file), which we'll call Foo.
  3534.  The missing routine is declared external so that the assembler or compiler
  3535.  doesn't flag it as an undefined symbol. At linktime, you present the linker
  3536.  with the .OBJ file that you created from your compiland, and you also
  3537.  provide a .OBJ file1 that contains the missing routine Foo. The linker
  3538.  combines the compilands into a final executable image--the .EXE file--that
  3539.  contains the routine Foo as well as the routines that call it. During the
  3540.  combination process, the linker adjusts the calls to Foo, which had been
  3541.  undefined external references, to point to the place in the .EXE file where
  3542.  the linker relocated the Foo routine. This process is diagramed in Figure
  3543.  7-1.
  3544.  
  3545.  
  3546.        .OBJ                            .LIB
  3547.  ┌───────────────┐               ┌───────────────┐
  3548.  │               │               │  Foo  ──────  │
  3549.  │   Call Foo    │               │       ──────  │
  3550.  │               │               │       ──────  │
  3551.  └───────┬───────┘               └───────┬───────┘      .EXE file
  3552.          │                               │          ┌───────────────┐
  3553.          └─────────┐           ┌─────────┘          │               │
  3554.                    │     +     │                    │   Call ──────┼─┐
  3555.                  ┌─────────────┐                  │               │ │
  3556.                  │               │                  │               │ │
  3557.                  │    Linker     ├─────────────────│               │ │
  3558.                  │               │                  │               │ │
  3559.                  └───────────────┘                  │               │ │
  3560.                                                     │ ────── ──────┼─┘
  3561.                                                     │ ──────        │
  3562.                                                     │ ──────        │
  3563.                                                     └───────────────┘
  3564.  
  3565.  Figure 7-1.  Static linking.
  3566.  
  3567.  
  3568.       In other words, with static linking you can write a program in pieces.
  3569.  You can compile one piece at a time by having it refer to the other pieces
  3570.  as externals. A program called a linker or a link editor combines these
  3571.  pieces into one final .EXE image, fixing up the external references (that
  3572.  is, references between one piece and another) that those pieces contain.
  3573.       Writing and compiling your program piecemeal is useful, but the
  3574.  primary advantage of static linking is that you can use it to reference a
  3575.  standard set of subroutines--a subroutine library--without compiling or
  3576.  even possessing the source code for those subroutines. Nearly all high-
  3577.  level language packages come with one or more standard runtime libraries
  3578.  that contain various useful subroutines that the compiler can call
  3579.  implicitly and that the programmer can call explicitly. Source for these
  3580.  runtime libraries is rarely provided; the language supplier provides only
  3581.  the .OBJ object files, typically in library format.
  3582.       To summarize, in traditional static linking the target code (that is,
  3583.  the external subroutine) must be present at linktime and is built into the
  3584.  final .EXE module. This makes the .EXE file larger, naturally, but more
  3585.  important, the target code can't be changed or upgraded without relinking
  3586.  to the main program's .OBJ files. Because the personal computer field is
  3587.  built on commercial software whose authors don't release source or .OBJ
  3588.  files, this relinking is out of the question for the typical end user.
  3589.  Finally, the target code can't be shared among several (different)
  3590.  applications that use the same library routines. This is true for two
  3591.  reasons. First, the target code was relocated differently by the linker for
  3592.  each client; so although the code remains logically the same for each
  3593.  application, the address components of the binary instructions are
  3594.  different in each .EXE file. Second, the operating system has no way of
  3595.  knowing that these applications are using the same library, and it has no
  3596.  way of knowing where that library is in each .EXE file. Therefore, it can't
  3597.  avoid having duplicate copies of the library in memory.
  3598.  
  3599.  
  3600.  7.2  Loadtime Dynamic Linking
  3601.  
  3602.  The mechanical process of loadtime dynamic linking is the same as that of
  3603.  static linking. The programmer makes an external reference to a subroutine
  3604.  and at linktime specifies a library file (or a .OBJ file) that defines the
  3605.  reference. The linker produces a .EXE file that OS/2 then loads and
  3606.  executes. Behind the scenes, however, things are very much different.
  3607.       Step 1 is the same for both kinds of linking. The external reference
  3608.  is compiled or assembled, resulting in a .OBJ file that contains an
  3609.  external reference fixup record. The assembler or compiler doesn't know
  3610.  about dynamic links; the .OBJ file that an assembler or a compiler produces
  3611.  may be used for static links, dynamic links, or, more frequently, a
  3612.  combination of both (some externals become dynamic links, others become
  3613.  static links).
  3614.       In static linking, the linker finds the actual externally referenced
  3615.  subroutine in the library file. In dynamic linking, the linker finds a
  3616.  special record that defines a module name string and an entry point name
  3617.  string. For example, in our hypothetical routine Foo, the library file
  3618.  contains only these two name strings, not the code for Foo itself. (The
  3619.  entry point name string doesn't have to be the name by which programs
  3620.  called the routine.) The resultant .EXE file doesn't contain the code for
  3621.  Foo; it contains a special dynamic link record that specifies these module
  3622.  and entry point names for Foo. This is illustrated in Figure 7-2.
  3623.  
  3624.  
  3625.  ┌───────────────┐         ┌───────────────┐     ┌───────────────┐
  3626.  │     .OBJ      │ Extern  │     .LIB      │     │     .EXE      │
  3627.  │               ├────────│ Foo:          │     │               │
  3628.  │   Call Foo    │         │ Module dlpack │     │      ┌────┐   │
  3629.  │               │         │ entry Foo     │     │ Call │????├───┼──┐
  3630.  └───────┬───────┘         └───────┬───────┘     │      └────┘   │  │
  3631.          │                         │         ┌──├───────────────┤  │
  3632.          │                         │         │   │ Reference to  │─┘
  3633.          └─────────  +  ─────────┘         │   │ dlpack: Foo   │
  3634.                  ┌─────────┐                 │   └───────────────┘
  3635.                  │  Link   │                 │
  3636.                  └────┬────┘                 │
  3637.                       │                      │
  3638.                       └──────────────────────┘
  3639.  
  3640.  Figure 7-2.  Dynamic linking.
  3641.  
  3642.  
  3643.       When this .EXE file is run, OS/2 loads the code in the .EXE file into
  3644.  memory and discovers the dynamic link record(s). For each dynamic link
  3645.  module that is named, OS/2 locates the code in the system's dynamic link
  3646.  library directory and loads it into memory (unless the module is already in
  3647.  use; see below). The system then links the external references in the
  3648.  application to the addresses of the called entry points. This process is
  3649.  diagramed in Figure 7-3.
  3650.  
  3651.  
  3652.  ┌───────────────┐                                    ┌───────────────┐
  3653.  │     .EXE      │                                    │     .DLL      │
  3654.  │               │                                    │  dlpack: Foo  │
  3655.  │     Call ─────┼──┐                                 │    ──────     │
  3656.  ├───────────────┤  │                                 │    ──────     │
  3657.  │  dlpack: Foo  │─┘ Disk                            │    ──────     │
  3658.  └───────┬───────┘                                    └───────┬───────┘
  3659.          │                                                    │
  3660.          │                                 ┌──────────────────┘
  3661.          │                                 │
  3662.  ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
  3663.          │                                 │
  3664.  ┌──────────────┐                 ┌──────────────┐
  3665.  │               │      RAM        │               │
  3666.  │               │      Fixup      │    ──────     │
  3667.  │               │     by OS/2     │    ──────     │
  3668.  │     Call  ────┼────────────────│    ──────     │
  3669.  │               │                 │               │
  3670.  └───────────────┘                 └───────────────┘
  3671.  
  3672.  Figure 7-3.  Loadtime dynlink fixups.
  3673.  
  3674.  
  3675.  To summarize, instead of linking in the target code at linktime, the linker
  3676.  places a module name and an entry point name into the .EXE file. When the
  3677.  program is loaded (that is, executed), OS/2 locates the target code, loads
  3678.  it, and does the necessary linking. Although all we're doing is postponing
  3679.  the linkage until loadtime, this technique has several important
  3680.  ramifications. First, the target code is not in the .EXE file but in a
  3681.  separate dynamic link library (.DLL) file. Thus, the .EXE file is smaller
  3682.  because it contains only the name of the target code, not the code itself.
  3683.  You can change or upgrade the target code at any time simply by replacing
  3684.  this .DLL file. The next time a referencing application is loaded,2 it is
  3685.  linked to the new version of the target code. Finally, having the target
  3686.  code in a .DLL file paves the way for automatic code sharing. OS/2 can
  3687.  easily understand that two applications are using the same dynlink code
  3688.  because it loaded and linked that code, and it can use this knowledge to
  3689.  share the pure segments of that dynlink package rather than loading
  3690.  duplicate copies.
  3691.       A final advantage of dynamic linking is that it's totally invisible to
  3692.  the user, and it can even be invisible to the programmer. You need to
  3693.  understand dynamic linking to create a dynamic link module, but you can use
  3694.  one without even knowing that it's not an ordinary static link. The one
  3695.  disadvantage of dynamic linking is that programs sometimes take longer to
  3696.  load into memory than do those linked with static linking. The good news
  3697.  about dynamic linking is that the target code(s) are separate from the main
  3698.  .EXE file; this is also the bad news. Because the target code(s) are
  3699.  separate from the main .EXE file, a few more disk operations may be
  3700.  necessary to load them.
  3701.       The actual performance ramifications depend on the kind of dynlink
  3702.  module that is referenced and whether this .EXE file is the first to
  3703.  reference the module. This is discussed in more detail in 7.11
  3704.  Implementation Details.
  3705.       Although this discussion has concentrated on processes calling dynlink
  3706.  routines, dynlink routines can in fact be called by other dynlink routines.
  3707.  When OS/2 loads a dynlink routine in response to a process's request, it
  3708.  examines that routine to see if it has any dynlink references of its own.
  3709.  Any such referenced dynlink routines are also loaded and so on until no
  3710.  unsatisfied dynlink references remain.
  3711.  
  3712.  
  3713.  7.3  Runtime Dynamic Linking
  3714.  
  3715.  
  3716.  
  3717.  
  3718.  
  3719.  works exactly like loadtime dynamic linking except that the process creates
  3720.  the dynlink module and entry point names at runtime and then passes them to
  3721.  OS/2 so that OS/2 can locate and load the specified dynlink code.
  3722.       Runtime linking takes place in four steps.
  3723.  
  3724.       1.  The process issues a DosLoadModule call to tell OS/2 to locate and
  3725.           load the dynlink code into memory.
  3726.  
  3727.       2.  The DosGetProcAddr call is used to obtain the addresses of the
  3728.           routines that the process wants to call.
  3729.  
  3730.       3.  The process calls the dynlink library entry points by means of an
  3731.           indirect call through the address returned by DosGetProcAddr.
  3732.  
  3733.       4.  When the process has no more use for the dynlink code, it can call
  3734.           DosFreeModule to release the dynlink code. After this call, the
  3735.           process will still have the addresses returned by DosGetProcAddr,
  3736.           but they will be illegal addresses; referencing them will cause a
  3737.           GP fault.
  3738.  
  3739.       Runtime dynamic links are useful when a program knows that it will
  3740.  want to call some dynlink routines but doesn't know which ones. For
  3741.  example, a charting program may support four plotters, and it may want to
  3742.  use dynlink plotter driver packages. It doesn't make sense for the
  3743.  application to contain loadtime dynamic links to all four plotters because
  3744.  only one will be used and the others will take up memory and swap space.
  3745.  Instead, the charting program can wait until it learns which plotter is
  3746.  installed and then use the runtime dynlink facility to load the appropriate
  3747.  package. The application need not even call DosLoadModule when it
  3748.  initializes; it can wait until the user issues a plot command before it
  3749.  calls DosLoadModule, thereby reducing memory demands on the system.
  3750.       The application need not even be able to enumerate all the modules or
  3751.  entry points that may be called. The application can learn the names of the
  3752.  dynlink modules from another process or by looking in a configuration file.
  3753.  This allows the user of our charting program, for example, to install
  3754.  additional plotter drivers that didn't even exist at the time that the
  3755.  application was written. Of course, in this example the calling sequences
  3756.  of the dynlink plotter driver must be standardized, or the programmer must
  3757.  devise a way for the application to figure out the proper way to call these
  3758.  newly found routines.
  3759.       Naturally, a process is not limited to one runtime dynlink module;
  3760.  multiple calls to DosLoadModule can be used to link to several dynlink
  3761.  modules simultaneously. Regardless of the number of modules in use,
  3762.  DosFreeModule should be used if the dynlink module will no longer be used
  3763.  and the process intends to continue executing. Issuing DosFreeModules is
  3764.  unnecessary if the process is about to terminate; OS/2 releases all dynlink
  3765.  modules at process termination time.
  3766.  
  3767.  
  3768.  7.4  Dynlinks, Processes, and Threads
  3769.  
  3770.  Simply put, OS/2 views dynlinks as a fancy subroutine package. Dynlinks
  3771.  aren't processes, and they don't own any resources. A dynlink executes only
  3772.  because a thread belonging to a client process called the dynlink code. The
  3773.  dynlink code is executing as the client thread and process because, in the
  3774.  eyes of the system, the dynlink is merely a subroutine that process has
  3775.  called. Before the client process can call a dynlink package, OS/2 ensures
  3776.  that the dynlink's segments are in the address space of the client. No ring
  3777.  transition or context switching overhead occurs when a client calls a
  3778.  dynlink routine; the far call to a dynlink entry point is just that--an
  3779.  ordinary far call to a subroutine in the process's address space.
  3780.       One side effect is that dynlink calls are very fast; little CPU time
  3781.  is spent getting to the dynlink package. Another side effect is no
  3782.  separation between a client's segments and a dynlink package's segments3
  3783.  because segments belong to processes and only one process is running both
  3784.  the client and the dynlink code. The same goes for file handles,
  3785.  semaphores, and so on.
  3786.  
  3787.  
  3788.  7.5  Data
  3789.  
  3790.  The careful reader will have noticed something missing in this discussion
  3791.  of dynamic linking: We've said nothing about how to handle a dynlink
  3792.  routine's data. Subroutines linked with static links have no problem with
  3793.  having their own static data; when the linker binds the external code with
  3794.  the main code, it sees how much static data the external code needs and
  3795.  allocates the necessary space in the proper data segment(s). References
  3796.  that the external code makes to its data are then fixed up to point
  3797.  to the proper location. Because the linker is combining all the .OBJ
  3798.  files into a .EXE file, it can easily divide the static data segment(s)
  3799.  among the various compilands.
  3800.       This technique doesn't work for dynamic link routines because their
  3801.  code and therefore their data requirements aren't present at linktime. It's
  3802.  possible to extend the special dynlink .OBJ file to describe the amount of
  3803.  static data that the dynlink package will need, but it won't work.4
  3804.  Because the main code in each application uses different amounts of static
  3805.  data, the data area reserved for the dynlink package would end up at a
  3806.  different offset in each .EXE file that was built. When these .EXE files
  3807.  were executed, the one set of shared dynlink code segments would need to
  3808.  reference the data that resides at different addresses for each different
  3809.  client. Relocating the static references in all dynlink code modules at
  3810.  each occurrence of a context switch is clearly out of the question.
  3811.       An alternative to letting dynamic link routines have their own static
  3812.  data is to require that their callers allocate the necessary data areas and
  3813.  pass pointers to them upon every call. We easily rejected this scheme: It's
  3814.  cumbersome; call statements must be written differently if they're for a
  3815.  dynlink routine; and, finally, this hack wouldn't support subsystems, which
  3816.  are discussed below.
  3817.       Instead, OS/2 takes advantage of the segmented architecture of the
  3818.  80286. Each dynamic link routine can use one or more data segments to hold
  3819.  its static data. Each client process has a separate set of these segments.
  3820.  Because these segments hold only the dynlink routine's data and none of the
  3821.  calling process's data, the offsets of the data items within that segment
  3822.  will be the same no matter which client process is calling the dynlink
  3823.  code. All we need do to solve our static data addressability problem is
  3824.  ensure that the segment selectors of the dynlink routine's static data
  3825.  segments are the same for each client process.
  3826.       OS/2 ensures that the dynlink library's segment selectors are the same
  3827.  for each client process by means of a technique called the disjoint LDT
  3828.  space. I won't attempt a general introduction to the segmented architecture
  3829.  of the 80286, but a brief summary is in order. Each process in 80286
  3830.  protect mode can have a maximum of 16,383 segments. These segments are
  3831.  described in two tables: the LDT (Local Descriptor Table) and the GDT
  3832.  (Global Descriptor Table). An application can't read from or write to these
  3833.  tables. OS/2 manages them, and the 80286 microprocessor uses their contents
  3834.  when a process loads selectors into its segment registers.
  3835.       In practice, the GDT is not used for application segments, which
  3836.  leaves the LDT 8192 segments--or, more precisely, 8192 segment selectors,
  3837.  which OS/2 can set up to point to memory segments. The 80286 does not
  3838.  support efficient position-independent code, so 80286 programs contain
  3839.  within them, as part of the instruction stream, the particular segment
  3840.  selector needed to access a particular memory location, as well as an
  3841.  offset within that segment. This applies to both code and data
  3842.  references.
  3843.       When OS/2 loads a program into memory, the .EXE file describes the
  3844.  number, type, and size of the program's segments. OS/2 creates these
  3845.  segments and allocates a selector for each from the 8192 possible LDT
  3846.  selectors. There isn't any conflict with other processes in the system, at
  3847.  this point, because each process has its own LDT and its own private set of
  3848.  8192 LDT selectors. After OS/2 chooses a selector for each segment, both
  3849.  code and data, it uses a table of addresses provided in the .EXE file to
  3850.  relocate each segment reference in the program, changing the place holder
  3851.  value put there by the linker into the proper segment selector value. OS/2
  3852.  never combines or splits segments, so it never has to relocate the offset
  3853.  part of addresses, only the segment parts. Address offsets are more common
  3854.  than segment references. Because the segment references are relatively few,
  3855.  this relocation process is not very time-consuming.
  3856.       If OS/2 discovers that the process that it's loading references a
  3857.  dynlink routine--say, our old friend Foo--the situation is more complex.
  3858.  For example, suppose that the process isn't the first caller of Foo; Foo is
  3859.  already in memory and already relocated to some particular LDT slots in the
  3860.  LDT of the earlier client of Foo. OS/2 has to fill in those same slots in
  3861.  the new process's LDT with pointers to Foo; it can't assign different LDT
  3862.  slots because Foo's code and data have already been relocated to the
  3863.  earlier process's slots. If the new process is already using Foo's slot
  3864.  numbers for something else, then we are in trouble. This is a problem with
  3865.  all of Foo's segments, both data segments and code segments.
  3866.       This is where the disjoint LDT space comes in. OS/2 reserves many of
  3867.  each process's LDT slots5 for the disjoint space. The same slot numbers are
  3868.  reserved in every process's LDT. When OS/2 allocates an LDT selector for a
  3869.  memory segment that may be shared between processes, it allocates an entry
  3870.  from the disjoint LDT space. After a selector is allocated, that same slot
  3871.  in all other LDTs in the system is reserved. The slot either remains empty
  3872.  (that is, invalid) or points to this shared segment; it can have no other
  3873.  use. This guarantees that a process that has been running for hours and
  3874.  that has created dozens of segments can still call DosLoadModule to get
  3875.  access to a dynlink routine; OS/2 will find that the proper slots in this
  3876.  process's LDT are ready and waiting. The disjoint LDT space is used for all
  3877.  shared memory objects, not just dynlink routines. Shared memory data
  3878.  segments are also allocated from the disjoint LDT space. A process's code
  3879.  segments are not allocated in the disjoint LDT space, yet they can still be
  3880.  shared.6 Figure 7-4 illustrates the disjoint LDT concept. Bullets in the
  3881.  shaded selectors denote reserved but invalid disjoint selectors. These are
  3882.  reserved in case that process later requests access to the shared memory
  3883.  segments that were assigned those disjoint slots. Only process A is using
  3884.  the dynlink package DLX, so its assigned disjoint LDT slots are reserved
  3885.  for it in Process B's LDT as well as in the LDT of all other processes in
  3886.  the system. Both processes are using the dynlink package DLY.
  3887.  
  3888.  
  3889.                   Process A                         Process B
  3890.  Segment table                     Segment table
  3891.   (LDT) for A                       (LDT) for B
  3892.    ┌───────┐   ┌────────┐ Process    ┌───────┐   ┌────────┐
  3893.    │       ├───┤        │ A's        │       │ ┌─┤        │ Process
  3894.    ├───────┤   │        │ segments   ├───────┤ │ │        │ B's
  3895.    │░░░░░░░│   └────────┘ ┌────────┐ │░░░░░░░│ │ └────────┘ segments
  3896.    ├───────┤ ┌────────────┤        │ ├───────┤ │            ┌────────┐
  3897.    │       │ │            │        │ │       ├─┘            │        │
  3898.    ├───────┤ │ ┌────────┐ └────────┘ ├───────┤    ┌─────────┤        │
  3899.    │░░░░░░░│ │┌┤        │            │░░░░░░░│    │         └────────┘
  3900.    ├───────┤ │││        │            ├───────┤    │
  3901.    │       ├─┘│└────────┘            │       ├────┘
  3902.    ├───────┤  │                      ├───────┤
  3903.    │░░░░░░░│  │                      │░░░░░░░│
  3904.    ├───────┤  │                      ├───────┤
  3905.    │       ├──┘                      │       │
  3906.    ├───────┤                         ├───────┤   ┌────────┐
  3907.    │░░░░░░│                         │░░░░░░░├───┤        │ Dynlink
  3908.    ├───────┤                         ├───────┤   │        │ DLZ's
  3909.    │       │                         │       │   └────────┘ segments
  3910.    ├───────┤                         ├───────┤              ┌────────┐
  3911.    │░░░░░░│                         │░░░░░░░├──────────────┤        │
  3912.    ├───────┤                         ├───────┤              │        │
  3913.    │       │                         │       │              └────────┘
  3914.    ├───────┤   ┌────────┐ Dynlink    ├───────┤
  3915.    │░░░░░░░├───┤        │ DLX's      │░░░░░░│
  3916.    ├───────┤   │        │ segments   ├───────┤
  3917.    │       │   └────────┘ ┌────────┐ │       │
  3918.    ├───────┤       ┌──────┤        │ ├───────┤
  3919.    │░░░░░░░├───────┘      │        │ │░░░░░░│
  3920.    ├───────┤   ┌────────┐ └────────┘ ├───────┤
  3921.    │       │ ┌─┤        │            │       │
  3922.    ├───────┤ │ │        │            ├───────┤
  3923.    │░░░░░░░├─┘ └────────┘            │░░░░░░│
  3924.    ├───────┤                         ├───────┤
  3925.    │       │                         │       │
  3926.    ├───────┤                         ├───────┤
  3927.    │░░░░░░░│                         │░░░░░░░│
  3928.    ├───────┤                         ├───────┤
  3929.    │       │                         │       │
  3930.    ├───────┤   ┌────────┐            ├───────┤   ┌────────┐
  3931.    │░░░░░░░├───┤        │ Dynlink    │░░░░░░░├───┤        │ Dynlink
  3932.    ├───────┤   │        │ DLY's      ├───────┤   │        │ DLY's
  3933.    │       │   └────────┘ segments   │       │   └────────┘ segments
  3934.    ├───────┤              ┌────────┐ ├───────┤              ┌────────┐
  3935.    │░░░░░░░├──────────────┤        │ │░░░░░░░├──────────────┤        │
  3936.    ├───────┤              │        │ ├───────┤              │        │
  3937.    │       │              └────────┘ │       │              └────────┘
  3938.    └───────┘                         └───────┘
  3939.  
  3940.  Figure 7-4.  The disjoint LDT space.
  3941.  
  3942.  
  3943.  7.5.1  Instance Data
  3944.  OS/2 supports two types of data segments for dynlink routines--instance
  3945.  and global. Instance data segments hold data specific to each instance of
  3946.  the dynlink routine. In other words, a dynlink routine has a separate set
  3947.  of instance data segments for each process using it. The dynlink code has
  3948.  no difficulty addressing its data; the code can reference the data segment
  3949.  selectors as immediate values. The linker and OS/2's loader conspire so
  3950.  that the proper selector value is in place when the code executes.
  3951.       The use of instance data segments is nearly invisible both to the
  3952.  client process and to the dynlink code. The client process simply calls the
  3953.  dynlink routine, totally unaffected by the presence or absence of the
  3954.  routine's instance data segment(s). A dynlink routine can even return
  3955.  addresses of items in its data segments to the client process. The client
  3956.  cannot distinguish between a dynlink routine and a statically linked one.
  3957.  Likewise, the code that makes up the dynlink routine doesn't need to do
  3958.  anything special to use its instance data segments. The dynlink code was
  3959.  assembled or compiled with its static data in one or more segments; the
  3960.  code itself references those segments normally. The linker and OS/2 handle
  3961.  all details of allocating the disjoint LDT selectors, loading the segments,
  3962.  fixing up the references, and so on.
  3963.       A dynlink routine that uses only instance data segments (or no data
  3964.  segments at all) can be written as a single client package, as would be a
  3965.  statically linked subroutine. Although such a dynlink routine may have
  3966.  multiple clients, the presence of multiple clients is invisible to the
  3967.  routine itself. Each client has a separate copy of the instance data
  3968.  segment(s). When a new client is created, OS/2 loads virgin copies of the
  3969.  instance data segments from the .DLL file. The fact that OS/2 is sharing
  3970.  the pure code segments of the routine has no effect on the operation of the
  3971.  routine itself.
  3972.  
  3973.  
  3974.  7.5.2  Global Data
  3975.  The second form of data segment available to a dynlink routine is a global
  3976.  data segment. A global data segment, as the name implies, is not duplicated
  3977.  for each client process. There is only one copy of each dynlink module's
  3978.  global data segment(s); each client process is given shared access to that
  3979.  segment. The segment is loaded only once--when the dynlink package is first
  3980.  brought into memory to be linked with its first client process. Global data
  3981.  segments allow a dynlink routine to be explicitly aware of its multiple
  3982.  clients because changes to a global segment made by calls from one client
  3983.  process are visible to the dynlink code when called from another client
  3984.  process. Global data segments are provided to support subsystems, which are
  3985.  discussed later. Figure 7-5 illustrates a dynlink routine with both
  3986.  instance and global data segments.
  3987.  
  3988.  
  3989.  ┌─────────────────────────────────────────────┐
  3990.  │                                             │
  3991.  │               Code Segment(s)               │
  3992.  │                                             │
  3993.  └─────────────────────────────────────────────┘
  3994.  ┌──────────────┐               ┌──────────────┐
  3995.  │              │              ┌┴─────────────┐│
  3996.  │    Global    │             ┌┴─────────────┐││
  3997.  │     data     │             │              │││
  3998.  │  segment(s)  │             │   Instance   │││
  3999.  │              │             │     data     │││
  4000.  └──────────────┘             │  segment(s)  │├┘
  4001.                               │              ├┘
  4002.                               └──────────────┘
  4003.  
  4004.  Figure 7-5.  Dynlink segments.
  4005.  
  4006.  
  4007.  7.6  Dynamic Link Packages As Subroutines
  4008.  
  4009.  Dynamic link subroutines (or packages) generally fall into two categories--
  4010.  subroutines and subsystems. As we discussed earlier, a dynamic link
  4011.  subroutine is written and executes in much the same way as a statically
  4012.  linked subroutine. The only difference is in the preparation of the dynamic
  4013.  link library file, which contains the actual subroutines, and in the
  4014.  preparation of the special .OBJ file, to which client programs can link.
  4015.  During execution, both the dynlink routines and the client routines can use
  4016.  their own static data freely, and they can pass pointers to their data
  4017.  areas back and forth to each other. The only difference between static
  4018.  linking and dynamic linking, in this model, is that the dynlink routine
  4019.  cannot reference any external symbols that the client code defines, nor can
  4020.  the client externally reference any dynlink package symbols other than the
  4021.  module entry points. Figure 7-6 illustrates a dynamic link routine being
  4022.  used as a subroutine. The execution environment is nearly identical to that
  4023.  of a traditional statically linked subroutine; the client and the
  4024.  subroutine each reference their own static data areas, all of which are
  4025.  contained in the process's address space. Note that a dynlink package can
  4026.  reference the application's data and the application can reference the
  4027.  dynlink package's data, but only if the application or the dynlink package
  4028.  passes a pointer to its data to the other.
  4029.  
  4030.  
  4031.                           Process address space
  4032.  ┌─────────────────────────────────────────────────────────────────────┐
  4033.  │            far calls                           far calls            │
  4034.  │ ┌────────────┐   ┌────────────┐ far ┌────────────┐   ┌────────────┐ │
  4035.  │ │            ├──│            │calls│  Dynlink   ├──│  Dynlink   │ │
  4036.  │ │  APP code  │   │  APP code  ├────│   code     │   │   code     │ │
  4037.  │ │ segment #1 │──┤ segment #n │     │ segment #1 │──┤ segment #n │ │
  4038.  │ └──┬───────┬─┘   └─┬───────┬──┘     └──┬───────┬─┘   └─┬───────┬──┘ │
  4039.  │    │     ┌─┼───────┘ data  │           │     ┌─┼───────┘ data  │    │
  4040.  │    │     │ │     references│           │     │ │     references│    │
  4041.  │    │     │ │               │           │     │ │               │    │
  4042.  │    │     │ │               │           │     │ │               │    │
  4043.  │    │     │ └─────────┐     │           │     │ └─────────┐     │    │
  4044.  │ ┌──────────┐   ┌──────────┐     ┌──────────┐   ┌──────────┐ │
  4045.  │ │            │   │            │     │  Dynlink   │   │  Dynlink   │ │
  4046.  │ │  APP data  │   │  APP data  │     │   data     │   │   data     │ │
  4047.  │ │ segment #1 │   │ segment #n │     │ segment #1 │   │ segment #n │ │
  4048.  │ └────────────┘   └────────────┘     └────────────┘   └────────────┘ │
  4049.  └─────────────────────────────────────────────────────────────────────┘
  4050.  
  4051.  Figure 7-6.  Dynamic link routines as subroutines.
  4052.  
  4053.  
  4054.  7.7  Subsystems
  4055.  
  4056.  The term dynlink subsystems refers to the design and intended function of a
  4057.  particular style of dynlink package and is somewhat artificial. Although
  4058.  OS/2 provides special features to help support subsystems, OS/2 does not
  4059.  actually classify dynlink modules as subroutines or subsystems; subsystem
  4060.  is merely a descriptive term.
  4061.       The term subsystem refers to a dynlink module that provides a set of
  4062.  services built around a resource.7 For example, OS/2's VIO dynlink entry
  4063.  points are considered a dynlink subsystem because they provide a set of
  4064.  services to manage the display screen. A subsystem usually has to manage a
  4065.  limited resource for an effectively unlimited number of clients; VIO does
  4066.  this, managing a single physical display controller and a small number of
  4067.  screen groups for an indefinite number of clients.
  4068.       Because subsystems generally manage a limited resource, they have one
  4069.  or more global data segments that they use to keep information about the
  4070.  state of the resource they're controlling; they also have buffers, flags,
  4071.  semaphores, and so on. Per-client work areas are generally kept in instance
  4072.  data segments; it's best to reserve the global data segment(s) for global
  4073.  information. Figure 7-7 illustrates a dynamic link routine being used as a
  4074.  subsystem. A dynlink subsystem differs from a dynlink being used as a
  4075.  subroutine only by the addition of a static data segment.
  4076.  
  4077.  
  4078.                           Process address space
  4079.  ┌─────────────────────────────────────────────────────────────────────┐
  4080.  │            far calls                           far calls            │
  4081.  │ ┌────────────┐   ┌────────────┐ far ┌────────────┐   ┌────────────┐ │
  4082.  │ │            ├──│            │calls│  Dynlink   ├──│  Dynlink   │ │
  4083.  │ │  APP code  │   │  APP code  ├────│   code     │   │   code     │ │
  4084.  │ │ segment #1 │──┤ segment #n │     │ segment #1 │──┤ segment #n │ │
  4085.  │ └──┬───────┬─┘   └─┬───────┬──┘     └──┬───────┬─┘   └─┬───────┬──┘ │
  4086.  │    │     ┌─┼───────┘ data  │           │     ┌─┼───────┘ data  │    │
  4087.  │    │     │ │     references│           │     │ │     references│    │
  4088.  │    │     │ │               │           │     │ │               │    │
  4089.  │    │     │ │               │           │     │ │               │    │
  4090.  │    │     │ └─────────┐     │           │     │ └─────────┐     │    │
  4091.  │ ┌══════════╗   ┌══════════╗     ┌──────────┐   ╓───┴─────┴──┐ │
  4092.  │ │            ║   │            ║     │  global    │   ║ instance   │ │
  4093.  │ │  APP data  ║   │  APP data  ║     │   data     │   ║   data     │ │
  4094.  │ │ segment #1 ║   │ segment #n ║     │ segment    │   ║ segment    │ │
  4095.  │ └────────────╜   └────────────╜     └────────────┘   ╚════════════┘ │
  4096.  └─────────────────────────────────────────────────────────────────────┘
  4097.  
  4098.  Figure 7-7.  Dynamic link routines as subsystems.
  4099.  
  4100.  
  4101.  7.7.1  Special Subsystem Support
  4102.  Two OS/2 features are particularly valuable to subsystems: global data
  4103.  segments (which we've already discussed) and special client initialization
  4104.  and termination support. Clearly, if a subsystem is going to manage a
  4105.  resource, keeping track of its clients in a global data segment, it needs
  4106.  to know when new clients arrive and when old clients terminate. The simple
  4107.  dynlink subroutine model doesn't provide this information in a reliable
  4108.  fashion. A subsystem undoubtedly has initialize and terminate entry points,
  4109.  but client programs may terminate without having called a subsystem's
  4110.  terminate entry point. Such a failure may be an error on the part of the
  4111.  client, but the system architecture decrees that errors should be
  4112.  localized; it's not acceptable for a bug in a client process to be able to
  4113.  hang up a subsystem and thus all its clients as well.
  4114.       The two forms of subsystem initialization are global and instance. A
  4115.  subsystem can specify either service but not both. If global initialization
  4116.  is specified, the initialization entry point is called only once per
  4117.  activation of the subsystem. When the subsystem dynlink package is first
  4118.  referenced, OS/2 allocates the subsystem's global data segment(s), taking
  4119.  their initial values from the .DLL file. OS/2 then calls the subsystem's
  4120.  global initialization entry point so that the module can do its one-time
  4121.  initialization. The thread that is used to call the initialization entry
  4122.  point belongs to that first client process,8 so the first client's instance
  4123.  data segments are also set up and may be used by the global initialization
  4124.  process. This means that although the dynlink subsystem is free to open
  4125.  files, read and write their contents, and close them again, it may not open
  4126.  a handle to a file, store the handle number in a global data segment, and
  4127.  expect to use that handle in the future.
  4128.       Remember, subsystems don't own resources; processes own resources.
  4129.  When a dynlink package opens a file, that file is open only for that one
  4130.  client process. That handle has meaning only when that particular client is
  4131.  calling the subsystem code. If a dynlink package were to store process A's
  4132.  handle number in a global data segment and then attempt to do a read from
  4133.  that handle when running as process B, at best the read would fail with
  4134.  "invalid handle"; at worst some unrelated file of B's would be molested.
  4135.  And, of course, when client process A eventually terminates, the handle
  4136.  becomes invalid for all clients.
  4137.       The second form of initialization is instance initialization. The
  4138.  instance initialization entry point is called in the same way as the global
  4139.  initialization entry point except that it is called for every new client
  4140.  when that client first attaches to the dynlink package. Any instance data
  4141.  segments that exist will already be allocated and will have been given
  4142.  their initial values from the .DLL file. The initialization entry point for
  4143.  a loadtime dynlink is called before the client's code begins executing. The
  4144.  initialization entry point for a runtime dynlink is called when the client
  4145.  calls the DosLoadModule function. A dynlink package may not specify both
  4146.  global and instance initialization; if it desires both, it should specify
  4147.  instance initialization and use a counter in one of its global data
  4148.  segments to detect the first instance initialization.
  4149.       Even more important than initialization control is termination
  4150.  control. In its global data area, a subsystem may have records, buffers, or
  4151.  semaphores on behalf of a client process. It may have queued-up requests
  4152.  from that client that it needs to purge when the client terminates. The
  4153.  dynlink package need not release instance data segments; because these
  4154.  belong to the client process, they are destroyed when the client
  4155.  terminates. The global data segments themselves are released if this is the
  4156.  dynlink module's last client, so the module may want to take this last
  4157.  chance to update a log file, release a system semaphore, and so on.
  4158.       Because a dynlink routine runs as the calling client process, it could
  4159.  use DosSetSigHandler to intercept the termination signal. This should never
  4160.  be done, however, because the termination signal is not activated for all
  4161.  causes of process termination. For example, if the process calls DosExit,
  4162.  the termination signal is not sent. Furthermore, there can be only one
  4163.  handler per signal type per process. Because client processes don't and
  4164.  shouldn't know what goes on inside a dynlink routine, the client process
  4165.  and a dynlink routine may conflict in the use of the signal. Such a
  4166.  conflict may also occur between two dynlink packages.
  4167.       Using DosExitList service prevents such a collision. DosExitList
  4168.  allows a process to specify one or more subroutine addresses that will be
  4169.  called when the process terminates. Addresses can be added to and removed
  4170.  from the list. DosExitList is ideally suited for termination control. There
  4171.  can be many such addresses, and the addresses are called under all
  4172.  termination conditions. Both the client process and the subsystem dynlinks
  4173.  that it calls can have their own termination routine or routines.
  4174.  DosExitList is discussed in more detail in 16.2 Data Integrity.
  4175.  
  4176.  
  4177.  7.8  Dynamic Links As Interfaces to Other Processes
  4178.  
  4179.  Earlier, I mentioned that dynlink subsystems have difficulty dealing with
  4180.  resources--other than global memory--because resource ownership and access
  4181.  are on a per-process basis. Life as a dynlink subsystem can be
  4182.  schizophrenic. Which files are open, which semaphores are owned and so on
  4183.  depends on which client is running your code at the moment. Global memory
  4184.  is different; it's the one resource that all clients own jointly. The
  4185.  memory remains as long as the client count doesn't go to zero.
  4186.       One way to deal with resource issues is for a dynlink package to act
  4187.  as a front end for a server process. During module initialization, the
  4188.  dynlink module can check a system semaphore to see whether the server
  4189.  process is already running and, if not, start it up. It needs to do this
  4190.  with the "detach" form of DosExecPgm so that the server process doesn't
  4191.  appear to the system as a child of the subsystem's first client. Such a
  4192.  mistake could mean that the client's parent thinks that the command subtree
  4193.  it founded by running the client never terminates because the server
  4194.  process appears to be part of the command subtree (see Figure 7-8).
  4195.  
  4196.  
  4197.     ┌─────────────────┐       ┌─────────────────┐
  4198.     │   Grandparent   │       │   Grandparent   │
  4199.     └────────┬────────┘       └────────┬────────┘
  4200.              │                         │
  4201.  ┌───────────┼───────────┐ ┌───────────┼───────────┐      ┌──────────┐
  4202.  │░░░░░░░░░░░│░░░░░░░░░░░│ │░░░░░░░░░░░│░░░░░░░░░░░│   ┌ │  Daemon  │
  4203.  │░░┌────────┴────────┐░░│ │░░┌────────┴────────┐░░│   |  └──────────┘
  4204.  │░░│     Process     │░░│ │░░│     Process     │─ ┼ ─ ┘
  4205.  │░░└────────┬────────┘░░│ │░░└─────────────────┘░░│
  4206.  │░░░░░░░░░░░│░░░░░░░░░░░│ │░░░ Command subtree ░░░│
  4207.  │░░┌────────┴────────┐░░│ │░░░░░░░░░░░░░░░░░░░░░░░│
  4208.  │░░│      Daemon     │░░│ │░░░░░░░░░░░░░░░░░░░░░░░│
  4209.  │░░└─────────────────┘░░│ │░░░░░░░░░░░░░░░░░░░░░░░│
  4210.  │░░░ Command subtree ░░░│ │░░░░░░░░░░░░░░░░░░░░░░░│
  4211.  │░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░░░░░░░░░░░░░░░░░│
  4212.  └───────────────────────┘ └───────────────────────┘
  4213.  
  4214.  Figure 7-8.  Dynlink daemon initiation.
  4215.  
  4216.  
  4217.       When the server process is running, the dynlink subsystem can forward
  4218.  some or all requests to it by one of the many IPC facilities. For example,
  4219.  a database subsystem might want to use a dedicated server process to hold
  4220.  open the database file and do reads and writes to it. It might keep buffers
  4221.  and ISAM directories in a shared memory segment to which the dynlink
  4222.  subsystem requests access for each of its clients; then requests that can
  4223.  be satisfied by data from these buffers won't require the IPC to the server
  4224.  process.
  4225.       The only function of some dynlink packages is to act as a procedural
  4226.  interface to another process. For example, a spreadsheet program might
  4227.  provide an interface through which other applications can retrieve data
  4228.  values from a spreadsheet. The best way to do this is for the spreadsheet
  4229.  package to contain a dynamic link library that provides clients a
  4230.  procedural interface to the spreadsheet process. The library routine itself
  4231.  will invoke a noninteractive copy (perhaps a special subset .EXE) of the
  4232.  spreadsheet to recover the information, passing it back to the client via
  4233.  IPC. Alternatively, the retrieval code that understands the spreadsheet
  4234.  data formats could be in the dynlink package itself because that package
  4235.  ships with the spreadsheet and will be upgraded when the spreadsheet is. In
  4236.  this case, the spreadsheet itself could use the package instead of
  4237.  duplicating the functionality in its own .EXE file. In any case, the
  4238.  implementation details are hidden from the client process; the client
  4239.  process simply makes a procedure call that returns the desired data.
  4240.       Viewed from the highest level, this arrangement is simple: A client
  4241.  process uses IPC to get service from a server process via a subroutine
  4242.  library. From the programmer's point of view, though, the entire mechanism
  4243.  is encapsulated in the dynlink subsystem's interface. A future upgrade to
  4244.  the dynlink package may use an improved server process and different forms
  4245.  of IPC to talk to it but retain full binary compatibility with the existing
  4246.  client base. Figure 7-9 illustrates a dynlink package being used as an
  4247.  interface to a daemon process. The figure shows the dynlink package
  4248.  interfacing with the daemon process by means of a shared memory segment and
  4249.  some other form of IPC, perhaps a named pipe.
  4250.  
  4251.  
  4252.                                     client process   |   daemon process
  4253.                                                      |
  4254.  ┌────────────┐    Far call     ┌────────────┐       |        ┌────────────┐
  4255.  │    APP     ├────────────────│  Dynlink   │──────|────────┤   Daemon   │
  4256.  │    code    │                 │    code    │      IPC       │    code    │
  4257.  │ segment(s) │                 │ segment(s) ├───────|───────│ segment(s) │
  4258.  └─────┬──────┘                 └─────┬───┬──┘       |        └──┬───┬─────┘
  4259.        │                              │   │          |           │   │
  4260.        │                              │   └───────┐  |   ┌───────┘   │
  4261.        │                              │           │  |   │           │
  4262.  ┌───────────┐                 ┌───────────┐ ┌────|─────┐ ┌───────────┐
  4263.  │    APP     │                 │  Dynlink   │ │     |      │ │   Daemon   │
  4264.  │    data    │                 │    data    │ │   Shared   │ │    data    │
  4265.  │ segment(s) │                 │ segment(s) │ │   memory   │ │ segment(s) │
  4266.  └────────────┘                 └────────────┘ └─────|──────┘ └────────────┘
  4267.                                                      |
  4268.  
  4269.  Figure 7-9.  Dynamic link routines as daemon interfaces.
  4270.  
  4271.  
  4272.  7.9  Dynamic Links As Interfaces to the Kernel
  4273.  
  4274.  We've seen how dynlink libraries can serve as simple subroutine libraries,
  4275.  how they can serve as subsystems, and how they can serve as interfaces to
  4276.  other processes. OS/2 has one more trick up its sleeve: Dynlink libraries
  4277.  can also serve as interfaces to OS/2 itself.
  4278.       Some OS/2 calls are actually implemented as simple library routines.
  4279.  For example, DosErrClass is implemented in OS/2 version 1.0 as a simple
  4280.  library routine. It takes an error code and locates, in a table, an
  4281.  explanatory text string, an error classification, and a recommended action.
  4282.  Services such as these were traditionally part of the kernel of operating
  4283.  systems, not because they needed to use privileged instructions, but
  4284.  because their error tables needed to be changed each time an upgrade to the
  4285.  operating system was released. If the service has been provided as a
  4286.  statically linked subroutine, older applications running on newer releases
  4287.  would receive new error codes that would not be in the library code's
  4288.  tables.
  4289.       Although OS/2 implements DosErrClass as a library routine, it's a
  4290.  dynlink library routine, and the .DLL file is bundled with the operating
  4291.  system itself. Any later release of the system will contain an upgraded
  4292.  version of the DosErrClass routine, one that knows about new error codes.
  4293.  Consequently, the dynlink facility provides OS/2 with a great deal of
  4294.  flexibility in packaging its functionality.
  4295.       Some functions, such as "open file" or "allocate memory," can't be
  4296.  implemented as ordinary subroutines. They need access to key internal data
  4297.  structures, and these structures are of course protected so that they can't
  4298.  be changed by unprivileged code. To get these services, the processor must
  4299.  make a system call, entering the kernel code in a very controlled fashion
  4300.  and there running with sufficient privilege to do its work. This privilege
  4301.  transition is via a call gate--a feature of the 80286/80386 hardware. A
  4302.  program calls a call gate exactly as it performs an ordinary far call;
  4303.  special flags in the GDT and LDT tell the processor that this is a call
  4304.  gate rather than a regular call.
  4305.       In OS/2, system calls are indistinguishable from ordinary dynlink
  4306.  calls. All OS/2 system calls are defined in a dynlink module called
  4307.  DosCalls. When OS/2 fixes up dynlink references to this module, it consults
  4308.  a special table, built into OS/2, of resident functions. If the function is
  4309.  not listed in this table, then an ordinary dynlink is set up. If the
  4310.  function is in the table, OS/2 sets up a call gate call in place of the
  4311.  ordinary dynlink call. The transparency between library and call gate
  4312.  functions explains why passing an invalid address to an OS/2 system call
  4313.  causes the calling process to GP fault. Because the OS/2 kernel code
  4314.  controls and manages the GP fault mechanism, OS/2 calls that are call gates
  4315.  could easily return an error code if an invalid address causes a GP fault.
  4316.  If this were done, however, the behavior of OS/2 calls would differ
  4317.  depending on their implementation: Dynlink entry points would GP fault for
  4318.  invalid addresses;9 call gate entries would return an error code. OS/2
  4319.  prevents this dichotomy and preserves its freedom to, in future releases,
  4320.  move function between dynlink and call gate entries by providing a uniform
  4321.  reaction to invalid addresses. Because non-call-gate dynlink routines must
  4322.  generate GP faults, call gate routines produce them as well.
  4323.  
  4324.  
  4325.  7.10  The Architectural Role of Dynamic Links
  4326.  
  4327.  Dynamic links play three major roles in OS/2: They provide the system
  4328.  interface; they provide a high-bandwidth device interface; and they support
  4329.  open architecture nonkernel service packages.
  4330.       The role of dynamic links as the system interface is clear. They
  4331.  provide a uniform, high-efficiency interface to the system kernel as well
  4332.  as a variety of nonkernel services. The interface is directly compatible
  4333.  with high-level languages, and it takes advantage of special speed-
  4334.  enhancing features of the 80286 and 80386 microprocessors.10 It provides a
  4335.  wide and convenient name space, and it allows the distribution of function
  4336.  between library code and kernel code. Finally, it provides an essentially
  4337.  unlimited expansion capability.
  4338.       But dynamic links do much more than act as system calls. You'll recall
  4339.  that in the opening chapters I expressed a need for a device interface that
  4340.  was as device independent as device drivers but without their attendant
  4341.  overhead. Dynamic links provide this interface because they allow
  4342.  applications to make a high-speed call to a subroutine package that can
  4343.  directly manipulate the device (see Chapter 18, I/O Privilege Mechanism
  4344.  and Debugging/Ptrace). The call itself is fast, and the package can specify
  4345.  an arbitrarily wide set of parameters. No privilege or ring transition is
  4346.  needed, and the dynlink package can directly access its client's data
  4347.  areas. Finally, the dynlink package can use subsystem support features to
  4348.  virtualize the device or to referee its use among multiple clients. Device
  4349.  independence is provided because a new version of the dynlink interface can
  4350.  be installed whenever new hardware is installed. VIO and the presentation
  4351.  manager are examples of this kind of dynlink use. Dynlink packages have an
  4352.  important drawback when they are being used as device driver replacements:
  4353.  They cannot receive hardware interrupts. Some devices, such as video
  4354.  displays, do not generate interrupts. Interrupt-driven devices, though,
  4355.  require a true device driver. That driver can contain all of the device
  4356.  interface function, or the work can be split between a device driver and a
  4357.  dynlink package that acts as a front end for that device driver. See
  4358.  Chapters 17 and 18 for further discussion of this.
  4359.       Dynlink routines can also act as nonkernel service packages--as an
  4360.  open system architecture for software. Most operating systems
  4361.  are like the early versions of the Apple Macintosh computer: They are
  4362.  closed systems; only their creators can add features to them. Because of
  4363.  OS/2's open system architecture, third parties and end users can add system
  4364.  services simply by plugging in dynlink modules, just as hardware cards plug
  4365.  into an open hardware system. The analogy extends further: Some hardware
  4366.  cards become so popular that their interface defines a standard. Examples
  4367.  are the Hayes modem and the Hercules Graphics Card. Third-party dynlink
  4368.  packages will, over time, establish similar standards. Vendors will offer,
  4369.  for example, improved database dynlink routines that are advertised as plug
  4370.  compatible with the standard database dynlink interface, but better,
  4371.  cheaper, and faster.
  4372.       Dynlinks allow third parties to add interfaces to OS/2; they also
  4373.  allow OS/2's developers to add future interfaces. The dynlink interface
  4374.  model allows additional functionality to be implemented as subroutines or
  4375.  processes or even to be distributed across a network environment.
  4376.  
  4377.  
  4378.  7.11  Implementation Details
  4379.  
  4380.  Although dynlink routines often act very much like traditional static
  4381.  subroutines, a programmer must be aware of some special considerations
  4382.  involved. This section discusses some issues that must be dealt with to
  4383.  produce a good dynlink package.
  4384.  
  4385.  
  4386.  7.11.1  Dynlink Data Security
  4387.  We have discussed how a dynlink package runs as a subroutine of the client
  4388.  process and that the client process has access to the dynlink package's
  4389.  instance and global data segments.11 This use of the dynlink interface is
  4390.  efficient and thus advantageous, but it's also disadvantageous because
  4391.  aberrant client processes can damage the dynlink package's global data
  4392.  segments.
  4393.       In most circumstances, accidental damage to a dynlink package's data
  4394.  segments is rare. Unless the dynlink package returns pointers into its data
  4395.  segments to the client process, the client doesn't "know" the dynlink
  4396.  package's data segment selectors. The only way such a process could access
  4397.  the dynlink's segments would be to accidentally create a random selector
  4398.  value that matched one belonging to a dynlink package. Because the
  4399.  majority of selector values are illegal, a process would have to be
  4400.  very "lucky" to generate a valid dynlink package data selector before it
  4401.  generated an unused or code segment selector.12 Naturally, dynlink packages
  4402.  shouldn't use global data segments to hold sensitive data because a
  4403.  malicious application can figure out the proper selector values.
  4404.       The measures a programmer takes to deal with the security issue depend
  4405.  on the nature and sensitivity of the dynlink package. Dynlink packages that
  4406.  don't have global data segments are at no risk; an aberrant program can
  4407.  damage its instance data segments and thereby fail to run correctly, but
  4408.  that's the expected outcome of a program bug. A dynlink package with global
  4409.  data segments can minimize the risk by never giving its callers pointers
  4410.  into its (the dynlink package's) global data segment. If the amount of
  4411.  global data is small and merely detecting damage is sufficient, the global
  4412.  data segments could be checksummed.
  4413.       Finally, if accidental damage would be grave, a dynlink package can
  4414.  work in conjunction with a special dedicated process, as described above.
  4415.  The dedicated process can keep the sensitive data and provide it on a per-
  4416.  client basis to the dynlink package in response to an IPC request. Because
  4417.  the dedicated process is a separate process, its segments are fully
  4418.  protected from the client process as well as from all others.
  4419.  
  4420.  
  4421.  7.11.2  Dynlink Life, Death, and Sharing
  4422.  Throughout this discussion, I have referred to sharing pure segments. The
  4423.  ability to share pure segments is an optimization that OS/2 makes for all
  4424.  memory segments whether they are dynlink segments or an application's .EXE
  4425.  file segments. A pure segment is one that is never modified during its
  4426.  lifetime. All code segments (except for those created by DosCreateCSAlias)
  4427.  are pure; read-only data segments are also pure. When OS/2 notices that
  4428.  it's going to load two copies of the same pure segment, it performs a
  4429.  behind-the-scenes optimization and gives the second client access to the
  4430.  earlier copy of the segment instead of wasting memory with a duplicate
  4431.  version.
  4432.       For example, if two copies of a program are run, all code segments are
  4433.  pure; at most, only one copy of each code segment will be in memory. OS/2
  4434.  flags these segments as "internally shared" and doesn't release them until
  4435.  the last user has finished with the segment. This is not the same as
  4436.  "shared memory" as it is generally defined in OS/2. Because pure segments
  4437.  can only be read, never written, no process can tell that pure segments are
  4438.  being shared or be affected by that sharing. Although threads from two or
  4439.  more processes may execute the same shared code segment at the same time,
  4440.  this is not the same as a multithreaded process. Each copy of a program has
  4441.  its own data areas, its own stack, its own file handles, and so on. They
  4442.  are totally independent of one another even if OS/2 is quietly sharing
  4443.  their pure code segments among them. Unlike multiple threads within a
  4444.  single process, threads from different processes cannot affect one another;
  4445.  the programmer can safely ignore their possible existence in shared code
  4446.  segments.
  4447.       Because the pure segments of a dynlink package are shared, the second
  4448.  and subsequent clients of a dynlink package can load much more quickly
  4449.  (because these pure segments don't have to be loaded from the .DLL disk
  4450.  file). This doesn't mean that OS/2 doesn't have to "hit the disk" at all:
  4451.  Many dynlink packages use instance data segments, and OS/2 loads a fresh
  4452.  copy of the initial values for these segments from the .DLL file.
  4453.       A dynlink package's second client is its second simultaneous client.
  4454.  Under OS/2, only processes have a life of their own. Objects such as
  4455.  dynlink packages and shared memory segments exist only as possessions of
  4456.  processes. When the last client process of such an object dies or otherwise
  4457.  releases the object, OS/2 destroys it and frees up the memory. For example,
  4458.  when the first client (since bootup) of a dynlink package references it,
  4459.  OS/2 loads the package's code and data segments. Then OS/2 calls the
  4460.  package's initialization routine--if the package has one. OS/2 records in
  4461.  an internal data structure that this dynlink package has one client. If
  4462.  additional clients come along while the first is still using the dynlink
  4463.  package, OS/2 increments the package's user count appropriately. Each time
  4464.  a client disconnects or dies, the user count is decremented. As long as the
  4465.  user count remains nonzero, the package remains in existence, each client
  4466.  sharing the original global data segments. When the client count goes to
  4467.  zero, OS/2 discards the dynlink package's code and global data segments and
  4468.  in effect forgets all about the package. When another client comes along,
  4469.  OS/2 reloads the package and reloads its global data segment as if the
  4470.  earlier use had never occurred.
  4471.       This mechanism affects a dynlink package only in the management of the
  4472.  package's global data segment. The package's code segments are pure, so it
  4473.  doesn't matter if they are reloaded from the .DLL file. The instance data
  4474.  segments are always reinitialized for each new client, but the data in a
  4475.  package's global data segment remains in existence only as long as the
  4476.  package has at least one client process. When the last client releases the
  4477.  package, the global data segment is discarded. If this is a problem for a
  4478.  dynlink package, an associated "dummy" process (which the dynlink package
  4479.  could start during its loadtime initialization) can reference the dynlink
  4480.  package. As long as this process stays alive, the dynlink package and its
  4481.  global data segments stay alive.13
  4482.       An alternative is for the dynlink package to keep track of the count
  4483.  of its clients and save the contents of its global data segments to a disk
  4484.  file when the last client terminates, but this is tricky. Because a process
  4485.  may fail to call a dynlink package's "I'm finished" entry point (presumably
  4486.  part of the dynlink package's interface) before it terminates, the dynlink
  4487.  package must get control to write its segment via DosExitList. If the
  4488.  client process is connected to the dynlink package via DosLoadModule (that
  4489.  is, via runtime dynamic linking), it cannot disconnect from the package via
  4490.  DosFreeModule as long as a DosExitList address points into the dynlink
  4491.  package. An attempt to do so returns an error code. Typically, one would
  4492.  expect the application to ignore this error code; but because the dynlink
  4493.  package is still attached to the client process, it will receive
  4494.  DosExitList service when the client eventually terminates. It's important
  4495.  that dynlink packages which maintain client state information and therefore
  4496.  need DosExitList also offer an "I'm finished" function. When a client calls
  4497.  this function, the package should close it out and then remove its
  4498.  processing address from DosExitList so that DosFreeModule can take effect
  4499.  if the client wishes.
  4500.       Note that OS/2's habit of sharing in-use dynlink libraries has
  4501.  implications for the replacement of dynlink packages. Specifically, OS/2
  4502.  holds the dynlink .DLL file open for as long as that library has any
  4503.  clients. To replace a dynlink library with an upgraded version,
  4504.  you must first ensure that all clients of the old package have been
  4505.  terminated.
  4506.       While we're on the subject, I'll point out that dynlink segments, like
  4507.  .EXE file segments, can be marked (by the linker) as "preload" or "load on
  4508.  demand." When a dynlink module or a .EXE file is loaded, OS/2 immediately
  4509.  loads all segments marked "preload" but usually14 does not load any
  4510.  segments marked "load on demand." These segments are loaded only when (and
  4511.  if) they are referenced. This mechanism speeds process and library loading
  4512.  and reduces swapping by leaving infrequently used segments out of memory
  4513.  until they are needed. Once a segment is loaded, its "preload" or "load on
  4514.  demand" status has no further bearing; the segment will be swapped or
  4515.  discarded without consideration for these bits.
  4516.       Finally, special OS/2 code keeps track of dynamic link "circular
  4517.  references." Because dynlink packages can call other dynlink packages,
  4518.  package A can call package B, and package B can call package A. Even if the
  4519.  client process C terminates, packages A and B might appear to be in use by
  4520.  each other, and they would both stay in memory. OS/2 keeps a graph of
  4521.  dynlink clients, both processes and other dynlink packages. When a process
  4522.  can no longer reach a dynlink package over this graph--in other words, when
  4523.  a package doesn't have a process for a client and when none of its client
  4524.  packages have processes for clients and so on--the dynlink package is
  4525.  released. Figure 7-10 illustrates a dynamic link circular reference. PA
  4526.  and PB are two processes, and LA through LG are dynlink library routines.
  4527.  
  4528.  
  4529.  ┌────────────┐                        ┌────────────┐
  4530.  │░░░░░░░░░░░░│                        │░░░░░░░░░░░░│
  4531.  │░░░░░PA░░░░░│                        │░░░░░PB░░░░░│
  4532.  │░░░░░░░░░░░░│                        │░░░░░░░░░░░░│
  4533.  └─────┬──────┘                        └─┬────────┬─┘
  4534.        │                                 │        │
  4535.  ┌───────────┐                          │  ┌───────────┐
  4536.  │     LA     │───────┐                 │  │     LF     │
  4537.  └──┬──────┬──┘        │                 │  └─────┬──────┘
  4538.     │   ┌──┴─────────┐ │                 │        │
  4539.     │   │     LB     ├─┘                 │  ┌───────────┐
  4540.     │   └─┬──────────┘                   │  │     LG     │
  4541.     │     │  ┌───────────────────────────┘  └─────┬──────┘
  4542.  ┌─────────┐           ┌────────────┐          │
  4543.  │     LC     ├──────────│     LD     │─────────┘
  4544.  └───────────┘           └─────┬──────┘
  4545.        └────────────────────────┘
  4546.  
  4547.  Figure 7-10.  Dynamic link circular references.
  4548.  
  4549.  
  4550.  7.11.3  Dynlink Side Effects
  4551.  A well-written dynlink library needs to adhere to the OS/2 religious tenet
  4552.  of zero side effects. A dynlink library should export to the client process
  4553.  only its functional interface and not accidentally export side effects that
  4554.  may interfere with the consistent execution of the client.
  4555.       Some possible side effects are obvious: A dynlink routine shouldn't
  4556.  close any file handles that it didn't itself open. The same applies to
  4557.  other system resources that the client process may be accessing, and it
  4558.  applies in the inverse, as well: A dynlink routine that obtains resources
  4559.  for itself, in the guise of the client process, should do so in a way that
  4560.  doesn't affect the client code. For example, consuming many of the
  4561.  available file handles would be a side effect because the client would then
  4562.  unexpectedly be short of available file handles. A dynlink package with a
  4563.  healthy file handle appetite should be sure to call OS/2 to raise the
  4564.  maximum number of file handles so that the client process isn't
  4565.  constrained. Finally, the amount of available stack space is a resource
  4566.  that a dynlink package must not exhaust. A dynlink routine should try to
  4567.  minimize its stack needs, and an upgrade to an existing dynlink package
  4568.  must not consume much more stack space than did the earlier version, lest
  4569.  the upgrade cause existing clients to fail in the field.
  4570.       Dynlink routines can also cause side effects by issuing some kinds of
  4571.  system calls. Because a dynlink routine runs as a subroutine of the client
  4572.  process, it must be sure that calls that it makes to OS/2 on behalf of the
  4573.  client process don't affect the client application. For example, each
  4574.  signal event can have only one handler address; if a dynlink routine
  4575.  establishes a signal handler, then that signal handler preempts any handler
  4576.  set up by the client application. Likewise, if a dynlink routine changes
  4577.  the priority of the thread with which it was called, the dynlink routine
  4578.  must be sure to restore that priority before it returns to its caller.
  4579.  Several other system functions such as DosError and DosSetVerify also cause
  4580.  side effects that can affect the client process.
  4581.       Enumerating all forms of side effects is not possible; it's up to the
  4582.  programmer to take the care needed to ensure that a dynlink module is
  4583.  properly house-trained. A dynlink module should avoid the side effects
  4584.  mentioned as well as similar ones, and, most important, it should behave
  4585.  consistently so that if a client application passes its acceptance tests in
  4586.  the lab it won't mysteriously fail in the field. This applies doubly to
  4587.  upgrades for existing dynlink routines. Upgrades must be written so that if
  4588.  a client application works with the earlier release of the dynlink package
  4589.  it will work with the new release; obviously the author of the application
  4590.  will not have an opportunity to retest existing copies of the application
  4591.  against the new release of the dynlink module.
  4592.  
  4593.  
  4594.  7.12  Dynlink Names
  4595.  
  4596.  Each dynlink entry point has three names associated with it: an external
  4597.  name, a module name, and an entry point name. The name the client program
  4598.  calls as an external reference is the external name. The programmer works
  4599.  with this name, and its syntax and form must be compatible with the
  4600.  assembler or compiler being used. The name should be simple and explanatory
  4601.  yet unlikely to collide with another external name in the client code or in
  4602.  another library. A name such as READ or RESET is a poor choice because of
  4603.  the collision possibilities; a name such as XR23P11 is obviously hard to
  4604.  work with.
  4605.       The linker replaces the external name with a module name and an entry
  4606.  point name, which are embedded in the resultant .EXE file. OS/2 uses the
  4607.  module name to locate the dynlink .DLL file; the code for module modname is
  4608.  in file MODNAME.DLL. The entry point name specifies the entry point in the
  4609.  module; the entry point name need not be the same as the external name. For
  4610.  modules with a lot of entry points, the client .EXE file size can be
  4611.  minimized and the loading speed maximized by using entry ordinals in place
  4612.  of entry point names. See the OS/2 technical reference literature for
  4613.  details.
  4614.       Runtime dynamic links are established by using the module name and the
  4615.  entry point name; the external name is not used.
  4616.  
  4617.  
  4618.  
  4619.  8  File System Name Space
  4620.  
  4621.  ───────────────────────────────────────────────────────────────────────────
  4622.  
  4623.  File system name space is a fancy term for how names of objects are defined
  4624.  in OS/2. The words file system are a hint that OS/2 uses one naming scheme
  4625.  both for files and for everything else with a name in ASCII format--system
  4626.  semaphores, named shared memory, and so forth. First, we'll discuss the
  4627.  syntax of names and how to manipulate them; we'll wind up with a discussion
  4628.  of how and why we use one naming scheme for all named objects.
  4629.  
  4630.  
  4631.  8.1  Filenames
  4632.  
  4633.  Before we discuss OS/2 filenames, let's review the format of filenames
  4634.  under MS-DOS. In MS-DOS, filenames are required to fit the 8.3 format: a
  4635.  name field (which can contain a maximum of 8 characters) and an extension
  4636.  field (which can contain a maximum of 3 characters).1 The period character
  4637.  (.) between the name and the extension is not part of the filename; it's a
  4638.  separator character. The filename can consist of uppercase characters only.
  4639.  If a user or an application creates a filename that contains lowercase
  4640.  characters or a mixture of uppercase and lowercase, MS-DOS converts the
  4641.  filename to all uppercase. If an application presents a filename whose name
  4642.  or extension field exceeds the allotted length, MS-DOS silently truncates
  4643.  the name to the 8.3 format before using it. MS-DOS establishes and enforces
  4644.  these rules and maintains the file system structure on the disks. The file
  4645.  system that MS-DOS version 3.x supports is called the FAT (File Allocation
  4646.  Table) file system. The following are typical MS-DOS and OS/2 filenames:
  4647.  
  4648.     \FOOTBALL\SRC\KERNEL\SCHED.ASM
  4649.  
  4650.  Football is a development project, so this name describes the source for
  4651.  the kernel scheduler for the football project.
  4652.  
  4653.     \MEMOS\286\MODESWIT.DOC
  4654.  
  4655.  is a memo discussing 80286 mode switching.
  4656.  
  4657.     \\HAGAR\SCRATCH\GORDONL\FOR_MARK
  4658.  
  4659.  is a file in my scratch directory on the network server HAGAR, placed there
  4660.  for use by Mark.
  4661.       The OS/2 architecture views file systems quite differently. As
  4662.  microcomputers become more powerful and are used in more and more ways,
  4663.  file system characteristics will be needed that might not be met by a
  4664.  built-in OS/2 file system. Exotic peripherals, such as WORM2 drives,
  4665.  definitely require special file systems to meet their special
  4666.  characteristics. For this reason, the file system is not built into OS/2
  4667.  but is a closely allied component--an installable file system (IFS). An IFS
  4668.  is similar to a device driver; it's a body of code that OS/2 loads at boot
  4669.  time. The code talks to OS/2 via a standard interface and provides the
  4670.  software to manage a file system on a storage device, including the ability
  4671.  to create and maintain directories, to allocate disk space, and so
  4672.  on.
  4673.       If you are familiar with OS/2 version 1.0, this information may be
  4674.  surprising because you have seen no mention of an IFS in the reference
  4675.  manuals. That's because the implementation hasn't yet caught up with the
  4676.  architecture. We designed OS/2, from the beginning, to support installable
  4677.  file systems, one of which would of course be the familiar FAT file system.
  4678.  We designed the file system calls, such as DosOpen and DosClose, with this
  4679.  in mind. Although scheduling pressures forced us to ship OS/2 version 1.0
  4680.  with only the FAT file system--still built in--a future release will
  4681.  include the full IFS package. Although at this writing the IFS release of
  4682.  OS/2 has not been announced, this information is included here so that you
  4683.  can understand the basis for the system name architecture. Also, this
  4684.  information will help you write programs that work well under the new
  4685.  releases of OS/2 that contain the IFS.
  4686.       Because the IFS will interpret filenames and pathnames and because
  4687.  installable file systems can vary considerably, OS/23 doesn't contain much
  4688.  specific information about the format and meaning of filenames and
  4689.  pathnames. In general, the form and meaning of filenames and pathnames are
  4690.  private matters between the user and the IFS; both the application and OS/2
  4691.  are simply go-betweens. Neither should attempt to parse or understand
  4692.  filenames and pathnames. Applications shouldn't parse names because some
  4693.  IFSs will support names in formats other than the 8.3 format. Applications
  4694.  shouldn't even assume a specific length for a filename or a pathname. All
  4695.  OS/2 filename and pathname interfaces, such as DosOpen, DosFindNext, and so
  4696.  on, are designed to take name strings of arbitrary length. Applications
  4697.  should use name buffers of at least 256 characters to ensure that a long
  4698.  name is not truncated.
  4699.  
  4700.  
  4701.  8.2  Network Access
  4702.  
  4703.  Two hundred and fifty-six characters may seem a bit extreme for the length
  4704.  of a filename, and perhaps it is. But OS/2 filenames are often pathnames,
  4705.  and pathnames can be quite lengthy. To provide transparent access to files
  4706.  on a LAN (local area network), OS/2 makes the network part of the file
  4707.  system name space. In other words, a file's pathname can specify a machine
  4708.  name as well as a directory path. An application can issue an open to a
  4709.  name string such as \WORK\BOOK.DAT or \\VOGON\TEMP\RECALC.ASM. The first
  4710.  name specifies the file BOOK.DAT in the directory WORK on the current drive
  4711.  of the local machine; the second name specifies the file RECALC.ASM in the
  4712.  directory TEMP on the machine VOGON.4 Future releases of the Microsoft LAN
  4713.  Manager will make further use of the file system name space, so filenames,
  4714.  especially program-generated filenames, can easily become very long.
  4715.  
  4716.  
  4717.  8.3  Name Generation and Compatibility
  4718.  
  4719.  Earlier, I said that applications should pass on filenames entered by the
  4720.  user, ignoring their form. This is, of course, a bit unrealistic. Programs
  4721.  often need to generate filenames--to hold scratch files, to hold derivative
  4722.  filenames (for example, FOO.OBJ derived from FOO.ASM), and so forth. How
  4723.  can an application generate or permute such filenames and yet ensure
  4724.  compatibility with all installable file systems? The answer is, of course:
  4725.  Use the least common denominator approach. In other words, you can safely
  4726.  assume that a new IFS must accept the FAT file system's names (the 8.3
  4727.  format) because otherwise it would be incompatible with too many programs.
  4728.  So if an application sticks to the 8.3 rules when it creates names, it can
  4729.  be sure that it is compatible with future file systems. Unlike MS-DOS,
  4730.  OS/25 will not truncate name or extension fields that are too long;
  4731.  instead, an error will be returned. The case of a filename will continue to
  4732.  be insignificant. Some operating systems, such as UNIX, are case sensitive;
  4733.  for example, in UNIX the names "foo" and "Foo" refer to different files.
  4734.  This works fine for a system used primarily by programmers, who know that a
  4735.  lowercase f is ASCII 66 (SUB16) and that an uppercase F is ASCII
  4736.  46 (SUB 16). Nonprogrammers, on the other hand, tend to see f and F as the
  4737.  same character. Because most OS/2 users are nonprogrammers, OS/2
  4738.  installable file systems will continue to be case insensitive.
  4739.       I said that it was safe if program-generated names adhered to the 8.3
  4740.  rule. Program-permuted names are likewise safe if they only substitute
  4741.  alphanumeric characters for other alphanumeric characters, for example,
  4742.  FOO.OBJ for FOO.ASM. Lengthening filenames is also safe (for example,
  4743.  changing FOO.C to FOO.OBJ) if your program checks for "invalid name" error
  4744.  codes for the new name and has some way to deal with that possibility. In
  4745.  any case, write your program so that it isn't confused by enhanced
  4746.  pathnames; in the above substitution cases, the algorithm should work from
  4747.  the end of the path string and ignore what comes before.
  4748.  
  4749.  
  4750.  8.4  Permissions
  4751.  
  4752.  Future releases of OS/2 will use the file system name space for more than
  4753.  locating a file; it will also contain the permissions for the file. A
  4754.  uniform mechanism will associate an access list with every entry in the
  4755.  file system name space. This list will prevent unauthorized access--
  4756.  accidental or deliberate--to the named file.
  4757.  
  4758.  
  4759.  8.5  Other Objects in the File System Name Space
  4760.  
  4761.  As we've seen, the file system name space is a valuable device in several
  4762.  aspects. First, it allows the generation of a variety of names. You can
  4763.  group names together (by putting them in the same directory), and you can
  4764.  generate entire families of unique names (by creating a new subdirectory).
  4765.  Second, the name space can encompass all files and devices on the local
  4766.  machine as well as files and devices on remote machines. Finally, file
  4767.  system names will eventually support a flexible access and protection
  4768.  mechanism.
  4769.       Thus, it comes as no surprise that when the designers of OS/2 needed a
  4770.  naming mechanism to deal with nonfile objects, such as shared memory,
  4771.  system semaphores, and named pipes, we chose to use the file system name
  4772.  space. One small disadvantage to this decision is that a shared memory
  4773.  object cannot have a name identical to that of a system semaphore, a named
  4774.  pipe, or a disk file. This drawback is trivial, however, compared with the
  4775.  benefits of sharing the file system name space. And, of course, you can use
  4776.  separate subdirectory names for each type of object, thus preventing name
  4777.  collision.
  4778.       Does this mean that system semaphores, shared memory, and pipes have
  4779.  actual file system entries on a disk somewhere? Not yet. The FAT file
  4780.  system does not support special object names in its directories. Although
  4781.  changing it to do so would be easy, the file system would no longer be
  4782.  downward compatible with MS-DOS. (MS-DOS 3.x could not read such disks
  4783.  written under OS/2.) Because only the FAT file system is available with
  4784.  OS/2 version 1.0, that release keeps special RAM-resident pseudo
  4785.  directories to hold the special object names. These names must start with
  4786.  \SEM\, \SHAREMEM\, \QUEUES\, and \DEV\ to minimize the chance of name
  4787.  collision with a real file when they do become special pseudo files in a
  4788.  future release of OS/2.
  4789.       Although all file system name space features--networking and (in the
  4790.  future) permissions--apply to all file system name space objects from an
  4791.  architectural standpoint, not all permutations may be supported.
  4792.  Specifically, supporting named shared memory across the network is very
  4793.  costly6 and won't be implemented.
  4794.  
  4795.  
  4796.  
  4797.  9  Memory Management
  4798.  
  4799.  ──────────────────────────────────────────────────────────────────────────
  4800.  
  4801.  A primary function of any multitasking operating system is to allocate
  4802.  system resources to each process according to its need. The scheduler
  4803.  allocates CPU time among processes (actually, among threads); the memory
  4804.  manager allocates both physical memory and virtual memory.
  4805.  
  4806.  
  4807.  9.1  Protection Model
  4808.  
  4809.  Although MS-DOS provided a simple form of memory management, OS/2 provides
  4810.  memory protection. Under MS-DOS 3.x, for example, a program should ask the
  4811.  operating system to allocate a memory area before the program uses it.
  4812.  Under OS/2, a program must ask the operating system to allocate a memory
  4813.  area before the program uses it. As we discussed earlier, the 80286
  4814.  microprocessor contains special memory protection hardware. Each memory
  4815.  reference that a program makes explicitly or implicitly references a
  4816.  segment selector. The segment selector, in turn, references an entry in the
  4817.  GDT or the LDT, depending on the form of the selector. Before any program,
  4818.  including OS/2 itself, can reference a memory location, that memory
  4819.  location must be described in an LDT or a GDT entry, and the selector for
  4820.  that entry must be loaded into one of the four segment registers.
  4821.       This hardware design places some restrictions on how programs can use
  4822.  addresses.
  4823.  
  4824.       ■  A program cannot address memory not set up for it in the LDT or
  4825.          GDT. The only way to address memory is via the LDT and GDT.
  4826.  
  4827.       ■  Each segment descriptor in the LDT and GDT contains the physical
  4828.          address and the length of that segment. A program cannot reference
  4829.          an offset into a segment beyond that segment's length.
  4830.  
  4831.       ■  A program can't put garbage (arbitrary values) into a segment
  4832.          register. Each time a segment register is loaded, the hardware
  4833.          examines the corresponding LDT and GDT to see if the entry is
  4834.          valid. If a program puts an arbitrary value--for example, the lower
  4835.          half of a floating point number--into a segment register, the
  4836.          arbitrary value will probably point to an invalid LDT or GDT entry,
  4837.          causing a GP fault.
  4838.  
  4839.       ■  A program can't execute instructions from within a data segment.
  4840.          Attempting to load a data segment selector into the CS register
  4841.          (usually via a far call or a far jump) causes a GP fault.
  4842.  
  4843.       ■  A program can't write into a code segment. Attempting to do so
  4844.          causes a GP fault.
  4845.  
  4846.       ■  A program can't perform segment arithmetic. Segment arithmetic
  4847.          refers to activities made possible by the addressing mechanism of
  4848.          the 8086 and 8088 microprocessors. Although they are described as
  4849.          having a segment architecture, they are actually linear address
  4850.          space machines that use offset registers--the so-called segment
  4851.          registers. An 8086 can address 1 MB of memory, which requires a 20-
  4852.          bit address. The processor creates this address by multiplying the
  4853.          16-bit segment value by 16 and adding it to the 16-bit offset
  4854.          value. The result is an address between 0 and 1,048,575 (that is, 1
  4855.          MB).1 The reason these are not true segments is that they don't
  4856.          have any associated length and their names (that is, their
  4857.          selectors) aren't names at all but physical addresses divided by
  4858.          16. These segment values are actually scaled offsets. An address
  4859.          that has a segment value of 100 and an offset value of 100 (shown
  4860.          as 100(SUB10):100(SUB10)), and the address (99(SUB10):116(SUB10))
  4861.          both refer to the same memory location.
  4862.             Many real mode programs take advantage of this situation. Some
  4863.          programs that keep a great many pointers store them as 20-bit
  4864.          values, decomposing those values into the segment:offset form only
  4865.          when they need to de-reference the pointer. To ensure that certain
  4866.          objects have a specific offset value, other programs choose a
  4867.          matching segment value so that the resultant 20-bit address is
  4868.          correct. Neither technique works in OS/2 protect mode. Each segment
  4869.          selector describes its own segment, a segment with a length and an
  4870.          address that are independent of the numeric value of the segment
  4871.          selector. The memory described by segment N has nothing in common
  4872.          with the memory described by segment N+4 or by any other segment
  4873.          unless OS/2 explicitly sets it up that way.
  4874.  
  4875.       The segmentation and protection hardware allows OS/2 to impose further
  4876.  restrictions on processes.
  4877.  
  4878.       ■  Processes cannot edit or examine the contents of the LDT or the
  4879.          GDT. OS/2 simply declines to build an LDT or GDT selector that a
  4880.          process can use to access the contents of those tables. Certain LDT
  4881.          and GDT selectors describe the contents of those tables themselves,
  4882.          but OS/2 sets them up so that they can only be used by ring 0 (that
  4883.          is, privileged) code.
  4884.  
  4885.       ■  Processes cannot hook interrupt vectors. MS-DOS version 3.x
  4886.          programs commonly hook interrupt vectors by replacing the address
  4887.          of the interrupt handler with an address from their own code. Thus,
  4888.          these programs can monitor or intercept system calls made via INT
  4889.          21h, BIOS calls also made via interrupts, and hardware interrupts
  4890.          such as the keyboard and the system clock. OS/2 programs cannot do
  4891.          this. OS/2 declines to set up a segment selector that processes can
  4892.          use to address the interrupt vector table.
  4893.  
  4894.       ■  Processes cannot call the ROM BIOS code because no selector
  4895.          addresses the ROM BIOS code. Even if such a selector were
  4896.          available, it would be of little use. The ROM BIOS is coded for
  4897.          real mode execution and performs segment arithmetic operations that
  4898.          are no longer legal. If OS/2 provided a ROM BIOS selector, calls to
  4899.          the ROM BIOS would usually generate GP faults.
  4900.  
  4901.       ■  Finally, processes cannot run in ring 0, that is, in privileged
  4902.          mode. Both OS/2 and the 80286 hardware are designed to prevent an
  4903.          application program from ever executing in ring 0. Code running in
  4904.          ring 0 can manipulate the LDT and GDT tables as well as other
  4905.          hardware protection features. If OS/2 allowed processes to run in
  4906.          ring 0, the system could never be stable or secure. OS/2 obtains
  4907.          its privileged (literally) state by being the first code loaded at
  4908.          boot time. The boot process takes place in ring 0 and grants ring 0
  4909.          permission to OS/2 by transferring control to OS/2 while remaining
  4910.          in ring 0. OS/2 does not, naturally, extend this favor to the
  4911.          application programs it loads; it ensures that applications can
  4912.          only run in ring 3 user mode.2
  4913.  
  4914.  
  4915.  9.2  Memory Management API
  4916.  
  4917.  OS/2 provides an extensive memory management API. This book is not a
  4918.  reference manual, so I won't cover all the calls. Instead, I'll focus on
  4919.  areas that may not be completely self-explanatory.
  4920.  
  4921.  
  4922.  9.2.1  Shared Memory
  4923.  OS/2 supports two kinds of shared memory--named shared memory and giveaway
  4924.  shared memory. In both, the memory object shared is a segment. Only an
  4925.  entire segment can be shared; sharing part of a segment is not possible.
  4926.  Named shared memory is volatile because neither the name of the named
  4927.  shared memory nor the memory itself can exist on the FAT file system. When
  4928.  the number of processes using a shared memory segment goes to zero, the
  4929.  memory is released. Shared memory can't stay around in the absence of
  4930.  client processes; it must be reinitialized via DosAllocShrSeg after a
  4931.  period of nonuse.
  4932.       Giveaway shared memory allows processes to share access to the same
  4933.  segment. Giveaway shared memory segments don't have names because processes
  4934.  can't ask to have access to them; a current user of the segment has to give
  4935.  access to the segment to a new client process. The term giveaway is a bit
  4936.  of a misnomer because the giving process retains access to the memory--the
  4937.  access is "given" but not especially "away." Giveaway shared memory is not
  4938.  as convenient as named shared memory. The owner3 has to know the PID of the
  4939.  recipient and  then communicate the recipient's segment selector (returned
  4940.  by DosGiveSeg) to that recipient process via some form of IPC.
  4941.       Despite its limitations, giveaway shared memory has important virtues.
  4942.  It's a fast and efficient way for one process to transfer data to another;
  4943.  and because access is passed "hand to hand," the wrong process cannot
  4944.  accidentally or deliberately gain access to the segment. Most clients of
  4945.  giveaway shared memory don't retain access to the segment once they've
  4946.  passed it off; they typically call DosFreeSeg on their handle after they've
  4947.  called DosGiveSeg. For example, consider the design of a database dynlink
  4948.  subsystem that acts as a front end for a database serving process. As part
  4949.  of the dynlink initialization process, the package arranged for its client
  4950.  process to share a small named shared memory segment with the database
  4951.  process. It might be best to use a named pipe or named shared memory--
  4952.  created by the database process--to establish initial communication and
  4953.  then use this interface only to set up a private piece of giveaway shared
  4954.  memory for all further transactions between the client process (via the
  4955.  dynlink subsystem) and the database process. Doing it this way, rather than
  4956.  having one named shared segment hold service requests from all clients,
  4957.  provides greater security. Because each client has its own separate shared
  4958.  memory communications area, an amok client can't damage the communications
  4959.  of other clients.
  4960.       When a client process asks the database process to read it a record,
  4961.  the database process must use a form of IPC to transfer the data to the
  4962.  client. Pipes are too slow for the volume of data that our example
  4963.  anticipates; shared memory is the best technique. If we were to use named
  4964.  shared memory, the database package would have to create a unique shared
  4965.  memory name for each record, allocate the memory, and then communicate the
  4966.  name to the client (actually, to the dynlink subsystem called by the
  4967.  client) so that it can request access. This process has some drawbacks:
  4968.  
  4969.       ■  A new unique shared memory name must be created for each request.
  4970.          We could reuse a single shared memory segment, but this would force
  4971.          the client to copy the data out of the segment before it could make
  4972.          another request--too costly a process for an application that must
  4973.          handle a high volume of data.
  4974.  
  4975.       ■  Creating named shared memory segments is generally slower than
  4976.          creating giveaway shared memory segments, especially if a large
  4977.          number of named shared memory objects exist, as would be the case
  4978.          in this scenario. The client spends more time when it then requests
  4979.          access to the segment. Creating named shared memory segments is
  4980.          plenty fast enough when it's done once in a while, but in a high-
  4981.          frequency application such as our example, it could become a
  4982.          bottleneck.
  4983.  
  4984.       Instead, the database process can create a giveaway shared memory
  4985.  segment, load the data into it, and then give it to the client process. The
  4986.  database process can easily learn the client's PID; the dynlink interface,
  4987.  which runs as the client process, can include it as part of the data
  4988.  request. Likewise, the database process can easily return the new client
  4989.  selector to the client. This process is fast and efficient and doesn't bog
  4990.  down the system by forcing it to deal with a great many name strings.
  4991.       Note that you must specify, at the time of the DosAllocSeg, that the
  4992.  segment might be "given away." Doing so allows OS/2 to allocate the
  4993.  selector in the disjoint space, as we discussed earlier.
  4994.  
  4995.  
  4996.  9.2.2  Huge Memory
  4997.  The design of the 80286 microprocessor specifies the maximum size of a
  4998.  memory segment as 64 KB. For many programs, this number is far too small.
  4999.  For example, the internal representation of a large spreadsheet commonly
  5000.  takes up 256 KB or more. OS/2 can do nothing to set up a segment that is
  5001.  truly larger than 64 KB, but the OS/2 facility called huge segments
  5002.  provides a reasonable emulation of segments larger than 64 KB. The trick is
  5003.  that a huge segment of, for example, 200 KB is not a single segment but a
  5004.  group of four segments, three of which are 64 KB and a fourth of 8 KB. With
  5005.  minimal programming burden, OS/2 allows an application to treat the group
  5006.  of four segments as a single huge segment.
  5007.       When a process calls DosAllocHuge to allocate a huge segment, OS/2
  5008.  allocates several physical segments, the sum of whose size equals the size
  5009.  of the virtual huge segment. All component segments are 64 KB, except
  5010.  possibly the last one. Unlike an arbitrary collection of segment selectors,
  5011.  DosAllocHuge guarantees that the segment selectors it returns are spaced
  5012.  uniformly from each other. The selector of the N+1th component segment is
  5013.  that of the Nth segment plus i, where i is a power of two. The value of i
  5014.  is constant for any given execution of OS/2, but it may vary between
  5015.  releases of OS/2 or as a result of internal configuration during bootup. In
  5016.  other words, a program must learn the factor i every time it executes; it
  5017.  must not hard code the value. There are three ways to learn this value.
  5018.  First, a program can call DosGetHugeShift; second, it can read this value
  5019.  from the global infoseg; and third, it can reference this value as the
  5020.  undefined absolute externals DOSHUGESHIFT (log(SUB2)(i)) or
  5021.  DOSHUGEINCR (i). OS/2 will insert the proper value for these
  5022.  externals at loadtime. This last method is the most efficient and is
  5023.  recommended. Family API programs should call DosGetHugeShift. Figure 9-1
  5024.  illustrates the layout of a 200 KB huge memory object. Selectors n + 4i and
  5025.  n + 5i are currently invalid but are reserved for future growth of the
  5026.  huge object.
  5027.  
  5028.  
  5029.       ┌───────┐
  5030.       │       │
  5031.       ├───────┤
  5032.       │       │
  5033.       ├───────┤
  5034.       │       │
  5035.       ├───────┤                              ┌────────────┐
  5036.       │       │                              │            │
  5037.       ├───────┤      ┌──────────────────────│            │
  5038.  n+0i │       ├──────┼───────────────┐       │            │
  5039.       ├───────┤      │               │       └────────────┘
  5040.       │       │      │               │
  5041.       ├───────┤      │               │
  5042.  n+1i │       ├──────┼───────┐       │
  5043.       ├───────┤      │       │       │       ┌────────────┐
  5044.       │       │      │       │       │       │            │
  5045.       ├───────┤      │       │       └──────│            │
  5046.  n+2i │       ├──────┘       │               │            │
  5047.       ├───────┤              │               └────────────┘
  5048.       │       │              │
  5049.       ├───────┤              │               ┌────────────┐
  5050.  n+3i │       ├──────────────┼──────────────│            │
  5051.       ├───────┤              │               └────────────┘
  5052.       │       │              │
  5053.       ├───────┤              │
  5054.  n+4i │      │              │
  5055.       ├───────┤              │               ┌────────────┐
  5056.       │       │              │               │            │
  5057.       ├───────┤              └──────────────│            │
  5058.  n+5i │      │                              │            │
  5059.       ├───────┤                              └────────────┘
  5060.       │       │
  5061.       ├───────┤
  5062.       │       │
  5063.       ├───────┤
  5064.       │       │
  5065.       ├───────┤
  5066.       │       │
  5067.       ├───────┤
  5068.       │       │
  5069.       ├───────┤
  5070.       │       │
  5071.       ├───────┤
  5072.       │       │
  5073.       ├───────┤
  5074.       │       │
  5075.       ├───────┤
  5076.       │       │
  5077.       └───────┘
  5078.  
  5079.  Figure 9-1.  Huge memory objects.
  5080.  
  5081.  
  5082.       Once an application has the first segment selector of the huge segment
  5083.  group, called the base segment, and the value log(SUB2)(i), computing the
  5084.  address of the Nth byte in the huge segment is easy. Take the high-order
  5085.  word of the value of N (that is, N/64 KB), shift it left by log(SUB2)(i)
  5086.  (that is, by the DosHugeShift value), and add the base segment selector
  5087.  returned by DosAllocHuge. The resultant value is the segment selector for
  5088.  the proper component segment; the low-order 16 bits of i are the offset
  5089.  into that segment. This computation is reasonably quick to perform since it
  5090.  involves only a shift and an addition.
  5091.       Huge segments can be shrunk or grown via DosReallocHuge. If the huge
  5092.  segment is to be grown, creating more component physical segments may be
  5093.  necessary. Because the address generation rules dictate which selector this
  5094.  new segment may have, growing the huge segment may not be possible if that
  5095.  selector has already been allocated for another purpose. DosAllocHuge takes
  5096.  a maximum growth parameter; it uses this value to reserve sufficient
  5097.  selectors to allow the huge segment to grow that big. Applications should
  5098.  not provide an unrealistically large number for this argument because doing
  5099.  so will waste LDT selectors.
  5100.       The astute reader will notice that the segment arithmetic of the 8086
  5101.  environment is not dead; in a sense, it's been resurrected by the huge
  5102.  segment mechanism. Applications written for the 8086 frequently use this
  5103.  technique to address memory regions greater than 64 KB, using a shift value
  5104.  of 12. In other words, if you add 2^12 to an 8086 segment register value,
  5105.  the segment register will point to an address 2^12*16, or 64 KB, further in
  5106.  physical memory. The offset value between the component segment values was
  5107.  always 4096 because of the way the 8086 generated addresses. Although the
  5108.  steps involved in computing the segment value are the same in protect mode,
  5109.  what's actually happening is considerably different. When you do this
  5110.  computation in protect mode, the segment selector value has no inherent
  5111.  relationship to the other selectors that make up the huge object. The trick
  5112.  only works because OS/2 has arranged for equally spaced-out selectors to
  5113.  exist and for each to point to an area of physical memory of the
  5114.  appropriate size. Figure 9-2 illustrates the similarities and differences
  5115.  between huge model addressing in real and protect modes. The application
  5116.  code sequence is identical: A segment selector is computed by adding N*i to
  5117.  the base selector. In real mode i is always 4096; in protect mode OS/2
  5118.  provides i.
  5119.  
  5120.  
  5121.            REAL MODE        |             PROTECT MODE
  5122.                             |
  5123.             Physical        |
  5124.              Memory         |            Segment
  5125.             ┌──────┐        |            Selector         Physical
  5126.             │      │        |             Table            Memory
  5127.             │      │        |            ┌──────┐         ┌──────┐
  5128.  n+0i ─────│      │▒       |            │      │  ┌─────│      │
  5129.             │      │▒64 KB | n+0i ─────│      ├──┼───┐  └──────┘
  5130.  n+1i ─────│      │▒       |            │      │  │   │
  5131.             │      │        | n+1i ─────│      ├──┼─┐ │  ┌──────┐
  5132.  n+2i ─────│      │        |            │      │  │ │ └─│      │
  5133.             │      │        | n+2i ─────│      ├──┘ │    └──────┘
  5134.  n+3i ─────│      │        |            │      │    │    ┌──────┐
  5135.             │      │        | n+3i ─────│      ├────┼───│      │
  5136.             │      │        |            │      │    │    └──────┘
  5137.             │      │        |            │      │    │    ┌──────┐
  5138.             │      │        |            └──────┘    └───│      │
  5139.             │      │        |        i is set by OS/2     └──────┘
  5140.             │      │        |
  5141.             │      │        |
  5142.             │      │        |
  5143.             │      │        |
  5144.             └──────┘        |
  5145.             i = 4096        |
  5146.  
  5147.  Figure 9-2.  Huge model addressing in real mode and in protect mode.
  5148.  
  5149.  
  5150.       Although the similarity between 8086 segment arithmetic and OS/2 huge
  5151.  segments is only apparent, it does make it easy to write a program as a
  5152.  dual mode application. By using the shift value of 12 in real mode and
  5153.  using the OS/2 supplied value in protect mode, the same code functions
  5154.  correctly in either mode.
  5155.  
  5156.  
  5157.  9.2.3  Executing from Data Segments
  5158.  We saw that OS/2 provides the huge segment mechanism to get around the
  5159.  segment size restriction imposed by the hardware. OS/2 likewise circumvents
  5160.  another hardware restriction--the inability to execute code from data
  5161.  segments. Although the demand loading, the discarding, and the swapping of
  5162.  code segments make one use of running code from data segments--code
  5163.  overlays--obsolete, the capability is still needed. Some high-performance
  5164.  programs--the presentation manager, for example--compile "on the fly"
  5165.  special code to perform time-critical tasks, such as flipping bits in EGA
  5166.  display memory. The optimal sequence may differ depending on several
  5167.  factors, so a program may need to compile such code and execute it, gaining
  5168.  a significant increase in efficiency over some other approach. OS/2
  5169.  supports this need by means of the DosCreateCSAlias call.
  5170.       When DosCreateCSAlias is called with a selector for a data segment, it
  5171.  creates a totally different code segment selector (in the eyes of 80286
  5172.  hardware) that by some strange coincidence points to exactly the same
  5173.  memory locations as does the data segment selector. As a result, code is
  5174.  not actually executing from a data segment but from a code segment. Because
  5175.  the code segment exactly overlaps that other data segment, the desired
  5176.  effect is achieved. The programmer need only be careful to use the data
  5177.  selector when writing the segment and to use the code selector when
  5178.  executing it.
  5179.  
  5180.  
  5181.  9.2.4  Memory Suballocation
  5182.  All memory objects discussed so far have been segments. OS/2 provides a
  5183.  facility called memory suballocation that allocates pieces of memory from
  5184.  within an application's segment. Pieces of memory can be suballocated from
  5185.  within a segment, grown, shrunk, and released. OS/2 uses a classic heap
  5186.  algorithm to do this. The DosSubAlloc call uses space made available from
  5187.  earlier DosSubFrees when possible, growing the segment as necessary when
  5188.  the free heap space is insufficient. We will call the pieces of memory
  5189.  returned by DosSubAlloc heap objects.
  5190.       The memory suballocation package works within the domain of a process.
  5191.  The suballocation package doesn't allocate the memory from some "system
  5192.  pool" outside the process's address space, as does the segment allocator.
  5193.  The suballocation package doesn't even allocate segments; it manages only
  5194.  segments supplied by and owned by (or at least accessible to) the caller.
  5195.  This is a feature because memory protection is on a per-segment basis. If
  5196.  the suballocation package were to get its space from some system global
  5197.  segment, a process that overwrote its heap object could damage one
  5198.  belonging to another process. Figure 9-3 illustrates memory suballocation.
  5199.  It shows a segment j being suballocated. H is the suballocation header; the
  5200.  shaded areas are free space.
  5201.  
  5202.  
  5203.         ┌───────────┐       ┌───────────┐       ┌───────────┐
  5204.         │  Segment  │       │  Segment  │       │  Segment  │
  5205.         │     i     │       │     j     │       │     k     │
  5206.         └───────────┘       └───────────┘       └───────────┘
  5207.                            /            \
  5208.                          /                \
  5209.                        /                    \
  5210.                      /                        \
  5211.                    /                            \
  5212.                  /                                \
  5213.                /                                    \
  5214.              /                                        \
  5215.            /                                            \
  5216.          /                                                \
  5217.        /                                                    \
  5218.      /                                                        \
  5219.    /                                                            \
  5220.  ┌──┬──┬─────┬───┬┬────┬───────────┬─┬───────┬───┬────┬───┬───────┐
  5221.  │H │  │     │░░░││░░░░│           │ │░░░░░░░│   │    │   │░░░░░░░│
  5222.  │  │  │     │░░░││░░░░│           │ │░░░░░░░│   │    │   │░░░░░░░│
  5223.  └──┴──┴─────┴───┴┴────┴───────────┴─┴───────┴───┴────┴───┴───────┘
  5224.  
  5225.  Figure 9-3.  Memory suballocation.
  5226.  
  5227.  
  5228.       We said that the memory suballocator subdivides segments that are
  5229.  accessible to the client process. This means that you can use it to
  5230.  subdivide space in a shared memory segment. Such a technique can be handy
  5231.  when two or more processes are using a shared memory segment for
  5232.  intercommunication, but there is risk because an error in one process can
  5233.  easily corrupt the heap objects of another.
  5234.       Earlier, in the discussion of dynamic link subsystems, we described
  5235.  facilities and techniques for writing a reliable subsystem. The OS/2 memory
  5236.  suballocation package is a good example of such a subsystem, so let's look
  5237.  at its workings more closely. The first try at the suballocation package
  5238.  produced a straightforward heap allocator, much like the one in a C or
  5239.  Pascal runtime library. It maintained a free chain of heap objects and
  5240.  allocated them at its client's request. If the closest-size free heap
  5241.  object was still bigger than the request, it was split into an allocated
  5242.  part and a free part. Freed heap objects were coalesced with any adjacent
  5243.  free objects. The suballocation package took a segment pointer and some
  5244.  other arguments and returned some values--an offset and the changed data in
  5245.  the segment itself where the heap headers were stored. If we stretch things
  5246.  a little and consider the changed state of the supplied data segment as a
  5247.  returned value, then the suballocation package at this stage is much like a
  5248.  function: It has no state of its own; it merely returns values computed
  5249.  only from the input arguments. This simple suballocation dynlink routine
  5250.  uses no global data segments and doesn't even need an instance data
  5251.  segment.
  5252.       This simple implementation has an important drawback: More than one
  5253.  process can't safely use it to manage a shared memory segment; likewise,
  5254.  multiple threads within one process can't use it. The heap free list is a
  5255.  critical section; if multiple threads call the suballocator on the same
  5256.  segment, the heap free list can become corrupted. This problem necessitated
  5257.  upgrading the suballocation package to use a semaphore to protect the
  5258.  critical section. If we didn't want to support suballocation of shared
  5259.  memory and were only worried about multiple threads within a task, we could
  5260.  use RAM semaphores located in the managed segment itself to protect the
  5261.  critical section. The semaphore might be left set if the process died
  5262.  unexpectedly, but the managed segment isn't shared. It's going to be
  5263.  destroyed in any case, so we don't care.
  5264.       But, even in this simple situation of managing only privately owned
  5265.  segments, we must concern ourselves with some special situations. One
  5266.  problem is signals: What if the suballocator is called with thread 1, and a
  5267.  signal (such as SIGINT, meaning that the user pressed Ctrl-C) comes in?
  5268.  Thread 1 is interrupted from the suballocation critical section to execute
  5269.  the signal handler. Often signal handlers return to the interrupted code,
  5270.  and all is well. But what if the signal handler does not return but jumps
  5271.  to the application's command loop? Or what if it does return, but before it
  5272.  does so calls the memory suballocator? In these two cases, we'd have a
  5273.  deadlock on the critical section. We can solve these problems by using the
  5274.  DosHoldSignal function. DosHoldSignal does for signals what the CLI
  5275.  instruction does for hardware interrupts: It holds them off for a short
  5276.  time. Actually, it holds them off forever unless the application releases
  5277.  them, but holding signals for more than a second or two is poor practice.
  5278.  If you precede the critical section's semaphore claim call with a signal
  5279.  hold and follow the critical section's semaphore release call with a signal
  5280.  release, you're protected from deadlocks caused by signal handling.
  5281.       Note that unlike the CLI instruction, DosHoldSignal calls nest. OS/2
  5282.  counts the number of DosHoldSignal "hold" calls made and holds signals off
  5283.  until an equal number of "release" calls are issued. This means that a
  5284.  routine can safely execute a hold/release pair without affecting the state
  5285.  of its calling code. If the caller had signals held at the time of the
  5286.  call, they will remain held. If signals were free at the time of the call,
  5287.  the callee's "release" call restores them to that state.
  5288.       Whenever dynlink packages make any call that changes the state of the
  5289.  process or the thread, they must be sure to restore that state before they
  5290.  return to their caller. Functions that nest, such as DosHoldSignal,
  5291.  accomplish this automatically. For other functions, the dynlink package
  5292.  should explicitly discover and remember the previous state so that it can
  5293.  be restored.
  5294.       Our problems aren't over though. A second problem is brought about by
  5295.  the DosExitList facility. If a client process's thread is in the
  5296.  suballocation package's critical section and the client terminates
  5297.  suddenly--it could be killed externally or have a GP fault--the process
  5298.  might not die immediately. If any DosExitList handlers are registered, they
  5299.  will be called. They might call the memory suballocator, and once again we
  5300.  face deadlock. We could solve this situation with the classic approach of
  5301.  making a bug into a feature: Document that the suballocator can't be called
  5302.  at exitlist time. This may make sense for some dynlink subsystems, but it's
  5303.  too restrictive for an important OS/2 facility. We've got to deal with this
  5304.  problem too.
  5305.       The DosHoldSignal trick won't help us here. It would indeed prevent
  5306.  external kills, but it would not prevent GP faults and the like. We could
  5307.  say, "A program that GP faults is very sick, so all bets are off." This
  5308.  position is valid, except that if the program or one of its dynlink
  5309.  subsystems uses DosExitList and the DosExitList handler tries to allocate
  5310.  or release a heap object, the process will hang and never terminate
  5311.  correctly. This is unacceptable because the user would be forced to reboot
  5312.  to get rid of the moribund application. The answer is to use a system
  5313.  semaphore rather than a RAM semaphore to protect the memory segment. System
  5314.  semaphores are a bit slower than RAM semaphores, but they have some extra
  5315.  features. One is that they can be made exclusive; only the thread that owns
  5316.  the semaphore can release it. Coupled with this is an "owner death"
  5317.  notification facility that allows a process's DosExitList handler an
  5318.  opportunity to determine that one of its threads has orphaned a semaphore
  5319.  (see 16.2 Data Integrity for details). Our suballocation package can now
  5320.  protect itself by using exclusive system semaphores to protect its critical
  5321.  section and by registering a DosExitList handler to release that semaphore.
  5322.  The exitlist code can discover if a thread in its process has orphaned the
  5323.  semaphore and, if so, can release it. Of course, releasing the semaphore
  5324.  won't help if the heap headers are in an inconsistent state. You can write
  5325.  the suballocation package so that the heap is never in an inconsistent
  5326.  state, or you can write it to keep track of the modified state so that the
  5327.  exitlist handler can repair the heap structure.
  5328.       In this later case, be sure the DosExitList handler you establish to
  5329.  clean up the heap is called first (see DosExitList documentation).
  5330.       Finally, even if we decide that the client application won't be
  5331.  allowed to issue suballocation requests during its own exitlist processing,
  5332.  we want the memory suballocator to support allocating a shared segment
  5333.  among many different processes. Because of this, the actual OS/2
  5334.  suballocation package makes use of DosExitList so that the suballocation
  5335.  structure and semaphores can be cleaned up should a client thread terminate
  5336.  while in the suballocation critical section.
  5337.       The suballocation dynlink package does more than illustrate subsystem
  5338.  design; it also illustrates the value of a system architecture that uses
  5339.  dynlinks as a standard system interface, regardless of the type of code
  5340.  that provides the service. As you have seen, the memory suballocation
  5341.  package released with OS/2 version 1.0 doesn't reside in the kernel; it's
  5342.  effectively a subroutine package. OS/2 in an 80286 environment will
  5343.  undoubtedly preserve this approach in future releases, but a forthcoming
  5344.  80386 version of OS/2 may not. The 80386 architecture supports paged
  5345.  virtual memory, so memory swapping (actually, paging) can take place on
  5346.  part of a segment. This future paging environment may precipitate some
  5347.  changes in the memory suballocator. Perhaps we'll want to rearrange the
  5348.  heap for better efficiency with paging, or perhaps the OS/2 kernel will
  5349.  want to become involved so that it can better anticipate paging demands. In
  5350.  any case, any future release of OS/2 has complete flexibility to upgrade
  5351.  the memory suballocation package in any externally compatible fashion,
  5352.  thanks to the standard interface provided by dynamic links.
  5353.  
  5354.  
  5355.  9.3  Segment Swapping
  5356.  
  5357.  One of the most important features of the 80286 memory management hardware
  5358.  is swapping support. Swapping is a technique by which some code or data
  5359.  segments in memory are written to a disk file, thus allowing the memory
  5360.  they were using to be reclaimed for another purpose. Later, the swapped-out
  5361.  code or data is reloaded into memory. This technique lets you run more
  5362.  programs than can simultaneously fit in memory; all you need is enough
  5363.  memory to hold the programs that are running at that particular moment.
  5364.  Quiescent programs can be swapped to disk to make room for active ones.
  5365.  Later, when the swapped programs become active, OS/2 reads them in and
  5366.  resumes them. If necessary OS/2 first makes memory available by swapping
  5367.  out another quiescent program.
  5368.       Although I used the word program above, swapping is actually done on a
  5369.  segment basis. Segments are swapped out individually and completely; the
  5370.  OS/2 swapping code doesn't pay attention to relationships between segments
  5371.  (they aren't swapped in groups), and the 80286 hardware does not allow only
  5372.  part of a segment to be swapped. I simplified the concept a bit in the
  5373.  above paragraph. You need not swap out an entire process; you can swap out
  5374.  some segments and leave others in memory. OS/2 can and commonly does run a
  5375.  process when some of its segments are swapped out. As long as a process
  5376.  does not try to use the swapped-out segments, it runs unhindered. If a
  5377.  process references a swapped-out segment, the 80286 hardware generates a
  5378.  special trap that OS/2 intercepts. The segment fault trap handler swaps in
  5379.  the missing segment, first swapping out some other if need be, and then the
  5380.  process resumes where it left off. Segment faulting is invisible to a
  5381.  process; the process executes normally, except that a segment load
  5382.  instruction takes on the order of 30 milliseconds instead of the usual 3
  5383.  microseconds.
  5384.       When memory is depleted and a segment must be swapped, OS/2 has to
  5385.  choose one to swap out. Making the right choice is important; for example,
  5386.  consider a process that alternates references between segment A and segment
  5387.  B. If A is swapped out, a poorly designed system might choose B to swap out
  5388.  to make room for A. After a few instructions are executed, B has to be
  5389.  swapped in. If A is in turn swapped out to make room for B, the system
  5390.  would soon spend all its time swapping A and B to and from the disk. This
  5391.  is called thrashing, and thrashing can destroy system performance. In other
  5392.  words, the effect of swapping is to make some segment loads take 10,000
  5393.  times longer than they would if the segment were in memory. Although the
  5394.  number 10,000 seems very large, the actual time of about 30 milliseconds is
  5395.  not, as long as we don't have to pay those 30 milliseconds very often.
  5396.       A lot hinges on choosing segments to swap out that won't be referenced
  5397.  in the near future. OS/2 uses the LRU (Least Recently Used) scheme to
  5398.  determine which segment it will swap out. The ideal choice is the segment--
  5399.  among those currently in memory--that will be referenced last because this
  5400.  postpones the swap-in of that segment as long as possible. Unfortunately,
  5401.  it's mathematically provable that no operating system can predict the
  5402.  behavior of arbitrary processes. Instead, operating systems try to make an
  5403.  educated guess as to which segment in memory is least likely to be
  5404.  referenced in the immediate future. The LRU scheme is precisely that--a
  5405.  good guess. OS/2 figures that if a segment hasn't been used in a long time
  5406.  then it probably won't be used for a long time yet, so it swaps out the
  5407.  segment that was last used the longest time ago--in other words, the least
  5408.  recently used segment.
  5409.       Of course, it's easy to construct an example where the LRU decision is
  5410.  the wrong one or even the worst one. The classic example is a program that
  5411.  references, round robin, N segments when there is room in memory for only
  5412.  N-1. When you attempt to make room for segment I, the least recently used
  5413.  segment will be I+1, which in fact is the segment that will next be used. A
  5414.  discussion of reference locality and working set problems, as these are
  5415.  called, is beyond the scope of this book. Authors of programs that will
  5416.  make repetitious accesses to large bodies of data or code should study the
  5417.  available literature on virtual memory systems. Remember, on an 80286, OS/2
  5418.  swaps only on a segment basis. A future 80386 release of OS/2 will swap, or
  5419.  page, on a 4 KB page basis.
  5420.       The swapping algorithm is strictly LRU among all swap-eligible
  5421.  segments in the system. Thread/process priority is not considered; system
  5422.  segments that are marked swappable get no special treatment. Some system
  5423.  segments are marked nonswappable, however. For example, swapping out the
  5424.  OS/2 code that performs swap-ins would be embarrassing. Likewise, the disk
  5425.  driver code for the swapping disk must not be swapped out. Some kernel and
  5426.  device driver code is called at interrupt time; this is never swapped
  5427.  because of the swap-in delay and because of potential interference between
  5428.  the swapped-out interrupt handling code and the interrupt handling code of
  5429.  the disk driver that will do the swap-in. Finally, some kernel code is
  5430.  called in real mode in response to requests from the 3x box. No real mode
  5431.  code can be swapped because the processor does not support segment faults
  5432.  when running in real mode.
  5433.       The technique of running more programs then there is RAM to hold them
  5434.  is called memory overcommit. OS/2 has to keep careful track of the degree
  5435.  of overcommit so that it doesn't find itself with too much of a good thing-
  5436.  -not enough free RAM, even with swapping, to swap in a swapped-out process.
  5437.  Such a situation is doubly painful: Not only can the user not access or
  5438.  save the data that he or she has spent the last four hours working on, but
  5439.  OS/2 can't even tell the program what's wrong because it can't get the
  5440.  program into memory to run it. To prevent this, OS/2 keeps track of its
  5441.  commitments and overcommitments in two ways. First, before it starts a
  5442.  process, OS/2 ensures that there is enough swap space to run it. Second, it
  5443.  ensures that there is always enough available RAM to execute a swapped-out
  5444.  process.
  5445.       At first glance, knowing if RAM is sufficient to run a process seems
  5446.  simple--either the process fits into memory or it doesn't. Life is a bit
  5447.  more complicated than that under OS/2 because the segments of a program or
  5448.  a dynlink library may be marked for demand loading. This means that they
  5449.  won't come in when the program starts executing but may be called in later.
  5450.  Obviously, once a program starts executing, it can make nearly unlimited
  5451.  demands for memory. When a program requests a memory allocation, however,
  5452.  OS/2 can return an error code if available memory is insufficient. The
  5453.  program can then deal with the problem: make do with less, refuse the
  5454.  user's command, and so forth.
  5455.       OS/2 isn't concerned about a program's explicit memory requests
  5456.  because they can always be refused; the implicit memory requests are the
  5457.  problem--faulting in a demand load segment, for example. Not only is there
  5458.  no interface to give the program an error code,4 but the program may be
  5459.  unable to proceed without the segment. As a result, when a program is first
  5460.  loaded (via a DosExecPgm call), OS/2 sums the size of all its impure
  5461.  segments even if they are marked for "load on demand." The same computation
  5462.  is done for all the loadtime dynlink libraries it references and for all
  5463.  the libraries they reference and so on. This final number, plus the
  5464.  internal system per-process overhead, is the maximum implicit memory demand
  5465.  of the program. If that much free swap space is available, the program can
  5466.  start execution.
  5467.       You have undoubtedly noticed that I said we could run the program if
  5468.  there was enough swap space. But a program must be in RAM to execute,
  5469.  so why don't we care about the amount of available RAM space? We
  5470.  do care. Not about the actual amount of free RAM when we start a program,
  5471.  but about the amount of RAM that can be made free--by swapping--if needed.
  5472.  If some RAM contains a swappable segment, then we can swap it
  5473.  because we set aside enough swap space for the task. Pure segments, by the
  5474.  way, are not normally swapped. In lieu of a swap-out, OS/2 simply discards
  5475.  them. When it's time to swap them in, OS/2 reloads them from their original
  5476.  .EXE or .DLL files.5
  5477.       Because not all segments of a process need to be in memory for the
  5478.  process to execute, we don't have to ensure enough free RAM for the entire
  5479.  process, just enough so that we can simultaneously load six 64 KB segments-
  5480.  -the maximum amount of memory needed to run any process. The numbers 6 and
  5481.  64 KB are derived from the design of the 80286. To execute even a single
  5482.  instruction of a process, all the segments selected by the four segment
  5483.  registers must be in memory. The other two necessary segments come from the
  5484.  worst case scenario of a program trying to execute a far return instruction
  5485.  from a ring 2 segment (see 18.1 I/O Privilege Mechanism). The four
  5486.  segments named in the registers must be present for the instruction to
  5487.  start, and the two new segments--CS and SS--that the far return instruction
  5488.  will reference must be present for the instruction to complete. That makes
  5489.  six; the 64 KB comes from the maximum size a segment can reach. As a
  5490.  result, as long as OS/2 can free up those six 64 KB memory regions, by
  5491.  swapping and discarding if necessary, any swapped-out program can execute.
  5492.       Naturally, if that were the only available memory and it had to be
  5493.  shared by all running processes, system response would be very poor.
  5494.  Normally, much more RAM space is available. The memory overcommit code is
  5495.  concerned only that all processes can run; it won't refuse to start a
  5496.  process because it might execute slowly. It could be that the applications
  5497.  that a particular user runs and their usage pattern are such that the user
  5498.  finds the performance acceptable and thus hasn't bought more memory. Or
  5499.  perhaps the slowness is a rare occurrence, and the user is willing to
  5500.  accept it just this once. In general, if the system thrashes--
  5501.  spends too much time swapping--it's a soft failure: The user knows what's
  5502.  wrong, the user knows what to do to make it get better (run fewer programs
  5503.  or buy more memory), and the user can meanwhile continue to work.
  5504.       Clearly, because all segments of the applications are swappable and
  5505.  because we've ensured that the swap space is sufficient for all of them,
  5506.  initiating a new process doesn't consume any of our free or freeable RAM.
  5507.  It's the device drivers and their ability to allocate nonswappable segments
  5508.  that can drain the RAM pool. For this reason, OS/2 may refuse to load a
  5509.  device driver or to honor a device driver's memory allocation request if to
  5510.  do so would leave less than six 64 KB areas of RAM available.
  5511.  
  5512.  
  5513.  9.3.1  Swapping Miscellany
  5514.  The system swap space consists of a special file, called SWAPPER.DAT,
  5515.  created at boot time. The location of the file is described in the
  5516.  CONFIG.SYS file. OS/2 may not allocate the entire maximum size of the swap
  5517.  file initially; instead, it may allocate a smaller size and grow the swap
  5518.  file to its maximum size if needed. The swap file may grow, but in OS/2
  5519.  version 1.0 it never shrinks.
  5520.       The available swap space in the system is more than the maximum size
  5521.  of the swap file; it also includes extra RAM. Clearly, a system with 8 MB
  5522.  of RAM and a 200 KB swap file should be able to run programs that consume
  5523.  more than 200 KB. After setting aside the memory consumed by nonswappable
  5524.  segments and our six 64 KB reserved areas, the remaining RAM is considered
  5525.  part of the swap file for memory overcommit accounting purposes.
  5526.       We mentioned in passing that memory used in real mode can't be
  5527.  swapped. This means that the entire 3x box memory area is nonswappable. In
  5528.  fact, the casual attitude of MS-DOS applications toward memory allocation
  5529.  forces OS/2 to keep a strict boundary between real mode and protect mode
  5530.  memory. Memory below the RMSIZE value specified in CONFIG.SYS belongs
  5531.  exclusively to the real mode program, minus that consumed by the device
  5532.  drivers and the parts of the OS/2 kernel that run in real mode.
  5533.       Early in the development of OS/2, attempts were made to put protect
  5534.  mode segments into any unused real mode memory, but we abandoned this
  5535.  approach. First, because the risk was great that the real mode program
  5536.  might overwrite part of the segment. Although this is technically a bug on
  5537.  the part of the real mode application, such bugs generally do not affect
  5538.  program execution in an MS-DOS environment because that memory is unused at
  5539.  the time. Thus, such bugs undoubtedly exist unnoticed in today's MS-DOS
  5540.  applications, waiting to wreak havoc in the OS/2 environment.
  5541.       A second reason concerns existing real mode applications having been
  5542.  written for a single-tasking environment. Such an application commonly asks
  5543.  for 1 MB of memory, a request that must be refused. The refusal, however,
  5544.  also specifies the amount of memory available at the time of the call. Real
  5545.  mode applications then turn around and ask for that amount, but they don't
  5546.  check to see if an "insufficient memory" error code was returned from the
  5547.  second call. After all, how could such a code be returned? The operating
  5548.  system has just said that the memory was available. This coding sequence
  5549.  can cause disaster in a multitasking environment where the memory might
  5550.  have been allocated elsewhere between the first and second call from the
  5551.  application. This is another reason OS/2 sets aside a fixed region of
  5552.  memory for the 3x box and never uses it for other purposes, even if it
  5553.  appears to be idle.
  5554.       We mentioned that OS/2's primary concern is that programs be able to
  5555.  execute at all; whether they execute well is the user's problem. This
  5556.  approach is acceptable because OS/2 is a single-user system. Multiuser
  5557.  systems need to deal with thrashing situations because the users that
  5558.  suffer from thrashing may not be the ones who created it and may be
  5559.  powerless to alleviate it. In a single-user environment, however, the user
  5560.  is responsible for the load that caused the thrashing, the user is the one
  5561.  who is suffering from it, and the user is the one who can fix the situation
  5562.  by buying more RAM or terminating a few applications. Nevertheless,
  5563.  applications with considerable memory needs should be written so as to
  5564.  minimize their impact on the system swapper.
  5565.       Fundamentally, all swapping optimization techniques boil down to one
  5566.  issue: locality of reference. This means keeping the memory locations that
  5567.  are referenced near one another in time and in space. If your program
  5568.  supports five functions, put the code of each function in a separate
  5569.  segment, with another segment holding common code. The user can then work
  5570.  with one function, and the other segments can be swapped. If each function
  5571.  had some code in each of five segments, all segments would have to be in
  5572.  memory at all times.
  5573.       A large body of literature deals with these issues because of the
  5574.  prevalence of virtual memory systems in the mainframe environment. Most of
  5575.  this work was done when RAM was very expensive. To precisely determine
  5576.  which segments or pages should be resident and which should be swapped was
  5577.  worth a great deal of effort. Memory was costly, and swapping devices were
  5578.  fast, so algorithms were designed to "crank the screws down tight" and free
  5579.  up as much memory as possible. After all, if they misjudged and swapped
  5580.  something that was needed soon, it could be brought back in quickly. The
  5581.  OS/2 environment is inverted: RAM is comparatively cheap, and the swapping
  5582.  disk, being the regular system hard disk, is comparatively slow.
  5583.  Consequently, OS/2's swapping strategy is to identify segments that are
  5584.  clearly idle and swap them (because cheap RAM doesn't mean free RAM) but
  5585.  not to judge things so closely that segments are frequently swapped when
  5586.  they should not be.
  5587.       A key concept derived from this classic virtual memory work is that of
  5588.  the working set. A thread's working set is the set of segments it will
  5589.  reference "soon"--in the next several seconds or few minutes. Programmers
  5590.  should analyze their code to determine its working sets; obviously the set
  5591.  of segments in the working set will vary with the work the application is
  5592.  doing. Code and data should be arranged between segments so that the size
  5593.  of each common working set consists of a minimum amount of memory. For
  5594.  example, if a program contains extensive code and data to deal with
  5595.  uncommon error situations, these items should reside in separate segments
  5596.  so that they aren't resident except when needed. You don't want to burden
  5597.  the system with too many segments; two functions that are frequently used
  5598.  together should occupy the same segment, but large unrelated bodies of code
  5599.  and data should have their own segments or be grouped with other items that
  5600.  are in their working set. Consider segment size when packing items into
  5601.  segments. Too many small segments increase system overhead; large segments
  5602.  decrease the efficiency of the swap mechanism. Splitting a segment in two
  5603.  doesn't make sense if all code in the segment belongs to the same working
  5604.  set, but it does make sense to split large bodies of unrelated code and
  5605.  data.
  5606.       As we said before, an exhaustive discussion of these issues is beyond
  5607.  the scope of this book. Programmers writing memory-intensive applications
  5608.  should study the literature and their programs to optimize their
  5609.  performance in an OS/2 environment. Minimizing an application's memory
  5610.  requirements is more than being a "good citizen"; the smaller a program's
  5611.  working set, the better it will run when the system load picks up.
  5612.  
  5613.  
  5614.  9.4  Status and Information
  5615.  
  5616.  OS/2 takes advantage of the 80286 LDT and GDT architecture in providing two
  5617.  special segments, called infosegs, that contain system information. OS/2
  5618.  updates these segments when changes take place, so their information is
  5619.  always current. One infoseg is global, and the other is local. The global
  5620.  infoseg contains information about the system as a whole; the local infoseg
  5621.  contains process specific data. Naturally, the global infoseg is read only
  5622.  and is shared among all processes. Local infosegs are also read only, but
  5623.  each process has its own.
  5624.       The global infoseg contains time and date information. The "seconds
  5625.  elapsed since 1970" field is particularly useful for time-stamping events
  5626.  because calculating the interval between two times is easy. Simply subtract
  5627.  and then divide by the number of seconds in the unit of time in which
  5628.  you're interested. It's important that you remember that the date/time
  5629.  fields are 32-bit fields but the 80286 reads data 16 bits at a time. Thus,
  5630.  if an application reads the two time-stamp words at the same time as they
  5631.  are being updated, it may read a bad value--not a value off by 1, but a
  5632.  value that is off by 63335. The easiest way to deal with this is to read
  5633.  the value and then compare the just read value with the infoseg contents.
  5634.  If they are the same, your read value is correct. If they differ, continue
  5635.  reading and comparing until the read and infoseg  values agree. The RAS6
  5636.  information is used for field system diagnosis and is not of general
  5637.  interest to programmers.
  5638.       The local infoseg segment contains process and thread information. The
  5639.  information is accurate for the currently executing thread. The subscreen
  5640.  group value is used by the presentation manager subsystem and is not of
  5641.  value to applications. For more information on global and local infosegs,
  5642.  see the OS/2 reference manual.
  5643.  
  5644.  
  5645.  
  5646.  10  Environment Strings
  5647.  
  5648.  ───────────────────────────────────────────────────────────────────────────
  5649.  
  5650.  A major requirement of OS/2 is the ability to support logical device and
  5651.  directory names. For example, a program needs to write a temporary scratch
  5652.  file to the user's fastest disk. Which disk is that? Is it drive C, the
  5653.  hard disk? Some machines don't have a hard disk. Is it drive B, the floppy
  5654.  drive? Some machines don't have a drive B. And even if a hard disk on drive
  5655.  C exists, maybe drive D also exists and has more free space. Or perhaps
  5656.  drive E is preferred because it's a RAM disk. Perhaps it's not, though,
  5657.  because the user wants the scratch file preserved when the machine is
  5658.  powered down. This program needs the ability to specify a logical
  5659.  directory--the scratch file directory--rather than a physical drive and
  5660.  directory such as A:\ or C:\TEMP. The user could then specify the physical
  5661.  location (drive and directory) that corresponds to the logical
  5662.  directory.
  5663.       Another example is a spell-checker program that stores two
  5664.  dictionaries on a disk. Presumably, the dictionary files were copied to a
  5665.  hard disk when the program was installed, but on which drive and directory?
  5666.  The checker's author could certainly hard code a directory such as
  5667.  C:\SPELLCHK\DICT1. But what if the user doesn't have a C drive, or what if
  5668.  drive C is full and the user wants to use drive D instead? How can this
  5669.  program offer the user the flexibility of putting the dictionary files
  5670.  where they best fit and yet still find them when it needs them?
  5671.       The answer to these problems is a logical device and directory name
  5672.  facility. Such a facility should have three characteristics:
  5673.  
  5674.       ■  It should allow the user to map the logical directories onto the
  5675.          actual (physical) devices and directories at will. It should be
  5676.          possible to change these mappings without changing the programs
  5677.          that use them.
  5678.  
  5679.       ■  The set of possible logical devices and directories should be very
  5680.          large and arbitrarily expandable. Some new program, such as our
  5681.          spelling checker, will always need a new logical directory.
  5682.  
  5683.       ■  The name set should be large and collision free. Many programs will
  5684.          want to use logical directory names. If all names must come from a
  5685.          small set of possibilities, such as X1, X2, X3, and so on, two
  5686.          applications, written independently, may each choose the same name
  5687.          for conflicting uses.
  5688.  
  5689.       The original version of MS-DOS did not provide for logical devices and
  5690.  directories. In those days a maximum PC configuration consisted of two
  5691.  floppy disks. Operating the machine entailed playing a lot of "disk jockey"
  5692.  as the user moved system, program, and data disks in and out of the drives.
  5693.  The user was the only one who could judge which drive should contain which
  5694.  floppy and its associated data, and data files moved from drive to drive
  5695.  dynamically. A logical device mechanism would have been of little use.
  5696.  Logical directories were not needed because MS-DOS version 1.0 didn't
  5697.  support directories. MS-DOS versions 2.x and 3.x propagated the "physical
  5698.  names only" architecture because of memory limitations and because of the
  5699.  catch-22 of new operating system features: Applications won't take
  5700.  advantage of the new feature because many machines are running older
  5701.  versions of MS-DOS without that new feature.
  5702.       None of these reasons holds true for OS/2. All OS/2 protect mode
  5703.  applications will be rewritten. OS/2 has access to plenty of memory.
  5704.  Finally, OS/2 needs a logical drive/directory mechanism: All OS/2 machines
  5705.  have hard disks or similar facilities, and all OS/2 machines will run a
  5706.  variety of sophisticated applications that need access to private files and
  5707.  work areas. As a result, the environment string mechanism in MS-DOS has
  5708.  been expanded to serve as the logical name in OS/2.
  5709.       Because of the memory allocation techniques employed by MS-DOS
  5710.  programs and because of the lack of segment motion and swapping in real
  5711.  mode, the MS-DOS environment list was very limited in size. The size of the
  5712.  environment segment was easily exceeded. OS/2 allows environment segments
  5713.  to be grown arbitrarily, at any time, subject only to the hardware's 64 KB
  5714.  length limitation. In keeping with the OS/2 architecture, each process has
  5715.  its own environment segment. By default, the child inherits a copy of the
  5716.  parent's segment, but the parent can substitute other environment values at
  5717.  DosExecPgm time.
  5718.       Using the environment string facility to provide logical names is
  5719.  straightforward. If a convention for the logical name that you need doesn't
  5720.  already exist, you must choose a meaningful name. Your installation
  5721.  instructions or software should document how to use the environment string;
  5722.  the application should display an error message or use an appropriate
  5723.  default if the logical names do not appear in the environment string.
  5724.  Because each process has its own environment segment that it inherited from
  5725.  its parent, batch files, startup scripts, and initiator programs that load
  5726.  applications can conveniently set up the necessary strings. This also
  5727.  allows several applications or multiple copies of the same application to
  5728.  define the same logical name differently.
  5729.       The existing conventions are:
  5730.  
  5731.       PATH=
  5732.       PATH defines a list of directories that CMD.EXE searches when it has
  5733.       been instructed to execute a program. The directories are searched
  5734.       from left to right and are separated by semicolons. For example,
  5735.  
  5736.          PATH=C:\BIN;D:\TOOLS;.
  5737.  
  5738.       means search C:\BIN first, D:\TOOLS second, and the current working
  5739.       directory third.
  5740.  
  5741.       DPATH=
  5742.       DPATH defines a list of directories that programs may search to locate
  5743.       a data file. The directories are searched from left to right and are
  5744.       separated by semicolons. For example:
  5745.  
  5746.          DPATH=C:\DBM;D:\TEMP;.
  5747.  
  5748.  Applications use DPATH as a convenience to the user: A user can work from
  5749.  one directory and reference data files in another directory, named in the
  5750.  DPATH string, without specifying the full path names of the data files.
  5751.  Obviously, applications and users must use this technique with care.
  5752.  Searching too widely for a filename is extremely dangerous; the wrong file
  5753.  may be found because filenames themselves are often duplicated in different
  5754.  directories. To use the DPATH string, an application must first use
  5755.  DosScanEnv to locate the DPATH string, and then it must use DosSearchPath
  5756.  to locate the data file.
  5757.  
  5758.       INCLUDE=
  5759.       The INCLUDE name defines the drive and directory where compiler and
  5760.       assembler standard include files are located.
  5761.  
  5762.       INIT=
  5763.       The INIT name defines the drive and directory that contains
  5764.       initialization and configuration information for the application. For
  5765.       example, some applications define files that contain the user's
  5766.       preferred defaults. These files might be stored in this directory.
  5767.  
  5768.       LIB=
  5769.       The LIB name defines the drive and directory where the standard
  5770.       language library modules are kept.
  5771.  
  5772.       PROMPT=
  5773.       The PROMPT name defines the CMD.EXE prompt string. Special character
  5774.       sequences are defined so that the CMD.EXE prompt can contain the
  5775.       working directory, the date and time, and so on. See CMD.EXE
  5776.       documentation for details.
  5777.  
  5778.       TEMP=
  5779.       The TEMP name defines the drive and directory for temporary files.
  5780.       This directory is on a device that is relatively fast and has
  5781.       sufficient room for scratch files. The TEMP directory should be
  5782.       considered volatile; its contents can be lost during a reboot
  5783.       operation.
  5784.  
  5785.       The environment segment is a very flexible tool that you can use to
  5786.  customize the environment of an application or a group of applications. For
  5787.  example, you can use environment strings to specify default options for
  5788.  applications. Users can use the same systemwide default or change that
  5789.  value for a particular screen group or activation of the application.
  5790.  
  5791.  
  5792.  
  5793.  11  Interprocess Communication
  5794.  
  5795.  ───────────────────────────────────────────────────────────────────────────
  5796.  
  5797.  Interprocess Communication (IPC) is central to OS/2. As we discussed
  5798.  earlier, effective IPC is needed to support both the tool-based
  5799.  architecture and the dynlink interface for interprocess services. Because
  5800.  IPC is so important, OS/2 provides several forms to fulfill a variety of
  5801.  needs.
  5802.  
  5803.  
  5804.  11.1  Shared Memory
  5805.  
  5806.  Shared memory has already been discussed in some detail. To summarize, the
  5807.  two forms are named shared memory (access is requested by the client by
  5808.  name) and giveaway shared memory (a current owner gives access to another
  5809.  process). Shared memory is the most efficient form of IPC because no data
  5810.  copying or calls to the operating system kernel are involved once the
  5811.  shared memory has been set up. Shared memory does require more effort on
  5812.  the part of the client processes; a protocol must be established,
  5813.  semaphores and flags are usually needed, and exposure to amok programs and
  5814.  premature termination must be considered. Applications that expect to deal
  5815.  with a low volume of data may want to consider using named pipes.
  5816.  
  5817.  
  5818.  11.2  Semaphores
  5819.  
  5820.  A semaphore is a flag or a signal. In its basic form a semaphore has only
  5821.  two states--on and off or stop and go. A railroad semaphore, for example,
  5822.  is either red or green--stop or go. In computer software, a semaphore is a
  5823.  flag or a signal used by one thread of execution to flag or signal
  5824.  another. Often it's for purposes of mutual exclusion: "I'm in here, stay
  5825.  out." But sometimes it can be used to indicate other events: "Your data is
  5826.  ready."
  5827.       OS/2 supports two kinds of semaphores, each of which can be used in
  5828.  two different ways. The two kinds of semaphores--RAM semaphores and system
  5829.  semaphores--have a lot in common, and the same system API is used to
  5830.  manipulate both. A RAM semaphore, as its name implies, uses a 4-byte data
  5831.  structure kept in a RAM location that must be accessible to all threads
  5832.  that use it. The system API that manipulates RAM semaphores is located in a
  5833.  dynlink subsystem. This code claims semaphores with an atomic test-and-set
  5834.  operation,1 so it need not enter the kernel (ring 0) to protect itself
  5835.  against preemption. As a result, the most common tasks--claiming a free
  5836.  semaphore and freeing a semaphore that has no waiters--are very fast, on
  5837.  the order of 100 microseconds on a 6-MHz 1-wait-state IBM PC/AT.2 If the
  5838.  semaphore is already claimed and the caller must block or if another thread
  5839.  is waiting on the semaphore, the semaphore dynlink package must enter
  5840.  kernel mode.
  5841.       System semaphores, on the other hand, use a data structure that is
  5842.  kept in system memory outside the address space of any process. Therefore,
  5843.  system semaphore operations are slower than RAM semaphore operations, on
  5844.  the order of 350 microseconds for an uncontested semaphore claim. Some
  5845.  important advantages offset this operating speed however. System semaphores
  5846.  support mechanisms that prevent deadlock by crashing programs, and system
  5847.  semaphores support exclusivity and counting features. As a general rule,
  5848.  you should use RAM semaphores when the requirement is wholly contained
  5849.  within one process. When multiple processes may be involved, use system
  5850.  semaphores.
  5851.       The first step, regardless of the type or use of the semaphore, is to
  5852.  create it. An application creates RAM semaphores simply by allocating a 4-
  5853.  byte area of memory initialized to zero. The far address of this area is
  5854.  the RAM semaphore handle. The DosCreateSem call creates system semaphores.
  5855.  (The DosCreateSem call takes an exclusivity argument, which we'll discuss
  5856.  later.) Although semaphores control thread execution, semaphore handles
  5857.  are owned by the process. Once a semaphore is created and its handle
  5858.  obtained, all threads in that process can use that handle. Other
  5859.  processes must open the semaphore via DosOpenSem. There is no explicit
  5860.  open for a RAM semaphore. To be useful for IPC, the RAM semaphore must
  5861.  be in a shared memory segment so that another process can access it;
  5862.  the other process simply learns the far address of the RAM semaphore. A RAM
  5863.  semaphore is initialized by zeroing out its 4-byte memory area.
  5864.       Except for opening and closing, RAM and system semaphores use exactly
  5865.  the same OS/2 semaphore calls. Each semaphore call takes a semaphore handle
  5866.  as an argument. A RAM semaphore's handle is its address; a system
  5867.  semaphore's handle was returned by the create or open call. The OS/2
  5868.  semaphore routines can distinguish between RAM and system semaphores by
  5869.  examining the handle they are passed. Because system semaphores and their
  5870.  names are kept in an internal OS/2 data area, they are a finite resource;
  5871.  the number of RAM semaphores is limited only by the amount of available RAM
  5872.  to hold them.
  5873.       The most common use of semaphores is to protect critical sections. To
  5874.  reiterate, a critical section is a body of code that manipulates a data
  5875.  resource in a nonreentrant way. In other words, a critical section will
  5876.  screw up if two threads call it at the same time on the same data resource.
  5877.  A critical section can cover more than one section of code; if one
  5878.  subroutine adds entries to a table and another subroutine removes entries,
  5879.  both subroutines are in the table's critical section. A critical section is
  5880.  much like an airplane washroom, and the semaphore is like the sign that
  5881.  says "Occupied." The first user sets the semaphore and starts manipulating
  5882.  the resource; meanwhile others arrive, see that the semaphore is set, and
  5883.  block (that is, wait) outside. When the critical section becomes available
  5884.  and the semaphore is cleared, only one of the waiting threads gets to claim
  5885.  it; the others keep on waiting.
  5886.       Using semaphores to protect critical sections is straightforward. At
  5887.  the top of a section of code that will manipulate the critical resource,
  5888.  insert a call to DosSemRequest. When this call returns, the semaphore is
  5889.  claimed, and the code can proceed. When the code is finished and the
  5890.  critical section is "clean," call DosSemClear. DosSemClear releases the
  5891.  semaphore and reactivates any thread waiting on it.
  5892.       System semaphores are different from RAM semaphores in this
  5893.  application in one critical respect. If a system semaphore is created for
  5894.  exclusive use, it can be used as a counting semaphore. Exclusive use means
  5895.  that only the thread that set the semaphore can clear it;3 this is expected
  5896.  when protecting critical sections. A counting semaphore can be set many
  5897.  times but must be released an equal number of times before it becomes free.
  5898.  For example, an application contains function A and function B, each of
  5899.  which manipulates the same critical section. Each claims the semaphore at
  5900.  its beginning and releases it at its end. However, under some
  5901.  circumstances, function A may need to call function B. Function A can't
  5902.  release the semaphore before it calls B because it's still in the critical
  5903.  section and the data is in an inconsistent state. But when B issues
  5904.  DosSemRequest on the semaphore, it blocks because the semaphore was already
  5905.  set by A.
  5906.       A counting semaphore solves this problem. When function B makes the
  5907.  second, redundant DosSemRequest call, OS/2 recognizes it as the same thread
  5908.  that already owns the semaphore, and instead of blocking the thread, it
  5909.  increments a counter to show that the semaphore has been claimed twice.
  5910.  Later, when function B releases the semaphore, OS/2 decrements the counter.
  5911.  Because the counter is not at zero, the semaphore is not really clear and
  5912.  thus not released. The semaphore is truly released only after function B
  5913.  returns to function A, and A, finishing its work, releases the semaphore a
  5914.  second time.
  5915.       A second major use of semaphores is signaling (unrelated to the signal
  5916.  facility of OS/2). Signaling is using semaphores to notify threads that
  5917.  certain events or activities have taken place. For example, consider a
  5918.  multithreaded application that uses one thread to communicate over a serial
  5919.  port and another thread to compute with the results of that communication.
  5920.  The computing thread tells the communication thread to send a message and
  5921.  get a reply, and then it goes about its own business. Later, the computing
  5922.  thread wants to block until the reply is received but only if the reply
  5923.  hasn't already been received--it may have already arrived, in which case
  5924.  the computing thread doesn't want to block.
  5925.       You can handle this by using a semaphore as a flag. The computing
  5926.  thread sets the semaphore via DosSemSet before it gives the order to the
  5927.  communications thread. When the computing thread is ready to wait for the
  5928.  reply, it does a DosSemWait on the semaphore it set earlier. When the
  5929.  communications thread receives the reply, it clears the semaphore. When the
  5930.  computing thread calls DosSemWait, it will continue without
  5931.  delay if the semaphore is already clear. Otherwise, the computing thread
  5932.  blocks until the semaphore is cleared. In this example, we aren't
  5933.  protecting a critical section; we're using the semaphore transition
  5934.  from set to clear to flag an event between multiple threads. Our needs are
  5935.  the opposite of a critical section semaphore: We don't want the semaphore
  5936.  to be exclusively owned; if it were, the communications thread couldn't
  5937.  release it. We also don't want the semaphore to be counting. If it counts,
  5938.  the computing thread won't block when it does the DosSemWait; OS/2 would
  5939.  recognize that it did the DosSemSet earlier and would increment the
  5940.  semaphore counter.
  5941.       OS/2 itself uses semaphore signaling in this fashion when asynchronous
  5942.  communication is needed. For example, asynchronous I/O uses semaphores in
  5943.  the signaling mode to indicate that an I/O operation has completed. The
  5944.  system timer services use semaphores in the signaling mode to indicate that
  5945.  the specified time has elapsed. OS/2 supports a special form of semaphore
  5946.  waiting, called DosMuxSemWait, which allows a thread to wait on more than
  5947.  one semaphore at one time. As soon as any specified semaphore becomes
  5948.  clear, DosMuxSemWait returns. DosMuxSemWait, like DosSemWait, only waits
  5949.  for a semaphore to become clear; it doesn't set or claim the semaphore as
  5950.  does DosSemRequest. DosMuxSemWait allows a thread to wait on a variety of
  5951.  events and to wake up whenever one of those events occurs.
  5952.  
  5953.  
  5954.  11.2.1  Semaphore Recovery
  5955.  We discussed earlier some difficulties that can arise if a semaphore is
  5956.  left set "orphaned" when its owner terminates unexpectedly. We'll review
  5957.  the topic because it's critical that applications handle the situation
  5958.  correctly and because that correctness generally has to be demonstrable by
  5959.  inspection. It's very difficult to demonstrate and fix timing-related bugs
  5960.  by just testing a program.
  5961.       Semaphores can become orphaned in at least four ways:
  5962.  
  5963.       1.  An incoming signal can divert the CPU, and the signal handler can
  5964.           fail to return to the point of interruption.
  5965.  
  5966.       2.  A process can kill another process without warning.
  5967.  
  5968.       3.  A process can incur a GP fault, which is fatal.
  5969.  
  5970.       4.  A process can malfunction because of a coding error and fail to
  5971.           release a semaphore.
  5972.  
  5973.       The action to take in such events depends on how semaphores are being
  5974.  used. In some situations, no action is needed. Our example of the computing
  5975.  and communications threads is such a situation. If the process dies, the
  5976.  semaphore and all its users die. Special treatment is necessary only if the
  5977.  application uses DosExitList to run code that needs to use the semaphore.
  5978.  This should rarely be necessary because semaphores are used within a
  5979.  process to coordinate multiple threads and only one thread remains
  5980.  when the exitlist is activated. Likewise, a process can receive signals
  5981.  only if it has asked for them, so an application that does not use signals
  5982.  need not worry about their interrupting its critical sections. An
  5983.  application that does use signals can use DosHoldSignal, always return
  5984.  from a signal handler, or prevent thread 1 (the signal-handling thread)
  5985.  from entering critical sections.
  5986.       In other situations, the semaphore can protect a recoverable resource.
  5987.  For example, you can use a system semaphore to protect access to a printer
  5988.  that for some reason is being dealt with directly by applications rather
  5989.  than by the system spooler. If the owner of the "I'm using the printer"
  5990.  system semaphore dies unexpectedly, the next thread that tries to claim the
  5991.  semaphore will be able to do so but will receive a special error code that
  5992.  says, "The owner of this semaphore died while holding it." In such a case,
  5993.  the application can simply write a form feed or two to the printer and
  5994.  continue. Other possible actions are to clean up the protected resource or
  5995.  to execute a process that will do so. Finally, an application can display a
  5996.  message to the user saying, "Gee, this database is corrupt! You better do
  5997.  something," and then terminate. In this case, the application should
  5998.  deliberately terminate while holding the semaphore so that any other
  5999.  threads waiting on it will receive the "owner died" message. Once the
  6000.  "owner died" code is received, that state is cleared; so if the recipient
  6001.  of the code releases the semaphore without fixing the inconsistencies in
  6002.  the critical section, problems will result.
  6003.       Additional matters must be considered if a process intends to clean up
  6004.  its own semaphores by means of a DosExitList handler. First, exclusive
  6005.  (that is, counting) semaphores must be used. Although an exitlist routine
  6006.  can tell that a RAM or nonexclusive system semaphore is reserved, it cannot
  6007.  tell whether it is the process that reserved it. You may be tempted simply
  6008.  to keep a flag byte that is set each time the semaphore is claimed and
  6009.  cleared each time the semaphore is released, but that solution contains a
  6010.  potentially deadly window of failure. If the thread sets the "I own it"
  6011.  flag before it calls DosSemRequest, the thread could terminate between
  6012.  setting the flag and receiving the semaphore. In that case, the exitlist
  6013.  routine would believe, wrongly, that it owns the semaphore and would
  6014.  therefore release it--a very unpleasant surprise for the true owner of the
  6015.  semaphore. Conversely, if the thread claims the semaphore and then sets the
  6016.  flag, a window exists in which the semaphore is claimed but the flag does
  6017.  not say so. This is also disastrous.
  6018.       Using exclusive system semaphores solves these problems. As I
  6019.  mentioned earlier, when the thread that has set a system semaphore dies
  6020.  with the semaphore set, the semaphore is placed into a special "owner died"
  6021.  state so that the next thread to attempt to claim the semaphore is informed
  6022.  of its orphan status. There is an extra twist to this for exclusive-use
  6023.  system semaphores. Should the process die due to an external cause or due
  6024.  to a DosExit call and that process has a DosExitList handler, all orphaned
  6025.  system semaphores are placed in a special "owner died" state so that only
  6026.  that process's remaining thread--the one executing the DosExitList
  6027.  handlers--can claim the semaphore. When it does so, it still receives the
  6028.  special "owner died" code. The exitlist handler can use DosSemWait with a
  6029.  timeout value of 0 to see if the semaphore is set. If the "owner died" code
  6030.  is returned, then the DosExitList handler cleans up the resource and then
  6031.  issues DosSemClear to clear the semaphore. If a thread terminates by
  6032.  explicitly calling DosExit with the "terminate this thread" subcode, any
  6033.  exclusive-use system semaphores that it has set will not enter this special
  6034.  "owner died" state but will instead assume the general "owner died" state
  6035.  that allows any thread in the system to claim the semaphore and receive the
  6036.  "owner died" code. Likewise, any semaphores in the special "owner died"
  6037.  state that are not cleared by the DosExitList handlers become normal "owner
  6038.  died" semaphores when the process completely terminates.
  6039.  
  6040.  
  6041.  11.2.2  Semaphore Scheduling
  6042.  Although multiple threads can wait for a semaphore, only one thread gets
  6043.  the semaphore when it becomes available. OS/2 schedules semaphore grants
  6044.  based on CPU priority: The highest-priority waiting thread claims the
  6045.  semaphore. If several waiting threads are at the highest priority, OS/2
  6046.  distributes the grants among them evenly.
  6047.  
  6048.  
  6049.  11.3  Named Pipes
  6050.  
  6051.  We've already discussed anonymous pipes--stream oriented IPC mechanisms
  6052.  that work via the DosRead and DosWrite calls. Two processes can communicate
  6053.  via anonymous pipes only if one is a descendant of the other and if the
  6054.  descendant has inherited the parent's handle to the pipe. Anonymous pipes
  6055.  are used almost exclusively to transfer input and output data to and from a
  6056.  child process or to and from a subtree of child processes.
  6057.       OS/2 supports another form of pipes called named pipes. Named pipes
  6058.  are not available in OS/2 version 1.0; they will be available in a later
  6059.  release. I discuss them here because of their importance in the system
  6060.  architecture. Also, because of the extensible nature of OS/2, it's possible
  6061.  that named pipe functionality will be added to the system by including the
  6062.  function in some other Microsoft system software package that, when it runs
  6063.  under OS/2, installs the capability. In such a case, application programs
  6064.  will be unable to distinguish the "add-on" named pipe facility from the
  6065.  "built-in" version that will eventually be included in OS/2.
  6066.       Named pipes are much like anonymous pipes in that they're a serial
  6067.  communications channel between two processes and they use the DosRead and
  6068.  DosWrite interface. They are different, however, in several important
  6069.  ways.
  6070.  
  6071.       ■  Named pipes have names in the file system name space. Users of a
  6072.          named pipe need not be related; they need only know the name of a
  6073.          pipe to access it.
  6074.  
  6075.       ■  Because named pipes use the file system name space and because that
  6076.          name space can describe machines on a network, named pipes work
  6077.          both locally (within a single machine) and remotely (across a
  6078.          network).
  6079.  
  6080.       ■  An anonymous pipe is a byte-stream mechanism. The system considers
  6081.          the data sent through an anonymous pipe as an undifferentiated
  6082.          stream of bytes. The writer can write a 100-byte block of data, and
  6083.          the reader can read the data with two 30-byte reads and one 40-byte
  6084.          read. If the byte stream contains individual messages, the
  6085.          recipient must determine where they start and stop. Named pipes can
  6086.          be used in this byte-stream mode, but named pipes also support
  6087.          support message mode, in which processes read and write streams
  6088.          of messages. When the named pipe is in message mode, OS/2
  6089.          (figuratively!) separates the messages from each other with pieces
  6090.          of waxed paper so that the reader can ask for "the next message"
  6091.          rather than for "the next 100 bytes."
  6092.  
  6093.       ■  Named pipes are full duplex, whereas anonymous pipes are actually a
  6094.          pair of pipes, each half duplex. When an anonymous pipe is created,
  6095.          two handles are returned--a read handle and a write handle. An open
  6096.          of a named pipe returns a single handle, which may (depending on
  6097.          the mode of the DosOpen) be both read and written. Although a full
  6098.          duplex named pipe is accessed via a single handle, the data moving
  6099.          in each direction is kept totally separate. A named pipe should be
  6100.          viewed as two separate pipes between the reader and the writer--one
  6101.          holds data going in, the other holds data coming back. For example,
  6102.          if a thread writes to a named pipe handle and then reads from that
  6103.          handle, the thread will not read back the data it just wrote. The
  6104.          data the thread just wrote in is in the outgoing side; the read
  6105.          reads from the incoming side.
  6106.  
  6107.       ■  Named pipes are frequently used to communicate with processes that
  6108.          provide a service to one or more clients, usually simultaneously.
  6109.          The named pipe API contains special functions to facilitate such
  6110.          use: pipe reusability, multiple pipes with identical names, and so
  6111.          on. These are discussed below.
  6112.  
  6113.       ■  Named pipes support transaction I/O calls that provide an efficient
  6114.          way to implement local and remote procedure call dialogs between
  6115.          processes.
  6116.  
  6117.       ■  Programs running on MS-DOS version 3.x workstations can access
  6118.          named pipes on an OS/2 server to conduct dialogs with server
  6119.          applications because, to a client, a named pipe looks exactly like
  6120.          a file.
  6121.  
  6122.       You'll recall that the creator of an anonymous pipe uses a special
  6123.  interface (DosMakePipe) to create the pipe but that the client process
  6124.  can use the DosRead and DosWrite functions, remaining ignorant of the
  6125.  nature of the handle. The same holds true for named pipes when they
  6126.  are used in stream mode. The creator of a named pipe uses a special API
  6127.  to set it up, but its clients can use the pipe while remaining ignorant
  6128.  of its nature as long as that use is serial.4 Named pipes are created by
  6129.  the DosMakeNmPipe call. Once the pipe is created, one of the serving
  6130.  process's threads must wait via the DosConnectNmPipe call for the client
  6131.  to open the pipe. The client cannot successfully open the pipe until a
  6132.  DosConnectNmPipe has been issued to it by the server process.
  6133.       Although the serving process understands that it's using a named pipe
  6134.  and can therefore call a special named pipe API, the client process need
  6135.  not be aware that it's using a named pipe because the normal DosOpen call
  6136.  is used to open the pipe. Because named pipes appear in the file system
  6137.  name space, the client can, for example, open a file called \PIPE\STATUS,
  6138.  unaware that it's a named pipe being managed by another process. The
  6139.  DosMakeNmPipe call returns a handle to the serving end of the pipe; the
  6140.  DosOpen call returns a handle to the client end. As soon as the client
  6141.  opens a pipe, the DosConnectNmPipe call returns to the serving process.
  6142.       Communication over a named pipe is similar to that over an anonymous
  6143.  pipe: The client and server each issue reads and writes to the handle, as
  6144.  appropriate for the mode of the open. When a process at one end of the pipe
  6145.  closes it, the process at the other end gets an error code in response to
  6146.  write operations and an EOF indication in response to read operations.
  6147.       The scenario just described is simple enough, but that's the problem:
  6148.  It's too simple. In real life, a serving process probably stays around so
  6149.  that it can serve the next client. This is the purpose behind the
  6150.  DosConnectNmPipe call. After the first client closes its end of the named
  6151.  pipe and the server end sees the EOF on the pipe, the server end issues a
  6152.  DosDisconnectNmPipe call to acknowledge that the client has closed the pipe
  6153.  (either explicitly or via termination). It can then issue another
  6154.  DosConnectNmPipe call to reenable that pipe for reopening by another client
  6155.  or by the same client. In other words, the connect and disconnect
  6156.  operations allow a server to let clients, one by one, connect to it via a
  6157.  single named pipe. The DosDisconnectNmPipe call can be used to forcibly
  6158.  disconnect a client. This action is appropriate if a client makes an
  6159.  invalid request or otherwise shows signs of ill health.
  6160.       We can serve multiple clients, one at a time, but what about serving
  6161.  them in parallel? As we've described it so far, our serving process handles
  6162.  only one client. A client's DosOpen call fails if the named pipe already
  6163.  has a client user or if the server process hasn't issued the
  6164.  DosConnectNmPipe call. This is where the instancing parameter, supplied to
  6165.  DosMakeNmPipe, comes in.
  6166.       When a named pipe is first opened,5 the instance count parameter is
  6167.  specified in the pipe flag's word. If this count is greater than 1, the
  6168.  pipe can be opened by a server process more than once. Additional opens are
  6169.  done via DosMakeNmPipe, which returns another handle to access the new
  6170.  instance of the pipe. Obviously the pipe isn't being "made" for the second
  6171.  and subsequent calls to DosMakeNmPipe, but the DosOpen call can't be used
  6172.  instead because it opens the client end of the named pipe, not the server
  6173.  end. The instance count argument is ignored for the second and subsequent
  6174.  DosMakeNmPipe calls. Extra instances of a named pipe can be created by the
  6175.  same process that created the first instance, or they can be created by
  6176.  other processes. Figure 11-1 illustrates multiple instances of a named
  6177.  pipe.
  6178.  
  6179.  
  6180.  ┌──────────┐              \\pipe\pipename
  6181.  │  Client  ├─────┐                               ┌───────────┐
  6182.  │     A    │     │     ░░░░░░░░░░░░░░░░░░░░░░     │           │
  6183.  └──────────┘     │               ┌──────────┐     │           │
  6184.                   └───────────────┤          │     │           │
  6185.  ┌──────────┐                  ┌──┴───────┐  │     │           │
  6186.  │  Client  ├──────────────────┤          │  │     │           │
  6187.  │     B    │               ┌──┴───────┐  │  │     │  Server   │
  6188.  └──────────┘     ┌─────────┤          │  │  ├─────┤  Process  │
  6189.                   │      ┌──┴───────┐  │  ├──┘     │           │
  6190.  ┌──────────┐     │  ┌───┤          │  │  ├────────┤           │
  6191.  │  Client  ├─────┘  │   │          │  ├──┘        │           │
  6192.  │     C    │        │   │          │  ├───────────┤           │
  6193.  └──────────┘        │   │          ├──┘           │           │
  6194.                      │   │          ├──────────────┤           │
  6195.  ┌──────────┐        │   └──────────┘              │           │
  6196.  │  Client  ├────────┘                             └───────────┘
  6197.  │     D    │
  6198.  └──────────┘
  6199.  
  6200.  Figure 11-1.  Multiple instances of a named pipe.
  6201.  
  6202.  
  6203.       When a client process does a DosOpen on a named pipe that has multiple
  6204.  instances, OS/2 connects it to any server instance of the pipe that has
  6205.  issued a DosConnectNmPipe call. If no instances are available and enabled,
  6206.  the client receives an error code. OS/2 makes no guarantees about
  6207.  distributing the incoming work evenly across all server instances; it
  6208.  assumes that all server threads that issued a DosConnectNmPipe call are
  6209.  equal.
  6210.       The multiple instance capability allows a single server process or
  6211.  perhaps multiple server processes to handle many clients simultaneously.
  6212.  One process using four threads can serve four clients as rapidly as four
  6213.  processes, each with one thread, can do the job. As long as threads don't
  6214.  interfere with one another by blocking on critical sections, a multiprocess
  6215.  server has no inherent efficiency advantage over a multithread server.
  6216.       The OS/2 named pipe package includes some composite operations for
  6217.  client processes: DosTransactNmPipe and DosCallNmPipe. DosTransactNmPipe is
  6218.  much like a DosWrite followed by a DosRead: It sends a message to the
  6219.  server end of the named pipe and then reads a reply. DosCallNmPipe does
  6220.  the same on an unopened named pipe: It has the combined effect of a
  6221.  DosOpen, a DosTransactNmPipe, and a DosClose. These calls are of little
  6222.  value if the client and server processes are on the same machine; the
  6223.  client could easily build such subroutines itself by appropriately
  6224.  combining DosOpen, DosClose, DosRead, and DosWrite. These calls are in the
  6225.  named pipe package because they provide significant performance savings in
  6226.  a networked environment. If the server process is on a different machine
  6227.  from the client process, OS/2 and the network transport can use a
  6228.  datagramlike mechanism to implement these calls in a network-efficient
  6229.  fashion. Because named pipes work invisibly across the network, any client
  6230.  process that performs these types of operations should use these composite
  6231.  calls, even if the author of the program didn't anticipate the program
  6232.  being used in a networked environment. Using the composite calls will
  6233.   ensure the performance gains if a user decides to use a server process
  6234.  located across the network. Readers familiar with network architecture will
  6235.  recognize the DosCallNmPipe function as a form of remote procedure call. In
  6236.  effect, it allows a process to make a procedure call to another process,
  6237.  even a process on another machine.
  6238.       The OS/2 named pipe facility contains a great many features, as befits
  6239.  its importance in realizing the OS/2 tool-based architecture. This book is
  6240.  not intended to provide an exhaustive coverage of features, but a few other
  6241.  miscellaneous items merit mention.
  6242.       Our above discussion concentrated on stream-based communications,
  6243.  which can be convenient because they allow a client process to use a named
  6244.  pipe while ignorant of its nature. For example, you can write a spooler
  6245.  package for a device not supported by the system spoolers--say, for a
  6246.  plotter device. Input to the spooler can be via a named pipe, perhaps
  6247.  \PIPE\PLOTOUT. An application could then be told to write its plotter
  6248.  output to a file named \PIPE\PLOTOUT or even \\PLOTMACH\PIPE\PLOTOUT
  6249.  (across a network). The application will then use the spooler at the
  6250.  other end of the named pipe.
  6251.       Sometimes, though, the client process does understand that it's
  6252.  talking to a named pipe, and the information exchanged is a series of
  6253.  messages rather than a long stream of plotter data. In this case, the named
  6254.  pipe can be configured as a message stream in which each message is
  6255.  indivisible and atomic at the interface. In other words, when a process
  6256.  reads from a named pipe, it gets only one message per read, and it gets the
  6257.  entire message. Messages can queue up in the pipe, but OS/2 remembers the
  6258.  message boundaries so that it can split them apart as they are read.
  6259.  Message streams can be used effectively in a networking environment because
  6260.  the network transport can better judge how to assemble packets.
  6261.       Although our examples have shown the client and server processes
  6262.  issuing calls and blocking until they are done, named pipes can be
  6263.  configured to operate in a nonblocking fashion. This allows a server or a
  6264.  client to test a pipe to see if it's ready for a particular operation,
  6265.  thereby guaranteeing that the process won't be held up for some period
  6266.  waiting for a request to complete. Processes can also use DosPeekNmPipe, a
  6267.  related facility that returns a peek at any data (without consuming the
  6268.  data) currently waiting to be read in the pipe interface. Servers can use
  6269.  this to scan a client's request to see if they're interested in handling it
  6270.  at that time.
  6271.       Finally, we mentioned that a process that attempts a DosOpen to a
  6272.  named pipe without any available instances is returned an error code.
  6273.  Typically, a client in this situation wants to wait for service to become
  6274.  available, and it doesn't want to sit in a polling loop periodically
  6275.  testing for server availability. The DosWaitNmPipe call is provided for
  6276.  this situation; it allows a client to block until an instance of the named
  6277.  pipe becomes available. When DosWaitNmPipe returns, the client must still
  6278.  do a DosOpen. The DosOpen can fail, however, if another process has taken
  6279.  the pipe instance in the time between the "wait" and the "open" calls. But
  6280.  because multiple waiters for a named pipe are serviced in priority order,
  6281.  such a "race" condition is uncommon.
  6282.  
  6283.  
  6284.  11.4  Queues
  6285.  
  6286.  Queues are another form of IPC. In many ways they are similar to named
  6287.  pipes, but they are also significantly different. Like named pipes, they
  6288.  use the file system name space, and they pass messages rather than byte
  6289.  streams. Unlike named pipes, queues allow multiple writes to a single queue
  6290.  because the messages bring with them information about their sending
  6291.  process that enables the queue reader to distinguish between messages from
  6292.  different senders. Named pipes are strictly FIFO, whereas queue messages
  6293.  can be read in a variety of orders. Finally, queues use shared memory as a
  6294.  transfer mechanism; so although they're faster than named pipes for higher
  6295.  volume data transfers on a single machine, they don't work across the
  6296.  network.
  6297.       The interface to the queue package is similar but not identical to
  6298.  that of the named pipe interface. Like named pipes, each queue has a single
  6299.  owner that creates it. Clients open and close the queue while the owner,
  6300.  typically, lives on. Unlike named pipes, the client process must use a
  6301.  special queue API (DosReadQueue, DosWriteQueue, and so on) and thus must be
  6302.  written especially to use the queue package. Although each queue has a
  6303.  single owner, each queue can have multiple clients; so the queue mechanism
  6304.  doesn't need a facility to have multiple queues of the same name, nor does
  6305.  it need a DosWaitNmPipe equivalent.
  6306.       Queue messages are somewhat different from named pipe messages. In
  6307.  addition to carrying the body of the message, each queue message carries
  6308.  two additional pieces of information. One is the PID of the sender; OS/2
  6309.  provides this information, and the sender cannot affect it. The other is a
  6310.  word value that the sender supplied and that OS/2 and the queue package do
  6311.  not interpret. Queue servers and clients can use this information as they
  6312.  wish to facilitate communication.
  6313.       The queue package also contains a peek facility, similar to that of
  6314.  named pipes but with an interesting twist. If a process peeks the named
  6315.  pipe and then later reads from it, it can be sure that the message it reads
  6316.  is the same one that it peeked because named pipes are always FIFO. Queues,
  6317.  however, allow records to be read in different orders of priority. If a
  6318.  queue is being read in priority order, a process might well peek a message,
  6319.  but by the time the process issues the queue read, some other message of
  6320.  higher priority may have arrived and thus be at the front of the list. To
  6321.  get around this problem, when the queue package peeks a message, it returns
  6322.  a magic cookie to the caller along with the message. The caller can supply
  6323.  this cookie to a subsequent DosReadQueue call to ensure that the peeked
  6324.  message is the one read, overriding the normal message-ranking process.
  6325.  This magic cookie can also be supplied to the DosPeekQueue call to peek the
  6326.  second and subsequent records in the queue.
  6327.       Finally, one extremely important difference between queues and named
  6328.  pipes is that named pipes transfer (that is, copy) the data from the client
  6329.  to the server process. Queues transfer only the address of the data; the
  6330.  queue package does not touch the data itself. Thus, the data body of the
  6331.  queue message must be addressable to both the client and the serving
  6332.  process. This is straightforward if both the client and serving threads
  6333.  belong to the same process. If the client and serving threads are from
  6334.  different processes, however, the data body of the queue message must be in
  6335.  a shared memory segment that is addressable to both the client and the
  6336.  server.
  6337.       A related issue is buffer reusability. An application can reuse a
  6338.  memory area immediately after its thread returns from the named pipe call
  6339.  that wrote the data from that area; but when using a queue, the sender must
  6340.  not overwrite the message area until it's sure the reading process is
  6341.  finished with the message.
  6342.       One way to kill both these birds--the shared memory and the memory
  6343.  reuse problems--with one stone is to use the memory suballocation package.
  6344.  Both the client and the queue server need to have shared access to a memory
  6345.  segment that is then managed by the memory suballocation package. The
  6346.  client allocates a memory object to hold the queue message and write it to
  6347.  the queue. The queue server can address that queue message because it's in
  6348.  the shared memory segment. When the queue manager is finished with the
  6349.  message, it calls the memory suballocator to release the memory object. The
  6350.  client need not worry about when the server is finished with the message
  6351.  because the client allocates a new message buffer for each new message,
  6352.  relying on the server to return the messages fast enough so that the memory
  6353.  suballocator doesn't run out of available space.
  6354.       A similar technique on a segment level is to use giveaway shared
  6355.  memory. The client allocates a giveaway segment for each message content,
  6356.  creates the message, gives away a shared addressability to the segment to
  6357.  the server process, and then writes the message (actually, the message's
  6358.  address) to the queue. Note that the sender uses the recipient's selector
  6359.  as the data address in this case, not its own selector. When the thread
  6360.  returns from that DosWriteQueue call, the client releases its access to the
  6361.  segment via DosFreeSeg. When the server process is finished with the
  6362.  message, it also releases the memory segment. Because the queue server is
  6363.  the last process with access to that segment, the segment is then returned
  6364.  to the free pool.
  6365.       Software designers need to consider carefully the tradeoffs between
  6366.  queues, named pipes, and other forms of IPC. Queues are potentially very
  6367.  fast because only addresses are copied, not the data itself; but the work
  6368.  involved in managing and reusing the shared memory may consume the time
  6369.  savings if the messages are small. In general, small messages that are
  6370.  always read FIFO should go by named pipes, as should applications that
  6371.  communicate with clients and servers across a network. Very large or high
  6372.  data rate messages may be better suited to queues.
  6373.  
  6374.  
  6375.  11.5  Dynamic Data Exchange (DDE)
  6376.  
  6377.  DDE is a form of IPC available to processes that use the presentation
  6378.  manager API. The presentation manager's interface is message oriented; that
  6379.  is, the primary means of communication between a process and the
  6380.  presentation manager is the passing of messages. The presentation manager
  6381.  message interface allows applications to define private messages that have
  6382.  a unique meaning throughout the PC. DDE is, strictly speaking, a protocol
  6383.  that defines new messages for communication between applications that use
  6384.  it.
  6385.       DDE messages can be directed at a particular recipient or broadcast to
  6386.  all presentation manager applications on a particular PC. Typically, a
  6387.  client process broadcasts a message that says, "Does anyone out there have
  6388.  this information?" or "Does anyone out there provide this service?" If no
  6389.  response is received, the answer is taken to be no. If a response is
  6390.  received, it contains an identifying code6 that allows the two processes to
  6391.  communicate privately.
  6392.       DDE's broadcast mechanism and message orientation gives it a lot of
  6393.  flexibility in a multiprocessing environment. For example, a specialized
  6394.  application might be scanning stock quotes that are arriving via a special
  6395.  link. A spreadsheet program could use DDE to tell this scanner application
  6396.  to notify it whenever the quotes change for certain stocks that are
  6397.  mentioned in its spreadsheet. Another application, perhaps called Market
  6398.  Alert, might ask the scanner to notify it of trades in a different set of
  6399.  stocks so that the alert program can flash a banner if those stocks trade
  6400.  outside a prescribed range. DDEs can be used only by presentation manager
  6401.  applications to communicate with the same.
  6402.  
  6403.  
  6404.  11.6  Signaling
  6405.  
  6406.  Signals are asynchronous notification mechanisms that operate in a fashion
  6407.  analogous to hardware interrupts. Like hardware interrupts, when a signal
  6408.  arrives at a process, that process's thread 1 stops after the instruction
  6409.  it is executing and begins executing at a specified handler address. The
  6410.  many special considerations to take into account when using signals are
  6411.  discussed in Chapter 12. This section discusses their use as a form of
  6412.  IPC.
  6413.       Processes typically receive signals in response to external events
  6414.  that must be serviced immediately. Examples of such events are the user
  6415.  pressing Ctrl-C or a process being killed. Three signals (flag A, flag B,
  6416.  and flag C), however, are caused by another process7 issuing an explicit
  6417.  DosFlagProcess API. DosFlagProcess is a unique form of IPC because it's
  6418.  asynchronous. The recipient doesn't have to block or poll waiting for the
  6419.  event; it finds out about it (by discovering itself to be executing the
  6420.  signal handler) as soon as the scheduler gives it CPU time.
  6421.       DosFlagProcess, however, has some unique drawbacks. First, a signal
  6422.  carries little information with it: only the number of the signal and a
  6423.  single argument word. Second, signals can interrupt and interfere with some
  6424.  system calls. Third, OS/2 views signals more as events than as messages; so
  6425.  if signals are sent faster than the recipient can process them, OS/2
  6426.  discards some of the overrun. These disadvantages (discussed in Chapter 12)
  6427.  restrict signals to a rather specialized role as an IPC mechanism.
  6428.  
  6429.  
  6430.  11.7  Combining IPC Forms
  6431.  
  6432.  We've discussed each form of IPC, listing its strengths and weaknesses. If
  6433.  you use forms in conjunction, however, you benefit from their combined
  6434.  strengths. For example, a process can use named pipes or DDE to establish
  6435.  contact with another process and then agree with it to send a high volume
  6436.  of data via shared memory. An application that provides an IPC interface
  6437.  should also provide a dynlink package to hide the details of the IPC. This
  6438.  gives designers the flexibility to improve the IPC component of their
  6439.  package in future releases while still maintaining interface compatibility
  6440.  with their clients.
  6441.  
  6442.  
  6443.  
  6444.  12  Signals
  6445.  
  6446.  ───────────────────────────────────────────────────────────────────────────
  6447.  
  6448.  The OS/2 signal mechanism is similar, but not identical, to the UNIX signal
  6449.  mechanism. A signal is much like a hardware interrupt except that it is
  6450.  initiated and implemented in software. Just as a hardware interrupt causes
  6451.  the CS, IP, and Flags registers to be saved on the stack and execution to
  6452.  begin at a handler address, a signal causes the application's CS, IP, and
  6453.  Flags registers to be saved on the stack and execution to begin at a
  6454.  signal-handler address. An IRET instruction returns control to the
  6455.  interrupted address in both cases. Signals are different from hardware
  6456.  interrupts in that they are a software construct and don't involve
  6457.  privilege transitions, stack switches, or ring 0 code.
  6458.       OS/2 supports six signals--three common signals (Ctrl-C, Ctrl-Break,
  6459.  and program termination) and three general-purpose signals. The Ctrl-C and
  6460.  Ctrl-Break signals occur in response to keyboard activity; the program
  6461.  termination signal occurs when a process is killed via the DosKill call.1
  6462.  The three general-purpose signals are generated by an explicit call from a
  6463.  thread, typically a thread from another process. A signal handler is in the
  6464.  form of a far subroutine, that is, a subroutine that returns with a far
  6465.  return instruction. When a signal arrives, the process's thread 1 is
  6466.  interrupted from its current location and made to call the signal-handler
  6467.  procedure with an argument provided by the signal generator. The signal-
  6468.  handler code can return,2 in which case the CPU returns from where it was
  6469.  interrupted, or the signal handler can clean its stack and jump into the
  6470.  process's code at some other spot, as in the C language's longjmp facility.
  6471.       The analogy between signals and hardware interrupts holds still
  6472.  further. As it does in a hardware interrupt, the system blocks further
  6473.  interrupts from the same source so that the signal handler won't be
  6474.  arbitrarily reentered. The signal handler must issue a special form of
  6475.  DosSetSigHandler to dismiss the signal and allow further signals to occur.
  6476.  Typically this is done at the end of the signal-handling routine unless the
  6477.  signal handler is reentrant. Also, like hardware interrupts, the equivalent
  6478.  of the CLI instruction--the DosHoldSignal call--is used to protect critical
  6479.  sections from being interrupted via a signal.
  6480.       Unlike hardware interrupts, signals have no interrupt priority. As
  6481.  each enabled signal occurs, the signal handler is entered, even if another
  6482.  signal handler must be interrupted. New signal events that come in while
  6483.  that signal is still being processed from an earlier event--before the
  6484.  signal has been dismissed by the handler--are held until the previous
  6485.  signal event has been dismissed. Like hardware interrupts, this is a
  6486.  pending-signal flag, not a counter. If three signals of the same kind are
  6487.  held off, only one signal event occurs when that signal becomes
  6488.  reenabled.
  6489.       A signal event occurs in the context of a process whose thread of
  6490.  execution is interrupted for the signal handler; a signal doesn't cause the
  6491.  CPU to stop executing another process in order to execute the first
  6492.  process's signal handler. When OS/2 "posts" a signal to a process, it
  6493.  simply makes a mark that says, "The next time we run this guy, store his
  6494.  CS, IP, and Flags values on the stack and start executing here instead."
  6495.  The system uses its regular priority rules to assign the CPU to threads;
  6496.  when the scheduler next runs the signaled thread, the dispatcher code that
  6497.  sends the CPU into the application's code reads the "posted signal" mark
  6498.  and does the required work.
  6499.       Because a signal "pseudo interrupt" is merely a trick of the
  6500.  dispatcher, signal handlers don't run in ring 0 as do hardware interrupt
  6501.  handlers; they run in ring 3 as do all application threads. In general, as
  6502.  far as OS/2 is concerned, the process isn't in any sort of special state
  6503.  when it's executing a signal handler, and no special rules govern what a
  6504.  thread can and cannot do in a signal handler.
  6505.       Receiving a signal when thread 1 is executing an application or
  6506.  dynlink code is straightforward: The system saves CS, IP, and Flags, and
  6507.  the signal handler saves the rest. The full register complement can be
  6508.  restored after the signal has been processed, and thread 1's normal
  6509.  execution resumes without incident. If thread 1 is executing a system call
  6510.  that takes the CPU inside the kernel, the situation is more complex. OS/2
  6511.  can't emulate an interrupt from the system ring 0 code to the application's
  6512.  ring 3 code, nor can OS/2 take the chance that the signal handler never
  6513.  returns from the signal3 and therefore leaves OS/2's internals in an
  6514.  intermediate state. Instead, when a signal is posted and thread 1 is
  6515.  executing ring 0 OS/2 code, the system either completes its operations
  6516.  before recognizing the signal or aborts the operation and then recognizes
  6517.  the signal. If the operation is expected to take place "quickly," the
  6518.  system completes the operation, and the signal is recognized at the point
  6519.  where the CPU resumes executing the application's ring 3 code.
  6520.       All non-I/O operations are deemed to complete "quickly," with the
  6521.  exception of the explicit blocking operations such as DosSleep, DosSemWait,
  6522.  and so on. I/O operations depend on the specific device. Disk I/O completes
  6523.  quickly, but keyboard and serial I/O generally do not. Clearly, if we wait
  6524.  for the user to finish typing a line before we recognize a signal, we might
  6525.  never recognize it--especially if the signal is Ctrl-C! In the case of
  6526.  "slow devices," OS/2 or the device driver terminates the operation and
  6527.  returns to the application with an error code. The signal is recognized
  6528.  when the CPU is about to resume executing the ring 3 application code that
  6529.  follows the system call that was interrupted.
  6530.       Although the application is given an error code to explain that the
  6531.  system call was interrupted, the application may be unable to reissue the
  6532.  system call to complete the work. In the case of device I/O, the
  6533.  application typically can't tell how much, if any, of the requested output
  6534.  or input took place before the signal interrupted the operation. If an
  6535.  output operation is not reissued, some data at the end of the write may be
  6536.  missing. If an output operation is restarted, then some data at the
  6537.  beginning of the write may be written twice. In the case of DosSleep, the
  6538.  application cannot tell how much of the requested sleep has elapsed. These
  6539.  issues are not usually a problem; it's typically keyboard input that is
  6540.  interrupted. In the case of the common signals (Ctrl-C, Ctrl-Break, and
  6541.  process killed) the application typically flushes partial keyboard input
  6542.  anyway. Applications that use other "slow" devices or the IPC flag signals
  6543.  need to deal with this, however.
  6544.       Although a process can have multiple threads, only thread 1 is used to
  6545.  execute the signal handler.4 This leads to an obvious solution to the
  6546.  interrupted system call problem: Applications that will be inconvenienced
  6547.  by interrupted system calls due to signals should dedicate thread 1 to work
  6548.  that doesn't make interruptible system calls and use other thread(s) for
  6549.  that work. In the worst case, thread 1 can be totally dedicated to waiting
  6550.  for signals: It can block on a RAM semaphore that is never released, or it
  6551.  can execute a DosSleep loop.
  6552.       A couple of practical details about signals are worth noting. First,
  6553.  the user of a high-level language such as C need not worry about saving the
  6554.  registers inside the signal-handler routine. The language runtimes
  6555.  typically provide code to handle all these details; as far as the
  6556.  application program is concerned, the signal handler is asynchronously far
  6557.  called, and it can return from the signal by the return() statement. Also,
  6558.  no application can receive a signal without first requesting it, so you
  6559.  need not worry about setting up signal handlers if your application doesn't
  6560.  explicitly ask to use them. A process can have only one signal-handling
  6561.  address for each signal, so general-purpose dynlink routines (ones that
  6562.  might be called by applications that aren't bundled with the dynlink
  6563.  package) should never set a signal handler; doing so might override a
  6564.  handler established by the client program code.
  6565.       Signals interact with critical sections in much the same way as
  6566.  interrupts do. If a signal arrives while thread 1 is executing a critical
  6567.  section that is protected by a semaphore and if that signal handler never
  6568.  returns to the interrupted location, the critical section's semaphore will
  6569.  be left jammed on. Even if the signal handler eventually returns, deadlock
  6570.  occurs if it attempts to enter the critical section during processing of
  6571.  the signal (perhaps it called a dynlink package, unaware that the package
  6572.  contained a critical section). Dynlink packages must deal with this problem
  6573.  by means of the DosHoldSignal call, which is analogous to the CLI/STI
  6574.  instructions for hardware interrupts: The DosHoldSignal holds off arriving
  6575.  signals until they are released. Held-off signals should be released within
  6576.  a second or two so that the user won't be pounding Ctrl-C and thinking that
  6577.  the application has crashed. Applications can use DosHoldSignal, or they
  6578.  can simply ensure that thread 1 never enters critical sections, perhaps by
  6579.  reserving it for signal handling, as discussed above.
  6580.       Ctrl-C and Ctrl-Break are special, device-specific operations. Setting
  6581.  a signal handler for these signals is a form of I/O to the keyboard device;
  6582.  applications must never do this until they have verified that they have
  6583.  been assigned the keyboard device. See Chapter 14, Interactive Programs.
  6584.  
  6585.  
  6586.  
  6587.  13  The Presentation Manager and VIO
  6588.  
  6589.  ───────────────────────────────────────────────────────────────────────────
  6590.  
  6591.  In the early chapters of this book, I emphasized the importance of a high-
  6592.  powered, high-bandwidth graphical user interface. It's a lot of work for an
  6593.  application to manage graphical rendition, windowing, menus, and so on, and
  6594.  it's hard for the user to learn a completely different interface for each
  6595.  application. Therefore, OS/2 contains a subsystem called the presentation
  6596.  manager (PM) that provides these services and more. The presentation
  6597.  manager is implemented as a dynlink subsystem and daemon process
  6598.  combination, and it provides:
  6599.  
  6600.       ■  High-performance graphical windowing.
  6601.  
  6602.       ■  A powerful user interface model, including drop-down menus, scroll
  6603.          bars, icons, and mouse and keyboard interfaces. Most of these
  6604.          facilities are optional to the application; it can choose the
  6605.          standard services or "roll its own."
  6606.  
  6607.       ■  Device independence. The presentation manager contains a
  6608.          sophisticated multilevel device interface so that as much work as
  6609.          possible is pushed down to "smart" graphics cards to optimize
  6610.          performance.
  6611.  
  6612.       Interfacing an application with the presentation manager involves a
  6613.  degree of effort that not all programmers may want to put forth. The
  6614.  interface to the application may be so simple that the presentation
  6615.  manager's features are of little value, or the programmer may want to port
  6616.  an MS-DOS application to OS/2 with the minimum degree of change. For these
  6617.  reasons, OS/2 provides a second interface package called VIO,1 which is
  6618.  primarily character oriented and looks much like the MS-DOS ROM BIOS video
  6619.  interface. The initial release of OS/2 contains only VIO, implemented as a
  6620.  separate package. The next release will contain the presentation manager,
  6621.  and VIO will then become an alternate interface to the presentation
  6622.  manager.
  6623.       Fundamentally, the presentation manager and VIO are the equivalent of
  6624.  device drivers. They are implemented as dynlink packages because they are
  6625.  device dependent and need to be replaced if different devices are used.
  6626.  Dynlinks are used instead of true device drivers because they can provide
  6627.  high throughput for the screen device: A simple call is made directly to
  6628.  the code that paints the pixels on the screen. Also, the dynlink interface
  6629.  allows the presentation manager to be implemented partially as a dynlink
  6630.  subsystem and partially as a daemon process accessed by that subsystem.
  6631.       These packages are complex; explaining them in detail is beyond the
  6632.  scope of this book. Instead, I will discuss from a general perspective the
  6633.  special issues for users of this package.
  6634.       VIO is essentially character oriented. It supports graphics-based
  6635.  applications, but only to the extent of allowing them to manipulate the
  6636.  display controller directly so that they can "go around" VIO and provide
  6637.  special interfaces related to screen switching of graphics applications
  6638.  (see below). The base VIO package plays a role similar to that of the ROM
  6639.  BIOS INT 10/INT 16 interface used in MS-DOS. It contains some useful
  6640.  enhancements but in general is a superset of the ROM BIOS functions, so INT
  6641.  10-based real mode applications can be quickly adjusted to use VIO instead.
  6642.  VIO is replaceable, in whole or in part, to allow applications being run
  6643.  with VIO to be managed later by the presentation manager package.
  6644.       The presentation manager is entirely different from VIO. It offers an
  6645.  extremely rich and powerful set of functions that support windowing, and it
  6646.  offers a full, device-independent graphics facility. Its message-oriented
  6647.  architecture is well suited to interactive applications. Once the
  6648.  presentation manager programming model is learned and the key elements of
  6649.  its complex interface are understood, a programmer can take advantage of a
  6650.  very sexy user interface with comparatively little effort. The presentation
  6651.  manager also replaces the existing VIO/KBD/MOU calls in order to support
  6652.  older programs that use these interfaces.
  6653.  
  6654.  
  6655.  13.1  Choosing Between PM and VIO
  6656.  
  6657.  The roles of VIO and the presentation manager sometimes cause confusion:
  6658.  Which should you use for your application? The default interface for a new
  6659.  application should be the presentation manager. It's not in OS/2 version
  6660.  1.0 because of scheduling restrictions; owners of version 1.0 will receive
  6661.  the presentation manager as soon as it is available, and all future
  6662.  releases will be bundled with the presentation manager. The presentation
  6663.  manager will be present on essentially all personal computer OS/2
  6664.  installations, so you are not restricting the potential market for an
  6665.  application if you write it for the presentation manager.
  6666.       The presentation manager interface allows an application to utilize a
  6667.  powerful, graphical user interface. In general form it's standardized for
  6668.  ease of use, but it can be customized in a specific implementation so that
  6669.  an application can provide important value-added features. On the other
  6670.  hand, if you are porting an application from the real mode environment, you
  6671.  will find it easier to use the VIO interface. Naturally, such programs run
  6672.  well under the presentation manager, but they forgo the ability to use
  6673.  graphics and to interact with the presentation manager. The user can still
  6674.  "window" the VIO application's screen image, but without the application's
  6675.  knowledge or cooperation. To summarize, you have three choices when writing
  6676.  an application:
  6677.  
  6678.       1.  Only use the VIO interface in character mode. This works well in a
  6679.           presentation manager environment and is a good choice for ported
  6680.           real mode applications. The VIO interface is also supported by the
  6681.           Family API mechanism. This mode is compatible with the Family API
  6682.           facility.
  6683.  
  6684.       2.  Use the special VIO interface facilities to sidestep VIO and
  6685.           directly manipulate the display screen in either character or
  6686.           graphics mode. This also works in a presentation manager
  6687.           environment, but the application will not be able to run in a
  6688.           window. This approach can be compatible with the Family API if it
  6689.           is carefully implemented.
  6690.  
  6691.       3.  Use the presentation manager interface--the most sophisticated
  6692.           interface for the least effort. The presentation manager interface
  6693.           provides a way to "operate" applications that will become a widely
  6694.           known user standard because of the capabilities of the interface,
  6695.           because of the support it receives from key software vendors, and
  6696.           because it's bundled with OS/2. The user is obviously at an
  6697.           advantage if he or she does not have to spend time learning a new
  6698.           interface and operational metaphors to use your application.
  6699.           Finally, Microsoft is a strong believer in the power of a
  6700.           graphical user interface; future releases of OS/2 will contain
  6701.           "more-faster-better" presentation manager features. Many of these
  6702.           improvements will apply to existing presentation manager
  6703.           applications; others will expand the interface API. The standard
  6704.           of performance for application interfaces, as well as for
  6705.           application performance, continues to evolve. The rudimentary
  6706.           interfaces and function of the first-generation PC software are no
  6707.           longer considered competitive. Although OS/2 can do nothing to
  6708.           alleviate the developer's burden of keeping an application's
  6709.           function competitive, the presentation manager is a great help in
  6710.           keeping the application's interface state of the art.
  6711.  
  6712.       Clearly, using the presentation manager interface is the best
  6713.  strategy for new or extensively reworked applications. The presentation
  6714.  manager API will be expanded and improved; the INT 10-like VIO functions
  6715.  and the VIO direct screen access capabilities will be supported for the
  6716.  foreseeable future, but they're an evolutionary dead end. Given that, you
  6717.  may want to use the VIO mechanism or the Family API facilities to quickly
  6718.  port an application from a real mode version and then use the presentation
  6719.  manager in a product upgrade release.
  6720.  
  6721.  
  6722.  13.2  Background I/O
  6723.  
  6724.  A process is in the background when it is no longer interacting directly
  6725.  with the user. In a presentation manager environment, this means that none
  6726.  of the process's windows are the keyboard focus. The windows themselves may
  6727.  still be visible, or they may be obscured or iconic. In a VIO environment,
  6728.  a process is in the background when the user has selected another screen
  6729.  group. In this case, the application's screen display is not visible.
  6730.       A presentation manager application can easily continue to update its
  6731.  window displays when it is in the background; the application can continue
  6732.  to call the presentation manager to change its window contents in any way
  6733.  it wishes. A presentation manager application can arrange to be informed
  6734.  when it enters and leaves the background (actually, receives and loses the
  6735.  keyboard focus), or it can simply carry on with its work, oblivious to the
  6736.  issue. Background I/O can continue regardless of whether I/O form is
  6737.  character or graphics based.
  6738.       VIO applications can continue to do I/O in background mode as well.
  6739.  The VIO package maintains a logical video buffer for each screen group;
  6740.  when VIO calls are made to update the display of a screen group that is in
  6741.  the background, VIO makes the requested changes to the logical video
  6742.  buffer. When the screen group is restored to the foreground, the updated
  6743.  contents of the logical video buffer are copied to the display's physical
  6744.  video buffer.
  6745.  
  6746.  
  6747.  13.3  Graphics Under VIO
  6748.  
  6749.  VIO is a character-oriented package and provides character mode
  6750.  applications with a variety of services. As we have just seen, when a
  6751.  screen switch takes place, VIO automatically handles saving the old screen
  6752.  image and restoring the new. VIO does provide a mechanism to allow an
  6753.  application to sidestep VIO and directly manipulate the physical video
  6754.  buffer, where it is then free to use any graphical capability of the
  6755.  hardware. There are two major disadvantages to sidestepping VIO for
  6756.  graphics rather than using the presentation manager services:
  6757.  
  6758.       1.  The application is device dependent because it must manipulate the
  6759.           video display hardware directly.
  6760.  
  6761.       2.  VIO can no longer save or restore the state of the physical video
  6762.           buffer during screen switch operations. The application must use a
  6763.           special VIO interface to provide these functions itself.
  6764.  
  6765.       The following discussion applies only to applications that want to
  6766.  sidestep the presentation manager and VIO interfaces and interact directly
  6767.  with the display hardware.
  6768.       Gaining access to the video hardware is easy; the VIO call VioGetBuf
  6769.  provides a selector to the video buffer and also gives the application's
  6770.  ring 2 code segments, if any, permission to program the video controller's
  6771.  registers. The complication arises from the screen-switching capabilities
  6772.  of OS/2. When the user switches the application into a background screen
  6773.  group, the contents of the video memory belong to someone else; the
  6774.  application's video memory is stored somewhere in RAM. It is disastrous
  6775.  when an application doesn't pay attention to this process and accidentally
  6776.  updates the video RAM or the video controller while they are assigned to
  6777.  another screen group.
  6778.       Two important issues are connected with screen switching: (1) How does
  6779.  an application find out that it's in background mode? (2) Who saves and
  6780.  restores its screen image and where? The VioScrLock call handles screen
  6781.  access. Before every access to the display memory or the display
  6782.  controller, an application must first issue the VioScrLock call. While the
  6783.  call is in effect, OS/2 cannot perform any screen switches. Naturally, the
  6784.  application must do its work and quickly release the screen switch lockout.
  6785.  Failure to release the lock in a timely fashion has the effect of hanging
  6786.  the system, not only for a user's explicit screen switch commands, but also
  6787.  for other facilities that use the screen switch mechanism, such as the hard
  6788.  error handler. Hard errors can't be presented to the user while the screen
  6789.  lock is in effect. If OS/2 needs to switch screens, and an aberrant
  6790.  application has the screen lock set, OS/2 will cancel the lock and perform
  6791.  the screen switch after a period (currently 30 seconds). This is still a
  6792.  disaster scenario, although a mollified one, because the application that
  6793.  was summarily "delocked" will probably end up with a trashed screen image.
  6794.  The screen lock and unlock calls execute relatively rapidly, so they can be
  6795.  called frequently to protect only the actual write-to-screen operation,
  6796.  leaving the screen unlocked during computation. Basically, an application
  6797.  should use VioScrLock to protect a block of I/O that can be written, in its
  6798.  entirety, without significant recomputation. Examples of such blocks are a
  6799.  screen scroll, a screen erase, and a write to a cell in a spreadsheet
  6800.  program.
  6801.       VioScrLock must be used to protect code sequences that program the
  6802.  display hardware as well as code sequences that write to video memory. Some
  6803.  peripheral programming sequences are noninterruptible. For example, a two-
  6804.  step programming sequence in which the first I/O write selects a
  6805.  multiplexed register and the second write modifies that register is
  6806.  uninterruptible because the first write placed the peripheral device into a
  6807.  special state. Such sequences must be protected within one lock/unlock
  6808.  pair.
  6809.       Sometimes when an application calls VioScrLock, it receives a special
  6810.  error code that says, "The screen is unavailable." This means that the
  6811.  screen has been switched into the background and that the application may
  6812.  not--and must not--manipulate the display hardware. Typically, the program
  6813.  issues a blocking form of VioScrLock that suspends the thread until the
  6814.  screen is again in the foreground and the video display buffers contain
  6815.  that process's image.
  6816.       An application that directly manipulates the video hardware must do
  6817.  more than simply lay low when it is in the background. It must also save
  6818.  and restore the entire video state--the contents of the display buffer and
  6819.  the modes, palates, cursors, and so on of the display controller. VIO does
  6820.  not provide this service to direct-screen manipulation processes for two
  6821.  reasons. First, the process is very likely using the display in a graphics
  6822.  mode. Some display cards contain a vast amount of video memory, and VIO
  6823.  would be forced to save it all just in case the application was using it
  6824.  all. Second, many popular display controllers such as the EGA and
  6825.  compatibles contain many write-only control registers. This means that VIO
  6826.  cannot read the controller state back from the card in order to save it for
  6827.  a later restoration. The only entity that understands the state of the
  6828.  card, and therefore the only entity that can restore that state, is the
  6829.  code that programmed it--the application itself.
  6830.       But how does the system notify the process when it's time to save or
  6831.  restore? Processes can call the system in many ways, but the system can't
  6832.  call processes. OS/2 deals with this situation by inverting the usual
  6833.  meaning of call and return. When a process first decides to refresh its own
  6834.  screen, it creates an extra thread and uses that thread to call the
  6835.  VioSavRedrawWait function. The thread doesn't return from this call right
  6836.  away; instead, VIO holds the thread "captive" until it's time for a screen
  6837.  switch. To notify the process that it must now save its screen image, VIO
  6838.  allows the captive thread to return from the VioSavRedrawWait call. The
  6839.  process then saves the display state and screen contents, typically using
  6840.  the returned thread. When the save operation is complete, VioSavRedrawWait
  6841.  is called again. This notifies VIO that the save is complete and that the
  6842.  screen can now be switched; it also resets the cycle so that the process
  6843.  can again be notified when it's time to restore its saved screen image. In
  6844.  effect, this mechanism makes the return from VioSavRedrawWait analogous to
  6845.  a system-to-process call, and it makes the later call to VioSavRedrawWait
  6846.  analogous to a return from process to system.
  6847.       The design of OS/2 generally avoids features in which the system calls
  6848.  a process to help a system activity such as screen switching. This is
  6849.  because a tenet of the OS/2 design religion is that an aberrant process
  6850.  should not be able to crash the system. Clearly, we're vulnerable to that
  6851.  in this case. VIO postpones the screen switch until the process saves its
  6852.  screen image, but what if the process somehow hangs up and doesn't complete
  6853.  the save? The screen is in an indeterminate state, and no process can use
  6854.  the screen and keyboard. As far as the user is concerned, the system has
  6855.  crashed. True, other processes in the system are alive and well, but if the
  6856.  user can't get to them, even to save his or her work, their continued
  6857.  health is of little comfort.
  6858.       The designers of OS/2 were stuck here, between a rock and a hard
  6859.  place: Applications had to be able to save their screen image if they were
  6860.  to have direct video access, but such a facility violated the "no crashing"
  6861.  tenet of the design religion. Because the video access had to be supported
  6862.  and the system had to be crash resistant, we found a two-part workaround.
  6863.       The first part concerns the most common cause of a process hanging up
  6864.  in its screen-save operation: hard errors. When a hard error occurs, the
  6865.  hard error daemon uses the screen switch mechanism to take control of the
  6866.  screen and the keyboard. The hard error daemon saves the existing screen
  6867.  image and keeps the application that was in the foreground at the time of
  6868.  the hard error from fighting with the daemon over control of the screen and
  6869.  the keyboard. However, if the hard error daemon uses the screen-switching
  6870.  mechanism and if the screen-switching mechanism allows the foreground
  6871.  process to save its own screen image, that process might, while saving its
  6872.  screen image, try to use the device that has the hard error and thus
  6873.  deadlock the system. The device in error won't service more requests until
  6874.  the hard error is cleared, but the hard error can't be cleared until the
  6875.  daemon takes control. The daemon can't take control until the foreground
  6876.  process is through saving, and the foreground process can't complete saving
  6877.  until the device services its request. Note that this deadlock doesn't
  6878.  require an explicit I/O operation on the part of the foreground process;
  6879.  simply allocating memory or referencing a segment might cause swapping or
  6880.  loading activity on the device that is experiencing the hard error.
  6881.       A two-part approach is used to solve this problem. First, deadlocks
  6882.  involving the hard error daemon are managed by having the hard error screen
  6883.  switch do a partial screen save. When I said earlier that VIO would not
  6884.  save the video memory of direct access screen groups, I was lying a bit.
  6885.  When the system is doing a hard error screen switch, VIO will save the
  6886.  first part (typically 4 KB) of the video memory--enough to display a page
  6887.  of text. We don't have to worry about how much video RAM the application
  6888.  was using because the video display will be switched to character mode and
  6889.  the hard error daemon will overwrite only a small part of video memory.
  6890.  Naturally, this means that the hard error daemon must always restore the
  6891.  original screen group; it can't switch to a third screen group because the
  6892.  first one's video memory wasn't fully saved.
  6893.       VIO and the hard error daemon keep enough free RAM around to save this
  6894.  piece of the video memory so that a hard error screen switch can always
  6895.  take place without the need for memory swapping. When the hard error daemon
  6896.  is finished with the screen, the overwritten video memory is restored from
  6897.  the buffer. As we discussed above, however, VIO can't restore the state of
  6898.  the video controller itself; only the application can do that. The
  6899.  VioModeWait function is used to notify the application that it must restore
  6900.  the screen state.
  6901.       In summary, any application that directly accesses the video hardware
  6902.  must provide captive threads to VioSavRedrawWait and to VioModeWait.
  6903.  VioSavRedrawWait will return when the application is to save or to restore
  6904.  the video memory. VioModeWait will return when the application is to
  6905.  restore the state of the video controller from the application's own record
  6906.  of the controller's state.
  6907.       The second part of the "application hangs while saving screen and
  6908.  hangs system" solution is unfortunately ad hoc: If the application does not
  6909.  complete its screen save operation within approximately 30 seconds, the
  6910.  system considers it hung and switches the screen anyway. The hung process
  6911.  is suspended while it's in background so that it won't suddenly "come
  6912.  alive" and manipulate the screen. When the process is again in the
  6913.  foreground, the system unsuspends it and hopes that it will straighten
  6914.  itself out. In such a case, the application's screen image may be trashed.
  6915.  At best, the user can enter a "repaint screen" command to the application
  6916.  and all will be well; at worst, the application is hung up, but the system
  6917.  itself is alive and well. Building a system that can detect and correct
  6918.  errors on the part of an application is impossible; the best that we can
  6919.  hope to do is to keep an aberrant application from damaging the rest of the
  6920.  system.
  6921.       I hope that this long and involved discussion of rules, regulations,
  6922.  doom, and disaster has not frightened you into contemplating programs that
  6923.  communicate by Morse code. You need be concerned with these issues only if
  6924.  you write applications that manipulate the display device directly,
  6925.  circumventing either VIO or the presentation manager interfaces. These
  6926.  concerns are not an issue when you are writing ordinary text applications
  6927.  that use VIO or the presentation manager or graphics applications that use
  6928.  the presentation manager. VIO and the presentation manager handle screen
  6929.  saving and support background display writing. Finally, I'll point out, as
  6930.  a curiosity, that even processes that use handle operations only to write
  6931.  to STDOUT use VIO or the presentation manager. When STDOUT points to the
  6932.  screen device, the operating system routes STDOUT writes to the
  6933.  VIO/presentation manager packages. This is, of course, invisible to the
  6934.  application; it need not concern itself with foreground/background, EGA
  6935.  screen modes, hard error screen restorations, and the like.
  6936.  
  6937.  
  6938.  
  6939.  14  Interactive Programs
  6940.  
  6941.  ───────────────────────────────────────────────────────────────────────────
  6942.  
  6943.  A great many applications interact with the user via the screen and the
  6944.  keyboard. Because the primary function of most desktop computers is to run
  6945.  interactive applications, OS/2 contains a variety of services that make
  6946.  interaction powerful and efficient.
  6947.       As we've seen in earlier chapters, interactive programs can use the
  6948.  presentation manager to manage their interface, or they can do it
  6949.  themselves, using VIO or direct video access for their I/O. The
  6950.  presentation manager provides a great deal of function and automatically
  6951.  solves a great many problems. For example, a presentation manager
  6952.  application doesn't have to concern itself with the sharing of the single
  6953.  keyboard among all processes in its screen group. The presentation manager
  6954.  takes care of that by handling the keyboard and by simply sending keyboard
  6955.  events to each process, as appropriate.
  6956.       If you're writing an application that uses the presentation manager,
  6957.  then you can skip this chapter. If you're writing an application that does
  6958.  not use the presentation manager but that may be used in an interactive
  6959.  fashion, it's very important that you understand the issues discussed in
  6960.  this chapter. They apply to all programs that use VIO or the STDIN/STDOUT
  6961.  handles to do interactive I/O, even if such programs are being run via the
  6962.  presentation manager.
  6963.  
  6964.  
  6965.  14.1  I/O Architecture
  6966.  
  6967.  Simply put, the system I/O architecture says that all programs read their
  6968.  main input from the STDIN handle and write their main output to the STDOUT
  6969.  handle. This applies to all non-presentation manager applications, but
  6970.  especially to interactive applications. The reason is that OS/2 and
  6971.  program-execution utilities such as CMD.EXE (shell programs) cooperate to
  6972.  use the STDIN/STDOUT mechanism to control access to the screen and
  6973.  keyboard. For example, if two processes read from the keyboard at the same
  6974.  time, some keys go to one process, and the rest go to the other in an
  6975.  unpredictable fashion. Likewise, it is a bad idea for more than one process
  6976.  to write to the screen at the same time.1 Clearly, you don't want too many
  6977.  processes doing keyboard/screen I/O within a single screen group, but you
  6978.  also don't want too few. It would be embarrassing if a user terminated one
  6979.  interactive application in a screen group, such as a program run from
  6980.  CMD.EXE, and CMD.EXE failed to resume use of the keyboard/screen to print a
  6981.  prompt.
  6982.       So how will we handle this? We might be running a great many processes
  6983.  in a screen group. For example, you could use CMD.EXE to execute a
  6984.  spreadsheet program, which was told to execute a subshell--another copy of
  6985.  CMD.EXE. The user could then execute a program to interpret a special batch
  6986.  script, which in turn executes an editor. And this editor was told to run a
  6987.  copy of a C compiler to scan the source being edited for errors. Oh, yes,
  6988.  and we forgot to mention that the top level CMD.EXE was told to run a copy
  6989.  of the assembler in parallel with all these other operations (similar to
  6990.  the UNIX "&" operation).
  6991.       Many processes are running in this screen group; some of them are
  6992.  interactive, and some are not, and at any time only one is using the
  6993.  keyboard and the screen. Although it would be handy to declare that the
  6994.  most recently executed process will use the keyboard and the screen, you
  6995.  can't: The most recently executed program was the C compiler, and it's not
  6996.  even interactive. OS/2 cannot decide which process should be using the
  6997.  screen and keyboard because OS/2 lacks any knowledge of the function of
  6998.  each process. OS/2 knows only their child-parent relationships, and the
  6999.  situation can be far too complex for that information to be sufficient.
  7000.       Because OS/2 can't determine which process should be using the screen
  7001.  and the keyboard, it doesn't try. The processes themselves make the
  7002.  determination. The rule is simple: The process that is currently using the
  7003.  screen and the keyboard can grant access to a child process, or it can keep
  7004.  access for itself. If a process grants access to a child process, then it
  7005.  must keep off the screen and the keyboard until that child terminates. Once
  7006.  the child process is granted use of the screen and the keyboard, the child
  7007.  process is free to do as it wishes, perhaps granting access to its own
  7008.  children. Until that child process terminates, the parent must avoid device
  7009.  conflict by staying quiet.
  7010.       Let's look at how this works in real life. For example, CMD.EXE, the
  7011.  first process in the screen group, starts up with STDIN open on the
  7012.  keyboard and STDOUT open on the screen. (The system did this by magic.)
  7013.  When this copy of CMD.EXE is told to execute the spreadsheet program,
  7014.  CMD.EXE doesn't know if the spreadsheet program is interactive or not, so
  7015.  it lets the child process--the spreadsheet program--inherit its STDIN and
  7016.  STDOUT handles, which point to the keyboard and to the screen. Because
  7017.  CMD.EXE granted access to the screen and the keyboard to the child, CMD.EXE
  7018.  can't use STDIN or STDOUT until that child process terminates. Typically,
  7019.  at this point CMD.EXE would DosCWait on its child process.
  7020.       Now the spreadsheet program comes alive. It writes to STDOUT, which is
  7021.  the screen, and it reads from STDIN, which is the keyboard. When the
  7022.  spreadsheet program is instructed to run CMD.EXE, it does so, presuming, as
  7023.  did its parent, that CMD.EXE is interactive and therefore letting CMD.EXE
  7024.  inherit its STDIN and STDOUT handles. Now the spreadsheet must avoid any
  7025.  STDIN/STDOUT I/O until its child--CMD.EXE--terminates. As long as these
  7026.  processes continue to run interactive children, things are going to work
  7027.  out OK. When the children start to die and execution starts popping back up
  7028.  the tree, applications restart, using the screen and the keyboard in the
  7029.  proper order.
  7030.       But what about the detached assembly that CMD.EXE started before it
  7031.  ran the spreadsheet? In this case, the user has explicitly told CMD.EXE
  7032.  that it wants the application run "detached" from the keyboard. If the user
  7033.  specified a STDIN for the assembler--perhaps a file--then CMD.EXE sets that
  7034.  up for the child's STDIN. If the user didn't specify an alternate STDIN,
  7035.  CMD.EXE opens STDIN on the  NULL device so that an application that reads
  7036.  it will receive EOF. In this way, CMD.EXE (which knew that the application
  7037.  wasn't to use the keyboard because the user gave explicit instructions) did
  7038.  not let the child process inherit STDIN, so CMD.EXE continues to use it,
  7039.  printing a new prompt and reading a new command. Figure 14-1 shows a
  7040.  typical process tree. The shaded processes have inherited a STDIN, which
  7041.  points to the keyboard, and a STDOUT, which points to the screen. All such
  7042.  processes must lie on a single path if the rules are followed because each
  7043.  process has the option of allowing a maximum of one child to inherit its
  7044.  STDIN and STDOUT handles unchanged.
  7045.  
  7046.  
  7047.                   ┌──────────────┐
  7048.                   │░░░░░░░░░░░░░░│
  7049.                   │░░░░░░░░░░░░░░│
  7050.                   └──┬────────┬──┘                 ┌────┐    Processes
  7051.               ┌──────┘        └──────┐             │░░░░│  = using the
  7052.               │                      │             └────┘    keyboard
  7053.           ┌───┴──┐                ┌──┴───┐
  7054.           │      │                │░░░░░░│
  7055.           │      │                │░░░░░░│
  7056.           └─┬────┘                └─┬──┬─┘
  7057.          ┌──┘                    ┌──┘  └──┐
  7058.      ┌───┴──┐                 ┌──┴─┐    ┌─┴──┐
  7059.      │      │                 │░░░░│    │    │
  7060.      │      │                 └┬──┬┘    └───┬┘
  7061.      └─┬──┬─┘               ┌──┘  └──┐      └──┐
  7062.     ┌──┘  └──┐           ┌──┴─┐    ┌─┴──┐    ┌─┴──┐
  7063.  ┌──┴─┐    ┌─┴──┐        │░░░░│    │    │    │    │
  7064.  │    │    │    │        └┬──┬┘    └────┘    └────┘
  7065.  └────┘    └────┘      ┌──┘  └──┐
  7066.                     ┌──┴─┐    ┌─┴──┐
  7067.                     │    │    │░░░░│
  7068.                     └────┘    └────┘
  7069.  
  7070.  Figure 14-1.  Processes using the keyboard.
  7071.  
  7072.  
  7073.       You are undoubtedly becoming a bit concerned at this point: "Does this
  7074.  mean I'm forced to use the limited, serial STDIN/STDOUT interface for my
  7075.  high-resolution graphics output?" I'm glad you asked. What we've been
  7076.  discussing is the architectural model that must be followed because it's
  7077.  used systemwide to avoid screen and keyboard conflicts. However,
  7078.  applications can and should use special services to optimize their
  7079.  interactive I/O as long as they do so according to the architectural model.
  7080.  Specifically, OS/2 provides the KBD, VIO, and MOU dynlink packages. These
  7081.  high-performance programs interface directly with the hardware, avoiding
  7082.  the STDIN/STDOUT limited interfaces. The key is "directly with the
  7083.  hardware": A process is welcome to use hardware-specific interfaces
  7084.  to optimize performance, but only after it has ensured that the
  7085.  architectural model grants it access to that device.
  7086.       In practice, this is straightforward. Any interactive program that
  7087.  wants to use KBD first ensures (via DosQHandType) that its STDIN handle is
  7088.  open on the keyboard. If STDIN is not open on the keyboard, the keyboard
  7089.  belongs to another program, and the interactive program must not burst in
  7090.  on the rightful owner by using KBD. All dynlink device interfaces are
  7091.  trusting souls and won't check your bona fides before they do their stuff,
  7092.  so the application must look before it leaps. The same applies to STDOUT,
  7093.  to the keyboard device, and to the VIO package. All applications must
  7094.  verify that STDIN and STDOUT point to the keyboard and the screen before
  7095.  they use any device-direct interface, which includes VIO, KBD, MOU, and
  7096.  direct device access.
  7097.       What's an interactive program to do if it finds that STDIN or STDOUT
  7098.  doesn't point to the keyboard and screen devices? I don't know, but the
  7099.  author of the application does. Some applications might not be truly
  7100.  interactive and therefore would work fine. For example, Microsoft Macro
  7101.  Assembler (MASM) can prompt the user for the names of source, object, and
  7102.  listing files. Although MASM is technically interacting with the user, MASM
  7103.  is not an interactive application because it doesn't depend on the ability
  7104.  to interact to do its work. If STDIN points to a file, MASM is perfectly
  7105.  happy reading the filenames from that file. MASM doesn't need to see if
  7106.  STDIN points to the keyboard because MASM doesn't need to use the KBD
  7107.  package. Instead, MASM reads its names from STDIN and takes what it
  7108.  gets.
  7109.       Other programs may not require an interactive interface, but when they
  7110.  are interacting, they may want to use KBD or VIO to improve performance.
  7111.  Such applications should test STDIN and STDOUT to see if they point to
  7112.  the appropriate devices. If they do, applications can circumvent the
  7113.  STDIN/STDOUT limitations and use KBD and VIO. If they don't, the
  7114.  applications are stuck with STDIN and STDOUT. Finally, many interactive
  7115.  applications make no sense at all in a noninteractive environment. These
  7116.  applications need to check STDIN and STDOUT, and, if they don't point to
  7117.  the devices, the applications should write an error message to STDERR and
  7118.  terminate. Admittedly, the user is in error if he or she attempts to run an
  7119.  interactive application, such as a WYSIWYG editor, detached, but printing
  7120.  an error message is far better than trashing the display screen and
  7121.  fighting with CMD.EXE over the keyboard. The screen group would then be
  7122.  totally unusable, and the user might not even be able to terminate the
  7123.  editor if he or she can't get the terminate command through the keyboard
  7124.  contention.
  7125.       It's technically possible, although highly unusual, for an application
  7126.  to inherit access to the keyboard yet not have access to the screen. More
  7127.  commonly, an application has access to the screen but not to the keyboard.
  7128.  Although most users would find it confusing, power users can detach
  7129.  programs such as compilers so that any output summary or error messages
  7130.  they produce appear on the screen. Although the user may end up with
  7131.  intermingled output, he or she may like the instant notification. Each
  7132.  application that wants to use VIO, KBD, or the environment manager needs to
  7133.  check STDIN and STDOUT individually for access to the appropriate device.
  7134.       Earlier in this section, we talked about how applications work when
  7135.  they create children that inherit the screen and the keyboard, and it
  7136.  probably sounded complicated. In practice, it can be simple. For example,
  7137.  the technique used to DosExecPgm a child that will inherit the keyboard can
  7138.  be used when the parent itself doesn't have the keyboard and thus can't
  7139.  bequeath it. Therefore, the parent doesn't need to check its STDIN status
  7140.  during the DosExecPgm. To summarize, here are the rules:
  7141.  
  7142.       Executing Programs
  7143.  
  7144.       ■  If the child process is to inherit the STDIN handle, the parent
  7145.          process must not access that handle any further until the child
  7146.          process terminates.
  7147.  
  7148.       ■  If the child process is not to inherit the STDIN handle (so that
  7149.          your program can continue to interact), then the child process
  7150.          STDIN must be opened on a file or on the NULL device. Don't rely on
  7151.          the child not to use the handle; the child might DosExecPgm a
  7152.          grandchild that is not so well mannered.
  7153.  
  7154.       ■  A process can let only one child at a time inherit its STDIN. If a
  7155.          process is going to run multiple child processes in parallel, only
  7156.          one can inherit STDIN; the others must use alternative STDIN
  7157.          sources.
  7158.  
  7159.       ■  All these rules apply to STDINs open on pipes and files as well as
  7160.          to KBD, so your application needn't check the source of STDIN.
  7161.  
  7162.       All Processes
  7163.  
  7164.       ■  Verify that the STDIN handle points to the keyboard before using
  7165.          KBD, SIGBRK, or SIGCTLC (see below). You must not use these direct
  7166.          device facilities if STDIN is not open on the keyboard.
  7167.  
  7168.       ■  Verify that the STDOUT handle points to the screen before using
  7169.          VIO. You must not use direct device facilities if STDOUT is not
  7170.          open on the screen.
  7171.  
  7172.       ■  If the process executes any child that inherits its STDIN, it must
  7173.          not terminate itself until that child process terminates. This is
  7174.          because the parent will assume that the termination of the direct
  7175.          child means that the STDIN handle is now available.
  7176.  
  7177.  
  7178.  14.2  Ctrl-C and Ctrl-Break Handling
  7179.  
  7180.  Just when you think that it's safe to go back into the operating system,
  7181.  one more device and process tree issue needs to be discussed: the handling
  7182.  of Ctrl-C and Ctrl-Break. (Once again, this discussion applies only to
  7183.  programs that don't explicitly use the presentation manager facility. Those
  7184.  applications that do use the presentation manager have all these issues
  7185.  handled for them automatically.) These two events are tied to the keyboard
  7186.  hardware, so their routing has a great deal in common with the above
  7187.  discussion. The fundamental problem is simple: When the user presses Ctrl-C
  7188.  or Ctrl-Break, what's the operating system to do? Clearly, a process or
  7189.  processes or perhaps an entire subtree of processes must be killed or
  7190.  signaled. But do we kill or signal? And which one(s)?
  7191.       OS/2 defines a convention that allows the processes themselves to
  7192.  decide. Consider a type of application--a "command application"--that runs
  7193.  in command mode. In command mode, a command application reads a command,
  7194.  executes the command, and then typically returns to command mode.
  7195.  Furthermore, when the user presses Ctrl-Break, the command application
  7196.  doesn't want to terminate but to stop what it's doing and return to command
  7197.  mode. This is the style of most interactive applications but not that of
  7198.  most noninteractive applications. For example, if the user types MASM to
  7199.  CMD.EXE, the CMD.EXE program runs MASM as a child process. CMD.EXE is a
  7200.  command application, but MASM is not. The distinction between "command
  7201.  application" and "noncommand application" is not made by OS/2 but is merely
  7202.  descriptive terminology that is useful in this discussion.
  7203.       The system convention is that Ctrl-C and Ctrl-Break mean "Stop what
  7204.  you're doing." OS/2 generates signals in response to Ctrl-C and Ctrl-Break;
  7205.  it never directly kills a process. OS/2 can easily decide which process to
  7206.  signal when Ctrl-C or Ctrl-Break is pressed: It signals the lowest command
  7207.  process in the process tree in that screen group. At first glance, this may
  7208.  not seem easy. How can OS/2 distinguish command processes, and how can it
  7209.  determine the "lowest"? The total process tree in a screen group may be
  7210.  very complex; some processes in it may have died, creating multiple now-
  7211.  independent "treelets."
  7212.       The process tree may be complex, but the tree of processes using the
  7213.  keyboard is simpler because a process can't let multiple children
  7214.  simultaneously inherit its STDIN. A process can only inherit the
  7215.  keyboard,2 not open it explicitly; so a single path down the tree must
  7216.  intersect (or contain) all command processes. This single path can't be
  7217.  fragmented because of missing processes due to child death because a
  7218.  process that has let a child inherit its STDIN must not terminate until the
  7219.  child does. So, all OS/2 needs is to find any command process in the
  7220.  command subtree and then look at its descendants for another command
  7221.  process and so on. The bottommost process receives the signal.3
  7222.       Figure 14-2 illustrates a possible process tree. The shaded processes
  7223.  have inherited handles to the keyboard and screen; those marked with C are
  7224.  command processes.
  7225.  
  7226.  
  7227.                   ┌──────────────┐
  7228.                   │░░░░░░░░░░░░░░│
  7229.                   │░░░░░░c░░░░░░░│
  7230.                   └──┬────────┬──┘                ┌────┐    Processes
  7231.               ┌──────┘        └──────┐            │░░░░│  = using the
  7232.               │                      │            └────┘    keyboard
  7233.           ┌───┴──┐                ┌──┴───┐
  7234.           │      │                │░░░░░░│
  7235.           │      │                │░░c░░░│
  7236.           └─┬────┘                └─┬──┬─┘
  7237.          ┌──┘                    ┌──┘  └──┐
  7238.      ┌───┴──┐                 ┌──┴─┐    ┌─┴──┐
  7239.      │      │                 │░c░░│    │    │
  7240.      │      │                 └┬──┬┘    └───┬┘
  7241.      └─┬──┬─┘               ┌──┘  └──┐      └──┐
  7242.     ┌──┘  └──┐           ┌──┴─┐    ┌─┴──┐    ┌─┴──┐
  7243.  ┌──┴─┐    ┌─┴──┐        │░c░░│    │    │    │    │
  7244.  │    │    │    │        └┬──┬┘    └────┘    └────┘
  7245.  └────┘    └────┘      ┌──┘  └──┐
  7246.                     ┌──┴─┐    ┌─┴──┐
  7247.                     │    │    │░░░░│
  7248.                     └────┘    └────┘
  7249.  
  7250.  Figure 14-2.  Ctrl-C routing in a process tree.
  7251.  
  7252.  
  7253.       This now begs the final question: How can OS/2 tell if an application
  7254.  is a command process or not? It can tell because all command
  7255.  processes/command applications do something that other processes never do.
  7256.  By definition, a command process doesn't want to be summarily killed when
  7257.  the user presses Ctrl-C or Ctrl-Break, so all command processes establish
  7258.  signal handlers for Ctrl-C and Ctrl-Break. Because all command processes
  7259.  intercept Ctrl-C and Ctrl-Break, all we need now is to establish the
  7260.  convention that only command processes intercept Ctrl-C and Ctrl-Break.
  7261.  This hearkens back to our earlier discussion of checking STDIN before
  7262.  directly using the keyboard device. Telling the keyboard device that you
  7263.  want the Ctrl-C or Ctrl-Break signals routed to your process is a form of
  7264.  I/O with the keyboard device, and it must only be done if your program has
  7265.  verified that STDIN points to the keyboard device. Furthermore,
  7266.  intercepting Ctrl-C or Ctrl-Break just so that your program can clean up
  7267.  during unexpected termination is unnecessary and insufficient. The SIGTERM
  7268.  signal or, better, the exitlist mechanism provides this capability and
  7269.  covers causes of death other than the keyboard. So all processes that
  7270.  intercept Ctrl-C and Ctrl-Break have access to the keyboard, and they want
  7271.  to do something other than die when the user presses Ctrl-C or Ctrl-Break.
  7272.  They fit the command process definition.
  7273.       Now that we've exhaustively shown how OS/2 finds which process to send
  7274.  a Ctrl-C signal, what should the process do when it gets the signal? Obey
  7275.  the system convention and stop what it's doing as quickly as is wise. If
  7276.  the application isn't working on a command, the application typically
  7277.  flushes the keyboard type-ahead buffer and reprompts. If the application is
  7278.  working on a command that is implemented in code within the application,
  7279.  the application jumps from the signal handler to its command loop or, more
  7280.  commonly, sets a flag to terminate the current command prematurely.4
  7281.       Finally, if the application is running a child process, it typically
  7282.  stops what it's doing by issuing a DosKill on that child command subtree.
  7283.  This, then, is how Ctrl-C can kill a program such as MASM. Ctrl-C is sent
  7284.  to MASM's closest ancestor that is a command process,5 which in turn issues
  7285.  a DosKill on MASM's subtree. MASM does any exitlist cleanup that it
  7286.  wishes (probably deleting scratch files) and then terminates. When the
  7287.  command process that ran MASM, typically CMD.EXE, sees that MASM has
  7288.  terminated, it prints ^C on the screen, followed by a new prompt.
  7289.  
  7290.  
  7291.  
  7292.  15  The File System
  7293.  
  7294.  ───────────────────────────────────────────────────────────────────────────
  7295.  
  7296.  The file system in OS/2 version 1.0 is little changed from that of MS-DOS,
  7297.  partially in an effort to preserve compatibility with MS-DOS programs and
  7298.  partially due to limitations imposed by the project's schedule. When the
  7299.  schedule for an "all singing, all dancing" OS/2 was shown to be too long,
  7300.  planned file system improvements were moved to a future release. To explain
  7301.  the rationale for postponing something so useful, I'll digress a little.
  7302.       The microcomputer industry developed around the dual concepts of mass
  7303.  market software and standards. Because software is mass marketed, you can
  7304.  buy some very sophisticated and useful programs for a modest sum of money--
  7305.  at least modest in comparison to the development cost, which is often
  7306.  measured in millions of dollars. Mass marketing encourages standards
  7307.  because users don't want to buy machines, peripherals, and systems that
  7308.  don't run these programs. Likewise, the acceptance of the standards
  7309.  encourages the development of mass market software because standards make
  7310.  it possible for a single binary program to execute correctly on a great
  7311.  many machines and thus provide a market big enough to repay the development
  7312.  costs of a major application.
  7313.       This synergy, or positive feedback, between standards and mass market
  7314.  software affected the process of developing operating systems. At first
  7315.  glance, adding new features to an operating system seems straightforward.
  7316.  The developers create new features in a new release, and then applications
  7317.  are written to use those new features. However, with mass market software,
  7318.  it doesn't work that way. Microsoft could indeed release a new version of
  7319.  OS/2 with new features (and, in fact, we certainly will do so), but
  7320.  initially few new applications will use said new features. This is
  7321.  because of the initial limited market penetration of the new release. For
  7322.  example, let's assume that at a certain time after the availability of a
  7323.  new release, 10 percent of OS/2 users have upgraded. An ISV (Independent
  7324.  Software Vendor) is planning its next new product--one it hopes will be a
  7325.  bestseller. The ISV must decide whether to use the new feature and
  7326.  automatically lock itself out of 90 percent of the potential market or to
  7327.  use the common subset of features contained in the earlier OS/2 release and
  7328.  be able to run on all machines, including the 10 percent running the OS/2
  7329.  upgrade. In general, ISVs won't use a nonvital feature until the great
  7330.  majority of existing systems support that feature.
  7331.       The key to introducing new features in an operating system isn't that
  7332.  they be available and useful; it's that the release which contains those
  7333.  new features sees widespread use as quickly as possible. If this doesn't
  7334.  happen, then the new feature pretty much dies stillborn. This is why each
  7335.  MS-DOS release that contained major new functionality coincided with a
  7336.  release that was required to use new hardware. MS-DOS version 2.0 was
  7337.  required for the IBM XT product line; MS-DOS version 3.0 was required for
  7338.  the IBM AT product line. And OS/2 is no exception: It wasn't required for a
  7339.  new "box," but it was required to bring out the protect mode machine lying
  7340.  fallow inside 80286-based machines. If a new release of a system doesn't
  7341.  provide a new feature that makes people want it or need it badly, then
  7342.  market penetration will be slow. People will pay the cost and endure the
  7343.  hassle of upgrading only if their applications require it, and those
  7344.  applications dare require it only if most people have already upgraded.
  7345.       Because of this, the initial release of OS/2 is "magical" in the eyes
  7346.  of its developers. It provides a window of opportunity in which to
  7347.  introduce new features into the PC operating system standard, a window that
  7348.  won't be open quite as wide again for a long time. And this postponed major
  7349.  file system enhancements: The file system can be enhanced in a later
  7350.  release and benefit existing applications without any change on their part,
  7351.  whereas many other OS/2 features needed to be in the first release or they
  7352.  might never be available.
  7353.  
  7354.  
  7355.  15.1  The OS/2 File System
  7356.  
  7357.  Although OS/2 version 1.0 contains little in the way of file system
  7358.  improvements, it does contain two that are significant. The first is
  7359.  asynchronous I/O. Asynchronous I/O consists of two functions, DosReadAsync
  7360.  and DosWriteAsync. These functions are identical to DosRead and DosWrite
  7361.  except that they return to the caller immediately, usually before the I/O
  7362.  operation has completed. Each takes the handle of a semaphore that is
  7363.  cleared when the I/O operation completes. The threads of the calling
  7364.  process can use this semaphore to poll for operation complete, or they can
  7365.  wait for the operation to complete. The DosMuxSemWait call is particularly
  7366.  useful in this regard because it allows a process to wait for several
  7367.  semaphore events, which can be asynchronous I/O events, IPC events, and
  7368.  timer events, intermingled as the programmer wishes.
  7369.       The second file system feature is extended partitioning; it supports
  7370.  dividing large physical disks into multiple sections, several of which may
  7371.  contain FAT file systems. In effect, it causes OS/2 to treat a large hard
  7372.  disk as two or more smaller ones, each of which meets the file system's
  7373.  size limits. It's widely believed that MS-DOS is limited to disks less than
  7374.  32 MB in size. This isn't strictly true. The limitation is that a disk can
  7375.  have no more than 65,535 sectors; the standard sector size is 512 bytes,
  7376.  which gives the 32 MB value. Furthermore, each disk is limited to 32,768
  7377.  clusters. A sector is the unit of disk storage; disks can read and write
  7378.  only integral sectors. A sector's size is established when the disk is
  7379.  formatted. A cluster is the unit of disk space allocation for files and
  7380.  directories. It may be as small as one sector, or it may be four sectors,
  7381.  eight sectors, or some other size. Because the MS-DOS file system supports
  7382.  a maximum of 65 KB sectors but only 32 KB clusters, a 32 MB disk must be
  7383.  allocated in two-sector (or bigger) clusters. It's possible to write a
  7384.  device driver that uses a sector size that is a multiple of 512 bytes,
  7385.  which gets around the 65 KB sector restriction and allows the use of a disk
  7386.  greater than 32 MB. This trick works for MS-DOS and for OS/2, but it's not
  7387.  optimal because it doesn't do anything to increase the maximum number of
  7388.  allocation clusters from the existing 32 KB value,1 which means that
  7389.  because many disk files are small a lot of space is wasted due to internal
  7390.  fragmentation.
  7391.       The OS/2 version 1.0 extended partitioning feature provides an interim
  7392.  solution that is not quite as convenient as large sectors but that reduces
  7393.  the wastage from internal fragmentation: It allows more than one disk
  7394.  partition to contain a FAT file system. Multipartitioned disks are possible
  7395.  under MS-DOS, but only one partition can be an MS-DOS (that is, FAT) file
  7396.  system. This restriction has been relaxed in OS/2 so that, for example, a
  7397.  60 MB disk can be partitioned into two separate logical disks (for example,
  7398.  C and D), each 30 MB.
  7399.  
  7400.  
  7401.  15.2  Media Volume Management
  7402.  
  7403.  The multitasking capability of OS/2 necessitated major file system
  7404.  enhancements in the area of volume management. A disk volume is the name
  7405.  given to the file system and files on a particular disk medium. A disk
  7406.  drive that contains a fixed medium always contains the same volume, but a
  7407.  disk drive from which the media (such as floppy disks) can be removed will
  7408.  contain whatever disk--whatever volume--the user has in it at the time.
  7409.  That volumes can change becomes a problem in a multitasking environment.
  7410.  For example, suppose a user is using a word processor to edit a file on a
  7411.  floppy disk in drive A. The editor has opened the file and is keeping it
  7412.  open for the duration of the edit. Without closing the file or terminating
  7413.  the editor, the user can switch to a screen group in which a spreadsheet
  7414.  program is running. The user might then need to insert a different disk
  7415.  into drive A--one that contains data needed by the spreadsheet. If the user
  7416.  then switches back to the word processor without remembering to change the
  7417.  floppy disk, disaster will strike. Pressing the Page Down key will cause
  7418.  the editor to try to read another sector from its already open disk file.
  7419.  The operating system knows--because of FAT information stored in RAM
  7420.  buffers_ that the next sector in the text file is sector N, and it will
  7421.  issue a read to sector N on the wrong medium--the spreadsheet floppy disk--
  7422.  and return that to the word processor program as the next sector of the
  7423.  text file. And, at that, the user is getting off lightly; he or she might
  7424.  just as easily have given the word processor a command that caused it to
  7425.  write a sector to the disk, which would do double damage. A file on the
  7426.  spreadsheet floppy would be destroyed by the "random" write, and the text
  7427.  file would be corrupted as well because it's missing a sector write that it
  7428.  should have received.
  7429.       We can't solve this problem by admonishing the user to be careful;
  7430.  many programs read from and write to disk without direct user intervention.
  7431.  For example, the word processor might save work in progress to disk every
  7432.  two minutes. If this time interval elapses while the user is still working
  7433.  with the spreadsheet program on the spreadsheet floppy disk, our
  7434.  hypothetical "flawless" user is still S.O.L.2
  7435.       OS/2 resolves these problems by recognizing that when an application
  7436.  does I/O to an open file the I/O is not really aimed at drive A; it's aimed
  7437.  at a particular floppy disk volume--the one containing the open file. Each
  7438.  disk volume, removable or not, has a volume name stored in its root
  7439.  directory and a unique 32-bit volume identifier stored in its boot sector.
  7440.  Figure 15-1 illustrates the two volume names--one for computer use and one
  7441.  for human use. Each file handle is associated with a particular 32-bit
  7442.  volume ID. When an I/O request is made for a file handle, OS/2 checks to
  7443.  see if the proper volume is in the drive by comparing the 32-bit value of
  7444.  the request with that of the medium currently spinning. If they match, the
  7445.  operation completes. If the mounted volume is different from the requested
  7446.  volume, OS/2 uses the hard error daemon mechanism to prompt the user to
  7447.  insert the correct volume in the drive.
  7448.  
  7449.  
  7450.                              VOLUME LABELS
  7451.  Sector                                                          Sector
  7452.    0                                                               N
  7453.    ┌─────┬─────────────┬──────┬────────────────────────────────────┐
  7454.    │     │             │      │                                    │
  7455.    │     │             │      │                                    │
  7456.    └────┴─────────────┴─────┴────────────────────────────────────┘
  7457.       └─ 32-bit volume ID  └─────── volume name in
  7458.          in boot sector             home directory
  7459.  
  7460.  Figure 15-1.  Volume ID and volume name location.
  7461.  
  7462.       Three problems must be overcome to make this scheme practical. First,
  7463.  checking the volume ID of the medium must be fast. You can't afford to read
  7464.  the boot sector each time you do an I/O operation if doing so halves the
  7465.  speed of disk I/O. Second, you need assurance that volume IDs are unique.
  7466.  Third, you need a plan to deal with a volume that doesn't have a volume ID.
  7467.       Keeping down the cost of volume verification is easy if you know when
  7468.  a volume is changed; obviously, the ID is read from the boot sector only
  7469.  when a medium has been changed. But how can OS/2 tell that a media change
  7470.  has occurred? It can't; that's a device driver issue.
  7471.       For starters, if the device contains a nonremovable medium, rechecking
  7472.  its volume ID is never necessary. The device driver understands this, and
  7473.  when it is asked the status of the medium, it responds, "Unchanged." Some
  7474.  removable media drives have a flag bit that warns the driver that the door
  7475.  has been opened. In this case, when asked, the device driver tells OS/2
  7476.  that the medium is "uncertain." The driver doesn't know for sure that it
  7477.  was really changed, but it may have been; so OS/2 rechecks the volume ID.
  7478.  Rechecking the volume ID is more difficult when a removable media device
  7479.  has no such indicator.
  7480.       In this case, the author of the device driver uses device-specific
  7481.  knowledge to decide on a minimum possible time to effect a media change. If
  7482.  the device is ready, yet less than the minimum possible time has elapsed
  7483.  since the last operation, the driver knows that the same medium must be in
  7484.  the drive. If more than the minimum possible time has elapsed, the driver
  7485.  returns "medium uncertain," and OS/2 rechecks the volume label. This time
  7486.  interval is typically 2 seconds for floppy disk drives, so effectively an
  7487.  extra disk read is done after every idle period; for any given episode of
  7488.  disk I/O, however, no extra reads are needed.
  7489.       Ensuring that a volume ID is unique is another problem. Simply
  7490.  lecturing the user on the wisdom of unique IDs is inadequate; the user will
  7491.  still label three disks "temp" or number them all as "10." And even the
  7492.  hypothetical perfect user might borrow from a neighbor a disk whose name is
  7493.  the same as one the user already owns. OS/2 deals with this problem by
  7494.  using a 32-bit randomized value for disk volume IDs. When a disk is
  7495.  formatted, the user enters a supposedly unique name. This name is
  7496.  checksummed, and the result, combined with the number of seconds between
  7497.  the present and 1980, is used to seed a random number generator. This
  7498.  generator returns a 32-bit volume ID. Although accidentally duplicating a
  7499.  volume ID is obviously possible, the four billion possible codes make it
  7500.  quite unlikely.
  7501.       The name the user enters is used only to prompt the user to insert the
  7502.  volume when necessary, so it need not be truly unique for the volume
  7503.  management system to work. If the user names several disks WORK, OS/2 still
  7504.  sees them as independent volumes because their volume IDs are different. If
  7505.  the user inserts the wrong WORK disk in response to a prompt, OS/2
  7506.  recognizes it as the wrong disk and reissues the "Insert disk WORK" prompt.
  7507.  After trying each WORK volume in turn, the user will probably decide to
  7508.  relabel the disks!
  7509.       The thorniest problem arises from unlabeled disks--disks formatted
  7510.  with MS-DOS. Forcing the user to label these disks is unacceptable, as is
  7511.  having OS/2 automatically label them with volume IDs: The disk may be read-
  7512.  only, perhaps permanently so. Even if the disk is not read-only, the
  7513.  problem of low density and high density raises its ugly head. Low-density
  7514.  disks can be read in a high-density drive, but writes made to a low-density
  7515.  disk from a high-density drive can only be read on high-density drives. If
  7516.  a low-density disk is placed in a high-density drive and then labeled by
  7517.  OS/2, its boot sector is no longer readable when the disk is placed in a
  7518.  low-density drive.
  7519.       For volumes without a proper volume ID, OS/2 attempts to create a
  7520.  unique substitute volume ID by checksumming parts of the volume's root
  7521.  directory and its FAT table. OS/2 uses the existing volume name if one
  7522.  exists; if there is no volume name, OS/2 attempts to describe the disk.
  7523.  None of these techniques is foolproof, and they require extra disk
  7524.  operations every time the medium is identified. Therefore, software
  7525.  distributors and users should make every effort to label disks that OS/2
  7526.  systems are to use. OS/2 labels are backward compatible with MS-DOS version
  7527.  3.x labels.
  7528.       The OS/2 DISKCOPY command makes a byte-by-byte verbatim copy of a
  7529.  floppy disk, except that the duplicate disk has a different volume ID value
  7530.  in the boot sector (the volume label name is not changed). OS/2 users can't
  7531.  tell this, however, because the DISKCOMP utility lies, and if two disks are
  7532.  identical in every byte except for the volume ID, it reports that the disks
  7533.  are identical. However, if the user uses DISKCOPY to duplicate the disk
  7534.  under OS/2 and then compares the two with DISKCOMP under MS-DOS 3.x, a
  7535.  difference is reported.
  7536.       Our discussion so far has centered on file reads and writes to an open
  7537.  handle. Reads and writes are volume-oriented operations because they're
  7538.  aimed at the volume on which the file resides. DosOpens, on the other hand,
  7539.  are drive oriented because they search the default or specified drive for
  7540.  the file in question (or create it) regardless of the volume in the drive.
  7541.  All handle operations are volume oriented, and all name-based calls are
  7542.  drive oriented. Currently, you cannot specify that a given file is to be
  7543.  opened on or created on a specific volume. To ensure that a scratch or
  7544.  output file is created on a certain volume, arrange to have a file open on
  7545.  that volume and issue a write to that file immediately before doing the
  7546.  file open. The write operation followed by a DosBufReset will ensure that
  7547.  the particular medium is in the drive at that time.
  7548.  
  7549.  
  7550.  15.3  I/O Efficiency
  7551.  
  7552.  OS/2 provides full blocking and deblocking services for all disk I/O
  7553.  requests. A program can read or write any number of bytes, and OS/2 will
  7554.  read the proper sectors into internal buffers so that only the specified
  7555.  bytes are affected. Naturally, every DosRead or DosWrite call takes time to
  7556.  execute, so if your program makes few I/O calls, each for large amounts of
  7557.  data, it will execute faster.
  7558.       I/O performance can be further improved by making sector aligned
  7559.  calls, that is, by requesting a transfer of an integral multiple of 512
  7560.  bytes to or from a file seek position that is itself a multiple of 512.
  7561.  OS/2 reads and writes entire disk sectors directly from and to the device
  7562.  hardware without an intermediate copy step through system buffers. Because
  7563.  the file system keeps logically adjacent sectors physically adjacent on the
  7564.  disk, disk seek times and rotational latency are such that one can read or
  7565.  write four sectors of data (2048 bytes) in essentially the same time needed
  7566.  to read or write one sector (512 bytes).
  7567.       Even if the length or the file position of the request isn't a
  7568.  multiple of 512, OS/2 performs the initial fraction of the request via its
  7569.  buffers, directly transfers any whole sectors out of the middle of the
  7570.  request, and uses the buffers for the fractional remainder. Even if your
  7571.  requests aren't sector aligned, making them as large as feasible is
  7572.  beneficial.
  7573.       To summarize, I/O is most efficient when requests are large and sector
  7574.  aligned. Even misaligned requests can be almost optimally serviced if they
  7575.  are large. Programs that cannot naturally make aligned requests and that
  7576.  are not I/O intensive should take advantage of the blocking and deblocking
  7577.  services that OS/2 provides. Likewise, programs that need to make large,
  7578.  unaligned requests should use OS/2's blocking management. Programs that
  7579.  need to make frequent, small, nonaligned requests will perform best if they
  7580.  read blocks of sectors into internal buffers and deblock the data
  7581.  themselves, avoiding the overhead of frequent DosRead or DosWrite calls.
  7582.  
  7583.  
  7584.  
  7585.  16  Device Monitors, Data Integrity, and Timer Services
  7586.  
  7587.  ───────────────────────────────────────────────────────────────────────────
  7588.  
  7589.  In discussing the design goals of OS/2, I mentioned continuing to support
  7590.  the kinds of functionality found in MS-DOS, even when that functionality
  7591.  was obtained by going around the operating system. A good example of such
  7592.  functionality is device data manipulation, a technique that usually
  7593.  involves hooking interrupt vectors and that is used by many application
  7594.  programs. For example, pop-up programs such as SideKick have become very
  7595.  popular. These programs get into memory via the terminate and stay resident
  7596.  mechanism and then edit the keyboard interrupt vector to point to their
  7597.  code. These programs examine each keystroke to see if it is their special
  7598.  activate key. If not, they transfer control to the original interrupt
  7599.  handler. If the keystroke is their special activate key, they retain
  7600.  control of the CPU and display, or "pop up," a message or a menu on the
  7601.  screen. Other programs hook the keyboard vector to provide spell checking
  7602.  or keyboard macro expansion. Some programs also hook the BIOS entry vector
  7603.  that commands the printer, either to substitute alternate printer driver
  7604.  code or to manipulate the data sent to the printer. Programs that turn a
  7605.  spreadsheet's output sideways are an example of this.
  7606.       In general, these programs edit, or hook, the interrupt vectors that
  7607.  receive device interrupts and communicate with device driver routines in
  7608.  the ROM BIOS. The functions provided by such programs and their evident
  7609.  popularity among users demonstrate a need for programs to be able to
  7610.  monitor and/or modify device data streams. The OS/2 mechanism that does
  7611.  this is called a device monitor.
  7612.  
  7613.  
  7614.  16.1  Device Monitors
  7615.  
  7616.  The design of device monitors had to meet the general requirements and
  7617.  religion of OS/2. Specifically, the MS-DOS technique of letting
  7618.  applications receive interrupts by editing the interrupt vectors could not
  7619.  be allowed because doing so would destroy the system's ability to provide a
  7620.  stable environment. Furthermore, unlike MS-DOS, OS/2 doesn't use the ROM
  7621.  BIOS as a form of device driver, so hooking the BIOS communication vectors
  7622.  would not provide access to the device data stream. In addition, allowing
  7623.  an application to arbitrarily interfere with a device driver's operation is
  7624.  contrary to OS/2 design principles; the device driver is the architectural
  7625.  embodiment of knowledge about the device, and it must be involved in and
  7626.  "aware" of any external manipulation of the data stream. The result is an
  7627.  OS/2 device monitor mechanism that allows processes, running in their
  7628.  normal ring 3 state, to monitor and edit device data streams with the prior
  7629.  permission and knowledge of the appropriate device driver.
  7630.       Specifically, a process registers itself as a device monitor by
  7631.  calling the appropriate device driver via a DosMonReg call.1 The process
  7632.  also provides two data buffers, one for incoming monitor data and another
  7633.  for outgoing monitor data. Processes can easily call OS/2, but OS/2 has no
  7634.  way to call processes.2 OS/2 gets around this by inverting the normal
  7635.  sense of a call and return sequence. When OS/2 needs to "call" a process,
  7636.  it requires that process to call OS/2 beforehand with one of its threads.
  7637.  OS/2 holds this thread captive until the callback event takes place. OS/2
  7638.  then accomplishes a call to the process by releasing the thread so that it
  7639.  returns from the holding system call and resumes execution within the
  7640.  process. When the process is ready to "return" to OS/2, it recalls the
  7641.  holding entry point (see Figure 16-1).
  7642.  
  7643.  
  7644.        Process calls OS/2     |        OS/2 "calls" Process
  7645.                               |
  7646.  Process  call                |  Process  call           FCN    call
  7647.      ──────┐       ┌──────    |      ──────┐       ┌─────────────┐
  7648.                             |                                
  7649.            │       │          |            │       │             │
  7650.            │       │          |            │       │             │
  7651.            │       │          |            │       │             │
  7652.            │       │          |            │       │             │
  7653.            │       │          |            │       │             │
  7654.            │       │          |            │       │             │
  7655.    ─ ─ ─ ─ ┼ ─ ─ ─ ┼ ─ ─ ─    |    ─ ─ ─ ─ ┼ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ┼ ─ ─
  7656.   OS/2     │       │          |   OS/2     │       │             │
  7657.            │       │          |            │       │             │
  7658.            │       │          |            │       │             │
  7659.                             |                                
  7660.            │       │          |            │       │             │
  7661.            │  FCN  │          |            │       │             │
  7662.            └───────┘          |            └──< <──┘             └───<
  7663.                  Return       |                > > Return             >
  7664.  
  7665.  Figure 16-1.  "Calling" a process from OS/2.
  7666.  
  7667.  
  7668.       OS/2 uses this technique for monitors as well. A monitoring process is
  7669.  required to call the OS/2 entry point directly after registering itself as
  7670.  a device monitor. OS/2 notifies the monitor process of the presence of data
  7671.  in the incoming buffer by allowing this thread to return to the process.
  7672.  Figure 16-2 illustrates a device with two monitoring processes, X and Y.
  7673.  
  7674.  
  7675.  ┌──────────────────────────┐              ┌──────────────────────────┐
  7676.  │         Monitor          │              │         Monitor          │
  7677.  │        Process X         │              │        Process Y         │
  7678.  │                          │              │                          │
  7679.  │ DosMonRead   DosMonWrite │              │ DosMonRead   DosMonWrite │
  7680.  └─────────────────┬───────┘              └─────────────────┬───────┘
  7681.        └─┐          └───────────┐      ┌─────────┘            │
  7682.          │                      │      │                      │
  7683.  ┌───────┼──────────────────────┼──────┼──────────────────────┼───────┐
  7684.  │       │                      │      │                      │       │
  7685.  │   ┌───┴────┐                ┌──────┴┐                ┌───────┐   │
  7686.  │   │ Buffer │                │ Buffer │                │ Buffer │   │
  7687.  │   │   1    │                │   2    │                │   3    │   │
  7688.  │   └───────┘                └────────┘                └────┬───┘   │
  7689.  │  ┌────┘                        OS/2                        └────┐  │
  7690.  └──┼──────────────────────────────────────────────────────────────┼──┘
  7691.     │ Data in                                             Data out │
  7692.     │                                                              │
  7693.  ┌──┼──────────────────────────────────────────────────────────────┼──┐
  7694.  │  │                        Device driver                           │
  7695.  └────────────────────────────────────────────────────────────────────┘
  7696.  
  7697.  Figure 16-2.  Device monitors.
  7698.  
  7699.  
  7700.       But we need to discuss a few additional details. First, because OS/2
  7701.  strives to make processes see a consistent environment regardless of the
  7702.  presence of other processes, each device can have as many monitors as the
  7703.  device driver allows. OS/2 connects multiple device monitors into a chain
  7704.  so that the device data stream is passed through the first monitor in the
  7705.  chain, then through the second monitor, and so on. When a process registers
  7706.  itself as a monitor, it specifies whether it wants to be first in the chain
  7707.  or last in the chain; some applications are sensitive to this. The first
  7708.  monitor to register itself as first is truly first; the next monitor to ask
  7709.  for first actually becomes second, and so forth. The same algorithm applies
  7710.  to monitors that want to be last: The first to so request becomes the last,
  7711.  the second to request last becomes next to last, and so forth.
  7712.       The actual format of the monitor data stream is device specific; the
  7713.  device driver decrees the format. Some device drivers have special rules
  7714.  and requirements. For example, the keyboard device driver allows monitoring
  7715.  processes to insert the "screen switch" key sequence into the data stream,
  7716.  whereupon it is recognized as if the user had typed it at the physical
  7717.  keyboard. But the device driver will not pass such sequences that really
  7718.  were typed through the monitor chain; they are directly obeyed instead.
  7719.       This approach prevents an amok keyboard monitor from effectively
  7720.  crashing the system by intercepting and consuming all attempts by the user
  7721.  to switch screen groups. The screen device driver does not allow device
  7722.  monitors, not because the performance impact would be too big (as it, in
  7723.  fact, would be) but because the VIO and presentation manager dynlink
  7724.  packages totally circumvent the screen device driver so that it never sees
  7725.  any screen data being written.
  7726.       The DosMonRead call holds the device's thread until incoming data is
  7727.  available. The DosMonWrite call returns the CPU to the process as soon as
  7728.  it is able. The same thread that calls DosMonRead need not be the one to
  7729.  call DosMonWrite (see below).
  7730.       Because monitors are an important component of OS/2, an application
  7731.  must be very careful to use them properly; therefore, some caveats are in
  7732.  order. First, monitors are inserted into the device data chain, with
  7733.  obvious effects on the data throughput rate of the device. Each time the
  7734.  user presses a key, for example, a packet must pass through every monitor
  7735.  in the keyboard chain before the application can read the key and obey or
  7736.  echo it. Clearly, any sluggishness on the part of a monitor or the presence
  7737.  of too many monitors in a chain will adversely affect system response. The
  7738.  thread involved in reading, processing, and writing monitor data should be
  7739.  set at a high priority. We recommend the lowest of the force run priority
  7740.  categories. Furthermore, the monitor component of a monitoring application
  7741.  must contain no critical sections or other events that could slow or
  7742.  suspend its operation. In addition, if a monitor data stream will be
  7743.  extensively processed, a normal-priority thread must be used to handle that
  7744.  processing so that the high-priority thread can continue to transfer
  7745.  monitor data in and out without impediment. For example, an auxiliary
  7746.  thread and buffer must be used if a keyboard monitor is to write all
  7747.  keystrokes to a disk buffer.
  7748.       Finally, if a monitor process terminates abnormally although OS/2
  7749.  properly unlinks it from the monitor chain, the data in the process's
  7750.  monitor buffers is lost. Clearly, losing an unspecified amount of data
  7751.  without warning from the keyboard data stream or perhaps from printer
  7752.  output will upset the user no little amount. Monitoring processes must be
  7753.  written carefully so that they minimize this risk.
  7754.       The device monitor feature threatens OS/2's fundamental architectural
  7755.  principles more than any other. Thus, its presence in the system testifies
  7756.  to its importance. Specifically, device monitors violate the design
  7757.  principle of minimizing interference between processes, a.k.a.
  7758.  encapsulation. Clearly, a process that is monitoring a device's data stream
  7759.  can affect the output of or input to a great many processes other than
  7760.  itself. This is sometimes called a feature, not a bug. For example, the
  7761.  printer spooler uses monitors to intercept output aimed at the printer,
  7762.  storing it on disk, and to feed data from those disk files to the actual
  7763.  printer device. Clearly, spooling printer output interferes with another
  7764.  process, but the interference is valuable. Designers of monitoring
  7765.  applications must ensure that their applications damage neither the
  7766.  system's performance nor its stability.
  7767.  
  7768.  
  7769.  16.2  Data Integrity
  7770.  
  7771.  I've discussed data integrity in a multitasking environment several times.
  7772.  This section does not review that material in detail but brings together
  7773.  all the elements and introduces a few related system facilities.
  7774.       The first problem in a multitasking system is that multiple processes,
  7775.  or multiple threads within a process, may try to simultaneously manipulate
  7776.  the same resource--a file, a device, a data structure in memory, or even a
  7777.  single byte of memory. When the manipulation of a resource must be
  7778.  serialized to work correctly, that manipulation is called a critical
  7779.  section. This term refers to the act of manipulating the resource, but not
  7780.  particularly to the code that does so. Clearly, if any of four subroutines
  7781.  can manipulate a particular resource, entering any of the four is entering
  7782.  the critical section.
  7783.       The problem is more pervasive than a programmer unfamiliar with the
  7784.  issue might assume. For example, even the simple act of testing a word to
  7785.  see if it holds the value 4 and incrementing it if it doesn't is a critical
  7786.  section. If only one thread in one process can access this word, then the
  7787.  critical section is serialized. But if more than one thread can access the
  7788.  word, then more than one thread could be in the critical section at the
  7789.  same time, with disastrous results. Specifically, consider the assembly
  7790.  language sequence shown in Listing 16-1. It looks simple enough: Test to
  7791.  see if COUNT holds 4; if it doesn't, increment it; if it does, jump to the
  7792.  label COMPLETE. Listing 16-2 shows what might go wrong in a multithreaded
  7793.  environment: Thread A checks the value to see if it's 4, but it's 3. Right
  7794.  after the compare instruction, a context switch takes place, and thread B
  7795.  is executed. Thread B also performs the compare, sees the value as 3, and
  7796.  increments it. Later, thread A resumes execution, after the compare
  7797.  instruction, at a location where it believes the COUNT value to be 3; so it
  7798.  also increments the value of COUNT. The value is now 5 and will continue to
  7799.  be incremented way past the value of 4 that was supposed to be its upper
  7800.  limit. The label COMPLETE may never be reached.
  7801.  
  7802.  
  7803.  COUNT        DW      0               ; Event counter
  7804.  
  7805.          .
  7806.          .
  7807.          CMP     COUNT,4        ; is this the 4th?
  7808.          JE      COMPLETE       ; yes, we're done
  7809.          INC     COUNT          ; count event
  7810.          .
  7811.          .
  7812.  
  7813.  Listing 16-1.
  7814.  
  7815.  
  7816.          Thread A                                     Thread B
  7817.  
  7818.               .
  7819.               .
  7820.               CMP     COUNT,4  [count is now 3]
  7821.               -------------------context switch--->
  7822.                                      CMP     COUNT,4 [count is 3]
  7823.                                      JE      COMPLETE
  7824.                                      INC     COUNT   [count is 4]
  7825.                                                .
  7826.                                                .
  7827.               <-----------context switch--------
  7828.               JE      COMPLETE [jmp not taken]
  7829.               INC     COUNT    [count is now 5]
  7830.  
  7831.  Listing 16-2.
  7832.  
  7833.  
  7834.       I apologize for again lecturing on this topic, but such problems are
  7835.  very nonobvious, rarely turn up in testing, are nearly impossible to find
  7836.  in the field, and the very possibility of their existence is new with OS/2.
  7837.  Thus, "too much is not enough," caveat-wise. Now that we've reviewed the
  7838.  problems, let's look at the solutions.
  7839.       A programmer inexperienced with a multitasking environment might
  7840.  protest that this scenario is unlikely, and indeed it is. Maybe the chances
  7841.  are only 1 in 1 million that it would happen. But because a microprocessor
  7842.  executes 1 million instructions a second, it might not be all that long
  7843.  before the 1-in-1-million unlucky chance comes true. Furthermore, an
  7844.  incorrect program normally has multiple unprotected critical sections, many
  7845.  of which are larger than the 2-instruction window in our simple example.
  7846.       The program must identify and protect all critical sections; a program
  7847.  that fails to do so will randomly fail. You can't take solace in there
  7848.  being only one CPU and assuming that OS/2 probably won't context switch in
  7849.  the critical section. OS/2 can context switch at any time, and because
  7850.  context switching can be triggered by unpredictable external events, such
  7851.  as serial port I/O and rotational latency on a disk, no amount of testing
  7852.  can prove that an unprotected critical section is safe. In reality, a test
  7853.  environment is often relatively simple; context switching tends to occur at
  7854.  consistent intervals, which means that such problems tend not to turn up
  7855.  during program test. Instead, they turn up in the real world, and give your
  7856.  program a reputation for instability.
  7857.       Naturally, testing has its place, but the only sure way to deal with
  7858.  critical sections is to examine your code carefully while assuming that all
  7859.  threads in the system are executing simultaneously.3 Furthermore, when
  7860.  examining a code sequence, always assume that the CPU will reschedule in
  7861.  the worst way. If there is any possible window, reality will find it.
  7862.  
  7863.  
  7864.  16.2.1  Semaphores
  7865.  The traditional solution for protecting critical sections is the semaphore.
  7866.  The two OS/2 semaphores--RAM and system--each have advantages, and the
  7867.  operation of each is guaranteed to be completely immune to critical section
  7868.  problems. In the jargon, their operation is guaranteed atomic. Whenever a
  7869.  thread is going to manipulate a critical resource, it first claims the
  7870.  semaphore that protects the resource. Only after it controls the semaphore
  7871.  does it look at the resource because the resource's values may have changed
  7872.  between the time the semaphore was requested and the time it was granted.
  7873.  After the thread completes its manipulation of the resource, it releases
  7874.  the semaphore.
  7875.       The semaphore mechanism protects well against all cooperating4
  7876.  threads, whether they belong to the same process or to different processes.
  7877.  Another OS/2 mechanism, called DosEnterCritSec, can be used to protect a
  7878.  critical section that is accessed only by threads belonging to a single
  7879.  process. When a thread issues the DosEnterCritSec call, OS/2 suspends
  7880.  execution of all other threads in that process until a subsequent
  7881.  DosEnterCritSec call is issued. Naturally, only threads executing in
  7882.  application mode are suspended; threads executing inside the OS/2 kernel
  7883.  are not suspended until they attempt to return to application mode.5 The
  7884.  use of DosEnterCritSec is dangerous because the process's other threads may
  7885.  be suspended while they are holding a critical section. If the thread that
  7886.  issued the DosEnterCritSec then also tries to enter that critical section,
  7887.  the process will deadlock. If a dynlink package is involved, it may have
  7888.  created extra threads unbeknownst to the client process so that the client
  7889.  may not even be aware that such a critical section exists and might be in
  7890.  use. For this reason, DosEnterCritSec is safe only when used to protect
  7891.  short sections of code that can't block or deadlock and that don't call any
  7892.  dynlink modules.
  7893.       Still another OS/2 critical section facility is file sharing and
  7894.  record locking, which can be used to protect critical sections when they
  7895.  consist of files or parts of files. For example, a database program
  7896.  certainly considers its master database file a critical section, and it
  7897.  doesn't want anyone messing with it while the database application has it
  7898.  open. It can open the file with the file-sharing mode set to "allow no
  7899.  (other) readers, allow no writers." As long as the database application
  7900.  keeps the file open, OS/2 prevents any other process from opening (or
  7901.  deleting!) that file.
  7902.       The record-locking mechanism can be used to provide a smaller
  7903.  granularity of protection. A process can lock a range of bytes within a
  7904.  file, and while that lock is in effect, OS/2 prevents any other process
  7905.  from reading or writing those bytes. These two specialized forms of
  7906.  critical section protection are unique in that they protect a process
  7907.  against all other processes, even "uncooperating" ones that don't protect
  7908.  their own access to the critical section. Unfortunately, the file-sharing
  7909.  and record-locking mechanisms don't contain any provision for blocking
  7910.  until the conflict is released. Applications that want to wait for the
  7911.  conflict to clear must use a polling loop. Use DosSleep to block for at
  7912.  least a half second between each poll.
  7913.       Unfortunately, although semaphores protect critical sections well,
  7914.  sometimes they bring problems of their own. Specifically, what happens if
  7915.  an asynchronous event, such as program termination or a signal, pulls the
  7916.  CPU away from inside a critical section and the CPU never returns to
  7917.  release the semaphore? The answers range from "moot" to "disaster,"
  7918.  depending on the circumstances. The possibilities are so manifold that
  7919.  I'll group some of them.
  7920.       What can you do if the CPU is pulled away inside a critical
  7921.  section?
  7922.  
  7923.       ■  Ignore it. This is fine if the critical section is wholly accessed
  7924.          by a single process and that process doesn't use signals to modify
  7925.          the normal path of execution and if neither the process nor its
  7926.          dynlink routines attempt to enter the critical section during
  7927.          DosExitList processing.
  7928.  
  7929.       ■  Clear the semaphore. This is an option if you know that the
  7930.          resource protected by the semaphore has no state, such as a
  7931.          semaphore that protects the right to be writing to the screen. The
  7932.          trick is to ensure that the interrupted thread set the semaphore
  7933.          and that you don't accidentally clear the semaphore when you don't
  7934.          set it. For example, if the semaphore is wholly used within a
  7935.          single process but that process's DosExitList handlers may use it,
  7936.          they can force the semaphore clear when they are entered.
  7937.  
  7938.       ■  Detect the situation and repair the critical section. This
  7939.          detection can be made for RAM semaphores only during process
  7940.          termination and only if the semaphore is solely used by that
  7941.          process. In such a case, you know that a thread in the process set
  7942.          the semaphore, and you know that the thread is no longer executing
  7943.          the critical section because all threads are terminated. You can
  7944.          test the semaphore by using a nonblocking DosSemSet; if it's set,
  7945.          "recover" the resource.
  7946.  
  7947.          System semaphores are generally better suited for this. When the
  7948.          owning thread of a system semaphore dies, the semaphore is given a
  7949.          special mark. The next attempt to set the semaphore returns with a
  7950.          code that tells the new owner that the previous owner died within
  7951.          the critical section. The new owner has the option of cleaning up
  7952.          the resource.
  7953.  
  7954.       Another possibility is to try to prevent the CPU from being yanked out
  7955.  of a critical section. Signals can be momentarily delayed with the
  7956.  DosHoldSignal mechanism. Process termination that results from an external
  7957.  kill can be postponed by setting up a signal handler for the KILL signal
  7958.  and then using DosHoldSignal. This last technique doesn't protect you
  7959.  against termination due to GP fault and the like however.
  7960.  
  7961.  
  7962.  16.2.2  DosBufReset
  7963.  One remaining data integrity issue--disk data synchronization--is not
  7964.  related to critical sections. Often, when a DosWrite call is made, OS/2
  7965.  holds the data in a buffer rather than writing it immediately to disk.
  7966.  Naturally, any subsequent calls made to read this data are satisfied
  7967.  correctly, so an application cannot see that the data has not yet been
  7968.  written unless the reading application uses direct physical access to the
  7969.  volume (that is, raw media reads). This case explains why CHKDSK may
  7970.  erroneously report errors that run on a volume that has open files.
  7971.       OS/2 eventually writes the data to the disk, so this buffering is of
  7972.  concern only when the system crashes with unwritten buffered data.
  7973.  Naturally, such crashes are expected to be rare, but some applications may
  7974.  find the possibility so threatening that they want to take protective
  7975.  steps. The two OS/2 functions for this purpose are flushing and
  7976.  writethroughs. The flush operation--DosBufReset--writes all dirty buffers--
  7977.  those with changed but unwritten data in them--to the disk. When the call
  7978.  returns, the data is on the disk. Use this call sparingly; although its
  7979.  specification promises only that it will flush buffers associated with the
  7980.  specified file handle(s), for most file systems it writes all dirty buffers
  7981.  in the system to disk. Moreover, if file handles are open to a server
  7982.  machine on the network, most or all of that server's buffers get flushed,
  7983.  even those that were used by other client machines on the network. Because
  7984.  of these costs, applications should use this operation judiciously.
  7985.       Note that it's not true that a flush operation simply causes a write
  7986.  to be done sooner rather than later. A flush operation may also cause extra
  7987.  disk writes. For example, consider an application that is writing data 10
  7988.  bytes at a time. In this case, OS/2 buffers the data until it has a full
  7989.  sector's worth. A series of buffer flush operations arriving at this time
  7990.  would cause the assembly buffer to be written to the disk many extra and
  7991.  unnecessary times.
  7992.  
  7993.  
  7994.  16.2.3  Writethroughs
  7995.  Buffer flushes are expensive, and unless they are used frequently, they
  7996.  don't guarantee a particular write ordering. Some applications, such as
  7997.  database managers, may want to guarantee that data be written to the disk
  7998.  in exactly the same order in which it was given to OS/2 via DosWrite. For
  7999.  example, an application may want to guarantee that the data is in place in
  8000.  a database before the allocation chain is written and that the chain be
  8001.  written before the database directory is updated. Such an ordering may make
  8002.  it easy for the package to recover the database in case of a crash.
  8003.       The OS/2 mechanism for doing this is called writethrough--a status bit
  8004.  that can be set for individual file handles. If a writethrough is in effect
  8005.  for a handle to which the write is issued, OS/2 guarantees that the data
  8006.  will be written to the disk before the DosWrite operation returns.
  8007.  Obviously, applications using writethrough should write their data in large
  8008.  chunks; writing many small chunks of data to a file marked for writethrough
  8009.  is very inefficient.
  8010.  
  8011.       Three caveats are associated with writethroughs:
  8012.  
  8013.       ■  If writethrough is set on a file after it is open, all subsequent
  8014.          writes are written through, but data from previous writes may still
  8015.          be in dirty buffers.
  8016.  
  8017.       ■  If a writethrough file is being shared by multiple processes or is
  8018.          open on multiple handles, all instances of that file should be
  8019.          marked writethrough. Data written to a handle not marked
  8020.          writethrough may go into the buffers.
  8021.  
  8022.       ■  The operation of data writethroughs has some nonintuitive surprises
  8023.          when used with the current FAT file system. Specifically, although
  8024.          this feature works as advertised to place the file's data sectors
  8025.          on the disk, it does not update the directory entry that specifies
  8026.          the size of the file. Thus, if you extend a file by 10 sectors and
  8027.          the system crashes before you close the file, the data in those 10
  8028.          sectors is lost. If you had writethrough set, then those 10 sectors
  8029.          of data were indeed written to the disk; but because the directory
  8030.          entry wasn't updated, CHKDSK will return those sectors to the free
  8031.          list.
  8032.  
  8033.          The writethrough operation protects the file's data but not the
  8034.          directory or allocation information. This is not a concern as long
  8035.          as you write over a portion of the file that has been already
  8036.          extended, but any writes that extend the file are not protected.
  8037.          The good news is that the data will be on the disk, as guaranteed,
  8038.          but the bad news is that the directory entry won't be updated; if
  8039.          the system crashes, file extensions cannot be recovered. The
  8040.          recommended solution is to use DosNewSize to extend the file as
  8041.          needed, followed by DosBufReset to update the directory information
  8042.          on the disk, and then to writethrough the data as needed.
  8043.          Overextending the file size is better than doing too many
  8044.          NewSize/BufReset combinations; if you overextend, you can always
  8045.          shrink the file before closing it with a final DosNewSize.
  8046.  
  8047.  
  8048.  16.3  Timer Services
  8049.  
  8050.  Frequently, applications want to keep track of the passage of real time. A
  8051.  game program may want events to occur asynchronously with the user's input;
  8052.  a telecommunications program may want to track how long a response takes
  8053.  and perhaps declare a link timed-out after some interval. Other programs
  8054.  may need to pace the display of a demonstration or assume a default action
  8055.  if the user doesn't respond in a reasonable amount of time. OS/2 provides
  8056.  several facilities to track the passage of real time; applications should
  8057.  use these facilities and shun polling and timing loops because the timing
  8058.  of such loops depends on the system's workload and the CPU's speed and
  8059.  because they totally lock out from execution any thread of a lower
  8060.  priority.
  8061.       Time intervals in OS/2 are discussed in terms of milliseconds to
  8062.  isolate the concept of a time interval from the physical mechanism
  8063.  (periodic clock interrupts) that measures time intervals. Although you can
  8064.  specify a time interval down to the millisecond, the system does not
  8065.  guarantee any such accuracy.
  8066.       On most hardware, OS/2 version 1.0 uses a periodic system clock
  8067.  interrupt of 32 Hz (32 times a second). This means that OS/2 measures time
  8068.  intervals with a quantum size of 31.25 milliseconds. As a result, any
  8069.  timeout value is subject to quantization error of this order. For example,
  8070.  if a process asks to sleep for 25 milliseconds, OS/2 knows that the request
  8071.  was made at some time after the most recent clock tick, but it cannot tell
  8072.  how long after, other than that less than 31.25 milliseconds had elapsed
  8073.  between the previous clock tick and the sleep request. After the sleep
  8074.  request is made, another clock tick occurs. Once again, OS/2 can't tell how
  8075.  much time has elapsed since the sleep request and the new clock tick, other
  8076.  than that it was less than 31.25 milliseconds. Lacking this knowledge, OS/2
  8077.  uses a simple algorithm: At each clock tick, OS/2 decrements each timeout
  8078.  value in the system by the clock tick interval (generally 31.25
  8079.  milliseconds). Thus, our 25-millisecond sleep request may come back in 1
  8080.  millisecond or less or in 31.25 milliseconds. A request to block for 33
  8081.  milliseconds could come back in 32 milliseconds or in 62.5 milliseconds.
  8082.       Clearly, the OS/2 timer functions are intended for human-scale timing,
  8083.  in which the 1/32-second quantization error is not noticeable, and not for
  8084.  high-precision timing of fast events. Regardless of the resolution of the
  8085.  timer, the system's preemptive scheduler prevents the implementation of
  8086.  high-accuracy short-interval timing. Even if a timer system call were to
  8087.  time out after a precise interval, the calling thread might not resume
  8088.  execution immediately because a higher-priority thread might be executing
  8089.  elsewhere.
  8090.       One form of OS/2 timer services is built into some system calls. For
  8091.  example, all semaphore blocking calls support an argument that allows the
  8092.  caller to specify a timeout value. When the specified time has elapsed, the
  8093.  call returns with a "call timed out" error code. Some threads use this
  8094.  facility to guard against being indefinitely locked out; if the semaphore
  8095.  call times out, the thread can give up, display an error message, or try
  8096.  another tactic. Other threads may use the facility expecting to be timed
  8097.  out: They use the timeout facility to perform periodic tasks and use the
  8098.  semaphore just as an emergency flag. Another thread in the system can
  8099.  provide an emergency wakeup for the timer thread simply by clearing the
  8100.  semaphore.
  8101.       Blocking on a semaphore merely to delay for a specific interval is
  8102.  unnecessary; the DosSleep call allows a thread to block unconditionally for
  8103.  an arbitrary length of time, subject, of course, to the timer's
  8104.  quantization error.6 DosSleep measures time intervals in a synchronous
  8105.  fashion: The thread is held inside the operating system until the time
  8106.  interval has elapsed. OS/2 provides an asynchronous timer service that
  8107.  allows timing to take place in parallel with a thread's normal execution.
  8108.  Specifically, the DosTimerAsync call is made with a timeout interval, such
  8109.  as DosSleep, and also with the handle of a system semaphore.7 The
  8110.  DosTimerAsync call returns immediately; later, when the time interval has
  8111.  elapsed, the system semaphore is cleared. The process can poll the
  8112.  semaphore to see if the time is up, and/or it can block on the semaphore to
  8113.  wait for the time to elapse. Of course, if a process contains multiple
  8114.  threads, some can poll and others can block.
  8115.       The DosTimerStart call is identical to the DosTimerAsync call except
  8116.  that the semaphore is repeatedly cleared at the specified interval until a
  8117.  corresponding DosTimerStop call is made. DosTimerStart clears the
  8118.  semaphore; the process must set it again after it's been cleared.
  8119.       None of the above-mentioned facilities is completely accurate for
  8120.  tracking the time of day or the amount of elapsed time. As we mentioned, if
  8121.  a higher-priority thread is consuming enough CPU time, unpredictable delays
  8122.  occur. Even DosTimerStart is susceptible to losing ticks because if the CPU
  8123.  is unavailable for a long enough period the process won't be able to reset
  8124.  the semaphore soon enough to prevent missing its next clearing.
  8125.  Applications that want a precise measurement of elapsed time should use the
  8126.  time values stored in the global infoseg. We also recommend that
  8127.  applications with a critical need to manage timeouts, even if they are
  8128.  executing in the lower-priority background, dedicate a thread to managing
  8129.  the time-critical work and elevate that thread to a higher priority. This
  8130.  will ensure that time-critical events aren't missed because a high-priority
  8131.  foreground thread is going through a period of intensive CPU usage. Of
  8132.  course, such an application must be designed so that the high-priority
  8133.  timer event thread does not itself consume significant CPU time; it should
  8134.  simply log the timer events and rely on its fellow normal-priority threads
  8135.  to handle the major work involved.
  8136.  
  8137.  
  8138.  
  8139.  17  Device Drivers and Hard Errors
  8140.  
  8141.  ──────────────────────────────────────────────────────────────────────────
  8142.  
  8143.  The multitasking nature of OS/2 makes OS/2 device drivers considerably more
  8144.  complex than MS-DOS device drivers. Furthermore, whenever you have devices,
  8145.  you must deal with device failures--the infamous hard errors. The handling
  8146.  of hard errors in a multitasking environment is likewise considerably more
  8147.  complex than it was under MS-DOS.
  8148.  
  8149.  
  8150.  17.1  Device Drivers
  8151.  
  8152.  This section gives an overview of device drivers, paying special attention
  8153.  to their key architectural elements. Writing a device driver is a complex
  8154.  task that must be undertaken with considerable care; a great many caveats
  8155.  and "gotchas" lie in wait for the unsuspecting programmer. Many of these
  8156.  "gotchas" are of that most favorite breed: ones that never show up in
  8157.  testing, only in the field. This section is by no means an exhaustive
  8158.  discussion of device drivers, nor is it a how-to guide. Study the OS/2
  8159.  device driver reference documentation carefully before setting out to write
  8160.  your own.
  8161.       In Chapter 2 I briefly discussed device independence and the role
  8162.  that device drivers play in bringing it about. I said that a device driver
  8163.  is a package of code that transforms I/O requests made in standard, device-
  8164.  independent fashion into the operations necessary to make a specific piece
  8165.  of hardware fulfill that request. A device driver takes data and status
  8166.  information from the hardware, in the hardware-specific format, and
  8167.  massages that information into the form that the operating system expects
  8168.  to receive.
  8169.       The device driver architecture has two key elements. First, each
  8170.  hardware device has its own device driver to hide the specific details of
  8171.  the device from the operating system. Second, device drivers are not hard-
  8172.  wired into the operating system when it is manufactured; they are
  8173.  dynamically installed at boot time. This second point is the interesting
  8174.  one. If all device drivers were hard-wired into OS/2, the technique of
  8175.  encapsulating device-dependent code into specific packages would be good
  8176.  engineering practice but of little interest to the user. OS/2 would run
  8177.  only on a system configured with a certain magic set of peripheral devices.
  8178.  But because device drivers are dynamically installable at boot time, OS/2
  8179.  can work with a variety of devices, even ones that didn't exist when OS/2
  8180.  was written, as long as a proper device driver for that device is installed
  8181.  at boot time. Note that device drivers can be installed only at boot time;
  8182.  they cannot be installed after the system has completed booting up. This is
  8183.  because in a future secure environment the ability to dynamically install a
  8184.  device driver would give any application the ability to violate system
  8185.  security.
  8186.       Saying that device drivers merely translate between the operating
  8187.  system and the device is a bit of oversimplification; in reality, they are
  8188.  responsible for encapsulating, or owning, nearly all device-specific
  8189.  knowledge about the device. Device drivers service the interrupts that
  8190.  their devices generate, and they work at task time (that is, at
  8191.  noninterrupt time). If a device monitor is necessary for a device, the
  8192.  device driver writer decides that and provides the necessary support. If an
  8193.  application needs direct access to a device's I/O ports or to its special
  8194.  mapped memory,1 the driver offers those services to processes. The device
  8195.  driver also knows whether multiple processes should simultaneously use the
  8196.  device and either allows or disallows this.
  8197.  
  8198.  
  8199.  17.1.1  Device Drivers and OS/2 Communication
  8200.  Because device drivers need to call and be called by OS/2 efficiently and
  8201.  because device drivers must handle hardware interrupts efficiently, they
  8202.  must run at ring 0. This means that device drivers must be trusted and must
  8203.  be trustworthy. A flaky device driver--or worse, a malicious one--can do
  8204.  unlimited and nearly untraceable damage to any application or data file in
  8205.  the system.
  8206.       OS/2 can easily call a device driver. Because OS/2 loaded the device
  8207.  driver into memory, it knows the address of its entry point and can call it
  8208.  directly. For the device driver to call OS/2 is trickier because the driver
  8209.  doesn't know the memory locations that OS/2 occupies nor does it have any
  8210.  control over the memory descriptor tables (LDT and GDT). When the device
  8211.  driver is initialized, OS/2 supplies the device driver with the address of
  8212.  the OS/2 DevHlp entry point. Device drivers call this address to access a
  8213.  variety of OS/2 services, called DevHlp services. The OS/2 DevHlp address
  8214.  references a GDT selector so that the DevHlp address is valid at all times--
  8215.  in protected mode, in real mode,2 at interrupt time, and during device
  8216.  driver initialization. Some DevHlp functions are only valid in certain
  8217.  modes, but the DevHlp facility is always available.
  8218.       Why don't device drivers simply use dynamic links to access OS/2
  8219.  services, the way that applications do? The OS/2 kernel dynlink interface
  8220.  is designed for processes running in user mode, at ring 3, to call the ring
  8221.  0 kernel. In other words, it's designed for outsiders to call in, but
  8222.  device drivers are already inside. They run at ring 0, in kernel mode, and
  8223.  at interrupt time. One, of course, could kludge things so that device
  8224.  drivers make dynlink calls, and then special code at those OS/2 entry
  8225.  points would recognize a device driver request and do all the special
  8226.  handling. But every system call from a normal application would be slowed
  8227.  by this extra code, and every service call from a device driver would
  8228.  likewise be slowed. As a result, device drivers have their own private,
  8229.  high-efficiency "backdoor" entry into OS/2. Figure 17-1 illustrates
  8230.  the call linkages between OS/2 and a device driver. OS/2 calls only one
  8231.  entry point in the device driver, providing a function code that the device
  8232.  driver uses to address a dispatch table. OS/2 learns the address of
  8233.  thisentry point when it loads the device driver. The device driver in turn
  8234.  calls only one OS/2 address, the DevHlp entry point. It also supplies a
  8235.  function code that is used to address a dispatch table. The device driver
  8236.  is told this address when it receives its initialize call from OS/2. Not
  8237.  shown is the device driver's interrupt entry point.
  8238.  
  8239.  
  8240.             OS/2                                   Device driver
  8241.  ┌────────────────────────┐ Far call (FCN)   ┌────────────────────────┐
  8242.  │                        ├─────────────┐    │  Function              │
  8243.  │               DevHlp   │             │    │   Table                │
  8244.  │         Function Table │             │    │  ┌──────┐  ┌──░░░░░░  │
  8245.  │  ░░░░░░──┐  ┌──────┐  │             └────┼─│      ├──┘           │
  8246.  │           └──┤      │─┼──────────┐       │  │      ├─────░░░░░░  │
  8247.  │  ░░░░░░─────┤      │  │ DevHlp   │       │  │      ├──┐           │
  8248.  │           ┌──┤      │  │ address  │       │  └──────┘  └──░░░░░░  │
  8249.  │  ░░░░░░──┘  └──────┘  │          │       │                        │
  8250.  │                        │          └───────┤                        │
  8251.  │                        │       Far call   │                        │
  8252.  └────────────────────────┘     (DevHlp FCN) └────────────────────────┘
  8253.  
  8254.  Figure 17-1.  Device driver call linkages.
  8255.  
  8256.  
  8257.  17.1.2  Device Driver Programming Model
  8258.  The programming model for device drivers under MS-DOS is simple. Device
  8259.  drivers are called to perform a function, and they return when that
  8260.  function is complete or they encounter an unrecoverable error. If the
  8261.  device is interrupt driven, the CPU hangs in a loop inside the device
  8262.  driver while waiting for the driver's interrupt handler to be entered; when
  8263.  the operation is complete, the interrupt handler sets a private flag to
  8264.  break the task-time CPU out of its wait loop.
  8265.       The OS/2 device driver model is considerably more complicated because
  8266.  OS/2 is a multitasking system. Even if the thread that calls the device
  8267.  driver with a request has nothing better to do than wait for the operation
  8268.  to complete, other threads in the system could make good use of the time.
  8269.  Another effect of the OS/2 multitasking architecture is that two or more
  8270.  threads can simultaneously call a device driver. To explore this last issue
  8271.  fully, we'll digress for a moment and discuss the OS/2 internal execution
  8272.  model.
  8273.       By design, OS/2 acts more like a subroutine library than like a
  8274.  process. The only dispatchable entities in the system are threads, and all
  8275.  threads belong to processes. When a process's thread calls OS/2, that
  8276.  thread executes OS/2's code.3 It's like walking up to the counter at a
  8277.  fast-food restaurant and placing your order. You then slip on an apron, run
  8278.  around behind the counter, and prepare your own order. When the food is
  8279.  ready, you take off the apron, run back around to the front of the counter,
  8280.  and pick up the food. The counter represents the boundary between ring 0
  8281.  (kernel mode) and ring 3 (application mode), and the apron represents the
  8282.  privileged state necessary to work behind the counter.
  8283.       Naturally, OS/2 is reentrant; at any one time many threads are
  8284.  executing inside OS/2, but each is doing work for only one process--the
  8285.  process to whom that thread belongs. Behind the counter are several folks
  8286.  wearing aprons, but each is working only on his or her own order. This
  8287.  approach simplifies the internals of OS/2: Each instance of a section of
  8288.  code is doing only one thing for one client. If a section of code must wait
  8289.  for something, it simply blocks (analogous to a semaphore wait) as long as
  8290.  it has to and resumes when it can. Threads within the kernel that are
  8291.  competing for a single resource do so by internal semaphores, and they are
  8292.  given access to these semaphores on a priority basis, just as they are when
  8293.  executing in application mode.
  8294.       OS/2 makes little distinction between a thread running inside the
  8295.  kernel and one running outside, in the application's code itself: The
  8296.  process's LDT remains valid, and the thread, while inside the kernel, can
  8297.  access any memory location that was accessible to the process in
  8298.  application mode, in addition to being able to access restricted ring 0
  8299.  memory. The only distinction the scheduler makes between threads inside and
  8300.  those outside the kernel is that the scheduler never preempts a thread
  8301.  running inside the kernel. This greatly relaxes the rigor with which kernel
  8302.  code needs to protect its critical sections: When the CPU is executing
  8303.  kernel code, the scheduler performs a context switch only when the CPU
  8304.  voluntarily blocks itself. As long as kernel code doesn't block itself,
  8305.  wait on a semaphore, or call a subroutine that waits on a semaphore, it
  8306.  needn't worry about any other thread entering its critical section.4
  8307.       When OS/2 calls a device driver at task time, it does so with the
  8308.  thread that was executing OS/2--the thread that belongs to the client
  8309.  process and that made the original service call. Thus, the task-time part
  8310.  of a device driver is running, at ring 0, in the client's context. The
  8311.  client's LDT is active, all the client's addresses are active, and the
  8312.  device driver is immune from being preempted by other task-time threads
  8313.  (but not by interrupt service) until it blocks via a DevHlp
  8314.  function or returns to OS/2.
  8315.       OS/2 device drivers are divided into two general categories: those for
  8316.  character mode devices and those for block mode devices. This terminology
  8317.  is traditional, but don't take it too literally because character mode
  8318.  operations can be done to block mode devices. The actual distinction is
  8319.  that character mode device drivers do I/O synchronously; that is, they do
  8320.  operations in first in, first out order. Block mode device drivers can be
  8321.  asynchronous; they can perform I/O requests in an order different from the
  8322.  one in which they received them. A traditional serial character device,
  8323.  such as a printer, must not change the order of its requests; doing so
  8324.  scrambles the output. A block device, such as a disk, can reverse the order
  8325.  of two sector reads without problems.
  8326.       Figure 17-2 shows an algorithm for character mode device drivers.5
  8327.  OS/2 calls the device driver with a request, as shown at the top of the
  8328.  figure. If the device driver is busy with another request, the new
  8329.  requesting thread should block on a RAM semaphore until the device is
  8330.  available. When the device is free, the task-time thread does the requested
  8331.  operation. Sometimes the work can be done at task time (such as an IOCTL
  8332.  call asking about the number of characters in an input buffer), but more
  8333.  frequently the task-time thread initiates the operation, and the work is
  8334.  completed at interrupt time. If the task-time thread needs to wait for
  8335.  interrupt service, it should block on a RAM semaphore6 that the interrupt-
  8336.  time code will clear. When the last associated device interrupt takes place
  8337.  and the operation is complete, the interrupt code releases the RAM
  8338.  semaphore. The task-time thread awakens and returns to OS/2 with the status
  8339.  bits properly set in the request block.
  8340.  
  8341.  
  8342.         OS/2 code            Device driver           Device interrupt
  8343.  ────────────────────── ─────────────────────── ───────────────────────────
  8344.  Issue request to
  8345.  device driver. ────── Block until device is
  8346.                         available.
  8347.  
  8348.                         Peform request. Block
  8349.                         until interrupts
  8350.                         complete if necessary.  ┌──────────────────────────
  8351.                                                 │ Device interrupt (if any)
  8352.                                                 ├──────────────────────────
  8353.                                                 │ Perform next step in I/O
  8354.                                                 │ operation.
  8355.                                                 │
  8356.                                                 │ When done, use DevHlp
  8357.                                                 │ ProcRun to unblock task
  8358.                                                 │ time thread.
  8359.                                                 ├──────────────────────────
  8360.                                                 │ End of interrupt
  8361.                         Complete request and    └──────────────────────────
  8362.                         return to OS/2
  8363.  Request now complete.
  8364.  Continue.
  8365.  
  8366.  Figure 17-2.  Simplified character mode device driver model.
  8367.  
  8368.  
  8369.       Figure 17-3 shows an algorithm for block devices. The general outline
  8370.  is the same as that for character devices but more complicated because of
  8371.  the asynchronous nature of random-access devices. Because requests can be
  8372.  processed in any order, most block device drivers maintain an internal work
  8373.  queue to which they add each new request. They usually use a special DevHlp
  8374.  function to sort the work queue in sector number order so that disk head
  8375.  motion is minimized.
  8376.  
  8377.  
  8378.         OS/2 code             Device driver           Device interrupt
  8379.  ─────────────────────── ─────────────────────── ───────────────────────────
  8380.  Issue request to
  8381.  device driver. ─────── Add request to device
  8382.                          request list. Fire up
  8383.                          device if not active.
  8384.  
  8385.                          Return to OS/2 with
  8386.                          DONE clear. ──┐
  8387.             ┌──────────────────────────┘         ┌──────────────────────────
  8388.                                                 │ Device interrupt
  8389.  OS/2 thread(s) block on                         ├──────────────────────────
  8390.  incomplete request when                         │ Perform next step in
  8391.  they can proceed no                             │ I/O operation.
  8392.  further without the                             │
  8393.  data.                                           │ If request is done
  8394.                                                  │    Pull request from
  8395.                                                  │    list
  8396.  Any threads blocked ────────────────────────────┤
  8397.  on this request are                             │    Use DevHlp DevDone
  8398.  awakened.                                       │    to tell OS/2 that
  8399.                                                  │    the I/O is done.
  8400.                                                  │
  8401.                                                  │    If further work on
  8402.                                                  │    queue, start work
  8403.                                                  │    on next item.
  8404.                                                  ├──────────────────────────
  8405.                                                  │ End of interrupt
  8406.  Request now complete.                           └──────────────────────────
  8407.  Continue.
  8408.  
  8409.  Figure 17-3.  Block mode device driver model.
  8410.  
  8411.  
  8412.       The easiest way to understand this figure is to think of a block mode
  8413.  device driver as being made up of N threads: one interrupt-time thread does
  8414.  the actual work, and all the others are task-time threads that queue the
  8415.  work. As each request comes into the driver via a task-time thread, that
  8416.  thread simply puts the request on the queue and returns to OS/2. Later, the
  8417.  device driver's interrupt service routine calls the DevHlp DevDone function
  8418.  to tell OS/2 that the operation is complete. Returning to OS/2 with the
  8419.  operation incomplete is permissible because the request block status bits
  8420.  show that the operation is incomplete.
  8421.       Sometimes, OS/2 needs to wait for the operation (such as a read from a
  8422.  directory); so when the device driver returns with the operation
  8423.  incomplete, OS/2 simply waits for it to finish. In other circumstances,
  8424.  such as flushing the cache buffers, OS/2 may not wait around for the
  8425.  operation to complete. It may go on about its business or even issue a new
  8426.  request to the driver, using, of course, a new request block because the
  8427.  old one is still in use. This design gives the system a great deal of
  8428.  parallelism and thereby improves throughput.
  8429.       I said that a block mode device driver consists of several task-time
  8430.  threads and one interrupt-time thread. The term interrupt-time thread is a
  8431.  bit misleading, however, because it's not a true thread managed by the
  8432.  scheduler but a pseudo thread created by the hardware interrupt mechanism.
  8433.  For example, a disk device driver has four requests queued up, and the READ
  8434.  operation for the first request is in progress. When it completes, the
  8435.  driver's interrupt service routine is entered by the hardware interrupt
  8436.  generated by the disk controller. That interrupt-time thread, executing the
  8437.  driver's interrupt service routine, checks the status, verifies that all is
  8438.  OK, and calls various DevHlp routines to post the request as complete and
  8439.  to remove it from the queue. It then notes that requests remain on the
  8440.  queue and starts work on the next one, which involves a seek operation. The
  8441.  driver's interrupt-time code issues the seek command to the hardware and
  8442.  then returns from the interrupt. When the disk stops seeking, another
  8443.  interrupt is generated; the interrupt-time code notes the successful seek,
  8444.  issues the read or write operation to the controller, and exits.
  8445.       As you can see, the repeated activation of the device driver's
  8446.  interrupt service routine is much like a thread, but with two major
  8447.  differences. First, every time an interrupt service routine is entered, it
  8448.  has a fresh stack. A task-time thread has register contents and a stack
  8449.  that are preserved by the system; neither is preserved for an interrupt
  8450.  service routine between interrupts. A task-time thread keeps track of what
  8451.  it was doing by its CS:IP address, its register contents, and its stack
  8452.  contents. An interrupt service routine must keep track of its work by means
  8453.  of static values stored in the device driver's data segment. Typically,
  8454.  interrupt service routines implement a state machine and maintain the
  8455.  current state in the driver's data segment. Second, a true thread remains
  8456.  in existence until explicitly terminated; an interrupt service thread is an
  8457.  illusion of a thread that is maintained by repeated interrupts. If any one
  8458.  execution of the interrupt service routine fails to give the hardware a
  8459.  command that will generate another interrupt, the interrupt pseudo thread
  8460.  will no longer exist after the interrupt service routine returns.
  8461.       The block mode driver algorithm description left out a detail. If the
  8462.  disk is idle when a new request comes in, the request is put on the queue,
  8463.  but there is no interrupt-time pseudo thread to service the request. Thus,
  8464.  both the task-time and interrupt-time parts of a device driver must be able
  8465.  to initiate an operation. The recommended approach is to use a software
  8466.  state machine to control the hardware and to ensure that the state machine,
  8467.  at least the start operation part of it, is callable at both task and
  8468.  interrupt time. The algorithm above is then modified so that after the
  8469.  task-time part of a block device driver puts its request on the driver's
  8470.  internal queue it verifies that the device (or state machine or interrupt
  8471.  pseudo thread) is active. If the device has been idle, the task-time thread
  8472.  in the device driver initiates the operation by calling the initial state
  8473.  of the state machine; it then returns to OS/2. This primes the pump;
  8474.  the interrupt pseudo thread now continues to run until the request queue is
  8475.  empty.
  8476.       Figure 17-4 shows an overview of the OS/2 device driver architecture.
  8477.  Each device driver consists of a task-time part, an interrupt-time part (if
  8478.  the device generates interrupts), and the start-operation code that is
  8479.  executed in either mode. The driver's data area typically contains state
  8480.  information, flags, and semaphores to handle communication between the
  8481.  task-time part and the interrupt. Figure 17-4 also shows that the task-time
  8482.  part of a device driver can have multiple instances. It can be called by
  8483.  several threads at the same time, just as a shared dynlink library routine
  8484.  might be. Unlike a dynlink library, a device driver has no instance data
  8485.  segment; the device driver's data segment is a global data segment,
  8486.  accessible to all execution instances of the device driver's task-time
  8487.  component. Just as a dynlink package uses semaphores to protect critical
  8488.  data areas in its global data segment, a device driver uses semaphores to
  8489.  protect the critical data values in its data segment. Unlike dynlink
  8490.  routines, the device driver has an additional special thread--the interrupt
  8491.  service thread. A device driver can't protect critical sections that are
  8492.  accessed at interrupt time by using semaphores because an interrupt service
  8493.  thread cannot block. It must complete the interrupt service and exit--
  8494.  quickly, at that. When you write device drivers, you must minimize the
  8495.  critical sections that are entered by the interrupt service thread and
  8496.  protect them via the CLI/STI instruction sequence.
  8497.  
  8498.  
  8499.           ┌─────────────────┐
  8500.           │              "D"│
  8501.        ┌──┴──────────────┐  │                ┌───────────────────┐
  8502.        │              "C"│  │                │   Device driver   │
  8503.     ┌──┴──────────────┐  ├──┘                │ interrupt service │
  8504.     │              "B"│  │                   └─┬───────────┬─────┘
  8505.  ┌──┴──────────────┐  ├──┘                     │           │
  8506.  │  Instance "A"   │  │                        │           │
  8507.  │  device driver  ├──┘                ┌──────────┐       │
  8508.  │  task-time code ├──────────────────│ Start I/O │       │
  8509.  └────────┬────────┘                   │   code    │       │
  8510.           │                            └─────┬─────┘       │
  8511.           │                                  │             │
  8512.           │                                  │             │
  8513.           │                                  │             │
  8514.           │                         ┌──────────────┐      │
  8515.           └────────────────────────│ Device driver │─────┘
  8516.                                     │     data      │
  8517.                                     └───────────────┘
  8518.  
  8519.  Figure 17-4.  Device driver code structure.
  8520.  
  8521.  
  8522.  17.1.3  Device Management
  8523.  Device drivers do more than talk to the device; they also manage it for the
  8524.  system. Device drivers are called each time a process opens or closes a
  8525.  device; device drivers determine whether a device can be used by more than
  8526.  one process simultaneously. Likewise, device drivers receive device monitor
  8527.  requests from applications via the IOCTL interface and, when appropriate,
  8528.  call OS/2 via the DevHlp interface to perform the bulk of the monitor work.
  8529.  Finally, device drivers can grant processes access to the device's I/O
  8530.  ports, to the device's mapped memory, and/or to special control areas in
  8531.  the device driver's data area itself. Once again, processes ask for these
  8532.  features via IOCTL; the device driver grants the requests via a DevHlp
  8533.  dialog with OS/2. Some device drivers are degenerate; they don't actually
  8534.  transfer data but exist solely to manage these other tasks. The screen
  8535.  device driver is an example. Screen data is always written directly to the
  8536.  display buffer by VIO, the application, or the presentation manager. The
  8537.  screen device driver exists to grant direct access, manage screen groups,
  8538.  and so on.
  8539.  
  8540.  
  8541.  17.1.4  Dual Mode
  8542.  The last key architectural feature of device drivers is that they are
  8543.  written in dual mode: The driver code, both task time and interrupt time,
  8544.  must be able to execute in protected mode and real mode. The process of
  8545.  mode switching between protected mode and real mode is quite slow--about
  8546.  800 microseconds. If we decreed that all device drivers run only in
  8547.  protected mode and that service interrupts run only in protected mode, a
  8548.  disk request from a real mode program might require six or more mode
  8549.  switches--one for the request, and five for the interrupts--for a penalty
  8550.  of almost 5 milliseconds. Consequently, device drivers must run in whatever
  8551.  mode the CPU is in when the request comes along or the interrupt arrives.
  8552.       At first glance, this seems easy enough: As long as the device driver
  8553.  refrains from computing its own segment selectors, it can execute in either
  8554.  mode. The catch is that OS/2 may switch between modes at every call and/or
  8555.  interrupt, and the addresses of code and data items are different in each
  8556.  mode. A device driver might be called in protected mode with an address in
  8557.  the client process's address space. When the "data ready" interrupt
  8558.  arrives, however, the CPU may be running in real mode, and that client's
  8559.  address is no longer valid--for two reasons. One, the segment selector part
  8560.  of a memory address has a different meaning in real mode than it does in
  8561.  protected mode; and, two, the client's selector was in the LDT, and the LDT
  8562.  is invalid at interrupt time.7 OS/2 helps device drivers deal with
  8563.  addressing in a dual mode environment in three ways:
  8564.  
  8565.       1.  Some addresses are the same in both modes and in either protected
  8566.           mode or real mode. The DevHlp entry point, the global infoseg
  8567.           address, the request packet address, and any addresses returned
  8568.           via the DevHlp GetDosVar function are valid at all times and in
  8569.           both modes.
  8570.  
  8571.       2.  Although the segment selector value for the device driver's code
  8572.           and data segments is different in each mode, OS/2 loads the proper
  8573.           values into CS and DS before it calls the device driver's task-
  8574.           time or interrupt-time entry points. As long as a device driver is
  8575.           careful not to "remember" and reuse these values, it won't notice
  8576.           that they (possibly) change at every call.
  8577.  
  8578.       3.  OS/2 provides a variety of DevHlp functions that allow a device
  8579.           driver to convert a selector:offset pair into a physical address
  8580.           and then later convert this physical address back into a
  8581.           selector:offset pair that is valid at that particular time. This
  8582.           allows device drivers to convert addresses that are outside their
  8583.           own segments into physical addresses and then, upon each task-time
  8584.           or interrupt-time call to the driver, convert that physical
  8585.           address back into one that is usable in the current mode. This
  8586.           avoids the problem of recording a selector:offset pair in protect
  8587.           mode and then trying to use it as a segment:offset pair in real
  8588.           mode.
  8589.  
  8590.  
  8591.  17.2  Hard Errors
  8592.  
  8593.  Sometimes the system encounters an error that it can neither ignore nor
  8594.  correct but which the user can correct. A classic example is the user
  8595.  leaving ajar the door to the floppy drive; the system can do nothing to
  8596.  access that floppy disk until someone closes the door. Such an error is
  8597.  called a hard error. The term originated to describe an error that won't go
  8598.  away when the operation is retried, but it also aptly describes the effort
  8599.  involved in the design of OS/2 to deal with such errors.
  8600.       The manner in which MS-DOS handles hard errors is straightforward. In
  8601.  our drive door example, MS-DOS discovers the problem when it is deep inside
  8602.  the bowels of the system, communicating with the disk driver. The driver
  8603.  reports the problem, and MS-DOS displays some text on the screen--the
  8604.  infamous "Abort, Retry, Ignore?" message. Typically, the user fixes the
  8605.  problem and replies; MS-DOS then takes the action specified by the user,
  8606.  finishes its work, and returns to the application. Often, applications
  8607.  didn't want the system to handle hard errors automatically. Perhaps they
  8608.  were concerned about data integrity and wanted to be aware of a disk
  8609.  writing problem, or they wanted to prevent the user from specifying
  8610.  "Ignore,"8 or they didn't want MS-DOS to write over their screen display
  8611.  without their knowing. To handle these situations, MS-DOS lets applications
  8612.  store the address of a hard error handler in the INT 24 vector; if a
  8613.  handler is present, MS-DOS calls it instead of its own handler.
  8614.       The system is in an unusual state while processing an MS-DOS hard
  8615.  error. The application originally calls MS-DOS via the INT 21 vector. MS-
  8616.  DOS then calls several levels deep within itself, whereupon an internal MS-
  8617.  DOS routine calls the hard error handler back in the application. Because
  8618.  MS-DOS is not generally reentrant, the application cannot recall MS-DOS via
  8619.  INT 21 at this point; doing so would mean that it has called MS-DOS twice
  8620.  at the same time. The application probably needs to do screen and keyboard
  8621.  I/O when handling the hard error, so MS-DOS was made partially reentrant.
  8622.  The original call involves disk I/O, so MS-DOS can be reentered via a
  8623.  screen/keyboard I/O call without problem.
  8624.       Several problems prevented us from adopting a similar scheme for OS/2.
  8625.  First, unlike the single-tasking MS-DOS, OS/2 cannot suspend operations
  8626.  while the operating system calls an application--a call that might not
  8627.  return for a long time. Second, major technical and security problems are
  8628.  involved with calling from ring 0 (the privileged kernel mode) to ring 3
  8629.  (the application mode). Also, in the MS-DOS environment, deciding which
  8630.  process was responsible for the operation that triggered the hard error is
  8631.  easy: Only one application is running. OS/2 may have a hard time
  8632.  determining which process to alert because more than one process may have
  8633.  caused a disk FAT sector or a disk directory to be edited. The improved
  8634.  buffering techniques employed by OS/2 may cause a hard error to occur at a
  8635.  time when no process is doing any I/O. Finally, even if we solve all these
  8636.  problems, the application that triggers the hard error may be running in a
  8637.  background screen group and be unable to display a message or use the
  8638.  keyboard. Even if the application is in the foreground screen group, it
  8639.  can't use the screen and keyboard if it's not the process currently
  8640.  controlling them.
  8641.  
  8642.  
  8643.  17.2.1  The Hard Error Daemon
  8644.  This last problem yields a clue to the solution. OS/2 supports multiple
  8645.  screen groups, and its screen group mechanism manages multiple simultaneous
  8646.  use of the screen and the keyboard, keeping the current users--one in each
  8647.  screen group--isolated from one another. Clearly, we need to use screen
  8648.  groups to allow a hard error dialog to be completed with the user without
  8649.  interfering with the current foreground application. Doing so solves the
  8650.  problem of writing on another application's screen image and therefore
  8651.  removes most of the need for notifying an application that a hard error has
  8652.  occurred.
  8653.       Specifically, OS/2 always has running a process called the hard error
  8654.  daemon. When a hard error occurs, OS/2 doesn't attempt to figure out which
  8655.  process caused it; instead, it notifies the hard error daemon. The hard
  8656.  error daemon performs a special form of screen group switch to the reserved
  8657.  hard error screen group and then displays its message and reads its input.
  8658.  Because the hard error daemon is the only process in this screen group,
  8659.  screen and keyboard usage do not conflict. The previous foreground process
  8660.  is now temporarily in the background; the screen group mechanism keeps it
  8661.  at bay.
  8662.       Meanwhile, the process thread that encountered the hard error in the
  8663.  kernel is blocked there, waiting for the hard error daemon to get a
  8664.  response from the user. The thread that handles the hard error is never the
  8665.  thread that caused the hard error, and the kernel is already fully
  8666.  reentrant for different threads; so the hard error daemon thread is free to
  8667.  call OS/2 at will. When the user corrects the problem and responds to the
  8668.  hard error daemon, the hard error daemon sends the response back to the
  8669.  kernel, which allows the thread that encountered the error to take the
  8670.  specified action. That thread either retries the operation or produces an
  8671.  error code; the hard error daemon returns the system to the original
  8672.  screen group. The screen group code then does its usual trick of
  8673.  restoring the screen image to its previous state. Figure 17-5 illustrates
  8674.  the hard error handling sequence. A process thread encounters a hard error
  8675.  while in the OS/2 kernel. The thread blocks at that point while the hard
  8676.  error daemon's previously captured thread is released. The hard error
  8677.  daemon performs a special modified screen switch at (1), displays its
  8678.  message, gets the user's response, restores the application screen group at
  8679.  (2), and reenters the OS/2 kernel. The response code is then passed to the
  8680.  blocked application thread, which then resumes execution.
  8681.  
  8682.  
  8683.               DosCall
  8684.     Process  ────┐                                        ┌────────
  8685.  ┌───────────────┼────────────────────────────────────────┼───────────┐
  8686.  │  OS/2 kernel  │                                        │  Return   │
  8687.  │               │ Hard                                   │  to       │
  8688.  │               │ error                                  │  process  │
  8689.  │               └──────┤                      ├───>─>────┘           │
  8690.  │                                                < <                 │
  8691.  │                      ├─┐                  ┌─┤                      │
  8692.  └────────────────────────┤──────────────────├────────────────────────┘
  8693.                                             
  8694.                   returns │                  │ calls
  8695.                           │                  │
  8696.     Hard error daemon     │ Display message  │
  8697.                           └──────────────────┘
  8698.                        (1)    Get response    (2)
  8699.  
  8700.                                      Time──────
  8701.  
  8702.  Figure 17-5.  Hard error handling.
  8703.  
  8704.  
  8705.       Although the most common cause of hard errors is a disk problem for
  8706.  example, an open drive door or a medium error--other events that require
  8707.  user intervention or user notification use the hard error mechanism. For
  8708.  example, the volume management package (see 15.2 Media Volume Management)
  8709.  uses the hard error mechanism to display its "Insert volume <name>"
  8710.  messages. As I mentioned earlier, MS-DOS applications running in the
  8711.  compatibility box can encounter problems, such as locked files, that they
  8712.  can't understand. Rather than have these applications fail mysteriously,
  8713.  OS/2 uses the hard error daemon mechanism to inform the user of the cause
  8714.  of the real mode application's difficulties. Although the application
  8715.  running in the compatibility box sees an operating system that acts like
  8716.  MS-DOS, the operating system is actually OS/2. Because of this, hard errors
  8717.  encountered by a real mode process are handled by an amalgam of the MS-DOS
  8718.  INT 24 mechanism and the OS/2 hard error daemon. See Chapter 19, The 3X
  8719.  Box.
  8720.  
  8721.  
  8722.  17.2.2  Application Hard Error Handling
  8723.  In some cases an application doesn't want the system to handle its hard
  8724.  errors. For example, an application designed for unattended or remote
  8725.  operation, such as a network server, may want to pass notification of hard
  8726.  errors to a remote correspondent rather than hanging up forever with a
  8727.  message on a screen that might not be read for hours. Another example is a
  8728.  database program concerned about the integrity of its master file; it may
  8729.  want to know about hard errors so that it can take some special action or
  8730.  perhaps use an alternative master file on another device. OS/2 allows a
  8731.  process to disable automatic hard error handling on a per file basis. Our
  8732.  network example will want to disable hard error pop-ups for anything the
  8733.  process does; our database example may want to disable hard error pop-ups
  8734.  only for its master file, keeping their convenience for any other files
  8735.  that it might access. When a hard error occurs on behalf of a process or a
  8736.  handle that has hard error pop-ups disabled, OS/2 assumes that a FAIL
  8737.  response was entered to a hypothetical hard error pop-up and returns to the
  8738.  application with a special error code. The application must analyze the
  8739.  code and take the necessary actions.
  8740.  
  8741.  
  8742.  
  8743.  18  I/O Privilege Mechanism and Debugging/Ptrace
  8744.  
  8745.  ───────────────────────────────────────────────────────────────────────────
  8746.  
  8747.  The earlier chapters of this book focused on the "captains and kings" of
  8748.  the operating system world, the major architectural features. But like any
  8749.  real world operating system, OS/2 contains a variety of miscellaneous
  8750.  facilities that have to be there to get the work done. Although these
  8751.  facilities may not be major elements in some architectural grand scheme,
  8752.  they still have to obey the principles of the design religion. Two of them
  8753.  are the I/O privilege mechanism and the debugging facility.
  8754.  
  8755.  
  8756.  18.1  I/O Privilege Mechanism
  8757.  
  8758.  Earlier I discussed the need for a mechanism that allows applications high-
  8759.  speed direct access to devices. But the mechanism must control access in
  8760.  such a way that the system's stability isn't jeopardized and in such a way
  8761.  that applications don't fight over device control. OS/2 meets this
  8762.  requirement with its I/O privilege mechanism. This facility allows a
  8763.  process to ask a device driver for direct access to the device's I/O
  8764.  ports and any dedicated or mapped memory locations it has. The I/O
  8765.  privilege mechanism can be used directly by an application, which
  8766.  necessarily makes it device dependent, or indirectly by a dynlink package.
  8767.  The dynlink package can act as a kind of device driver; a new version can
  8768.  be shipped with new hardware to maintain application compatibility. This
  8769.  pseudo device driver is normally much faster than a true device driver
  8770.  because of the customized procedural interface; not entering ring 0 and the
  8771.  OS/2 kernel code saves much time.
  8772.       Unfortunately, this isn't a free lunch. Dynlink pseudo device drivers
  8773.  can do everything that true device drivers can except handle interrupts.
  8774.  Because hardware interrupts must be handled at ring 0, the handler must be
  8775.  part of a true device driver. Frequently, a compromise is in order: Both a
  8776.  dynlink package and a true device driver are provided. The true device
  8777.  driver handles the interrupts, and the dynlink package does the rest of the
  8778.  work. The two typically communicate via shared memory and/or private
  8779.  IOCTLs. An example of such a compromise is the system KBD dynlink package.
  8780.  The system VIO package doesn't need a device driver to handle interrupts
  8781.  because the display device doesn't generate any.
  8782.       The two components in the OS/2 I/O access model are access to the
  8783.  device's memory and access to its I/O ports. Granting and controlling
  8784.  access to a device's mapped memory is easy because the 80286 protect mode
  8785.  supports powerful memory management facilities. First, a process asks the
  8786.  device driver for access to the device's memory, for example, to the memory
  8787.  buffer of a CGA board. Typically, a dynlink package, rather than an
  8788.  application, does this via the DosDevIOCtl call. If the device driver
  8789.  approves the request, it asks OS/2 via the DevHlp interface to set up an
  8790.  LDT memory descriptor to the proper physical memory locations. OS/2 returns
  8791.  the resultant selector to the device driver, which returns it to the
  8792.  calling process. This technique isn't limited to memory-mapped device
  8793.  memory; device drivers can use it to allow their companion dynlink packages
  8794.  direct access to a piece of the device driver's data segment. In this way,
  8795.  a combination dynlink/device driver device interface can optimize
  8796.  communication between the dynlink package and the device driver.
  8797.       Providing I/O port access to a process is more difficult because it is
  8798.  supported more modestly by the 80286 processor. The 80286 uses its ring
  8799.  protection mechanism to control I/O access; the system can grant code
  8800.  running at a certain ring privilege access to all I/O ports, but it can't
  8801.  grant access to only some I/O ports. It's too dangerous to grant an
  8802.  application access to all I/O ports simply because it uses VIO and VIO
  8803.  needs direct port access for the display adapter. This solution would mean
  8804.  that OS/2's I/O space is effectively unprotected because almost all
  8805.  programs use VIO or the presentation manager directly or indirectly.
  8806.       Instead, OS/2 was designed to allow, upon request from the device
  8807.  driver, any code segments marked1 to execute at ring 2 to have I/O access.
  8808.  The bad news is that access to all I/O ports must be granted
  8809.  indiscriminately, but the good news is that the system is vulnerable to
  8810.  program bugs only when those ring 2 segments are being executed. The
  8811.  capabilities of ring 2 code, as it's called, are restricted: Ring 2 code
  8812.  cannot issue dynlink calls to the system. This is partly a result of ring
  8813.  architecture (supporting ring 2 system calls would require significant
  8814.  additional overhead) and partly to discourage lazy programmers from
  8815.  flagging their entire process as ring 2 to avoid sequestering their I/O
  8816.  routines.
  8817.       As I said, in OS/2 version 1.0 the ring mechanism can restrict I/O
  8818.  access only to a limited degree. Any malicious program and some buggy
  8819.  programs can still damage system stability by manipulating the system's
  8820.  peripherals. Furthermore, a real mode application can issue any I/O
  8821.  instruction at any time. A future release of OS/2 that runs only on the
  8822.  80386 processor will solve these problems. The 80386 hardware is
  8823.  specifically designed to allow processes access to some I/O ports but not
  8824.  to others through a bit map the system maintains. This map, which of course
  8825.  the application cannot directly change, tells the 80386 which port
  8826.  addresses may be accessed and which must be refused. This map applies
  8827.  equally to protect mode and real mode applications.2 OS/2 will use the
  8828.  port addresses supplied by the device driver to allow access only to the
  8829.  I/O ports associated with the device(s) to which the process has been
  8830.  granted access. This release will not support application code segments
  8831.  running at ring 2; any segments so marked will be loaded and run at ring 3.
  8832.  The change will be invisible to all applications that use only the
  8833.  proper I/O ports. Applications that request access to one device and then
  8834.  use their I/O permissions to program another device will fail.
  8835.  
  8836.  
  8837.  18.2  Debugging/Ptrace
  8838.  
  8839.  Because OS/2 goes to a great deal of effort to keep one application from
  8840.  interfering with another, special facilities were built to allow debugging
  8841.  programs to manipulate and examine a debuggee (the process being debugged).
  8842.  Because a debugger is available for OS/2 and writing your own is laborious,
  8843.  we expect few programmers to write debuggers. This discussion is included,
  8844.  nevertheless, because it further illuminates the OS/2 architectural
  8845.  approach.
  8846.       The first concern of a debugger is that it be able to read and write
  8847.  the debuggee's code and data segments as well as intercept traps, signals,
  8848.  breakpoints, and the like. All these capabilities are strictly in the
  8849.  domain of OS/2, so OS/2 must "export" them to the debugger program. A
  8850.  second concern is system security: Obviously, the debug interface provides
  8851.  a golden opportunity for "cracker" programs to manipulate any other
  8852.  program, thereby circumventing passwords, encryption, or any other
  8853.  protection scheme. OS/2 prevents this by requiring that the debuggee
  8854.  process be flagged as a debug target when it is initially executed; a
  8855.  debugger can't latch onto an already-running process. Furthermore, when
  8856.  secure versions of OS/2 are available, processes executed under control of
  8857.  a debugger will be shorn of any permissions they might have that are in
  8858.  excess of those owned by the debugger.
  8859.       Before we examine the debugging interface, we should digress for a
  8860.  moment and discuss the OS/2 approach to forcing actions upon threads and
  8861.  processes. Earlier I described the process of kernel execution. I mentioned
  8862.  that when a process thread makes a kernel request that thread itself enters
  8863.  kernel mode and services its own request. This arrangement simplified the
  8864.  design of the kernel because a function is coded to perform one action for
  8865.  one client in a serial, synchronous fashion. Furthermore, nothing is ever
  8866.  forced on a thread that is in kernel mode; any action taken on a thread in
  8867.  kernel mode is taken by that thread itself. For example, if a process is to
  8868.  be killed and one of its threads is in kernel mode, OS/2 doesn't terminate
  8869.  that thread; it sets a flag that says, "Please kill yourself at your
  8870.  earliest convenience." Consequently, OS/2 doesn't need special code to
  8871.  enumerate and release any internal flags or resources that a killed kernel
  8872.  mode thread might leave orphaned, and in general no thread need
  8873.  "understand" the state of any other. The thread to be killed cleans itself
  8874.  up, releasing resources, flags, and whatever before it obligingly commits
  8875.  suicide.
  8876.       But when is the thread's "earliest convenience"? Thread termination is
  8877.  a forced event, and all threads check for any pending forced events
  8878.  immediately before they leave kernel mode and reenter application mode.
  8879.  This transition takes place frequently: not only when a system call returns
  8880.  to the calling application, but also each time a context switch takes
  8881.  place.
  8882.       Although it may appear that forced events might languish unprocessed,
  8883.  they are serviced rapidly. For example, when a process issues a DosKill
  8884.  function on its child process, each thread in the child process is marked
  8885.  "kill yourself." Because the parent process had the CPU, obviously, when it
  8886.  issued the DosKill, each of the child's threads is in kernel mode, either
  8887.  because the thread is working on a system call or because it was
  8888.  artificially placed in kernel mode when the scheduler preempted it. Before
  8889.  any of those now-marked threads can execute even a single instruction of
  8890.  the child application's code, they must go through OS/2's dispatch routine.
  8891.  The "kill yourself" flag is noted, and the thread terminates itself instead
  8892.  of returning to application mode. As you can see, the final effect of this
  8893.  approach is far from slow: The DosKill takes effect immediately--not one
  8894.  more instruction of the child process is executed.3 The only significant
  8895.  delay in recognizing a forced event occurs when a system call takes a long
  8896.  time to process. OS/2 is not very CPU bound, so any call that takes a "long
  8897.  time" (1 second or more) must be blocked for most of that time.
  8898.       When a kernel thread issues a block call for an event that might take
  8899.  a long time--such as waiting for a keystroke or waiting for a semaphore to
  8900.  clear--it uses a special form of block called an interruptible block. When
  8901.  OS/2 posts a force flag against a thread, it checks to see if that thread
  8902.  is blocking interruptibly. If it is, that thread is released from its block
  8903.  with a special code that says, "You were awakened not because the event has
  8904.  come to pass but because a forced event was posted." That thread must then
  8905.  finish the system call quickly (generally by declaring an error) so that
  8906.  the thread can go through the dispatch routine and recognize the force
  8907.  flag. I described this mechanism in Chapter 12 when I talked about another
  8908.  kind of forced event--the OS/2 signal mechanism. An incoming signal is a
  8909.  forced event for a process's thread 1; it therefore receives the same
  8910.  timely response and has the same effect of aborting a slow system call.
  8911.       I've gone through this long discussion of forced events and how
  8912.  they're processed because the internal debugging facility is based on one
  8913.  giant special forced event. When a process is placed in debug state, a
  8914.  trace force flag is permanently set for the initial thread of that process
  8915.  and for any other threads it creates. When any of those threads are in
  8916.  kernel mode--and they enter kernel mode whenever anything of interest takes
  8917.  place--they execute the debuggee half of the OS/2 trace code. The debugger
  8918.  half is executed by a debugger thread that issues special DosPtrace calls;
  8919.  the two halves of the package communicate through a shared memory area
  8920.  built into OS/2.
  8921.       When the debuggee encounters a special event (for example, a Ctrl-C
  8922.  signal or a GP fault), the trace force event takes precedence over any
  8923.  other, and the debuggee's thread executes the debuggee half of the
  8924.  DosPtrace code. This code writes a record describing the event into a
  8925.  communications buffer, wakes up the debugger thread, which is typically
  8926.  blocked in the debugger's part of the DosPtrace code, and blocks, awaiting
  8927.  a reply. The debugger's thread wakes up and returns to the debugger with
  8928.  the event information. When the debugger recalls DosPtrace with a command,
  8929.  the command is written into the communications area, and the debuggee is
  8930.  awakened to read and obey. The command might be "Resume normal execution,"
  8931.  "Process the event as you normally would," or "Give me the contents of
  8932.  these locations in your address space," whereupon the debuggee thread
  8933.  replies and remains in the DosPtrace handler.
  8934.       This approach is simple to implement, does the job well, and takes
  8935.  advantage of existing OS/2 features. For example, no special code is needed
  8936.  to allow the debugger access to the debuggee's address space because the
  8937.  debuggee itself, unwittingly in the DosPtrace code, reads and writes its
  8938.  own address space. Credit goes to the UNIX ptrace facility, upon which this
  8939.  facility was closely modeled.
  8940.       Finally, here are a few incidental facts that the readers of this
  8941.  book, being likely users of debugging facilities, should know. OS/2
  8942.  maintains a linkage between the debugger process and the debuggee process.
  8943.  When the debugger process terminates, the debuggee process also terminates
  8944.  if it has not already done so. The debuggee program need not be a direct
  8945.  child of the debugger; when the debugger process makes its initial
  8946.  DosPtrace call, OS/2 connects it to the last process that was executed with
  8947.  the special tracing option. If a process is executed with the tracing
  8948.  option but no debugger process subsequently issues a DosPtrace function,
  8949.  the jilted debuggee process is terminated in about two minutes.
  8950.  
  8951.  
  8952.  
  8953.  19  The 3x Box
  8954.  
  8955.  ───────────────────────────────────────────────────────────────────────────
  8956.  
  8957.  It's of critical importance that OS/2 do a good job of running existing MS-
  8958.  DOS applications, but as we've discussed, this is a difficult task. To
  8959.  offer the official MS-DOS interfaces under OS/2 and therefore claim upward
  8960.  compatibility would be easy; unfortunately, few popular applications would
  8961.  run successfully in such an environment. Most sophisticated applications
  8962.  take direct control of the machine environment and use MS-DOS for tasks the
  8963.  application doesn't want to bother with, such as file I/O, keyboard
  8964.  buffering, and so forth. If we're to run existing applications
  8965.  successfully, we must provide a close facsimile to a real mode PC running
  8966.  MS-DOS in all respects, not just the INT 21 program interface.
  8967.       OS/2 provides such a highly compatible environment, called the real
  8968.  mode screen group, the compatibility box, or simply the 3x box. The 3x box
  8969.  is an environment that emulates an 8086-based PC running MS-DOS version
  8970.  3.3.1 MS-DOS programs execute in real mode, and because emulating real
  8971.  mode from within protected mode is prohibitively slow, OS/2 physically
  8972.  switches into real mode to execute MS-DOS applications. Because MS-DOS
  8973.  programs are well aware of the MS-DOS memory layout, this layout is
  8974.  replicated for the OS/2 3x box. The first N bytes (typically 640 KB) are
  8975.  reserved for the exclusive use of the low-memory parts of OS/2 and the 3x
  8976.  box; protected mode applications never use any of this memory. Thus,
  8977.  programs that are careless about memory allocation or that make single-
  8978.  tasking assumptions about the availability of memory can run in a
  8979.  multitasking environment. Figure 19-1 illustrates the OS/2 memory layout.
  8980.  The low bytes of memory are reserved for the device drivers and portions of
  8981.  OS/2 that must run in real mode. The remainder of the space, up to the
  8982.  RMSIZE value, is dedicated to the 3x box. Memory from 640 KB to 1 MB is
  8983.  reserved for ROMs and video display buffers. Memory above 1 MB holds the
  8984.  remainder of OS/2 and all protect mode applications. Nonswappable, fixed
  8985.  segments are kept at one end of this memory to reduce fragmentation.
  8986.  
  8987.  
  8988.        N ┌────────────────┐
  8989.          │    Protect     │
  8990.          │      mode      │
  8991.          │  applications  │
  8992.          │   (movable)    │
  8993.          <                <
  8994.           >                >
  8995.          <                <
  8996.          ├────────────────┤
  8997.          │     Fixed      │
  8998.          ├────────────────┤
  8999.          │      OS/2      │
  9000.     1 MB └────────────────┘
  9001.  
  9002.   640 KB ┌────────────────┐
  9003.          │      Real      │
  9004.          │      mode      │
  9005.          │  application   │
  9006.          │                │
  9007.          │                │
  9008.          ├────────────────┤
  9009.          │   Additional   │
  9010.          │ device drivers │
  9011.    ~100k ├────────────────┤
  9012.          │                │
  9013.          │    Low OS/2    │
  9014.          │                │
  9015.     90:0 ├────────────────┤
  9016.          │    Bios ROM    │
  9017.        0 └────────────────┘
  9018.  
  9019.  Figure 19-1.  System memory layout.
  9020.  
  9021.  
  9022.       OS/2 uses the screen group mechanism to provide a user interface to
  9023.  the 3x box. One screen group is designated the real mode screen group;
  9024.  automatically, OS/2 executes COMMAND.COM in that screen group when it is
  9025.  first selected. The user accesses the real mode environment by selecting
  9026.  that screen group and returns to the protected mode environment by
  9027.  selecting another screen group. OS/2 version 1.0 supports a single real
  9028.  mode screen group because the real mode compatibility is provided by
  9029.  actually running the application in real mode. Thus, only one 640 KB area
  9030.  is reserved for all real mode applications, and adjudicating between the
  9031.  conflicting hardware manipulations of multiple real mode applications
  9032.  without any assistance from the 80286 microprocessor hardware would be
  9033.  prohibitively difficult. The 80386 microprocessor, however, provides a
  9034.  special hardware facility called virtual 8086 mode that will allow a future
  9035.  release of OS/2 to support multiple real mode screen groups, but only on an
  9036.  80386-based machine.
  9037.       The operating system that services the 3x application's INT 21
  9038.  requests is not an exact copy of MS-DOS; it's actually a low-memory
  9039.  extension of OS/2 itself. Because OS/2 is derived from MS-DOS, OS/2
  9040.  executes MS-DOS functions in a manner identical to that of the real MS-DOS.
  9041.  OS/2 supports the non-MS-DOS functions mentioned above by staying out of
  9042.  the way as much as possible and letting the 3x application "party hearty"
  9043.  with the hardware. For example, hooking most interrupt vectors is
  9044.  supported, as is hooking INT 21 and the ROM BIOS INT vectors. The ROM BIOS
  9045.  calls themselves are fully supported. Frequently, staying out of the way is
  9046.  not as easy as it may sound. For example, OS/2 must intercept and monitor
  9047.  real mode calls made to the disk driver part of the ROM BIOS so that it can
  9048.  prevent conflict with ongoing, asynchronous protect-mode disk I/O. OS/2 may
  9049.  find it necessary to momentarily block a real mode application's BIOS call
  9050.  until the protect mode device driver can release the hardware. Once the
  9051.  real mode application is in the BIOS, the same interlock mechanism prevents
  9052.  the protect mode device driver from entering the disk I/O critical
  9053.  section.
  9054.       Hard errors encountered by the real mode application are handled by a
  9055.  hybrid of the OS/2 hard error daemon and the 3x box INT 24 mechanism in a
  9056.  three-step process, as follows:
  9057.  
  9058.       1:  Hard error codes caused by events unique to the OS/2 environment--
  9059.           such as a volume manager media change request--activate the hard
  9060.           error daemon so that the user can get an accurate explanation
  9061.           of the problem. The user's response to the hard error is saved
  9062.           but is not yet acted upon. Hard error codes, which are also
  9063.           present in MS-DOS version 3.3, skip this step and start at
  9064.           step 2.
  9065.  
  9066.       2:  If the real mode application has installed its own hard error
  9067.           handler via the INT 24 vector, it is called. If step 1 was
  9068.           skipped, the code should be known to the application, and it is
  9069.           presented unchanged. If step 1 was taken, the error code is
  9070.           transformed to ERROR_I24_GEN_FAILURE for this step. The response
  9071.           returned by the program, if valid for this class of hard error, is
  9072.           acted upon. This means that hard errors new to OS/2 can actually
  9073.           generate two pop-ups--one from the hard error daemon with an
  9074.           accurate message and one from the application itself with a
  9075.           General Failure message. This allows the user to understand the
  9076.           true cause of the hard error and yet notifies the application that
  9077.           a hard error has occurred. In such a case, the action specified by
  9078.           the application when it returned from its own hard error handler
  9079.           is the one taken, not the action specified by the user to the
  9080.           initial hard error daemon pop-up.
  9081.  
  9082.  3:       If the real mode application has not registered its hard error
  9083.           handler via the INT 24 mechanism, OS/2 provides a default handler
  9084.           that uses the hard error daemon. If step 1 was taken and the hard
  9085.           error daemon has already run, it is not run again; OS/2 takes the
  9086.           action specified in response to the hard error pop-up that was
  9087.           displayed. If step 1 was not taken because the hard error code is
  9088.           MS-DOS 3.x compatible and if step 2 was not taken because the
  9089.           application did not provide its own handler, then OS/2 activates
  9090.           the hard error daemon in step 3 to present the message and receive
  9091.           a reply.
  9092.  
  9093.       The 3x box supports only MS-DOS functionality; no new OS/2 features
  9094.  are available to 3x box applications--no new API, no multiple threads, no
  9095.  IPC, no semaphores, and so on.2 This decision was made for two reasons.
  9096.  First, although any real mode application can damage the system's
  9097.  stability, allowing real mode applications to access some protect mode
  9098.  features may aggravate the problem. For example, terminate and stay
  9099.  resident programs may manipulate the CPU in such a way as to make it
  9100.  impossible for a real mode application to protect a critical section with
  9101.  semaphores and yet guarantee that it won't leave the semaphore orphaned.
  9102.  Second, because OS/2 has only one real mode box and it labors under a 640
  9103.  KB memory ceiling, it doesn't make sense to develop new real mode
  9104.  applications that use new OS/2 functions and thus require OS/2.
  9105.       The 3x box emulation extends to interrupts. OS/2 continues to context
  9106.  switch the CPU when the 3x box is active; that is, the 3x box application
  9107.  is the foreground application. Because the foreground process receives a
  9108.  favorable priority, its CPU is preempted only when a time-critical protect
  9109.  mode application needs to run or when the real mode application blocks. If
  9110.  the CPU is running a protect mode application when a device interrupt comes
  9111.  in, OS/2 switches to real mode so that a real mode application that is
  9112.  hooking the interrupt vectors can receive the interrupt in real mode. When
  9113.  the interrupt is complete, OS/2 switches back to protected mode and resumes
  9114.  the protected application.
  9115.       Although protected mode applications can continue to run when the 3x
  9116.  box is in the foreground, the reverse is not true. When the 3x box screen
  9117.  group is in the background, all 3x box execution is suspended, including
  9118.  interrupts. Unlike protected mode applications, real mode applications
  9119.  cannot be trusted to refrain from manipulating the screen hardware when
  9120.  they are in a background screen group. Normally, a real mode application
  9121.  doesn't notice its suspension when it's in background mode; the only thing
  9122.  it might notice is that the system time-of-day has apparently "jumped
  9123.  forward." Because mode switching is a slow process and leaves interrupts
  9124.  disabled for almost 1 millisecond, mode switching can cause interrupt
  9125.  overruns on fast devices such as serial ports. The best way to deal with
  9126.  this is to switch the real mode application into a background screen group;
  9127.  with no more real mode programs to execute, OS/2 does no further mode
  9128.  switching.
  9129.       Some OS/2 utility programs such as FIND are packaged as Family API
  9130.  applications. A single binary can run in both protected mode and real mode,
  9131.  and the user is saved the inconvenience of switching from real mode to
  9132.  protected mode to do simple utility functions. This works well for simple
  9133.  utility programs without full screen or graphical interfaces and for
  9134.  programs that have modest memory demands and that in other ways have little
  9135.  need of OS/2's extended capabilities. Obviously, if an application can make
  9136.  good use of OS/2's protect mode features, it should be written to be
  9137.  protect mode only so that it can take advantage of those features.
  9138.  
  9139.  
  9140.  
  9141.  20  Family API
  9142.  
  9143.  ───────────────────────────────────────────────────────────────────────────
  9144.  
  9145.  When a new release of a PC operating system is announced, application
  9146.  writers face a decision: Should they write a new application to use some of
  9147.  the new features or should they use only the features in earlier releases?
  9148.  If they go for the sexy new features, their product might do more, be
  9149.  easier to write, or be more efficient; but when the program hits the
  9150.  market, only 10 percent of existing PCs may be running the new release. Not
  9151.  all of the existing machines have the proper processor to be able to run
  9152.  the new system, and, of those, many of their users haven't seen the need to
  9153.  go to the expense and endure the hassle of upgrading their operating
  9154.  system. If it's viable to write the new application so that it requires
  9155.  only the old operating system (and therefore runs in compatibility mode
  9156.  under the new operating system), then it's tempting to do so. Even though
  9157.  the product is not as good as it might be, it can sell to 100 percent of
  9158.  the installed base of machines--10 times as many as it would if it required
  9159.  the new operating system.
  9160.       And here you have the classic "catch-22" of software standards: If
  9161.  users don't see a need, they won't use the new system. If they don't use
  9162.  the new system, applications will not be written explicitly for it; so the
  9163.  users never see a need. Without some way to prime the pump, it will be a
  9164.  long time before a comprehensive set of applications are available that use
  9165.  the new system's features.
  9166.       OS/2 tackles this problem in several ways. The software bundled with
  9167.  OS/2 runs in protected mode, and OS/2 attempts to include as much
  9168.  additional user function as possible to increase its value to a user who
  9169.  initially owns no protected mode applications. The most important user
  9170.  acceptance feature of OS/2, however, is called Family API. Family API is a
  9171.  special subset of the OS/2 protected mode API. Using special tools included
  9172.  in the OS/2 developer's kit, you can build applications that use only the
  9173.  Family API. The resultant .EXE file(s) run unchanged in OS/2 protect mode
  9174.  or on an 8086 running MS-DOS 2.x or 3.x.1 Thus, developers don't have to
  9175.  choose between writing applications that are OS/2 protect mode and writing
  9176.  applications that are MS-DOS compatible; they can use the Family API
  9177.  mechanism and do both. Your applications will run as protected mode
  9178.  applications under OS/2 and as MS-DOS applications under a true MS-DOS
  9179.  system.
  9180.       Clearly, the Family API is a noteworthy feature. It offers some OS/2
  9181.  functions, together with the dynamic link system interface, to programs
  9182.  that run under MS-DOS without a copy of OS/2 anywhere in sight. It does
  9183.  this by providing an OS/2 compatibility library that accepts the OS/2
  9184.  system interface calls and implements them itself, calling the underlying
  9185.  MS-DOS system via INT 21 as necessary. This information should give you a
  9186.  big head start in figuring out which OS/2 functions are included in the
  9187.  Family API: Clearly all functions that have similar INT 21 functions--such
  9188.  as DosOpen, DosRead, and DosAllocSeg--are supported. Also present are
  9189.  functions, such as DosSubAlloc, that can be supported directly by the
  9190.  special Family API library. Features that are extremely difficult to
  9191.  support in a true MS-DOS environment, such as multiple threads and
  9192.  asynchronous I/O, are not present in the Family API.
  9193.       Where does this library come from? And how does it get loaded by MS-
  9194.  DOS to satisfy the OS/2 executable's dynlink requests? It's all done with
  9195.  mirrors, as the expression goes, and the "mirrors" must be built into the
  9196.  application's .EXE file because that file is all that's present when a
  9197.  Family API application is executed under MS-DOS. Figure 20-1 shows the
  9198.  layout of a Family API .EXE file.
  9199.  
  9200.  
  9201.           Family API
  9202.              .EXE
  9203.       ┌─────────────────┐
  9204.       │░░░░░░░░░░░░░░░░░│
  9205.       │░░░MS-DOS 3.x░░░░│
  9206.       │░░░.EXE header░──┼──┐
  9207.       ├─────────────────┤  │
  9208.       │░░░░░░░░░░░░░░░░░│  │
  9209.       │░░░Family API░░░░│  │ Shaded area is
  9210.       │░░░░░loader░░░░░░│  │ read by MS-DOS as
  9211.       ├─────────────────┤  │ a real mode
  9212.       │░░░░░░░░░░░░░░░░░│  │ application
  9213.       │░░░Family API░░░░│  │
  9214.       │░░░░░library░░░░░│  │
  9215.       ├─────────────────┤─┘
  9216.       │                 │
  9217.       │      OS/2       │
  9218.       │   .EXE header   │
  9219.       ├─────────────────┤
  9220.       │      OS/2       │
  9221.       │   application   │
  9222.       │    segments     │
  9223.       ├ ─ ─ ─ ─ ─ ─ ─ ─ ┤
  9224.       │                 │
  9225.       │                 │
  9226.       ├ ─ ─ ─ ─ ─ ─ ─ ─ ┤
  9227.       │                 │
  9228.       │                 │
  9229.       ├ ─ ─ ─ ─ ─ ─ ─ ─ ┤
  9230.       │                 │
  9231.       │                 │
  9232.       ├ ─ ─ ─ ─ ─ ─ ─ ─ ┤
  9233.       │                 │
  9234.       │                 │
  9235.       ├─────────────────┤
  9236.       │                 │
  9237.       │     Dynlink     │
  9238.       │      names      │
  9239.       └─────────────────┘
  9240.  
  9241.  Figure 20-1.  Family API executable (.EXE) format.
  9242.  
  9243.  
  9244.       OS/2 needed to define a new .EXE file because the existing MS-DOS .EXE
  9245.  file format contained too little information for the OS/2 protect mode
  9246.  segmented environment. Because, as we've discussed, the 8086 memory
  9247.  architecture is--despite the terminology normally used--a linear memory
  9248.  architecture, the MS-DOS .EXE format described only a single hunk of memory
  9249.  that was to be loaded contiguously. OS/2 needs each segment described
  9250.  separately, with information on its status: read only, code or data, demand
  9251.  load or preload, and so on. Naturally, OS/2 also needs a .EXE format with
  9252.  special records to describe loadtime dynamic links. This new .EXE format
  9253.  was defined so that its initial bytes look exactly like those of the old
  9254.  MS-DOS .EXE file header. A special flag bit is set in this fake .EXE
  9255.   header that is ignored by all releases of MS-DOS but that OS/2 recognizes
  9256.  to mean "It's not true. I'm really an OS/2 .EXE file. Seek to this location
  9257.  to find the true, new-style .EXE header."
  9258.       When an MS-DOS system is told to load this .EXE file, it sees and
  9259.  believes the old .EXE file header. This header does not describe the
  9260.  application itself but a body of special code built into the .EXE file
  9261.  before the actual application's code: the Family API loader and library. In
  9262.  other words, to MS-DOS this .EXE file looks like a valid, executable
  9263.  program, and that program is the Family API loader and library. The Family
  9264.  API loader and library are loaded into memory, and execution begins. MS-DOS
  9265.  doesn't load in the body of the application itself because it wasn't
  9266.  described as part of the load image in the special MS-DOS .EXE file header.
  9267.  As soon as it starts to execute, the Family API loader begins reading in
  9268.  the application's segments, performs a loader's general relocation chores,
  9269.  and fixes up dynlink references to the proper entry points in the Family
  9270.  API library package. When the application is loaded, the Family API loader
  9271.  block moves the application to its final execution address, which overlays
  9272.  most of the Family API loader to reclaim that space, and execution
  9273.  begins.
  9274.       All OS/2 .EXE files have this fake MS-DOS .EXE format header. In non-
  9275.  Family API executables, the Family API loader and library are missing, and
  9276.  by default the header describes an impossibly big MS-DOS executable. Should
  9277.  the application be accidentally run under a non-OS/2 system or in the OS/2
  9278.  compatibility screen group, MS-DOS will refuse to load the program.
  9279.  Optionally, the programmer can link in a small stub program that goes where
  9280.  the Family API loader would and that prints a more meaningful error
  9281.  message. As we said earlier, the old-style .EXE headers on the front of the
  9282.  file contain a flag bit to alert OS/2 to the presence of a new-style .EXE
  9283.  header further into the file. Because this header doesn't describe the
  9284.  Family API loader and library parts of the file, OS/2 ignores their
  9285.  presence when it loads a Family API application in protected mode; the
  9286.  application's dynlink references are fixed up to the normal dynlink
  9287.  libraries, and the Family API versions of those libraries are ignored.
  9288.       There Ain't No Such Thing As A Free Lunch, and the same unfortunately
  9289.  applies to the Family API mechanism. First, although the Family API allows
  9290.  dynlink calls to be used in an MS-DOS environment, this is not true
  9291.  dynlinking; it's quasi dynlinking. Obviously, runtime dynlinking is not
  9292.  supported, but even loadtime dynlinking is special because the dynlink
  9293.  target library is bound into the .EXE file. One of the advantages of
  9294.  dynlinks is that the target code is not part of the .EXE file and can
  9295.  therefore be changed and upgraded without changing the .EXE file. This is
  9296.  not true of the dynlink emulation library used by the Family API because
  9297.  it is built into the .EXE file. Fortunately, this disadvantage isn't
  9298.  normally a problem. Dynlink libraries are updated either to improve
  9299.  their implementation or to add new features. The Family API library can't
  9300.  be improved very much because its environment--MS-DOS--is limited and
  9301.  unchanging. If new Family API features were added, loading that new library
  9302.  with preexisting Family API .EXE files would make no sense; those programs
  9303.  wouldn't be calling the new features.
  9304.       A more significant drawback is the size and speed hit that the Family
  9305.  API introduces. Clearly, the size of a Family API .EXE file is extended by
  9306.  the size of the Family API loader and the support library. The tools used
  9307.  to build Family API executables include only those library routines used by
  9308.  the program, but even so the library and the loader add up to a nontrivial
  9309.  amount of memory--typically 10 KB to 14 KB in the .EXE file and perhaps
  9310.  9KB (the loader is not included) in RAM. Finally, loading a Family API
  9311.  application under MS-DOS is slower than loading a true MS-DOS .EXE file.
  9312.  Comparing loadtime against the loadtime of MS-DOS is tough for any
  9313.  operating system because loading faster than MS-DOS is difficult. The .EXE
  9314.  file consists of a single lump of contiguous data that can be read into
  9315.  memory in a single disk read operation. A relocation table must also be
  9316.  read, but it's typically very small. It's hard for any system to be faster
  9317.  than this. Clearly, loading a Family API application is slower because the
  9318.  loader and library must be loaded, and then they must, a segment at a time,
  9319.  bring in the body of the application.
  9320.       Although the Family API makes dual environment applications possible,
  9321.  it can't totally hide from an application the difference between the MS-DOS
  9322.  3.x and the OS/2 execution environment. For example, the Family API
  9323.  supports only the DosFindFirst function for a single search handle at a
  9324.  time. An application that wants to perform multiple directory searches
  9325.  simultaneously should use DosGetMachineMode to determine its environment
  9326.  and then use the unrestricted DosFindFirst function if running in protect
  9327.  mode or use the INT 21 functions if running in real mode. Likewise, an
  9328.  application that wants to manipulate printer data needs to contain version-
  9329.  specific code to hook INT 17 or to use device monitors, depending on the
  9330.  environment.
  9331.  
  9332.  
  9333.  
  9334.  ───────────────────────────────────────────────────────────────────────────
  9335.  
  9336.  
  9337.  
  9338.  Part III  The Future
  9339.  
  9340.  
  9341.  
  9342.  ───────────────────────────────────────────────────────────────────────────
  9343.  
  9344.  
  9345.  
  9346.  21  The Future
  9347.  
  9348.  ───────────────────────────────────────────────────────────────────────────
  9349.  
  9350.  This chapter is difficult to write because describing the second version of
  9351.  OS/2 becomes uninteresting when that version is released. Furthermore,
  9352.  preannouncing products is bad practice; the trade-offs between schedule and
  9353.  customer demand can accelerate the inclusion of some features and postpone
  9354.  others, often late in the development cycle. If we talk explicitly about
  9355.  future features, developers may plan their work around the availability of
  9356.  those features and be left high and dry if said features are postponed. As
  9357.  a result, this chapter is necessarily vague about both the functional
  9358.  details and the release schedule, discussing future goals for features
  9359.  rather than the features themselves. Design your application, not so that
  9360.  it depends on the features described here, but so that it is compatible
  9361.  with them.
  9362.       OS/2 version 1.0 is the first standard MS-DOS-compatible operating
  9363.  system that unlocks the memory-addressing potential of the 80286--a "train"
  9364.  that will "pull" a great many APIs into the standard. On the other hand,
  9365.  foreseeable future releases cannot expect such penetration, so the
  9366.  designers of OS/2 version 1.0 focused primarily on including a full set of
  9367.  APIs. Major performance improvements were postponed for future releases
  9368.  simply because such improvements can be easily added later, whereas new
  9369.  APIs cannot. Most of the planned work is to take further advantage of
  9370.  existing interfaces, not to create new ones.
  9371.  
  9372.  
  9373.  21.1  File System
  9374.  
  9375.  Clearly, the heart of the office automation environment is data--lots of
  9376.  data--searching for it, reading it, and, less frequently, writing it. A
  9377.  machine's raw number-crunching capacity is relatively uninteresting in this
  9378.  milieu; the important issue is how fast the machine can get the data and
  9379.  manipulate it. Certainly, raw CPU power is advantageous; it allows the use
  9380.  of a relatively compute bound graphical user interface, for example. But
  9381.  I/O performance is becoming the limiting factor, especially in a
  9382.  multitasking environment. Where does this data come from? If it's from the
  9383.  keyboard, no problem; human typing speeds are glacially slow to a computer.
  9384.  If the data is from a non-mass-storage device, OS/2's direct device access
  9385.  facilities should provide sufficient throughput. That leaves the file
  9386.  system for local disks and the network for remote data. The file system is
  9387.  a natural for a future release upgrade. Its interface is generic so that
  9388.  applications written for the first release will work compatibly with new
  9389.  file systems in subsequent releases.
  9390.       Talking about pending file system improvements is relatively easy
  9391.  because the weaknesses in the current FAT file system are obvious.
  9392.  
  9393.       ■  Large Disk Support
  9394.          Clearly, a new file system will support arbitrarily large
  9395.          disks without introducing prohibitive allocation fragmentation.
  9396.          Allocation fragmentation refers to the minimum amount of disk space
  9397.          that a file system can allocate to a small file--the allocation
  9398.          unit. If the allocation unit is size N, the average file on the
  9399.          disk is expected to waste N/2 bytes of disk space because each file
  9400.          has a last allocation unit and on the average that unit will be
  9401.          only half filled. Actually, if the allocation unit is large, say
  9402.          more than 2 KB, the average fragmentation loss is greater than this
  9403.          estimate because a disproportionate number of files are small.
  9404.             The existing MS-DOS FAT file system can handle large disks,
  9405.          but at the cost of using very large allocation units. Depending on
  9406.          the number and the size of the files, a 100 MB disk might be as
  9407.          much as 50 percent wasted by this fragmentation. The new Microsoft
  9408.          file system will support a very small allocation unit--probably 512
  9409.          bytes--to reduce this fragmentation, and this small allocation unit
  9410.          size will not adversely affect the performance of the file system.
  9411.  
  9412.       ■  File Protection
  9413.          A new file system also must support file access protection as part
  9414.          of the move toward a fully secure environment. File protection is
  9415.          typically a feature of multiuser operating systems; the MS-DOS FAT
  9416.          file system was designed for a single-user environment and contains
  9417.          no protection facilities. So why do we need them now? One reason is
  9418.          that a networked PC is physically a single-user machine, but
  9419.          logically it's a multiuser machine because multiple users can
  9420.          access the same files over the network. Also, as we shall see, it
  9421.          is sometimes useful to be able to protect your own files from
  9422.          access by yourself.
  9423.             Today, most network installations consist of server machines
  9424.          and client machines, with client machines able to access only files
  9425.          on server machines. MSNET and PCNET servers have a rudimentary form
  9426.          of file protection, but it needs improvement (see below). In the
  9427.          future, as machines become bigger and as products improve, files on
  9428.          client machines will also be available across the network. Clearly,
  9429.          a strong protection mechanism is needed to eliminate risks to a
  9430.          client machine's files. Finally, a file protection mechanism can be
  9431.          useful even on a single-user machine that is not accessible from a
  9432.          network. Today a variety of "Trojan" programs claim to be one thing
  9433.          but actually are another. In a nonnetworked environment, these
  9434.          programs are generally examples of mindless vandalism; typically,
  9435.          they purge the contents of the victim's hard disk. In a future
  9436.          office environment, they might edit payroll files or send sensitive
  9437.          data to someone waiting across the network. If you, as a user, can
  9438.          put sensitive files under password protection, they are safe even
  9439.          from yourself when you unwittingly run a Trojan program. That
  9440.          program doesn't know the password, and you certainly will decline
  9441.          to supply it. Self-protection also prevents someone from sitting
  9442.          down at your PC while you are at lunch or on vacation and wreaking
  9443.          havoc with your files.
  9444.             Protection mechanisms take two general forms:
  9445.          capability tokens and access lists. capability token gives access
  9446.          to an object if the requestor can supply the proper token,
  9447.          which itself can take a variety of forms. A per-file or per-
  9448.          directory password, such as is available on existing MSNET
  9449.          and PCNET products, is a kind of  capability token: If you
  9450.          can present the password, you can access the file. Note that
  9451.          the password is associated with the item, not the user. The
  9452.          front door key to your house is a good example of a
  9453.          capability token, and it shows the features and limitations
  9454.          of the approach very well. Access to your house depends on
  9455.          owning the capability token--the key--and not on who you
  9456.          are. If you don't have your key, you can't get in, even if
  9457.          it's your own house. Anybody that does have the key can get
  9458.          in, no matter who they are. A key can sometimes be handy:
  9459.          You can loan it to someone for a day and then get it back.
  9460.          You can give it to the plumber's office, for example, and
  9461.          the office can give it to the plumber, who can in turn give
  9462.          it to an assistant. Capability tokens are flexible because
  9463.          you can pass them around without notifying the owner of the
  9464.          protected object.
  9465.             This benefit is also the major drawback of capability token
  9466.          systems: The capabilities can be passed around willy-nilly
  9467.          and, like a key, can be duplicated. Once you give your key
  9468.          out, you never know if you've gotten "them" back again. You
  9469.          can't enumerate who has access to your house, and if they
  9470.          refuse to return a key or if they've duplicated it, you
  9471.          can't withdraw access to your house. The only way to regain
  9472.          control over your house is to change the lock, which means
  9473.          that you have to reissue keys to everybody who should get
  9474.          access. In the world of houses and keys, this isn't much of
  9475.          a problem because keys aren't given out that much and it's
  9476.          easy to contact the few people who should have them.
  9477.          Changing the capability "lock" on a computer file is much
  9478.          more difficult, however, because it may mean updating a
  9479.          great many programs that are allowed access, and they all
  9480.          have to be updated simultaneously so that none is
  9481.          accidentally locked out. And, of course, the distribution of
  9482.          the new capability token must be carried out securely; you
  9483.          must ensure that no "bad guy" gets a chance to see and copy
  9484.          the token.
  9485.             And, finally, because a separate capability token, or
  9486.          password, needs to be kept for each file or directory, you can't
  9487.          possibly memorize them all. Instead, they get built into programs,
  9488.          stored in files, entered into batch scripts, and so on. All these
  9489.          passwords--the ones that are difficult to change because of the
  9490.          hassle of updating everybody--are being kept around in "plain text"
  9491.          in standardized locations, an invitation for pilferage. And just as
  9492.          the lock on your door won't tell you how many keys exist, a
  9493.          capability token system won't be able to warn you that someone has
  9494.          stolen a copy of the capability token.
  9495.             An alternative approach is the access list mechanism. It is
  9496.          equivalent to the guard at the movie studio gate who has a
  9497.          list of people on his clipboard. Each protected object is
  9498.          associated with a list of who is allowed what kind of access. It's
  9499.          easy to see who has access--simply look at the list. It's easy to
  9500.          give or take away access--simply edit the list. Maintaining the
  9501.          list is easy because no change is made unless someone is to be
  9502.          added or removed, and the list can contain group names, such as
  9503.          "anyone from the production department" or "all vice presidents."
  9504.             The fly in this particular ointment--and the reason that
  9505.          MSNET didn't use this approach--is in authenticating the
  9506.          identification of the person who wants access. In our movie studio,
  9507.          a picture badge is probably sufficient.1 With the computer, we use
  9508.          a personal password. This password doesn't show that you have
  9509.          access to a particular file; it shows that you are who you claim to
  9510.          be. Also, because you have only one password, you can memorize it;
  9511.          it needn't be written on any list. Finally, you can change the
  9512.          password frequently because only one person--the one changing it,
  9513.          you--needs to be notified. Once the computer system knows that
  9514.          you're truly Hiram G. Hornswoggle, it grants or refuses access
  9515.          based on whether you're on an access list or belong to a group that
  9516.          is on an access list. MS-DOS can't use this approach because it's
  9517.          an unprotected system; whatever flag it sets in memory to say that
  9518.          you have properly authenticated yourself can be set by a cheater
  9519.          program. OS/2 is a protect mode operating system and is secure from
  9520.          such manipulation provided that no untrusted real mode applications
  9521.          are executed.2 A networking environment provides an extra
  9522.          challenge because you can write a program--perhaps running
  9523.          on an MS-DOS machine to avoid protection mechanisms--that "sniffs"
  9524.          the network, examining every communication. A client machine can't
  9525.          send a plain-text password over the network to authenticate its
  9526.          user because a sniffer could see it. And it certainly can't send a
  9527.          message saying, "I'm satisfied that this is really Hiram." The
  9528.          client machine may be running bogus software that will lie and say
  9529.          that when it isn't true. In other words, a network authentication
  9530.          protocol must assume that "bad guys" can read all net transmissions
  9531.          and can generate any transmission they wish.
  9532.             As should be clear by now, a future OS/2 file system will
  9533.          support per-object permission lists. OS/2 will be enhanced
  9534.          to support users' identifying themselves by means of personal
  9535.          passwords. Future network software will support a secure network
  9536.          authentication protocol.
  9537.  
  9538.       A new file system will do more than support access lists; it will also
  9539.  support filenames longer than the FAT 8.3 convention, and it will support
  9540.  extended file attributes. The FAT file system supports a very limited set
  9541.  of attributes, each of which are binary flags--system, hidden, read-only,
  9542.  and so on. Extended attributes allow an arbitrary set of attributes,
  9543.  represented as text strings, to be associated with each file. Individual
  9544.  applications will be able to define specific attributes, set them on files,
  9545.  and later query their values. Extended attributes can be used, for example,
  9546.  to name the application that created the file. This would allow a user to
  9547.  click the mouse over the filename on a directory display and have the
  9548.  presentation manager bring up the proper application on that file.
  9549.       Finally, although this file system wish list looks pretty good, how do
  9550.  we know that we've covered all the bases? And will our new file system work
  9551.  well with CD-ROM3 disks and WORM drives? The answers are "We don't" and
  9552.  "It doesn't," so a future OS/2 release will support installable file
  9553.  systems. An installable file system is similar to an installable device
  9554.  driver. When the system is initialized, not only device drivers but new
  9555.  file system management packages can be installed into OS/2. This will allow
  9556.  specialized file systems to handle specialized devices such as CD-ROMs and
  9557.  WORM, as well as providing an easy interface to media written on foreign
  9558.  file systems that are on non-MS-DOS or non-OS/2 systems.
  9559.  
  9560.  
  9561.  21.2  The 80386
  9562.  
  9563.  Throughout this book, the name 80386 keeps cropping up, almost as a kind of
  9564.  magical incantation. To a system designer, it is a magical device. It
  9565.  provides the protection facilities of the 80286, but it also provides three
  9566.  other key features.
  9567.  
  9568.  
  9569.  21.2.1  Large Segments
  9570.  The 80386 has a segmented architecture very much like that of the 80286,
  9571.  but 80286 segments are limited to 64 KB. On the 80386, segments can be as
  9572.  large as 4 million KB; segments can be so large that an entire program can
  9573.  run in 2 segments (one code and one data) and essentially ignore the
  9574.  segmentation facilities of the processor. This is called flat model.
  9575.  Writing programs that deal with large structures is easier using flat
  9576.  model, and because compilers have a hard time generating optimal segmented
  9577.  code, converting 8086/80286 large model programs to 80386 flat model can
  9578.  produce dramatic increases in execution speed.
  9579.       Although a future release of OS/2 will certainly support large
  9580.  segments and applications that use flat model internally, OS/2 will not
  9581.  necessarily provide a flat model API. The system API for 32-bit
  9582.  applications may continue to use segmented (that is, 48-bit) addresses.
  9583.  
  9584.  
  9585.  21.2.2  Multiple Real Mode Boxes
  9586.  The 80386 provides a mode of execution called virtual real mode. Processes
  9587.  that run in this mode execute instructions exactly as they would in real
  9588.  mode, but they are not truly in real mode; they are in a special 8086-
  9589.  compatible protected mode. The additional memory management and protection
  9590.  facilities that this mode provides allow a future version of OS/2 to
  9591.  support more than one real mode box at the same time; multiple real mode
  9592.  applications will be able to execute simultaneously and to continue
  9593.  executing while in background mode. The virtual real mode eliminates the
  9594.  need for mode switching; thus, the user can execute real mode applications
  9595.  while running communications applications that have a high interrupt
  9596.  rate.
  9597.  
  9598.  
  9599.  21.2.3  Full Protection Capability
  9600.  The virtual real mode capability, coupled with the 80386's ability to
  9601.  allow/disallow I/O access on a port-by-port basis, provides the hardware
  9602.  foundation for a future OS/2 that is fully secure. In a fully secure OS/2,
  9603.  the modules loaded in during bootup--the operating system itself, device
  9604.  drivers, installable file systems, and so on--must be trusted, but no other
  9605.  program can accidentally or deliberately damage others, read protected
  9606.  files, or otherwise access or damage restricted data. The only damage an
  9607.  aberrant or malicious program will be able to do is to slow down the
  9608.  machine by hogging the resources, such as consuming most of the RAM or CPU
  9609.  time. This is relatively harmless; the user can simply kill the offending
  9610.  program and not run it anymore.
  9611.  
  9612.  
  9613.  21.2.4  Other Features
  9614.  The 80386 contains other significant features besides speed, such as paged
  9615.  virtual memory, that don't appear in an API or in a specific user benefit.
  9616.  For this reason, we won't discuss them here other than to state the
  9617.  obvious: An 80386 machine is generally considerably faster than an 80286-
  9618.  based one.
  9619.       So what do these 80386 features mean for the 80286? What role will it
  9620.  play in the near and far future? Should a developer write for the 80286 or
  9621.  the 80386? First, OS/2 for the 803864 is the same operating system,
  9622.  essentially, as OS/2 for the 80286. The only new API in 80386 OS/2 will be
  9623.  the 32-bit wide one for 32-bit mode 80386-only binaries. The other
  9624.  features--such as virtual memory, I/O permission mapping, and multiple real
  9625.  mode boxes--are of value to the user but don't present any new APIs and
  9626.  therefore are compatible with all applications. Certainly, taking advantage
  9627.  of the 80386's new instruction order codes and 2^32-byte-length segments
  9628.  will require a new API; in fact, a program must be specially written and
  9629.  compiled for that environment. Only applications that can't function at all
  9630.  using the smaller 80286-compatible segments need to become 80386 dependent;
  9631.  80286 protect mode programs will run without change and without any
  9632.  disadvantage on the 80386, taking advantage of its improved speed.
  9633.       To summarize, there is only one operating system, OS/2. OS/2 supports
  9634.  16-bit protected mode applications that run on all machines, and OS/2 will
  9635.  support 32-bit protected mode applications that will run only on 80386
  9636.  machines. A developer should consider writing an application for the
  9637.  32-bit model5 only if the application performs so poorly in the 16-bit
  9638.  model that a 16-bit version is worthless. Otherwise, one should develop
  9639.  applications for the 16-bit model; such applications will run well on all
  9640.  existing OS/2-compatible machines and on all OS/2 releases. Later, when the
  9641.  80386 and OS/2-386 have sufficient market penetration, you may want to
  9642.  release higher-performance upgrades to products that require the 80386.
  9643.  
  9644.  
  9645.  21.3  The Next Ten Years
  9646.  
  9647.  Microsoft believes that OS/2 will be a major influence in the personal
  9648.  computer industry for roughly the next ten years. The standardization of
  9649.  computing environments that mass market software brings about gives such
  9650.  standards abnormal longevity, while the incredible rate of hardware
  9651.  improvements brings on great pressure to change. As a result, we expect
  9652.  OS/2 to live long and prosper, where long is a relative term in an industry
  9653.  in which nothing can survive more than a decade. What might OS/2's
  9654.  successor system look like? If we could answer that today, a successor
  9655.  system would be unnecessary. Clearly, the increases in CPU performance will
  9656.  continue. Personal computers will undoubtedly follow in the footsteps of
  9657.  their supercomputer brethren and become used for more than calculation, but
  9658.  also for simulation, modeling, and expert systems, not only in the
  9659.  workplace but also in the home. The future will become clearer, over time,
  9660.  as this most wonderful of tools continues to change its users.
  9661.       The development of OS/2 is, to date, the largest project that
  9662.  Microsoft has ever taken on. From an initially very small group of
  9663.  Microsoft engineers, to a still small joint Microsoft-IBM design team, to
  9664.  finally a great many developers, builders, testers, and documenters from
  9665.  both Microsoft and IBM, the project became known affectionately as the
  9666.  "Black Hole."
  9667.       As I write this, OS/2 is just weeks away from retail sale. It's been a
  9668.  great pleasure for me and for the people who worked with me to see our
  9669.  black hole begin to give back to our customers the fruits of the labors
  9670.  that were poured into it.
  9671.  
  9672.  
  9673.  
  9674.  Glossary
  9675.  
  9676.  ───────────────────────────────────────────────────────────────────────────
  9677.  
  9678.  anonymous pipe
  9679.  a data storage buffer that OS/2 maintains in RAM; used for interprocess
  9680.  communications.
  9681.  
  9682.  Applications Program Interface (API)
  9683.  the set of calls a program uses to obtain services from the operating
  9684.  system. The term API denotes a service interface, whatever its form.
  9685.  
  9686.  background category
  9687.  a classification of processes that consists of those associated with a
  9688.  screen group not currently being displayed.
  9689.  
  9690.  call gate
  9691.  a special LDT or GDT entry that describes a subroutine entry point rather
  9692.  than a memory segment. A far call to a call gate selector will cause a
  9693.  transfer to the entry point specified in the call gate. This is a feature
  9694.  of the 80286/80386 hardware and is normally used to provide a transition
  9695.  from a lower privilege state to a higher one.
  9696.  
  9697.  captive thread
  9698.  a thread that has been created by a dynlink package and that stays within
  9699.  the dynlink code, never transferring back to the client process's code;
  9700.  also a thread that is used to call a service entry point and that will
  9701.  never return or that will return only if some specific event occurs.
  9702.  
  9703.  child process
  9704.  a process created by another process (its parent process).
  9705.  
  9706.  closed system
  9707.  hardware or software design that cannot be enhanced in the field by third-
  9708.  party suppliers.
  9709.  
  9710.  command subtree
  9711.  a process and all its descendants.
  9712.  
  9713.  context switch
  9714.  the act of switching the CPU from the execution of one thread to another,
  9715.  which may belong to the same process or to a different one.
  9716.  
  9717.  cooked mode
  9718.  a mode established by programs for keyboard input. In cooked mode, OS/2
  9719.  handles the line-editing characters such as the back space.
  9720.  
  9721.  critical section
  9722.  a body of code that manipulates a data resource in a non-reentrant way.
  9723.  
  9724.  daemon program
  9725.  a process that performs a utility function without interaction with the
  9726.  user. For example, the swapper process is a daemon program.
  9727.  
  9728.  debuggee
  9729.  the program being debugged.
  9730.  
  9731.  debugger
  9732.  a program that helps the programmer locate the source of problems found
  9733.  during runtime testing of a program.
  9734.  
  9735.  device driver
  9736.  a program that transforms I/O requests made in a standard, device-
  9737.  independent fashion into the operations necessary to make a specific piece
  9738.  of hardware fulfill that request.
  9739.  
  9740.  device monitor
  9741.  a mechanism that allows processes to track and/or modify device data
  9742.  streams.
  9743.  
  9744.  disjoint LDT space
  9745.  the LDT selectors reserved for memory objects that are shared or that may
  9746.  be shared among processes.
  9747.  
  9748.  dynamic link
  9749.  a method of postponing the resolution of external references until loadtime
  9750.  or runtime. A dynamic link allows the called subroutines to be packaged,
  9751.  dist ributed, and maintained independently of their callers. OS/2 extends
  9752.  the dynamic link (or dynlink) mechanism to serve as the primary method by
  9753.  which all system and nonsystem services are obtained.
  9754.  
  9755.  dynlink
  9756.  see dynamic link.
  9757.  
  9758.  dynlink library
  9759.  a file, in a special format, that contains the binary code for a group of
  9760.  dynamically linked subroutines.
  9761.  
  9762.  dynlink routine
  9763.  see dynamic link.
  9764.  
  9765.  dynlink subsystem
  9766.  a dynlink module that provides a set of services built around a resource.
  9767.  
  9768.  encapsulation
  9769.  the principle of hiding the internal implementation of a program, function,
  9770.  or service so that its clients can tell what it does but not how it does
  9771.  it.
  9772.  
  9773.  environment strings
  9774.  a series of user-definable and program-definable strings that are
  9775.  associated with each process. The initial values of environment strings are
  9776.  established by a process's parent.
  9777.  
  9778.  exitlist
  9779.  a list of subroutines that OS/2 calls when a process has terminated. The
  9780.  exitlist is executed after process termination but before the process is
  9781.  actually destroyed.
  9782.  
  9783.  Family Applications Program Interface (Family API)
  9784.  a standard execution environment under MS-DOS versions 2.x and 3.x and
  9785.  OS/2. The programmer can use the Family API to create an application that
  9786.  uses a subset of OS/2 functions (but a superset of MS-DOS 3.x functions)
  9787.  and that runs in a binary-compatible fashion under MS-DOS versions 2.x and
  9788.  3.x and OS/2.
  9789.  
  9790.  file handle
  9791.  a binary value that represents an open file; used in all file I/O calls.
  9792.  
  9793.  file locking
  9794.  an OS/2 facility that allows one program to temporarily prevent other
  9795.  programs from reading and/or writing a particular file.
  9796.  
  9797.  file system name space
  9798.  names that have the format of filenames. All such names will eventually
  9799.  represent disk "files"--data or special. Initially, some of these names are
  9800.  kept in internal OS/2 RAM tables and are not present on any disk volume.
  9801.  
  9802.  forced event
  9803.  an event or action that is forced upon a thread or a process from an
  9804.  external source; for example, a Ctrl-C or a DosKill command.
  9805.  
  9806.  foreground category
  9807.  a classification of processes that consists of those associated with the
  9808.  currently active screen group.
  9809.  
  9810.  GDT
  9811.  see global descriptor table.
  9812.  
  9813.  general priority category
  9814.  the OS/2 classification of threads that consists of three subcategories:
  9815.  background, foreground, and interactive.
  9816.  
  9817.  general protection (GP) fault
  9818.  an error that occurs when a program accesses invalid memory locations or
  9819.  accesses valid locations in an invalid way (such as writing into read-only
  9820.  memory areas).
  9821.  
  9822.  giveaway shared memory
  9823.  a shared memory mechanism in which a process that already has access to the
  9824.  segment can grant access to another process. Processes cannot obtain access
  9825.  for themselves; access must be granted by another process that already has
  9826.  access.
  9827.  
  9828.  global data segment
  9829.  a data segment that is shared among all instances of a dynlink routine; in
  9830.  other words, a single segment that is accessible to all processes that call
  9831.  a particular dynlink routine.
  9832.  
  9833.  global descriptor table (GDT)
  9834.  an element of the 80286/80386 memory management hardware. The GDT holds the
  9835.  descriptions of as many as 4095 global segments. A global segment is
  9836.  accessible to all processes.
  9837.  
  9838.  global subsystem initialization
  9839.  a facility that allows a dynlink routine to specify that its initialize
  9840.  entry point should be called when the dynlink package is loaded on behalf
  9841.  of its first client.
  9842.  
  9843.  grandparent process
  9844.  the parent process of a process that created a process.
  9845.  
  9846.  handle
  9847.  an arbitrary integer value that OS/2 returns to a process so that the
  9848.  process can return it to OS/2 on subsequent calls; known to programmers as
  9849.  a magic cookie.
  9850.  
  9851.  hard error
  9852.  an error that the system detects but which it cannot correct without user
  9853.  intervention.
  9854.  
  9855.  hard error daemon
  9856.  a daemon process that services hard errors. The hard error daemon may be an
  9857.  independent process, or it may be a thread that belongs to the session
  9858.  manager or to the presentation manager.
  9859.  
  9860.  huge segments
  9861.  a software technique that allows the creation and use of pseudo segments
  9862.  larger than 65 KB.
  9863.  
  9864.  installable file system (IFS)
  9865.  a body of code that OS/2 loads at boot time and that provides the software
  9866.  to manage a file system on a storage device, including the ability to
  9867.  create and maintain directories, allocate disk space, and so on.
  9868.  
  9869.  instance data segment
  9870.  a memory segment that holds data specific to each instance of the dynlink
  9871.  routine.
  9872.  
  9873.  instance subsystem initialization
  9874.  a service that dynlink routines can request. A dynlink routine's initialize
  9875.  entry point is called each time a new client is linked to the routine.
  9876.  
  9877.  interactive category
  9878.  a classification of processes that consists of the process currently
  9879.  interacting with the keyboard.
  9880.  
  9881.  interactive program
  9882.  a program whose function is to obey commands from a user, such as an editor
  9883.  or a spreadsheet program. Programs such as compilers may literally interact
  9884.  by asking for filenames and compilation options, but they are considered
  9885.  noninteractive because their function is to compile a source program, not
  9886.  to provide answers to user-entered commands.
  9887.  
  9888.  interprocess communications (IPC)
  9889.  the ability of processes and threads to transfer data and messages among
  9890.  themselves; used to offer services to and receive services from other
  9891.  programs.
  9892.  
  9893.  interruptible block
  9894.  a special form of a blocking operation used inside the OS/2  kernel so that
  9895.  events such as process kill and Ctrl-C can interrupt a thread that is
  9896.  waiting, inside OS/2, for an event.
  9897.  
  9898.  I/O privilege mechanism
  9899.  a facility that allows a process to ask a device driver for direct access
  9900.  to the device's I/O ports and any dedicated or mapped memory locations it
  9901.  has. The I/O privilege mechanism can be used directly by an application or
  9902.  indirectly by a dynlink package.
  9903.  
  9904.  IPC
  9905.  see interprocess communications.
  9906.  
  9907.  KBD
  9908.  an abbreviated name for the dynlink package that manages the keyboard
  9909.  device. All its entry points start with Kbd.
  9910.  
  9911.  kernel
  9912.  the central part of OS/2. It resides permanently in fixed memory locations
  9913.  and executes in the privileged ring 0 state.
  9914.  
  9915.  LDT
  9916.  see local descriptor table.
  9917.  
  9918.  loadtime dynamic linking
  9919.  the act of connecting a client process to dynamic link libraries when the
  9920.  process is first loaded into memory.
  9921.  
  9922.  local descriptor table (LDT)
  9923.  an element of the 80286/80386 memory management hardware. The LDT holds the
  9924.  descriptions of as many as 4095 local segments. Each process has its own
  9925.  LDT and cannot access the LDTs of other processes.
  9926.  
  9927.  logical device
  9928.  a symbolic name for a device that the user can cause to be mapped to any
  9929.  physical (actual) device.
  9930.  
  9931.  logical directory
  9932.  a symbolic name for a directory that the user can cause to be mapped to any
  9933.  actual drive and directory.
  9934.  
  9935.  low priority category
  9936.  a classification of processes that consists of processes that get CPU time
  9937.  only when no other thread in the other categories needs it; this category
  9938.  is lower in priority than the general priority category.
  9939.  
  9940.  magic cookie
  9941.  see handle.
  9942.  
  9943.  memory manager
  9944.  the section of OS/2 that allocates both physical memory and virtual memory.
  9945.  
  9946.  memory overcommit
  9947.  allocating more memory to the running program than physically exists.
  9948.  
  9949.  memory suballocation
  9950.  the OS/2 facility that allocates pieces of memory from within an
  9951.  application's segment.
  9952.  
  9953.  MOU
  9954.  an abbreviated name for the dynlink package that manages the mouse device.
  9955.  All its entry points start with Mou.
  9956.  
  9957.  multitasking operating system
  9958.  an operating system in which two or more programs/threads can execute
  9959.  simultaneously.
  9960.  
  9961.  named pipe
  9962.  a data storage buffer that OS/2 maintains in RAM; used for interprocess
  9963.  communication.
  9964.  
  9965.  named shared memory
  9966.  a memory segment that can be accessed simultaneously by more than one
  9967.  process. Its name allows processes to request access to it.
  9968.  
  9969.  open system
  9970.  hardware or software design that allows third-party additions and upgrades
  9971.  in the field.
  9972.  
  9973.  object name buffer
  9974.  the area in which OS/2 returns a character string if the DosExecPgm
  9975.  function fails.
  9976.  
  9977.  parallel multitasking
  9978.  the process whereby programs execute simultaneously.
  9979.  
  9980.  parent process
  9981.  a process that creates another process, which is called the child process.
  9982.  
  9983.  physical memory
  9984.  the RAM (Random Access Memory) physically present inside the machine.
  9985.  
  9986.  PID (Process Identification Number)
  9987.  a unique code that OS/2 assigns to a process when the process is created.
  9988.  The PID may be any value except 0.
  9989.  
  9990.  pipe
  9991.  see anonymous pipe; named pipe.
  9992.  
  9993.  presentation manager
  9994.  the graphical user interface for OS/2.
  9995.  
  9996.  priority
  9997.  (also known as CPU priority) the numeric value assigned to each runnable
  9998.  thread in the system. Threads with a higher priority are assigned the CPU
  9999.  in preference to those with a lower priority.
  10000.  
  10001.  privilege mode
  10002.  a special execution mode (also known as ring 0) supported by the
  10003.  80286/80386 hardware. Code executing in this mode can execute restricted
  10004.  instructions that are used to manipulate key system structures and tables.
  10005.  Only the OS/2 kernel and device drivers run in this mode.
  10006.  
  10007.  process
  10008.  the executing instance of a binary file. In OS/2, the terms task and
  10009.  process are used interchangeably. A process is the unit of ownership, and
  10010.  processes own resources such as memory, open files, dynlink libraries, and
  10011.  semaphores.
  10012.  
  10013.  protect mode
  10014.  the operating mode of the 80286 microprocessor that allows the operating
  10015.  system to use features that protect one application from another; also
  10016.  called protected mode.
  10017.  
  10018.  queue
  10019.  an orderly list of elements waiting for processing.
  10020.  
  10021.  RAM semaphore
  10022.  a kind of semaphore that is based in memory accessible to a thread; fast,
  10023.  but with limited functionality. See system semaphore.
  10024.  
  10025.  raw mode
  10026.  a mode established by programs for keyboard input. In raw mode OS/2 passes
  10027.  to the caller each character typed immediately as it is typed. The caller
  10028.  is responsible for handling line-editing characters such as the back space.
  10029.  
  10030.  real mode
  10031.  the operating mode of the 80286 microprocessor that runs programs designed
  10032.  for the 8086/8088 microprocessor.
  10033.  
  10034.  record locking
  10035.  the mechanism that allows a process to lock a range of bytes within a file.
  10036.  While the lock is in effect, no other process can read or write those
  10037.  bytes.
  10038.  
  10039.  ring 3
  10040.  the privilege level that is used to run applications. Code executing at
  10041.  this level cannot modify critical system structures.
  10042.  
  10043.  runtime dynamic linking
  10044.  the act of establishing a dynamic link after a process has begun execution.
  10045.  This is done by providing OS/2 with the module and entry point names; OS/2
  10046.  returns the address of the routine.
  10047.  
  10048.  scheduler
  10049.  the part of OS/2 that decides which thread to run and how long to run it
  10050.  before assigning the CPU to another thread; also, the part of OS/2 that
  10051.  determines the priority value for each thread.
  10052.  
  10053.  screen group
  10054.  a group of one or more processes that share (generally in a serial fashion)
  10055.  a single logical screen and keyboard.
  10056.  
  10057.  semaphore
  10058.  a software flag or signal used to coordinate the activities of two or more
  10059.  threads; commonly used to protect a critical section.
  10060.  
  10061.  serial multitasking
  10062.  the process whereby multiple programs execute, but only one at a time.
  10063.  
  10064.  session manager
  10065.  a system utility that manages screen group switching. The session manager
  10066.  is used only in the absence of the presentation manager; the presentation
  10067.  manager replaces the session manager.
  10068.  
  10069.  shared memory
  10070.  a memory segment that can be accessed simultaneously by more than one
  10071.  process.
  10072.  
  10073.  signaling
  10074.  using semaphores to notify threads that certain events or activities have
  10075.  taken place.
  10076.  
  10077.  signals
  10078.  notification mechanisms implemented in software that operate in a fashion
  10079.  analogous to hardware interrupts.
  10080.  
  10081.  software tools approach
  10082.  a design philosophy in which each program and application in a package is
  10083.  dedicated to performing a specific task and doing that task very well. See
  10084.  also encapsulation.
  10085.  
  10086.  stack frame
  10087.  a portion of a thread's stack that contains a procedure's local variables
  10088.  and parameters.
  10089.  
  10090.  static linking
  10091.  the combining of multiple compilands into a single executable file, thereby
  10092.  resolving undefined external references.
  10093.  
  10094.  single-tasking
  10095.  a computer environment in which only one program runs at a time.
  10096.  
  10097.  swapping
  10098.  the technique by which some code or data in memory is written to a disk
  10099.  file, thus allowing the memory it was using to be reused for another
  10100.  purpose.
  10101.  
  10102.  system semaphore
  10103.  a semaphore that is implemented in OS/2's internal memory area; somewhat
  10104.  slower than RAM semaphores, but providing more features.
  10105.  
  10106.  System File Table (SFT)
  10107.  an internal OS/2 table that contains an entry for every file currently
  10108.  open.
  10109.  
  10110.  task
  10111.  see process.
  10112.  
  10113.  thread
  10114.  the OS/2 mechanism that allows more than one path of execution through the
  10115.  same instance of an application program.
  10116.  
  10117.  thread ID
  10118.  the handle of a particular thread within a process.
  10119.  
  10120.  thread of execution
  10121.  the passage of the CPU through the instruction sequence.
  10122.  
  10123.  time-critical priority
  10124.  a classification of processes that may be interactive or noninteractive, in
  10125.  the foreground or background screen group, which have a higher priority
  10126.  than any non-time-critical thread in the system.
  10127.  
  10128.  time slice
  10129.  the amount of execution time that the scheduler will give a thread before
  10130.  reassigning the CPU to another thread of equal priority.
  10131.  
  10132.  VIO
  10133.  an abbreviated name of the dynlink package that manages the display device.
  10134.  All its entry points start with Vio.
  10135.  
  10136.  virtual memory
  10137.  the memory space allocated to and used by a process. At the time it is
  10138.  being referenced, the virtual memory must be present in physical memory,
  10139.  but otherwise it may be swapped to a disk file.
  10140.  
  10141.  virtualization
  10142.  the general technique of hiding a complicated actual situation behind a
  10143.  simple, standard interface.
  10144.  
  10145.  writethrough
  10146.  an option available when a file write operation is performed which
  10147.  specifies that the normal caching mechanism is to be sidestepped and the
  10148.  data is to be written through to the disk surface immediately.
  10149.  
  10150.  3x box
  10151.  the OS/2 environment that emulates an 8086-based PC running MS-DOS versions
  10152.  2.x or 3.x.
  10153.  
  10154.  
  10155.  
  10156.  
  10157.  Index
  10158.  
  10159.  
  10160.  Symbols
  10161.  ──────────────────────────────────────────────────────────────────────
  10162.  3x box
  10163.  80286 processor
  10164.     bugs in
  10165.     I/O access control in
  10166.     segmented architecture of
  10167.     size of segments
  10168.  80386 processor
  10169.     I/O access control in
  10170.     key features of
  10171.  8080 processor
  10172.  8086/8088 processor
  10173.     memory limitations of
  10174.     real mode
  10175.  
  10176.  
  10177.  A
  10178.  ──────────────────────────────────────────────────────────────────────
  10179.  Abort, Retry, Ignore message (MS-DOS)
  10180.  access lists
  10181.  addresses
  10182.     invalid
  10183.     subroutine
  10184.  addressing, huge model
  10185.  address offsets
  10186.  address space, linear
  10187.  allocation. See also memory
  10188.     file
  10189.     memory
  10190.  anonymous pipes
  10191.  API
  10192.     80386 processor
  10193.     Family
  10194.     memory management
  10195.  Apple Macintosh
  10196.  application environment. See environment
  10197.  application mode
  10198.  applications
  10199.     ``combo''
  10200.     command
  10201.     communicating between
  10202.     compatibility with MS-DOS
  10203.     designing for both OS/2 and MS-DOS
  10204.     device monitors and
  10205.     dual mode
  10206.     I/O-bound
  10207.     protecting
  10208.     real mode
  10209.     running MS-DOS
  10210.     time-critical
  10211.  Applications Program Interface. See API
  10212.     Family
  10213.  architecture
  10214.     80386 processor
  10215.     design concepts of OS/2
  10216.     device driver
  10217.     I/O
  10218.     segmented
  10219.  arguments
  10220.     DosCWait
  10221.     DosExecPgm
  10222.  ASCII text strings
  10223.  ASCIIZ strings
  10224.  asynchronous I/O
  10225.  asynchronous processing
  10226.  atomic operation
  10227.  
  10228.  
  10229.  B
  10230.  ──────────────────────────────────────────────────────────────────────
  10231.  background
  10232.     category
  10233.     I/O
  10234.     processing
  10235.     threads and applications
  10236.  base segment
  10237.  BAT files, MS-DOS
  10238.  BIOS entry vector, hooking the
  10239.  blocking services
  10240.  block mode
  10241.     device drivers
  10242.     driver algorithm
  10243.  blocks, interruptible
  10244.  boot process
  10245.  boot time, installing device drivers at
  10246.  breakthroughs, technological
  10247.  buffer reusability
  10248.  buffers
  10249.     flushing
  10250.     monitor
  10251.  bugs
  10252.     80286 processor
  10253.     program
  10254.  byte-stream mechanism
  10255.  
  10256.  
  10257.  C
  10258.  ──────────────────────────────────────────────────────────────────────
  10259.  call gate
  10260.  call and return sequence, OS/2
  10261.  call statements, writing for dynamic link routines
  10262.  call timed out error
  10263.  capability tokens
  10264.  captive threads
  10265.  categories, priority
  10266.  Central Processing Unit. See CPU
  10267.  character mode
  10268.     device driver model for
  10269.  child processes
  10270.     controlling
  10271.  CHKDSK
  10272.  circular references
  10273.  CLI instruction
  10274.  client processes
  10275.  closed system
  10276.  clusters
  10277.  CMD.EXE
  10278.     I/O architecture and
  10279.     logical device and directory names
  10280.  code, swapping
  10281.  CodeView
  10282.  command application
  10283.  COMMAND.COM
  10284.  command mode
  10285.  command processes
  10286.  command subtrees
  10287.     controlling
  10288.  command threads
  10289.  communication, interprocess
  10290.  compatibility
  10291.     downward
  10292.     functional
  10293.     levels of
  10294.     MS-DOS
  10295.     name generation and
  10296.     VIO and presentation manager
  10297.  compatibility box
  10298.  compatibility issues, OS/2
  10299.  compatibility mode
  10300.  computers
  10301.     mental work and
  10302.     multiple CPU
  10303.     networked (see also networks)
  10304.     Von Neumann
  10305.  CONFIG.SYS file
  10306.  consistency, design
  10307.  context switching
  10308.  conventions, system
  10309.  cooked mode
  10310.  CP/M, compatibility with
  10311.  CP/M-80
  10312.  CPU See also 8086/8088 processor, 80286 processor, 80386 processor
  10313.     priority
  10314.  crashes, system
  10315.  critical sections
  10316.     protecting
  10317.     signals and
  10318.  CS register
  10319.  Ctrl-Break and Ctrl-C
  10320.  customized environment
  10321.  
  10322.  
  10323.  
  10324.  D
  10325.  ──────────────────────────────────────────────────────────────────────
  10326.  daemon, hard error
  10327.  daemon interfaces, dynamic links as
  10328.  daemon program
  10329.  data
  10330.     global
  10331.     handling in dynamic linking
  10332.     instance
  10333.     instructions and
  10334.     swapped-out
  10335.  data integrity
  10336.  data segments, executing from
  10337.  data streams, monitoring
  10338.  debuggee
  10339.  debugger
  10340.     system
  10341.  debugging
  10342.  demand loading
  10343.  demand load segment
  10344.  densities, disk
  10345.  design, concepts of OS/2
  10346.  design goals
  10347.  DevHlp
  10348.  device data streams
  10349.  device drivers
  10350.     architecture of
  10351.     block mode
  10352.     character mode
  10353.     code structure
  10354.     definition of
  10355.     dynamic link pseudo
  10356.     OS/2 communication and
  10357.     programming model for
  10358.  device independence
  10359.     definition of
  10360.  device management
  10361.  device monitors
  10362.  device names
  10363.  devices
  10364.     direct access of
  10365.     logical
  10366.  device-specific code, encapsulating
  10367.  Digital Research
  10368.  direct device access
  10369.  directories
  10370.     ISAM
  10371.     logical
  10372.     working
  10373.  directory names
  10374.  directory tree hierarchy
  10375.  disjoint LDT space
  10376.  disk data synchronization
  10377.  disk I/O requests
  10378.  disk seek times
  10379.  disk space, allocation of
  10380.  DISKCOMP
  10381.  DISKCOPY
  10382.  disks
  10383.     laser
  10384.     unlabeled
  10385.  dispatcher
  10386.  display device, manipulating the
  10387.  display memory
  10388.  .DLL files
  10389.  DosAllocHuge
  10390.  DosAllocSeg
  10391.  DosAllocShrSeg
  10392.  DosBufReset
  10393.  DosCallNmPipe
  10394.  DosCalls
  10395.  DosClose
  10396.  DosConnectNmPipe
  10397.  DosCreateCSAlias
  10398.  DosCreateSem
  10399.  DosCreateThread
  10400.  DosCWait
  10401.  DosDevIOCtl
  10402.  DosDupHandle
  10403.  DosEnterCritSec
  10404.  DosErrClass
  10405.  DosError
  10406.  DosExecPgm
  10407.  DosExit
  10408.  DosExitCritSec
  10409.  DosExitList
  10410.  DosFindFirst
  10411.  DosFindNext
  10412.  DosFlagProcess
  10413.  DosFreeModule
  10414.  DosFreeSeg
  10415.  DosGetHugeShift
  10416.  DosGetMachineMode
  10417.  DosGetProcAddr
  10418.  DosGiveSeg
  10419.  DosHoldSignal
  10420.  DosKill
  10421.  DosKillProcess
  10422.  DosLoadModule
  10423.  DosMakeNmPipe
  10424.  DosMakePipe
  10425.  DosMonRead
  10426.  DosMonReq
  10427.  DosMonWrite
  10428.  DosMuxSemWait
  10429.  DosNewSize
  10430.  DosOpen
  10431.     named pipes and
  10432.  DosPeekNmPipe
  10433.  DosPtrace
  10434.  DosRead
  10435.     named pipes and
  10436.  DosReadQueue
  10437.  DosReallocHuge
  10438.  DosResumeThread
  10439.  DosScanEnv
  10440.  DosSearchPath
  10441.  DosSemClear
  10442.  DosSemRequest
  10443.  DosSemSet
  10444.  DosSemWait
  10445.  DosSetFHandState
  10446.  DosSetPrty
  10447.  DosSetSigHandler
  10448.  DosSetVerify
  10449.  DosSleep
  10450.  DosSubAlloc
  10451.  DosSubFrees
  10452.  DosSuspendThread
  10453.  DosTimerAsync
  10454.  DosTimerStart
  10455.  DosTimerStop
  10456.  DosTransactNmPipe
  10457.  DosWrite
  10458.     named pipes and
  10459.  DosWriteQueue
  10460.  downward compatibility
  10461.  DPATH
  10462.  drive-oriented operations
  10463.  drives, high- and low-density
  10464.  dual mode
  10465.  Dynamic Data Exchange (DDE)
  10466.  dynamic linking
  10467.     Family API use of
  10468.     loadtime
  10469.     runtime
  10470.  dynamic link libraries
  10471.  dynamic link routines, calling
  10472.  dynamic links
  10473.     architectural role of
  10474.     circular references in
  10475.     details on implementing
  10476.     device drivers and
  10477.     interfaces to other processes
  10478.     naming
  10479.     side effects of
  10480.     using for pseudo device drivers
  10481.  dynlink See also dynamic links
  10482.     routines
  10483.  
  10484.  
  10485.  E
  10486.  ──────────────────────────────────────────────────────────────────────
  10487.  encapsulation
  10488.     device driver
  10489.  entry ordinals
  10490.  entry point name
  10491.  environment
  10492.     customized
  10493.     dual mode
  10494.     protected
  10495.     single-tasking
  10496.     single-tasking versus multitasking
  10497.     stable
  10498.     stand-alone
  10499.     virtualizing the
  10500.  environment block
  10501.  environment strings
  10502.  EnvPointer
  10503.  EnvString
  10504.  error codes, compatibility mode
  10505.  errors
  10506.     general protection fault
  10507.     hard
  10508.     localization of
  10509.     program
  10510.     timed out
  10511.  .EXE files
  10512.     executing
  10513.     Family API
  10514.  EXEC function (MS-DOS)
  10515.  execute threads
  10516.  executing programs
  10517.  execution speed
  10518.  exitlist
  10519.  expandability
  10520.  extended file attributes
  10521.  extended partitioning
  10522.  extension, filename
  10523.  external name, dynamic link
  10524.  
  10525.  
  10526.  F
  10527.  ──────────────────────────────────────────────────────────────────────
  10528.  Family API
  10529.     drawbacks of
  10530.  Family Applications Program Interface (Family API)
  10531.  far return instruction
  10532.  fault, memory not present
  10533.  fault errors, general protection
  10534.  faults, GP
  10535.  features
  10536.     additional
  10537.     introducing new operating system
  10538.  file
  10539.     allocation, improving
  10540.     attributes, extended
  10541.     handles
  10542.     I/O
  10543.     limits, floppy disk
  10544.     locking
  10545.     protection
  10546.     seek position
  10547.     sharing
  10548.     system
  10549.  File Allocation Table
  10550.  filenames, OS/2 and MS-DOS
  10551.  files
  10552.     .DLL
  10553.     .EXE
  10554.     linking
  10555.     naming
  10556.     .OBJ
  10557.  file system
  10558.     future of
  10559.     hierarchical
  10560.     installable
  10561.  file system name space
  10562.     named pipes and
  10563.  file utilization
  10564.  FIND utility program
  10565.  flags register
  10566.  flat model, 80386 processor
  10567.  flexibility, maximum
  10568.  flush operation, buffer
  10569.  forced events
  10570.  foreground
  10571.     category
  10572.     processing
  10573.     threads and applications
  10574.  fprintf
  10575.  fragmentation
  10576.     disk
  10577.     internal
  10578.  functions
  10579.     addition of
  10580.     resident
  10581.  future versions of OS/2
  10582.  
  10583.  
  10584.  G
  10585.  ──────────────────────────────────────────────────────────────────────
  10586.  garbage collection
  10587.  garbage handles
  10588.  general failure error
  10589.  general priority category
  10590.  general protection fault (GP fault)
  10591.     errors
  10592.  GetDosVar
  10593.  giveaway shared memory
  10594.  global data
  10595.  global data segments
  10596.  Global Descriptor Table (GDT)
  10597.  global subsystem initialization
  10598.  glossary
  10599.  goals
  10600.     design
  10601.     OS/2
  10602.  GP faults
  10603.  grandparent processes
  10604.  graphical user interface
  10605.     standard
  10606.  graphics, VIO and
  10607.  graphics devices
  10608.  graphics drivers, device-independent
  10609.  
  10610.  
  10611.  H
  10612.  ──────────────────────────────────────────────────────────────────────
  10613.  handles
  10614.     closing
  10615.     closing with dynamic links
  10616.     duplicated and inherited
  10617.     garbage
  10618.     semaphore
  10619.  hard error daemon
  10620.  hard errors
  10621.     handling in real mode
  10622.  hardware, nonstandard
  10623.  hardware devices. See devices
  10624.  hardware interrupts
  10625.     device drivers and
  10626.  hardware-specific interfaces
  10627.  Hayes modems
  10628.  heap algorithm
  10629.  heap objects
  10630.  Hercules Graphics Card
  10631.  hierarchical file system
  10632.  hooking the keyboard vector
  10633.  huge memory
  10634.  huge model addressing
  10635.  huge segments
  10636.  
  10637.  
  10638.  I
  10639.  ──────────────────────────────────────────────────────────────────────
  10640.  IBM PC/AT
  10641.  INCLUDE
  10642.  Independent Software Vendors (ISVs)
  10643.  industrial revolution, second
  10644.  infosegs
  10645.  inheritance
  10646.  INIT
  10647.  initialization
  10648.     device driver
  10649.     global subsystem
  10650.     instance subsystem
  10651.  initialization entry points, dynamic link
  10652.  input/output. See I/O
  10653.  installable file system (IFS)
  10654.  instance data
  10655.     segment
  10656.  instance subsystem initialization
  10657.  instructions
  10658.     sequence of
  10659.  insufficient memory
  10660.  INT 21
  10661.  INT 2F multiplex function
  10662.  integrated applications
  10663.  integrity, data
  10664.  Intel. See also 8086/8088 processor, 80286 processor, 80386 processor
  10665.     80286 processor
  10666.     80386 processor
  10667.     8080 processor
  10668.     8086/8088 processor
  10669.  interactive
  10670.     category
  10671.     processes
  10672.     programs
  10673.  interface, user
  10674.  interlocking
  10675.  internal fragmentation
  10676.  interprocess communication (IPC)
  10677.  interrupt handling code
  10678.  interruptible block
  10679.  interrupts
  10680.     3x box emulation
  10681.     hardware
  10682.     signals and
  10683.  interrupt service routine, device driver
  10684.  interrupt-time thread
  10685.  interrupt vectors
  10686.     hooking
  10687.  I/O
  10688.     architecture
  10689.     asynchronous
  10690.     background
  10691.     disk requests
  10692.     efficiency
  10693.     file
  10694.     port access
  10695.     privilege mechanism
  10696.     requesting an operation
  10697.     signals and
  10698.     video
  10699.  I/O-bound applications
  10700.  IOCTL call
  10701.  IP register
  10702.  IPC See also interprocess communication
  10703.     combining forms of
  10704.     using with dynamic links
  10705.  IRET instruction
  10706.  
  10707.  
  10708.  K
  10709.  ──────────────────────────────────────────────────────────────────────
  10710.  KBD
  10711.  kernel
  10712.     interfacing with dynamic links
  10713.     mode
  10714.  keyboard, processes using the
  10715.  keyboard mode, cooked and raw
  10716.  keyboard vector, hooking the
  10717.  
  10718.  
  10719.  L
  10720.  ──────────────────────────────────────────────────────────────────────
  10721.  label names, volume
  10722.  large disk support
  10723.  LAR instruction
  10724.  latency, rotational
  10725.  Least Recently Used (LRU) scheme
  10726.  levels, compatibility
  10727.  LIB
  10728.  libraries
  10729.     dynamic link
  10730.     sharing dynamic link
  10731.     subroutine and runtime
  10732.  linear address space
  10733.  linking
  10734.     dynamic
  10735.     static
  10736.  loadtime dynamic linking
  10737.  local area networks. See networks
  10738.  Local Descriptor Table (LDT)
  10739.  locality of reference
  10740.  localization of errors
  10741.  local variables
  10742.  locking, file. See file and record locking
  10743.  logical
  10744.     device and directory name facility
  10745.     devices
  10746.     directories
  10747.     disks, partitioning
  10748.  low priority category
  10749.  LSL instruction
  10750.  
  10751.  
  10752.  M
  10753.  ──────────────────────────────────────────────────────────────────────
  10754.  magic cookie
  10755.  mass marketing, software and
  10756.  media volume management
  10757.  memory
  10758.     display
  10759.     huge
  10760.     insufficient
  10761.     layout of system
  10762.     limitations of in 8088 processor
  10763.     named shared
  10764.     physical
  10765.     protection
  10766.     shared
  10767.     shared giveaway
  10768.     swapping
  10769.     swapping segments in
  10770.     swapped-out
  10771.     utilization
  10772.     video
  10773.     virtual
  10774.  memory management
  10775.     API
  10776.     hardware
  10777.  memory manager
  10778.     definition of
  10779.  memory not present fault
  10780.  memory objects, tracking
  10781.  memory overcommit
  10782.  memory segments, allocating
  10783.  memory suballocation
  10784.  memory unit
  10785.  mental work, mechanizing
  10786.  message mode
  10787.  Microsoft, vision of
  10788.  Microsoft Macro Assembler (MASM)
  10789.  model, architectural
  10790.  modes
  10791.     incompatible
  10792.     keyboard
  10793.  modular tools
  10794.  module name
  10795.  monitors, device
  10796.  MOU
  10797.  MS-DOS
  10798.     compatibility with
  10799.     environment list
  10800.     expandability of
  10801.     filenames in
  10802.     history of
  10803.     memory allocation in
  10804.     running applications in OS/2
  10805.     version 1.0
  10806.     version 2.0
  10807.     version 3.0
  10808.     version 4.0
  10809.     version 5.0
  10810.     versions of
  10811.  multitasking
  10812.     data integrity and
  10813.     definition of
  10814.     device drivers and
  10815.     full
  10816.     MS-DOS version
  10817.     parallel
  10818.     serial
  10819.  multiuser systems, thrashing and
  10820.  
  10821.  
  10822.  N
  10823.  ──────────────────────────────────────────────────────────────────────
  10824.  named pipes
  10825.     multiple instances of
  10826.  named shared memory
  10827.  name generation, compatibility and
  10828.  names, dynamic link
  10829.  name set
  10830.  network piggybacking
  10831.  networks
  10832.     access to
  10833.     compatibility on
  10834.     file protection on
  10835.     importance of security on
  10836.  network virtual circuit
  10837.  NUMADD program
  10838.  NUMARITH application
  10839.  
  10840.  
  10841.  O
  10842.  ──────────────────────────────────────────────────────────────────────
  10843.  .OBJ files
  10844.  obj namebuf
  10845.  object name buffer
  10846.  objects
  10847.     defining
  10848.     file system name space and
  10849.  office automation
  10850.     objective of
  10851.     operating system for
  10852.  offset registers
  10853.  OPEN function
  10854.  open system
  10855.  operating systems
  10856.     multitasking
  10857.     other
  10858.  ordinals, entry
  10859.  orphaned semaphores
  10860.  overlays, code
  10861.  
  10862.  
  10863.  P
  10864.  ──────────────────────────────────────────────────────────────────────
  10865.  packet, request
  10866.  paged virtual memory
  10867.  paperless office
  10868.  parallel multitasking
  10869.  parent processes
  10870.  partitioning, extended
  10871.  passwords, file
  10872.  Paterson, Tim
  10873.  PATH
  10874.  pathnames
  10875.  peripherals, high-bandwidth
  10876.  permissions
  10877.  physical memory
  10878.  PID
  10879.  piggybacking
  10880.  pipes
  10881.     anonymous
  10882.     named
  10883.  pointer, seek
  10884.  preemptive scheduler
  10885.  presentation manager
  10886.     choosing between VIO and
  10887.     DDE and
  10888.  priority
  10889.     categories of
  10890.     time-critical
  10891.  priority scheduler
  10892.     semaphore
  10893.  privilege mechanism, I/O
  10894.  privilege mode
  10895.  privilege transition
  10896.  process termination signal handler
  10897.  processes
  10898.     calling from OS/2
  10899.     child
  10900.     client
  10901.     command
  10902.     daemon
  10903.     dynamic linking and
  10904.     foreground and background
  10905.     grandparent
  10906.     interactive
  10907.     interfacing with dynamic links
  10908.     parent
  10909.     problems with multiple
  10910.     threads and
  10911.  processing
  10912.     asynchronous
  10913.     foreground and background
  10914.  processing unit
  10915.  process tree
  10916.  programming model, device drivers
  10917.  programs
  10918.     debugger
  10919.     executing
  10920.     interactive
  10921.     running on OS/2
  10922.  program termination signal
  10923.  PROMPT
  10924.  protected environment
  10925.  protection
  10926.     file
  10927.     memory
  10928.     model
  10929.     side-effects
  10930.  protect mode
  10931.     huge model addressing in
  10932.     real mode versus
  10933.  protocol, DDE
  10934.  pseudo interrupts
  10935.  Ptrace
  10936.  pure segment
  10937.  
  10938.  
  10939.  Q, R
  10940.  ──────────────────────────────────────────────────────────────────────
  10941.  queues
  10942.  RAM See also memory
  10943.     available
  10944.     semaphores
  10945.  Random Access Memory. See RAM
  10946.  random selector values
  10947.  RAS information
  10948.  raw mode
  10949.  real mode
  10950.     80386 processor
  10951.     8086 processor
  10952.     applications
  10953.     compatibility box
  10954.     device drivers in
  10955.     hard errors in
  10956.     huge model addressing in
  10957.     protect mode versus
  10958.     screen group
  10959.     swapping in
  10960.  real time, tracking passage of
  10961.  record locking
  10962.  redirector code (MS-DOS)
  10963.  reference locality
  10964.  registers
  10965.     offset
  10966.     segment
  10967.     signals and
  10968.  Reliability, Availability, and Serviceability (RAS)
  10969.  religion, OS/2
  10970.  remote procedure call
  10971.  request packet
  10972.  resident functions
  10973.  resources, manipulating
  10974.  return()
  10975.  ring 3
  10976.  ring transition
  10977.  ROM BIOS
  10978.  root directory
  10979.  rotational latency
  10980.  runtime dynamic linking
  10981.  runtime library
  10982.  
  10983.  
  10984.  S
  10985.  ──────────────────────────────────────────────────────────────────────
  10986.  scheduler
  10987.     preemptive
  10988.     priority
  10989.     semaphore
  10990.  SCP-DOS
  10991.  screen device, handle for
  10992.  screen groups
  10993.     multiple
  10994.     using in 3x box
  10995.  screen images, manipulating
  10996.  screen-save operation
  10997.  screen switch
  10998.  screen updates, requirements for
  10999.  Seattle Computer Products
  11000.  sector aligned calls
  11001.  sectors
  11002.     blocks of
  11003.  security, dynamic link
  11004.  seek pointer
  11005.  segment arithmetic
  11006.  segment fault
  11007.  segments
  11008.     80286 architecture
  11009.     80386 architecture
  11010.     data
  11011.     dynamic link
  11012.     executing from data
  11013.     global data
  11014.     huge
  11015.     internally shared
  11016.     nonswappable
  11017.     pure
  11018.     sharing
  11019.     stack
  11020.     status and information
  11021.  segment selectors
  11022.  segment swapping
  11023.  semaphore handles
  11024.  semaphores
  11025.     data integrity and
  11026.     file system name space and
  11027.     orphaned
  11028.     RAM
  11029.     recovering
  11030.     scheduling
  11031.     system
  11032.  serial multitasking
  11033.  session manager
  11034.  SetSignalHandler
  11035.  shared memory
  11036.     giveaway
  11037.     named
  11038.  sharing segments
  11039.  side effects
  11040.     controlling
  11041.     dynamic link
  11042.     protection
  11043.  signal handler address
  11044.  signal handlers
  11045.  signaling
  11046.  signals
  11047.     holding
  11048.  SIGTERM signal
  11049.  single-tasking
  11050.  single-tasking environment, containing errors in
  11051.  software design, OS/2 concepts of
  11052.  software tools approach
  11053.  speed execution
  11054.  stable environment
  11055.  stack frames
  11056.  stacks, thread
  11057.  stack segments
  11058.  standard error. See STDERR
  11059.  standard file handles
  11060.  standard input. See STDIN
  11061.  standard output. See STDOUT
  11062.  standards, software
  11063.  static data segments, adding
  11064.  static links
  11065.  status and information, segment
  11066.  STDERR
  11067.  STDIN
  11068.     compatibility with presentation manager
  11069.  STDIN/STDOUT mechanism, I/O
  11070.  STDOUT
  11071.     compatibility with presentation manager
  11072.  strings, environment
  11073.  suballocation, memory
  11074.  subroutine library
  11075.  subroutines, dynamic link packages as
  11076.  subsystems
  11077.     dynamic link
  11078.     special support
  11079.  subtask model
  11080.  subtrees, command
  11081.     controlling
  11082.  swapped-out memory
  11083.  SWAPPER.DAT
  11084.  swapping
  11085.     segment
  11086.  system
  11087.     conventions
  11088.     crashes
  11089.     diagnosis
  11090.     File Table (SFT)
  11091.     memory layout
  11092.     security, debuggers and
  11093.     semaphores
  11094.  system, hanging the
  11095.  systems
  11096.     closed and open
  11097.     file
  11098.  
  11099.  
  11100.  T
  11101.  ──────────────────────────────────────────────────────────────────────
  11102.  task See also processes
  11103.  task-time thread
  11104.  technological breakthroughs
  11105.  TEMP
  11106.  terminate and stay resident mechanism
  11107.  thrashing
  11108.  thread 1
  11109.  thread death
  11110.  thread of execution
  11111.  thread ID
  11112.  threads
  11113.     background
  11114.     captive
  11115.     collisions of
  11116.     dynamic linking and
  11117.     foreground and background processing with
  11118.     foreground and interactive
  11119.     from different processes
  11120.     I/O-bound
  11121.     interrupt-time
  11122.     low priority
  11123.     multiple
  11124.     organizing programs using
  11125.     performance characteristics of
  11126.     priority categories of
  11127.     problems with multiple
  11128.     switching the CPU among
  11129.     task-time
  11130.     working sets and
  11131.  thread stacks
  11132.  throughput balancing
  11133.  time-critical priority category
  11134.  time intervals
  11135.  timer services
  11136.  time slice
  11137.  timing, thread
  11138.  tools, software
  11139.  TOPS-10
  11140.  TREE
  11141.  tree, process
  11142.  tree-structured file system
  11143.  Trojan programs
  11144.  
  11145.  
  11146.  U
  11147.  ──────────────────────────────────────────────────────────────────────
  11148.  UNIX
  11149.     naming conventions in
  11150.     ptrace facility
  11151.  upper- and lowercase
  11152.  upward compatibility
  11153.  user interface
  11154.     graphical
  11155.     presentation manager
  11156.     VIO
  11157.  utilization
  11158.     file
  11159.     memory
  11160.  
  11161.  
  11162.  V
  11163.  ──────────────────────────────────────────────────────────────────────
  11164.  variables, local
  11165.  versions, future OS/2
  11166.  video hardware, accessing
  11167.  VIO,
  11168.     choosing between presentation manager and
  11169.     dynamic link entry points
  11170.     graphics under
  11171.     subsystem
  11172.     user interface
  11173.  VioGetBuf
  11174.  VioModeWait
  11175.  VioSavRedrawWait
  11176.  VioScrLock
  11177.  virtual circuit, network
  11178.  virtual display device
  11179.  virtualization
  11180.     device
  11181.  virtualizing the environment
  11182.  virtual memory
  11183.     concepts of
  11184.     paged
  11185.  virtual real mode (80386)
  11186.  volume, disk
  11187.  volume ID
  11188.  volume-oriented operations
  11189.  Von Neumann, John
  11190.  
  11191.  
  11192.  W
  11193.  ──────────────────────────────────────────────────────────────────────
  11194.  windowing interface
  11195.  working directories
  11196.  working set
  11197.  WORM drives
  11198.  writethrough
  11199.  WYSIWYG
  11200.  
  11201.  
  11202.  X
  11203.  ──────────────────────────────────────────────────────────────────────
  11204.  XENIX
  11205.  
  11206.  
  11207.  Z
  11208.  ──────────────────────────────────────────────────────────────────────
  11209.  zero-terminated strings
  11210.