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.
Example 7-2 does the following:
The actor started by an arun command allocates a memory region, then spawns an actor running the same executable file using the afexecve() call.
The spawned actor uses the parameters set up by the spawning actor to establish a sharing of memory.
Some data is passed through the shared memory from the spawning to the spawned actor. A semaphore allocated in the shared memory area, and initialized by the spawning actor, is used to synchronize both actors. Thus, the first actor will know when the spawned actor has changed the contents of the shared area.
Both actors then terminate.
The spawned actor should retrieve the information needed to establish the mapping from its arguments.
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.
(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, ¶m , 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; }
Semaphores have been tuned to be highly optimized. There are thus some constraints on their use: they may be used freely in a region of shared memory between two user actors, even though the shared area is not mapped at the same address in each actor. In supervisor space, a semaphore cannot be accessed using two different addresses. As supervisor address space is partitioned common space, there is no real need to invoke the rgnMapFromActor() service. Finally, it is not possible to use a semaphore in a memory region shared between a user actor and a supervisor actor.
The above example would work in a similar fashion if the spawned actor did not impose the address of the created region, but used the K_ANYWHERE flag instead.
The spawned actor maps only one page from the region created by the spawning actor, although this region is three pages long. Access to an address beyond the shared page would result in access to private data for the spawning actor, and in a memory fault for the spawned actor.
The above example does not invoke the rgnFree() call. The region in the spawned actor will be freed at exit time. This does not mean that the physical memory will be freed as soon as the target actor disappears. Physical memory will be effectively freed when both actors have exited, regardless of the order in which they terminate.