HP OpenVMS Systems Documentation

Content starts here

OpenVMS Programming Concepts Manual


Previous Contents Index

23.4.2 Function Modifiers

The high-order 10 bits of the function value are function modifiers. These are individual bits that alter the basic operation to be performed. For example, you can specify the function modifier IO$M_NOECHO with the function IO$_READLBLK to a terminal. When used together, the two values are written in VAX MACRO as IO$_READLBLK!IO$M_NOECHO. This causes data typed at the terminal keyboard to be entered into the user buffer but not echoed to the terminal. Figure 23-6 shows the format of function modifiers.

Figure 23-6 Function Modifier Format


As shown in Figure 23-6, bits <15:13> are device- or function-independent bits, and bits <12:6> are device- or function-dependent bits. Device- or function-dependent bits have the same meaning, whenever possible, for different device classes. For example, the function modifier IO$M_ACCESS is used with both disk and magnetic tape devices to cause a file to be accessed during a create operation. Device- or function-dependent bits always have the same function within the same device class.

There are two device- or function-independent modifier bits: IO$M_INHRETRY and IO$M_DATACHECK (a third bit is reserved). IO$M_INHRETRY is used to inhibit all error recovery. If any error occurs and this modifier bit is specified, the operation is terminated immediately and a failure status is returned in the I/O status block (see Section 23.10). Use IO$M_DATACHECK to compare the data in memory with that on a disk or magnetic tape.

23.5 Assigning Channels

Before any input or output operation can be performed on a physical device, you must assign a channel to the device to provide a path between the process and the device. The Assign I/O Channel (SYS$ASSIGN) system service establishes this path.

When you write a call to the SYS$ASSIGN service, you must supply the name of the device, which can be a physical device name or a logical name, and the address of a word to receive the channel number. The service returns a channel number, and you use this channel number when you write an input or output request.

For example, the following lines assign an I/O channel to the device TTA2. The channel number is returned in the word at TTCHAN.




#include <descrip.h>
#include <lib$routines.h>
#include <ssdef.h>
#include <starlet.h>
#include <stdio.h>
#include <stsdef.h>

main() {
        unsigned int status;
        unsigned short ttchan;
        $DESCRIPTOR(ttname,"TTA2:");

        /* Assign a channel to a device */
        status = SYS$ASSIGN( &ttname,   /* devnam - device name */
                             &ttchan,   /* chan - channel number */
                             0,         /* acmode - access mode */
                             0,         /* mbxnam - logical name for mailbox */
                             0 );       /* flags */
        if (!$VMS_STATUS_SUCCESS(status))
            LIB$SIGNAL(status);

 return SS$_NORMAL;
}



To assign a channel to the current default input or output device, use the logical name SYS$INPUT or SYS$OUTPUT.

For more details on how SYS$ASSIGN and other I/O services handle logical names, see Section 23.2.5.

23.5.1 Using the Share Privilege with the SYS$ASSIGN and SYS$DASSGN Services

Use of SHARE privilege should be made only with caution, as applications, application protocols, and device drivers coded to expect only exclusive access can encounter unexpected and potentially errant behavior when access to the device is unexpectedly shared via use of SHARE privilege.

If you use the SHARE privilege to override the exclusivity requested by another process's call to the system service SYS$ASSIGN, and the original process then attempts to deassign its channels via explicit calls to SYS$DASSGN or via the implicit calls to SYS$DASSGN made during image or process rundown, the OpenVMS last-channel-deassign code may not operate as expected due to the assignment of the additional I/O channels to the device. The presence of these extra channels will prevent the last-channel-deassign code from releasing the ownership of the device, potentially resulting in a device owned by the process identification (PID) of a nonexistent process.

Unless its use is explicitly supported by the application, the application protocol, and the device driver, the use of SHARE privilege is generally discouraged.

23.6 Queuing I/O Requests

All input and output operations in the operating system are initiated with the Queue I/O Request (SYS$QIO) system service. The SYS$QIO system service permits direct interaction with the system's terminal driver. SYS$QIOs permit some operations that cannot be performed with language I/O statements and RTL routines; calls to SYS$QIO reduce overhead and permit asynchronous I/O operations. However, calls to SYS$QIO are device dependent. The SYS$QIO service queues the request and returns immediately to the caller. While the operating system processes the request, the program that issued the request can continue execution.

The format for SYS$QIO is as follows:


SYS$QIO([efn],chan,func[,iosb][,astadr][,astprm][,p1][,p2][,p3][,p4][,p5][,p6]

Required arguments to the SYS$QIO service include the channel number assigned to the device on which the I/O is to be performed, and a function code (expressed symbolically) that indicates the specific operation to be performed. Depending on the function code, one to six additional parameters may be required.

For example, the IO$_WRITEVBLK and IO$_READVBLK function codes are device-independent codes used to read and write single records or virtual blocks. These function codes are suitable for simple terminal I/O. They require parameters indicating the address of an input or output buffer and the buffer length. A call to SYS$QIO to write a line to a terminal may look like the following:



#include <starlet.h>

        unsigned int status, func=IO$_WRITEVBLK;
   .
   .
   .
        status = SYS$QIO(0,             /* efn - event flag */
                        ttchan,         /* chan - channel number */
                        func,           /* func - function modifier */
                        0,              /* iosb - I/O status block */
                        0,              /* astadr - AST routine */
                        0,              /* astprm - AST parameter */
                        buffadr,        /* p1 - output buffer */
                        buflen);        /* p2 - length of message */


Function codes are defined for all supported device types, and most of the codes are device dependent; that is, they perform functions specific to a particular device. The $IODEF macro defines symbolic names for these function codes. For information about how to obtain a listing of these symbolic names, see Appendix D. For details about all function codes and an explanation of the parameters required by each, see the OpenVMS I/O User's Reference Manual.

To read from or write to a terminal with the SYS$QIO or SYS$QIOW system service, you must first associate the terminal name with an I/O channel by calling the SYS$ASSIGN system service, then use the assigned channel in the SYS$QIO or SYS$QIOW system service. To read from SYS$INPUT or write to SYS$OUTPUT, specify the appropriate logical name as the terminal name in the SYS$ASSIGN system service. In general, use SYS$QIO for asynchronous operations, and use SYS$QIOW for all other operations.

23.7 Synchronizing Service Completion

The SYS$QIO system service returns control to the calling program as soon as a request is queued; the status code returned in R0 indicates whether the request was queued successfully. To ensure proper synchronization of the queuing operation with respect to the program, the program must do the following:

  • Test whether the operation was queued successfully.
  • Test whether the operation itself completed successfully.

Optional arguments to the SYS$QIO service provide techniques for synchronizing I/O completion. There are three methods you can use to test for the completion of an I/O request:

  • Specify the number of an event flag to be set when the operation completes.
  • Specify the address of an AST routine to be executed when the operation completes.
  • Specify the address of an I/O status block in which the system can place the return status when the operation completes.
    I/O status blocks are explained in Section 23.10.

The use of these three techniques is shown in the examples that follow. Example 23-1 shows specifying event flags.

Example 23-1 Event Flags

#include <lib$routines.h>
#include <starlet.h>
unsigned int status, efn=0, efn1=1, efn=2;
   .
   .
   .
status = SYS$QIO(efn1,...);   /* Issue 1st I/O request */
if (!$VMS_STATUS_SUCCESS(status))
LIB$SIGNAL( status );                 /* Queued successfully? */  (1)
   .
   .
   .
status = SYS$QIO(efn2,...);   /* Issue second I/O request */  (2)
if (!$VMS_STATUS_SUCCESS(status))     /* Queued successfully? */
        LIB$SIGNAL( status );
   .
   .
   .(3)
status = SYS$WFLAND( efn,             / *Wait until both are done */
                     &mask,...(4)
                     .
                     .
                     .

  1. When you specify an event flag number as an argument, SYS$QIO clears the event flag when it queues the I/O request. When the I/O completes, the flag is set.
  2. In this example, the program issues two Queue I/O requests. A different event flag is specified for each request.
  3. The Wait for Logical AND of Event Flags (SYS$WFLAND) system service places the process in a wait state until both I/O operations are complete. The efn argument indicates that the event flags are both in cluster 0; the mask argument indicates the flags for which the process is to wait.
  4. Note that the SYS$WFLAND system service (and the other wait system services) wait for the event flag to be set; they do not wait for the I/O operation to complete. If some other event were to set the required event flags, the wait for event flag would complete too soon. You must coordinate the use of event flags carefully. (See Section 23.8 for a discussion of the recommended method for testing I/O completion.)

Example 23-2 shows specifying an AST routine.

Example 23-2 AST Routine


#include <lib$routines.h>
#include <starlet.h>
#include <stsdef.h>
        unsigned int status, astprm=1;
   .
   .
   .
        status = SYS$QIO(...&ttast,   /* I/O request with AST */ (1)
                        astprm...);
        if (!$VMS_STATUS_SUCCESS( status ))   /* Queued successfully? */
                LIB$SIGNAL( status );
   .
   .
   .
}

void ttast ( int astprm ) {                   /* AST service routine */ (2)

/* Handle I/O completion */
   .
   .
   .

        return;
}                              /* End of AST routine */

  1. When you specify the astadr argument to the SYS$QIO system service, the system interrupts the process when the I/O completes and passes control to the specified AST service routine.
    The SYS$QIO system service call specifies the address of the AST routine, TTAST, and a parameter to pass as an argument to the AST service routine. When $QIO returns control, the process continues execution.
  2. When the I/O completes, the AST routine TTAST is called, and it responds to the I/O completion. By examining the AST parameter, TTAST can determine the origin of the I/O request.
    When this routine is finished executing, control returns to the process at the point at which it was interrupted. If you specify the astadr argument in your call to SYS$QIO, you should also specify the iosb argument so that the AST routine can evaluate whether the I/O completed successfully.

Example 23-3 shows specifying an I/O status block.

Example 23-3 I/O Status Block

#include <lib$routines.h>
#include <stdio.h>
#include <ssdef.h>
#include <starlet.h>
#include <stsdef.h>

   .
   .
   .
/* I/O  status block */
        struct {
                 unsigned short iostat, iolen;
                 unsigned int dev_info;
}ttiosb;                                                        (1)

        unsigned int status;
   .
   .
   .
        status = SYS$QIO(,..., &ttiosb, ...);   (2)
        if( !$VMS_STATUS_SUCCESS( status )) /* Queued successfully? */
                LIB$SIGNAL( status );
   .
   .
   .
        while(ttiosb.iostat == 0) {
        /* Loop -- with delay -- until done */                  (3)

        }

        if( !$VMS_STATUS_SUCCESS( ttiosb.iostat )) {
        /* Perform error handling */
   .
   .
   .
        }

  1. An I/O status block is a quadword structure that the system uses to post the status of an I/O operation. You must define the quadword area in your program. TTIOSB defines the I/O status block for this I/O operation. The iosb argument in the SYS$QIO system service refers to this quadword.
  2. Instead of polling the low-order word of the I/O status block for the completion status, the program uses the preferred method of using an event flag and calling SYS$SYNCH to determine I/O completion.
  3. The process polls the I/O status block. If the low-order word still contains zero, the I/O operation has not yet completed. In this example, the program loops until the request is complete.

23.8 Recommended Method for Testing Asynchronous Completion

Compaq recommends that you use the Synchronize (SYS$SYNCH) system service to wait for completion of an asynchronous event. The SYS$SYNCH service correctly waits for the actual completion of an asynchronous event, even if some other event sets the event flag.

To use the SYS$SYNCH service to wait for the completion of an asynchronous event, you must specify both an event flag number and the address of an I/O status block (IOSB) in your call to the asynchronous system service. The asynchronous service queues the request and returns control to your program. When the asynchronous service completes, it sets the event flag and places the final status of the request in the IOSB.

In your call to SYS$SYNCH, you must specify the same efn and I/O status block that you specified in your call to the asynchronous service. The SYS$SYNCH service waits for the event flag to be set by means of the SYS$WAITFR system service. When the specified event flag is set, SYS$SYNCH checks the specified I/O status block. If the I/O status block is nonzero, the system service has completed and SYS$SYNCH returns control to your program. If the I/O status block is zero, SYS$SYNCH clears the event flag by means of the SYS$CLREF service and calls the $WAITFR service to wait for the event flag to be set.

The SYS$SYNCH service sets the event flag before returning control to your program. This ensures that the call to SYS$SYNCH does not interfere with testing for completion of another asynchronous event that completes at approximately the same time and uses the same event flag to signal completion.

The following call to the Queue I/O Request (SYS$QIO) system service demonstrates how the SYS$SYNCH service is used:



   .
   .
   .
#include <lib$routines.h>
#include <starlet.h>
        unsigned int status, event_flag = 1;
        struct {
                        short int iostat, iolen;
                        unsigned int dev_info;
}ttiosb;
   .
   .
   .
/* Request I/O */
        status = SYS$QIO (event_flag, ..., &ttiosb ...);
        if (!$VMS_STATUS_SUCCESS(status))
                LIB$SIGNAL( status );
   .
   .
   .
/* Wait until I/O completes */
        status = SYS$SYNCH (event_flag, &ttiosb );
        if (!$VMS_STATUS_SUCCESS(status))
                LIB$SIGNAL( status );
   .
   .
   .



Note

The SYS$QIOW service provides a combination of SYS$QIO and SYS$SYNCH.

23.9 Synchronous and Asynchronous Forms of Input/Output Services

You can execute some input/output services either synchronously or asynchronously. A "W" at the end of a system service name indicates the synchronous version of the system service.

The synchronous version of a system service combines the functions of the asynchronous version of the service and the Synchronize (SYS$SYNCH) system service. The synchronous version acts exactly as if you had used the asynchronous version of the system service followed immediately by a call to SYS$SYNCH; it queues the I/O request, and then places the program in a wait state until the I/O request completes. The synchronous version takes the same arguments as the asynchronous version.

Table 23-2 lists the asynchronous and synchronous names of input/output services that have synchronous versions.

Table 23-2 Asynchronous Input/Output Services and Their Synchronous Versions
Asynchronous Name Synchronous Name Description
$BRKTHRU $BRKTHRUW Breakthrough
$GETDVI $GETDVIW Get Device/Volume Information
$GETJPI $GETJPIW Get Job/Process Information
$GETLKI $GETLKIW Get Lock Information
$GETQUI $GETQUIW Get Queue Information
$GETSYI $GETSYIW Get Systemwide Information
$QIO $QIOW Queue I/O Request
$SNDJBC $SNDJBCW Send to Job Controller
$UPDSEC $UPDSECW Update Section File on Disk

23.9.1 Reading Operations with SYS$QIOW

The SYS$QIO and SYS$QIOW system services move one record of data from a terminal to a variable. For synchronous I/O, use SYS$QIOW. Complete information about the SYS$QIO and SYS$QIOW system services is presented in the OpenVMS System Services Reference Manual.

Note

Do not use the SYS$QIO and SYS$QIOW system services for input from a file or nonterminal device.

The SYS$QIO and SYS$QIOW system services place the data read in the variable specified in the 1 argument. The second word of the status block contains the offset from the beginning of the buffer to the terminator---hence, it equals the size of the data read. Always reference the data as a substring, using the offset to the terminator as the position of the last character (that is, the size of the substring). If you reference the entire buffer, your data will include the terminator for the operation (for example, the CR character) and any excess characters from a previous operation using the buffer. (The only exception to the substring guideline is if you deliberately overflow the buffer to terminate the I/O operation.)

Example 23-4 shows use of the SYS$QIOW system service and reads a line of data from the terminal and waits for the I/O to complete.

Example 23-4 Reading Data from the Terminal Synchronously

   .
   .
   .
INTEGER STATUS
! QIOW structures
INTEGER*2 INPUT_CHAN             ! I/O channel
INTEGER CODE,                    ! Type of I/O operation
2       INPUT_BUFF_SIZE,         ! Size of input buffer
2       PROMPT_SIZE,             ! Size of prompt
2       INPUT_SIZE               ! Size of input line as read
PARAMETER (PROMPT_SIZE = 13,
2          INPUT_BUFF_SIZE = 132)
CHARACTER*132 INPUT
CHARACTER*(*) PROMPT
PARAMETER (PROMPT = 'Input value: ')
! Define symbols used in I/O operations
INCLUDE '($IODEF)'
! Status block for QIOW
STRUCTURE /IOSTAT_BLOCK/
  INTEGER*2 IOSTAT,              ! Return status
2           TERM_OFFSET,         ! Location of line terminator
2           TERMINATOR,          ! Value of terminator
2           TERM_SIZE            ! Size of terminator
END STRUCTURE
RECORD /IOSTAT_BLOCK/ IOSB
! Subprograms
INTEGER*4 SYS$ASSIGN,
2         SYS$QIOW
   .
   .
   .
! Assign an I/O channel to SYS$INPUT
STATUS = SYS$ASSIGN ('SYS$INPUT',
2                    INPUT_CHAN,,)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! Read with prompt
CODE = IO$_READPROMPT
STATUS = SYS$QIOW (,
2                  %VAL (INPUT_CHAN),
2                  %VAL (CODE),
2                  IOSB,
2                  ,,
2                  %REF (INPUT),
2                  %VAL (INPUT_BUFF_SIZE),
2                  ,,
2                  %REF (PROMPT),
2                  %VAL (PROMPT_SIZE))
! Check QIOW status
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! Check status of I/O operation
IF (.NOT. IOSB.IOSTAT) CALL LIB$SIGNAL (%VAL (IOSB.IOSTAT))
! Set size of input string
INPUT_SIZE = IOSB.TERM_OFFSET
   .
   .
   .


Previous Next Contents Index