Path: tut!draken!kth!mcvax!uunet!husc6!rutgers!orstcs!jacobs.CS.ORST.EDU!go From: go@jacobs.CS.ORST.EDU (Gary Oliver) Newsgroups: comp.os.minix Subject: Re: shared code segments, multi-tasking Keywords: pure code shared Message-ID: <10454@orstcs.CS.ORST.EDU> Date: 8 May 89 10:44:30 GMT References: <8024@killer.Dallas.TX.US> Sender: usenet@orstcs.CS.ORST.EDU Reply-To: go@jacobs.CS.ORST.EDU.UUCP (Gary Oliver) Organization: Oregon State University - CS - Corvallis Oregon Lines: 824 .... Funny you should mentioned shared text... A few weeks ago I happened to hack together this set of changes (for 1.3 though it has been tested on a 1.3c system belonging to a friend) and thought you all might get some use out of it. The basic strategy was to implement a "shared text" structure in the memory manager task maintains a global copy of any text descriptors that are candidates for sharing. For my purposes, I defined "sharable" any program compiled with separate I & D space. Whenever a "shareable" program is exec'd, a scan is made in the shareable text structure for a segment matching the device number, inode number and change date of the program being exec'd. If a match is found, a copy is made of the shared text info into the callers [T] memory map and the reference counter in the shared text structure is advanced. If no match is found, the program is exec'd in the normal way. After an exec of a non-shared program, a test is made to see if it can be made shareable (separate I & D) and if so, a new shared info structure is allocated (if possible) and the necessary information is copied into the structure and the shared info reference count is set to 1. A change was made to the mproc structure (mproc.h) that added a pointer to the shared info structure (if the program currently running is shared or NULL if not shared.) Whenever a fork() call is made, the proc structure is duplicated and if the duplicate is of a shared text program, then no copy of the text portion of memory is made and the shared info reference counter is upped by 1. Whenever a shared text program exits, the reference count in the shared text structure is decremented and if zero, the shared text memory is deallocated (unless sticky) and the structure is marked free. The number of shared text elements is set by the constant NR_SHARE (I placed mine in ../h/const.h) Additionally, another ifdef "STICKY_SHARE" is available that prevents freeing the text memory when all users of the program have exited. The upside of this addition is that shared programs remain a little faster to exectute when run one at a time. The down side is that memory tends to get a little fragmented from the haphazard order of freeing of these text segments. No memory is lost, it just doesn't become available until no other memory is available. I chose a rather simple means to free the unused shared text segments: When alloc_mem is called (in alloc.c) it first tries to allocate from free memory and if none is found, all unreferenced shared text segments are freed and the alloc is attempted a second time. Cheap. Anyway, I've been running about a week and no problems seem to exist (running on two XTs - mine and a friend - and on an AT - three systems in all.) Try it. One other potential change that you may want to make. Since implementing this change, it is quite a bit harder to run out of memory before running out of process slots, so you may want to up NR_PROCS in ../h/const.h ( I made mine 32 instead of 16 and don't run out anymore.) Chose any number you want for NR_SHARE -- the structure consumes 16 bytes per item. Too few and programs won't be shared as efficiently and too many and you waste 16 byte chunks of kernel memory. The total effect to system memory seems to be about 800 more bytes. Pretty cheap. I've made NO attempts to make this work with the ATARI_ST ifdefs. Thanks to Dave Regan for acting as guinea pig for these changes. Gary Oliver go@jacobs.cs.orst.edu Below is the shar file for the five files needing change. The produced files are .cdiff and "patch" compatible. All of these changes are applied to files in ../mm directory. ---------------as they say: cut here and run through sh ------------ echo x - alloc.cdiff sed '/^X/s///' > alloc.cdiff << '/' X*** alloc.c.old Wed Jan 1 00:35:24 1970 X--- alloc.c Wed Jan 1 00:34:12 1970 X*************** X*** 13,23 **** X--- 13,25 ---- X * free_mem: release a previously allocated chunk of memory X * mem_init: initialize the tables when MM start up X * max_hole: returns the largest hole currently available X+ * free_user_mem: added 26-Apr-89 G. Oliver -- frees mem for entire task X */ X X #include "../h/const.h" X #include "../h/type.h" X #include "const.h" X+ #include "mproc.h" X X #define NR_HOLES 128 /* max # entries in hole table */ X #define NIL_HOLE (struct hole *) 0 X*************** X*** 35,43 **** X--- 37,67 ---- X /*===========================================================================* X * alloc_mem * X *===========================================================================*/ X+ #ifdef STICKY_SHARE X PUBLIC phys_clicks alloc_mem(clicks) X phys_clicks clicks; /* amount of memory requested */ X { X+ phys_clicks alloc_try(); X+ phys_clicks memseg; X+ X+ memseg = alloc_try(clicks); X+ if (memseg == NO_MEM) X+ { X+ sh_purge(); /* Purge empty shared text segments */ X+ memseg = alloc_try(clicks); X+ } X+ return (memseg); X+ } X+ #endif /* STICKY_SHARE */ X+ X+ #ifdef STICKY_SHARE X+ /* Change name to helper function if SHARED CODE system */ X+ PRIVATE phys_clicks alloc_try(clicks) X+ #else X+ PUBLIC phys_clicks alloc_mem(clicks) X+ #endif /* STICKY_SHARE */ X+ phys_clicks clicks; /* amount of memory requested */ X+ { X /* Allocate a block of memory from the free list using first fit. The block X * consists of a sequence of contiguous bytes, whose length in clicks is X * given by 'clicks'. A pointer to the block is returned. The block is X*************** X*** 56,66 **** X--- 80,95 ---- X hp->h_base += clicks; /* bite a piece off */ X hp->h_len -= clicks; /* ditto */ X X+ #ifdef NOTUSED X /* If hole is only partly used, reduce size and return. */ X if (hp->h_len != 0) return(old_base); X X /* The entire hole has been used up. Manipulate free list. */ X del_slot(prev_ptr, hp); X+ #else X+ if (hp->h_len == 0) X+ del_slot(prev_ptr, hp); X+ #endif /* NOTUSED */ X return(old_base); X } X X*************** X*** 222,224 **** X--- 251,254 ---- X hole[0].h_base = 0; X hole[0].h_len = clicks; X } X+ / echo x - exec.cdiff sed '/^X/s///' > exec.cdiff << '/' X*** exec.c.old Wed Jan 1 00:34:03 1970 X--- exec.c Wed Jan 1 00:34:40 1970 X*************** X*** 10,15 **** X--- 10,20 ---- X * - tell kernel about EXEC X * X * The only entry point is do_exec. X+ * X+ * 25-Apr-89 G. Oliver. The above is statement is no longer true. X+ * The sh_purge() routine (purge shared structures of unused X+ * entries) is now callable by memory allocation routines when X+ * STICKY_SHARE is defined. X */ X X #include "../h/const.h" X*************** X*** 30,36 **** X #define TOTB 6 /* location of total size in header */ X #define SYMB 7 /* location of symbol size in header */ X X! #ifdef ATARI_ST X PUBLIC long lseek(); X #endif X X--- 35,41 ---- X #define TOTB 6 /* location of total size in header */ X #define SYMB 7 /* location of symbol size in header */ X X! #if defined(ATARI_ST) || NR_SHARE != 0 X PUBLIC long lseek(); X #endif X X*************** X*** 57,62 **** 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*** 92,99 **** 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--- 101,118 ---- 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*** 108,115 **** 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 #ifdef ATARI_ST X if (lseek(fd, sym_bytes, 1) < 0) X--- 127,142 ---- 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 #ifdef ATARI_ST X if (lseek(fd, sym_bytes, 1) < 0) X*************** X*** 117,123 **** 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 (s_buf.st_mode & I_SET_UID_BIT) { X--- 144,163 ---- 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 (s_buf.st_mode & I_SET_UID_BIT) { X*************** X*** 228,234 **** 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--- 268,279 ---- 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*** 260,265 **** X--- 305,315 ---- 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*** 276,285 **** X--- 326,341 ---- X /* There is enough memory for the new core image. Release the old one. */ X rmp = mp; X #ifndef ATARI_ST 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*************** X*** 287,294 **** 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--- 343,365 ---- 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! #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*** 313,320 **** 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--- 384,395 ---- 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! base = base << 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*** 467,469 **** X--- 542,763 ---- 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. Further, the def STICKY_SHARE should be defined X+ * if you wish to implement the "sticky" shared text feature. With X+ * the sticky text, the text segment is not deallocated after the X+ * last user finishes, but rather the freeing is delayed until X+ * the memory allocator (alloc_mem) needs memory and no free chunks X+ * of appropriate size are available. The effect of enabling X+ * STICKY_SHARE is a performance increase when repeatedly calling X+ * separate I&D programs at the expense of increased memory X+ * fragmentation. 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+ #ifdef STICKY_SHARE X+ if ((sh_ptr->sh_seg.mem_len != 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+ #else 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+ #endif /* STICKY_SHARE */ 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+ #ifdef STICKY_SHARE X+ register struct shared_info *sh_free; X+ #endif /* STICKY_SHARE */ X+ register struct shared_info *sh_ptr; X+ X+ /* Look for an unused entry. If no unused entries (sh_seg.mem_len X+ * equal to 0, then take the first entry not currently used for a X+ * running program and free its memory. Note that this approximates X+ * the Un*x "sticky" bit procedure -- shared text segments will lie X+ * around for awhile after no running program uses them until such X+ * time as a new memory is needed. Note that alloc_mem X+ * needs to free these things too, if non-shared text programs are X+ * run when no memory is available. X+ */ X+ #ifdef STICKY_SHARE X+ for (sh_free = &sh_text[NR_SHARE], sh_ptr = sh_text; X+ sh_ptr < &sh_text[NR_SHARE]; X+ ++sh_ptr) X+ { X+ if ((sh_ptr->sh_users == 0) && (sh_free == NULL)) X+ sh_free = sh_ptr; /* Remember first available */ X+ X+ if (sh_ptr->sh_seg.mem_len == 0) X+ break; /* Found an empty -- reuse it */ X+ } X+ X+ if (sh_ptr >= sh_text) X+ sh_ptr = sh_free; /* Use free entry */ X+ #else X+ for (sh_ptr = sh_text; sh_ptr < &sh_text[NR_SHARE]; ++sh_ptr) X+ { X+ if (sh_ptr->sh_users == 0) X+ break; /* Found an unused item */ X+ } X+ #endif /* STICKY_SHARE */ X+ X+ if (sh_ptr < &sh_text[NR_SHARE]) X+ { X+ #ifdef STICKY_SHARE X+ if (sh_ptr->sh_seg.mem_len !=0) X+ { X+ /* First free the memory used on this segment */ X+ free_mem(sh_ptr->sh_seg.mem_phys, sh_ptr->sh_seg.mem_len); X+ } X+ #endif /* STICKY_SHARE */ 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+ } X+ } X+ X+ X+ #ifdef STICKY_SHARE X+ /* X+ * Purge shared text table of all text segments not currently X+ * allocated to any running programs. X+ */ X+ sh_purge() 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) && (sh_ptr->sh_seg.mem_len != 0)) X+ { X+ /* Free this entry */ X+ free_mem(sh_ptr->sh_seg.mem_phys, sh_ptr->sh_seg.mem_len); X+ X+ sh_ptr->sh_seg.mem_len = 0; /* None alloc'd */ X+ } X+ } X+ } X+ #endif /* STICKY_SHARE */ 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+ #ifdef STICKY_SHARE X+ sh_ptr->sh_seg.mem_len = sh_ptr->sh_users = 0; X+ #else X+ sh_ptr->sh_users = 0; X+ #endif /* STICKY_SHARE */ 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+ #ifdef STICKY_SHARE X+ --(rmp->mp_shared->sh_users); /* Just decrease use count */ X+ #else 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+ #endif /* STICKY_SHARE */ 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+ X+ X+ #ifdef NOTUSED X+ /* X+ * Used by F4 key function processor (future) X+ */ X+ sh_dump() X+ { X+ struct shared_info *sh_ptr; X+ int hdr; X+ X+ for (hdr = TRUE, sh_ptr = sh_text; sh_ptr < &sh_text[NR_SHARE]; ++sh_ptr) X+ { X+ #ifdef STICKY_SHARE X+ if (sh_ptr->sh_seg.mem_len != 0) X+ #else X+ if (sh_ptr->sh_users != 0) X+ #endif /* STICKY_SHARE */ X+ { X+ if (hdr) X+ { X+ printf("Index Seg Len Users\n"); X+ hdr = FALSE; X+ } X+ X+ /* Print contents of this segment */ X+ printf(" %3d %4x %4x %3d\n", X+ sh_ptr - sh_text, X+ sh_ptr->sh_seg.mem_phys, X+ sh_ptr->sh_seg.mem_len, X+ sh_ptr->sh_users); X+ } X+ } X+ } X+ #endif /* NOTUSED */ X+ #endif /* NR_SHARE */ X+ / echo x - forkexit.cdiff sed '/^X/s///' > forkexit.cdiff << '/' X*** forkexit.c.old Wed Jan 1 00:35:19 1970 X--- forkexit.c Wed Jan 1 00:34:44 1970 X*************** X*** 59,65 **** 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 #ifndef ATARI_ST 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--- 59,71 ---- 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 #ifndef ATARI_ST 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 if ( (child_base = alloc_mem(prog_clicks)) == NO_MEM) return(EAGAIN); X*************** X*** 67,73 **** X #ifndef ATARI_ST 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--- 73,93 ---- X #ifndef ATARI_ST 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*** 86,93 **** X X rmc->mp_parent = who; /* record child's parent */ X #ifndef ATARI_ST 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--- 106,127 ---- X X rmc->mp_parent = who; /* record child's parent */ X #ifndef ATARI_ST 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*** 186,196 **** X--- 220,234 ---- X tell_fs(EXIT, proc_nr, 0, 0); /* file system can free the proc slot */ X X #ifndef ATARI_ST 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*** main.c.old Wed Jan 1 00:35:40 1970 X--- main.c Wed Jan 1 00:34:47 1970 X*************** X*** 122,127 **** X--- 122,131 ---- X mproc[INIT_PROC_NR].mp_flags |= IN_USE; X procs_in_use = 3; X X+ #if NR_SHARE != 0 X+ sh_init(); /* Initialize shared code tables in exec.c */ X+ #endif /* NR_SHARE */ X+ X /* Set stack limit, which is checked on every procedure call. */ X } X X*************** X*** 188,193 **** X--- 192,200 ---- 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 - mproc.cdiff sed '/^X/s///' > mproc.cdiff << '/' X*** mproc.h.old Wed Jan 1 00:35:21 1970 X--- mproc.h Wed Jan 1 00:34:08 1970 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 int mp_pid; /* process id */ X*************** X*** 27,36 **** X unsigned mp_flags; /* flag bits */ X } mproc[NR_PROCS]; X X! /* Flag values */ X! #define IN_USE 001 /* set when 'mproc' slot in use */ X! #define WAITING 002 /* set by WAIT system call */ X! #define HANGING 004 /* set by EXIT system call */ X! #define PAUSED 010 /* set by PAUSE system call */ X! #define ALARM_ON 020 /* set when SIGALRM timer started */ X! #define SEPARATE 040 /* set if file is separate I & D space */ X--- 30,49 ---- 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 */ X! #define HANGING 004 /* set by EXIT system call */ X! #define PAUSED 010 /* set by PAUSE system call */ X! #define ALARM_ON 020 /* set when SIGALRM timer started */ X! #define SEPARATE 040 /* set if file is separate I & D space */ /