[comp.os.mach] Efficient copying from task to task - one more time

roland@sics.se (Roland Karlsson) (05/21/91)

Maybe I am not so good at mach terminology or maybe I am not so good
at the English language.   Hmmm.  I shall try to restate my question.


I have an UNIX application.  This application consists of a static
number of parallel processes.  One UNIX process is (more or less)
equal to a MACH task with one thread.  The UNIX processes are created
with fork, so they have the same address space.  Of particular
interest are some huge stacks.  Those stacks contain references
(pointers) to other elements in the same or other stacks.  Sometimes
one UNIX process would like to help another one.  Then it need to get
a copy of (parts of) the first process stacks.  The original and the
copy have to be placed at the same addresses.  For efficiency I then
use shared memory implemented with mmap and a paging file.  All
processes can then map different parts of the map file as their stack
area.  Those stack areas shall all be at the same addresses though.
The processes can also map other processes stack areas at arbitrary
addresses.  This way two processes can help each other to copy memory
from one processes (say P) to another (say Q).  Different parts of the
stack areas can be copied in parallel from P to Q without the need to
copy via any buffer.  This is the most optimal way to copy I have find in
UNIX.  I have tried to implement the same behavior in MACH.  But without
success.  It might also be possible to do better with some "virtual
copy method".  Pages could (maybe) be transferred from process P to
process Q in a lazy way without making a physical copy.  The physical
copy should be created when any of P and Q write in the page.

I have tried map_fd and vm_map.   For you that that want too have a look
on what I have done I do include two test programs.  They are both wrong.
The vm_map even more.

This is the version of MACH I use:
2.6 MSD #1.0 (on sun3)


Roland Karlsson






----- map_fd.c ----------------------------------------------------------
#include <mach.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>

int fd;

init_memory(size)
     vm_size_t size;
{
  int err;

  fd = open("tmpfile", O_RDWR | O_CREAT, 0666);

  if (fd < 0)
    { perror("open");
      exit(1);
    }

  err = lseek(fd, size, L_SET);
  if (err < 0)
    { perror("lseek");
      exit(1);
    }

  err = write(fd, "", 1);
  if (err < 0)
    { perror("write");
      exit(1);
    }

  err = ftruncate(fd, size);
  if (err < 0)
    { perror("ftruncate");
      exit(1);
    }
}



char *map_memory(address, size, offset)
     char *address;
     vm_size_t size;
     vm_offset_t offset;
{
  kern_return_t ret;
  boolean_t anywhere = (address == NULL);
  vm_offset_t *addr = (vm_offset_t *)&address;

  fprintf(stderr,
	  "%8smap_fd(fd=%x,off=%x,addr=%x@%x,any=%x,siz=%x)\n",
	  "",
	  fd,
	  offset,
	  *addr, addr,
	  anywhere,
	  size
	  );

  ret = map_fd(fd,		/* The file */
	       offset,		/* Offset inside memory object */
	       addr,		/* Pointer to in/out address */
	       anywhere,	/* TRUE=>system free to choose address */
	       size		/* Size */
	       );

  if (ret != KERN_SUCCESS)
    { mach_error("vm_map: ",ret);

      exit(1);
    }

  return address;
}

/* remote_adr is the remote address for a shared area.
   local_adr is the local address for a shared area.
   private_adr is the local address for a private area.
   */

main()
{
  char *remote_adr = NULL, *local_adr = NULL, *private_adr = NULL;
  int child;

  init_memory(vm_page_size*0x20);

  remote_adr = map_memory(remote_adr, vm_page_size, 0);

  printf("parent: mapping remote_adr at %x\n", remote_adr);

#ifdef MAP_BEFORE_FORK
#ifdef LOCAL_ANYWHERE
  local_adr = NULL;
#else
  local_adr = remote_adr + vm_page_size*0x10;
#endif
  local_adr = map_memory(local_adr, vm_page_size, 0);
  printf("child:  mapping local_adr at %x\n", local_adr);
#endif

  child = fork();
  
  if (child == -1)
    { perror("fork:");
      exit(1);
    }

  if (child != 0)
    {
      printf("child:  Fork\n");
      sleep(10);

#ifndef MAP_BEFORE_FORK
#ifdef LOCAL_ANYWHERE
      local_adr = NULL;
#else
      local_adr = remote_adr + vm_page_size*0x8;
#endif
      local_adr = map_memory(local_adr, vm_page_size, 0);
      printf("child:  mapping local_adr at %x\n", local_adr);
#endif

      private_adr = map_memory(private_adr, vm_page_size, vm_page_size);
      printf("child:  mapping private_adr at %x\n", private_adr);

      printf("child:  Read %3d@%-7x (Remote)%s\n",
	     remote_adr[0], remote_adr,
	     remote_adr[0]==10 ? "" : "     (should be: 10)");
      printf("child:  Read %3d@%-7x (Local)%s\n",
	     local_adr[0], local_adr,
	     local_adr[0]==10 ? "" : "     (should be: 10)");
      printf("child:  Read %3d@%-7x (Private)%s\n",
	     private_adr[0], private_adr,
	     private_adr[0]==0 ? "" : "     (should be:  0)");
    }
  else
    {
      printf("parent: Fork\n");

#ifndef MAP_BEFORE_FORK
#ifdef LOCAL_ANYWHERE
      local_adr = NULL;
#else
      local_adr = remote_adr + vm_page_size*0x10;
#endif
      local_adr = map_memory(local_adr, vm_page_size, 0);
      printf("child:  mapping local_adr at %x\n", local_adr);
#endif

      private_adr = map_memory(private_adr, vm_page_size, vm_page_size);
      printf("parent: mapping private_adr at %x\n", private_adr);

      remote_adr[0] = 10;
      printf("parent: Wrote %d@%-7x (Remote==Local)\n",
	     remote_adr[0],remote_adr);

      private_adr[0] = 11;
      printf("parent: Wrote %d@%-7x (Private)\n",
	     private_adr[0],private_adr);

      printf("parent: Read %3d@%-7x (Remote)%s\n",
	     remote_adr[0], remote_adr,
	     remote_adr[0]==10 ? "" : "     (should be: 10)");
      printf("parent: Read %3d@%-7x (Local)%s\n",
	     local_adr[0], local_adr,
	     local_adr[0]==10 ? "" : "     (should be: 10)");
      printf("parent: Read %3d@%-7x (Private)%s\n",
	     private_adr[0], private_adr,
	     private_adr[0]==11 ? "" : "     (should be: 11)");
    }

  exit(0);
}
-------------------------------------------------------------------------

----- vm_map.c ----------------------------------------------------------
#include <mach_init.h>
#include <mach.h>
#include <stdio.h>
#include <sys/wait.h>
#include <servers/env_mgr.h>
#include <servers/service.h>
#include <mach/message.h>

memory_object_t memory_object=MEMORY_OBJECT_NULL;

char *map_memory(address, size, offset)
     char *address;
     vm_size_t size;
     vm_offset_t offset;
{
  kern_return_t ret;
  boolean_t anywhere = (address == NULL);
  vm_offset_t *addr = (vm_offset_t *)&address;

  fprintf(stderr,
	  "%8svm_map(t=%x,ad=%x@%x,sz=%x,%x,any=%x,%x,off=%x,%x,%x,%x,%x)\n",
	  "",
	  task_self_,
	  *addr, addr,
	  size,
	  0,
	  anywhere,
	  memory_object,
	  offset,
	  FALSE,
	  VM_PROT_ALL,
	  VM_PROT_ALL,
	  VM_INHERIT_SHARE
	  );

  ret = vm_map(task_self_,	/* My port */
	       addr,		/* Pointer to in/out address */
	       size,		/* Size */
	       0,		/* Mask (Should it be 0xf000000f ?) */
	       anywhere,	/* TRUE=>system free to choose address */
	       memory_object,	/* The default external pager */
	       offset,		/* Offset inside memory object */
	       FALSE,		/* TRUE => Make a copy */
	       VM_PROT_ALL,	/* Current protection (all=>no protection) */
	       VM_PROT_ALL,	/* Max protection (all=>no protection) */
	       VM_INHERIT_SHARE	/* Inherit properties. */
	       );

  if (ret != KERN_SUCCESS)
    { mach_error("vm_map: ",ret);

      exit(1);
    }

  return address;
}

char *allocate_memory(address, size)
     char *address;
     vm_size_t size;
{
  kern_return_t ret;
  boolean_t anywhere = (address == NULL);
  vm_offset_t *addr = (vm_offset_t *)&address;

  fprintf(stderr,
	  "%8svm_allocate(t=%x,ad=%x@%x,sz=%x,any=%x)\n",
	  "",
	  task_self_,
	  *addr, addr,
	  size,
	  anywhere
	  );

  ret = vm_allocate(task_self_,	/* My port */
		    addr,	/* Pointer to in/out address */
		    size,	/* Size */
		    anywhere	/* TRUE=>system nfree to choose address */
		    );

  if (ret != KERN_SUCCESS)
    { mach_error("vm_allocate: ",ret);

      exit(1);
    }

  fprintf(stderr,
	  "%8svm_inherit(t=%x,ad=%x@%x,sz=%x,%x)\n",
	  "",
	  task_self_,
	  *addr, addr,
	  size,
	  VM_INHERIT_SHARE
	  );
  ret = vm_inherit(task_self_,	/* My port */
		   *addr,	/* Pointer to in/out address */
		   size,	/* Size */
		   VM_INHERIT_SHARE
		   );

  if (ret != KERN_SUCCESS)
    { mach_error("vm_inherit: ",ret);

      exit(1);
    }

  return address;
}


/* remote_adr is the remote address for a shared area.
   local_adr is the local address for a shared area.
   private_adr is the local address for a private area.
   */

main()
{
  char *remote_adr = NULL, *local_adr = NULL, *private_adr = NULL;
  int child;

#ifdef ALLOCATE_MEMORY
  remote_adr = allocate_memory(remote_adr, vm_page_size);
#else
  remote_adr = map_memory(remote_adr, vm_page_size, 0);
#endif


  printf("parent: mapping remote_adr at %x\n", remote_adr);

#ifdef MAP_BEFORE_FORK
#ifdef LOCAL_ANYWHERE
  local_adr = NULL;
#else
  local_adr = remote_adr + vm_page_size*0x10;
#endif
  local_adr = map_memory(local_adr, vm_page_size, 0);
  printf("child:  mapping local_adr at %x\n", local_adr);
#endif

  child = fork();
  
  if (child == -1)
    { perror("fork:");
      exit(1);
    }

  if (child != 0)
    {
      printf("child:  Fork\n");
      sleep(10);

#ifndef MAP_BEFORE_FORK
#ifdef LOCAL_ANYWHERE
      local_adr = NULL;
#else
      local_adr = remote_adr + vm_page_size*0x8;
#endif
      local_adr = map_memory(local_adr, vm_page_size, 0);
      printf("child:  mapping local_adr at %x\n", local_adr);
#endif

      private_adr = map_memory(private_adr, vm_page_size, vm_page_size);
      printf("child:  mapping private_adr at %x\n", private_adr);

      printf("child:  Read %3d@%-7x (Remote)%s\n",
	     remote_adr[0], remote_adr,
	     remote_adr[0]==10 ? "" : "     (should be: 10)");
      printf("child:  Read %3d@%-7x (Local)%s\n",
	     local_adr[0], local_adr,
	     local_adr[0]==10 ? "" : "     (should be: 10)");
      printf("child:  Read %3d@%-7x (Private)%s\n",
	     private_adr[0], private_adr,
	     private_adr[0]==0 ? "" : "     (should be:  0)");
    }
  else
    {
      printf("parent: Fork\n");

#ifndef MAP_BEFORE_FORK
#ifdef LOCAL_ANYWHERE
      local_adr = NULL;
#else
      local_adr = remote_adr + vm_page_size*0x10;
#endif
      local_adr = map_memory(local_adr, vm_page_size, 0);
      printf("child:  mapping local_adr at %x\n", local_adr);
#endif

      private_adr = map_memory(private_adr, vm_page_size, vm_page_size);
      printf("parent: mapping private_adr at %x\n", private_adr);

      remote_adr[0] = 10;
      printf("parent: Wrote %d@%-7x (Remote==Local)\n",
	     remote_adr[0],remote_adr);

      private_adr[0] = 11;
      printf("parent: Wrote %d@%-7x (Private)\n",
	     private_adr[0],private_adr);

      printf("parent: Read %3d@%-7x (Remote)%s\n",
	     remote_adr[0], remote_adr,
	     remote_adr[0]==10 ? "" : "     (should be: 10)");
      printf("parent: Read %3d@%-7x (Local)%s\n",
	     local_adr[0], local_adr,
	     local_adr[0]==10 ? "" : "     (should be: 10)");
      printf("parent: Read %3d@%-7x (Private)%s\n",
	     private_adr[0], private_adr,
	     private_adr[0]==11 ? "" : "     (should be: 11)");
    }

  exit(0);
}
-------------------------------------------------------------------------

--
Roland Karlsson
SICS, PO Box 1263, S-164 28 KISTA, SWEDEN	Internet: roland@sics.se
Tel: +46 8 752 15 40	Ttx: 812 61 54 SICS S	Fax: +46 8 751 72 30