longpelaexpertise.com.au/ezine/CtBlksBeginners2.php?ezinemode=printfriend

LongEx Mainframe Quarterly - February 2009

technical: Control Blocks for Beginners Pt 2: z/OS Control Blocks

In the first part of this two part series, we introduced control blocks: what they are, how to access them and how they can be useful. In this second article, we talk about z/OS control blocks: the basic control blocks and how to write programs to access them.

Introduction

In the previous article in this series, we introduced control blocks: what they are, why they're used, and how to look at them. In this article, we're going to look closer at z/OS control blocks. We'll look at some of the basic control blocks that every Systems Programmer needs to know, and coding examples to access these control blocks in Assembler, PL/1 and COBOL.

The Two Basic z/OS control blocks

If you're looking for control blocks in z/OS, the chances are that you'll start looking for them by referring to the two basic z/OS control blocks that point to almost everything: The PSA and CVT.

PSA - Prefixed Save Area

The PSA is the first control block you need. It holds the basic information z/OS needs when scheduling work on a Central Processor (CP), which is why there's one for every CP. And the PSA is easy to find - it's always at address zero.

The big things that PSA gives Systems Programmers are:

  • FLCCVT - A pointer to the CVT (more on the CVT in a moment).
  • PSAAOLD - A pointer to the Address Space Control Block (ASCB) of the address space currently scheduled on this CP. The ASCB holds basic information about an address space, including Jobname and Address Space ID. More information is held in the Address Space Extension Block (ASXB), which is pointed to by the ASCB.
  • PSATOLD - A pointer to the Task Control Block (TCB) of the task currently scheduled on this CP. The TCB holds information on a specific task.

CVT - Communication Vector Table

The CVT is a great control block. You can find pointers to a whole lot of other control blocks from here, including all ASCBs, SSVTs (Subsystem Vector Table - information on subsystems) and UCBs (Unit Control Blocks - information on hardware units like DASD and tape). Think of the CVT as an index to all the other z/OS control blocks.

So these basic control blocks are linked like this:

Accessing Control Blocks in Assembler

So now that we've been introduced to the basic z/OS control blocks, let's start using them. Look at the Assembler (HLASM) example below:

* --- Get the CVT Address --------------------
         L     R1,FLCCVT-PSA(0)        R1 -> CVT

* --- Get the SMCA Address -------------------
         L     R1,CVTSMCA-CVT(R1)      R1 -> SMCA  
 
* --- Get the z/OS SMFID  --------------------
         USING SMCABASE,R1                         
         MVC   ZOSNAME(L'SMCASID),SMCASID          
                 
* --- DSECTs we need for these control blocks -- 
         IHAPSA ,                      Map PSA 
         CVT DSECT=YES                 Map CVT 
         IEESMCA DSECT=YES             Map SMCA
Figure 1: HLASM Code Fragment

This fragment gets the name of the z/OS image - the SMFID. Looking at this fragment, you can see that we

  1. get the address of the CVT from the PSA
  2. get the address of a control block called the SMCA (used to store SMF related information)
  3. get the the SMFID from the SMCA.

You can also see that we include z/OS macros to include DSECTS that map these control blocks at the bottom.

Control Blocks from High Level Languages

But you don't have to program in Assembler to access control blocks. Suppose we want to get the Jobname and Address Space where we're running. Check out the following PL/1, C, COBOL and REXX examples.

/*=============================================  
  Storage Definitions                                        
  ==============================================*/
 /* --- Map PSA ------------------------------------------ */
 Dcl 1 psa               Based(psap),                        
       3 psastuff        char(548),    /* fill  548 bytes  */
       3 psaaold         Pointer;                  
 /*      (ignore rest of PSA)                              */
                                                   
 /* --- Map ASCB------------------------------------------ */
 Dcl 1 ascb              Based(psaaold),                 
       3 ascbstuff       char(172),    /* fill 172 bytes   */
       3 ascbjbni        Pointer,                      
       3 ascbjbns        Pointer;                   
 /*      (ignore rest of ASCB)                             */
                                                   
 Dcl STCName           char(8) Based(ascbjbns);        
 Dcl JobName           char(8) Based(ascbjbni);           
                                        
 /*=============================================  
  Main Program                 
  ==============================================*/
 psap = null();                               
 Display ('(DZSPLI) STC: ' || STCName);                
 Display ('(DZSPLI) Job: ' || JobName);           
 End DZSPLI;            
Figure 2: PL/1 Code Fragment

       DATA DIVISION.               
       Linkage Section.                         
                                   
      * --- Map PSA  --------------------------------------  
       01  PSA.                             
           02   filler                 pic x(548).          
           02   PSAAOLD                pointer.            
                                               
      * --- Map ASCB  -------------------------------------  
       01  ASCB.                                         
           02   filler                 pic x(172).         
           02   ASCBJBNI               pointer.          
           02   ASCBJBNS               pointer.         
                                                     
       01  STCNAME                     pic x(8).         
       01  JOBNAME                     pic x(8).         
                                                        
       PROCEDURE DIVISION.                            
           SET ADDRESS OF PSA TO NULL.               
           SET ADDRESS OF ASCB TO PSAAOLD.              
           SET ADDRESS OF JOBNAME TO ASCBJBNI.        
           SET ADDRESS OF STCNAME TO ASCBJBNS.           
           DISPLAY "(DZSCOB1) " STCNAME ", " JOBNAME.  
           GOBACK.   
Figure 3: COBOL Code Fragment

/*=============================================  
  Map z/OS Control Blocks               
  ==============================================*/
/* --- Map PSA --------------------------------- */
struct psa {                             
   int psastuff[4];                /* 4 bytes before CVT Ptr*/
   struct cvt *psacvt;      
   /* Ignore the rest of the PSA */      
};
         
/* --- Map ASCB -------------------------------- */
struct ascb {    
   char ascbstuff[172];            /* 140 bytes before ptr  */
   char *ascbjbni;                 /* Jobname (if any)      */
   char *ascbjbns;                 /* STC Name              */
   /* Ignore the rest of the CVT */  
};
                          
struct psa *psa_ptr = 0;           /* PSA is at address 0   */
char stcname[10], jobname[10];       
                      
/*=============================================  
  Main Program            
  ==============================================*/
strncpy(jobname,psa_ptr->psaaold->ascbjbni,8);
strncpy(stcname,psa_ptr->psaaold->ascbjbns,8);  
printf("Jobname: %s, STCName: %s\n", jobname, stcname); 
Figure 4: C Code Fragment

/* Rexx */
/* --- Get Address of ASCB ----------------------- */  
ASCB_Addr = C2D(Storage(224,4))  /* Get address of ASCB */
 
/* --- First check ASCBJBNI for Jobname ------------------- */
Interpret "JobAddr = Storage("D2X(ASCB_ADDR+172)",4)"

If C2D(JobAddr) = 0 Then
   /* --- Not in initiator, so get jobname from ASCBJBNS -- */
   Interpret "JobAddr = Storage("D2X(ASCB_ADDR+176)",4)"
   
Interpret "Job = Storage("C2X(JobAddr)",8)" 
say "Jobname: " Job
exit
Figure 5: REXX Code Fragment

All of these programs use the PSAAOLD field in the PSA to get the address of the current ASCB, then get both the Jobname (which will only exist if the code is running in a batch job or something similar that runs in an initiator) and the started task name.

The problem with using high level languages is that IBM doesn't supply a way of mapping the control blocks in these languages - only Assembler. So you need to map them yourself, and we've done this in the above examples. However if you're using the IBM C compiler, there is a batch utility that converts Assembler macros to C defines.

Summary

Many z/OS control blocks are chained off two basic blocks: the PSA and CVT. You can quite easily access these control blocks not only from Assembler programs, but from High Level languages like C, PL/1 and COBOL.


David Stephens