|
by George Stachnik
The last several articles
in this series have focussed on files and how to access them. We've seen
how to use industry standard COBOL programming verbs such as OPEN, READ,
WRITE, and CLOSE to access data stored in ordinary files. And we closed
the last article by explaining how to open a file in a much more proprietary
way, using a special MPE/iX interface called an intrinsic. This month
we're going to explore the subject of HP 3000 intrinsics in greater detail.
Application Programs
Application programs are complex beasts made up of numerous parts. They
perform a variety of tasks including reading data from input files, writing
data to output files, organizing data into tables, performing calculations,
sorting, editing, and so forth. These tasks can be thought of as being divided
into two groups: user tasks and system tasks.
- User tasks are things that an application program does by itself, with
little or no assistance or interaction from the operating system.
- System tasks are those things that an application doesn't do on its
own. Instead, each application has the operating system do the work on
its behalf.
Some examples may help to make this distinction clearer. When a COBOL
application program adds two numbers together, the calculation is performed
by machine instructions that reside entirely inside the application program
itself. We'll refer to these machine instructions as user code and
to the things they do as user tasks. User code is generated by the
COBOL compiler when the application program is created. It resides in an
application program file.
The performance of simple calculations is a user task because
the operating system doesn't get involved in it, nor does it need to. The
application program can handle it on its own without any risk to the system
or its resources.
On the other hand, many of the tasks application programs perform require
a lot of involvement and help from the operating system. These system tasks
are performed by instructions that reside inside the operating system. This
is system code, which does not reside inside any application program
file. System code is part of the operating system and generally resides
in a system library such as NL.PUB.SYS. System tasks usually involve a system
resource, such as a file, a system table, or a network connection. These
system resources are critical to the operation of the system, and so they
must be protected from application errors. Toward this end, MPE/iX acts
as a middleman between the application programs and the system resources
to ensure that they are uncorrupted and available to authorized users.
It's instructive to compare this way of doing things with earlier operating
systems that date back before MPE and the HP 3000. In the "good old
days," when an application program wanted to print a report, the operating
system would give control of the system's printer to the application, which
took possession of and wrote directly to the printer. This approach worked
fine until some other application wanted to use the printer. The second
application would simply have to wait until the first application had finished
with it. If this wasn't bad enough, applications that were holding system
resources (such as printers) sometimes encountered an error and aborted.
This scenario could easily "hang" the device. The operating system
thought that the device was being used by the application, which had aborted.
This left the printer unavailable to other applications on the system. Typically,
you'd have to reboot the system before any other application could use the
device again.
Early operating system designers solved this problem by introducing a
new component called a spooler. "SPOOL" was originally an acronym,
standing for "Simultaneous Peripheral Operations OnLine." The
idea of the spooler was to isolate system resources (in this case, the printer)
from application programs, and allow multiple applications to create reports
at the same time. In this way, the system's printing resources could be
protected from errors in the applications.
The designers of the HP 3000's operating system included a spooler in
MPE. But they also extended this principle to other parts of the operating
system. They tried to isolate the HP 3000 from virtually any potential
application program error, not just those involving a printer.
Ordinary application programs were forbidden to interact directly with
system resources. Instead, in order for an application to use a system resource,
it had to use the operating system as a middleman. The operating system
alone controlled access to files, devices, network connections, internal
data structures (including system tables), and just about anything else
that might be shared among multiple users or multiple applications. If an
application program aborted, the operating system would ensure that the
error was handled gracefully, without leaving any system resources in an
unknown or unusable state.
Next we're going to see how the designers of MPE/iX implemented this
system. To understand this, we'll first have to take a closer look at how
programs are built on the HP 3000.
Procedures
In HP 3000 terminology, all software programs are built from building
blocks called procedures. A procedure is a self-contained piece of
code that can be called by other procedures. Each procedure has a
name that is used to call it. When a procedure is called by its name, the
calling procedure waits while the called procedure executes
on its behalf. Called procedures can call other procedures in turn, which
may then call still other procedures. When a called procedure is finished,
it returns control to its calling procedure, which picks up from the point
immediately following the call.
Callable procedures are a very common programming concept found on virtually
all computer platforms. If you learned about programming on some platform
other than the HP 3000, you may have learned slightly different terminology
but the concept is the same. The terms routines, subroutines, or
modules have all been used at one time or another to describe things
that are conceptually identical to MPE's callable procedures.
MPE/iX is itself a program--albeit a very large and complex one--and
is therefore made up of procedures. Each procedure within MPE/iX has a specific
job that it was designed to do. There are procedures in MPE/iX that open
files, procedures that manage memory, procedures that manage system tables,
and procedures to perform each and every task handled by MPE/iX.
For example, suppose you want to write an application program that is
going to open a file on an HP 3000. Your application program would call
a procedure inside of MPE/iX that specializes in opening files. We saw last
time that FOPEN is such a procedure. When you call FOPEN, you tell it what
file you want to open by passing the formal file designator to the procedure
as a parameter. A parameter is a data item that is defined by the
calling procedure, and made available to the called procedure.
Now that you understand how application programs interact with MPE/iX
and its procedures, it may surprise you to learn that the vast majority
of procedures inside MPE/iX are uncallable.
Uncallable Procedures
Most of the procedures that make up the MPE/iX operating system can be
called only by other procedures that are also part of the operating system
itself. They cannot be called by external procedures such as those in your
application programs. For this reason, they are referred to as uncallable
procedures. You might be wondering what the point is of having uncallable
procedures. An example will help to explain.
There is a procedure inside MPE/iX named
"SYSTEM_ABORT." This procedure has a specialized job--it crashes
the system. At first glance, it may seem strange to have a procedure for
this purpose. Think of the hundreds of procedures that make up MPE/iX as
a team of football players all involved in playing a very complex game of
football (either American football or European football--it doesn't matter).
The field this game is played on consists of the tables and data structures
that MPE/iX uses to represent the state of your system and its resources
at any given moment. MPE/iX uses these tables to keep track of who's logged
on, which files are open, and where to find the files, tables, and data
structures on the system.
As long as the information in these tables reflects the true state of
the system, the game can continue. But suppose one of the "players"
(i.e., one of the
procedures that make up MPE/iX) discovers that that a table or data structure
no longer reflects the true state of the system. It's as if a football player
discovered a huge pit had been dug in the middle of the field. If the game
is allowed to continue, somebody will eventually fall into the pit, almost
certainly resulting in a serious injury. Surely the player would want to
call a timeout--or stop the game entirely.
Similarly, corrupt system tables are very dangerous to your HP 3000 and
its applications and user data. They can cause application programs to abort,
or worse, they can cause them to spread corruption to other files, tables,
and data structures. Valuable user data could easily become corrupt. In
order to prevent this, the procedures that make up the MPE/iX operating
system are constantly on the lookout for corruption in the tables and other
internals of MPE/iX. If one of them finds a corrupt table, it will do one
of two things:
- It will try to repair the corruption (so that the game can continue,
i.e., so that the system can stay up).
- If it cannot repair the damage, it will stop the game. That is, it
will call the SYSTEM_ABORT procedure. SYSTEM_ABORT will display a message
on the console that explains what sort of corruption or error occurred.
It then halts the system dead in its tracks in order to prevent the corruption
from spreading or becoming worse.
Clearly, you would not want application programs to be able to call a
procedure like SYSTEM_ABORT, because crashing the system has a devastating
impact on all the applications running on the entire system. For
this reason, SYSTEM_ABORT is an uncallable procedure (at least so
far as application programs are concerned). The only procedures that can
call uncallable procedures such as SYSTEM_ABORT are trusted procedures,
i.e., those that are part of MPE/iX itself.
Most of the procedures that make up MPE/iX are uncallable. Even the ones
that do not perform drastic actions (such as crashing the system) can be
dangerous if the parameters that are passed to them are invalid. Each uncallable
procedure in MPE/iX trusts its calling procedures to pass only parameters
that are legal and valid.
The designers of MPE/iX realized that when they allowed a procedure inside
MPE/iX to be called by an untrusted calling procedure (such as one in an
application program), the called procedure must be ready for anything. A
user-callable procedure cannot assume anything about the parameters that
are passed to it. They could be invalid, or totally nonsensical. Therefore,
every user-callable procedure in MPE/iX begins by putting the parameters
that were passed to it through a rigorous editing process. The objective
is to ensure that invalid parameters won't cause the procedure to do anything
that could be dangerous to the application or (especially) to the system.
It's worth noting that this safety feature of MPE/iX is not something that
all operating systems have. On many operating systems, system crashes happen
when a user program calls a procedure inside the operating system, and passes
it invalid parameters.
MPE/iX contains a comparatively small number
of user-callable procedures: the intrinsics. Application programs
call intrinsics in order to tell the operating system what they want it
to do on their behalf. For example, whenever an application accesses a file,
runs a program, prints a report, displays data on a terminal, or does anything
else that requires the operating system's assistance, it undertakes this
system task by calling an MPE/iX intrinsic. Intrinsics are similar, conceptually
speaking, to UNIX system calls or the Application Program Interfaces
(APIs) found in Microsoft Windows.
If you plan to write programs that will call intrinsics, you'll find
a great resource on the Web at //www.docs.hp.com/.
This HP Web site contains a treasure trove of technical documentation
about the HP 3000 (as well as many other HP computer products). Follow the
pointers from "MPE/iX Operating System" to "MPE/iX Development
Tools Documentation" to the MPE/iX Intrinsics Reference Manual.
(Incidentally, the Customer Order Number for this manual is 32650-90028,
if you'd prefer a paper copy.) Part three of this manual, entitled "Intrinsic
Tasks," contains a table showing each and every intrinsic, and its
purpose. Part four gives the details, including instructions on calling
the intrinsics, and what parameters need to be passed.
Calling Intrinsics
When an application program calls an intrinsic, it uses the same mechanism
that is used by programmers to call user-written procedures (subroutines).
In the COBOL language, this mechanism is invoked by the CALL verb. For example,
here's a line of industry standard COBOL that would be used to call a user-written
subroutine called LOADMT:
CALL "LOADMT" USING YEAR-INDEX
NUM-OF-MONTHS
MONTH-TOTALS
This example passes three parameters to the called procedure.
In COBOL, these parameters are defined in the data division using ordinary
COBOL syntax. For example, the data items YEAR-INDEX, NUM-OF-MONTHS, and
MONTH-TOTALS might be defined as follows:
01 YEAR-INDEX PIC 9(4) COMP.
01 NUM-OF-MONTHS PIC 9(4) COMP.
01 MONTH-TOTALS VALUE ZEROS.
05 MT-TABLE PIC 9(6)V99
OCCURS 60 TIMES.
This sample COBOL code is fully compliant with ANSII standards: it would
work the same way on an HP 3000, an IBM mainframe, or any other platform
that supports ANSII standard COBOL.
In order to call an MPE/iX intrinsic from a COBOL program, you would
use COBOL code that is very similar to the ANSII standard code shown above.
On the HP 3000, if you know how to call a subroutine, then you also know
how to call an intrinsic. For example, there is an MPE/iX intrinsic called
HPCICOMMAND that application programs can use to cause MPE commands to be
executed. The following example shows how to call the HPCICOMMAND intrinsic.
The program's procedure division would include a statement such as the following:
CALL INTRINSIC "HPCICOMMAND" USING COMMAND,
ERROR-VALUE,
ERROR-PARM,
MSG-LEVEL.
The only difference between this code and the ANSII standard example
that we saw a moment ago is the addition of the keyword INTRINSIC. And even
this keyword is optional. It is used to tip off the system that the procedure
HPCICOMMAND is located in a system library--not in a user library. If you
omit it, both libraries are searched. It's also useful to tip off maintenance
programmers that HPCICOMMAND is not a user-written subroutine, but rather
a system call.
The parameters being passed to the HPCICOMMAND intrinsic are all ordinary
COBOL data items, which could be defined in the program's WORKING STORAGE
section as follows:
01 COMMAND.
05 COMMAND-STRING PIC X(20) VALUE "LISTF,2".
05 FILLER PIC X VALUE %15.
01 ERROR-VALUE PIC S9(4) COMP.
01 ERROR-PARM PIC S9(4) COMP.
01 MSG-LEVEL PIC S9(4) COMP VALUE 0.
Each MPE intrinsic is called as if it were a user-written subroutine.
But intrinsics have three key characteristics that differentiate them from
subroutines written by users:
- As we've seen, subroutines are written by the same programmers who
wrote the programs that call them. By contrast, the intrinsics were created
by Hewlett-Packard. They are a fundamental part of MPE/iX, and are bundled
with every HP 3000.
- When an application program begins executing, it is running in MPE/iX's
User Mode. User Mode places restrictions on programs, preventing
them from calling uncallable procedures or doing anything else that could
compromise the integrity of the system or even crash it.
- When an application program calls an MPE/iX intrinsic, the intrinsic
places itself in MPE/iX's "privileged mode." Privileged
mode lifts the restrictions of user mode. This enables the intrinsics to
call MPE/iX's uncallable procedures and interact with MPE/iX's internal
tables and other data structures directly.
Privileged Mode and User Mode
The concept of privileged mode is one of the key reasons for the
HP 3000's legendary reputation for reliability. On early operating systems,
all programs were created equal. That is, an application program could access
the system's resources, including its internal tables, directly, in exactly
the same way as the operating system did. Operating system designers learned
that this scenario was an invitation to disaster. Application software developers
often tried to improve the performance and functionality of their programs
by interacting with the system internals directly. But these improvements
in performance or functionality came at a high price. The moment an application
program became "aware" of system internal data structures or tables,
it was tied to those structures. If the structures changed (as often happens
when new releases of the operating system are installed), then the application
programs had to change too.
This is why updating to a new version of the operating system often means
updating your applications as well. Applications that worked on release
X generally don't work on release Y because applications are interacting
directly with parts of the operating system that changed between release
X and release Y. If the applications aren't updated, they begin behaving
like loose cannons--looking for system tables in the wrong places--and sometimes
corrupting the data structures that they find.
When you update your HP 3000 to a new release of MPE/iX, it's always
a good idea to validate the applications to ensure that they still work
on the new release. In most cases, they will. Experienced IT managers have
learned to be very wary of application programs that access system internal
data structures directly. They demand that MPE/iX place restrictions on
HP 3000 applications, to prevent them from doing anything that could foul
up the system. This is what led to the development of the intrinsics. Application
programs running in user mode can interact with the operating system only
by invoking intrinsics.
This, at least, is the rule. And like all rules, it has exceptions. One
exception is the community of programmers who develop programs not for their
own use, but for sale. These independent software vendors, or ISVs, insisted
that the functionality provided by the intrinsics wasn't enough to meet
their needs. User mode might be OK for the kind of plain vanilla applications
that students write in college. But if they were going to compete with other
ISVs in the real world, they needed to be able to exploit the system for
all it was worth. They needed to be able to write software that got the
most bang for their customers' hardware buck. And to do that, they needed
the same degree of access to the hardware as the operating system.
When MPE was born in the early 1970s, commercial operating systems were
expected to provide a means of lowering the defenses provided by user mode.
The key to the kingdom was (and is) an intrinsic called GETPRIVMODE. By
calling this intrinsic, a program can elevate itself to the same godlike
status as MPE/iX itself. This is both a good thing and a bad thing. It's
good because a procedure that has called GETPRIVMODE can access any of MPE's
internal data structures, making it a very powerful piece of software. It's
bad because this procedure is also capable of bypassing MPE's internal safeguards
and security features, which makes it a very dangerous piece of software.
Managing Privileged Mode Apps
If your HP 3000 is being used to run privileged mode applications (or
even if it isn't), it's important that you monitor and control access to
privileged mode. Fortunately, the designers of MPE made it easy for the
system administrator to achieve this goal.
The rules are simple:
- No program can call GETPRIVMODE unless it has been linked (or prepped)
with PM capability.
- A programmer cannot link (or prep) a program with PM capability unless
his logon userid has been given PM capability.
- A userid cannot have PM capability unless the account in which it resides
also has PM capability.
Thus, by carefully controlling which users and accounts have PM capability,
system managers of development systems can exercise controls over the activities
of their programmers, and thus prevent the development of unauthorized privileged
mode software. Of course, there's still the danger of imported software.
That is, a programmer who has PM capability on one system (say, at a university)
might develop a privileged mode program and then copy it (via a tape, floppy
disk, or even through the Internet) to another HP 3000 system. Fortunately,
the designers of MPE thought of that.
- A program that has been linked (or prepped) with PM capability cannot
be executed on an HP 3000 unless it resides in a group with PM capability.
- Groups with PM capability can reside only in accounts with PM capability.
This means that if a programmer should acquire a piece of "hacker
software" that uses privileged mode, he will be not be able to execute
it on your system unless he also has access to a privileged mode group and
account. So once again, the system managers have the tools necessary to
protect themselves from this kind of activity.
Hewlett-Packard does not support the use of privileged mode on the HP
3000, so you shouldn't use it without a clear understanding of what you're
getting yourself into. Deciding to use privileged mode is like deciding
to drive 90 mph on a road where the speed limit is only 55 mph. Maybe you'll
get away with it. Maybe you won't. If you do, you could get to your destination
more quickly, as long as your trip goes as planned. But if something goes
wrong, the consequences could be very severe indeed. When a user mode program
encounters an error, it will simply abort. But when a privileged mode program
encounters an error, it could abort, corrupt user data, corrupt system data,
or cause the whole system to crash.
Before you decide to take advantage of privileged mode, be sure that
you cannot achieve your programming goal using supported mechanisms such
as the intrinsics.
Architected Interfaces
If you find that the intrinsics do not allow you to do what you want
to do, there is one more option to consider. There is a set of internal
MPE/iX APIs called the Architected Interfaces that occupy a sort
of grey area between uncallable procedures and the intrinsics.
The Architected Interfaces (or AIFs) are a set of routines that, like
the intrinsics, are internal to MPE/iX and can be called by application
programs. The AIFs make it possible for application programs to do a lot
of things that cannot be done through the intrinsic interface alone. They
provide access to system tables and file structures in ways that are beyond
the intrinsics. Unlike the intrinsics, they are not available to ordinary
application programs. To use an Architected Interface, the calling program
must first be in privileged mode--that is, it must be LINKed with PM capability
and call the GETPRIVMODE intrinsic.
Earlier I made the statement that the use of privileged mode is not supported
by HP. The AIFs are one of the exceptions to that rule. Since the AIFs are
a Hewlett-Packard product, HP supports their use. But it's important to
recognize that the AIFs should be used only when intrinsics simply cannot
do the job. In most cases, the MPE/iX intrinsics are the best tools for
the application programmer (especially the beginner) who wants his programs
to interact with MPE/iX. They should always be your first choice.
Invoking Intrinsics
Applications can invoke intrinsics in two ways: explicitly or
implicitly. Explicit intrinsic access happens when the application
program source code contains an explicit reference to the intrinsic. We
saw examples of invoking the HPCICOMMAND intrinsic earlier in this article.
Most applications invoke the intrinsics implicitly. Implicit intrinsic
access happens when there is no explicit reference to an intrinsic in the
application source code. Instead the HP 3000's compiler generates the intrinsic
calls automatically. For example, the standard COBOL verbs OPEN, CLOSE,
READ, and WRITE are translated by the HP 3000 compilers into calls to MPE/iX
intrinsics such as FOPEN (or HPFOPEN), FREAD, FWRITE, and FCLOSE.
Here's a bit of sample COBOL code. This is industry standard COBOL source
code that opens a file using the name DATA-IN.
OPEN INPUT DATA-IN.
The line of code shown above would be exactly the same, regardless of
what platform the program was compiled on (or for). It would appear in the
program's PROCEDURE division. The input file that the sample code shown
above references would be defined in the COBOL program's INPUT-OUTPUT section
as shown below:
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT DATA-IN ASSIGN TO "WKHRDATA".
This code is entirely compliant with industry standards. If it is executed
on the HP 3000, however, it invokes a number of proprietary internal mechanisms.
A procedure call (PCAL) is generated by the compiler, which is used to call
the FOPEN intrinsic, passing it the formal file designator (in this case
WKHRDATA). But the point is that the programmer who wrote this code did
not need to be aware of any of these mechanisms. All the programmer needed
to know was the same COBOL language that he needed to know on other ANSII
standard COBOL platforms, because the proprietary FOPEN intrinsic was invoked
implicitly.
There are a number of advantages to the use of implicit intrinsic calls.
Implicit calls are necessary if you want to write programs that are compliant
with industry standards. Some people think that industry standards only
apply to so-called "open" systems such as UNIX or NT, but in fact
they are at least as important to proprietary systems, if not more so.
A program that is written to conform to industry standards can be ported
from one platform to another with a lot less effort than it would take to
port a similar program that does not conform. This means that software developers
who observe industry standards can leverage their work across many platforms,
while programmers who ignore them are doomed to write code that runs on
only one platform. Programmers who learned COBOL on a non-HP platform such
as an IBM mainframe can be productive on an HP 3000 with very little retraining,
if the applications that they're working on conform
to the ANSII standards for COBOL. IBM's COBOL compilers and HP's COBOL compilers
both comply with ANSII standards for the language. This means an application
that is written entirely in ANSII standard COBOL can be compiled and executed
successfully on an HP 3000, an IBM mainframe, or on any other platform that
supports an ANSII standard COBOL compiler.
Having said that, you may be surprised that very few (if any) commercial
applications are written entirely in ANSII standard COBOL. This is
because most commercial applications take advantage of non-standard features
of the platforms that they run on. Next month, we'll begin taking a hard
look at one of these features: IMAGE/SQL.
George Stachnik works in technical training in HP's
Network Server Division.
|