ChorusOS 4.0 Introduction

Chapter 7 Memory Management

Actors within the ChorusOS operating system environment may extend their address space using the malloc() library call as illustrated earlier in this document. However, this is a rather inflexible way of allocating memory, as 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() routine uses the ChorusOS operating system services described in this section. These services may also be used to share a region of memory between two or more actors.

This chapter explains the recommended way of allocating memory for an actor. It contains the following sections:

The ChorusOS hot restart feature provides support for using persistent memory; memory which 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 the ChorusOS 4.0 Hot Restart Programmer's Guide.

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. An area of memory to be allocated or freed is described to 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. The size field defines its length expressed in bytes. The options field enables a low level control on 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 is a combination of flags, of which the following are the most important:

On a ChorusOS operating system configured with the Virtual Memory feature, further options are available. The most important one allows control of the swapping policy to be applied to the pages of the created 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 creates a memory region as described by the rgnDesc parameter within the address space of the actor defined by the actorCap parameter. Most applications set actorCap to K_MYACTOR to manage their own address space.

An unused part of an address space may be freed by the following call:

#include <chorus.h>

int rgnFree(KnCap*     actorCap,
            KnRgnDesc* rgnDesc);

This call frees a memory region as described by the rgnDesc parameter within the address space of the actor defined by the actorCap parameter.

Figure 7-1 Memory Region Allocation and Deallocation

Graphic

Example 7-1 does the following:

The main steps of the example are illustrated in Figure 7-1. Refer to the rgnAllocate(2K) and rgnFree(2K) man pages.


Example 7-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 "uninited" warning */
  char*               ptr2 = NULL;	/* Avoids "uninited" 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;
}


Sharing Memory Between Two Actors

The ChorusOS operating system offers the possibility of sharing an area of memory between two or more actors, regardless of whether these actors are user or supervisor actors. The memory area does not need to be located at the same address within the address space of each actor.

This mechanism is based on the following service:

#include <chorus.h>

int rgnMapFromActor(KnCap*    targetActor,
                    KnRgnDesc*targetRgnDesc,
                    KnCap*    sourceActor,
                    KnRgnDesc*sourceRgnDesc);

This call allows mapping of a memory area as defined by sourceActor and sourceRgnDesc in the address space of the actor defined by the targetActor parameter. The source memory area is explicitly defined by the startAddr and size fields of the sourceRgnDesc parameter. The region created within the address space of the target actor is defined by the targetRgnDesc parameter: the address may be fixed or undefined if the K_ANYWHERE flag is set. After the mapping has been established, both actors may freely use the shared memory area.

Figure 7-2 shows two actors before they share memory and after sharing has been established. Usually some synchronization mechanism is required in order to get a consistent view of the memory: semaphores may be used in shared memory areas to synchronize threads from the various actors.

Figure 7-2 Actors Sharing Memory

Graphic

Example 7-2 does the following:

This example uses a call which enables an actor to get its own capability in order to pass it to another actor.

#include <chorus.h>

int actorSelf(KnCap* myCap);

Refer to the rgnMapFromActor(2K) man page.


Example 7-2 Sharing a Memory Region

(file: progov/rgnMapFromActor.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <chorus.h>
#include <am/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
         */
    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);							      
    }								      

    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;
}