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)