[comp.os.minix] A shared text

go@jacobs.CS.ORST.EDU (Gary Oliver) (07/24/90)

Since I received no NEGATIVE comments from the referees, I'm letting
this out to the users at-large.

This package contains a shared text implementation for minix 1.5.10.
This code was written about a year ago when all I was running was 1.3
on an old XT.  I have upgraded to 1.5.10 and an AT and reimplemented
the patches.  Let me know if you have any problems.

Caveats:

	This is for INTEL implementations only.  Sorry.

	I have not tried this with any of the 386 full segment
	implementations.

	It has been a long while since I have played with an XT
	running Minix, so I have not (recently) tried this in
	that environment.  I don't think you will have any problems,
	but it hasn't been tested there lately.

	Gary Oliver
	go@jacobs.cs.orst.edu

-----Cut here and do the usual thing-----
echo x - const.h.cdiff
sed '/^X/s///' > const.h.cdiff << '/'
X*** /usr/minix-1.5.10/mm/const.h	Thu May 17 19:07:48 1990
X--- /usr/local/mm/const.h	Sun Jul 15 22:05:34 1990
X***************
X*** 25,27 ****
X--- 25,29 ----
X  #define printf        printk
X  
X  #define INIT_PID	   1	/* init's process id number */
X+ 
X+ #define	NR_SHARE	NR_PROCS	/* Allows one for each process */
/
echo x - dmp.cdiff
sed '/^X/s///' > dmp.cdiff << '/'
X*** /usr/minix-1.5.10/kernel/dmp.c	Fri May 18 19:25:18 1990
X--- /usr/local/kernel/dmp.c	Sun Jun 17 10:44:24 1990
X***************
X*** 33,39 ****
X    for (rp = BEG_PROC_ADDR; rp < END_PROC_ADDR; rp++)  {
X  	if (rp->p_flags & P_SLOT_FREE) continue;
X  	base = rp->p_map[T].mem_phys;
X! 	size = rp->p_map[S].mem_phys + rp->p_map[S].mem_len - base;
X  	prname(proc_number(rp));
X  	printf("%5u %4lx %4lx %2x %7U %7U %5uK %5uK  ",
X  		rp->p_pid,
X--- 33,44 ----
X    for (rp = BEG_PROC_ADDR; rp < END_PROC_ADDR; rp++)  {
X  	if (rp->p_flags & P_SLOT_FREE) continue;
X  	base = rp->p_map[T].mem_phys;
X! 	/* Size allows for non-contiguous text and data segments */
X! 	size = rp->p_map[T].mem_len
X! 			 + rp->p_map[S].mem_phys
X! 			 - rp->p_map[D].mem_phys
X! 			 + rp->p_map[S].mem_len;
X! 			 
X  	prname(proc_number(rp));
X  	printf("%5u %4lx %4lx %2x %7U %7U %5uK %5uK  ",
X  		rp->p_pid,
X***************
X*** 74,80 ****
X    for (rp = BEG_SERV_ADDR; rp < END_PROC_ADDR; rp++)  {
X  	if (rp->p_flags & P_SLOT_FREE) continue;
X  	base = rp->p_map[T].mem_phys;
X! 	size = rp->p_map[S].mem_phys + rp->p_map[S].mem_len - base;
X  	prname(proc_number(rp));
X  	printf(" %4x %4x %4x  %4x %4x %4x  %4x %4x %4x  %5uK %5uK\r\n", 
X  	    rp->p_map[T].mem_vir, rp->p_map[T].mem_phys, rp->p_map[T].mem_len,
X--- 79,89 ----
X    for (rp = BEG_SERV_ADDR; rp < END_PROC_ADDR; rp++)  {
X  	if (rp->p_flags & P_SLOT_FREE) continue;
X  	base = rp->p_map[T].mem_phys;
X! 	/* Size allows for non-contiguous text and data segments */
X! 	size = rp->p_map[T].mem_len
X! 			+ rp->p_map[S].mem_phys
X! 			- rp->p_map[D].mem_phys
X! 			+ rp->p_map[S].mem_len;
X  	prname(proc_number(rp));
X  	printf(" %4x %4x %4x  %4x %4x %4x  %4x %4x %4x  %5uK %5uK\r\n", 
X  	    rp->p_map[T].mem_vir, rp->p_map[T].mem_phys, rp->p_map[T].mem_len,
/
echo x - dmp_crc_after
sed '/^X/s///' > dmp_crc_after << '/'
X31086   3485 dmp.c
/
echo x - dmp_crc_before
sed '/^X/s///' > dmp_crc_before << '/'
X50036   3257 dmp.c
/
echo x - exec.cdiff
sed '/^X/s///' > exec.cdiff << '/'
X*** /usr/minix-1.5.10/mm/exec.c	Sun Jun 10 12:32:03 1990
X--- /usr/local/mm/exec.c	Sun Jul 15 22:04:16 1990
X***************
X*** 62,67 ****
X--- 62,71 ----
X    long sym_bytes;
X    vir_clicks sc;
X    struct stat s_buf;
X+ #if	NR_SHARE != 0
X+   struct shared_info *sh_ptr;
X+   struct shared_info *shared_text();
X+ #endif	/* NR_SHARE */
X  
X    /* Do some validity checks. */
X    rmp = mp;
X***************
X*** 98,105 ****
X    }
X  
X    /* Allocate new memory and release old memory.  Fix map and tell kernel. */
X!   r = new_mem(text_bytes, data_bytes, bss_bytes, stk_bytes, tot_bytes,
X! 							u.zb, ZEROBUF_SIZE);
X    if (r != OK) {
X  	close(fd);		/* insufficient core or program too big */
X  	return(r);
X--- 102,119 ----
X    }
X  
X    /* Allocate new memory and release old memory.  Fix map and tell kernel. */
X! #if	NR_SHARE != 0
X!   if (ft == SEPARATE)
X! 	sh_ptr = shared_text(&s_buf);
X!   else
X! 	sh_ptr = 0;	/* Not shared at all */
X! 
X!   r = new_mem(sh_ptr, text_bytes, data_bytes, bss_bytes, stk_bytes,
X! 					tot_bytes, u.zb, ZEROBUF_SIZE);
X! #else
X!   r = new_mem(text_bytes, data_bytes, bss_bytes, stk_bytes, tot_bytes,
X! 							u.zb, ZEROBUF_SIZE);
X! #endif	/* NR_SHARE */
X    if (r != OK) {
X  	close(fd);		/* insufficient core or program too big */
X  	return(r);
X***************
X*** 114,121 ****
X    r = mem_copy(MM_PROC_NR, D, (long) src, who, D, (long) vsp, (long) stk_bytes);
X    if (r != OK) panic("do_exec stack copy err", NO_NUM);
X  
X!   /* Read in text and data segments. */
X!   load_seg(fd, T, text_bytes);
X    load_seg(fd, D, data_bytes);
X  #if (CHIP == M68000)
X    if (lseek(fd, sym_bytes, 1) < 0)
X--- 128,143 ----
X    r = mem_copy(MM_PROC_NR, D, (long) src, who, D, (long) vsp, (long) stk_bytes);
X    if (r != OK) panic("do_exec stack copy err", NO_NUM);
X  
X! #if	NR_SHARE != 0
X!   /* Read in text and data segments. */
X!   if (sh_ptr)
X! 	lseek(fd, (long) text_bytes, 1);	/* Skip text on file */
X!   else
X! 	load_seg(fd, T, text_bytes);
X! #else
X!   load_seg(fd, T, text_bytes);
X! #endif	/* NR_SHARE */
X! 
X    load_seg(fd, D, data_bytes);
X  #if (CHIP == M68000)
X    if (lseek(fd, sym_bytes, 1) < 0)
X***************
X*** 123,129 ****
X    if (relocate(fd, mbuf) < 0)
X  	;	/* error */
X  #endif
X!   close(fd);			/* don't need exec file any more */
X  
X    /* Take care of setuid/setgid bits. */
X    if ((rmp->mp_flags & TRACED) == 0) { /* suppress if tracing */
X--- 145,164 ----
X    if (relocate(fd, mbuf) < 0)
X  	;	/* error */
X  #endif
X! 
X!   close(fd);
X! 
X! #if	NR_SHARE != 0
X!   /* See if creating a separate I/D program for first time and create
X!    * a shared segment descriptor if so.
X!    */
X! 
X!   if (!sh_ptr && (ft == SEPARATE))
X! 	{
X! 	/* Create a shared-text structure to remember this one */
X! 	make_shared(rmp, &s_buf);
X! 	}
X! #endif	/* NR_SHARE */
X  
X    /* Take care of setuid/setgid bits. */
X    if ((rmp->mp_flags & TRACED) == 0) { /* suppress if tracing */
X***************
X*** 237,243 ****
X  /*===========================================================================*
X   *				new_mem					     *
X   *===========================================================================*/
X! PRIVATE int new_mem(text_bytes, data_bytes, bss_bytes,stk_bytes,tot_bytes,bf,zs)
X  vir_bytes text_bytes;		/* text segment size in bytes */
X  vir_bytes data_bytes;		/* size of initialized data in bytes */
X  vir_bytes bss_bytes;		/* size of bss in bytes */
X--- 272,283 ----
X  /*===========================================================================*
X   *				new_mem					     *
X   *===========================================================================*/
X! #if	NR_SHARE != 0
X! PRIVATE int new_mem(sh_ptr, text_bytes, data_bytes, bss_bytes,stk_bytes,tot_bytes,bf,zs)
X! struct shared_info *sh_ptr;
X! #else
X! PRIVATE int new_mem(text_bytes, data_bytes, bss_bytes,stk_bytes,tot_bytes,bf,zs)
X! #endif	/* NR_SHARE */
X  vir_bytes text_bytes;		/* text segment size in bytes */
X  vir_bytes data_bytes;		/* size of initialized data in bytes */
X  vir_bytes bss_bytes;		/* size of bss in bytes */
X***************
X*** 267,272 ****
X--- 307,317 ----
X     * boundary.  The data and bss parts are run together with no space.
X     */
X  
X+ #if	NR_SHARE != 0
X+   if (sh_ptr)
X+     text_bytes = 0;	/* No text if shared segment */
X+ #endif	/* NR_SHARE */
X+ 
X    text_clicks = (text_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
X    data_clicks = (data_bytes + bss_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
X    stack_clicks = (stk_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
X***************
X*** 283,301 ****
X    /* There is enough memory for the new core image.  Release the old one. */
X    rmp = mp;
X  #if (CHIP != M68000)
X    old_clicks = (phys_clicks) rmp->mp_seg[S].mem_len;
X    old_clicks += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
X    if (rmp->mp_flags & SEPARATE) old_clicks += rmp->mp_seg[T].mem_len;
X    free_mem(rmp->mp_seg[T].mem_phys, old_clicks);	/* free the memory */
X  #endif
X  
X    /* We have now passed the point of no return.  The old core image has been
X     * forever lost.  The call must go through now.  Set up and report new map.
X     */
X    new_base = alloc_mem(text_clicks + tot_clicks);	/* new core image */
X!   if (new_base == NO_MEM) panic("MM hole list is inconsistent", NO_NUM);
X!   rmp->mp_seg[T].mem_len = text_clicks;
X!   rmp->mp_seg[T].mem_phys = new_base;
X    rmp->mp_seg[D].mem_len = data_clicks;
X    rmp->mp_seg[D].mem_phys = new_base + text_clicks;
X    rmp->mp_seg[S].mem_len = stack_clicks;
X--- 328,368 ----
X    /* There is enough memory for the new core image.  Release the old one. */
X    rmp = mp;
X  #if (CHIP != M68000)
X+ #if NR_SHARE != 0
X+   if (sh_ptr)
X+      ++(sh_ptr->sh_users);	/* Count another so it isn't freed under us */
X+   free_user_mem(rmp);
X+ #else
X    old_clicks = (phys_clicks) rmp->mp_seg[S].mem_len;
X    old_clicks += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
X    if (rmp->mp_flags & SEPARATE) old_clicks += rmp->mp_seg[T].mem_len;
X    free_mem(rmp->mp_seg[T].mem_phys, old_clicks);	/* free the memory */
X+ #endif	/* NR_SHARE */
X  #endif
X  
X    /* We have now passed the point of no return.  The old core image has been
X     * forever lost.  The call must go through now.  Set up and report new map.
X     */
X    new_base = alloc_mem(text_clicks + tot_clicks);	/* new core image */
X! 
X!   if (new_base == NO_MEM) panic("MM hole list is inconsistent", NO_NUM);
X! #if	NR_SHARE != 0
X!   if (sh_ptr)
X!     {
X!     /* Set tasks map to shared segment */
X!     rmp->mp_seg[T].mem_phys = sh_ptr->sh_seg.mem_phys;
X!     rmp->mp_seg[T].mem_len = sh_ptr->sh_seg.mem_len;
X!     rmp->mp_shared = sh_ptr;	/* Shared text ptr */
X!     }
X!   else
X!     {
X!     rmp->mp_seg[T].mem_len = text_clicks;
X!     rmp->mp_seg[T].mem_phys = new_base;
X!     }
X! #else
X!   rmp->mp_seg[T].mem_len = text_clicks;
X!   rmp->mp_seg[T].mem_phys = new_base;
X! #endif	/* NR_SHARE */
X    rmp->mp_seg[D].mem_len = data_clicks;
X    rmp->mp_seg[D].mem_phys = new_base + text_clicks;
X    rmp->mp_seg[S].mem_len = stack_clicks;
X***************
X*** 320,327 ****
X    for (rzp = &bf[0]; rzp < &bf[zs]; rzp++) *rzp = 0;	/* clear buffer */
X    bytes = (phys_bytes) (data_clicks + gap_clicks + stack_clicks) << CLICK_SHIFT;
X    vzb = (vir_bytes) bf;
X!   base = (long) rmp->mp_seg[T].mem_phys + rmp->mp_seg[T].mem_len;
X!   base = base << CLICK_SHIFT;
X    bss_offset = (data_bytes >> CLICK_SHIFT) << CLICK_SHIFT;
X    base += bss_offset;
X    bytes -= bss_offset;
X--- 387,398 ----
X    for (rzp = &bf[0]; rzp < &bf[zs]; rzp++) *rzp = 0;	/* clear buffer */
X    bytes = (phys_bytes) (data_clicks + gap_clicks + stack_clicks) << CLICK_SHIFT;
X    vzb = (vir_bytes) bf;
X! #if	NR_SHARE != 0
X!   base = (long) rmp->mp_seg[D].mem_phys << CLICK_SHIFT;
X! #else
X!   base = ((long) rmp->mp_seg[T].mem_phys + rmp->mp_seg[T].mem_len)
X!   				 << CLICK_SHIFT;
X! #endif	/* NR_SHARE */
X    bss_offset = (data_bytes >> CLICK_SHIFT) << CLICK_SHIFT;
X    base += bss_offset;
X    bytes -= bss_offset;
X***************
X*** 474,476 ****
X--- 545,651 ----
X    return(0);	/* ok */
X  }
X  #endif
X+ 
X+ #if	NR_SHARE != 0
X+ /*
X+  *	Shared text segment stuff is here.  If NR_SHARE is non-zero, this
X+  *	code is included.
X+  */
X+ 
X+ /*
X+  *	shared_text()
X+  *
X+  *	Lookup the dev/inode/time info in the shared text table
X+  *	and return its TRUE if found, otherwise return FALSE.
X+  *	If found, plug the user proc structure with the details.
X+  */
X+ struct shared_info		*
X+ shared_text(st_buf)
X+   register struct stat		*st_buf;
X+     {
X+     register struct shared_info	*sh_ptr;
X+ 
X+     for (sh_ptr = sh_text; sh_ptr < &sh_text[NR_SHARE]; ++sh_ptr)
X+ 	{
X+ 	if ((sh_ptr->sh_users != 0) &&
X+ 	    (sh_ptr->sh_dev == st_buf->st_dev) &&
X+ 	    (sh_ptr->sh_ino == st_buf->st_ino) &&
X+ 	    (sh_ptr->sh_mtime == st_buf->st_mtime))
X+ 	    {
X+ 	    return (sh_ptr);
X+ 	    }
X+ 	}
X+ 
X+     return (0);		/* Didn't find it */
X+     }
X+ 
X+ 
X+ /*
X+  *	make_shared()
X+  *
X+  *	Create a shared_text structure for the current users text info.
X+  */
X+ make_shared(rmp, st_buf)
X+   struct mproc			*rmp;
X+   struct stat			*st_buf;
X+     {
X+     register struct shared_info	*sh_ptr;
X+ 
X+     /* Look for an unused entry.  If none are found, then don't bother
X+      * trying to make this one a shared segment - just let the normal
X+      * non-shared things happen.
X+      */
X+     for (sh_ptr = sh_text; sh_ptr < &sh_text[NR_SHARE]; ++sh_ptr)
X+ 	{
X+ 	if (sh_ptr->sh_users == 0)
X+ 	    {
X+ 	    /* Free entry -- use this one */
X+ 	    sh_ptr->sh_dev = st_buf->st_dev;
X+ 	    sh_ptr->sh_ino = st_buf->st_ino;
X+ 	    sh_ptr->sh_mtime = st_buf->st_mtime;
X+ 	    sh_ptr->sh_seg.mem_phys = rmp->mp_seg[T].mem_phys;
X+ 	    sh_ptr->sh_seg.mem_len = rmp->mp_seg[T].mem_len;
X+ 	    sh_ptr->sh_users = 1;	/* One user thus far */
X+ 	    rmp->mp_shared = sh_ptr;	/* Shared text ptr */
X+ 	    return;
X+ 	    }
X+ 	}
X+     }
X+ 
X+ sh_init()
X+     {
X+     register struct shared_info	*sh_ptr;
X+ 
X+     for (sh_ptr = sh_text; sh_ptr < &sh_text[NR_SHARE]; ++sh_ptr)
X+ 	sh_ptr->sh_users = 0;
X+     }
X+ 
X+ free_user_mem(rmp)
X+   register struct mproc *rmp;
X+     {
X+     phys_clicks		s;
X+ 
X+     s = (phys_clicks) rmp->mp_seg[S].mem_len +
X+                       (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
X+ 
X+     if (rmp->mp_shared)
X+ 	{
X+ 	/* Shared code -- free data and text separately */
X+ 	free_mem(rmp->mp_seg[D].mem_phys, s);
X+ 	if (--(rmp->mp_shared->sh_users) == 0)
X+ 	    {
X+ 	    free_mem(rmp->mp_shared->sh_seg.mem_phys,
X+ 		     rmp->mp_shared->sh_seg.mem_len);
X+ 	    }
X+ 	rmp->mp_shared = (struct shared_info *) 0; /* No longer accessed */
X+ 	}
X+     else
X+ 	{
X+ 	if (rmp->mp_flags & SEPARATE)
X+ 	    s += rmp->mp_seg[T].mem_len;
X+ 
X+ 	free_mem(rmp->mp_seg[T].mem_phys, s);
X+ 	}
X+     }
X+ #endif	/* NR_SHARE */
X+ 
/
echo x - forkexit.cdiff
sed '/^X/s///' > forkexit.cdiff << '/'
X*** /usr/minix-1.5.10/mm/forkexit.c	Sun Jun 10 12:32:04 1990
X--- /usr/local/mm/forkexit.c	Sun Jul 15 21:57:38 1990
X***************
X*** 54,68 ****
X    prog_clicks = (phys_clicks) rmp->mp_seg[S].mem_len;
X    prog_clicks += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
X  #if (CHIP == INTEL)
X!   if (rmp->mp_flags & SEPARATE) prog_clicks += rmp->mp_seg[T].mem_len;
X!   prog_bytes = (long) prog_clicks << CLICK_SHIFT;
X! #endif
X    if ( (child_base = alloc_mem(prog_clicks)) == NO_MEM) return(EAGAIN);
X  
X  #if (CHIP == INTEL)
X    /* Create a copy of the parent's core image for the child. */
X    child_abs = (long) child_base << CLICK_SHIFT;
X!   parent_abs = (long) rmp->mp_seg[T].mem_phys << CLICK_SHIFT;
X    i = mem_copy(ABS, 0, parent_abs, ABS, 0, child_abs, prog_bytes);
X    if ( i < 0) panic("do_fork can't copy", i);
X  #endif
X--- 54,89 ----
X    prog_clicks = (phys_clicks) rmp->mp_seg[S].mem_len;
X    prog_clicks += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
X  #if (CHIP == INTEL)
X! #if	NR_SHARE != 0
X!   /* If separate I&D and not shared, include the size of the Text region */
X!   if ((rmp->mp_flags & SEPARATE) && !rmp->mp_shared)
X! 	prog_clicks += rmp->mp_seg[T].mem_len;
X! #else
X!   if (rmp->mp_flags & SEPARATE) prog_clicks += rmp->mp_seg[T].mem_len;
X! #endif	/* NR_SHARE */
X!   prog_bytes = (long) prog_clicks << CLICK_SHIFT;
X! #endif
X! 
X    if ( (child_base = alloc_mem(prog_clicks)) == NO_MEM) return(EAGAIN);
X  
X  #if (CHIP == INTEL)
X    /* Create a copy of the parent's core image for the child. */
X    child_abs = (long) child_base << CLICK_SHIFT;
X! #if	NR_SHARE != 0
X!   if (rmp->mp_shared)
X! 	{
X! 	/* Shared -- copy only the data region */
X! 	parent_abs = (long) rmp->mp_seg[D].mem_phys << CLICK_SHIFT;
X! 	}
X!   else
X! 	{
X! 	/* If not shared, copy entire text + data region */
X! 	parent_abs = (long) rmp->mp_seg[T].mem_phys << CLICK_SHIFT;
X! 	}
X! #else
X!   parent_abs = (long) rmp->mp_seg[T].mem_phys << CLICK_SHIFT;
X! #endif	/* NR_SHARE */
X! 
X    i = mem_copy(ABS, 0, parent_abs, ABS, 0, child_abs, prog_bytes);
X    if ( i < 0) panic("do_fork can't copy", i);
X  #endif
X***************
X*** 82,89 ****
X    rmc->mp_parent = who;		/* record child's parent */
X    rmc->mp_flags &= ~TRACED;	/* child does not inherit trace status */
X  #if (CHIP == INTEL)
X!   rmc->mp_seg[T].mem_phys = child_base;
X!   rmc->mp_seg[D].mem_phys = child_base + rmc->mp_seg[T].mem_len;
X    rmc->mp_seg[S].mem_phys = rmc->mp_seg[D].mem_phys + 
X  			(rmp->mp_seg[S].mem_phys - rmp->mp_seg[D].mem_phys);
X  #endif
X--- 103,124 ----
X    rmc->mp_parent = who;		/* record child's parent */
X    rmc->mp_flags &= ~TRACED;	/* child does not inherit trace status */
X  #if (CHIP == INTEL)
X! #if	NR_SHARE != 0
X!   if (rmc->mp_shared)
X! 	{
X! 	/* A shared text segment -- Text is ok -- fix D and S */
X! 	rmc->mp_seg[D].mem_phys = child_base; /* Only D & S copied */
X! 	rmc->mp_shared->sh_users++;	/* Add to counter */
X! 	}
X!   else
X! 	{
X! 	rmc->mp_seg[T].mem_phys = child_base;
X! 	rmc->mp_seg[D].mem_phys = child_base + rmc->mp_seg[T].mem_len;
X! 	}
X! #else
X!   rmc->mp_seg[T].mem_phys = child_base;
X!   rmc->mp_seg[D].mem_phys = child_base + rmc->mp_seg[T].mem_len;
X! #endif	/* NR_SHARE */
X    rmc->mp_seg[S].mem_phys = rmc->mp_seg[D].mem_phys + 
X  			(rmp->mp_seg[S].mem_phys - rmp->mp_seg[D].mem_phys);
X  #endif
X***************
X*** 183,193 ****
X--- 218,232 ----
X    tell_fs(EXIT, proc_nr, 0, 0);  /* file system can free the proc slot */
X  
X  #if (CHIP == INTEL)
X+ #if	NR_SHARE != 0
X+   free_user_mem(rmp);
X+ #else
X    /* Release the memory occupied by the child. */
X    s = (phys_clicks) rmp->mp_seg[S].mem_len;
X    s += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
X    if (rmp->mp_flags & SEPARATE) s += rmp->mp_seg[T].mem_len;
X    free_mem(rmp->mp_seg[T].mem_phys, s);	/* free the memory */
X+ #endif	/* NR_SHARE */
X  #endif
X  
X  }
/
echo x - main.cdiff
sed '/^X/s///' > main.cdiff << '/'
X*** /usr/minix-1.5.10/mm/main.c	Sun Jun 10 12:32:05 1990
X--- /usr/local/mm/main.c	Sun Jun 10 22:36:35 1990
X***************
X*** 109,114 ****
X--- 109,117 ----
X    mproc[INIT_PROC_NR].mp_flags |= IN_USE;
X    mproc[INIT_PROC_NR].mp_pid = INIT_PID;
X    procs_in_use = 3;
X+ #if NR_SHARE != 0
X+   sh_init();	/* Initialize tables in exec.c */
X+ #endif	/* NR_SHARE */
X  }
X  
X  
X***************
X*** 195,200 ****
X--- 198,206 ----
X    rmp->mp_seg[S].mem_vir  = init_clicks;
X  #endif
X    if (init_text_clicks != 0) rmp->mp_flags |= SEPARATE;
X+ #if	NR_SHARE != 0
X+   rmp->mp_shared = 0;	/* Not a shared program */
X+ #endif /* NR_SHARE */
X  
X    return(OK);
X  }
/
echo x - mm_crc_after
sed '/^X/s///' > mm_crc_after << '/'
X31238   1051 const.h
X64471  21318 exec.c
X06214  10998 forkexit.c
X37354   8155 main.c
X23250   2113 mproc.h
/
echo x - mm_crc_before
sed '/^X/s///' > mm_crc_before << '/'
X53152    990 const.h
X35847  17169 exec.c
X27670  10047 forkexit.c
X16831   7979 main.c
X51976   1793 mproc.h
/
echo x - mproc.h.cdiff
sed '/^X/s///' > mproc.h.cdiff << '/'
X*** /usr/minix-1.5.10/mm/mproc.h	Sun Jun 10 12:32:05 1990
X--- /usr/local/mm/mproc.h	Sun Jun 10 22:36:36 1990
X***************
X*** 7,12 ****
X--- 7,15 ----
X  
X  EXTERN struct mproc {
X    struct mem_map mp_seg[NR_SEGS];	/* points to text, data, stack */
X+ #if	NR_SHARE != 0
X+   struct shared_info *mp_shared;	/* != NULL if shared code segment running */
X+ #endif	/* NR_SHARE */
X    char mp_exitstatus;		/* storage for status when process exits */
X    char mp_sigstatus;		/* storage for signal # for killed processes */
X    pid_t mp_pid;			/* process id */
X***************
X*** 27,32 ****
X--- 30,45 ----
X    unsigned mp_flags;		/* flag bits */
X  } mproc[NR_PROCS];
X  
X+ #if	NR_SHARE != 0
X+ EXTERN struct shared_info {
X+   unsigned int		sh_dev;
X+   unsigned int		sh_ino;
X+   long			sh_mtime;
X+   struct mem_map	sh_seg;
X+   int			sh_users;
X+   } sh_text[NR_SHARE];
X+ #endif	/* NR_SHARE */
X+ 
X  /* Flag values */
X  #define IN_USE           001	/* set when 'mproc' slot in use */
X  #define WAITING          002	/* set by WAIT system call */
/
echo x - mu.c
sed '/^X/s///' > mu.c << '/'
X/* mu.c  Print memory usage */
X 
X#include <minix/config.h>
X#include <limits.h>
X#include <sys/types.h>
X
X#include <minix/const.h>
X#undef EXTERN				/* <minix/const.h> defined this */
X#define EXTERN				/* so we get proc, mproc and fproc */
X#include <minix/type.h>
X
X#include "../kernel/const.h"
X#include "../kernel/type.h"
X#include "../kernel/proc.h"
X#undef printf				/* kernel's const.h defined this */
X
X#include "../mm/const.h"
X#include "../mm/mproc.h"
X#include "../fs/fproc.h"
X#include "../fs/const.h"
X#undef printf				/* fs's const.h defined this */
X
X
X#include <minix/com.h>
X#include <fcntl.h>
X#include <a.out.h>
X#include <string.h>
X
X#include <time.h>
X
X#include <stdio.h>
X
X#define mindev(dev)	(((dev)>>MINOR) & 0377)	/* yield minor device */
X#define majdev(dev)	(((dev)>>MAJOR) & 0377)	/* yield major device */
X
X/* macro to convert memory offsets to rounded kilo-units */
X#define	off_to_k(off)	((unsigned) (((off) + 512) / 1024))
X
X/* what we think the relevant identifiers in the namelists are */
X#define	ID_PROC		"_proc"		/* from kernel namelist */
X#define	ID_SHTEXT	"_sh_text"	/* from mm namelist */
X#define	ID_FPROC	"_fproc"	/* from fs namelist */
X#define	ID_HOLEHEAD	"_hole_head"
X
X/* This struct is privately defined in mm/alloc.c  Define here as kludge */
Xstruct hole
X  {
X  phys_clicks h_base;
X  phys_clicks h_len;
X  struct hole *h_next;
X  };
X#define	NR_HOLES 128
Xstruct hole	hole[NR_HOLES];
X
Xstruct nlist ke_proc[2];
Xstruct nlist mm_mproc[2];
Xstruct nlist mm_shtext[2];
Xstruct nlist mm_holehead[2];
X
X#define	NAME_SIZ	(sizeof(ke_proc[0].n_name))	/* 8 chars */
X
X/* what we think the identfiers of the imported variables in this program are */
X#define	PROC	proc
X#define	SHTEXT	sh_text
X#define	FPROC	fproc
Xstruct hole *HOLEHEAD;
X
X
X/* default paths for system binaries */
X#if (CHIP == M68000)
X#define KERNEL_PATH	"/usr/src/kernel/kernel.mix"
X#define MM_PATH		"/usr/src/mm/mm.mix"
X#else
X#define KERNEL_PATH	"/usr/local/kernel/kernel"
X#define MM_PATH		"/usr/local/mm/mm"
X# endif
X
X#define	KMEM_PATH	"/dev/kmem"	/* opened for kernel proc table */
X#define	MEM_PATH	"/dev/mem"	/* opened for mm/fs + user processes */
X
Xchar	*kpath = KERNEL_PATH;
Xchar	*mpath = MM_PATH;
X
Xint kmemfd, memfd;			/* file descriptors of [k]mem */
X
X/* * Main interprets arguments, gets system addresses, opens [k]mem, reads in
X * process tables from kernel/mm/fs and calls pstat() for relevant entries.
X */
Xmain(argc, argv)
Xchar *argv[];
X{
X	int cont;
X	int i;
X	int db_fd;
X	int uid = getuid();		/* real uid of caller */
X	struct hole *memp;
X	int endhole;
X	char tbuf[20];
X
X	if (argc == 2 && strcmp(argv[1], "-c") == 0)
X		cont = TRUE;
X	else
X		cont = FALSE;
X		
X	strncpy(ke_proc[0].n_name, ID_PROC, NAME_SIZ);
X	if (nlist(kpath, ke_proc) != 0) 
X		err("Can't read kernel namelist");
X	strncpy(mm_shtext[0].n_name, ID_SHTEXT, NAME_SIZ);
X	if (nlist(mpath, mm_shtext) != 0)
X		err("Can't read mm shared text namelist");
X	strncpy(mm_holehead[0].n_name, ID_HOLEHEAD, NAME_SIZ);
X	if (nlist(mpath, mm_holehead) != 0)
X		err("Can't read mm hole head namelist");
X	    	
X	/* get kernel tables */    	
X	if ((kmemfd = open(KMEM_PATH, O_RDONLY)) == -1)
X		err(KMEM_PATH);
X	if (addrread(kmemfd, (phys_clicks) 0,
X		     (vir_bytes) ke_proc[0].n_value,
X		     (char *) PROC, sizeof(PROC)) != sizeof(PROC))
X		err("Can't get kernel proc table from /dev/kmem");
X
X	/* get mm/fs tables */
X	if ((memfd = open(MEM_PATH, O_RDONLY)) == -1)
X		err(MEM_PATH);
X
X	do
X	    {
X	    if (addrread(memfd, PROC[NR_TASKS + MM_PROC_NR].p_map[D].mem_phys,
X			(vir_bytes) mm_shtext[0].n_value,
X			(char *) SHTEXT, sizeof(SHTEXT)) != sizeof(SHTEXT))
X		err("Can't get mm shared text info from /dev/mem");
X	    if (addrread(memfd, PROC[NR_TASKS + MM_PROC_NR].p_map[D].mem_phys,
X			(vir_bytes) mm_holehead[0].n_value,
X			(char *) &HOLEHEAD, sizeof(HOLEHEAD)) != sizeof(HOLEHEAD))
X		err("Can't read mm hole head pointer from /dev/mem");
X	    for (i = 0, memp = HOLEHEAD; memp != NULL; memp = hole[i++].h_next)
X		{
X		if (addrread(memfd, PROC[NR_TASKS + MM_PROC_NR].p_map[D].mem_phys,
X	    		     (vir_bytes) memp,
X	   		     (char *) &hole[i],
X			     sizeof(struct hole)) != sizeof(struct hole))
X	    	    err("Can't read hole table from /dev/mem");
X		}
X	    endhole = i;	/* Mark end of hole list */
X		
X	    if (cont)
X	    	printf("\033[H\033[J");	/* Clear and home */
X	    printf("N  Maj/Min  Inode  Date/Time        Phys   Len  Users\n");
X	    /* Print out shared text info */
X	    for (i = 0; i < NR_SHARE; ++i)
X	    	{
X#ifdef STICKY_SHARE
X	    	if (SHTEXT[i].sh_seg.mem_len != 0)
X#else
X		if (SHTEXT[i].sh_users != 0)
X#endif	/* STICKY_SHARE */
X		    {
X	  	    strncpy(tbuf, ctime(&SHTEXT[i].sh_mtime) + 4, 15);
X	    	    tbuf[15] = '\0';
X	            printf("%-2d %3d/%-3d  %5d  %s  %04x  %4d  %5d\n",
X	            	    i,
X	       		    majdev(SHTEXT[i].sh_dev),
X	       		    mindev(SHTEXT[i].sh_dev),
X	       		    SHTEXT[i].sh_ino,
X	       		    tbuf,
X	       		    SHTEXT[i].sh_seg.mem_phys,
X	       		    SHTEXT[i].sh_seg.mem_len,
X	       		    SHTEXT[i].sh_users);
X	       	    }
X	        }
X	    printf("\n\n\nFree memory pool:\n");
X	    for (i = 0; i < endhole; ++i)
X		printf("%4x %4x (%d)\n", hole[i].h_base, hole[i].h_len, hole[i].h_len);
X	    if (cont)
X	    	sleep(1);
X	    } while (cont);
X}
X
X/*
X * Addrread reads nbytes from offset addr to click base of fd into buf.
X */
Xint addrread(fd, base, addr, buf, nbytes)
Xphys_clicks base;
Xvir_bytes addr;
Xchar *buf;
X{
X	extern long lseek();
X    
X	if (lseek(fd, ((long) base << CLICK_SHIFT) + (long) addr, 0) < 0)
X		return -1;
X
X	return read(fd, buf, nbytes);
X}
X
Xerr(s)
Xchar *s;
X{
X	perror(s);
X	exit(2);
X}	
/
echo x - ps.cdiff
sed '/^X/s///' > ps.cdiff << '/'
X*** /usr/minix-1.5.10/commands/ps.c	Thu May 17 20:58:56 1990
X--- /usr/local/commands/ps.c	Sun Jul  1 15:53:45 1990
X***************
X*** 59,64 ****
X--- 59,65 ----
X  #include "../kernel/proc.h"
X  #undef printf				/* kernel's const.h defined this */
X  
X+ #include "../mm/const.h"
X  #include "../mm/mproc.h"
X  #include "../fs/fproc.h"
X  #include "../fs/const.h"
X***************
X*** 111,119 ****
X  #define MM_PATH		"/usr/src/mm/mm.mix"
X  #define FS_PATH		"/usr/src/fs/fs.mix"
X  #else
X! #define KERNEL_PATH	"/usr/src/kernel/kernel"
X! #define MM_PATH		"/usr/src/mm/mm"
X! #define FS_PATH		"/usr/src/fs/fs"
X  # endif
X  
X  #define	KMEM_PATH	"/dev/kmem"	/* opened for kernel proc table */
X--- 112,120 ----
X  #define MM_PATH		"/usr/src/mm/mm.mix"
X  #define FS_PATH		"/usr/src/fs/fs.mix"
X  #else
X! #define KERNEL_PATH	"/usr/local/kernel/kernel"
X! #define MM_PATH		"/usr/local/mm/mm"
X! #define FS_PATH		"/usr/local/fs/fs"
X  # endif
X  
X  #define	KMEM_PATH	"/dev/kmem"	/* opened for kernel proc table */
X***************
X*** 367,374 ****
X  				       buf.ps_euid, buf.ps_pid, buf.ps_ppid,
X  				       buf.ps_pgrp,
X  				       off_to_k(buf.ps_text),
X! 				       off_to_k((buf.ps_stack + buf.ps_ssize
X! 				       			- buf.ps_text)),
X  				       (buf.ps_flags & RECEIVING ?
X  						prrecv(&buf) :
X  				       		""),
X--- 368,376 ----
X  				       buf.ps_euid, buf.ps_pid, buf.ps_ppid,
X  				       buf.ps_pgrp,
X  				       off_to_k(buf.ps_text),
X! 				       off_to_k(buf.ps_tsize +
X! 				       		buf.ps_stack - buf.ps_data +
X! 				       		buf.ps_ssize),
X  				       (buf.ps_flags & RECEIVING ?
X  						prrecv(&buf) :
X  				       		""),
/
echo x - ps_crc_after
sed '/^X/s///' > ps_crc_after << '/'
X23499  19080 ps.c
/
echo x - ps_crc_before
sed '/^X/s///' > ps_crc_before << '/'
X04124  19024 ps.c
/
echo x - read.me
sed '/^X/s///' > read.me << '/'
XThis package contains the patches for an implementation of shared
Xtext segments for Minix (version 1.5.10.)  The files included are:
X
X	mm/
X		exec.cdiff
X		forkexit.cdiff
X		main.cdiff
X		const.h.cdiff
X		mproc.h.cdiff
X		
X	kernel/
X		dmp.cdiff
X		
X	commands/
X		ps.cdiff
X		mu.c
X				
X	read.me
X	mm_crc_before
X	mm_crc_after
X	dmp_crc_before
X	dmp_crc_after
X	ps_crc_before
X	ps_crc_after
X
XApply the cdiffs with patch and do a "make" and you should have a system
Xready to run.  The before and after crcs are included to make the mods
Xverifiable.
X
XImplementation notes:
X
XTo implement the shared text mechanism, a new table "sh_text" was added to mm.
XThis table contains a global copy of the text segment of processes involved
Xin the sharing.  Each process entry also contains a copy of the shared text
Xinformation, as does the kernel (who maintains loading of the segmentation
Xregisters.)
X
XWhenever an exec() is executed, mm looks up the file, and tests to see
Xif the file is Separate I&D.  If so, mm looks in the sh_text table for an in-use
Xentry (sh_users != 0) containing information about the file being exec'd
X(dev, inode and file time match file being exec'd.)  If a match is found,
Xthe number of users of this text (sh_users) is incremented, and a pointer
Xto the shared text structure is placed in the process table (only in mm.)
X
XIf no match is found, mm attempts to create a shared text structure
Xfor the file being exec'd.  If an empty entry can be found (sh_users == 0)
Xthe pertinent information about the file (where memory was allocated,
Xdev number, inode number and file time) are added to the table and the
Xnumber of users (sh_users) is set to 1.  The users proc entry also
Xreceives a pointer to the shared text structure.
X
XWhen an exit() is executed, mm checks to see if the process table points
Xto a shared text structure - if so, the number of users of that shared
Xtext entry are decremented, and if zero, the text memory is freed.
X
XAlso, when a fork() is executed, the number of shared text users for the
Xprocess being forked is incremented if the process is executing a shared text
Xprogram. 
X
XNote that no changes were "required" to the kernel - Minix is will built in
Xisolating the memory manager from the grundgies of running a process.
XAlso, these changes seem to work equally well on XTs as ATs.  In fact, I
Xposted these patches about a year ago (no one noticed except some far-away
Xarchive site) and when I had 1.5.10 up and running I just tried re-patching.
XAfter elimininating problems bothering patch (like the new #ifdefs
Xwere using MACHINE, etc.) the system just came up without problems.
XThe original patches were for my XT running 1.3.
X
XBefore implementing this system I frequently ran out of memory (only had 1M.)
XNow I usually run out of process slots before running out of memory.
X
XGarnish:
X
XAlong with the patches to mm, I have enclosed patches to the dmp() routine
X(kernel) and to ps (commands.)  The algorithms these programs used to compute
Xthe size of a processes' memory depended upon the text through stack areas
Xbeing contiguous - it was pretty wild seeing lengths of 8meg in some
Xprocesses before I made these patches.  The system will run
Xfine without them, but things look a little better with.
X
XAlso included is a hack I put together (from parts of ps.c) to display
Xthe contents of the shared memory pool.  mu will print the shared text
Xstructure and the free memory list and optionally (-c flag) will do so
Xcontinuously, updating the display once each second.  Pretty dumb program,
Xbut it helped me debug things.  You will most likely have to change
Xsome things, as it "knows" where to find the various kernel/mm parts.  No
Xdatabase (as ps) - it just looks at images everytime it starts.
X
X
XAfternote:
X
XAlong with the shared text, I tried implementing a STICKY shared text
Xcondition.  The theory was to not free the text segment when sh_users get to 
Xzero and let it lie around pending another exec().  If the memory allocator
Xattempted to allocate memory and none was available, all shared text segments
Xwith sh_users == 0 would be freed.  This worked ok, but memory fragmentation
Xwas pretty rotten.  Memory would fill up with lots of junk and then get
Xfreed all at once (my rule was pretty simple minded anyway.)  However, since
Xexec has to open the file every time anyway, the savings in the
Xtime to do a new exec was minimal, if measurable at all.  I have a
Xmedium speed disk (28ms,) and even with disk buffering, it didn't seem to
Xmake things fast enough to matter - the code submitted here does NOT
Xhave the sticky shared text implemented.  If someone wants to try it and
Xmake things smarter, go ahead.  I'd like to get a copy if you make
Xit work better than I did.
X
X
XDon't forget to do a ps -U to update ps's database after building.
X
XJust to make certain there is no confusion:
X
X	"All programs and modifications are hereby placed into the
X	Public Domain - all copyrights and lefts are surrendered.
X	Do with it what you will."
X	
XGary Oliver
Xgo@jacobs.cs.orst.edu
/

brucee@runxtsa.runx.oz.au (Bruce Evans) (07/29/90)

In article <19490@orstcs.CS.ORST.EDU> go@jacobs.CS.ORST.EDU (Gary Oliver) writes:
>This package contains a shared text implementation for minix 1.5.10.
>This code was written about a year ago when all I was running was 1.3
>on an old XT.  I have upgraded to 1.5.10 and an AT and reimplemented
>the patches.  Let me know if you have any problems.

I have been working on another version of shared text. Most of it was
written by Peter Housel before he went on holidays. It is basically the
same as Gary's version except it attempts to implement `sticky' text
(where memory allocated for program text is kept around for a while
after all copies of a program exit, to be used with a later copy). This
still has fragmentation problems like those described by Gary.

For me, the time savings from sticky shared text are more important than
the space savings:

386 kernel make with Housel/Evans' (sticky) shared text:
    1:11.00 real      44.13 user      11.15 sys (300K cache)
    2:39.00 real      44.36 user      15.46 sys  (50K cache)

386 kernel make with Oliver's shared text:
    1:54.00 real      44.25 user      16.10 sys (300K cache)
    3:13.00 real      43.90 user      20.86 sys  (50K cache)

386 kernel make with no shared text (actually Housel/Evans' disabled)
    1:53.00 real      44.33 user      15.36 sys (300K cache)
    3:14.00 real      44.00 user      21.65 sys  (50K cache)

The differences are probably due to both caches being too small, so without
sticky text, the compiler passes get reloaded from the disk unnecessarily,
and/or they clog up the cache and waste space that would be better given to
i/o files.

These times are with a kernel change that improves both the real and system
times of the fastest case by 3 or 4 sec, and another change to count the
system time better, so the system time reported is increased by 2 or 3 sec.

Both implementations are limited to sharing separate I&D executables, so
they are of no use on the ST or with Minix-386 executables produced by gcc.
I have started to work on that. Another possibilty is to copy memory around
to completely eliminate memory fragmentation on Minix-PC (safely in protected
mode, not quite safely in real mode). This should be worth 50K, and sharing
shells should be worth 50K, so 640K memory might even be usable :-).
-- 
Bruce Evans
Internet: brucee@runxtsa.runx.oz.au    UUCP: uunet!runxtsa.runx.oz.au!brucee
(My other address (evans@ditsyda.oz.au) no longer works)