hp.com home products and services support and drivers solutions how to buy
cd-rom home
End of Jump to page title
HP OpenVMS systems
documentation

Jump to content


HP OpenVMS Programming Concepts Manual

HP OpenVMS Programming Concepts Manual


Previous Contents Index

4.2.5.3 Conducting Multiple Simultaneous Searches with SYS$PROCESS_SCAN

Only one asynchronous remote SYS$GETJPI request per SYS$PROCESS_SCAN context is permitted at a time. If you issue a second SYS$GETJPI request using a context before a previous remote request using the same context has completed, your process stalls in a resource wait until the previous remote SYS$GETJPI request completes. This stall in the RWAST state prevents your process from executing in user mode or receiving user-mode ASTs.

If you want to run remote searches in parallel, create multiple contexts by calling SYS$PROCESS_SCAN once for each context. For example, you can design a program that calls SYS$GETSYI in a loop to find the nodes in the VMScluster system and creates a separate SYS$PROCESS_SCAN context for each remote node. Each of these separate contexts can run in parallel. The DCL command SHOW USERS uses this technique to obtain user information more quickly.

Only requests to remote nodes must wait until the previous search using the same context has completed. If the SYS$PROCESS_SCAN context specifies the local node, any number of SYS$GETJPI requests using that context can be executed in parallel (within the limits implied by the process quotas for ASTLM and BYTLM).

Note

When you use SYS$GETJPI to reference remote processes, you must properly synchronize all SYS$GETJPI calls. Before the operating system's Version 5.2, if you did not follow these synchronization rules, your programs might have appeared to run correctly. However, if you attempt to run such improperly synchronized programs using SYS$GETJPI with SYS$PROCESS_SCAN with a remote process, your program might attempt to use the data before SYS$GETJPI has returned it.

To perform a synchronous search in which the program waits until all requested information is available, use SYS$GETJPIW with an iosb argument.

See the HP OpenVMS System Services Reference Manual for more information about process identification, SYS$GETJPI, and SYS$PROCESS_SCAN.

4.2.6 Programming with SYS$GETJPI

The following sections describe some important considerations for programming with SYS$GETJPI.

4.2.6.1 Using Item Lists Correctly

When SYS$GETJPI collects data, it makes multiple passes through the item list. If the item list is self-modifying---that is, if the addresses for the output buffers in the item list point back at the item list---SYS$GETJPI replaces the item list information with the returned data. Therefore, incorrect data might be read or unexpected errors might occur when SYS$GETJPI reads the item list again. To prevent confusing errors, HP recommends that you do not use self-modifying item lists.

The number of passes that SYS$GETJPI needs depends on which item codes are referenced and the state of the target process. A program that appears to work normally might fail when a system has processes that are swapped out of memory, or when a process is on a remote node.

4.2.6.2 Improving Performance by Using Buffered $GETJPI Operations

To request information about a process located on a remote node, SYS$GETJPI must send a message to the remote node, wait for the response, and then extract the data from the message received. When you perform a search on a remote system, the program must repeat this sequence for each process that SYS$GETJPI locates.

To reduce the overhead of such a remote search, use SYS$PROCESS_SCAN with the PSCAN$_GETJPI_BUFFER_SIZE item code to specify a buffer size for SYS$GETJPI. When the buffer size is specified by SYS$PROCESS_SCAN, SYS$GETJPI packs information for several processes into one buffer and transmits them in a single message. This reduction in the number of messages improves performance.

For example, if the SYS$GETJPI item list requests 100 bytes of information, you might specify a PSCAN$_GETJPI_BUFFER_SIZE of 1000 bytes so that the service can place information for at least 10 processes in each message. (SYS$GETJPI does not send fill data in the message buffer; therefore, information for more than 10 processes can be packed into the buffer.)

The SYS$GETJPI buffer must be large enough to hold the data for at least one process. If the buffer is too small, the error code SS$_IVBUFLEN is returned from the SYS$GETJPI call.

You do not have to allocate space for the SYS$GETJPI buffer; buffer space is allocated by SYS$PROCESS_SCAN as part of the search context that it creates. Because SYS$GETJPI buffering is transparent to the program that calls SYS$GETJPI, you do not have to modify the loop that calls SYS$GETJPI.

If you use PSCAN$_GETJPI_BUFFER_SIZE with SYS$PROCESS_SCAN, all calls to SYS$GETJPI using that context must request the same item code information. Because SYS$GETJPI collects information for more than one process at a time within its buffers, you cannot change the item codes or the lengths of the buffers in the SYS$GETJPI item list between calls. SYS$GETJPI returns the error SS$_BADPARAM if any item code or buffer length changes between SYS$GETJPI calls. However, you can change the buffer addresses in the SYS$GETJPI item list from call to call.

The SYS$GETJPI buffered operation is not used for searching the local node. When a search specifies both multiple nodes and SYS$GETJPI buffering, the buffering is used on remote nodes but is ignored on the local node. Example 4-11 demonstrates how to use a SYS$GETJPI buffer to improve performance.

Example 4-11 Using a SYS$GETJPI Buffer to Improve Performance

!******************************************** 
!*  Initialize item list for $PROCESS_SCAN  * 
!******************************************** 
 
! Search for jobs owned by users SMITH and JONES 
! across the cluster with $GETJPI buffering 
 
PSCANLIST(1).BUFLEN   = 0 
PSCANLIST(1).CODE     = PSCAN$_NODE_CSID 
PSCANLIST(1).BUFADR   = 0 
PSCANLIST(1).ITMFLAGS = PSCAN$M_NEQ 
PSCANLIST(2).BUFLEN   = LEN('SMITH') 
PSCANLIST(2).CODE     = PSCAN$_USERNAME 
PSCANLIST(2).BUFADR   = %LOC('SMITH') 
PSCANLIST(2).ITMFLAGS = PSCAN$M_OR 
PSCANLIST(3).BUFLEN   = LEN('JONES') 
PSCANLIST(3).CODE     = PSCAN$_USERNAME 
PSCANLIST(3).BUFADR   = %LOC('JONES') 
PSCANLIST(3).ITMFLAGS = 0 
PSCANLIST(4).BUFLEN   = 0 
PSCANLIST(4).CODE     = PSCAN$_GETJPI_BUFFER_SIZE 
PSCANLIST(4).BUFADR   = 1000 
PSCANLIST(4).ITMFLAGS = 0 
PSCANLIST(5).END_LIST = 0 
 
!******************************************** 
!*     End of item list initialization      * 
!******************************************** 

4.2.6.3 Fulfilling Remote SYS$GETJPI Quota Requirements

A remote SYS$GETJPI request uses system dynamic memory for messages. System dynamic memory uses the process quota BYTLM. Follow these steps to determine the number of bytes required by a SYS$GETJPI request:

  1. Add the following together:
  2. Double this total.
    The total is doubled because the messages consume system dynamic memory on both the sending node and the receiving node.

This formula for BYTLM quota applies to both buffered and nonbuffered SYS$GETJPI requests. For buffered requests, use the value specified in the SYS$PROCESS_SCAN item, PSCAN$_GETJPI_BUFFER_SIZE, as the size of the buffer. For nonbuffered requests, use the total length of all data buffers specified in the SYS$GETJPI item list as the size of the buffer.

If the BYTLM quota is insufficient, SYS$GETJPI (not SYS$PROCESS_SCAN) returns the error SS$_EXBYTLM.

4.2.6.4 Using the SYS$GETJPI Control Flags

The JPI$_GETJPI_CONTROL_FLAGS item code, which is specified in the SYS$GETJPI item list, provides additional control over SYS$GETJPI. Therefore, SYS$GETJPI may be unable to retrieve all the data requested in an item list because JPI$_GETJPI_CONTROL_FLAGS requests that SYS$GETJPI not perform certain actions that may be necessary to collect the data. For example, a SYS$GETJPI control flag may instruct the calling program not to retrieve a process that has been swapped out of the balance set.

If SYS$GETJPI is unable to retrieve any data item because of the restrictions imposed by the control flags, it returns the data length as 0. To verify that SYS$GETJPI received a data item, examine the data length to be sure that it is not 0. To make this verification possible, be sure to specify the return length for each item in the SYS$GETJPI item list when any of the JPI$_GETJPI_CONTROL_FLAGS flags is used.

Unlike other SYS$GETJPI item codes, the JPI$_GETJPI_CONTROL_FLAGS item is an input item. The item list entry should specify a longword buffer. The desired control flags should be set in this buffer.

Because the JPI$_GETJPI_CONTROL_FLAGS item code tells SYS$GETJPI how to interpret the item list, it must be the first entry in the SYS$GETJPI item list. The error code SS$_BADPARAM is returned if it is not the first item in the list.

The following are the SYS$GETJPI control flags.

JPI$M_NO_TARGET_INSWAP

When you specify JPI$M_NO_TARGET_INSWAP, SYS$GETJPI does not retrieve a process that has been swapped out of the balance set. Use JPI$M_NO_TARGET_INSWAP to avoid the additional load of swapping processes into a system. For example, use this flag with SHOW SYSTEM to avoid bringing processes into memory to display their accumulated CPU time.

If you specify JPI$M_NO_TARGET_INSWAP and request information from a process that has been swapped out, the following consequences occur:

You must examine the return length of an item to verify that the item was retrieved. The information may be located in a different data structure in another release of the operating system.

JPI$M_NO_TARGET_AST

When you specify JPI$M_NO_TARGET_AST, SYS$GETJPI does not deliver a kernel-mode AST to the target process. Use JPI$M_NO_TARGET_AST to avoid executing a target process in order to retrieve information.

If you specify JPI$M_NO_TARGET_AST and cannot deliver an AST to a target process, the following consequences occur:

You must examine the return length of an item to verify that the item was retrieved. The information may be located in a different data structure in another release of the operating system.

The use of the flag JPI$M_NO_TARGET_AST also implies that SYS$GETJPI does not swap in a process, because SYS$GETJPI would only bring a process into memory to deliver an AST to that process.

JPI$M_IGNORE_TARGET_STATUS

When you specify JPI$M_IGNORE_TARGET_STATUS, SYS$GETJPI attempts to retrieve as much information as possible, even if the process is suspended or being deleted. Use JPI$M_IGNORE_TARGET_STATUS to retrieve all possible information from a process. For example, use this flag with SHOW SYSTEM to display processes that are suspended, being deleted, or in miscellaneous wait states.

Example 4-12 demonstrates how to use SYS$GETJPI control flags to avoid swapping processes during a SYS$GETJPI call.

Example 4-12 Using SYS$GETJPI Control Flags to Avoid Swapping a Process into the Balance Set

        PROGRAM CONTROL_FLAGS 
 
        IMPLICIT NONE                   ! Implicit none 
 
        INCLUDE '($jpidef)   /nolist'   ! Definitions for $GETJPI 
        INCLUDE '($pscandef) /nolist'   ! Definitions for $PROCESS_SCAN 
        INCLUDE '($ssdef)    /nolist'   ! Definitions for SS$_ names 
 
        STRUCTURE /JPIITMLST/           ! Structure declaration for 
         UNION                          !  $GETJPI item lists 
          MAP 
           INTEGER*2 BUFLEN, 
        2            CODE 
           INTEGER*4 BUFADR, 
        2            RETLENADR 
          END MAP 
          MAP                           ! A longword of 0 terminates 
           INTEGER*4 END_LIST           !  an item list 
          END MAP 
         END UNION 
        END STRUCTURE 
        STRUCTURE /PSCANITMLST/         ! Structure declaration for 
         UNION                          !  $PROCESS_SCAN item lists 
          MAP 
           INTEGER*2 BUFLEN, 
        2            CODE 
           INTEGER*4 BUFADR, 
        2            ITMFLAGS 
          END MAP 
          MAP                           ! A longword of 0 terminates 
           INTEGER*4 END_LIST           !  an item list 
          END MAP 
         END UNION 
        END STRUCTURE 
        RECORD /PSCANITMLST/            ! Declare the item list for 
        2         PSCANLIST(5)          !  $PROCESS_SCAN 
 
        RECORD /JPIITMLST/              ! Declare the item list for 
        2         JPILIST(6)            !  $GETJPI 
 
        INTEGER*4 SYS$GETJPIW,          ! System service entry points 
        2         SYS$PROCESS_SCAN 
 
        INTEGER*4 STATUS,               ! Status variable 
        2         CONTEXT,              ! Context from $PROCESS_SCAN 
        2         PID,                  ! PID from $GETJPI 
        2         JPIFLAGS              ! Flags for $GETJPI 
 
        INTEGER*2 IOSB(4)               ! I/O Status Block for $GETJPI 
 
        CHARACTER*16 
        2         PRCNAM,               ! Process name from $GETJPI 
        2         NODENAME              ! Node name from $GETJPI 
        INTEGER*2 PRCNAM_LEN,           ! Process name length 
        2         NODENAME_LEN          ! Node name length 
 
        CHARACTER*80 
        2         IMAGNAME              ! Image name from $GETJPI 
        INTEGER*2 IMAGNAME_LEN          ! Image name length 
 
        LOGICAL*4 DONE                  ! Done with data loop 
 
        !******************************************** 
        !*  Initialize item list for $PROCESS_SCAN  * 
        !******************************************** 
 
        ! Look for interactive and batch jobs across 
        ! the cluster with $GETJPI buffering 
 
        PSCANLIST(1).BUFLEN   = 0 
        PSCANLIST(1).CODE     = PSCAN$_NODE_CSID 
        PSCANLIST(1).BUFADR   = 0 
        PSCANLIST(1).ITMFLAGS = PSCAN$M_NEQ 
        PSCANLIST(2).BUFLEN   = 0 
        PSCANLIST(2).CODE     = PSCAN$_MODE 
        PSCANLIST(2).BUFADR   = JPI$K_INTERACTIVE 
        PSCANLIST(2).ITMFLAGS = PSCAN$M_OR 
        PSCANLIST(3).BUFLEN   = 0 
        PSCANLIST(3).CODE     = PSCAN$_MODE 
        PSCANLIST(3).BUFADR   = JPI$K_BATCH 
        PSCANLIST(3).ITMFLAGS = 0 
        PSCANLIST(4).BUFLEN   = 0 
        PSCANLIST(4).CODE     = PSCAN$_GETJPI_BUFFER_SIZE 
        PSCANLIST(4).BUFADR   = 1000 
        PSCANLIST(4).ITMFLAGS = 0 
        PSCANLIST(5).END_LIST = 0 
 
        !******************************************** 
        !*     End of item list initialization      * 
        !******************************************** 
 
        STATUS = SYS$PROCESS_SCAN (        ! Set up the scan context 
        2                        CONTEXT, 
        2                        PSCANLIST) 
 
        IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS)) 
 
        ! Initialize $GETJPI item list 
 
        JPILIST(1).BUFLEN    = 4 
        JPILIST(1).CODE      = IAND ('FFFF'X, JPI$_GETJPI_CONTROL_FLAGS) 
        JPILIST(1).BUFADR    = %LOC(JPIFLAGS) 
        JPILIST(1).RETLENADR = 0 
        JPILIST(2).BUFLEN    = 4 
        JPILIST(2).CODE      = JPI$_PID 
        JPILIST(2).BUFADR    = %LOC(PID) 
        JPILIST(2).RETLENADR = 0 
        JPILIST(3).BUFLEN    = LEN(PRCNAM) 
        JPILIST(3).CODE      = JPI$_PRCNAM 
        JPILIST(3).BUFADR    = %LOC(PRCNAM) 
        JPILIST(3).RETLENADR = %LOC(PRCNAM_LEN) 
        JPILIST(4).BUFLEN    = LEN(IMAGNAME) 
        JPILIST(4).CODE      = JPI$_IMAGNAME 
        JPILIST(4).BUFADR    = %LOC(IMAGNAME) 
        JPILIST(4).RETLENADR = %LOC(IMAGNAME_LEN) 
        JPILIST(5).BUFLEN    = LEN(NODENAME) 
        JPILIST(5).CODE      = JPI$_NODENAME 
        JPILIST(5).BUFADR    = %LOC(NODENAME) 
        JPILIST(5).RETLENADR = %LOC(NODENAME_LEN) 
        JPILIST(6).END_LIST  = 0 
        ! Loop calling $GETJPI with the context 
 
        DONE = .FALSE. 
        JPIFLAGS = IOR (JPI$M_NO_TARGET_INSWAP, JPI$M_IGNORE_TARGET_STATUS) 
        DO WHILE (.NOT. DONE) 
 
                ! Call $GETJPI to get the next process 
 
                STATUS = SYS$GETJPIW ( 
        2                    ,           ! No event flag 
        2                    CONTEXT,    ! Process context 
        2                    ,           ! No process name 
        2                    JPILIST,    ! Itemlist 
        2                    IOSB,       ! Always use IOSB with $GETJPI! 
        2                    ,           ! No AST 
        2                    )           ! No AST arg 
                ! Check the status in both STATUS and the IOSB, if 
                ! STATUS is OK then copy IOSB(1) to STATUS 
 
                IF (STATUS) STATUS = IOSB(1) 
 
                ! If $GETJPI worked, display the process, if done then 
                ! prepare to exit, otherwise signal an error 
 
                IF (STATUS) THEN 
                        IF (IMAGNAME_LEN .EQ. 0) THEN 
                                TYPE 1010, PID, NODENAME, PRCNAM 
                        ELSE 
                                TYPE 1020, PID, NODENAME, PRCNAM, 
        2                                  IMAGNAME(1:IMAGNAME_LEN) 
                        END IF 
                ELSE IF (STATUS .EQ. SS$_NOMOREPROC) THEN 
                        DONE = .TRUE. 
                ELSE 
                        CALL LIB$SIGNAL(%VAL(STATUS)) 
                END IF 
 
        END DO 
 
1010    FORMAT (' ',Z8.8,'  ',A6,':: ',A,' (no image)') 
1020    FORMAT (' ',Z8.8,'  ',A6,':: ',A,' ',A) 
 
        END 

4.2.7 Using SYS$GETLKI

The SYS$GETLKI system service allows you to obtain process lock information. Example 4-13 is a C program that illustrates the procedure for obtaining process lock information for both Alpha and VAX systems. However, to compile on Alpha systems, you need to supply the /DEFINE=Alpha=1 qualifier.

Example 4-13 Procedure for Obtaining Process Lock Information

#pragma nostandard 
#ifdef  Alpha 
#pragma module               LOCK_SCAN 
#else              /* Alpha */ 
#module                      LOCK_SCAN 
#endif            /* Alpha */ 
#pragma standard 
 
#include        <ssdef.h> 
#include        <lkidef.h> 
 
#pragma nostandard 
globalvalue 
      ss$_normal, ss$_nomorelock; 
#pragma standard 
 
struct lock_item_list 
                { 
                short int       buffer_length; 
                short int       item_code; 
                void            *bufaddress; 
                void            *retaddress; 
                }; 
 
typedef struct lock_item_list lock_item_list_type; 
 
unsigned long lock_id; 
long int value_block[4]; 
 
#pragma nostandard 
static  lock_item_list_type 
  getlki_item_list[] = { 
   {sizeof(value_block), LKI$_VALBLK,    &value_block,  0}, 
   {sizeof(lock_id),     LKI$_LOCKID,    &lock_id,      0}, 
   {0,0,0,0} 
}; 
globalvalue ss$_normal, ss$_nomorelock; 
#pragma standard 
 
main() 
{ 
  int status = ss$_normal; 
  unsigned long lock_context = -1;   /* init for wild-card operation */ 
 
  while (status == ss$_normal) { 
      status = sys$getlkiw( 1, &lock_context, getlki_item_list,0,0,0,0); 
      /*                                                  */ 
      /* Dequeue the lock if the value block contains a 1 */ 
      /*                                                  */ 
      if ((status == ss$_normal) & (value_block[0] == 1)){ 
          status = sys$deq( lock_id, 0, 0, 0 ); 
      } 
  } 
  if (status != ss$_nomorelock){ 
      exit(status); 
  } 
} 

4.2.8 Setting Process Privileges

Use the SYS$SETPRV system service to set process privileges. Setting process privileges allows you to limit executing privileged code to a specific process, to limit functions within a process, and to limit access from other processes. You can either enable or disable a set of privileges and assign privileges on a temporary or permanent basis. To use this service, the creating process must have the appropriate privileges.

4.3 Changing Process and Kernel Threads Scheduling

Prior to kernel threads, the OpenVMS scheduler selected a process to run. With kernel threads, the OpenVMS scheduler selects a kernel thread to run. All processes are thread-capable processes with at least one kernel thread. A process may have only one kernel thread, or a process may have a variable number of kernel threads. A single-threaded process is equivalent to a process before OpenVMS Version 7.0.

With kernel threads, all base and current priorities are perkernel thread. To alter a thread's scheduling, you can change the base priority of the thread with the SYS$SETPRI system service, which affects the specified kernel thread and not the entire process.

To alter a process's scheduling, you can lock the process into physical memory so that it is not swapped out. Processes that have been locked into physical memory are executed before processes that have been swapped out. For kernel threads, the thread with the highest priority level is executed first.

If you create a subprocess with the LIB$SPAWN routine, you can set the priority of the subprocess by executing the DCL command SET PROCESS/PRIORITY as the first command in a command procedure. You must have the ALTPRI privilege to increase the base priority above the base priority of the creating process.

If you create a subprocess with the LIB$SPAWN routine, you can inhibit swapping by executing the DCL command SET PROCESS/NOSWAP as the first command in a command procedure. Use the SYS$SETSWM system service to inhibit swapping for any process. A process must have the PSWAPM privilege to inhibit swapping.

If you alter a kernel thread's scheduling, you must do so with care. Review the following considerations before you attempt to alter the standard kernel threads or process scheduling with either SYS$SETPRI or SYS$SETSWM:


Previous Next Contents Index