ChorusOS 5.0 Application Developer's Guide

Managing Memory

A memory management module may support several different user address spaces and perform memory context switches when required in thread scheduling. Actors within the ChorusOS operating system environment may extend their address space using the malloc(3STDC) library call. However, this is an inflexible way of allocating memory because there is no way to control the attributes of the allocated memory, that is, whether it is a read-only or a read/write memory area. The malloc(3STDC) routine uses the ChorusOS operating system services described in this section. These services can also be used to share a region of memory between two or more actors.


Note -

The hot restart feature provides support for persistent memory (memory that can extend beyond the lifetime of the runtime instance of an actor). Hot restart is not covered in this chapter. For information about using hot restart and the persistent memory services it provides, see Chapter 15, Recovering From Application Failure: Hot Restart.


Memory Region Descriptors

The ChorusOS operating system offers various services which enable an actor to extend its address space dynamically by allocating memory regions. An actor may also shrink its address space by freeing memory regions. A memory area to be allocated or freed is identified by the system through a region descriptor of the following type:

	typedef struct {
		VmAddr    startAddr;
		VmSize    size;     
		VmFlags   options;
		VmAddr    endAddr;
		void*     opaque1;
		VmFlags   opaque2; 
	} KnRgnDesc;

The startAddr field defines the starting address of the memory region, while the size field defines its length (expressed in bytes). The options field enables low level control over the attributes of the memory region to be allocated. The opaque1 and opaque2 fields should be set to NULL if they are not being used by the application.

The options field consists of a number of flags, of which the following are the most important:

Additional options are available on a ChorusOS operating system configured with the virtual memory feature. The most important of these enables control of the swap policy to be applied to the pages of the created memory region:

Allocating and Freeing Memory Regions

A memory region is allocated through the following call:

#include <chorus.h>
int rgnAllocate(KnCap*				actorCap,
                KnRgnDesc*			rgnDesc);		

This call frees a memory region within the address space of an actor. The memory region is described by the rgnDesc parameter and the actor is defined by the actorCap parameter.

Figure 8-1 Allocating and Freeing Memory Regions

Graphic

The following example includes these steps:

The illustration in Figure 8-1 shows only the main steps of the example. For more information, refer to the rgnAllocate(2K) and rgnFree(2K) man pages.


Example 8-1 Allocating a Memory Region

(file: progov/rgnAlloc.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <chorus.h>
#define RGN_SIZE_1 (6 * vmPageSize())
#define RGN_SIZE_2 (3 * vmPageSize())
#define FREE_START (2 * vmPageSize())
#define FREE_SIZE  (4 * vmPageSize())
#define STILL_ALLOC_START (FREE_SIZE - (RGN_SIZE_2 - FREE_START))
int main(int argc, char** argv, char**envp)
{
  KnRgnDesc           rgnDesc;
  KnActorPrivilege    actorP;
  int                 res;
  VmFlags             rgnOpt = 0;
  char*               ptr1 = NULL; /* Avoids "unexpected" warning */
  char*               ptr2 = NULL; /* Avoids "unexpected" warning */
  printf("Starting rgnAllocate example\n");         
  res = actorPrivilege(K_MYACTOR, &actorP, NULL);
  if (res != K_OK) {            
    printf("Cannot get actor privilege, error %d\n", res);       
    exit(1);             
  }              
  if (actorP == K_SUPACTOR) {
    rgnOpt = K_SUPERVISOR;
  }
  rgnDesc.size = RGN_SIZE_1;
  rgnDesc.options = rgnOpt | K_WRITABLE | K_FILLZERO | K_ANYWHERE;
  rgnDesc.opaque1 = NULL;
  rgnDesc.opaque2 = NULL;
      /*
       * No need to set rgnDesc.startAddr
       * since we set the K_ANYWHERE flag
       */
  res = rgnAllocate(K_MYACTOR, &rgnDesc);
  if (res == K_OK) {
    printf("Successfully allocated memory starting at 0x%x\n", 
    rgnDesc.startAddr);
    ptr1 = (char*) rgnDesc.startAddr;
  } else {
    printf("First rgnAllocate failed with error %d\n", res);
    exit(1);
  }
  strcpy(ptr1, "Fill the allocated memory with this string\n");
      /* 
       * Second allocate has a fixed address, such that
       * both memory areas will be contiguous. Hence
       * we do not want the K_ANYWHERE flag any more.
       */
  rgnDesc.size       = RGN_SIZE_2;
  rgnDesc.options   &= ~K_ANYWHERE;
  rgnDesc.startAddr -= RGN_SIZE_2;
  res = rgnAllocate(K_MYACTOR, &rgnDesc);
  if (res == K_OK) {
    printf("Successfully allocated memory starting at 0x%x)\n", 
    rgnDesc.startAddr);
    ptr2 = (char*) rgnDesc.startAddr;
  } else {
    printf("Second rgnAllocate failed with error %d\n", res);
    exit(1);
  }
      /* Copy from first allocated area to second one */
  strcpy(ptr2, ptr1);
      /* 
       * Free a memory area spanning both areas 
       * previously created
       */
  rgnDesc.options = NULL;
  rgnDesc.startAddr = (VmAddr) (ptr2 + FREE_START);
  rgnDesc.size      = FREE_SIZE;
  res = rgnFree(K_MYACTOR, &rgnDesc);
  if (res != K_OK) {            
    printf("Cannot free memory, error %d\n", res);        
    exit(1);             
  }              
      /* 
       * Access to ptr2: beginning of secondly allocated area
       * is still valid.
       * Access to ptr1 is now invalid: memory has been freed.
       */
  printf("%s", ptr2); 
      /*
       * Access to "end" of first allocated area
       * is still valid
       */
  ptr1 += STILL_ALLOC_START;
  strcpy(ptr1, ptr2);
      /*
       * Remaining memory areas not yet freed will 
       * effectively be freed at actor termination time.
       */
  return 0;
}

Region descriptors are only used to describe a creation or deletion operation on the system. They are not kept by the system because of the way in which they are given to the rgnAllocate(2K) call. For example, an allocation of two contiguous areas with the same attributes (writable, fill zero) and the same opaque fields results in the system recognizing a single region, the size of which is the sum of the sizes passed as part of the two region descriptors.

You cannot allocate a region in a range of addresses that are not free. No implicit deallocation of the address space is undertaken by the system. Instead an error code K_EOVERLAP is returned to the caller.

A call to rgnFree(2K) does not reuse a region descriptor that was previously used to allocate a memory area. A free operation can freely span several regions that were allocated by separate operations. Also, a free operation can only free a chunk of memory in the center of a large memory area that was allocated in a single operation.

Only the precise region described by the region descriptor can be freed. The free operation is not extended to match the address range which was allocated at rgnAllocate(2K) time.

The options field of the region descriptor must be set to 0 to use the free operation. If the options field is set to K_FREEALL, all memory regions of the actor will be freed (the code, the data, and the stacks). The K_FREEALL option should therefore be used with care.

All memory areas that have been dynamically allocated are freed when an actor terminates.

Sharing Memory Between Applications

The following code example shows how to share memory between applications.


Example 8-2 Sharing Memory Between Actors

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <chorus.h>
#include <cx/afexec.h>

AcParam param;

#define RGN_SIZE  (3 * vmPageSize())
#define SHARED_RGN_SIZE (1 * vmPageSize())

typedef struct sampleSharedArea {
  KnSem    sem;
  char     data[1];
} sharea_t;

char capString[80];
char sharedAddr[20];

int main(int argc, char** argv, char**envp)
{
  KnRgnDesc    rgnDesc;
  sharea_t*    ptr;
  KnCap        spawningCap;
  KnCap        spawnedCap;
  int          res;
  VmFlags      rgnOpt = 0;
  KnActorPrivilege actorP;

  res = actorPrivilege(K_MYACTOR, &actorP, NULL);
  if (res != K_OK) {                                                  
    printf("Cannot get privilege, error %d\n", res);                  
    exit(1);                                                          
  }                                                                   
                                                                      
  if (actorP == K_SUPACTOR) {
    rgnOpt = K_SUPERVISOR;
  }

  if (argc == 1) {
        /*
         * This is the first actor (or spawning actor):
         *   Allocate a memory region
         *   Initialize a semaphore within the region
         *   Spawn the second actor
         *   Wait on the semaphore
         *   Get data written in shared mem by spawned actor
         *   Terminate
         */
    rgnDesc.size = RGN_SIZE;
    rgnDesc.options = rgnOpt | K_ANYWHERE | K_WRITABLE | K_FILLZERO;
    rgnDesc.opaque1 = NULL;
    rgnDesc.opaque2 = NULL;

    res = rgnAllocate(K_MYACTOR, &rgnDesc);
    if (res != K_OK) {                                                
      printf("Cannot allocate memory, error %d\n", res);              
      exit(1);                                                        
    }                                                                 

    ptr = (sharea_t*) rgnDesc.startAddr;

    strcpy(&ptr->data[0], "First actor initializing the shared mem\n");

    res = semInit(&ptr->sem, 0);

        /*
         * Get my own capability and pass it as a string argument
         * to spawned actor, so that it may use it to share memory
         */
    (void) actorSelf(&spawningCap);
    sprintf(capString, "0x%x 0x%x 0x%x 0x%x", spawningCap.ui.uiHead,
            spawningCap.ui.uiTail, spawningCap.key.keyHead,
            spawningCap.key.keyTail);

        /*
         * Pass address of memory to be shared as a string argument
         * to spawned actor.
         */
    sprintf(sharedAddr, "0x%x", ptr);

    param.acFlags = (actorP == K_SUPACTOR)? AFX_SUPERVISOR_SPACE : 
                                            AFX_USER_SPACE;
    res = afexeclp(argv[0], &spawnedCap, &param , argv[0], capString,
                   sharedAddr, NULL);
    if (res == -1) {                                                  
      printf("cannot spawn second actor, error %d\n", errno);         
      exit(1);                                                        
    }                                                                 

    (void) semP(&ptr->sem, K_NOTIMEOUT);

    printf("%s", &ptr->data[0]);

  } else {
    
    KnRgnDesc srcRgn;
    KnRgnDesc tgtRgn;
    unsigned long uHead, uTail, kHead, kTail;
        /*
         * This is the spawned actor:
         *   Get arguments
         *   Set up the memory sharing 
         *   Write some string in shared memory
         *   Wake up spawning actor
         *   Terminate
         */
    sscanf(argv[1], "0x%x 0x%x 0x%x 0x%x", &uHead, &uTail, &kHead, &kTail);
    spawningCap.ui.uiHead = uHead;
    spawningCap.ui.uiTail = uTail;
    spawningCap.key.keyHead = kHead;
    spawningCap.key.keyTail = kTail;

    sscanf(argv[2], "0x%x", &srcRgn.startAddr);

    if (actorP != K_SUPACTOR) {

      srcRgn.size      = SHARED_RGN_SIZE;
      tgtRgn.startAddr = srcRgn.startAddr;
      tgtRgn.size      = SHARED_RGN_SIZE; 
      tgtRgn.options   = rgnOpt | K_WRITABLE;
      tgtRgn.opaque1   = NULL;
      tgtRgn.opaque2   = NULL;

      res = rgnMapFromActor(K_MYACTOR, &tgtRgn, &spawningCap, &srcRgn);

      if (res != K_OK) {
        printf("Cannot share memory, error %d\n", res);
        exit(1);
      }
      ptr = (sharea_t*) tgtRgn.startAddr;

    } else {
          /*
           * Both actors are running in supervisor space,
           * There is no need to perform the rgnMapFromActor.
           * One may use the received shared address.
           */
      ptr = (sharea_t*) srcRgn.startAddr;
    }

        /* Get data from spawning actor */
    printf("Spawning actor sent: %s", &ptr->data[0]);

        /* Modify contents of shared memory */
    sprintf(&ptr->data[0], "Spawned actor mapped shared memory at 0x%x\n",
            ptr);

    res = semV(&ptr->sem);
    if (res != K_OK) {                                                
      printf("Spawned actor failed on semV, error %d\n", res);        
      exit(1);                                                        
    }                                                                 
  }
  return 0;
}