How do I create a header file from an assembler macro?

You can easily do this using standard JCL

//COLINZP  JOB 1,MSGCLASS=H 
// JCLLIB ORDER=CBC.SCCNPRC
//DSECT EXEC PROC=EDCDSECT,
// INFILE=COLIN.C.SOURCE(ASMMAC2),
// OUTFILE=COLIN.C.H.VB(TESTASM),
// DPARM='EQU(BIT)'
//ASSEMBLE.SYSLIB DD
// DD DISP=SHR,DSN=HLA.SASMMAC1
//ASSEMBLE.SYSIN DD *
DUMMY DSECT
ABC DS 10F
END
//DSECT.EDCDSECT DD SYSOUT=*
/*

This produced in //DSECT.EDCDSECT

#pragma pack(packed)                 

struct dummy {
int abc[10];
};

#pragma pack(reset)

You can point //DSECT.EDCDSECT to where you want your header file stored.

The syntax of the DPARM is defined here.

The comment option

By default comments are produced. One comment was

short int adplasid; /* Address space identifier @P2C */

which went past column 75.
With NOCOMMENT it produced

short int adplasid;

and all the lines were less than 72 characters long.

With COMMENT(@) it ignores anything beyond the specified string

short int adplasid; /* Address space identifier */

But some lines were still longer than 75 characters.

Using the default COMMENT

This worked first time – ish. When I tried to use the generated header file, it failed with a syntax error, because comments had wrapped over line boundary. I had to edit the output and remove the comments causing the problem.

Having fixed the obvious comment wraps, I then found comments extended into columns 72-80.

This meant, as far as C was concerned, the comment was still open, so the field on the next line was taken as part of the comment, and I was getting messages like

CCN3022 "adplserv" is not a member of "struct abdpl". 

I edited the file and fixed up these comments.

Plan b) was to specify the option NOCOMMENT and no comments are produced at all.

It could not find a macro…

The DSECT I was trying to generate referenced BIT0. My challenge was to find where this was defined!

  • If you use ISPF 3.4 on SYS1.MACLIB you can use the command SRCHFOR BIT0 and it searches all the members for the specified value. Tab to the “Prompt” heading, and press enter. The members with the value will be at top of the list.
  • You can use ISPF 2, and specify ‘SYS1.MACLIB(IEZ*)’ to display only a subset of the members – beginning with IEZ. Tab to the “prompt” heading, as above
  • You can use ISPF 3.15 Extended Search-For Utility. Specify DS name ‘SYS1.MACLIB’, and * for all members. Specify your search argument and press enter. You get one file with every occurrence it found and the member name. This was the easiest way for looking for what wanted.

The definition I wanted was not in SYS1.MACLIB, but I found it in SYS1.AMODGEN.

Calling C from Rexx – overview

Rexx is a very flexible language. You can call other Rexx programs, you can call z/OS functions, or you can call functions written in C or assembler.

Programming a C function was not straight forward because it involved twisting and bending to get it to work – one of the downsides of being flexible.

See

What options are there?

You can use

  • call function(p1,p2,p3) This gets passed an array of parameters, and has to return data in and an EVALBLOCK. See here.
  • address link program “p1 p2 p3” where “p1 p2 p3” is a literal string which is passed to your program
  • address linkmvs program “p1 p2 p3” where p1, p2, p3 are variable names, whose values are substituted before being passed to the program. If the parameter is not a variable – the parameter is passed through as is. The program gets passed a list
    • length p1 value, p1 value
    • length p2 value, p2 value
    • length p3 value, p3 value
  • address linkpgm program “p1 p2 p3” where p1, p2, p3 is a variable names, whose values are substituted before being passed to the program. If the parameter is not a variable – the parameter is passed through as is. The program gets passed a list of (null terminated values)
  • p1 value
  • p2 value
  • p3 value

If you have code

a = "COLIN A"
address linkpgm CPQUERY "A B C"

The parameters received by the program are

  • “COLIN A”
  • “B”
  • “C”

If you pass a string with hex value in it – it may have a 0x00 – which would look like an end of string. This means the linkpgm is not a good interface for passing non character data.

Passing back values

With address link you can pass back a return code. You can also use Rexx services to get and set variables in the calling rexx programs.

With address linkmvs and address linkpgm you can update the values passed in (this is non trivial). You can set a return code and use Rexx services to get and set variables in the calling rexx programs.

Which should I use?

address link

The simplest, where you have greatest control is address link. Your program gets passed a string which it can process.

address linkmvs and address linkpgm

These are similar, and you need to be careful.

A = "COLINA" 
B = ""
P = "A B C"
(1) address linkmvs CPLINKM "A B C "
(2) address linkmvs CPLINKM "P"
(3) address linkmvs CPLINKM P

With the above, my program produces.

(1)
parm 0 >COLINA<
parm 1 is Null
parm 2 >C<

(2)
parm 0 >A B C<

(3)
parm 0 >COLINA<
parm 1 is Null
parm 2 >C<

If you are using linkpgm or linkmvs, the parameter specified will be substituted (if there is a variable with the same name).

my_stem. = "used for variables"
address linkmvs CPMVS "my_stem. "

If you want to specify a stem to your program so you can set my_stem.0, my_stem.1 etc you will not be using the value you specify, it will be used for variables.0 etc.

Using

address linkmvs CPLINKM "A 'B' C "

gives return code -2.

The return code set in RC may be -2, which indicates that processing of the variables was not successful. Variable processing may have been unsuccessful because the host command environment could not: Perform variable substitution before linking to or attaching the program.

Calling C from Rexx – writing the program.

You can write a Rexx external function in C, have it process the parameters, and return data.

I found writing a program to do this was non trivial, and used bits of C I was not familiar with.

See Calling C from Rexx – accessing variables.

If you use the address… environment, the C program does not pass parameters using the normal “main” interface;

int main( int argc, char **argv ){}

because the parameters are passed in via register 1, and you need a different technique.

Header files

There are header files in SYS1.SIEAHDR.H for the Rexx facilities.

#include <irxefpl.h> // REXX External Functions Parameter List 
#include <irxargtb.h> // REXX Argument Table control block mapping
#include <irxshvb.h> // REXX Shared Variable Request Block
#include <irxenvb.h> // REXX Environment Block
#include <irxexte.h> // REXX access to shared variables etc

Program for address link …

With this, the parameters are passed in one string. The program is given the address and the length of the string.

#pragma runopts(plist(os)) 
struct plistlink {
char ** pData;
int * len;
};
int main() {
struct plistlink * pLink = (struct plistlink *)__R1;
int k = * pLink -> len;
char * pc = *pLink -> pData;
printf("plistlink length %i %.*s\n",k,k,pc);
return 0;
}

The important things to note here are

  • #pragma runopts(plist(os)) this says that the parameters are in standard z/OS parameter format based off register 1.
  • int main() this defines the entry point, and sets up the C environment.
  • __R1 is a special #define which returns the value of register one on entry to the routine.
  • struct plistlink * pLink = (struct plistlink *)__R1; defines the input parameter list.

Program for address linkmvs…

Thanks to David Crayford for his assistance in this.

With address linkmvs the specified parameters are treated as variables and their values substituted. If a variable does not exist with the name, the value is passed as-is.

On entry register 1 points to a list of pointers to data. The last element in the list has the top bit on. Process the list until the top bit is on. For example for the parameter list with values “AAA BB C” (which are not substituted)

  • addr1 -> 0X0003″AAA”
  • addr2 -> 0X0002″BB”
  • *addr3 -> 0x001″C”

where * has the top bit on.

If there are no parameters the parameter list is

  • *addr -> 0x0000…
#pragma runopts(plist(os)) 
#define EOL(a) (((unsigned int)a) & 0x80000000)

struct plist {
short len;
char parm[0];
};
int main() {
for ( i = 0; ; i++ ) {
struct plist *p = __osplist[i];
if ( p->len ==0 ) printf("parm %i is Null\n",i);
if ( p->len > 0 )
printf( "parm %i >%.*s<\n",i, p->len, p->parm );
if ( EOL(__osplist[i]) ) break;
}

Where __osplist is a special #define for the parameters based off register 1.

Return code

In both cases you can use the C return … to return an integer value.

Calling C from Rexx – accessing variables.

Usually your program can access variables in the calling Rexx program. You can get, set or delete variables. It is sometimes more complex that the documentation implies.

If your program is called from ISPF you can also set and get ISPF variables, or use ISPF tables to pass data.

To use the Rexx variable interface the applications need access to the ENVironmentBlock (ENVB).

Getting to the ENVB

The Rexx documentation describes how this is passed in register 0, unfortunately a C program does not have access to register 0 (without writing some assembler glue code).

You can get the environment block by calling IRXINIT and passing the parameter “FINDENVB”.
I was unable to use fetch() to dynamically load IRXINIT. (It may work – I couldn’t get it to). Initially I user the binder to include the IRXINIT code, but this is not good practice as you should use the version from the system you are running on.

A better way based on code from David Crayford (thank you) is

#include <stdio.h>                     
#include <stdlib.h>
#include <string.h>
// specify how the irxinit routine is called
#pragma linkage(tsvt_rexxfunc_t,OS)

int main( int argc, char **argv ) {
struct envblock * pEnv;
int rc;
// the TSO anchor block
struct TSVT {
char dummy[140];
char * irxinit;
};

#pragma pack(1)
// this defines a function with parmlist(char *... )
// returning an int
typedef int tsvt_rexxfunc_t( char *, ... );

typedef struct tsvt TSVT;
// TSTV comes from ikjtstv - but no C header equivalent
// so fake one up.
struct tsvt {
int padding[35];
tsvt_rexxfunc_t *irxinit; // Address of IRXEXEC
};
#pragma pack(reset)

int rc2;
// now chain through the control blocks to find the rexx init
// cvtmap provided in SYS1.SIEAHDR.H(CVT)
#define CVTPTR 16L
struct cvtmap * cvt =*( (struct cvtmap ** ) CVTPTR);
// Comment: I think the *((xxx **)) is so unnatural, and always
// get it wrong.

// TSTV comes from ikjtstv - but no C header equivalent
TSVT * tsvt = cvt->cvttvt;
tsvt->irxinit("FINDENVB ",
0, //
0, // instor plist
0, // 4 user field
0, // 5 reserved
&pEnv , // 6 ->envblock
&rc2); // rexx parm 7 return code
printf(" rc2 %i\n",rc2);
}

Set a symbol

This took me a couple of hours to get right. The documentation is not clear in places.

char dummy; 
struct shvblock shv;
memset(&shv,0,sizeof(shv));
char * pSymbol = "COLINSSYMBOL";
char * pValue ="VALUECP";
shv. shvcode = 'S'; // SHVSTORE; // symbolic name set
shv. shvnama = pSymbol; // a symbolic name
shv. shvnaml = strlen(pSymbol); // Len symbolic name
shv. shvvala = pValue ;
shv. shvvall = strlen(pValue);
int rc3;
struct irxexte * pExte = (struct irxexte * ) pEnv-> envblock_irxexte;
tsvt_rexxfunc_t * fptr = (tsvt_rexxfunc_t *) pExte -> irxexcom;

rc = (fptr)("IRXEXCOM",
&dummy,
&dummy,
&shv,
&pEnv,
rc3);
rc2 = shv.shvret;
printf("post rc %i rc2 %i rc3 %i\n",rc,rc2,rc3);

Notes:

The Rexx header file provides

#define  SHVSTORE  "S"             /* Set variable from given value          */    

but you cannot use this because shv. shvcode expects a char ‘S’ , not “S”.

tsvt_rexxfunc_t is used to define the function at address fptr as a z/OS routine with parameter list in register 1, and the high end bit of the last parameter turned on.

After this executed, and the program returned, the Rexx “say COLINSSYMBOL” printed “VALUECP” so it was a success.

A slightly harder case of setting a value.

I put the above code into a subroutine so I was able to use

setSymbol('S',"COLINSSYMBOL","VALUECP");

You can use option upper case ‘S’ which takes the string you give it, and makes a Rexx variable, or you can use the lower case ‘s’ option, which says it does variables substitution.

Uppercase: (The Direct interface). No substitution or case translation takes place. Simple symbols must be valid REXX variable names (that is, in uppercase and not starting with a digit or a period), but in compound symbols any characters (including lowercase, blanks, and so on) are permitted following a valid REXX stem.

This is not entirely true.

With upper case ‘S’

With setSymbol(“MYKEY.aaa“,”VALUECP”), Rexx displayed “MYKEY.AAA” showing the variable did not exist, even though the call to defined it worked successfully.

With setSymbol(“MYKEY.AAA“,”VALUECP”), Rexx displayed “VALUECPA” showing the correct value.

If you are using ‘S’ then always specify the name in upper-case despite what the documentation says.

With lower case ‘s’

Both

setSymbol("MYKEY.aaa","VALUECPA"); 
setSymbol("mykey.bbb","VALUECPA");

worked.

But it gets more complex…

I had a small Rexx program:

/* REXX */
A = "COLINA"
address link CPLINK "A B C "
drop A
say value("MYKEY.A")
say value("MYKEY.B")
say value("MYKEY.COLINA")
say value("A")
say value("SMYKEY.A")
say value("SMYKEY.B")
say value("SMYKEY.COLINA")

If value(“MYKEY.A”) is “MYKEY.A” then there is no variable with that name.

and my program had

setSymbol('S',"MYKEY.A","BIGSA"); 
setSymbol('S',"MYKEY.B","BIGSB");
setSymbol('s',"SMYKEY.A","SMALLSA");
setSymbol('s',"SMYKEY.B","SMALLSB");

The output had

  1. say value(“MYKEY.A”) -> “BIGSA” from my program
  2. say value(“MYKEY.B”) -> “BIGSB” from my program
  3. say value(“MYKEY.COLINA”) -> “MYKEY.COLINA” not a variable
  4. say value(“SMYKEY.A”) -> “SMYKEY.A” not a variable
  5. say value(“SMYKEY.B”) -> “SMALLSB” set from my program
  6. say value(“SMYKEY.COLINA”) -> “SMALLSA” ‘.A’ was substituted with COLINA as part of the set call
  • Lines 1-3 show that there was no substitution of variables.
  • Lines 4 shows that variable SMKEY.A was not created; SMKEY.COLINA was substituted
  • Line 5 had no substitution and was like line 2
  • Line 6 this is the variable name used.

This means that if you specify a lower case ‘s’, the output may not be as you expect. I would suggest you use upper case ‘S’ unless you know what you are doing.

C includes ” is not the same as <

The one line description: I found the difference between #include <a.h> and #include “a.h”.

I had problems finding compiling some sample C code which included

#include <__iew_api.h>

I was getting

WARNING CCN3296  #include file <__iew_api.h> not found.

There were a couple of problem. The length of the name __iew_api is more than 8 characters, so will not be in a PDS or PDSE. I found it in /usr/include.

In my JCL for comping C programs I had

LSEARCH(/usr/include/)

But still it was not found. After a cup of tea and walk down to the shops I wondered if it was the include that was causing the problems.

I changed it to

#include "__iew_api.h" 

and it found it. The documentation says

The file_path can be an absolute or relative path. If the double quotation marks are used, … the preprocessor adds the directory of the including file to the list of paths to be searched for the included file.
If the double angle brackets are used, … the preprocessor does not add the directory of the including file to the list of paths to be searched for the included file.

Using Rexx under z/OS Unix Services to display thread information

I wanted to display information about threads running in a big Java application, and see what was executing during set up.

Rexx can provide information on threads, and so I was able to create a script which provided process thread data. There was documentation, but it was not very clear, so I’m documenting what I learned

My program

/* rexx */ 
do k = 1 to 20
call procinfo
say "jobname asid ppid pid threadid tcb cmdline"
do i=1 to bpxw_pid.0
x =procinfo(bpxw_pid.i,'process')
if x = '' then iterate
if bpxw_LOGNAME.i <> "ZWESVUSR" then iterate
y = procinfo(bpxw_pid.i,'thread')
if y = '' then iterate
do j=1 to bpxw_threads
xtcb = d2x( bpxw_TCB.j)
say bpxw_JOBNAME d2x(bpxw_ASID) right(bpxw_PPID,8),
right(bpxw_PID,8) bpxw_THREAD_ID.j xtcb bpxw_CMDLINE
end
end
sleep(1)
end
  • call procinfo returns a list of process ids in a stem bpxw_pid. and userids in a stem bpxw_LOGNAME
  • do i=1 to bpxw_pid.0 iterate through the list of the processes
  • x =procinfo(bpxw_pid.i,’process’) get the process information about the process id in bpxw_pid.i.
  • if x = ” then iterate if the thread no longer exists – do the next one
  • if bpxw_LOGNAME.i <> “ZWESVUSR” then iterate ignore threads from other userids
  • y = procinfo(bpxw_pid.i,’thread’) get the thread information for the process id in bpxw_pid.i.
  • if y = ” then iterate if no data is returned skip this
  • do j=1 to bpxw_threads for each thread in the process …
  • xtcb = d2x( bpxw_TCB.j) convert the thread TCB from decimal to hex.
  • say …
    • bpxw_JOBNAME this is from the process information
    • d2x(bpxw_ASID) display the ASID of the process in hex
    • bpxw_THREAD_ID.j the thread id.

Java persistent shared classes cache on z/OS

With Java shared classes cache, by default on z/OS saves the data in shared memory. You can use the snapshot command to save a copy on disk, and use the restore command after IPL to recreate it. For my zPDT system running z/OS on a Linux server this many seconds of start up time.

In more recent Java versions, the Shared Classes Cache has supported the persistent option, where shared virtual storage is mapped to a file – and so updating memory, updates the file.

I had a few problems getting this to work, and there was no documentation on the use of the persistent option.

When I enabled it, for example with

 -Xshareclasses:name=zoweGW,cacheDirPerm=0777,cacheDir=/u/tmp/zowec/,persistent" 

I got

JVMSHRC245E Error mapping shared class cache file 
JVMSHRC336E Port layer error code = -155
JVMSHRC337E Platform error message: EDC5132I Not enough memory.
JVMSHRC840E Failed to start up the shared cache.
JVMJ9VM015W Initialization error for library j9shr29(11): JVMJ9VM009E J9VMDllMain failed
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

I had to change the SMFLIMxx parmlib member to fix this

Display the current SMFLIM configuration

You can display your current SMFLIMxx configuration using

d smflim
d smflim,r

The d smflim,r gave me

...
Member and rule number SMFLIMCP 0003
User:
ZWESVUSR
Attributes:
EXECUTE: NOCHANGE
JOBMSG: ISSUE
MAXSHARE: 9000000
...

Which shows the rule for user ZWEVSUR came from the third rule in SMFLIMCP. It sets MAXSHARE, and other parameters.

Update the member

I updated my SMFLIMCP member to be

REGION USER(COLIN) JOBMSG(ISSUE) MAXSHARE(90000) 

activated it using t SMFLIM=(CP,C2) where CP2,C2 is my list of SMFLIM members. Note: The T SMFLIM command, replaces all of the definitions with what is in the list, so you need to specify the whole list, not just the changed member.

The definitions become active immediately, you do not need to logoff and logon, or resubmit a job.

When the Java job had started, it created a file C290M21F1A64P_hw_G43L00 in the specified directory.

When persistent was not specified, files were stored in the javasharedresources subdirectory.

Should I use this persistent option?

You have the choice of using the persistent option in the -Xshareclasses…persistent parameters, or not to specify it. If you do not use the persistent option you need to save the shared memory across IPLs, by using -Xshareclasses:…,snapshotCache and restoring it after an IPL using -Xshareclasses:…restoreFromSnapshot. I used this method, and added a steps to my started tasks, one to restore (if the cache exists already, it does nothing), and one at the end, to save it.

How does the performance compare?

On my zPDT system which is not meant to be used for performance evaluations, they both had similar durations, and used similar amounts of CPU, though non persistent was usually slight better.

Funny…

I also go message

JVMSHRC561E Failed to initialize the shared classes cache, there is not enough space in the file system. Available free disk space bytes = 516144128, requested bytes = 536870912.

Which was a surprise as I thought I had enough free disk space.

Creating and running a jar file

You can start a Java program by specifying the class name and the class path to use (containing .class and/or .jar files), or you can specify just a self contained .jar file, which contains all it needs. I wont describe Java Modules ( Java Platform Module System, .jmod -> stronger encapsulation, improved dependency management, better code organization, enhanced security, streamlined JRE, and potentially faster application startup times), or a Java Run Time image (.jrt, which includes only the necessary modules required for a specific application, making it smaller, safer, and capable of starting faster than a full JRE).

Create class files

The documentation for Java says you have to create a manifest so Java knows which classes to use.

I have a simple program hw.java

class HelloWorld { 
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}

I compiled it with

/usr/lpp/java/J21.0_64/bin/javac hw.java

This creates .class files for each class in the original source.

  • HelloWorld.class

It will start the function “main” within the class.

Create a jar file and specify the entry point

Java needs to know the entry point class of the jar file. The code below creates the jar file, specifying the entry point class name ( -e ..) and includes all of the classes specified (just HelloWorld.class).

The next line runs the jar file.

/usr/lpp/java/J21.0_64/bin/jar -c -f hw.jar -e HelloWorld HelloWorld.class
/usr/lpp/java/J21.0_64/bin/java -jar hw.jar

Under the covers it creates a manifest file META-INF/MANIFEST.MF . It is not in the default code page, so to look at it, I expanded the jar file, used oedit META-INF/. , then used the line command ea to edit it in ASCII.

Manifest-Version: 1.0 
Created-By: 21.0.4 (IBM Corporation)
Main-Class: HelloWorld

Create a jar file and specify a manifest.

Java needs to know the entry point class name of the jar file. You can specify it in the manifest. The code below creates the jar file, using the specified manifest file, and includes all of the classes specified (just HelloWorld.class). The manifest is the one in the previous section.

The next line runs the jar file

/usr/lpp/java/J21.0_64/bin/jar -c -f hw.jar-m META*/MAN*.MF HelloWorld.class 
/usr/lpp/java/J8.0_64/J21.0_64/bin/java -jar hw.jar

When you create a jar file and do not specify a manifest or entry point

The code below creates the jar file, using the classes specified (just HelloWorld.class), but does not include a manifest.

If you try running the jar file it complains about no manifest.

You can run the jar file in the class path (-cp) and specify the entry class as a parameter

/usr/lpp/java/J21.0_64/bin/jar -c -f hw.jar HelloWorld.class

COLIN:/u/tmp/java: >/usr/lpp/java/J21.0_64/bin/java -jar hw.jar
no main manifest attribute, in hw.jar

/usr/lpp/java/J21.0_64/bin/java -cp hw.jar HelloWorld

It’s not quite that simple…

I was looking at some Jar files, and the manifest does not have a Main-Class entry, and there is no entry point specify on the java command – so there is clearly another way of specifying the entry point.

Programming shared memory – more head banging.

I was trying to use shared memory (to look at Java Shared Classes), and it took me a day to get it working – better documentation would have helped.

I had two basic problems

  1. Using smctl to display information about the shared memory, gave the size as 0 bytes, even though the ipcs command showed me there were megabytes of data in the shared memory area.
  2. Trying to attach the shared memory gave me “invalid parameters” return code – even though the documented reasons for this error code did not apply to my program.

I tried many things, from using different userids, to running with a different storage key, running APF authorised….

I eventually got it to work by compiling my C program in 64 bit mode rather than 31 bit mode. There is no discussion about 31 bit/64 bit in the documentation. If the shared memory in 64 bit mode, you will need 64 bit addressability, so you need a 64 bit program. But there is no way of determining that the shared memory is 64 bit!

My basic program

{ 
//struct shmid_ds buf;
struct shmid_ds64 buf;
memset(&buf ,0,sizeof(buf));
int shmid = 8197;
int rc = 0;
long l;
int cmd = IPC_STAT;
char * fn = "COLIN";
int shmflg =0;
shmflg = IPC_STAT;
// rc =shmctl(shmid, cmd, &buf);
rc =shmctl64(shmid, cmd, &buf);
perror("shmctl " );
printf("shctl rc %i\n",rc);
l = buf.shm_segsz;
printf("size %ld\n",l);
printHex(stdout,&buf,sizeof(buf));
///////////////////////////////////////////////
// shmat
///////////////////////////////////////////////
char * pData = NULL;
pData = shmat(shmid, NULL , 0 );
printf("Address %ld\n",pData);
printHex(stdout,pData+4096*1024,1024*1024);
int e = errno;
perror("shmat ");
printf("Errno: %s\n",strerror(e));
return 0;
}

Originally I was using EDCCB to compile and bind this.

The EINVAL error return code was (from the documentation) for cases where the pointer in shmat was non NULL. I was passing NULL – so none of this made sense.

The reason code 0717014A was

JRInvalidAmode: An incorrect access mode was specified on the access service
Action: The access mode specified on the access service has unsupported bits turned on. Reissue the request and specify a valid access mode.

It turned out that my program was 31 bit. When I used 64 bit – it magically started working.

I compiled it with EDCQCB, and had to change a few things to be 64 bit mode.

  • shmid_ds buf -> shmid_ds64
  • shmctl -> shmctl64

When I ran it in 31 bit mode, the length of the storage returned was 0. In 64 bit mode, it gave the correct length. This looks like a way of telling what mode the shared memory is!

Notes on shared storage

Shared storage is a Unix concept, which works on Unix on z/OS.

It allows you to share data between threads, and between different address spaces. Java Shared Classes exploit this. Java classes can be stored in the shared storage, and “Ahead Of Time” compilation can be stored in the shared cache. Next time a JVM starts, it can look in the shared cache, and use the already compiled class.

Each shared storage segment has a key which you use in the various shared memory API calls.

There are no real restrictions, in some of the documentation on the web, it suggests using 1234 as a key! This has its limitations. A better way, used by Java is based on the inode of a file

You can display information about InterProcess Communication Status (ipcs) using the ipcs command.

You can display shared memory, and shared semaphores.

ipcs command lists…

Shared Memory:                                                           
T ID KEY MODE OWNER GROUP
m 8196 0x00000000 --rw------- WEBSRV WEBGRP
m 73733 0x6137142d --rw------- OMVSKERN SYS1
m 73734 0x6137e82f --rw------- ZWESVUSR ZWEADMIN
m 139271 0x6100410f --rw------- ZWESVUSR ZWEADMIN
m 8200 0x610ea00f --rw------- OMVSKERN SYS1

The Java shared class files on my system are

767 -rw-r--r--   1 ZWESVUSR ZWEADMIN  ... C290M17F1A64_semaphore_zoweGW_G43L00             
768 -rw-r--r-- 1 ZWESVUSR ZWEADMIN ... C290M17F1A64_memory_zoweGW_G43L00

Inode 767 is 0x2ff, Inode 768 is 0x300.

0x61… inode…. 2d

061 because created with t = ftok(fn,idi );

Shared Memory:                                                          
T ID KEY MODE OWNER GROUP
m 8196 0x00000000 --rw------- WEBSRV WEBGRP
m 139269 0x6137142d --rw------- OMVSKERN SYS1
m 73734 0x6137e82f --rw------- ZWESVUSR ZWEADMIN
m 139271 0x6100410f --rw------- ZWESVUSR ZWEADMIN
m 8200 0x610ea00f --rw------- OMVSKERN SYS1
Semaphores:
T ID KEY MODE OWNER GROUP
s 135172 0x8137132d --ra------- OMVSKERN SYS1
s 69637 0x8137e72f --ra------- ZWESVUSR ZWEADMIN
s 135174 0x8100400f --ra------- ZWESVUSR ZWEADMIN
s 69639 0x8c10840f --ra-ra-ra- ZWESVUSR ZWEADMIN
s 135178 0x810e9f0f --ra------- OMVSKERN SYS1
s 69643 0x8c108a0f --ra-ra-ra- ZWESVUSR ZWEADMIN

More details

You can use

ipcs -m -a

to display more information including size, and number of users.