Interact

The HP 3000--for Complete Novices, Part 17: Using Intrinsics

 

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:

  1. It will try to repair the corruption (so that the game can continue, i.e., so that the system can stay up).
  2. 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 http://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.